From ddb8d9d8ed55a9a46c805543a841fc88e936639f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Br=C3=B6der?= Date: Tue, 23 Aug 2022 17:14:14 +0200 Subject: [PATCH 01/64] Add HMC footer and header to layout, App is not well embeded yet. --- dashboard/description.html | 1422 ++++++++++++++++++++++++- dashboard/hmc_layout/footer_.html | 1108 +++++++++++++++++++ dashboard/hmc_layout/header_.html | 271 +++++ dashboard/hmc_layout/hmc_laylout.html | 6 + 4 files changed, 2766 insertions(+), 41 deletions(-) create mode 100644 dashboard/hmc_layout/footer_.html create mode 100644 dashboard/hmc_layout/header_.html create mode 100644 dashboard/hmc_layout/hmc_laylout.html diff --git a/dashboard/description.html b/dashboard/description.html index 8a5a737..e53f7a9 100644 --- a/dashboard/description.html +++ b/dashboard/description.html @@ -1,41 +1,1381 @@ - - - -

The HMC Survey explorer

- -

- Explorer interactively the HMC 2021 Survey results. - Interact with the visualizations and widgets on the left to explorer the data. -

-

-
\ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ + + +
+ + + +
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +
+ + + + + +
+ +
+
+ + The HMC Survey explorer + +
+ +
+
+ + + + + +
+
+ + + + +
+ + + +
+ + + + +
+
+ + + +
+
+ + + +
+ +
+ +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + diff --git a/dashboard/hmc_layout/footer_.html b/dashboard/hmc_layout/footer_.html new file mode 100644 index 0000000..a4d9660 --- /dev/null +++ b/dashboard/hmc_layout/footer_.html @@ -0,0 +1,1108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dashboard/hmc_layout/header_.html b/dashboard/hmc_layout/header_.html new file mode 100644 index 0000000..24874c9 --- /dev/null +++ b/dashboard/hmc_layout/header_.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+ +
+ + + +
+ +
+ +
+ +
+ + + +
+ + + + + +
+ +
+
+ + The HMC Survey explorer + +
+ +
+
+ + + + + +
+
+ + + + +
+ + + +
+ + + + +
+
+ diff --git a/dashboard/hmc_layout/hmc_laylout.html b/dashboard/hmc_layout/hmc_laylout.html new file mode 100644 index 0000000..112bdac --- /dev/null +++ b/dashboard/hmc_layout/hmc_laylout.html @@ -0,0 +1,6 @@ + + +
+ + +
From 6d08b1c3b09bee269ce9f8e6643ff971f0104c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Br=C3=B6der?= Date: Wed, 24 Aug 2022 12:34:46 +0200 Subject: [PATCH 02/64] Properly place dashboard between HMC header and footer, through footer alone is broken --- dashboard/main.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dashboard/main.py b/dashboard/main.py index 89b5ed8..1239119 100644 --- a/dashboard/main.py +++ b/dashboard/main.py @@ -26,7 +26,8 @@ pwd = os.getcwd() # todo better use the absolute location of this file... datafilepath= join(pwd, 'dashboard/data/20211130_HMCCommSurvey_clean.csv') -desc = Div(text=open(join(dirname(__file__), "description.html")).read(), sizing_mode="stretch_width") +header = Div(text=open(join(dirname(__file__), "hmc_layout/header_.html")).read(), sizing_mode="stretch_width") +footer = Div(text=open(join(dirname(__file__), "hmc_layout/footer_.html")).read(), sizing_mode="stretch_width") pwd = os.getcwd() questions = open(join(pwd, 'dashboard/data/barchart_allowed.txt')).read().split('\n') @@ -218,8 +219,8 @@ def generate_corr_controls(): row1 = row(inputs, fig, fig_corr, inputs_corr) -first = column(desc, row1, sizing_mode="scale_both") -layout = column(first, row(button_bar), sizing_mode="scale_both") +first = column(header, row1, sizing_mode="scale_both") +layout = column(first, row(button_bar), footer, sizing_mode="scale_both") curdoc().add_root(layout) curdoc().title = "HMC Survey Dashboard" \ No newline at end of file From f9098cc33df80188438e4e7db455b2931670a211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Br=C3=B6der?= Date: Tue, 28 Feb 2023 11:33:16 +0100 Subject: [PATCH 03/64] Add first dirty Jinja2 template created out of HMC webpage html and static resource. --- dashboard/main.py | 308 - survey_dashboard/hmc_layout/en_template.html | 4629 + survey_dashboard/hmc_layout/footer_.html | 1108 - survey_dashboard/hmc_layout/header_.html | 271 - survey_dashboard/hmc_layout/hmc_laylout.html | 6 - .../en_files/2022_HMC-Conference_News-md.jpg | Bin 0 -> 87651 bytes .../static/en_files/2022_HMC_@FDO-md.jpg | Bin 0 -> 35801 bytes .../en_files/2022_HMC_FAIR-DOscope2-md.jpg | Bin 0 -> 47781 bytes .../static/en_files/2022_HMC_FAIRwish-md.jpg | Bin 0 -> 41999 bytes .../static/en_files/2022_HMC_PIDA-md.jpg | Bin 0 -> 49470 bytes .../2023-01-23_ProjectCall_Visual_news-md.jpg | Bin 0 -> 21543 bytes .../en_files/2023-01_CourseVisual-md.png | Bin 0 -> 93700 bytes .../2023_HMC-FAIRFriday_vanReisen-md.jpg | Bin 0 -> 45460 bytes ...23_HMC-Projects_Workshops_Secop@HMC-md.jpg | Bin 0 -> 43433 bytes ...HMC_Projects@RDA-FAIR-DO-Fabric-IG--md.jpg | Bin 0 -> 42958 bytes .../static/en_files/2023_HMC_at_DESY-md.jpg | Bin 0 -> 55796 bytes .../en_files/623c39184ca97bb437e258ef(1).html | 3 + .../en_files/623c39184ca97bb437e258ef.html | 3 + .../en_files/AI-HERO_flyer-(002)-md.png | Bin 0 -> 230280 bytes ...MC-FAIRFriday_Bertuch_Garcia-Castro-md.jpg | Bin 0 -> 53376 bytes .../en_files/HMC-FAIRFriday_Britta-md.jpg | Bin 0 -> 41486 bytes .../en_files/HMC-FAIRFriday_huang-md.jpg | Bin 0 -> 45795 bytes .../static/en_files/HMC-HEC-2023-md.PNG | Bin 0 -> 75794 bytes .../HMC_Events_deRSE-20223-preview-md.jpg | Bin 0 -> 21686 bytes .../en_files/HMC_TEACH-2-2022-lg-md.jpg | Bin 0 -> 54927 bytes .../static/en_files/MC2023_Header-md.jpg | Bin 0 -> 52345 bytes .../en_files/Marble_Workshop_visual-md.png | Bin 0 -> 103510 bytes .../en_files/Partner-Website_900x675px-md.png | Bin 0 -> 318627 bytes .../en_files/Teaser_M4R_20230327-md.PNG | Bin 0 -> 61438 bytes .../static/en_files/Teaser_raw-md.PNG | Bin 0 -> 60815 bytes .../hmc_layout/static/en_files/app.css | 3 + .../hmc_layout/static/en_files/app.js | 2 + .../static/en_files/cables-logo.svg | 15 + .../static/en_files/cables.min(1).js | 4 + .../hmc_layout/static/en_files/cables.min.js | 4 + .../hmc_layout/static/en_files/code | 116994 +++++++++++++++ .../hmc_layout/static/en_files/lang.js | 1 + .../hmc_layout/static/en_files/libs(1).js | 0 .../static/en_files/libs.core.min(1).js | 44 + .../static/en_files/libs.core.min.js | 44 + .../hmc_layout/static/en_files/libs.js | 0 .../hmc_layout/static/en_files/libs.min.js | 36 + .../hmc_layout/static/en_files/matomo.js | 76 + .../static/en_files/micromodal.min.js | 1 + .../static/en_files/projectops(1).js | 4269 + .../hmc_layout/static/en_files/projectops.js | 4269 + .../hmc_layout/static/en_files/script.min.js | 3 + .../hmc_layout/static/en_files/talkerapi.js | 2 + .../visual_EMGlossary_2022-06_1-md.png | Bin 0 -> 254061 bytes survey_dashboard/main.py | 22 +- 50 files changed, 130421 insertions(+), 1696 deletions(-) delete mode 100644 dashboard/main.py create mode 100644 survey_dashboard/hmc_layout/en_template.html delete mode 100644 survey_dashboard/hmc_layout/footer_.html delete mode 100644 survey_dashboard/hmc_layout/header_.html delete mode 100644 survey_dashboard/hmc_layout/hmc_laylout.html create mode 100644 survey_dashboard/hmc_layout/static/en_files/2022_HMC-Conference_News-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/2022_HMC_@FDO-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/2022_HMC_FAIR-DOscope2-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/2022_HMC_FAIRwish-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/2022_HMC_PIDA-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/2023-01-23_ProjectCall_Visual_news-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/2023-01_CourseVisual-md.png create mode 100644 survey_dashboard/hmc_layout/static/en_files/2023_HMC-FAIRFriday_vanReisen-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/2023_HMC-Projects_Workshops_Secop@HMC-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/2023_HMC_Projects@RDA-FAIR-DO-Fabric-IG--md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/2023_HMC_at_DESY-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/623c39184ca97bb437e258ef(1).html create mode 100644 survey_dashboard/hmc_layout/static/en_files/623c39184ca97bb437e258ef.html create mode 100644 survey_dashboard/hmc_layout/static/en_files/AI-HERO_flyer-(002)-md.png create mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC-FAIRFriday_Bertuch_Garcia-Castro-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC-FAIRFriday_Britta-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC-FAIRFriday_huang-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC-HEC-2023-md.PNG create mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC_Events_deRSE-20223-preview-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC_TEACH-2-2022-lg-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/MC2023_Header-md.jpg create mode 100644 survey_dashboard/hmc_layout/static/en_files/Marble_Workshop_visual-md.png create mode 100644 survey_dashboard/hmc_layout/static/en_files/Partner-Website_900x675px-md.png create mode 100644 survey_dashboard/hmc_layout/static/en_files/Teaser_M4R_20230327-md.PNG create mode 100644 survey_dashboard/hmc_layout/static/en_files/Teaser_raw-md.PNG create mode 100644 survey_dashboard/hmc_layout/static/en_files/app.css create mode 100644 survey_dashboard/hmc_layout/static/en_files/app.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/cables-logo.svg create mode 100644 survey_dashboard/hmc_layout/static/en_files/cables.min(1).js create mode 100644 survey_dashboard/hmc_layout/static/en_files/cables.min.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/code create mode 100644 survey_dashboard/hmc_layout/static/en_files/lang.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/libs(1).js create mode 100644 survey_dashboard/hmc_layout/static/en_files/libs.core.min(1).js create mode 100644 survey_dashboard/hmc_layout/static/en_files/libs.core.min.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/libs.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/libs.min.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/matomo.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/micromodal.min.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/projectops(1).js create mode 100644 survey_dashboard/hmc_layout/static/en_files/projectops.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/script.min.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/talkerapi.js create mode 100644 survey_dashboard/hmc_layout/static/en_files/visual_EMGlossary_2022-06_1-md.png diff --git a/dashboard/main.py b/dashboard/main.py deleted file mode 100644 index 28793a4..0000000 --- a/dashboard/main.py +++ /dev/null @@ -1,308 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# Copyright (c), Forschungszentrum Jülich GmbH, IAS-9, Germany. # -# All rights reserved. # -# This file is part of the survey_dashboard package. # -# # -# The code is hosted on GitHub at # -# https://github.com/Materials-Data-Science-and-Informatics/survey_dashboard # -# For further information on the license, see the LICENSE file # -############################################################################### -""" -This file contains the main code for the dashboard and its layout. -""" -import random -import os -from random import randint -from os.path import dirname, join -import pandas as pd -#import panel as pn -from bokeh.models import Panel, Tabs -from bokeh.layouts import column, row -from bokeh.models import Dropdown -from bokeh.palettes import RdYlBu3 -from bokeh.palettes import Category20c -from bokeh.plotting import curdoc -from bokeh.models import ColumnDataSource, Div, Select -from bokeh.plotting import figure as bokeh_figure -from bokeh.models import CheckboxButtonGroup, CustomJS -from bokeh.models import MultiChoice -from bokeh.models import Paragraph -from bokeh.client import logging -from masci_tools.vis.bokeh_plots import bokeh_scatter, bokeh_multi_scatter -from .plots import bokeh_barchart, bokeh_piechart, add_legend_at, bokeh_corr_plot#, bokeh_barchart2 -from .plots import create_legend_corr -from .analysis import calculate_crosstab, prepare_data_research_field, filter_dataframe -from .analysis import percentage_to_area -from .data.helpers.hcs_clean_dictionaries import HCSquestions, HCS_orderedCats, HCS_MCsubquestions -from .data.helpers.hcs_clean_dictionaries import HCS_colnamesDict, abbrevCenterAffilDict - - -HCS_COLNAMES_REVERT_DICT = {val:key for key, val in HCS_colnamesDict.items()} -pwd = os.getcwd() # todo better use the absolute location of this file... -datafilepath= join(pwd, 'dashboard/data/20211130_HMCCommSurvey_clean.csv') - - -header = Div(text=open(join(dirname(__file__), "hmc_layout/header_.html")).read(), sizing_mode="stretch_width") -footer = Div(text=open(join(dirname(__file__), "hmc_layout/footer_.html")).read(), sizing_mode="stretch_width") - -pwd = os.getcwd() -questions = open(join(pwd, 'dashboard/data/barchart_allowed.txt')).read().split('\n') -QUESTION_MAP = {question : i for i, question in enumerate(questions)} -FILTER_OPTIONS = open(join(pwd, 'dashboard/data/filters.txt')).read().split('\n') -MARGINS = (10,10,10,10) - -# Data displayed on startup: -START_DATA_BAR = "careerLevel" -START_DATA_CORR_1 = "careerLevel" -START_DATA_CORR_2 = "docStructured" - - -# colors of research areas -#ra_colors = {'All': "#75968f", 'Chemistry': "#a5bab7", 'Earth Science':"#c9d9d3", -#Engineering Science': "#e2e2e2", 'Life Science': "#dfccce", 'Mathematics': "#ddb7b1", -#'Other': "#cc7878", 'Physics': "#933b41", 'Psychology': "#550b1d"} -re = ['All', 'Cum. Sum', 'Chemistry', 'Earth Science', 'Engineering Science', 'Life Science', - 'Mathematics', 'Other', 'Physics', 'Psychology'] -re_c = Category20c[len(re)] -ra_colors = {field: re_c[i] for i, field in enumerate(re)} - - -# Read data -survey_data = pd.read_csv(datafilepath) -source = ColumnDataSource(survey_data) -source_corr = ColumnDataSource(survey_data) - - -#### Control widgets - -# Bar chart -# D: question select could also be a slider? -question_select = Select(title="Question", value=START_DATA_BAR, - options=questions, description="Select the survey question, which results should be displayed.", margin=MARGINS) - -multi_choice = MultiChoice(title="Filter data", value=["Cum. Sum", "Physics"], options=FILTER_OPTIONS, - description="Select which data subset to be displayed.", margin=MARGINS) - -# D: better than a Select would be to use the HMC template for this... -# D: Maybe have two dashboards running a German and a English one and link build in the link in the template... -#lang_select = Select(title="Language", value="en", -# options=['en', 'de'], description="Change the language of the Dashboard.") - -# Correlation plot -question_select2 = Select(title="Question X-Axis", value=START_DATA_CORR_1, - options=questions, description="Select the survey question which results should be displayed on the X-Axis.") -question_select3 = Select(title="Question Y-Axis", value=START_DATA_CORR_2, - options=questions, description="Select the survey question which results should be displayed on the Y-Axis.") - - -# Others -plot_aspect_ratio_select = Select(title="Filter data", value="Cum. Sum", - options=FILTER_OPTIONS) -menu = [('Barchart', 'bar_chart'), ('Correlation Plot', '(corr_plot')] -button_bar = Dropdown(label="Add a chart", button_type="success", menu=menu) -#button_corr = Button(label="Add correlation chart display", button_type="success") - -TOOLTIPS=[ - ("Title", "@title"), - ("Answer", "@x"), - ("Number of Answers", "@y") -] - - - -def select_data(question_select=question_select, multi_choice=multi_choice): - """Select the data to display""" - - question = question_select.value - q_index = question #index = QUESTION_MAP[question] - question_full = HCSquestions['EN'][HCS_COLNAMES_REVERT_DICT[question]] - to_exclude = [] - #TODO map the question to index/key - #TODO there should be a certain display order, i.e mappings needed - #TODO, also some things are excluded - - data_filters = multi_choice.value - - # need something like [index: index_name, full question: '', title; '', 'key order' : [], 'skipped keys':[]] - - # for now this is greedy, if to slow think of another way - # we want to display anything in terms of researchArea, todo better was to d filter, generalize this - df = filter_dataframe(survey_data, include=[q_index, "researchArea"], exclude=[(q_index, to_exclude)]) - - exclude = [] - for field in re: - if field not in data_filters: - exclude.append(field) - for area in exclude: - df = df[df["researchArea"] != area] - data, y_keys = prepare_data_research_field(df, q_index) - #df = pd.DataFrame.from_dict(data) - - #data_filters = y_keys - ydata_spec = {} - colors = [] - for key in y_keys: - if key in data_filters: - colors.append(ra_colors[key]) - - ydata_spec['y_keys'] = data_filters - ydata_spec['colors'] = colors - ydata_spec['legend_labels'] = data_filters - selected = ColumnDataSource(data=data) - #dict(value=values, counts=counts, factors=values, color=colors, legend_labels=data_filters))#, title=question)) - ydata_spec = ColumnDataSource(data=ydata_spec) - - return selected, ydata_spec, question_full - - - -def select_data_corr(question_select2=question_select2, question_select3=question_select3): - """Select the data to display""" - - question = question_select2.value - question2 = question_select3.value - - #q1_key = QUESTION_MAP[question] - q1_key = question - q2_key = question2 - - # Enforced order of x and y axis - x_range = HCS_orderedCats[q1_key] #list(survey_data[q1_key].value_counts().keys()) - y_range = HCS_orderedCats[q2_key] #list(survey_data[q2_key].value_counts().keys()) - - # If this is slow to calculate each time, it might make sense to calculate all of these - # at start up. i.e n^2 tables - cross_tab = calculate_crosstab(survey_data, q1_key, q2_key) - # marker size is radius, we want the Area to be proportional to the value - # we also scale the markers with the max values are to small, or depending how many Cat, or - # figure width... - marker_scale = 20.0 - cross_tab['markersize'] = percentage_to_area(cross_tab['percentage'], scale_m=marker_scale) - cross_tab['x_values'] = cross_tab[q1_key] - cross_tab['y_values'] = cross_tab[q2_key] - cross_tab['color'] = ['#A0235A' for i in cross_tab[q2_key]] - - # for hover tool - tooltips = [(f"{q1_key}", "@x_values"), - (f"{q2_key}", "@y_values"), - ("total", "@total"), - ("percentage", "@percentage")] - - title = f'{q1_key} in dependence to {q2_key}' - # Bokeh plots need a ColumnDataSource, but this can be initialized from a pandas - #selected = ColumnDataSource(data=dict(x_values=cross_tab[q1_key], y_values=cross_tab[q2_key], - # markersize=cross_tab['percentage'], total=)) - - selected = ColumnDataSource(cross_tab) - - display_options = {'x_range' : x_range, 'y_range' : y_range, - 'tooltips' : tooltips, 'title': title} - return selected, display_options, marker_scale - - - -#def select_chart(chart_select=chart_select): -## """Select how the data should be displayed""" -# chart_type = chart_select.value.strip() -# return chart_type - -def update(ob_index=0): - """Update the charts""" - df, ydata_spec, question = select_data() - #charttype = select_chart(chart_select=chart_select) - - #source.data = dict(value=df.data['value'], counts=df.data['counts'], legend_labels=df.data['legend_labels']) - source = df - # TODO: always plot piechart in seperate tab - #if charttype == 'pie': - # fig = bokeh_piechart(source)#, figure=figure) - # row1.children[1] = fig - #else: - #print(y_keys, df.data['colors']) - y_keys = ydata_spec.data['y_keys'] - fill_colors = ydata_spec.data['colors'] - fig = bokeh_barchart(source, y=y_keys, factors=y_keys, legend_labels=y_keys, fill_color=fill_colors, title=f'Q: {question}', orientation='vertical')#df.data['factors'], legend_labels='legend_labels')#, figure=figure) - fig.margin = MARGINS - #fig2 = bokeh_barchart(source, y=y_keys, factors=y_keys, legend_labels=y_keys, fill_color=fill_colors, title=question, orientation='horizontal')#'horizontal')#df.data['factors'], legend_labels='legend_labels')#, figure=figure) - #fig2.margin = MARGINS - - tab1 = Panel(child=fig, title="Vertical Bar chart") - tab2 = Panel(child=fig, title="Horizontal Bar chart") - tab3 = Panel(child=fig, title="Pie chart") - tabs = Tabs(tabs=[tab1, tab2, tab3]) - row2.children[ob_index] = tabs - #add_legend_at(fig) - -def update_corr(ob_index=1): - """Update the correlation plot""" - df, display_options, marker_scale = select_data_corr() - fig_corr = bokeh_corr_plot(df, **display_options) - leg_corr = create_legend_corr(fig_corr, colors=df.data['color'], scale_m=marker_scale) - #fig_corr.margin = MARGINS - - row3.children[ob_index] = fig_corr - row3.children[ob_index+1] = leg_corr - - - -# Generate Start figures - -start_display_data, ydata_spec, question= select_data() -y_keys = ydata_spec.data['y_keys'] -fill_colors = ydata_spec.data['colors'] -fig = bokeh_barchart(start_display_data, y=y_keys, factors=y_keys, legend_labels=y_keys, - fill_color=fill_colors, title=f'Q: {question}', orientation='vertical')#factors=start_display_data.data['factors']) - -#fig.margin = MARGINS -start_corr_data, display_options, marker_scale = select_data_corr() -fig_corr = bokeh_corr_plot(start_corr_data, **display_options) -leg_corr = create_legend_corr(fig_corr, colors=start_corr_data.data['color'], scale_m=marker_scale) -#fig_corr.margin = MARGINS -#fig2 = bokeh_barchart(source_corr) - -#fig2 = bokeh_multi_scatter(source2, marker_size=[int(x)+6 for x in xs[1]) - - -def generate_bar_controls(): - controls_bar = [question_select, multi_choice] - return controls_bar - -def generate_corr_controls(): - controls_corr = [question_select2, question_select3] - return controls_corr - - - -# Dashboard layout - -# for spaces: https://docs.bokeh.org/en/latest/docs/reference/models/layouts.html#spacer -controls_bar = generate_bar_controls() -inputs = column(*controls_bar, width=800) -for control in controls_bar: - control.on_change('value', lambda attr, old, new: update()) - -controls_corr = generate_corr_controls() -for control in controls_corr: - control.on_change('value', lambda attr, old, new: update_corr()) -inputs_corr = column(*controls_corr, width=200) - -# create tabs -tab1 = Panel(child=fig, title="Vertical Bar chart") -tab2 = Panel(child=fig, title="Horizontal Bar chart") -tab3 = Panel(child=fig, title="Pie chart") -tabs = Tabs(tabs=[tab1, tab2, tab3]) - -row1 = row(inputs) -row2 = row(tabs) -row3 = row(inputs_corr, fig_corr, leg_corr) -first = column(header, row1, sizing_mode="scale_both") -layout = column(first, row2, row3, row(button_bar), footer, sizing_mode="scale_both") - -#row1 = pn.Row(inputs, tabs, fig_corr, inputs_corr) -#first = pn.Column(desc, row1, sizing_mode="scale_both") -#layout = pn.Ccolumn(first, pn.Row(button_bar), sizing_mode="scale_both") - - -curdoc().add_root(layout) -curdoc().title = "HMC Survey Dashboard" \ No newline at end of file diff --git a/survey_dashboard/hmc_layout/en_template.html b/survey_dashboard/hmc_layout/en_template.html new file mode 100644 index 0000000..9641599 --- /dev/null +++ b/survey_dashboard/hmc_layout/en_template.html @@ -0,0 +1,4629 @@ +{% extends base %} + +{% block postamble %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{% endblock %} + + {% block inner_body %} +
+
+
+
+ + +
+ + + + + +
+ + +
+ + +
+
+ +
+
+
+
+ {% block contents %} + {{ app_title }} +
+
+ {% for doc in docs %} + {{ embed(doc) if doc.elementid }} + {% for root in doc.roots %} + {{ embed(root) | indent(10) }} + {% endfor %} + {% endfor %} + {{ plot_script | indent(8) }} +
+
+
+ {{ embed(roots.App) }} +
+
+
+ {% endblock %} +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + {% endblock %} diff --git a/survey_dashboard/hmc_layout/footer_.html b/survey_dashboard/hmc_layout/footer_.html deleted file mode 100644 index a4d9660..0000000 --- a/survey_dashboard/hmc_layout/footer_.html +++ /dev/null @@ -1,1108 +0,0 @@ - -
-
- - - -
- -
- -
- - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - diff --git a/survey_dashboard/hmc_layout/header_.html b/survey_dashboard/hmc_layout/header_.html deleted file mode 100644 index 24874c9..0000000 --- a/survey_dashboard/hmc_layout/header_.html +++ /dev/null @@ -1,271 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- - - -
-
- -
- - - -
- -
- -
- -
- - - -
- - - - - -
- -
-
- - The HMC Survey explorer - -
- -
-
- - - - - -
-
- - - - -
- - - -
- - - - -
-
- diff --git a/survey_dashboard/hmc_layout/hmc_laylout.html b/survey_dashboard/hmc_layout/hmc_laylout.html deleted file mode 100644 index 112bdac..0000000 --- a/survey_dashboard/hmc_layout/hmc_laylout.html +++ /dev/null @@ -1,6 +0,0 @@ - - -
- - -
diff --git a/survey_dashboard/hmc_layout/static/en_files/2022_HMC-Conference_News-md.jpg b/survey_dashboard/hmc_layout/static/en_files/2022_HMC-Conference_News-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..968b89d72ae5ce399ae587f4462ca54488e488a6 GIT binary patch literal 87651 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

(K(Us@ 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 b%yuGVNR-EmDx+ms=uM@iENmi!x~%*c>9}Qj literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/2022_HMC_@FDO-md.jpg b/survey_dashboard/hmc_layout/static/en_files/2022_HMC_@FDO-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..291acf02fbac0b8ac234fb8f324596f0062a0f41 GIT binary patch literal 35801 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�))n|(o>&aHe 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 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/2022_HMC_FAIR-DOscope2-md.jpg b/survey_dashboard/hmc_layout/static/en_files/2022_HMC_FAIR-DOscope2-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..683a81624f5e5cde4f940e09c7865c6fb3b4c759 GIT binary patch literal 47781 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 I`N#bK1JtjzcmMzZ literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/2022_HMC_FAIRwish-md.jpg b/survey_dashboard/hmc_layout/static/en_files/2022_HMC_FAIRwish-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8ab223e522cfec7d982c17b9cab02173f11605cf GIT binary patch literal 41999 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 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/2023-01-23_ProjectCall_Visual_news-md.jpg b/survey_dashboard/hmc_layout/static/en_files/2023-01-23_ProjectCall_Visual_news-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..70a868c10bcf2e8378970f36dd856134936c343d GIT binary patch literal 21543 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

sBc;mX8g=T7TVO{1>nXZ0OLj(0W18V( zp-?rAu-eECjm&Hi7_?UxyZ->91-zGBlXL2tXKr11%o9T;HEfh`xVxKumPdMuzAA?J zO`XvO=*`BcGgjhI(G4A%Pr?e%Y#b?Trk+PUxx)Ub+Xs`xG+hYO7~L8+Ff~BXbg>zM z)7b$vjEhF*XjhR5swC=E)tdfpJRcBo1M*3nQg2JpTZ*E4(3s!>xk8L;5vgW{an%jQjS4xfnxKY-Y#4AmAhZ_Iau6yxJUT5;K=T3$p-#{g8KmZe6q}S8$kiECI!iAsakD>SS*bI*dSVh%F(|a(3`LzQO6HS86g7;QFp?+?uVO4OSLnQU4M z8X&N5bOys@8RK_{{PkL%gTC$607dSB4{oIfceg@;J>RM(nrNAt7ykgXaQ^@bD@!7_ ziU+ZPtcNgI6b9Xqxi%7XD8Qod%un#2@dOWRvt0oLWD@8M6a+L_vq0GUU1V8tC=G|g zfvqSAXoA7I2u*?ZDri0@M?ZlMKSpa{tY;G3CsEYR`K)< zY82fUa<(iRlmrcsTU>5|5o;L#06fZ}%_M>f(4(<4GIfsH$>3dZir-i6bu64G}Lgha(>_a z*8Zz3l?b{GENIKJ4?MEdNo%0C$GQeKAklZ7IRUx_y_UO~t48hV>VmgH5JV6lO!Z8s z)Snk1gB>;?Sn?zu9;nNv#|MDsn}UrEy4?d~*rJ@#;n^FL!SR7&NE#zcW0+2WDi|0X z4uL?tmc7E%07j)$(vi&rg8?URR7WQ7;u=mtp%GD9cpT*>Z1;0wdN2y_U3@v zbk$;l3t+Pb>ZSBC)?#84mOm4gaR*0;vT>E#Jt*6K?6MmX(^LL+K--{=Ium<@8LBIq zz*@~p0;R+NA(Cm*%Oj%KTmyTB0|tcw0*r~LeNa=U$!50idHaLvpbD%US4P)&nQEK& zzT>?QMF%a6d=kDXS>4LrEP?Thw@pxa`HL>=O(SEv19hX*?|STkWS1M0a)QAYK~83K zc=U1IypQ}4A+98w^|As6*Fl!5xJEL@ZsX{KnE6Y|E;lI1hKL_cQ1~yD_<4jQtxr2< zXf4yx8f}CFbacnj*08@MX-|_e(?n-;K}R!LF#_C?yic%uR-riwkYo7_q;dYfrS||6hmjj|Sg|`IsL5ixky9KS>gdFoBB$KHq z${>9`Q=lTp!_gXcX{uXM7%kBBP&colgFRhXrC}#q>=Q$cJwxgZ$Pa_1Z+lqFxY!`&Pl&!}7DwS=(3BFMm^d4Y^FT7v zz9$#8X2H7;#BX*YG)P}Hb6FTHn>J~|JhQEB_sd< literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/2023-01_CourseVisual-md.png b/survey_dashboard/hmc_layout/static/en_files/2023-01_CourseVisual-md.png new file mode 100644 index 0000000000000000000000000000000000000000..3b559c157131184cb576751a39fcea2f5e9b364d GIT binary patch literal 93700 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

EK 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
7%1QIq$Uyb63k42 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 Ms*Vcc!P8g&3&BJ6=l}o! literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/2023_HMC-FAIRFriday_vanReisen-md.jpg b/survey_dashboard/hmc_layout/static/en_files/2023_HMC-FAIRFriday_vanReisen-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5bb55437d78e85fb7b89deecf849151e0f3f1afa GIT binary patch literal 45460 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 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/2023_HMC-Projects_Workshops_Secop@HMC-md.jpg b/survey_dashboard/hmc_layout/static/en_files/2023_HMC-Projects_Workshops_Secop@HMC-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4ad54d50687499877db4012b8498ef2162d1d2e0 GIT binary patch literal 43433 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�J#6Qk_*x=!Ay2=(v*t;TQd4VXe5q==T@EFLCn-m5si*E; z&Q<)B|4+sWxDFpT$P#Cm+oPz3-e5%@=h>E(0{P4=K#>R_^UDN#EfwSW6Y6Qe2CXOvGhVnbdRSxabHp@msHtVKmc zL`-pQzU}cn=jY;y@C=hxH>P4gQGQo?nF&!65Y?nDwy!j@EF*{`XGw#t? z0c5H?180jfdGUtIb6CBK-F>#nU)`zv8-zV&hjR9#_5RSfTSvglBW0zL(Af{=w!ch| z2U%re1kSxl)~>o*J>8hQW{(i8>=gdRH$8g0h1VU}FKxH)wzi_x1?LQ_;kg;vy>~BD zt|>o#zj^fWG{|i$fvM@P>O7ii(O>#XLVedp8Wr!y>(wPK{w<*iBkH~;tCizG&E#t= zZPbWMnU_aIAsHxR@s&-^irHhE_kgI{Em*tI`U64s?_9?)apJ}OGK0C56Q7^2^C!FO zj9-Rs-+l9wmQgIOurM4r`m{RqGf0tO(sdPc?6$X{Y!DMkzBBH)lF{C^_QR+DW5PAg z>#$qTh3cDke}(j%J(NY_bZ^Ho553-NdU9oRymcj87&=*Z5BwEUG6FlxZ~q)9&v~Zf*Ma-T=K(b0cx|)uTG^C>zbsD1JLE7` z=xixU>+WaDw*UImgFnAu{g2PHJo&c7^2(c(kU8r6BAnxVATF=0ue`4c|Z;Rry zd_~akKcf9G9{e334tKKi3CH}OovVqE05Dzf-?-q7p$WO(IoH0j{1cE71aiHvkq*F* zp@B){&&huC?_x;3<_P?6-f(urGF12WbH`6??V^Y}%0Gd!v0FG8Ag!?y+_wD4rQZ#2}|0KzI;gP?6mRJ8{QGPbt8Rqy6 zOcHhQ973xxxOfGo=Z#Wdqm%2|l}qJ085J=z0hUsL48S%5 zy3y@s*B~)aLCSIOrT8q@4?iFE=xa+C(|liLTc7+L{f&!xgWu>i_~)m6)}b=hkFGuL zuloz>6geQd?8&y$nxZ_IXe~r0s-rB8P==-$tqmqmO2#F0crRISa;+GEmNd>--6x>v zgo)w*d{_jsjrVRA!aVciQ+W~ZKq!DerDkkI6_5i*Ruamy?U>mxVyB1@@cIwFv9;D2 z$xX_J2`yA{`YDW(INtDS`HO+gNKdLH%Ue0=#DoFjZ`~wqw9d(yAu)Rq9X;>BPorNG z+fvbz78wiVV1-r_lttmtsE7HlwN`OF^5i#um9AcfLLE2c@+qfN8k&nDcIEqhI(U*C9t~DU^Fa6Zsa+eD)p3{6V^^8uNQUD%~PGQCGBQb8#CO z3H4Y%qZI<|`ZQzxt#)RWV+?0UxYtB(uAwOnEWi8Mg}ci?c0x||{n^n7yqW2eRUpL= z@Y$-4Wajv;O^7CIY-sYmy5)`NLMT-G5iF0{*#w^*xT0a%gv=(vd?Pza>{ybm@mQjv z4mg;zl&zE#9&9BwV3(x0!DHt4&!*t2CFqa~d0A4su}4qe2ax_E6~C~V6B%7r3e``N zLncnRnH3Fq8F>x|!_p%C-~9L(qnbYC)Qn>5>5))ENjL=FB*cp2+kc?ODktw5@;lZ& zuwY?01V4~t+<-ian9Fc`L&MfTHE~FnHkLcZj+%y39Qj5|HaYPOCvC!790`$_W^<#@ z$Fn!`nPEStOE&rVdA^HuOBd(n)|o|PX{gnNr9Uyc*S5>v#42QfdI0%fcmN+Q`euvsLr!6?5ADVQHuJ_4qj>Cp95 zi-CKx{4tZ_xHTT~|0d)pfciT6pmtvxoZ{XqM-A%pVekXAqEncMi>t@(vUxT#K2Ceu z$W26L_w22*MCyU_VjAe;?>53r`W`4jZX{l4e^st{n*$w!_-c4Tw3R&jX-tS9>@2@x z-?l5gea(sbf2ev3uc+Us?RP+0q+3851_UYTlAZwu7={k%k{VJ{q&tRgX@>4b>F!dx zJ0%qn&-|X}ob#^tADHi2bKmRUdtdvrubp0&G3Le+HppzqES|Vw&seBjkCv*>j?Oze zF+ZL6LdG-H8fq6AqHD@P1(MuE(+`L1L5aW8tgCp^I~FsXgBS*^>ax7%(}!XX4*))c ziR0sR3HLqkF2?N=V)|mnim64SG?ml1;vI?QW7-pgamo_t8AW6CXCuZkI6&7i4SF+* z+Xhu1EikF0wH?flzCJswsG2z~CAN3Wu(T+Ki12H#bNONL1Wim_$SmYz^o2mhBCa6b%(o@2X|u4dqtQrolF-SR zi_g{rqK&!OJJA}-5cSBn_!x0aBdsaIJ%I&eqv&qLOxuIS2NIaq%L33kh_FI~K$Tc7 z1={^!IVfFlu^>%w7MqL6TDHN5I2*Ymu9X+ZE^3b-b*u0l?HMlp;lxCP@|=+}SW{C} zG-jq=hcw-SEF}>Tt|c(Udk)c{Y*>CC8W>a7O+)e$VHhkKVi0WYM?<$2j{VE}CehlF&)vAL zX5A!&swW-p3#8`jM(&2w?v}{Vohs_y7_LTYEs~7T^U-8@u9OZ*0e`&I-oRMoem7 z5%dR$Of2liYSUX_`7hXrL`<>SB8&K2$U1lX*B??-BF2wIDcmD`Llaf?$o zxmsvzB!)6fWE!epZovG?6PIG@ImhISJQIRY$%5pmkm@j5q15{)D zwx7$ngyCQjp&UNm6}h3K`z0}g&t57}jig2>V%VE*tuuh||X# z`}YCjCx!n2>zs*?`_2c?|F6pe+Y1=5{q!+!;??_s-01lPQzH3IS*q-a*Gvmgq>ggz z>x?MNx25L8H!;f%+|HypHc)q_Pu3fsd`qAHi#E*(7gwSm4pA@;O6qt1@ISOBst51; zAJ$zRe>YPJ+8nZL9ZgwhSYULh2}U<#LHBZz*lca2Kq?fA4w~(mYZKFP!s|U#*tcU& z4K;m|^z1$QciC;F>@R4Fh4aVv)Clk_qD0~PQioq-yX1;hWF0D_9o$+tZ~2Psip+p* z_Mm=ynZ7f0gxOoy?PV*iZXt-clK5KZN!-soI=M5Ds#R8Ei9){z>C~lg-xQCfm}Z@n zF`kDgG^+4p#j%?>g$ggbFD>D?X zo(6wy9BcrsbR8(Mg-3PVfl|4#M z@@^nh%!zIj%g60a%8O!E?X1jX8;iWt_=K-LV6EUgpyOX4#S!j0tKgfjFUs;nHBksd zOxwzG@S5QpkTK>HeA!7P<5u`}Kqb-JTFD5WG$RY?rE08i@&tvYjEqOV9zI|-8jPYH zcBO^2Di1Q%NAiUVk(hIv&%H|a=dQpQP)KBTogqNaC9u!}q#kSFLIdfqaT^0;tY;fS zVrN97Kp|gXq9KApB|TVdeX3D)G5wj$U&W;UZkPQ7OjIG zB&pmvM?FR-xuqq_gv9IU4(R)4=rYd(jKPivaqP&nv^Jn#zVRs59{bu@gnE z<#0`;F`$mNmPdmI@dYI?-fg!FRfDo$88<@Hao0%wZPt#=L2i2?d=Sd$DJ&uhZ;Col zHAWQ!3m?Fejf2ZJT2HS;JESd`DQbD221Bwib25;=WVN8E9uvVs6A3g-=u5hqu<<=B zq%S9;Zm%^0mxnH~z7Az=`gQVj-LeyT4&m79h z?kgAJb47$Wx^5d4+Em)c^tu;fC*s&ve4^&d5|Xz#AmYVA1Hb{#!TEukqJh{NF%d-} zYAGpn!?DbyLL5MRCo%$CxG;x2ACpe1W(VeG36~wKSeYvqcGT2 zR6R_6&VPOWOJYd879k6i)?WOAE7_ZY zc4tzwxGg7fJ*4$WqvtBp=}%a~XWnDQXBE#@3vsd&cfpAX0+C$MIHr93LAT7XT~#$M zY~mJ0o7uz?7TAc$uA*ybteK{y@(+e>0U^jH5y2)Azy-4h|6LH@xOfB)!>Z(FB$WnW zC3+d3f$n70&}kJ^~Jly7J?tr#cDna&Jry{NPk&aL0EwNCEj0# zMYs8rU)vUsSCPNMX60J;pWbPs0xSDK&(l*((4m)7O2HIaqS?zcY0?W8#~)k}yA;;_d5DAexE>20~mW;l0qE#7(dhq0OoNt?CPgrbgg_RgP(P#mC#2-Atj7 z8YDJc3Z7W20boG;?XAnE9TMMbo@~fC!n6>@PsHC@`43Paq@kikDayN+2u1@EUF9Q* z=K~{RUr=1BHuj>sK$wyfUl4MdHR7kl4-xW{S1Sq($e&Wv*HLev*MDWwF>&`A8hGwY z10r7F@E+M`2ihBjQ7kEaM9j5@n&QYYa~p3mM#^4(6p5;%d?kN8Ew#dH95oCsC94s@ zx?E)+WVtfa^OD3lTrzB=i22ludf!@qOBP;`oIlX}J3Hq3&GlxdSfb^bNoPuZooJYL zRj#*xXXsjkGVgIiWo{5~NKxXm2sXa%5KWgJ$9}R_8J&;3Y^{+Snk`mjG0A^qU&f=iK!-UBI(ez8tP6y=uZ(5I{y$_l3NMMHN+rf)f zyzHM#!_OY+%5Gc;xL%+noeCjTLd~q(W~edjSoHb0F#Xiy6^1*f(Gu}!=L|N%**^d| zgS<@||AR>4k6g6BbC?kJW_1=>$mA(4`L_uAbUXF3M%VaHLFHdGHN>>Gc=~eGZ8Sl z$L(>daVtB$7O9u@{8K1&?npN*&qeM+8u6M}wiX-5>pw>*N|p>GW|7$XHKQRuI&ShB z+nCvoy26x1?sikj!2pqULB#xSisHzNQK8|H?tTY^BGUu})qFYj@*yy@iP}~hcDwBt z3jJuyd7q0R-dG(p-e>O-Zk_r?`PsChu6^IGDuD|KM6@hF?%#zAeK%-mi9E=c2&FoPCHQobmC)QbYXcSd`@5 zMNNth^NDTPUfQv{c1z{DWGPeJx0ZU^xW3OM!QrQ?q4$9;G~{q?c0j=DP| z=J)>d=X{*{oE|bb_QLoF1;%vsdec7qgr7nt@=zX8t-t9?sv;4m53~(lDU;--!})EB zLZ$S*p9K9YKmtXnC6`Pp^1`z(iK4BAC`wb+bIsgVy2N%5{&C8?5q>KL_&`u+%Oe}fG)_J>s z^=b>>?>{c{e{#Z-@Vl7`&;?zyaBJ^(%urXch4ts8aKt6OU*G8K{z$to{s?m#+gW~f zIA1+sJEr;Cz!v+r9b&@>62qQhQ4~Xu4pL=Ybtf_<^7~IO=|_Y2gWC^)vJtoaZz*nA z=c5*)wI%0^5KQettlE4DOL#ro%)k=IzVoj%O7)$83RXZU!l8p9GYPT> z;?g5Da>Ha{%um|v|D|0 ze|8#GS){r)@50Iu>U;%C%%(E8QFTl~Ze$73)HJI%L9#DDCKGPHNhBC1-zj8&-t1Bo z6!yf6FBmn&W97G|@O$n~+tLT8DB_Hnz&s4CpW#)C$$+AL|5Bc|l6MUF7( zS25C%vDQ1CqIfu}SD`4O5&F7v56tpsi-sjN2#(kHoJbEX;`OuHYslDvc`acwbS<9% zh#;5d-6mX8z9(Q@Ly7lc1t3wDJS`FfkS&FJ>EFrtXvGH61Cqz7s+$(H|2H_MdMSVsG31`O0$r&ZBKmEJe%97Y9S-OM6ZGdvuCx ziHpx5QNsY2zli};tEHcxzu|@StA{+gT*Zr~l16mjV36MjqVZfiwxtnovro&)EG+I+ zK2Ug#=b5Z8|?@8J8eeYKE^*N~+u-w*oBIZ$~dKGj*N=7WEH6>sucn4g5^^ta;vTuGu}?t z|M=rl45g-d`|FR-f)Wbh};Q2=p>+_-*VGa7P2yOE|@xmK}^gNS5_}H>lV?! zfEk3EB;jof#NN|;LmFXc6n2WTnvkbe*kk7HtLKEc;H!ldDHvMOZL5*CMquIhqYN`i z>QTCKakuYJvP?7{qIW&v9}r81{4fw2OP)(u!KB>;V=9u=jq@MC4TwEHc+CDX=Z0TQ zyLJu_&)rGmHcCeZ*=BFp#uk{(0uYxea!f?!+pRC1^sWOz+nfi2E&LG^_^$k>BsPUz z#IM`tCWw;qmbAW!x=JzI`_6ZK4fL+!yp9{KEv{104a?e|V;2Q*+TZ_YWX3|MBJj5Ii4qu`-Wi zZ|QtGr*#Sv{!pR1?ms5Hm5KxZ>#01=9dOzDRZG9Jno#*{+}IW76fw^z+W!0pCwgd{`wLuyXFr`+>6wo0Q5XFf51U_c#;s zWzX1Je$iMuzxGZ)JM*G5JsPa=SvZ0{`1dJPG3Aa=a5o83%m%ZBq8F1$jeneS58_M8 zDy9tUFXv4fUAVvh8_SG|S1$3|nQOG|)7wfCK|HH?wela=Txs&VkNv4&!}o-FM3wAI z5C|TM$xgqr8;t&P0Bn+a`H0VypE%NWKzqEJL$N$xQJo|w`+~;+t~U0|$yvn@z3ACt z3OV~)jMD6uJ!mF6w~DS7nheY0W&KK;YoS6XEbqlOn(X zmQ4r<^3NX}l9Uo@%$z=Oq~PjgrB8BUR8+eZ0Vj5Ht)lqEW^UpBi4lY0-t}~?a!qzo z-egs`E-Y_89i}Y>J(;kS5P*0eksT3WTVC*4Y~k|DuiqsG82DQpLpl?v4U9n4c<(-g z1Z}rIS;ed9zm798h`hm~u*8d5uO66>rPp~UZQ;o9&~I%$Q%H4Tno~KV%<`+Ikogx- zlo#RPRLNX&O@>@8fRFPo$p#!5j~nO2q$sZcY-UT|+>;*?*AdTW8JUngP2Aw4d6}<2 z&KPJh2_SSb3%seE-6I3bXh`;a7AIL4)3@(st^qge5wl)G{QI{MotYtFBQ_W(`*EV&?tzUcZinqDC|k4 zE|XAHatz)#hYJ}kr{dZHN4g{1N?;LM9=~z9dqzT12saCpaM^AIJWLz;ey?oV8xbro65Fhmrz{W*}wN_DC#^g{Mh-nD2kyutmJy+INv z+24)dc5f$wC8(Z3ZE#2BMwX_J=}^(H?k5M5#LNm7`}BoLO-TbisWI$m>CsK|y2DCe z>p%6f}hc6hP9r5f5osF^H&6*Q7psPQtzUOR0T`H2pP(=Zsr z3Y({jA@PyOMczlmh%}4<#_8nR_3P=77JpO=+Q>FJ4~{FFWvjN2{c%;oPLtmm80kL# zH3jpJp~&RzZW;yFwv32!oNn0ALP2eVrp9W`;Mlhjtozmh_Y(t7it4a6*q?xcTasJk zY80}*x!6mH+Ry7_MCOB3V#;R$8Cv>rHj~qF+6mI(KY?7H+yc{X6 z<)c`k@{6BqmeX#4|9i;(-_xYQS@0Fw2qvu!Uns3481}(zw%u#=9=Gt z+JBic=5!4@SfxG=113$VAcyRnfeIikisu>;sPbAsVjai%9z5CjI?;=lwwN4i{SUI;~OyG?<-!+ z+bF9%vvrV^{r-U;GWX74t;VQDp@zFrxgdhs!Lsk$m-UrQcq3izg5I1ebw(CVb2!@eKPFJc8dXKAxP~bCQF#s$!0O6U*@W~Nr z>Y(1*E6ZM~C4H&LaMRT$3aiDCz1J1=MGCCLq(&I^vpH_d8Zva(FAO6{vvX9ldR*gy zO@xB!G-`yd)5+1J1tE@{jyC7Xai3;L6Uk@QGq7CSB&8Ehdz=4``X@d`*mHz?`$Bec zzOeY=Zce+IEi1n3M|0WR;NZ(Hbo;0dwBz}uaX~wwcM(#p;%FL?1RFw)kOpE}qhUt8 z2CU$ip{hjF5CE8Lt5;FIokT6Nv5Acj1n)`lWfFw_&JgPiQ5$AWcy|>`Mogd{Y0`E_ zG!*g`M}nB9By|UWIbk5c$ylk|AEbnU5FUhoNH7jC~=2t5WD8i!Q=zJ+SUMv+-$ich*R%`4@RdSU$4a2%7nr!Bwue*jHM znoWbso7C$%v1gSnx>?_FYt=~V_rL}UL`JJoR ztQuCjH2e=BWUExuz}7{9d;3?f@@jSV8*&b9Etgnx%9MH0b3VBy-&wthEe~WkiEH^A zz+Xt!9%<`LW3*)PR|QlXdriv4K{q;2!sMgW-I>UonL?4NoMy2_Kqwd|P5lp0&-Ho_ zDY`?4r8c6O=hmC-h@Z4#i+XQR)PcNI9N+rz5p7PQrok`uIAX!=#aihwH<|%1CjrNn z&PrgIGJ*l!gE)h|RMsZvGGmB7c!+Un)9Tmwp08JdX^_j&Uk3dd)6XVxHpF;9esEXmDEp1#({I-(;CFR~9xf6TF zp=^^&t>g@$o_w|>SmQE)K>+~ubAoAW8ZhOK3K!K_#HzqAX6fBla$70eu96btBRD6U zUbZPdv;E)=p{l&Fw$*`TniIecy?q`C3DAg>_K7VlVF>uMRa1#4v8g*+b$+b7# zPEPNT11FoB<7Phfk2Svea$?zregMF?8$C-YYFmQ5fE-nHrg8Zw+P+$*jMU$Fw2l6vPqe2U&rvzl7aDI2!Zkp|`^1on%g=5JkNb^ci19W05LLQ;3AE~VeCWt?MV?Kw+f zCl??mh-j!;@e3h-x(s@3*Onf^w}%{dfrrSUKMTUw6GLN;Ye28xXvl~Ba@D!^pe_l@ zxH;Y)4qfKE1}V*BheM$0(fwcjB73lSti!_gF)mlKo&BFL%JFgqC)_ZV!0-#|A+#6y zCH|Y1sGcrOQETktZd7Ds%K>HAfzELk%#EffHH-&h5Ni=p0GiLNKXOhZ39Lo=C1>ZS zx2kX~fNgFS;m#}-oCkPUQBn1E+^bYEYC%-m|sP|daO2G^fiz(1t;{Pm)GAofdZMyQeAgM6ccV1M6S9P<0BRyF-G%poNu zF)PU@Hl!Q1cmWp})Xh%JfCCWxlG_U)DZ)5lW5JI~GfY$9&agWoIjtRqUMZ@(?fnCs z^EW1Ss=*2tib4YlSLUMD*HpSePw11YpJqezGHF6g5UdO<$?jGn*)E@Od@Y~oXN3JQ zb|7LGOcNV#966HGX0F+-cv0a zBS#Rl58~qa;T%hSktfga!3tS_Q(>N&2@5KVnE)n>weTPLn6c^`M4pDk{n{37R`W^E zB=9g^JG+u`hg7@!+szBMJK2xs<9ST+jM`AWP?b37^(^JIv7gUR!9P1Yb3CA)3JzIr zDm3Q2>NGij+5Qjk5pBx2 zUN|QL*>gEYK`wKfD6NNl6_iCNY{d0-J*HdbT*RKOfMO ztDPbKWgI|WevwaOXJ0wchL+r{X(| ztmy*eO;?dvg)K}mYg~X)FNHx^X#TYC5d1SC+z<|C#QXDwpD!_+ET^y$o^B95t37wD z(%WlS1(9kNY;W;6HWXrhA-rH-YKsjqu%g1doHsy-%#BEEJm#~qQNyyzf zexk$h#C)&t@Nvf8pEJoU}X4g9Hi1Inif2)(8QWmD~;Vc%E`bfq_B^!7SdgQBK9QfY15Mu`( zsS(2Z@b1e^v4=Lx5o~vkK08;>Dg^j@(Fma_LV~rlGD>ziV zl;su=3zv7w(ap_Fax%Aj(_Y_Lt-nxa21c1>Vt?MF511PGUKN!ihG+*}?PGDxd`(+^ z$nySP%wKf6LYEP>*r-$J{-Mi$qS-1l zG0oWAT1P{yVXJnOtf4gtA*+G#8yOT&KMvy*yiBao-f(qp^jvc*I$p+{Yfe;#9_jXv zz0skw=!cEpDq6Kzqt4?pQx6B?icK$XnV{--3hz)NSv}-l136t-rlUrN&l6)QD04m= zCKr#5b{d#9PIT~2yz7PNp;@V@bPW$tVY}eo%`F5E>_{?y=~uiz2Cw<&RV3j&2&f$m z7Mrf98h=AGUj1X%R>XbOU!@N0B_Zf*Bu&2C#@AXVu3%SL+WPX+P(#qBl#YiV{kRCM zIsOk2dFf1J?_t6=(ebm%rupXnHeM)>-PZigjQ9!?XHP%cbGXf?IHAy>eq!>6n}_?a+S9M^?;P-_dI5F%ert$NeK!C;j=3u8~!bV4oY%>~}bg zxkB-dIi#0tr+k%Pw1*Lh5tEA#k?Jy7BcKeQx}@Ouk&3!Gt8zbXVj6Naj_P|IPAb66 z$NwsAq}#2}Lq;TflQ>B98AE~vu6f-rol-+chd0(v`k5q~_P2R|cJ=cmPn(1_%{Kk! zW%#?66{v8rOb`wp7*WF{5IoFxF-4q*bu#O@)~dLFd{KN%9&0;loT~78Zg*FAOWTU* zBrg7iC5D7>Sz?pPXx(n@$7PF&r7YQE`?wSv&znDFd^2C8^u=<_S5uxBgskbfLW(!* zkN4@F?IkD{T5$BR5$apvjXynVZ<%SJ^&cv-NJKfc635Wv_ZYN8Ce(ze$;&~m9CDN78*@d2VwP%Sf^dYvYq(yDPV*196-&t)w*GI>W zTbx&G8>kuv9&77WG!|7)5WR}`#nvK)N)IzjhM(j#9;T+oj1z!a9Dy<9wcg5q!@=OMEV2F4g!V zZ*D&la*}dfZosA*$^^nqM?zSvY#l1lUIY)`ng*%^FiEo=r96enX;~%CjJt^FjCE%T zGONUb8#4=`mGi8!&}%1c8(3qu&GPa-+Vv3KAEn9Oud@}jmovAhQQh&sNgB5*1wq&BjF@rB^B&d|tm7 zz~sR)GO6NTa8=(nY99a9$LnTC`bjJ4^4+XT#srOBG2sWDs;QOK$39ng&yDm9qYF+I z5|FPwETI}!Ko*$bd%zy3>g#b*$&Cpu=vdpI{FcFEdcdUDR_X}EDOGD@2?yi9h;vgns}`y z%Ac|SCoKaSm7J69wCZ_<1+Bz$m>(W0zMSdy8wIV2dLUw)?QEuACWU&(e-yp<^c0^1cCOEUKWA1C zWL`v*F-OCWz`&Ogm zEShK_{-EdAYe&Qw17Z7nz7V?&9nba=+Zb!lLIJUc9kF7`XR5M83*e}{22;#r z_d|{a`3t%KP4!04{!8`HYJ8#-9M^oiD3mzUO_QD+=Di$#zYRepd)9TaJ#*tpYA2z& zMLVw6-=B(yKORXdAeZ(<6^BQBj*BAK^itj0$goJ`pZuo&{+y~~Pb2vLCvS$j*u$;L zDMt}8x2LZP3zRQ*^&rdVEesqhxBB3a=b9WC57(3Y9G_Mko)VBaq*w8H|99kVQ%0c@ zA@97Q&DMZZ^ zD)^MmRWv{a%jXgsj;dkD{0*lQA}em(yc*M~-kACxcG$}XifB`k9YZ}1%Qr5!2b_~5 z74ZEwa%z!|f|??K=B~yo&&^7c+1HwKl}!4ns(p~wT7o^1g5~6pB2U_bZvX(KLyKIt zJ%&U4={8ODz<{Pfaieh6*bHxt&9SZIRfV>R<6=s}Hrf0nnPflFyLc_i%$LH2nGIlR z7(q^92Gx`StYzbSX5k@3!?IX0fQ9Pt$C(e4vBUVv+RMfgABTCeGSjEBDZfDziK?oL z#y^xOGa;gb`JaE!s=r+o1URii)}C&}C4SH<$(M|OjpUB0jIX;=lw>}&oNe>gU-ADm zOQ}<^6bWt)k$#)>GoSe9A3QKWGN6Vx#*IQ!J5tmn`}AA5Y2*BAoG|{LtmdZ3BRqG! zV-Pkr@>f%%KJ^;^(4^%Jbe=0Q(i#@Rl$zqK@l4uU`Harjnn%!lLz;0u*`OZi-VQ&C z>#*m5?^V56I35AxZUR=GG^-g|>(ea#Vpg+s_|_YbVqXNBh5vR0A%|lU`uV$Hy9~%bF=&d>^WjT=5s$a@x`j2jnyII z8XgM!-W+}Z)%4~cfI{knxt&(WxWlxh&^$F_(NL`v>ZPj9suBA6$FE@2?tn%9r+BHs z4-k+dC~K<5z_S1EZ-q;1T1V-!T_BD=;BU(0w!~W0Y$4b66=oJ!bC}0M4r6<~dMYVl z#fv5lghAUNoS9epEKV}@EV}F=Zvj(e-&4>=aboQ4d~y&gjAGW(dT{H7Udt35<&{czZ+25EZX#LESl?~Xj;EKkvbgIn zFJ8I)p2yD%t>GTlVQzR8p;rni*!$LeN+`73mTd7K;rM5$jD!ytZRP&kKfq9O{BNqX{W6lS}Y=kPU#!2DOi_N>?lzjARpFe$O_g8~NtMbMzCjQtz5h2}c zZ87yD>X+T8-KU5b!M9=rwvzc5Z&`KBn$x7h=C`TgO&o=u)BZ_aa?BzklUT|>JyU6_c{1G> ze_gT;aw&DLQ({Bf{VPYxbR4p#HXGcWdCU%u8o1SKX$_*1VL@Ij2vD~He1$$&g6~&0 zYre_LG|8#^R)NvQwsEnYITS6^c%#Ef>Uw*O?kYa;KJQ!Fgqrv#;P|% znaxJe8J34Kr0z&NfJb);A?Up7Akbe zRw?e%=-)Krs~`K)9xwDf$-SSXj*D&j8*%rWSMdDn(-Y;zZ)=;Uws>94^Z7qSRBW)K zd`?ZE3{ZxhkpZDhAIy>$lYXX5F!p6#K#Y7Qw##MNwI0zlI=?C3e=>ntRt1qv!;#$6S>}hI@12Hsh6u zI-0T;BS-@fhlb({F_w$ZFj;oEI_&9k~eICyb2N+w>>OgmUISoUQ&19nLAl zZ#BrtHQn=QLrK?09$~=c3Z8K7vw$!BUlK(B0X#GQ0op~HU(=R}`>7vB1%sRgzII7= zF*HiP(EpbCjnrZTU3Ka1Zc_a_gh;*waLdgIp6kBxtTeHP1H$)ZXE z4DUbcxQ9*a@2w5LuXG%liZ~-*ZEXMk2jIwih>WjadOA|CB}hDC{zFl7aq#w4Mia_T zOY$kt{GnC^KDEsCXX$a~j#lQz7nx(!>h7z;?Nim<22-1WG;nNXSq!L$S>?Qc#*EFO z&UYy;{#>2h#HFG@_xBfu6-v3k`ZrSsrA+QxPAU9I(f%U>cf!s(*1i=(PyP&;j(=Qnh-qgs%9uXu)(GN&8B zgVOvCQiU`$+0NN%!sDWq&byjUc;T(ix%qxe1?(ai0&WgB%;sDWB9ocx|nl{JwS$B^s#rk~~lGALW>JMu5g#L^C)OWj= z%ihu(9Ge78+U$*Rc#6om&L!mDlGUrV;7}}gO{%9Y!%?Pe9-%z-7zL0%l#UW!ah)(A zteA+?gwGFEJw;VxagyL7b&hNkWaoCu)%&$Nlw1Of(2`;-a|LqY!JT&l!juS}zt%ZS zyBfFGWsS!<(BKcygGYl{;US+dRJ!;n#b}Kj@moY>sCzvHe~GftDx}uVTs?EnKqSS5 zjUo+`*DKo{@|6v5ONx#K{{e!zK_AJntd}jDn@yfA141kC=#d)3o4b*K zo-R!$pN~g>{e^_xjJ^B^7@$av*%ad4Dc# zQYrKlY*C_bJx;SMcM`R^Y9`R);th34r5^Q4`^bq+B@qO8IaD0J98Vf?xJ9`A===V8 zz%AO8CxUEkO%~(R8MV_tfWN9Q`KK11LFTHbvT4k}VpFU0-y|yiCi`b=ywU}-Zs@2M z$DTj708#P(-2Cr)sRfC!^a4H?O};Y*w=!E~OwrEYzdx?--rQNO>_&|*#;h)knPe>t zsWm?qcNp430&v)NBB?z&hU3~ zGkg2I8tf4e^)tPAe6iTj?24D}g-U)wt<)|OBVGfQ?-iubWp7DBKN&_0`z*C{3h8Pt z76&BUf7UA1EH0taKUL1i9MwKf!7qyEEnucgfxLA|DwTZsNnW7N{o$$9-ZO8`$zK0& zv@~~TBLp@U%;;FLo`T8ps86djzF5dfZY*ja=1s|@)I){xOOwu&R`scUwT45m{*~}4 z9+&hVpac0y(p*Y0{_oP=*kSh`9YrfulZW|y6~vhYDsa}tTZ9fa+y&~b72!5fM5oJuv=k#j}cy*4{v*|k|Ey-~f zNsSDrw}v;;_kbO#R>iGYY^b{@vE%8Q6g9w=biaw_lJ}dvLJtt5_AFd@&(4 z-+=#3r0SZKwPgi9v^v7V;=n)xnw&wnT7=ukYX6MuWKQM$8+BBH#;jTi?9w<4%K0$2 zk!tQ#$a$jHA*ddL$XF;lU&ejHy^849(ep1l9+Q6zoC7}Oq}9W4stkcr`RWIMVLrJX zg_mP{_vkH!9n~qt?LW0kj7Y1%ktMGRg@r))86o4$wk$hnCNzQUQ?}Q!LOG(_yydXP zRR6_iRv#$k(#Q|WZpXqtzb|0|m;cVvdi#c$NANgKCqW|=lU*XX=KzD&@^Z_wOAKZK zDKcI>>;Ay~>Bvb*zLFDDZw-H9(d7MSQKrk4%akMp(iF?{3lVuoYtJYMS1JzMc8pp_ zW27T^Z)N4r_=^;|2DYZTLN3qs#{FWp3U|Bdwn*U|91Q~_Uk;W_1aupQyBROCm7*QA ztcBEnOU>;k)opO7n7Z)bG3TgOy%9GuOI-$PoKs^nxZ9w37-E2hT^iIR#*LO zO}99KmWFpUJggj<(jLNxti(QcW!u@??Uy%z!P73#d}i(-!fG?ekX-&uzcf_!^DJfg zJDsAZF{AEYzZZ*TRMwZfTfF#^o7Zpu0a{*RFcU$OPvS(jlXs#u4HoNR#T?lP&^hRU z??>thBLKBopr4n3C@7Fnp`G(-X!*@Gaw@y+Ro?$r-(>M3MH<>{L+f+D3vVb|>Or`+ zIG=yOx$~3(diE?hrg?h#h@-2G=U%24Mv?8sr~!qgXi&^gqCI zPr|Lt`1F-3k=d5_2@{kH&`;N)UEi|*1B8~Aau3wM9d|D_+K>P3fTba!2lS{7S;?^R z@%B+8hlF3S?xk=% zk&=RwIVu|$(lkiV9YO6EKrEJu@8pU#Y<-^>)3g0h7PlZ}4eTjA=F9X;`(<@K5 z(`f;bosA=+nxrKS%hh0`6gqa(H^c=uvCI>ok>v}4nO7!IYd2L2S5+w4XQ;Fv zU-7?=dHZ^NyZ0r1B=wzAE1Y(LHmUj{^IiOAIq^-vS;wZV=4;?eHAupc2W8t{>D6h?e7@b5VT8D^zv~`z^$?wZ)6Yq4=lcQF$}AD zNz<=YQHFxcY8wWnb3dr;wKE37R{p>LBdK>9$qwuh{=r{z^3BO`%k{Xm;+Aux=X$fm z#|lzhc@G4keBdlUCw)F5$f1Eb&kS%~p|^vpZwf1G&gMsFn->Wd@@00(Du#p5qPMKM zr?hz5oDB9$1vvMhhq=|#Y!v!w$WEOSud|I7sSd4QA#QWU;MjSlLN`d1nssnn)Tk<~ z%BQmNw4p{>eY7=A&%dEbt%{uyB=u#mJ}TMBs!QM{qB?82!w(`L?6PpcC7{}}C3JIA zJitoLZp%K#lGjHJNd~eZ2ZzlJzE|~Gs(#~h`p!wFl&7pxo{*ts!a?1wfsHo^5y-aO z#!E&n=pf1d$TZX(|GjmeT`yUH$zMUVKkw_nM9yFwAfkB zxkkL~CdDkF)0oHlz8KZostQNB@mI_*!F<_9meuKzX$pJBUG^`gR>|$!$9EjDi21~h z;C2o`FfPV#E3kA(zg8)p3in$}e*da+UH>G8)7Woru)`}zyNOX}_Gm)r>p;9(6lnRV zDbP1tc_G5qLpJ{)xO<74W~T5gu(118nArUryUfAt4DbJ^rn3%fs{h~k870j~X=&I1 z0jZIWW}`<)DlOrVkQSK;NDHI8MvMtamna}1-65e!3W9`$$@AOueXif1=bX64MeKPz=XK zsq=FTCQJNc_f2XIkwuyzox7nYm+AgA9a%-}Q=`kR}VXO@XB z5K$P1YC&+4Xl$wF4xa3jBluhX)$SX0hQDU^tk7k*VTrOZHy{4S@C;l5NrYccr-i54 zTRq|uVNpu%G53+owhOGlSJ|1`a%(17#E_PBA#3LQ;IV8cF zZOXRLiodI%{jAaIeA1$Pxcze|>NFjy<>m@lw!IPprSCpvpL;j`^1DUC1DdCEH>>Vn zhC2MTN%~7ecO}10K_&OQg)08}j=lNOn-B>KUwVF%maZP{9J$WrQy{qyyP}2Ubb8Xwy@cc=GnoR*xRjyl2y2>Kj zwGh8btfLrHP#|PxiIY*Hc>}u-`fYns|H1sBk=;4|m-rDM|NQ$mm9Mo^dg0vS+?#4m z*(ZDp8JJLD;0|f=Cu`GFPXcK?Beg zU{;rp%#<}OBW}hH>TUzDvzLTzy7meoe(k5L85M%Zl%mhet!(yn6rx>*;Q(k!4+$>e z1Hp+ZHw}08cT~_zP10$fgxR*LeX)N4>Vlu|(tchoT@D9Hl}ztiBx;Yy{Hs0t3)=6= z$N(|%UG(rZkYnmGPNiOG4TLb1CoaZ2lWpP5Vdk4~b4h%ez1~_1(L1Wn>|u1<g=k9N3LO|DwianTjeXcc9+xzgVbU^o*Hz_u zbEmAKn+_l06Z;4{U&BW-rwWQbleyXVDdJrVom^B!^)En4CM#ULv60dH5wun&v;8l1 z^*cz9MSp){tz1C?Z7qTX%E~Y5CcKeUfSS)4TTNT4AOQ4;)0B*{=jP6<-%4-Fqpb=( z9U(UrjUo`Hr+m@tanEhBt2?hvS-jCk%9}pIM(xW9Y%x(RnWRyaYA-q!9N@x&A|Q^! zHn(~2nUSzu-D-f`VkPYxJs zexBP(FuBznLv(`GA(BaiL_oA!YqmEh`k_t?d{MHcP4sSsu|Y`uYAh66C*302 zYW|u;Z!%L z?qsj|zwA64@3zcxdu7qaof-(y=NT4;k?>pMF5T~nWthD+3XR_N`MfXq0RKgWjb;3H zr?*7wpX4^;{_x00>)^d|<${`Kl&`kqt}*>gp2N17g26+R`);4?3>*mc>HlEhJ;K)R|QZ<6V55V_u~i{S)exu;3~x^24hIZ#4!au zAT1(D$5tB!a@67D){iBb;m0(PN3+M0Jog}D$AG$K06H-!(Uw2O5#u}MCwW52psg&; zKE?U}+rY7k%O~NbH_!A68?`RDZfuDDCr2u!WyioVI{ZK%VRQ06(jIt-cJbhQnzqG~s#RHYFy@S_2ow*l+KFuu zQ+CFEQ1+}$S6tI{w%OlU&at`QuaV=HHHZ6w=1V)2at{~`1}!Tt->Q$Rdv$VeDA@ff zA{N^fqt|8)oMhzO!oJF~=hj4gs%3K_K?AKm46=_39by+svDY1Y1nF!j zJe+?h{pg@BV9~tQlEFY;ewfqohg|eQejvrR9zV8?M?*8Rd(`!X(b9q7#}6$v&K~YX z_$9$^hddwmLrR1p0d_lR_wV$6js57JAdh09+iZpN*i14@-kfdocX_Tnx90^x4pOb| zrppc}E~m<2u3$7W{<~;;bkgHD>Z~W$nXysLmR)l5xk&%49am1p1-YWekEa)Ct4Wsn zdEL8OQ06|rHflT6!b-fPAe9*M=WYRQKw7Mn>nIgsG5W~cw~cqP|C!}Sri*(fYl!BA z7=B9?baM-AwK3iXdo#!u6}d+0g^WX-3U*?t&OJJR9MFu48yK%d+*GG<@cV1J#~J7! znxKI{S6efF zSZ%fTR_<#Oa;}DURCUvuh7PY4G!yVFX+70f|LBH6T=kdfLY*2zJwNZ;rgBfR^V?p2 z4Vv@u6EGRnewF(}<<@}@%KQ**V>^d?S@3-oi+GXTyMDj9X#ubD@bJ?qyJf;QDZZ$% zMloQnWoG{oKC~)tq;FADdW1S7x}G%L3#9a(gUpvDEeecpYDh@PoNWtx;-+il;o_h& z_cB6mMrmBT#%OfxPS)6HVm5uNyPann8)jA?dQE(ZK4jwa1lc2(&3lY-)*e!uT}_eK zmz0p(MQMSh!h}0>nWg4UK%Ck2sLySrdC{YRA8lLP3q~)Y4QN*G@$=WC!gZru* z+-CXu16Mou=)7+3OmqF6$~_Bhe4gtn!II4_{(bhxDyGVrCtGa8yiehwdQ%6h+9$9; z<-95AlWvJ5C*qLWim_6i_vHlk;Ua6s@nz^be4*JokLAxJzG`p;}O_ z+$b}?x@kJ=oMrjjI>pdDonf1@Yy&#~$!cq|PQXZTuUh~wrS-%6N#7?9&CH|QKOKh- zA25;me*R*y94Lyy)jk}@YLo;84xj@Ya4nw5c{+>uVI}Nj<9aCS#o_3~#vLXT>Mii? zXe{McO)9?eYZaBl?Yymb=Ei4Qx((G?-F9YmN_+ec8G#F~A95o={s$EqlHTY>4RNb; zQT1n}999u(1m*8}?`H1}eBGd&IQw0Xuv@sQFfDH4t(@Hts7`YZl?Q*vn=NGNoY_>~0i*y0LkMv5XY6y|P&hC~O5sF2waAN(0n;A2IFD;!SOOfDmQ%tFPXIX* zd7shxq5Y(k+!s6SGzM-C>)=yPV(JwvLlgX}01oQ8R$9pM2qXK%p!`I53{~G|u$Z>$JRv-mp&F++(g+uX@>; zQ#{A>5pDkVe{G@*&wtgi5thWfP?*Zf&v!TE-PMwFzdibW^)lUhbgb^)q2<#Z29GmN z!}j6wQstn%#wvna%^I411}#K@*y=9Uu&Y+oYln6GAOf_CX?U6iV~E@WlgC$u-4S=y z&|UeW_P^3vUwYB}@~b)=j+PtQs@$;#Mo0Lf*k&VxJ`btu~wkVgBB4t6Y=D=#;OvlT`5cBQNSyQW@S_nU!BtP8sI0 zDa*PD{44OOG0dPTvW6?mjrmfdkD949y<;&u#U@K*kO?+2A-kqcEA+Uz=FW1avNCe| zUfX@us(h`&!O2pVb|UkKU#ajwbt&DG>-o@a5ny@voiAB?nV{w!%DQt39%C`L2pyxne_KJzD31- zB!ktWGR%qTsG_GeKg*y7dDm|`t;uJ$Ym$!^3;Km{7SxO)IIQ@ev}WyGb2UBeA}z~j zwG8XpBdpE7mwb*&JI-yPH!vhB1Uj1OsWT!5v;n&IT>%G7-w}bv(g`R$1YD`w1jV}LFdp1vFp4$vf zI(XA!{mli!>P|%zD&5da#)jAyCP|E^EuIDt9?AX#v=e9E$Z?CO1kc-RX+8c&=;$QmS`T_mR{ie0W(|EPw+@5JsIq z6C2BftzcN7U|DTSZ=xM-?AvkYwYBriO0{ZZGiJ)b`6GmYYaw5Jh8uXwOS;9U&qfjg zuURgN-#oyQjmqt{kV^4gyU%-S*D*Lp8;w=}ppE`1;XYL);6QQx1+P>S;nGMu*{`bm zugmID-auoGzvAKR0r%>2qX1e}VzyIX7L5c>G%QxHIi~QUK2eCQU0zPJXUi<;n2}{|!`hRH*JiMs*3sKZ>5(_i=JwCigu( ztJ|Yt2a##KVR${Y&4Xx8hoKNKy)Zn^ksA^~w!rDs@q9iBV0N7v{I2dq*%TB#XdK&^&{GK~m zpmc+@RqfOv@r&#vmDKWs$T*^H=gGNiN+0l41ON<4sI8K!SPyl55S-Pybm|FnowCh7 z=Q4@;?t3;qB2XjPArnrNxjamafI9r}^@Am6QiYn7q5#;v*ud`CT{gM@KF5tDy~)Fz ziL$Cp#rm{FE4hc}N27BsgGc%4IbL2ii%VjmF)^iD>xZN<^bCA2dU*#YjA8l^aboVN zl}3_Blfs_`f}Mnece>7N_xJ)!jj6deQ!x;Afp}7%^&+a?gAkck$vF#=FQ4Sw?97Uy z?QP3(i;^8OT9Iawd&uy^SgX8Q2a%`#!Kczvbm`MQUno#<%h)nq%52w%A z499O8K|Y`5A!;Q5;CFtl$u&S z{DPuX`lQ(KbaKuvWHhVtqh;bx1!gmb57}pmD%=XPeQcXN$`I)g)9j)jk6&kehK>@p z?T!JSiN0VL805Ty+eeh_s5AIut^1#vMK87U#z8tfPx7ps!`Ox=Ve(){c#bo8O8@y%g$|spMFK9btXd+QWUI zNu9E;#D`vYbI9Q)_cALIh+Osj5O{t;w1Ld(H!p-$=G$5tXtofI`<@AN(KGIBYfvXC z>B_XI;_J>SEXEZ6EE;!8218&QHG>tZmZvR4cQ-wBhE={i;G7T835zP7h2DAGAER6j z#YT-CYOM#*GGDy^$WqM6vBj%bOqRwi9jgL@XeOZ5kzbG9*QaB?>NZ(-I^l=~uS#d* zlA7qI#zcR_Cyscl0y5af+VBaFsSpUOv90YW>=2^LE65yoR$w__c)5RA`HeE_D~>Q~ zBtgeh`B-7I^-D1AX`w$(WM87?R*Ud1)2BF=+8#2p&#C}D{30H9h=kSrc`z95-yg#i8tRjlzNFEqER@_#Ro!t3{w zsVS(QX`T7JS^30rakWvmSVw1dZkokjs8dgyMPp&_s{(#K?jqS%HLCfcQL4Og1BJf} zL2Wmuq|OvvbZd>m=JE<+P|pMn(|<5OQo?_j6UIq;1b>bsH&3`o?|^Wn7m!95*6h;S z`?dHsmy5No9l3oDe{vao`FcuWSghDq{l51&oylSKq{S2alI&us5RiB%5*4z2uAKlCFN_wY% z{NaIvC%V#nx1qU{3F}J*8}1oi*Q2}k;@yxW6dn2X2V?0&yd=#mom0augfgoWEk(Bo zHPW|?v;fccdeXr*f~EY;hb6asNE;hk?zUV{uGcIpKFOvb1Q8bpSJ>x<&b!@TnJxE5 z6V{x4O6qym2L^>nvcjvId5i~mcQFCjP!ZKHmdh4pC1nS1S0W3a`PFykEw0=ut~~J6 z&8M;%(Tkl8kZnx4F1+MPLt|hhk-kxj)Q8^H?^VSkh1GIsdG1?oG;kG9p8WpEot!i@ zO(~hCPqtnOuw=`81lTg&!Pnv(iEZNspf}|hFw=J=R@J0gikwP#h!wT|oD#Nz931I* zQ<33yMt(Z7zIN+_ZnD`3&N@%Yw`z}?H02jl_i|+erKo1KCdj^-#K{~){|wW6@8ZY) z#aWb7ar9mA9mlcX=OuebR|3m_>i+=}xz)3qJ4W9fgRYv@)LvXBdY`_%lHmRC9`M&A zEd0h1+v0ZQA8R!!&&aO-#;Hg4+uh6j2RLy&>#mr%djoUrtIq81ADxz;_lP1{27_h$ z?ax1eC(RE34vhT&fmcTiJsjKRHnWLrvE!W2>`Kk?wGeOZu0WAFCX2EPZoKO$Lgt+n zd(HPrl;oQp)z=yCGhKuDscw9EG4m0Z@|2PsE=Nf+Xgbzu8m`APfqQ8P?}?KG9g>hV zmdcHKLWH4rszMeZxd=N{toH$4h?YTvvz!F$rh-Ud!!NnDWy*e1 zkpqM73tXYi88={_*l8n$9-+a#+*_4STmhW?j6D-h7-xVU05-tSg?Ah>ePJV`-n^dC zGvT>@1tmvp4x`UOSl2!=;pidej)!H#nGIIgNAxo2t>lvo%kE~3b%moa>ils7!%c==(Vcy^N&w}Z z4uw}M^YNYvlPT-EDxl%pQLKr!ECGw-`zC8zu5rMUc`f)L9zT8nVeFp4*+>{ zoEDMt{E-IO*O#`}*p%*(d<~aj$2d9D#Ps)oa0C4=<5A92Oty|ihNet*4@3YSV z#lSYSP9|f+*z}Am%52aVd-Ix_z*lUqtHTBrq~u5CCUCg#RD2y2UT!!Ni&ttN{cwl| z0~u)^vz3+agQI?e0z5r3)Ef+HpI6~yd^E5zfZlJZM+MYoHPfq^U{W1DVpL0G z@-brLRvZ7C%qJfDf+$+D?B*&aFq!-K`fw8~RjBD>J@{>ZVy2A9c>b5VF(14`(0zzw zi2nH%V=`02IHaP3eXK9T-i+v2c|4}V-um%EwxDlH!yRDjQ`I?)rZ6$8;uGSgyy=nV z+`)oY)n0vBA&InsAU1Pw_nn_kM>z_?G2}%3Dqs6E643KFGP-9R7%~@d56Vg}1g_MM z#yC?&t&0GZd<5w!23e`1lQK7^We*`8dzax@e$Vf*Bv`*3M=0=bNj-HP7y{FBU^G%3 zK^oEkaqfenh!5Bt8Fl}xG>Jp~++2UH!Z<*#8^eW2n6@W`RAt-B=q*|r=HF-9~u z;|K{Ui{LxpGcau5Z!IO)TO%=wGRi`oQ9aka6V0Gs;c8{aG_8SDZF}tNuGQczXr!f; z#?ir&<}-`X?_n$)e*n=ZV+X%9pXWk?Uk*0h^5_>SK-j(tplTA)g~s?%eag8hv*5-Z z;!6o0aypvZHHD3VoQUmsAm%>!C5fBOi9O#tO`X@>FsY}UAQ^?LlVzT1#4Wk;*C={s zngAMBakroHU@8*+ehRDKiR`E(c-`PSm<$l3B!dXPCQegw5J02lb#ED$^YUd7L!V%K zKR+4Di9}eT00$>YwBh|hAtZM#gOpYjOo53760|g*do+{un^F*KeC*Oa(!>mjrxT9A zI@ptE9puT5(TJlEBetXg)pfuffMYaxrvOvR!Am{q-W)QzsaV=Rpb2=I16L7OKFiNv zw$Zp`FErv;q@%6O3^@XI6bN0cs%Y=wXF1b~&870HTTg?9Jxwp!-E2z5L!@8{{AqU| zi1G4Qx=&jl#Q8at%?b=;Drd#ct`u9_B(oT3j|wrg#CKS6`DI{?`AeX0>WC2bO#y&6Y;*GxpC zd87!YADdt|S`$e2^4lHhBNrWJfvoZh%HL+p0hXX`)Qp}Jk(3F~FC!N^VacT@7o4jY zQR*gbwsj9`xRtLw9l{^!a_c&6m_)<-BU9cLyizDJ&qL zlw|+FIp7vKH4MzY6kVBshzF-pYR>ReO7oZSxW#lvWL&Z1o;xof6T7wgch^|*bZ|Cm z5_3(~L;#Iq`!e({+zbYeV?XYmqV%I&$)OM*0hk;CXHZNWAmI#=rq!vGfnO#PU)B_< zQY;8ZJdtc2L7D(t4izgvLh|h2kN8lG2I)tDBD@C#2Whsm&jnDz0PrAXDrF9w%~_kx z5$Ff>GYf*u!c)uCriBNIux#U65jU7A#9r=aer3^_u`Cc*N-+|Z2?#9PaXHFq=jbG< zmt=?)P3w>6wCJ{HEtE__i3)~?ei=omEcXf-`P4T0PaWKpiMh(ybscH@F$B1WugDL$ zexO`=UPt!pDHk-?{3$f!Ms12G3!JnM<^TSDfOv3ppFR|zwrLJ|!J^#`H;PD%n| z0|d&7HCdLH*pH(J;a5wV-f6F)wW1sDjjR-264z|ZAnN6e6yGEtu2Yp^qi9jL+*On$ zuoXH5hb%|KDIu6B8tjq?iJqIMvHFuGAsw+gL9-p(HZ64B)%3aaw{})%J0{43i@5d~ zq3R!@E0kei_v-~)TRm}nwOZ2D?}*E?2TUnO!2(c%NB|nOjdU`wOqm-sBb=RhYG?yu segHKBqltA|B`1w@6k`X##4?LJKt=*^5LvBp`N@1g;ySEu|MTJh0F9I_yz)i^@SXzs4t2*LG1-3Ang=1SRGd)UgPfxz%)ZQq!=C zywlJz53PS4oRmDS{TH=#_%J*DgIMeb+5by8;6X0#V=O%Uhf$daJ+W}G9^vCWevAWn zSipxheGpEHOZJja_PM&L^VhuUzvS^9ulNPzmJT0NFbhJ=SX_?YX|RsH&+ojS1w6rd zn4T1e6d(im>vP@F7lHMb9T$*Gj{QZA@c$>b{wa2XyFshNZD@-AT872=ly9qh>j9sn zRKW>ZVp<6ylzQ6R+o8!chK9omM3I5*%&Yq3W0UCb8V)bzIpul>KzL^O((g6Bde`-o zK^*dH(iXw0br-KDKDI4JEPHf$wT~W13bM3~vyLdBmTxaKlZfL&8A} zB^4vp;s}3KQx7LF5#Z8S*tRh@^W0iPIRc3ybokJP$BnMnKQxOtRjoe%*lNeS?koEd z^|64Y(-5g^3GAKukx@vAjhaQx_AyuHnOm~HD|vWtO^V9B66JB2q|o$jxD8qh#!v$9 zW`dm7a{rx!4cQ6Sw3s6EQdX%en#WrjkXPEtbww}V?&dYBPZtKGNVVA~i=By^RoTB4 ziD~%qzt=BjW=jX-kvdy9Wi(!;Q!k$>G z(p6!GO<#93@K1U&%>-1I2B^1`s3|D?_wiP?nO)yMmG~v*)|L$4lPv@k3lqtWkuhaf)=}GVXdIHw3btaL9=rwPmS%Q05JK}-ylT$3it2|bN&igd_@A%y1~2MQWi0>zOE zNUraDkQ8<8BC&C=^p0n+-=ob^5i6X3Ik$({<{NsPfifKI^TaUYw>kF!w1dbA-EY{4 znmgHCQ__aFqslU6Vo5USC(0FBG=W(Ri+OTs%l|Kz{ILdkPeoIkl3ic9+HmKIYuxv^ z<*;Wn?h_0N*_JR4x}1pb6d@b5&aN4q3hI#=>V_a5vAmf{gUnT3XaSSvPzIxe2I2#@ zg#~qg)IXb(kz#DBfqJZc4ugZnIrkE4S8$f-L{i&Ob&TXCnL^SU8#y9hqnMs?hX-!< z%%xlrH{jq?#^}<|j41Y{1^FcQ5XoZ-1pkLcXv{n(?bJEy^%g3db~o@nNM#oOVNc$H zIdvEdMei_^Zs_1OyDZnmTK)KDD}%zAU?VA{E{JT4$Cc`th&?;;b$m6<++D$eGzu~3 zGk5VX`fsLYhN0+>Tj4S8KAjDtm!>IHmB)K=$xbO7-F(9T6=k}|Ng^GnU2^2Wwse) zJcL!E1>eZu#Wa|_Q{(mXs z@>o5O@@yf(2c&;ZSx(#*&5b+j#S@c7B^!^(irITW@S*sROMQ@k+qL>u`^+4AgYcr& zS?YL(Wjcs=5Newq_)@~16Y`6Rp_J8Ac5LgsZPfT4a4WFM$#LqnAmkZF{PE9sCoAj5 zsIo_5gBt2D^GzO>VUZ9XCJ`d7n64LJmuEv74+=dw>i~dN`8OChXg(O zf7O#_KD`H+YqbNtr*{!h{vU2I$E0tHUSZ_Z>+7oN&5l#HBUwG1b=RLI`ziyOX&_0g zRg=dPCe{d2d~gz6$cvTP#UEjRd=J>RO+7J?xf^BM)x8v-SHJ=M8`ZCWN77)a+d#NC z+u>s0LiVZomo@T(I!H1`IynZ$T*9aYG?JT)nio&(GK!t{-o|V$@d#yjs#M&QnpT+eBuvP*XGVD1|}ydtGhiBJvkZm z!#*2^@G))78xs%7)Rvt(XB1oR$2KkPEAcoBbeYPEcDXyKgRa*`pAoMAX`wagj0V*2-K z>Ul+=Gt+gYwqv;r!3BnPYPREiWL75K=^oHC0&fVreNdwIR9@*G&~4Pa;bHAxLS1~l zHhAMYkvnk@XiW{v7Zwf-ionz>%C&oln>wH(EdqQSI4jlzU-@r9_sh&wx?JZ)A$f-c z20!7S5mF2~a)zv3?PupNkwx{y^*!{(kRUS6f||E;>3hlqv)QA!c;d`mx79lM^Sw$5 zq+U&-#nyBiANF-6qWhQ)TMDm$Dn3)A?h-lSjb4FP5~&ketZ<%)>^trf3FE-+NSr%o zro+mKTcV@l`Lp*__keFL!!En$ON5KvPeIOu{3xo!Rv$3+d0^)nZxU~_-aVj2PBWC< ze@a|2RR~(Iv|&e~lCzMSz%hd^g^w7LqJWx{QABU~exyo{T+z|h18pKHu)-Mhe=|Ho z16h^S0Pbrb0!ClLmCxwO=EP{$RLqWuD~!;N-;47bMzPVuz59hN_L!!eTxQi=Tzq?J zloQ~pe_CsCO(bV&9r;_DGxmz+j)&QXN4;xr%;V>frB}QSS?*jOkSl=+0y1?<5?ObD zi^+9JX32*B(Ndpo`EJN6>tA6vC6&|mTI*U&!=adD5htwt`yDIR1aisu7kORY%Y}o0 zLN@=VhD*nWdjRdVcyGYpfl)`xQ?J?H-NRAtl~lc^MmfHV!%JoJj?`WG{8@E`i10mt zTa;v;Vf2l3{XF`VCqgse;2;)tQ&xtQqYVXZ(`ePEo;uql+%&S^8vi+4>A)V(Wqga9^~x0YPmD~8^Ug+>VwX1;?PqLxDZCU6i>9xO)_Cp& z4^QMhgTvuxACoa+~J-UEV8j!Rk8Tob#D)9yWet!p18yiqtPnMf+&e@;{xDy8;i|U5ECTgt=mC16|l- zD%_I$4lw&y^;iC3snFJm_Ra>)tg)8L!+xn9u~7%UAgaHog4Z7}sUG{S^~Z2|W%vxh zKD3Dx5o+SSaP>-Rp9_lFEMC}H$494DV4zdB1xnUSK-4Al;3{EI3i#1^MktoLX`$ja zUB7N`!Kl+0f8gKi=LkG{Hx}3K4PuzZ>0H*Y%fuan@LoRDR!O;kY7T#cQ5ex8tp-_}ITj zo^dUgsO-J;RfK&IhR^jt2gH@iuM}*C&<2O2_Z5wT6yE}Haw%Gh@y8=VC|A{zGBXmF z&A9|f{{xgn@EY%nkr4LYzrD@R38D5_#Ec zn82C6lFv6<+wEapmFmT9 zC=XX~@ZvDWH%I=C>W*Z~!?FIuZ8l%<2AXH2-1IN#@p&V@#c|=m}N2Hg;@MDcvIkVQvS+7Nsqq! zX*ftakZ{#)psK)?2IXaNOViR6oW(|cBxZRJcopq=59r)kT(iCh^c&@z(5c>ae{Q2h zTxK!uj*qprfCLqp>#RIzboWLSKHl`^53Ow&yk>ci8v{}3Q7#XBKgCuyS!~>D3D(oS zu64Fn(cG$Mjb6_^ukx>Lh+WT=IdU!Y&ASKK-UD`TN#!2ADV1~6eA=SzV~)Efmj_RI z_E{+KfOTmzIy&cHfu&>02O40)l6AAZ1GHv|;derTxX8O z?D-6~XC2G@k}Ve+-DkcBXl)Pe1`>5oI!t)0BD;4f>Am}C%;~=C+bFRDlV#X4lDvt} z>Gn3NPT_6eQ#%o}ccF9}x9?ksCE!0jUkw^aS*8Ij&pnCRnqc~OLHR-{!_}fI>9(#1x9R*2H8{%J(p7HS}U3=pIEjMI$M)Y0-hyG-c;>(e_-K(9a~ z@gn8Biq*KEb+=7VdXI=S{$aiX?Y&3t0mLz96uO;mYqUA;M*_h~7MWfNyS#etbPTA> zLB77p?V5}syP!%35j&;1a>-2(ebnMhViw?wWP5Bs1a>aWQO>OZ9O!ONca7jV{xAWf z0jn=xCMdsIG1qnT#Y32hwyAYX;I?GS$J{mu415OKI={>Mz{#8jmI9&uTfbn_&brro zHzzYA&uc{uCyE(RYS5* zd%a)4aJlGmlS-Z`^qFzmXcV~=h#=XL7rw7>j175NyGqlKYViAA;CJ(t%@y`nnU#MC z{w&A*vTcp$!V;BSh+TUwJlYCkim549$l@4rHdsT3Yi8!>U%t}djgV4KEa#YDu_z5s z|Mr|hBfr61FpgGKD=RF0=`?KkKk1AvQFkgl~xOX*Ma;|1rPF9m_xA8noA}PhlI8+&mbaGIhWT2!a%#pr{Ei8;`p6V(J4`Nj>J4GWNy> zt>WEraJKIl4e$o~@ij^Vt1j!@s^JkC=QGO}nJ*L~^E zvh??uJ6dGLSI;fy3Bv5hq}q@HLnDw+H(-WlO*k2-#IXS({JyS^cJWwre8E`+6`yUL z2le^xSyVNbsYvkoMzlkOXzu}KV#q0;f!DA6uh;eY3xx-0z?g=(F=06Xj)m~oKYk)* zNj7pJ6ykOSfm@F@I1^@5$%Hhh{}XP`xK(h?V_MnJp3K6NiogZuXl&y>a{p0M;q8U; z`Q?qsjuB;|J}IPd;ruu@Hjv2%A(SO??K~{1Xr;T1K%_Jyj@E`6k1;qi(jwB6Jv}=b zH3q9>x+hG0m&ZBG)p%w;lgCIl{Y)6lTyiwU#u#~-Z>AGT~$C^trTB5(& zE``q1W6CXdX;ox`#r@s)OCa8I;yvKh<3+^M_TMamMonGoZ7CV`h6&)rM0qXMi7Ko@ z^&X(DO%qw79A2H3mE)k09k?xnaVtgrPw+#R8a9h) z_;cDvr#}`7G{vWT@q7Y|Gq_^Nz^PIBfb6(8twlSaN?+CP0V7yl7k4ZVUSYE+@Uryc zxMlRNUDc(%NBzMs{T)M^b=6mNa9)I%(6;NZfHkQK>&Bx)B$HlR(g8VSSi2d{(F@nM zS+*3wwQ4QX#Lhxn?v6G3UwfZ0)=;Kl(ZJ;m9n=5Y_55tVk)Lz$wDO$&8WC7w*xo0l zPi%te_wr#jx4vW_+VVis_!-;rB+M5ktjfP_)syJ)t}FACWlh@mpf6*0!a`kClp2n zOAd&8ypfx`Y+@*5Q@=dVAtu;Qf_^2r$fcI}7c%CT^iTQ&qs?XFRPL1#W10z9|y_h#o!xigrx{~5l>kduczHJ1HezhDyUVySFhbtZ7SXdqCK#Vhp_q&%b8t8EYE1E{yv)gVH5eQg!vCx` z3H9!EjO`e-4YIF6lJpJ;Wf|Rpoco2QwDlf3vx~{?#O8rgivoDc3d)@{OD*kU+-bAw zjsrpmVyUhj@YJ7d8_#xKauRKFyyE5pt-Ca$gvVb3JHhWF^ioz@Zm>O|@5i;)2%o8E z=tlZM*6;kM&$!*xlhDrn8uPEDB;NJ1T95kzCx>lOW#2?NUCi3h@p|}Qx*$0ztNPD>Ic1%OW7}L(r{|iUY_ab)mWx56<4E#kHEEy{bv{F*E!Gb*zQO?Fgmjqu21=cZjjYGvHEL+oK2 zJZ((Ddk(r3mR$c7@mq+$B!W@#n6$nT5jrHy_U}3d=ffmVon7qr2Hza=OOU%v!E)PY z)Xn0f=1(H7k5?D@DVhrg?HTt@uM2NTQnsWN|5i&vd+PRzYh~~z0}{uCB8_YpiyM5W z79v-u&@-q@>6;?=a}WO}TJL*6$vt3itSo0Dur?>GRNVJ}q6WHIqw}1##T}2uqoFBa zVW2k?lY#@!J>an-OJgxM}YOOwh9^Y(_Y(IT~tu&P4>UcZ+nXg7>-KSQ=i^V zrGxxYna3_Gs4k$rcC%3mR-H(1K5^SOoZ|MOGF+GzP9{Ej4xW3TKO4*$a7jLG+?Q1H z7>n5-0SMMV%RN}s7(C++e6t;!hP_+TqSEdxl40bKkeTJ06AMn|(I|FrJxMG8_LOZv zl0etCBlQ$?x(ZwbLYu#L4csjjbx=($|BK0*Y?r&Sx$sxFx(6Iid7pZ`H-3rCPebOB zJu8c{Pt#zCA!+@V6-Gl`9xKlg_V(^&rOIXEQz1 zji^FyZPHkO?AaJ=Gq~G8ana_{%q|}EgSrGC&NyF?@Z05iPQolvCIY(f?e>Ae6J1|^ z0bl1k+cHl@Ik~wRE8i^TPkMNHwkdAF zAYn~m2fs;euRd~I4qSPfXxpchvQL)2F~d00AO4*mC6xJA*GST9<^fdaMT^$_8Cq-*e$x^0t@&$ppYaWS-9-{*evmt*_SJ5IuR zqo)h|^Kx1ouv3U(?I6ALiKKdBWcogH!`6i(iL=il=h7TUUD}Ae`v_!uGyAe|Z*T)4 z0!}X3S7rD{fV`A;u#uBYc4Y4AC*UoiaFHbZ4KnF4zSKq^pqQd9Ti7!Cg;L3fg_zhK z>7yC9+>wv9w@pX9K@d92R0fvRB<#1)r{qBjw{o6r=1)o?MZ|~@!i%tH<^r39ejpg{@70AHCS|#;$qIQ6giR zTlcUFP1uW%P+6iVJsq!v4*>GLMi$r$w-e^c+JRoz#uJjSTkiE$C(+`ef;+bC26iBD zT;87`5;esl-##Bn8NvTFeNOTS8(&H`_n%R^d_~*fJ8i?fGafQUZr_Bj`e{s$#2VH> zr#e55C1QcMF$LAPoClt4`+gah8J7~v^p$Rn%Yo_i&-Y?kuBY&N99N!9D61xxq&m@U z#9Bt=zfzDQY{Tt&$Y1(P;}pXLd3n~{rO^3}ys(Kt;(GuIySB&)uOkI6kCdAfDs zuCUmT#L5FGy_|^u+r^;Vjx~WQhM(=DYy8iq+~0DZGU7>b(k@(CD4P*@`4Vo*(RR%G zB$z_8@FL*b-qBunTk^K*6EUG+%295_AS(O~?`tXqNVQ!F;-*tW8_u=WigJp;`$QF_-v68DL56>f^Zw;ztno z>!xOC^ex0LabnMIs*(m0Y{#VSU|%{ODqMKfn%AOu#!E3_`~sL-E`3Y%s$L|zC!)!T z{(w#imIwsPO6PIO-o?RF;uVUBJZa;*22bVf`g>hfcqO7s{xOC7bB!BQSGPRY$kwy> zEIBTQPYbYwh<|BvfIo`OVj(@D9ozQsq;Mz_cY->c^2|;a6N13HR`Ey3G~F|t2*aJ1 zy%p7!+Mu5VvEp=@dlG}#;uO_aNy#`T!A6u>DsFI7f^wl8Z95)|B7I2Ye9NDLG7Bx? zzFHkkJ|O$#P{iJk)o0FwcGEl6`iV|%!lBKUifV6Vp4X6jTt(Xm`3Q0SiL&YAl~FLM z$ut#SJ!(Wqd_3DxIdlA2wMlJdZrlI5yN!mV+R;z60ev{i_9OPo{M!KWyaKd#p0-1| zzV3C3BWI@*m{(%>&7WJR0k6POGfS_++_|Ku+nQ|QhbKQ4-T5~eKkYS2+enZa;SSeJ zlsaUo-U!u|4n#HNySFGyygk*B>3ub4wf@GLD&Bu}9)G zoO?)?3p+9;)AQ4x5mFcxVI;@C&7sYa zYU{M5@Apd_Vh{PHBC)vMnQEHCqy~Jsx7>7)l{#h?xH10j~lR;pUO|0OK}CS4*bkHH75Y;o89VJ;3K0i4x9!$RP$jlg8PRwstkV zwH>dNYL4|jn-}?5KL(wsFL!EcgPtLVubm8{q~1btvBdsF4J;O?j|W*?nAOd~MM`Sc z;Dr>hCq*42x$zSQY_3(?9@AQO=`Lk*ZYw=Ml@!1f%xwKB(nK+4v~*6@Bki`Km8{=d zOM9=A9($xFu0a{g*= zhub3-y@kS_{*CRAMnaQHl(_CqYbge$JU?%4k+AB&mP#&3j9~06Y;aRj^22%&&QaJu zaVu$L7eCERh}7+MK1Vq0UlC|V4ipP!uUuG98hUJG$hMk^=RkiQ85kEDJ6;Jj8_ql< zzMek)J|bABAV(5OWSKfk%9IV2O7?3qU#aVGn$)V@Ii_n%(~B(GwXuc1bs%m9buLyYo_6s1wfz8)4Hw;mnsZ^*7P;qrMrizvwe(nNf)Fvejj&U_wat4PxNh) z3Y#%#Rn;h(%4!s$0Jbl3pJ@~%zkHvdGOuh80*X8aGsx_woAwxY7;l%@{F9 zyPZ}3j8iMLj&sA+t+>t831yW)nCabh2qq&qW3!0v2TWBOQ=ZQ29llT6YOYZWZ=aF2 z&b`fy>gg+|9uUIQR_kO)lLp77POsC2ieD#GJ|Ta)Wm%X2A=6r?tS}?rKeHcknJLDs zjUw;9%?w26%qa15afR7hHAxYBe%ICJBkOACXF=LzpU6r__QL#qtHO z_sbbgbzjC_fLEo{sFQE%`3vt0gdcZ zvsv>f(niQLMg9&3*$)yooSBut0${4Tb!(Pd>7=IKv5nOiTgH0BO3E8H%0#~mYj)+1 zAPlJ%Ia~=WhdC=SZ?U8Ej?rkFNVY2#ejsG3a-If+C2fwB*>Zh_&SFGFzc!LCnjY?D z+*ldsGqjiLZ%0n-POXmyvuSJPyb=m^GB44Ku}hn#+)L>qdg@i}|GVO(CBt5RZ+L)y zjnz7}^8J^`b3GE#>#!2_V*F8GmwyQhrgSy~%1(V$zXz;uD2@4srCZk<1N_5mvm$!q zTAF%zurB`P1}R0zd4GLNz>@vx?W5y30%{1#Ts$KAgQ!RsS9s>0C3Ta0aZG_xnJq-* z9#8`r#wH%sb`?{0{auGPdv9ZN1+wYHjK=nw*Iv~R?j%4p3Y!q8N)s7&k5cBEzlT}J zBzG*E8wp6spSFuFz4CeyT&wR`t`D{8Wz z2cJmxL&*IhA|}{WBw2Hm3kbtebr3(DJNomAlJWm;Nh}GLtnquM{4eKyQKyK6 zArHg2E1zLpPB8)$uX% z6$kju)59SPIz=;_pF&RJ|AqUJWJ}*}UW;+2_USj-pGL~G`4KUZ=+Mkqf_Ao_b~Ak~ z>v3@`pp}TTYw&;mw3iB(%A30xW3_ypW|v%d!nFX_^h7V!Yd1EA){AHtPo6IeGr~f(rM@WN_-Lghin;ptS)y~?%TX4T@Y?5;!kvK^UsFV z!EAg}hk4@&-!$p8E;f=$y6y~ymGB0GmfDw2Q}fG}c`1zG2y%KIufjrHYyvI-bVEyP zZM6VF=f8OVejbnsCn*A5;oM9DXg<7h3_a)b+p3ICcTe)!ih-BU-H2*&INIQu|7 zeTo?*1I%;_&YFBC3w0m_YYnby=M$?H0~OY#1X-!sIs(SI>!F}VknlidL^k8@tOytB z@58c{8OAKDIO5KJVO{KZetEd|Emd@J^!7oJ#0e0y8=0e?5qJqqq0L71O}kQFqdWJ6 z>KCSNBYX}|wTjCqTZe?&*uXDjApRA`PoK%K-jzL+qexN09$$4p+9aZ?%vv9=$Z8Cd zR!U9F58 zh$j!BV`tt^6Bh|Az6-gxcuB|QJ}JGJh$epuYjbwBxkkT*V`Pi_4(#KXjn>b0S{IOg z@8>kP`@K6FS(*l#H1f~PUvj-9roIPsG?D=xH6~L)o7jA6l~n7aS@9lSIB%RR%S)tJ zI2b6XM)16X>*u7+xN<*rU!iKBFL(Yin2rEWbN|kq-jtQQa0ApcyZh0pj_T^^gQHl% zHOjBgUE4kXoFgs8INrJ1dGk&oSSL9YrL3WBT=+l`w);Vi5?IJ!)?h zW#hLzVH;sckCBZADLgEGDdN0FbE9&=yMi5~FBPtpBsz!w1D z*9%mfckcl13;Ie9?a(2fEk!#bV%Lp*b2+DE5d}vYevdm^Az#He<%`T?_KTz_L#q{g z7qp)-D=_VVhZG!fmF7&S+P{8BY-`-T%Z29+brzLde#)=PC4soR9@QeOTVYcHRd@;E zJ|cgr(TL82A61Cc%skt9o;@kSyziDuIbF3_8aViD>26Ghy+X%mV6(8vdJ}qjBP29` z#%C0Jfzo%!QRfS{ZpsoKf2tT}BIhSiA^h)JHX$A{s<%@;TTB=kwiht5@wF)}b(dD;&};ECfy{3jT5gHI-W!@=a%qDP?i1lA%7&iKfs1g)Oz ztXaW=?{f!aRdVOeaYn|11wMa;xB{P*YP-T9@L@4f ztJHkjDE6>d=bK{o{H;BSr2|HUxo{MQu+Cu5rb;?P=#HV3llmPLS+gQunENO?rA_&K z?lOd=2_6vbF2bfzHsV=+?tFXxeBp0I%pBFz*Ag|s)S>2ynYKXG1rT_tThw%b>C1M0 zDml&cyy<&@Y15^)&)i%CdtZZU*ZC;-s}w6tl*yCE+qQ|`oM$u7&(|G9xJ4~Ql-}zx zyM=|Ug)~YB$Z50}6khErc)BFk%6%905jx%)co^>zyOvZ(P~0T6x+RM-q+`o-G67{AO?wVz(mLNHjH#mbQNq)N+517lahzEfbfE|o|OD0@!ATi z)-If{;%M6z;ql|N9EJvR*cbBVWk_g=ebuuX&?A@bi-nEC*H zzqNJ93Q?4iB>fV;->9kYjVz-7MSzQjnr)fzLmkumhQO%s5xbcJ7~;taTVsD(`115> zSzXhUIR}KX%7+1e+jp5IvOB+Jr{kycm`BPqCN?>pBa{b;Tnzh*tVLd!MUa83DQAbAFNJ235>3 z9RLB=h#^I^g5!mkEE!&^o}Q>bWsaY5G=|=y^_m$bMvpNG_%|mU+W#Zl zO!~!Ri0WVcS+Z#qR}m^F;Q}}!)=<2-uWl&+Ib?y+9kLQU#{*qph-3&S$V9nxGAO}A z9mq5*z8dx~2DG-5aL&vcmNkP*bvY&rH%Cn~D0(c8K94&P@yojkW2jfHDRutrc0wZw zuSxj7Kh$KIHIy24)nDyV?Mn}FC|&%n@tR(-Q?Da3IiPOiUl72i3XbG})j+dW`^3;Q zhC4g}c*Fmu%@NO&IVERjNU|bz{`aNI@0e`8-gA-?@lcQ0MMLry z!|>s%?|b&E*1G0SSzj2IGjJ6Lhq9AvTI!-DX#VCmI-U%2s+X@Bsn|!@ITF&sqIVRu zJ_i@iFf4ON)}_M7p(%($g?NU(q=qC0Iq>s+m}Cc-KFrBuwz#1Uk%iws%odtiOW1Hm zKC8}eX!3deWu6C=y$60c8&;Cv3rkDaOW#ep3@wWLBRRr>{}Pn=7wbjPQ#AP`B^3pa zrFKD}xs3JbsZ?VzA(+q5J0bI{U7Lr=vZ+MU^vQ-?!zFIM`YpfWTFIvdcaTRPdOE&! zmZw*~(Q~~cuf}78Sph=1hV_7ua1{iK(zm4xZ7;4iftcr52#xO&`Y{9HkN9l%Odp2 zla9$>#J8A3zXD1)fqtmYrn=0$r<>zyK~wKUecYwA?$^2+?F#DI;Y_YrZV6%}`a(b45^WLYa^ zMpijS>?ME&f4J%31(RCpD!o!Hdf~OvECEF~5srxb}`JsZVG( zaW6~x@?A@!8sacc*XD_QjB1+FX8;8gkEJV@L&n}%1$tJ!UKtW2aKeRxi?{nHBMn~u zZ$R_*34%U6hu2y^8vd>You3D^SKxZ%p~;y9{TF zIECM*PhYzy{5E5OiG)vGn7?|<&5Dnq@>o{nGysb~ZEd+aFr3VhTd%Kig4Z%&s%N1N zoRCoMq9<*~xV=A>Y*BZ_ZUO?;=6pv)>S?YB#hNZ(q%Ezid)xcUWV#@;gJXHU@jOnE zWSEM9D#1qOMS?^)$KQ~#PGKg?Tc! z@V2%d=C6LKU=f{;F_TZy;-E*MzaEc@v%9#MQv#QmuteAs(T7TvSw*LP(uBE_QxnIs zi1jV?=g%47w-EK0bb)0z!etTQV;vEq{gNfW+S8L4Gj3YqhUn3>4e)-0A9LM~Hs8)% zQfF+6Vwz1%F*#Z2hlC$BaS5u*s3bTC69vZ$_ZJkFoashleaA~THL)D^L@@aiA^CkG z_h!aVtx~k^egVD?mzstK-`lM!&8ys$n49sL7v@Tyi9aI&&jaNqx(d;{+Z)Hr>X)`H z1`@Abqg$D=#+6OOzklLeshLk7rWM*!H*rVttPL$^Z7(Nz;sYJ9HbE-C3Q<1p;E9sv zp!e5*`#ZZ19B$1jH!X`avF*os$^m0V?J*N@`I${q`y*|sb&-fc zZo9ze`aeS&#K#;54;mGJ?y1NHgQ%4RPnO;Md zR5_)T%3t4>Kh`SlWvgg`Lx#^Y72ow4=2>ls82NqEGgWoN%h~0usL(^!OLd zz*P(EF*RA1pU2*KJW7E%dYx?G({7t@b53FXJX0QBpUuM;zDzx}FQUw+K-u#^p^Ggz zmE~u91u2T*K%0_0At>$qS{?g6R);l6c|$P|?{uYc4eWb|8P`|I*gn=o-s;spE4^3+}9nt^?pCZ!9*S#X)No$fZvO!EtYZ!C(GnWu62Y zyEGMN;>so_(k55WvJ-V+{L7waz?4W^F!S3Okr{S8nEDAM2E7Zw;sDEI40QG1nbDmf z5k1MJlgWBe{-Y+30z2|61r6b-SBu<*Y&{CKt;HU6yJDGfmB1Y%2HF`12W8K@&|-h} zV|h~e1Mp3ECh|heorM3j?*GzRpm_M_Pp8^!@{laS$<7Ya9uBwB@99lg)`aQ18(Ud% zOB(HH3&iC2Kv69xl)Zmwp$NuzBf?*V3Eow(s<2@F0pwL3&sHUemaic=?Ov-lmQmCc zYNbjPqUQ&+dN+iX2UdWcr{Rf=8l%#+>-F&WFtBuCqK-KQc>zVpcRJ`=c02h!AoLK4 zX`{<&-IwT^Kk>id!8rbL|4Hd0@cR_@@>3{4_LBDW@TOr9>iBzBy?r6Ngk&C&%Ogd+ ze4yFrXZfc3;};2YA|5)5nabxxE+9$R91z~ma$%>iy6-7e*;JM1-d5;&1|Pjx&?|C= z#B3saJA@vXBj`K)_-|%q$q1c<-4N!$Ws1bx(#gPA1zF<(^6l%-w-wr)*?K0Gd$@Pq zhq5WYEtF9weE(c4yiHzp^|@Ts(A^L1$$|cqBjnhw+~DCMxSP~0yx`ndD;26gv0*}| z^wmh;iN5p(0`x*2uWKFqQB+?buEo@B=Ss(87ZQxwzsjiwy{V{K^~|)HoE(RjSOx=D%|k`$jyOqbP(c*@98ahv{oBPaBWW@qG8UiYEgz7Kx_T zGf{61Z5&xA;86JZXtTmEayYX?`|J91%9^@@zo3l(B@qfOn?2TGnuML3*3@sAii!-? zRVYc1{o9$cUkRE!$fYDD`8J~jk)(>&zSXB$cI^5kh68I-$GUxk)8N`-=`(ytKW;019=plUl3;9Z@(( z|I{i>C}Mx;$sF(ZLDCjE;bnz*p)5dFPOP_ALG0MiQpU)VLJIu=(`t;LA5PIrlHsMz zemN`Ez`s%zmQE(T09-?lOj>vo9(fk?4yJI~y4}4G)@>;0zIc*qAgH5W30gCpuN?7d#GU^^%GMJK@{W*9-dJbSA4;)ctOw zCXhN;dfdS?C*9nKp=`>FmV_7>m9MX_KL@{9Tj|&B6uWysVn2q=kHH~d%XatIlyA)Z zZ97x_!6kI=fuEM!rm+)_40ilAtUKWc(rT~K(9YI9YLcd*U@Qlx9Nl~qmr;IuHW;9u zn%cznOLOOa!_+h;&|Bs-28K4x`Ov_|5?FW5NnrQqrtan*ASYkk=E8E`);IXh*H=~3 zfeh)#kk%kG>pl>=Gj>xq^*}j1Ny%>dog%v%J=Tzr$jdv-Qto}>V%iXV*~%ef^dKol zJvOI__ZMWxqI6=$InU#^^&{8Wl%oulmowcxK$BE$P_M<0*7&jQKm@CrgoLOMq8v!; z6P6@H&@$@9)Q$LkT}oBDATzp(43{R0y}V99>)`1#d5ikVqTRW;3;R@{MeTuooAxj;H$~e z*0bqSpTY^^siDyRApf8LV6XnFXY#coCjm)=rr#Ic1!TuQO?JI$8pDO7 zl{q3Z<<6LJ?gV{H4-+CUm6P!Sd1aS?4&PUhJk>|IaFEv33hAoNb7@Ne@nyy|KKZ-j zv@{snDz1n75VkMhy|0<*c#JfD*jXlC3U|i9>7#c2ZCe^!;rJDG@3b=g(c=VPM{B%i zt`oJ>ns%QtTb?Brfgao2RAv8zwXY0nv)}fmN`V3`MT(c=P@vG_R%o$MELcg9wm2a` zaHlVY7MI{&B)Ch0Q{3GN?gY2s^rrjF{c>i{+sdcpdL@Ht56ud< zoyfLrO{I_n3U5YBx81I$g_Z9A!E>R0zn#AiuF`+zO*9+xN!t^|N?F`hBWBlQPQL4YoowRrrx)X*nAadfd73E-KFN)#sW=&D~A zetK4J!#^Tj7xrDf!Dj71$yM4x@QZR09BpgnLpr2Uah`k?d9@`xohLn4`_HKsscyX| zYxsj5#lp?v52@q7&HmB5@JM-E)qq+mZsI;I=63%9N|~)5^H%H!4eP&D{%nWJ)ERhB z3wWJ95e{GKT+TDTlxw)H{bydkQIPf6q=8ua(CDR+#po=UaSdxIXpTI(a1|jIM6G9O zkre=q+*QsKIOOa;eDBG4^KW{3k|&2gR#;(j_Z-@|xWw!0(59H_g&ExuYt|}=3fA35 z)Wbihe4mS}QTN|`_^+1-UWCLZDG(tL9*uc+lB-*dUrR>e;8N@7-{-K;oK;=Ym9pJ& zh^s-dGnFptw!di2?!?UgbM$&gU~b_UEO@_|qjO({k6bZNEACOb3S>zE#7FB2

F>0;95()2f|l@YG4NVnPl6@i#x9)ZyIv)u(^hfWIa2fO`_|Ve-)vH!4npc$Mp!y zL}O>2gNcW-Hk%S7Bfjx_#$Aau+0!j=khf2U0LXoV@1=q5PVnf%2nie0gl;>}Pu{dj z(g7VBml*tF-L>ZzSMUNt*ZFB&ym}T+O}1n>9vk|gt~RpiR7vhVkJ;8i3ohQb8eIQO z_qUEcU{(4}w9QB@ob>(I4X5U+m-fAJTgHte!?rX6g=-~f&UeYj70_8rLtgB{5AZPi zX*zg}tf@ARCVbtjXzOncsiU}&iK@QdOC>w0+P0Jbs9S&uR|=5+vqGietniXiA3y4N z-!_EcdNKc!A^O;ylu^eaE-jSr-d~vTUrBZED2FUO`d3=k5JQEK$fl7s<4Vxk6oV#HbH{@U_Y{6Y~-@O~Ao;$dzeMV0x5l3ZOy zT!2#Vn#%*Ld8tcn3<2X@eZ{F3pH78QTK5(8HN8!VfB|HuVB*Tx#ao^kD(%6GIh3C3 z#N9^EpJMiA=an%gbUh{=_HbgB=DA1au zGcX9CEvz#wbCtcaVsa}-{r*IY)KLF#^-062^w3=~pt=`j50TjHj8)uija>2Y58e!k zmcXQy_a{E-p-9UEeG>rmCW=P#spkmabjok9m9t0PjB_1IxQr^AZ6uUt-!nfTuTz4S zRPeoffyZJj%$a3i!WPP+GBvpAUD?yMr znFA{>GajgFf}^PuD8Wijaq(J6_%fycU2-`5mkjL_tA<`He+8;0(%~o#rLiDS0T+hJ z)`1v3szIAk38}mF7a3D8k+@S*l3Z612+O^--g4*iS#JE$GyN5*r_pVL`tU8B_fsKj z*-ZpjT{NP7HIk*8(dn!E|Hc9SZ!m*@|6aOZyHFHoicdRxS`_nh=szbYUO@P^obwE> zQ3+>7pU4OO6`{J+WH}AfG~<-Rci_9@BaupPKryLes9C+)J5&t2|9aeO4KaX z4p`2KT7RnYiKD|2ulIokG>}j;bz^Zto}+u3Ng+qRu|pu4RSkLq1_McM15i{tP-}tR zw~r-*3dL>U7h))_o2To@0Wlj%qdJ=aHq_s`zl8Y=jDlN0(z(n2k;gd33>k ztU@HZXlQFB+MKO|FFmI*dV*{b(tXpwmqxIHcO`&tofz#tSBA8 zl2^(1S>iXVCH~wLa3UQ*G11c6apXa*sx?1JBEK~$W&O9q$7jC+GdG5Mb?9q`xr#n- z<#>F|AX}*%t+aNuV473X$Bi61JUH4O9H!yEJ_)Xyo#uTaXoJq8Pe#u6EQUX2r2o2^ zNzJv&C%#K?Cst-^M+Z^)&3UUI6pSG&$*tA6o;s1kP*8}Q3ir-#NK0kIVv3+lopjxA zh-zcz(mTbgOFyY%n8e}KUrNEY7WWt?=}Afubf1XN^9f5ndn;tqi- zR?Y83JrcYuymM%Ku7ERw}!T|C12TUf59KCrJGmCiHNMUjz@yWmU8XYjT~E}Jw1ZPRS(B^NuKS&ofoFhAfBN& z!mff>M#U(xw`I<8AfKj}F_?Zz*nHh+%D+zWy~>89<9b{CPcOJPz$E&5y8TrxY8=<| zpbKnanVU4}2S;Su_h+UB>frywp}%uzP~yD(*yx!<8s!{gQe0!~5lH^(84~ltwp1Ek z9-^Q6vfOn>Us0tupn<)jFvC0N%TTP;y4)?Z`4JUYOJ#K&q3vqdCCC#;lsPrN23YcH z!)zAJ$7TU5lH)&Sf*H=}RjW%rFsC_r^K4Ip^kvMnW(@kcD(BZ&^eEJ$gD{4<%eB&h z-Q5nfV;=MgzE6yN>~$&{yPyOQRA22l17z5ZN7M6ihw}_Q$Yb?z241+Q*he_nzUqv@ z-wyn#IB9m@4(?25Gbyx-(H(g5V}5I^27io{Ycp?C6LJ|t<`pc}fdu(!Uh#J`kED*@ zNcR?pfR8^qFZptw`mwoss`)J(1q0M8ozNxD?~YmSG{_l!C{lp}V<)3?A?SsQla$I)wK<&HVIVK-@wv%7fRaq2 zIc#OcsD$Y0{5U~#yW^w4htG(l6`WgCY$p=T=`m?ZXM%fs73-ds<6$hvl6|kzw2HaZ z&%B~fn_t}AN%1!w`ZT)oy!hHnjY2t4vOgtbgdmRgN7NFqcvt!B&JFxo1x;#9wk(6@ zBVK&jo5j)$H{aU*#v91S>6o@&?m_RV7?<0W-pcn5-GXxlqSqg~9jsj#qTfljPwcdr z?szs#QW7)C7RklSG_Fps=F5s_jl7A+Dx3H6z7b?)jULd(R?28f$QzemIDZJy5feTIvi2@uK#)otW8RVvd&#WebB-$pb?5dg zQOb_*{UybaCTBV8CLde9DoiUC1WeK&MIVvrjxjZiM7#sR71dunov4}wIkorCDQSXh z7f2o?3cuab2#T5qUrNs~X4W=G%9YAj&+-?vb>8D=HqnTp7tryHh*PgZlB@n^1w8oh z?GC$Dx)Gozol2-YPIsR*$i*>Vf<5d=t(}iz!jKY5Nqv??JaC@Wy4PtcwDvY)CL$tp zKqno!crwMve7;m(903I7o+O)zo{Y_U56{ z*DMd+u~91`Pd%FE4c_hPH@U+R8D2?nfnmjy!vh9s&APF1%7G&MiN?_9qdo^~3p<`( z@PTv*g;i+u-Q`13F2G>?T@%U9m052zeKM}{T3iRt-OLW(_&s$rCwY2kW)Ul~~(i zxCz#JRJn{6D~dN$NJXoqr~ZPoQs`@GHXLk`ejg!`$0W`YAv8r_kWfURS-_#PmVIQs zI2r_7)RIh(J=L$47}JSbN2{-Tvu!BY-6#tB09@;ThG%lmc|UIQ9AMWS8nF1+(=$mu zR>#28ho~U5zo}yX*m!rVkJba*WMEf226IZSYzXshUk?wg)=n4tZ>`YXnl9If5o^^Y zfd#j*K6y0k$CBAcPT{Vuh@R#tgi6fugL~_b=8I%UmMuj_p;8?mFr)e=0os${Ja;P{ zTxQ}R9=6i=M~|Ac{B3oNvkiLvg*QL!<(Bk$R_8FfGbOK6>Kifktj_M8xp4$il$j^? z5Cv^U=xi(9+bS{7HqiW@C$hvlzQ^nUP?6^gG}#U-pMFdFac{aqwyoQ_*FHfu6AbrP zbW*MV0e=!xM8NLk>!Lo*q_Biz+pIZg1AkJg05fu^L}?lVoC_9fet3kh^eJ<--Qx<0#Ep>8@m8(8<2{?h}+PG*^!lu3N4c5}g#sc0smE!Yjpwn4S z^Cz6&=Fe-TODv0aes30ZKk}ylO6X+?s}A43FyW3j-vI-?*Ci7lF3byLo=(bFCJA1= zS-$Bv>d^JP4w2xMuG&r5ik`^rbsIe+k@Z84V?>E0nj&;;l`A3ij7et_=e6B8E9rDZ zu-|7-Z>7n5nc7ii*v)w!-j)aad;BfFTFXGCRT!({y6P&!=d2xJfSZ@H>iI{l>|?4` zDAdN?wt|;!UMHZ@(g-3~(s_V3@yz0UeH2i_4QqN&qv?ZaHhB2s(0)?iC|_`L!GGDI z?&rYsH@qx+!nvP}%E9(SXx<*}5n#NcGU5weeXgD)Sm#E}@wB)Ya}<-ITI_O46ZDo9 zEG`kaUXFFc?1g@D5C|>=^U9d|O}pFgJfri1(J*qhBezxHk?R1_8hMO33mF$0gFJht zwGzGX`1shQiib-xY?9nyF2Pf00o0#qNupuzy=7kgdZ|>B)3lSReuS{kF2iLndWI56 za0a;hZrVU_rXgC0zF+W1H5PjoTUgLg9Ax-FWk#h?m{@jM=kKci&1~x9+ zh4SmX(uVA4t7&I?DaYimD{rPpKI`ky+taQQfvw(uyPG{0JMl}M2J98)3aqs#6#3>+;VHGkvhlkt-p_I|YCUKgIA z``n|JLaWYW*uGpZL5)oNk#J*8rkR5-lIE_4%ZOf{(mE`-f=KsJn^=i)M%!tIXinS3 zOGPUE!c!Sso_IdI6nhZJ$MoZ~$w>T$lME+b8TG=O>-2oFF`u9U z<2mtv@Pw-z`pnS1NA;%5;$Sa{UW%O121~Cg;l?A_5H8T$J0%DDO=R5xJ~_8^4{H*p-l;!d(e?-3*ile zZd61TBcQ=|$o_R=X`X|4E+byMfQB2m*#Oey%oF-WfnpaIjYT{=%F7aC*UP z197=bYf;M#vG0QJidpOm`|0R|4w}V(5X**!b=5JysGLndpQ3uwXpKg^Ve)MzJPOOYpk6VnADN(JS7WKvwVn|to=o<(R;=?urtjfXX5Kn;27wjIj#X!x ziq44Y{#pY-H>{JEsr5+c4wqWwf)|#|3RMNwGFEnWi<}3E;rGd z9TO*X1(WD^g-U&^B;c@})l=F8$N-}@f5njeqDj+9PPb&I8b4yHP@2#a3vb6KnP_CK ztW{+f(iBQ)c`xwUP?xl==79x_)%4MZ{PnIs(ranh<8+E}#d;56uA2p(6r8Z-*3eEb z?;<7eeA>2T%+zSGyzXGnI+vcoJUr(YuzT14nDz4m4!E#7#(Q(TU{`LwU9R={%XI5p zq0s@A#3E6o;AwUIlt>->?>IW&GaUg+(7D z0=gQ&W*{^;hyakR{=Q3fV>zSax2F=G)5}84$?Ptzexi?B9&t3%YUx-QU%&3{3y4|8 z>cwixyGV1hlWmeK0YL=!t7rwwD6F>lz5``X<2!VcO`VOS&P*LLlZK1BQv!QNONg{8 zoW+(}s67YWGhM3FG5#{~(O3S%bS1x-@Q!iEDXBp+epk>|A#aY0 z+*mTK>0$nIw8fwqAG=mHN$r)9pVwQDq@-FDFV01yt8vBXK$j{{!E7&MS2(uh?N@Wx zMkL#I%Y30g5z>|>jk%=YBTw^bMn+j||G4l>gA(_v5%`YCdn16T#=i^=#WJU#+AAlr zCV_8vLsEdGL#ra)j9~fd1}7gw(h5xf>G#_tgKRWrqV#Jb)6be{GhHg4SeF} z;?1^R4~29Pm&9+!CBM8JpBED9drr`?XY1$ z+!(4_#4#2_7nCD}C&cdc))vdR{w>}cJ7bTgz!StV}*$Kx$6M<5tHin#R{J&>y!HN$jY*KA*5 zrS08yx9YV%iBZrfzcChkDW2;so=YOM4Iq>qkzaZj=J#~ceQm~aDanSNr%v%ay#H0b zYzal%8)dApO$EqUSU@syb?04;YD~RW#td9(FZr|6A3QH-kQ{;&d=m2_33{kE*E-1t zKdM~OG~;_NHPAP}YA;$l?9@_0m%1M*3~$+o?i&{KeFOtn{9mO%Xy=aR$-smU^g!qh z2MB!&0G!qh=bvW(;2CM6xsIY|&5omTwL59k-`UlL))|$amyENCI?r#i>|PfTgvx_|CD2bIY+p)I7D+{%a-G z>m2CW)HvbgNP()>xD;oxbh=eaSX(r*n$ZPP4BOciXfC6i0PZgDtThj49wmkhD=A$B zts+Yh6k+~qg65ie#v6a|d~ZMbhU@iuvWnsczLjag^sE*Y7TvirWNN!z0jIf%aY}qu0SU{# z{gH9;wb+A5SmzJkd0M%T8ZRT?wEdSHAJskOVd>?WyrC<%0%Xkl2QOKHfQ(3skX(+Lja-JtJM=nZrSIdG+)rq7rABS&>u^Lqdp}GCyLXeuyyiK3v#>H#N3Y9>FM)N(8OEM>?Ls|IHv2)l0!V^cCQ|_t zbaN6kLYi(O@rHzEc5|O9(!lM+l)F9g)i`fgT?wr4q8*iVz0LR`%*YS}cy?u5jB;Of zkSE){^!6u~ys6=B&5c8U@bvA5RrNx7+f(Sf28GIMwfwUX@4^FKB(4~<6L9j%?-mxm z`JT)+M^_1>$|{%Pv|DxF+*tJPIig`c5`k>#7Ff;X@0jz;ELXxNlw5ZzJ=lYZwE~ei zvP2~6s7%iV8$+4EQvk577_Q((1dWJGW}f-jZt*^ITuADeTI^n!O!32k9mki4(*+`@ z!kB@eq`qf6f>)K;zavUM;NKCY_}>v_L~Ik}bYfIKmUjfls4Z}MD-l zX+#yLxt;9rxEz4xZAv#AN??@d^QCe^jc|Xx_pK>~la8`kG;B81b>hV8-esU;LjL1H z$lL0-eF;C-d#JrEcvy)knDFjj1t{M8g5ryTLTf2hxP5ACgPfePNqKEE1Cr`>3y(c{ zR*5i(Ph>NLmufOUmT))+Zvxh)mHShyh)M1r$4O_mMlnD3g1NyP@A7idPW5}8H=%P+EmNAwY~SX4p6j}(7b1|MVrr(1lhXu z^mrr+YOtF=F4yldj7lcggd z#`Z97`$Q0_46&iNNI!QUr}h+rto`Jy1F=+}`hpMkkf{M)Op36d!{x|3Z4_i=iVqa= zA_$mGo#YJAJ;_E4r2P~YgXy_h7qpq}RF-D6iJPZr>e1|@hex!n7ACU>)-9CSi+^A; zmPVoupMiJ9JlbL$r}b}QBXv?t#im-;I@vm^c1(#AJ@uaK|m6L7WlhUj2l@LOh{WnGqmc2PV zKgq%ABEwIRoK+N(mwk2uSn6Ha20${0(yjyfPA@aTKIh6!vn5wADHu-8CdA1nz%dWJ z`!$E>?kK`|GeDGZsTV(f3z{4BnF1hTfAG>t(i&8xXhXf1J^tz2|Jhe;+RA{`Kcags zVFE4@>X1XrEJ(056+@BcF`Aroe*#w8|mz=!#VcQH<%$y z&r&oO8OgfrRH^tsdT^Hamd?MBmf)ouv#%zrDG~Ek7>IiSbB|sEVl=alQYBy8Lcgvk z$tyodFVYFO-CTErtgRm+sSj(s+*Yd7&c?;vtabcq6HnQ4D+qX7FHXMb?;uOGz8h(X zzBx1eyIJARh-zn2Q1U+ol(+ST4G_3g+%r!0Q`s6GH{0y1+aDWhORTd-l8D#O-hA&H z9wCgQRH<6EmXeb6dv;Nv4S9>pSJz8{xf#`vFvtO{H3k(pr&_b@!o5>>55_}cKz1$k zWI1`&EjGw&@B8^@m+dtJr<>WrEv`tt-p-OT1Tv$byS<*)CD~bV<+WW>VY0b!lQ>)< zY-L!(-bQS>zA`If2L59t>-Z_^wZ__lw>GqF7&yr#iU1OsSBMIcm4@|Bd-SU<;seb; zOQ#og@V8T{0EO>Xyl*cGsW}7IgFz=SZ^a}zFt~3G;5Ztc&>vl z45CqfXpkQX7u6~VG6}!6HcMH!>KRn|uja-~SeYWri3;GVI&?LVY)s`d-$Wfzmy-Re zs)miw5EvYSFxhM9IzFc8m}saCRZ__ENLshq8p>Ku?U{G@gIB5h!_)R>|A$VqFNdLZ z^xYYa%TWJuk@%74nqTI3a%1juNDLFO?6_g4#aSh#qTIyu?X`ugk9T~|t%^70qocwf z+MJmg`n(ZdvYIAy=!w9JO36i%6n)683{%ik8~QOXi+9dSQ0$_cl{Y8UB+y-6o|M9K zjiMVJ$SDZ0;p4S}t}ulZ@PBwTh;xJL*XHD()*YgLBpp%Ap8^Br(HZ9EZs9J-q^bKOhY^!<$D zS-Rdzl(@t9b;>!JR%WL?b?^?F@a4K^^wmlC-uG%7b)R(w!q=?i}c|!R+!C-0v#uz5f#*65>LzJ zYu~WJ`+&-oDg)5Zyy|SW6n>SW;N2OXiq4t{mwfX^Cmw&7udml%=(Ckrxy~ z*;c6vPVx-a92}oAm*)N0YBb}nYMl5qX)U{gQWBXl-oK8{8Nad`lyyrN!QM!}r#-#- za77@O^7U%!)FEVw(0-1`BQ0R95wUXPuSIqx*5c)JTg|E_q{i5=9%CtlpMsGG+ytwA znYE$Hd6~D72}T6;XcB<#21O%&k^A$|6CA&+)D#C0T?PPQ|DBF5tf5cp-k2ce<(ky_IP{at_0oGTdua`rQV&7 zO>9lgwl%fj<^zqTLe}2_S35eCvgG>e_x-Z&=a@R!)y7=!nGPG9aUN`>E*Sr+LTSZL z*iR#?GWnuO8L%aggTTCxN5?MepcvGV0F8^!j?Uo6*#0?T^oc1Lktpz%v^;w~o=yW` zl6E|6W(_;}xm%BXEL4MmV5|xmxT>a{8n>N(8dvqz=$i@>E)d6bMX{dx3H2%} z0Vfa)-|z_7hj0S^8o}p>W*3#Qddc{d8f zZIn5tS*c+ZQ+0Y7%lCEM?6Uu85ggF>Mrw#opoI^4F3Kwo`8q5#$_4;ONQjY*&;zt& zfRug+{qEVj8E}{TB<32G1%PFTR#;Sz*ZRavCA-S2eL3?Pa9b*wBXBJn89Yn7r|c0! zE={w*#OF$>7ZFGZMzHw7L9((0gc7T}rEJBPlrip3hRRR%4tts(tXVkP5UiD_?F6-M zD35!ta0AGBVz0QMR(iCowo+F%-y{K=&!e^mQm#ABPJ04@{!-A5wLf?WFDX77;Eq_F z{+GJ9IaL{V5q2Blii&^m-u7gQWTN07^(=0Tp?pkIS?6syJ5f_XTSKECSGRs*J_N4xFN;5-lQ(zWe(#elSz^uUf0}&So#&ks zZ3`Wy=#1`305;scpiDI$wfmxxEdRw}43Y`KQ&Houfr zL>W4jd^)0s4YF_OfmSjMnhjcI-i(d!csgjM&BmHRM>5*TW|Ta3p~)m50!_@~Gq2=nO%}Q82Y04rWjQHf zb%0nthO)%5hJRoNR9V`2vnvWp`+0;WeVaV7CHO+M;~z=9TD=kw#@cRLYH0sQlRNPu`c!xUM!9Lx+4v0GqWM0aZvTysqoMGV~#qr2OB278ezC6zDZOS4qK>e(K5CS^hTN{vipdHl6%9Kyjj6 zQ3rX=^cyV0YWsTAp-Q!v4H70uNXrR$rZ;nBsknaEmq zbHo%f{3|~!S!$pi-cJ|9`ojenTX7V-JO+rZD4hear2I7Wt-*}v>n5cr#9F668U^!z z`jDUg=CxiJey9~iQJtA~IOqrrv1OC}GPfkm8L@c(!I21D^c(P-xTlJeYSxxyzP%;Z zh93r;V~ra9xrJoT$&Conr7U0Jj38rBNuBL`x2bwvKQ|+P<-iTSMoCre0;;%fS(5hM zBtn=;YfBX|%x^kcy|A>GLpUYN9ZVc`O_^cX=DOt=XH~emeCNqz-qPB=i}sL_sW7xE ziSCmK|9c0_=_Lip-zB3Z{<3UnFQ)I|o_+CwMac^rI@Lhu)4dcYY2@X0{G{=XnEkF# z6EifY;YfZB)^COFBydk2n4KHX?YTv?)iq(cWwLwC_L6pmgt_0EgaHm3+CI!v3%=Ab zdf3%}GfjyqvifnINiR_>(wjWnOt|p1#~nK9BeuuKL$M|mT5mg;m!7US6g#5F$0NI| zIkwYER|Db^A|l~sD@RgD`kn;7!Z|L3fzi-uK?`pA z>Urt_10CN?aA7Rn?v=xEY4Vp9z8TUNh#Gr%3~Q_&`&x9Ne{N`ttPl$YVU$jtKbJs8 z+DsgLm%k#DvtOmCD-0U}e$4RtjjrOBm}H~kr39^tch^|$`~GBI9WOoq<#zK$Ck21% zts1-TtbK%>!WoY>^h3%SkXgvz%Tlm?zEFS6Xy-;{m;PiWEX#`Bn!eratZ~SBb%`oKidb`QkPD-eL6&{z{%8 zm!#nC^V21_`uLf#DO3_h@csAMk|(37Os{9-glmWlMNKtF^gUV{UgQnaw+Y;(k&S6` z&Eob#o_TJ&_z=cGHwcw^uCUQOi09+Lq*;(by4Wb<@+@KbLh6n~jMN4LZR~bJ`@QqY@4ng=h(JbM^W)=x+I*hN|R>NXZ{gidcuqV)N0O z_ku(Q*`ezzud}$fNPBEggt%*m8K5AH?WP4^EZ0V9m8f=&O;4pNnpb`FVA*QG+E2e&OUp(r7A*DIAy(Si9V&4(bt4B zX@|~z>E*6fY~Q)Psi7&u7B)7H)`&|Vpj{5f*>=;{`f_V%YS$J};qwf-+&Azq;*dbp zuVy!xb!wf~iFbox!0EdAtb8WL*pkr(&U=M0}@|Fp|n{RALJ5C7*WVf+0 zC#%_wbk@nXqMsi+F(y{$nV6RB4I#Iu?4h=}3r|h*l+)g{wp6yv&6$}ThwtjEd@+CE z;=fu$`H$5&{CcNI|p6$j^~DrJ{#F5G0Ao)!B{N z9T#~VN)A$u)JdXzW|YWx%<*8uI-VET;?8a@$j~{j@*ROT~ktFqQjrlep@+h5)sTUfHJ)^*vISZ z*QAmoLhqAo_x!kj$#zdzOjiHF3)MLD@SrTQFuyE2WR{G-)(x@3OoXkyxeL|B2OM_W z@_k{D4FoQex!YH}03{lZvQBwf2&#hVs}A2zoOafgw5FNE{9`@= z3B+V@T5@Ra=donf^k|X!#oYI|Q4?yG*vrZ5s5BtN?Tl({v)+Xo2&1If`e5%{u!8Lm zpY$gl9qqbU5NS>nj1vE(|FQgy@BvGh^LV*iWc5M}%M;+E(P=}~0S}G&G>QrRB3Z%A z9}aH>cX}rE-8ZX|*Y@c6HvHKnNUeh9eNWLyRuxY6F+7|T?S_Gukc2qd7ON>s^2E33)MfIpr{y7jGmpAXu5#DMldOy53nCzXgO6SS@3PGtF6}G zgrpk-)pl*f{>czy53wLgYiZ*b={37Y$S28k`aqiJ^v=L>KAr=Q{x3&7yyz^v=x-w1 z!9j6?xXrpgIbUjXRNrP+khy6G0?+~U4{^h}oUkv`kJ0{(^EDrP4=JmVaUXX2E#3wvsF)0Tm3$4qf}%2vRr z9$M3eux;Iv6g3wgokLa)*07Qr6G!Nh9E!noA2iZ^tX~wdFLd#m_vxpUhssu#=ObW} zx0pv$IJP4b4w#`i6{d=>f7225$@Hx&%g}cxVbb|JhW((Lx-wcb;adgoecPlst0^D; zx{g6y&+C=NNUAJ4&7=5SN7Cva+wX*});}N4`p$9;FCl*{XGH+OimF}r{}~o{Bh136 zs-W0oKW37bMkWAc$?m?xbGxIF@Gn{Ezag=bZp3L68McgBH%_AX{%KGRM&-CLwLwpH z$2NPdqD?2_(z{ix`!ghg@qZNz#6vp?$)A=1H_uCA$oQ4m>&u>GIR0A0^_As)RMX3zFN!dR4-CPwkoqw$)tJH!m8C-W%+#t7b8SvL z_F3nDi5Y~HIAn<{|65b=b-5b8)cp&;**tkxPV4GkM2jH0R4?k*yO%a@ z-N=xmOj^gsezj!q+k#)`V6rKtO_`^PZ3Z7dh> z^6^r5C6SV2+D~id=Uq4r`6^{DAe4JdWvhZ4Zm23L8RAj1aUyYIU^62r*>0K&=c21;Pq z`|hAdt?3h^lKRp#0P9OSmI}Uabn9(4GQ3iFI%}&I_iEFl6=LJVeB(51OiQ-`FD~;1 zsIK)MU0_58glWQzTarEh;MsQd=Kv;s*1bRIe&2hHfayO zw98}O=|3XB|KeuiX7LDJcy{SqJ`EOE*+`WN37bdf^bWkXMr@R`E!ei(k~WVK#mwA} z3FBs#|5%BWDn8PH63yn~N|CS3Cb1QBG3sqHnf}=kucRc)Aj}rq*d`|XVoyBWNK{NS z=B8*umjwCw7-VTv!^fy)7dxVrIj00Oe1BoJb0W&?YkB0OHZ;6Yf=v5@^hyFmPZB(@ zq>g0#an0#MSyw~MS!JCUE=JOWxAQ*LDQmp!(BAT}GrhF(nmoZ3Gh4Sx{Ho8y_$fmg zT-YMEz{E^w!&%q9agEttYCmoTgdrp;)K;kDmTXVhFkK1klX`MaGhXOnWXFyD@?Eif zc_J{|G0@(uO+$9Z6k@!i)31UmZy%--l%TVJFQ=rO_XzrKZ_+s|3py7rdFhzw zoks+cr1&t;u;YE%Z`z)fKWOy|yXuva!24@cK50>uBts@pn%LXL!9Lu|9;hF33D0`d zlas_870p7AT_d1%EMdxTiE)ofCm!k8S#dv*bPjV|A&`2I{_C2mA~TURZOgB(0e$_d zT3LBC5fu;=#uyOayCwtcALP12n zl8X{WnNkZ*oLcVJ#hTVo^Xtx+u5fM0>Jf^ww#fI=zdJq>(8rA1Sk?lAl+I2!3Oi!5 zIF&77LfO_FXB@Vz@vXoNMW~8I>n0Jjjl()Ck3Ic9*&`{Bi5Q_&4OroxS;LIDM74L> zK)3+gy~%0^^3lVyf*2RJL)LX{MBohz* zGnwRHU5p5Q+jxV{0Q0Gi*pJRLO?y|;gl=9DDMZkvX;Occ@NL1+TEXqBx}J&RC;uw0 zh@HOLEAe0tA_+9kiR--1eP`0+3TGpK*q@cADYwlY)U}N@wZPsoc~%`AyH4?StetK^ zmC)P8l*vdo#MpqZGQY)Y8h$Ej8l|;CFT@T1>Pgj@77dDGsQ;Fpg}=ezcNep zHtz_c7v{lQs5})Fq2UZvx{e@u;;fUdPGxy(;iZRrtG(jdm3DvXEpYuLu@T}9@qWm6t!_mr z32*L(#xMF?sMIRk<5mGg|Jn|_8(QO{xKA#&<83wm$x6LEheHGv2On=QC}DlInEMMW zA-$JxQ;ciJ6gs-Dg9Mu9bmtq!l?P9_|KRcAD%{KM;l{oq&QaP*Nc)zOVdbYy8zzDV|by6<$d}T^a#}8r*iA)(vWqdtP z4@;P!6JUuy$nJZS#bpvBwLHfmE4;27u?CgISCiJ%n4QzEg{SB?*t|gi{d@tRr{dv!SM6WTqFj_V!C&3b ze`6K@MPmNLSLwu8YNvSU46Qt2$et7i1-0ubYw^FF462W@<0-;d%L7(hUn_%NB28uf z!aLpv#4weT5aKs(=5tzk*51M|x~;veC(zqQd=cW15PG;W!q;obJ%upGhLcg_p^@%k5?mVjIGjI4$Dcp(V3bs03h3 z-!CPw`xpP{A3VX6N*{E-3l7=O*fGIg<#*Qa8+i6%{@?z(|EBFEQNWfoe>7x+k|M+j zL<;3{FLMF$QK$DOVd?c~!CRbWi}vrX?D_RX-FJ|`zkELvz}wh7uFtU5#P<)*J=1op zo3qOHREuH-B@e?f0^m{8iC#G;>_)s|&HwoY{clnIn>@G|Cs`s?yrOx6T|-@rr+$Rq zc;t+fpuL=?Er7Q%irA8c$gCzKt5-w$TcPUzQcD(VMgUDJot?tVf8q9nq-JiDcepSV zv!6JR2D(*>_&;EJ$H~R`#Is;^!_YhT-~8hLMLsgVx+>(>G}xJrTtb~4(F5?;*cvqK zXCqfEVN8<-o};u+UvA`1rBsLW^IlMtV3R4n3XQLwS)vxZ=0K2;xV~Tm*PbD?w6ITp zbsgFpANC<_oWfR9z6GPt*%bTOz+Gd7*U|Yao_UT$(A;ojo-kImPCO8iF zEdtl$5=~kkP9OA0nV4;(??g5Yf?6kSj6A zy;7vKEz3x6lGa$v&lNJDRDOzu^Yt1Z-&g7Iys?0y$;xd*8;QC8nNG;U0k;^*f`U6@ zYxJ}8TF1OcWRw{I07~8RJ%lc-i7G3p(zaRZiGvhf?hs?7Fx`v7A6v6? zZk5+R6xX|MQe-AIj`eDyz3-#{C6v(Y{8)$6-3NVHbJ}WEK zg!|N6GH>T)W*JKHcLtsDyF}{>FvE7g4p#$aB-8k7NE<_K!Xj^0{cx5xHJ%_tzNz4q zE$2v&639H+#BJ~B4qP`U)cdU$uISik4G-ntT9&-Q_j%APz$uQTznO>!$@@yJbyG9p zA7;r(@BXj$zALK9w%Zm9iYWMr6s3p=5eQWzl+XmJL4qU{>4Jt9LJ88FpHigvPN)eG z5Re)=(mN<6^j-xMq<7u<_Stv4oPUgcb;daN@69vb^~^QLv*wypcS~`rX!SJNU(|eg zO+~n|Wa{#+c{gfk`#R+gVrJSby`J_$dgB!b{kH#O2VeC^sB)(pxAmD!P z)C!Zo10;A1F2peAj5;o*%$0R^l_9HBitX$N3`ASj={@E0Htw^|aed}EtC}`>!H_xq ztzB*wGbzyK-oTQp5;@}hocl?xA0A7Bb!k;b(ISUtT4weAlS80OGWukoPE4U^VQU@# zjc#1_^;3SQ+lBf1iG6`m=P|+B2InbEb!)YNqqHecj-}Os^<@VUx#o4g(VcvniwWF< z@;Yxv^6l&F1qELwb@l*PTwp7Eyk$aMoPZnJ-y}+J-yZuxn^K8*n}&Ro3lRb4^z_0f2ta_fJS0`c-eNZhjVq;PNas8w+4 z+$$x2{!*CZ-)m0K$y2L&ha4CA1yWV@6n7x6aN>M=8N73_(R2;dc=KPr?8^p&{#0VbS13~+HC5!`zz7?PV+%Sg7`Zmp#!j_x@}YPR^J^N znNXQPlN^Zzyy-3xm-yz-8OpnfPYiY*)Y&@}Jw7#k5i%?D`ZHFPh;vsOZUV?(8NZy25jDPL9K6#yh(Za zjmW6(d^#;QyE|uJG+XY0$2jkc0i2Q^k()es%`%$hZK3#)6J0H6Y}8e?#G!;I_tbh2 zW&1q(hStGfsAPlYRXlEs|0^Vjqx$DrZ5rYFsnd?kku$bqixOrfwAQ>(irEaa+RY*F zgN@ZvpGYuQAju2=eP0{pF{;D zY(zv)v}Q{?4cYyO`nRKJBlI)boR*R^PoJ83B9d~qZf7Jb(a+%w{&@(szEIy|UI-!J zm-A--+1OGg+m!aJlyKmDdDm@*6AdZug4wL<^MD#UncN(VRPoBs122!(R%fc@v8O9NPnx>hA&FB@70tYosXN~ zbF=l&N6h>>g@51t@D}QB5B?C(Wy=fpTScRx&yR#t-dh)@jZO$KUZTwUekb@vINIH&*KZwU}1Mmt(64 zpPOqWEbXU|{jsU=_;o0hM7PB3D0Q*Ja-a6>)1kMQ+v$zh1%5^tTYcoNXS2)g@ZxdG zc3{J6r0;rK`(}nZ=KNf+neH?TQ)n|`)D;NYR3-~3^mjM5nH-3bKZKmdrD!Rhg-A1Y z-95R9)$N@Mpxj^{%Pn2+bPBC8$(sw2ykB0zbCMEOyyP#HicwJ`f4+lg5fgp9_TqBNh)NR&oi08{I8fx-=Dmi0GI@8b(mj$)}6t%;sR zMwp}p37)C6=A}7)$=e*F)jh_y-GR^TXmoU7ojp8PNu%*Zrc#@4IM$ItK)A=YSYX4Z zR*SQWY@kb)0E)myGU)9}z0w*Ve_2=Q*>T)6kLRD>IRsKi>0&l+B}B2;uk4yeAu3Ho z#8|bTMi^U4bJ;v7q|tcj3kt};C)l&x(4*4#L7pYm#5x19M(pb^$nL~wK)z+KF;XJ_ zvOg0qHrgx8piH1($xfY^SNoeM^8L#eYdCj)$7V&gz{)_pfHRLPH5DcP7Y{w0O0Nd! zye(t%DmRA6gm&8VOH7tq4217X$E3qWI%B8OT$32wV9bG3uq?%^cWNG$JjRyS{3Slp z21r%GN<(YbMRx?{=XhtaT-tm!O{Czrxsuh zrKk+70F%*&{#H6q&SuWdpSD)fu3FkcX9Zskd}{_vLC?QF#Jcd=`;PSHR`*aV(~dva z#jYf%L->E89E4;vENUuuMqhilEOPlM+8$~r{oY31^R4Gdo`xs6N9K$;hcYa!uy}rD z#{(yMs32Z=nQhqYypNFj$OIa_7}=D|DsdrhQW1WCR(_F}cC^^suO-56n&q6K$5L;P zyLY-u_}-_#He>VHo`=5V!2*020bH`{cy&llk*|IPN9}NXTC3quNW9RZ^paSwrcmPJ zP^anE&c@A0&gp7$L;ansu6qodeYeh3uu+L^8SLm?G9&B@qvA$P*`wl)*$avq0L#6I znLX>le+wfOO#{UIo-+yS#SP#6ytwDi%ZMSP16UI`W^*X0BmXH zHF)t^@}HbmOsv#G7-Y&^fNIGmLMd=S-D2+w;ewpdEsi=j2%8V#`d9-)Mu zbg%VW*nMmK`G>2){m97`iGI;xPt3MHmg+-5#ORHg+u)B&e5-0G#IHF*>^nWtxxN{Q z_)x%rIg#CQ`q=L#cnhHeGfrRmw8Q3YsVKxUqvmnxDnmr)Lc|f9E~vD)K+_WY>)bpW zBjZ)YJgUF6yxja(LfDT?y2r~%(;Twso@0%QXY@!x3`fo1U(2@om+FYkFADPYmv0Lo z1-bx5HTJC>o!P`=Vj4A8mOG!KEjr)^kIFnwSlGo11L}OoasemgYzU;j`gsIdI(p$m zMXla&b(OtK&+II>=o{=~dJ;SOKo~}t3f7RexCiBtGE2xw?g-|qL?jLNBZKNb1AQWm26Rjaw^SRC?-hO;#W)1~wh^1^`C{@nPpP6@*17y! z)Rn5GYqE>4&Tw+z=W1sHTDG4ljA(hQ4d2)Eh-6JkZcj4}P-@AoMyZe8gd~_=$06DEy=ii_pqnp!aRzOPcd&=1 z@`E521B}CQy7L<8aON@S`xBlSY-|9&HM+7UZ`edl7xP{d8;IT=o32Y@z|%@hR}*UA zNSS3;DylM$BEV@o-CrD%&Ej@A+uX9(Cmy6}44>y-(-fO=kZYNA627CW*ME7GAvP;< zmN(%c7ROc)h|+rlTN++tv>TlcKNB5!?38a5pHK6%Qr)?~p5?cmCXQ#a?o^j0_V zdje*=jbHMH;OE`tpZ!3~M*5_Cwkfk{;yEEuj{m!?q=r>jJ89g+H0+{t{lVSa&Cc#2Fg5-pQ{Q09dCYt9d(w^K8K7 zx4i56r|Z5}u03tjl4K>f;=TlA=zhkuUP&XzljiW;vp1$zW$Y4li|jRjF4i0r_Xmy! zdOo*xT%WQJzT0o6(=N2M#T&*3O#va%k`+6jcFmog{<@EPEyqL;5D;e2_iU|={c^kh z3u~ApXRwa`py!|&={I1ir!d7;v@XJEd0o22mHQL*$}!XTu#To>>(JW0H(!?d(3Z@5 zkO`rOnPn1PIyEOf*9b+!-hCLk$DEW$sN}!=wQ_qZ8AYZh*z!yFp;gm^E0s5 zpf2bSRrpH+4d(;XVA!Q3-S7k{{m{YoxL@3rw!JSMLt7l3v|I8DH=6Gx%4SOajq3dT z3l8LVka-umTM{YF(GIBDKp~9xg}oPf6Rzi-k)FRC6(S4apmC@Mv^QaIU(ncC7pDg? zj>%1>SisTetP&e*edyNgO&euu>A8Qi{^0wMIb>1lWSX)UPV`@X|lx zrOQFTM$!#Mo?*4x*qq)`ERQy}$I(*z9m{Y8wW#?86`IU+5^1p`NU0euNL;Z{as_Ye zI{n^p%GlkY&iY&S6_>Wc@uh8IH)&9)to*ul)s^B=-c`D~Q+<{syTK#~(S7-7S1enF zm@(+&d#{;}pqbBf%Ldn}8IzZHNUByQrcSEL*c#_4{gvZJYr(OkIG+=AutHvDGY^fr zW!)7dZ(Wu(>ktx$bia8V2@l`7HV5);80vdB=w0-Y>wEL4&LN1Z5t*$S6Qy!d# zsnB+=72~Xe_!6asxip2cRjRPDvQIZwc`fxeskMEbrS@Ou3QAWsR~&v1Y~(qd8J{(? zPMJc6Tc_VN+M99fW1df5IEu6HeGpM&GBLy$lxgJ76EmmAcH+P(|LkXkU3@9#enTfx zpsd8KNwM`QVLEiKCg~zKI9!H%!LKh^wcHr`#pH>k%LLr;!+d0PFAr{|+Vo0_$w2q> zOFX1eUy|LbD~x5Z$T1f-xm+J4pCVNF z7x=MCujJ&v=jYY*hpGsV>%Ft*e`;WEIY!x41C76Mm}?pBm)KzHTX2j9xOky8P{tQ} z)x{iz@F*j{K%k?Q?O~eEltL+M9GlsmEUUaBy4v#2LRa`$-rP>)Xps4*K-mYY6ZQSL znpmcq)gOe6Q`5&&9|;i#jH?+UWxFC~dIg12yfw}TY34s~d=K}2&}bq!ffn_+%QaoR zo7m<{-c$qE(yZ~LZUt;Ni!@fxbh#x#8GcxUQcw!>1$F6(?UqLtoV<4pc^Z`#MS^6z zcv8fU&m3lo_i(a1CLuR8yOjt|nBHE?n11NW0k7`>!kQXFUW#Xy0-f#1em>k7iLX~z zbuN&7A`@b^U~#IF_|(r&pxAhuC2a4#iVrqu7Ooy$$;a~(qW+Ln^$G|1LxloY=}Tjq zm87Bbh=Pulq=_f?MzgJMS2DMPG!j;lQ%hi^nmo?PYxf+UXhs3XsIxdjHRas;g!ls$LQg2&E{J9E$gfN26^CSoQM{_|B}mJ)m9Z@6uQTZ6^k_tzZ; zk7(bDCr@s{LHK2(lKZo?_7qtqZ`I}5J0pO_BHeNq; zCQf^3W*4JgHOol;$p_+es|?xsO)=jf3IT3N5K?M}YthsV+i zcYS2HP_2*6i@@5kGMRZY_A^WoP>9eGNFq8OKQNE7WuvRU0lYB@K;B?7V|GvM86gx@ zOCuc3UL=fX!%4c4zXuVfcpsfoJ&&NWP0DI6eYIAZ;R~B@JJ%C$IbQBf#D1yTwa?LA z8mm==uF%6`%Ia17vIq$@TB~~`pWeMvf(}#@z4BVG9Wc%|K6#4EPb@I<=b8je5MS}r zx7*^l_?#1X6S!>6`C;!NzN-!yFb3u3oA;-SyoQj3l&BZWaO{T`tNHrpPkZ!>+FlX) z17Ayq4buu6UX!$gOJ46efzb0?BRGZ{N)U8MSO21eMcqtHyYaq$sXSPk56(U zz8*O8kC-@$d|4)3TbSDIzC7uUa87&I%noSZ{ZJYEv0~5_+EsE!rna~HmT#n!PCRbN zsRTk&?L#dp<(G5)<;CXo0HNOh0A zXoP!QpviDS7R%;S@+>Hih#J`nra!$|5M3pObW_3a+S~Z{d$S%Y$E!oqQCe5Qds)0W z`hK?}zRGaaLh}k6YV$VU0q-yEHY)7bUssK<)2cpAqyzVh*-Dp~`}aJqo_^|Ra9>h# z8o9#Ng)uIK_`TMDR(v1@bP8v=8hgU}qh}DRof3k-^{NAg zCQ(4sLDu1zz|N!_A(-POCUEP2EqVGul(ca!jCSY71VYk1LINem`+@45j;md}r3 z+`S#+@!$7>SFKHesL!bg3f0hB_gh+UYv*O$3dVB3w>+` zQn3}bf2bb3Lj0k+U#F58u$Xw}CXO(E`&ik!c+)0@8fYK?IZH$C(uw`0BM>YQTQ~u| zYi_62c;VA1+mmmov?$fsM-#fUhX;Fmgybg+$IUF6l*w9wTFrhj>-KWS{-F}J=Xfuh z9>(qXvB@fLgpU}8*I%@Rb;WRu{;0YKUkG(H{pn>KX;SuwYA%Yu;ceMWHP=}f!J{KV zEv5DdUT<@3uYT#74|DOUy%53~B=RZL_~=bRu^v3kl&yz*S;TOs*mwMXN~8emGVAoC zdDf?&>coOD!i2%tXG%nAPfrP+kLx2uA^ADCN-!{a(EN%_XiO2@j%VxMQoUCBgx*=Ru@Oyl^YP@S&V;C_YN6$q4J$wUsc4)Ys90|)Dw2srdw#q z_!c;aHh#V#ffs$b)}4PIeQhPKwB}2$vwW-}Y)hLu)!;6+T7TcCF1MreGm;~8*`l{R zMxsUf5Gy=7N;4ofmG-cjjjQP$)w2zzaS9+IrY9*cL{ZA&-kf(qhJs~JB6sHtI`M#kcRRCK}J z+BjIgwJOHeAoNUoQyaJ!&m?igP4{M8@Q7_hvn0b<%vSVQUv8v~*l>^`|GUpTg6@IT z?bra`cerPs`$v(xkg4#^!r`bXlYTwJPWn{u6l$1KEKGG_|Iz1|`=4_`-FqPsFM7}d zO=?0|=II!Z8abhM9!xJRpE2j!g&Pw)e`{=q6mI5=HGi-7%C1ESaCHq}lA9odIehSO r7%O`9$~y7=B|^e~n#}zV&t3oj+y717{L9VnzZ^dNzw%rB|4jZHGcx)I literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/2023_HMC_at_DESY-md.jpg b/survey_dashboard/hmc_layout/static/en_files/2023_HMC_at_DESY-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..32429d668f7e890372e84f864c72c7fc4ead0a5d GIT binary patch literal 55796 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 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/623c39184ca97bb437e258ef(1).html b/survey_dashboard/hmc_layout/static/en_files/623c39184ca97bb437e258ef(1).html new file mode 100644 index 0000000..a4a1eda --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/623c39184ca97bb437e258ef(1).html @@ -0,0 +1,3 @@ + + +cables HMC Animation
\ No newline at end of file diff --git a/survey_dashboard/hmc_layout/static/en_files/623c39184ca97bb437e258ef.html b/survey_dashboard/hmc_layout/static/en_files/623c39184ca97bb437e258ef.html new file mode 100644 index 0000000..74f9fe0 --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/623c39184ca97bb437e258ef.html @@ -0,0 +1,3 @@ + + +cables




Cables Logo


\ No newline at end of file diff --git a/survey_dashboard/hmc_layout/static/en_files/AI-HERO_flyer-(002)-md.png b/survey_dashboard/hmc_layout/static/en_files/AI-HERO_flyer-(002)-md.png new file mode 100644 index 0000000000000000000000000000000000000000..500d64af521344d9631f4b519863a6f224606561 GIT binary patch literal 230280 zcmV(_K-9m9P)00DOh1^@s6Q}Vg|00004XF*Lt006O% z3;baP0000WV@Og>004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv00000008+zyMF)x00(qQO+^Rh2^|qE9}0I65C8yx07*naRCwCl{aLeZS#}Joolasn%iW7UR5{GIeV`)*K9}gjgI(pUljlt z0)Xt_ZI=i@1fugB1@~;^mNw>lc(*wL02$L4B=!ZJHv`E0Ig!5Kz7l^<5?VE_VR#?(!+ zKWt9`tb1z^h&n{9qs+i^04+!T*$9IO7@X5)n~4U+80Te@n1kt^Qu;~+FoB5m-DIEH znE^M0>S{|1cHEMVDt{G zC(!8{vYvp^LC}8dn+6~RfneCA2@!_R3jt#Qggp0x%{a@dis&;m?OmSk6Op{9&gaes z6x%z~^x1x8_A|HhTu{DN+MqZORL-l&0JE(VpfnCnkF0;JYtKAN-|D+_PRLf**WcGJ z7n!Gi_+Am@+!0Y}Qokb~uTU8$z_EvQUd7&5=@Cb#%07qK-qHIQxo-No^+zQBCf{cx zXFTU|p4%YeG=fkZ%}dqdwRSnxcT*ap5YQxUn}eGhqV z=I4Oi^1LmY)5Yns^TD}vGn2bhHkliIIgEC0f1+0}r_s#>Yzf8lzlb)%M$BjZ`xP+x9{GW*oU??Mpi?}B6vJ+_gukuQUFq8>V1F*&&KD7gE z1lB|hab}o8&9py4-lR#$u17yUjU`C6B6x~8qn}qrwwu#uWrvmwAshF0KW*FFv{snA zh%*;w1{BN&5EU9g!Sp1Aug#hA^p^FCC4ySsAgFPz1)5aqq(1IK(GrEGnZz@CTMY^BB*zDj#3J)aZt z`(JWCS5mh7OO_a9%Umh`Q^xpmrYc`j=H^V$%RL}>&1FSG{BPHcxj~3iT#gMlJ$Si` zTZXXJ)QOAQQtnTc8k{d@*LS% z#Qz!L8v@hPoz0MHc(FBIFer0h`;9WT9LoOs$5S*d%l3YvN)RaKaBn+g4>z9{&9*+j zM+pV@UaJ)egDoLAlKXE4zD%`?r| zejWe0{auwnI^q_6&|ju^gw%EqhV#-G_+Gz}8&!gkpWkzjUfPchBe-h<@7wxj4yH|> z9ey=hxxTPYUU{*c3ve64InBey?2sC%Guoz7K5-%Jn`yqZE>gG(m3yg}B(~2wbKgL~ zcddOe_1ZDltjlm21O}lwFyQ)uqJja5n$C#f5Ga%pO0?^5QCQv442gL%c+s>XhY({f zoF?Q9ZT5;omVTd1OP1?KXU`ULSf<};kp5$5a78nxojxg2HX~cf0Q1F{)+f%f(;ecy zyQcQUH1HHel4c5VG74K?3E{u=#StIZ?Cia6K5)o=LWHIs?VMdH%+}A^G&BC|H-X-$9ko${Xd2(*Q@19KH%$(>yw+jr1t0Y%ka??IU zQN2?jc?lN*7!`1Qp;yt}^L@`TpSAo7=irVGc?V*M+aXWCR7M|vZ&kM>_-S@gss2oZ z-g(^&v!iat@)_knf7!&f%QJ=P-S?_OtM`aOU{XS>wsL?aVafPsbL1L>`2M5RDfAs= z=18*k4Nm6+;S!gfNCo3eK5kAGTpPahOQvixk>PVUTvlQ^s*E-S<+$y7@gy|2HDb z4vlkLv5v1IoC#BJnYjxcE*Mohk~<`Zp}VZycS$S1>zek2oh3(*s0g6BJcLGWk9J|S zLOjWY;b$SV<#WNC$Pj^;b2f65eOJGdq2wi&)A1IVX(Znh<{MwWjo$KbhJNQn-h=qO z3I{f;OR_0St`(0~Bw7hZa+HFp%vCcc+V0^bj=KCiOm&Oao$Z~R-`Zhi>eesWA$J??{LK;v0K{(b@O@&z$|Hc_?PtKs+YeJ|;oak%lvh>d1u)45ETA(E=t0F_-h}`*_75 zwU>(w2+dSK1+CfH(oq5`urm>`)WdY|>o%J)?GV%rvEAA_Uix(poAy>heUa-$wDt2Y zrrm7r?4O_Ot^@Sauibq%b;xg=_uG8h^4<*rp6`{-&w9f~Xs~9^yE3zzw1tHMdbS&pBAKmRTV`&a$$}&$(RMgM&9mmFGeV(d1%%rQRY4>xguP+t)%50$d=)|1 z0P+P5*=n=C9%q{*J)t*!tLDP_tSMtJFPQhzWJfN~y4G9h?j*E?DMa@l+1gH5Y`xS- z>m8MuN3-JVAZ6>%8#vq;%+0;7)O4@!?XCQJiGd?!h@kZ@_&Cym(q;(J&Qv9j0FwRc zH&v)}?YuhSr75G~3XP6v?SQ2nz{D6sJ>`cCKvwk8A?O(6gn^EZ4w6xhqi#NrKo0(4zZW{*v zp+fCv_~qtsS4v`1YXHP~sy9NM9o&gSREECQ((5y>dB)Mo=OhVfQYk8NV;RL|c;GOhR5m^`^PcJ$-3kQ}I?xHgPV9QS3i1&(7_+ z_9vts^J5GP9V?@BO|kT?!rU&r#1SCYNXyOs&UF#JryL&_GL^{_K+VGFlIF-}q#)7g z2dPm2&iEV=a(LM11Vyki)4{I)RABAa+`J~knA=^xBvX5SE{SsaNpgKm2%Y@lHC7Hp z!0-?|+gQH7CJumT0Mg=2l6n2p7+`8(Zs2yn5wBrsPw?pKb-e!MqhMZeI<4sa2B&ci z={H!{Yjjz0Iz7eBcnaVdfD?qwnbZy#30c%K1HyKuIzTIE3<%l=a=$Nqb_alsbh%^c zC>xt@z4Jw`vwcDILR#~Eo_OiZ?2Onrq5t_^dm(v0FMIhG=1Zyi>_(`q(EjwXY+>&D z`veE>Xu+=S7;#7y^}M;svC63-2Sq_pCZ~2pfTZ`aw$Bq2+Irwt*=2esl({i091hdS zi4>Z1X6_=0VAKfN$+TlStdc3hMmv#UoFIZYLlF;8+b{%s|A-N6rfxIePSBYq3u}q@ z-FW%#*neGzq|n?cnCEt1RR)s#%tjmF%Ir)wp(^Dc?VQi8IMzx^)W|KNRm@bo?0oIb$&@4tuj<_6E6y$8Da z8L$F01SeTllP|kld<=DL##zBQZ4G>qQiZa}MiT#ORSeCUI9^ww&e4w0dw0I9FdAD{W(;>|HU`8l2ug{e zr}U-(wU7^a5^%SD&wfl}-I)t1{hb~DOmyIS-$pzu@2hNOFBEVJjk|-?ZOsx}r0`7f zfSC-a^pOBa+lk6ac--fM&Eo_-EHO+KX!N`=5zto9V2}UkTJc=p2+`uhSo zbqE8*VnR2AFu<)rMhdAqmxO^u$14oNXMROA=|G6?w_mr~rMJ@Sg(ixI^)i{`rP#ce z(e5@u#LMcpxUq0c1-<|LqFTB&I_SX~|3!%zVeG{+S9fMF<8Fo=AbA!kMl3 zbH1i7K#2A?AaqtZmt=q~8H#tAkdV8MTRDh83gRxn7}GNXLMMs1alm89H37zeby*l9 z1Ea4PH_w2ZYf$eHQq4wfe@dLHEz;kw_Qjc9r>2ygWRPablFIz6KnCwON9Hbo)Je}Z z-_1LwckMdJo!=Mh?Q4Q$RyERjLJpknF;7+`CI(v;Ky^}M3}2`a{E|T_@T21yVW+Ps zLor>9szsN5x0!=6Mm#G;K&@0`cw=IEA!VktFm>5ILvo)oQF!ZnI})0E$!_of+FI{b zfoK%kVEibcX+mo)kTWU7E9Q(4ArwP~#iE;EfT%aR3?FMV1gn8TSlWVx9^qH|PY6Wr2)=hMKXDAW)NuU^SKI z%=utGAZOR53BR}2RyO+%toyvzR=R%`!osVFJ&!qY;&ZQl%LC^7;p2RXExw$`_d*?~ zzRvJ$YAI6Mk^Oz`u$u(!pTC@7%JQCAt4wJbF!*E^D9n6gFAq5)@K=v7E<+^2M528v zjG2d{n?d9hFeZiFk|1c?xg!OCbDFk$ik#h>+*F*~px)3*gegs_o5hpbU&=Xj;UO)P z+j|h-OkVBe#Gf)vz%BGBLW;O*g!BE(9C5^#t{RIBq#fDq%jL#8a>YINU~XS{zgv(@ z8XVr()$@qFXh5{3`+-4D5*6J^+9yKp$o!$@i#^$IsS zbEF4MP4)LE?UX-rsvQ}Sw2%A4m5EQnK>4n~jZ$8fvd0c0a>nTNKsPW+x2-UHtUVRlMJk~ zK}ZuKsw>Pp&HoPKwg>wsGv}a@5`E8i@}~b>)}P8&ahSyPSD3zQNU4Rp854q` zbh4R4PZpEq2r5(0*>d~z_8z})c6`k<5YC2mulh}FH4k9ni-oZ(DwGYmyn(Vj#p^o(hgpOU?Wl#Xxzb7_wx%U#P5!_)9 zo|+-Z`C}&pdz_`dd&|tXYvyiboc&A?(y_?sAHnV8YGg>VU?L6y&Eax~Mv+=qwZY9} zHIhokq#WKhNPO75`f!KLg2p^WR0uS}X)cY*8(H2hLm^$veVdrd7OFT~e<@n(yg$w} z>7B#s^IE4+GC8Nk`et;#Skyz+$X!F97^1JpBoM?Zfto2+W+K=oeTnRC#EK9*3hoYr zF;DkH$EukjL(FE!s~Vpjc>*@;9r|q%PHj=ocG|SYE>}|Rx6em?e-+j|o1?z0coyg$ zMKbMaZUfP=&x7H+u&U=d)_Qn4TSpu$@?$hKK7hF4^*7$cul>cph+qBcSMiZI-oQWm z=l=`-=|B8;`2KglfoC^A!`g4KjvI_|D#}Qr0VIS2Ex7sFHU9ZO`A4`p-QcsI{Ve|0 z@BT;lr~mjL<3~UI7Qh`c6lowZm=`o&05I0|24fI-Ibzu+SJupcqV3NwE5C1< zvp-X`!L8;_MdO;B5t3l#{!d{dlBp8Rjz$rJM##)>2FB(fotsv>=ow>^&{ zNU>3;&&#ZN>nQn5kf?K!=T2?eGZ<7-v$BGzqs9f)_H0juFh$ugK1^cIg*bfR z{De()CCr4A$AC7c2bPCECz))FmlcWHyVTq3e)RjIc+Irdo4;e8GHtLURgopGjz@pmmL9W2aZ5dgh;&%7Fx%^rFF3uGHf62^c-g-gUU-5V^Ir#j!y*7?z z<%n7?r% z>{P~>3qBza)pI#o@{n8Hex;%_3wTo=%8f}VH0RBJ3+Jz3xB=*9Ux?IJ4uz&^X`$Ih z9`-VsQyP8P4cy8Yr)oi%k zz;|D=`C0$moc_|Mf1j=y72KK4J!TV73{B6zl>Y2!<+si2xekNrpudz}oy8Jg{{GC4 zH`(a6duF4VRSi6Zr(_&dD38>8&n&p*nxOL{6-=KO2ETEPn%Z`z5Q$gdSUCXGyvfqH zl0+4$Z4cAT5M z)@^R{JP6!D&@B`+IU{b;c@`~fVrG~2+qCtvx}3*$3#C??nb++8&HDpkAwSx=2ex?x zvN{y`c=eSI)jhB5jKGA zLw)DoeEX7@aLsM?%L#wkF`bZaM0YGK0yP;lPga^Zn0^(yiX!0S(iYgXVn*WwT3hhh zFMSdJ#sB(uadmhN|MVaIK7RjS{{epbvmauWXBc>fjvEYN6#M=chl$ffp=?D-bc-!g%eq*YUBB zej2a8{$>X5)(vu>RInM(`dqp^aDiF8JZ|yl;Bt6wZ#MYrVlGJSe-4$_H4S??j3^Toh@GNlvqOc`6j9l(5zQnT zLL=wro6II3@7_)~Dg!^SjUIFw2s=OlFf)e#OnBOCTiV~mZvU=MWD^(^pYf#Sdp>&z zfjIj!$9q$_QfTEltAR~cRgEq784@8_&BB!GMkSksV5V>n`Eo{VBX3c-y-J7zgU12& zLMM(=26~V7NvLNn^bqKBUR+=b^H4wySdyFx~m;2IH(?kg(ndTry>lxgtf?emQ zm?gyeq-#`_nk?%oq6uUKBm7Jl>iovMpkcw))f0T=&5z)}{IC8?eBrZ?@qhk5{y*@I zuYDUIJbN2`ypO@x7+5i6h2{uaKwu2gGFvsWLkHWju%HVt(7~eTZmb{R&wlV-{PZX9 z;9K8)3xD_T{%`P`zxGvp^rH(-H^B8X^-DZn=~;O9oq-?y=sggB?yF}z`*MtNg;|PZ zeml~0&#NBB?e*tXLzmmXDa1Z?g`ZFMx4pd4x8ILK!tJ6<>vPu}r5$Vg67rCtJ%)bi zea@un0T+OInWGVI?N`<8+!pI zyZ~frU4<=ve4q*JcYtVUc$%;w3VvR!r9nB%!-xmW1KsC$W0N0X3y0GXKp-S`0=79}_4(p$|P+n}-Q|7?9IT=aXxQi#X?H3M#fV1CRggzp48Q|q$ zQ&ORDb1=7#NQu36=Prm?OgYey@!9E3UQ+7zks*6yMTEy~g%a`en)dcF1~B?8JVL_& zFL~4Z{G2oN6hJ)IovQ<$FN^ql2sN2c0B<2U)(fA6V`OIhVpZ#aQi~sm{{}TSM|Cj$B|KRt25AS{OHUujMZZNRw*>O*JSn-va z;*>ok=zU;W7-Mwn11SvvqtO9O3m6CV8v#VBvP7daczQ|Q=hnASDkL;>7S`W+fJ76EFcvg43rRbv4V^PTJ%=H> zI4QYyWO1WH$D|PCU<_EswSYz3IedAgO3ZC1bsXMv&=x12&fG-9?w$` zz4{2|Y1g>P^Sk#eve8+K-V_t_RzT*uIkAr1u#Kr2AQ?2`>q9H*T3wD%T>-i5fb zWzs<^JE7nUTWqr@#nJCLyb(1kz{GMq%&VAVLLdv&!TMTt0<-x>>^9{(IsMgpo9S2* zYgh%V#Iar59*_=a>9>_Pp(Hi#n)h zd5Wbkdn(R!sPJrl9~ql2W)5OAZ=nyZj>b>$xkt482Tju7B9w%2LEl`#zWZP3l$P7sIvirw6y)U5JU32c7mjKNEr zDywSh{3aTv)=0fzgQ+IdCOtTL5^GLAH)IcD-$Cywz{6bFErqnuHus4$uGxjd327qwb9m%X`{ zA&z0*>$!cVG_8)ZOpy}Pcl+g0Z>>*$;o2q!Vm1vNm)kiL5^Q3aZ4KBpeO4GhVEPPR zBNL()FhB<^Sg^Dw_|g|Yi@*NYe+6$mIpY8Mdw+np-ufP{*Jlt~vGx;0y2(r_bjCS? zS>A?3BBEHNW@~Gxf~QAz?|0{@104e^2#hgKAZ9GmGzY{`#luY`gW3Zb`Ym)fmFjxa z_5jTr^0w>Md$!+~so!x&8S@nWu+b`iUP*FZ?Yrn6pKVmZ%N;F`l=*a9?pc#m9&>Hx z`QUbMZIbaL6G{pbv|UrTmrq1GqQ#Y<@dgnPw_m3Hj!3nGCP0%09?f1cK$Ng1i4$*Q zaN>HiW{R7pNW8r1c8=3~Jrp>1)CrqqItmS&92ZbsaQ8`@8K*YCJNJbhYHht#MSW$ ze&tJFz~A}Xe+iI*zyJ6DDZc*oZ{TKqiY_{|!8(!Xs^ZP#8VPu-&Z~Ebh z6N0Wn$({#rK)Fzj+xP60zLYR){nX6r!l8MCTx&GDu5$g5rnU%A>Zei|xx~D=K%spc z=Etd$UI=@mtXr%{YV4cRG$|zcHup0oQW*N2-gS>b-=>kz>1EF&QnJ1Lcb+RFBM_XQ zoWu0)Xy|)1!^Ftulzq(&onZHhAA&{)91lBfBbU9W!$%=cv$zPY8N_xygd zS#f^Q&-%Da9Oky4$EvYE547`p-LS>3h}6p6MmLEEPROVBM(h%X`-hQbW(JrC79o(; zK$dob@PK-~A3$i6NnrA0L6QpfP*0}qns~L3=Lw)f=kpx;U+%rHOuU-V5E>|zyVZ`T zT8l(43q-z|_WSI|0Cp%pQCcX<%$TvB7}N!_&KjH$W%?(M_iWSg?&<_3QzQiH=W=Fdag>C>XrS;YZ(v%m&=VZ_ z5x)8>U&e3z#+UGK{@^?K!$0^_yz|aG=yI*z>~g9kf7=PI8fcvul8$}3qtaHcutP8B z!_7|i0yA@X{5o;vR^$JjN8Cn)8=x&7h&;z;mU7F z1~XA`HkZ}H=P$jaq99BDd;^ubd0 zllM#NGqTfe6-~;yKkGBaISmDq8;Ba3G&EWwlBCfA%i#)-me=vxlgIe0zw@gY9r)8f z{Vx9Ohd)G@8?$E*&7~mqeH&rJ*N{2j@gyx9Q&8CarED$t!^tTKvf>Voq3l-)U9fDA z)9x}kWaxKXAh?qoQ6y|WKd=MyBv9P8bGUc3xP-rMhm&7Ubu_u-+l_|l3wOrgch2v{ z*S#1s-?L{uWVCeW9rwIK@Doh=#4k+3^Lpk_@b(=&bdARQmwbG?$2QWJd2ho~HR_e| zx}kvup_cF~As~{#crlfzUk;!Nfu*7I3VHM%-uR_=@C#r009TI~Z-1}j$A1j;pS}i` zE4bcU**L`Ol8;CfGtiVkQ@uNdW@n7+cD2EqbR4@THm;Ph-ozg|H@G^Sk$8lII5S%R4xyk~pR znR|9Fhw8jJD~6Tfxp3oJqkf{C>fUv^5MC!cc z?QK2gVS7S^O>s|HKF^D5%ajaR2GUKsAVp$khoafVBGxZ>F6&Cz$*~|}Q1O0jxWrl% zC>@qhKs=x^wPS8<4BOXxV65iA*_7cf3-3GHp_FZ%=utsUe2MLBIU=I#qLy(A=vq`O z=SN^Z;D{q09X^8BpS+2$eCc!e^}qNfeD-snz%P9Ib^Hgv_EG%wX9NGu|NUR!fBc{R zIo|rgw{Ww*j{&Qc))djn5bdN@m0e9pkLO{KEJmb#Q${V3^Fq@v)v!FWwHW{wLL{xJ zpqY$J$Gpfrl81%F>pbcbk>&cPOP6*$4p{D~^E;5K0RF|?p+x++*Xq@896vAHm& zubD)QP+F1}hn9uVTGdLueUDVoh8YB5%|xg+OsF77nzogy#_nTLNuw|oy*xu25k10 zV42ER-v_3x*1!gcU|ALj`n1+MvoksnBA3Z4>Im3uk9VN85DbPN^eC5VSO#mZbqe9y zlhPPQ#0^J&4a<0p!|^dba(IGIf9kXN>woPp;_v(?U%^*@^);LZ@WUTpgi@I+1&xw6i0S$;I}k8O6dx(9p#M1cD|- z0+4BA57I_Ikdq=5A=v)rAlmRSa%uSG;#+LJrT(8z8V~!MA9@V*Os9glaj;{ z#YbFo+w)-vnA&ZGwnsdiNqSH7io=EF$((YpGM4*bFY5%A|2lFr2^+h6sL&QF`UNE_ zK?cuf*0vEg+o~A>h%~KYa5F9Lxz6c7!5n+X_lSPiQlOb2Y%bQgI&S|&_lj!2BfJ{5 z@9ykICTRF$iAH7)3}*}}F+0Rv+yfwh)dB+=wf(iX&ssC3VJQ5|+@g&E( z&&eYn0*=qr%|cEaksEZ2m|JooYODy}qIT zG=Cn~_WdI;*9VRAc_-&yeTt*Mg|)Yzp&$7fKm?JA(rz&N;`1i(GUHMMEFn`P!MCH$C*PynXQQr=rw zZ4i!ISGRal`^XZaPsbk_17lqcZXcLf;tnj8jPwj#-&xWM+0MUjJUcc z(W}K1Zu+aS;1r=}oh*i2pz-AlRH$6Cv#RM*h6kqHGQ&r~bPZ0W>^jnm(e`6N=w)6c zksVJivbW76r~H;y6*#PjFRJ;|0+3*g9&M1B2V4UUrn(6~+t!LWH~A&xU!n7D=R?EZ zQHm0U%*uJQb6uV^A_j8<(*XxQ;-hbT2A})Pui(G>-~J|k=?N zdy5jW_On;#*-_YgGj!QZ?4QePk1@RB_c?qtfBYfNlhfxNPtQmRTC|q?jSGdARucVy zIjtHx_Q`j&x?QV1bmJChI)%7qSi#E{-Z^o=g!>8r;VlwH9nPUob3 zo)Uw*k#KUfDQbd{(Lr4+MO6>wz168&C1}PoLr^KYbhTJpCcw zxp@cIr+3i%GxKPt%&uq!loTOpDLRs^mkT*zx|*X0Fwe@ghc&qm>v`gY+t2Il`ZNBn z%+KluhKDDXdBs}q?!w*u;MBj2rF`{Fy%LC@#}&f%j!+SnH@|7ScDr33+2|zkKJdEB zneC4I_fkyGPt*CFpB)^je{9;>Yp-Z~;Sd+prA(0keFd#I03Xq~fr!ya!_XR}XyU~I zjy#2{SXxThxuvi>Q#fHrXd?p@p0HB;NwlT$+N3euIy$B46gNo0J@?LCB(sTmbGvlW z^~ZpW6&Ncx!quwea8~*x9otGR_4?g(e-P>mhCX*fOxr~=S5{vIxG>|d3JId*4j|T~ z9Uea(iD4>Z$ovAH8{9SP>dk&Om2r#;6LV*=;b%=SPi^8Vk<-TCRmOgXZn+p5ktJn} zUPt}xSQVkbONpMDvphMNj<2-^UDluzWSyJC5uyGDBNH*zzSn7T1YruHvu+Ht@h5na zh=A`6y}vjZQflaahS^HgNgr5GD@cIDLf}Pk8rOKXzK_$;EO0BXZN$zV1)vp)gtW3h z_XU$0VmF21`4J&qlc3FxUIcoR^TXy?r+#cBHnZ0jfYArYb1?K*er>!j3ov{8bt?mS zRb7>x&0oI5!v&ySH1a`XFRW~uzCxa5{&Ulq{p8a0&iJ0IZx5JcIPBM4)M(bfIC*@8Wznl)y+^j;-gt82SwGeTdg>Fze2BdmQA;zqZig9z^_*Bq`C@p4Z5oovF{4zv}_n z+;snpX&=IM0d~JUNB(`aC=^X2!jP`-;X5jHbK*Fy?M^ z93JhoA;j61$8(1*P1341Ttjz9nmF!dEkoF-(EC~UFieQSjrDaDd>WxN%`J0ZNE%_JGOa7Hv$93&@~UcLhg#P zxJSD0JJp0Hiby5wtq3KtSBeTZ1fV)!$p^}@FAvCu925o;j(%o`jk4|5@0tUCmSZ<- zPigU`@|pJ|Aba~PXxd~lFVHtUoNdk;SJ3{_-e!+%H2K_s67QJoUW0A)LIMwnyIZJoiqTVld) zl+2wF9STMzHiuOEGYRCDP>7oSWJ63CnaXK_FyYy{f{5|f_uj?7`u(rto8S5-PW?R{ zyPZ9e1q!hP8Yb%IBnHW%`^HR6+u(}rJQz`lA1tVMFy^R>vX@ya;_-xxOiKiS ze?AQTQcL@xGJg)QS7a~GR?oG^4{r zv~jk8$QW#nR+6y#Skd|dUb`}R*049z`K-SJrWWKhGdp=5b4@ftGK`TDx(w@&GeOtj zIW*L0-8K7-z4{Da2t-^`yDRgOaAIkIYA$RY8>FkbVby;Uxzb&@C@jr*nAQ{Didmjr zuYNSAyPU0(c`J!n4s2(ro*}Ms1V=zpa|e@#8FDjNUG3xwq0T9MLvEZHiQ8%C0w7v% zg;VG#vEZ<@xMaC@hnXv;Brwt6@LdnbxH=1GmzUZemol}L@rVnHl4r;Sg5H5;VKiC{ z7Ur{;awI>gXUB&i#q(VmQuTOGdv~@=1j~)#+BA%@WhONsL*J@Xr_Wv3?5)IX^))JAeTbLb z?|mB^)ve<u z=ifU+5eY&U3Q^%JRAR?nX=Ad?S`aiDz*s@j!O|@28-ZA}c##o)b6v>;@1ZwvSY3&| z1AH-MfH}OF=FKt@{d6f13r$RitwK7iolNxOB_l`CAZBAH#XZ768&-T}?T}Lk)(#rO zm@7<6xE6-Pz=lL7G7zD*rBGt;5%asQI`CQSkpnQ|g>(47on&8k<$C;)c=8 z1DeQ$5{GJie-^WYC2|3y*y5A1Poj!xvQgzs23VAnYu_S0O3E^l>&-;rySz=ono$uO z8pT!c$mi&F_PLIE+RiIjrhki_PkTDf ziQ4RIGerE!{LW5F*6MHG57AILND{k*92Bb#h}(#YiNe zbw#6VG{f4377z|#wi2n*jmbOXEsPo}cz0urHF1qf7OtsjMlz$`!7GI_og@Pq7Y@%H zXIdk9*_IKuVZ9<(nTF?-IdFoZ$xQ?#t9tg26GTqXEMP|cl^O$~5n-|`CdbwBc$T(= zt&JFSQtrrGM}TI@$`rfWV~dlPB2bGn0szDqtj{qw5VtH9K@RL)Akk9jEWEd>H{tMxjAQ>^TZ~EI81w~ z>Y2ks>72>umB`wn`4y0Rz!8v4)!Tv@F#|#^1fqPA(SEV2W<0+q`o5@^o&s*ncxZxT zL7x>-a(@nz5$hRNbYpTb#7$*YNrXX~_KO$G{Kw!%XuLqy9;u6`3JBsk`%Cf~6LNE< zwf~xkNb^Uu)*!u@nnJNv3cy$Z(Hab%E*R{Y`c$MQVYIejUDrtDQ-wQUYi7Pj9J)(8 z_s<%7|4S;H-+unUYr`bwTG#b08!73$p_gJOey}6ltGFHtsXxaU{o2bcwfFn>v**#$ zm5Cu3+Wp2@we>9wQw2Cl&0nmvDbZP}Q`OonFSRkVvEGCx&I^O77jJ28bC{MkOpqN8 zmpc=yU2_Md=&|0H-sBfhA;7Gu4I&+*&IQfrK%`dx!|SFuKR% zruOgZa%N|9&9$gkB9pcqt5N&}wsr$&j^;vL`~Ws3Yi-?Ni9e_*6DPKu?`Q#+Q=|&j zb?g9EK&ii>*%dfFAaDv&6~#qh&W@7t;-#}D0F6{w!jVA7As7n(j~ugW%pJ3~UDr4q zD|gPD<2E$~^vsm^MewLXMh>0Re9NALu5(y%M8mgN*^`?yIG_3A4Y4&71W1%;GPie| z?#;fp0dxRDdyfBzl)@t=GLpZNF_eEO5Ggo?g|>5nQV4 z2~0pK^u3y+VvZe|RzXo|$p!`i=~&3a;AkE{G9%s;iO^?LS)FU2-#a1qhuF?*YR|f~ z^+()r20@%d)h}|LGYh))`#qRyznz`&BBkTKHtz1*3tpGrw{_0&pQWGA-Iua{+l*1$ zH%@T-cHN)jnr)>Q=3ug+)ATb4T$oL&$GInek(r?*e-dDMw_V#+NR@gAnbVC!e@!Urwu)YbXB2$j13E1r412?A|Je7Cx&;IFm@VEZvui#5x{uI9Pt?%RJXD4(y zK}eIyP}!F`4i}9TErX#0J=j|vaUz0A`ZnKNJ6Xud!`l2#>w^rq17P?{Y+52m?QjOU z-%oqHwTAHyA*}~BzvKJ0@f=rdi;BM9J*d5o+^UJ**6-UYpuLi-w!7PB)A%Z5-Hh)( z9xgNe`Y-Za1MlW(|?hG`@9S9_%2BL=@x#E@&|HxFMj&7AUg4^JbD5vMUg zeGR4v_WaN46gN-`awygx{dcDueX{;9wYd6y9eeyM1afRqs=^R50f|do1qs;rpjA;o@-UY~uBhu^_(|JLX6g)e>@U-vZt~*FMvgfNuis%A3w%-%HHq7UW=W4(_Ybzw&DbvO4SAefqOA zo)^*VUx`372@D0dt}*HrdQ1rbsdKwFuKT|slf?vicLpR%mRYiT6aa-;ySmqi2++JT zS8Btf5HSvCGzwcd5ISIi+Qo$a8V=^)(!|U|%P*R*&yAP{6U0Lh1dj;Gu+5WQ`l=~z zhgMV*u>`QLCAuS?6RbL~*-^|4t7^iSfA`yixcd|C#8ree%0ek*nfipAhNCe@E_MKB zJFoe-x%AT_@Tui~n6&g^b8k%BZ4~bGHc}I(Es->a9$`qaN0ClkCJ;6*U69U;9A}O4-q^&x?kQAdV%QuHn}DEk=oSu#DtMa^%jVBgP?N1!{j$qd zOOEBRk!=@p=kIuYXYnffv=75yz8=m5%Fdw8=PpJ2X1wJc;VFH`wI@*AW>Z$u<}mzuwXu1R1q2=)^Et`{73!&P-^);e z#L~G+Um?9i))jE{p){lK2&o|YnM_NvvRP=pUTKYUZOWdU3~jK=X?KLSo*Iwy?qJNWS^Jst1Tj*KB~^KFq0*5UgH%1#Ypau26;0A9 zpfK)a9GH4frRNePF8w9QOf^#GbN;EfI$96_-sZ9-DU#0s8VX2J``q`dL)Vm%gXTX% zbeH4szAuye2gJVcb%y`$$0{Qh$ktyvL9k+F!Rd6uzx~7S;mcqAB);&4kKz|U`w2Yz z`ZM(L3@bGdxzm_AFUH>WunNoFE}pqCt0$J#*D(w`Yh|C)hGuEvq71?FZ9675Rrl>y zZcMFxsXnjUNY+qrDg<*+V(o$eZ9a3Sg8MS_hcKVmz?GTgSGzzdTbR`2?d(yBwmG#yAQ7<#U)m9JwAD4_;}1wF&$FB_g2N z7}=vRY9XA$Y^Tk*shSsCc#f&hxb*tDjh5XRjtd>!j;mP&!-^Gxb-lqizwtJ{^^JG% z8^7^6{N>;NWqkKLKgDT%3Zfe@34{iEF!6bxip{Th21qxz*!CJ$0gqV|L^(ndE~G)L zy6tae=*@)!J1-g=?*&|pF8TaoA0?e^=oex1&36}x_iapNg4uT%&>o|@r&c?ASC-GF zzSINzYOa^VEN>A$rr3@1xvs?Gwj>iTW1N@jCcmm{yZM&H)uJbezEo249m`xA%NTOk zf^!HVQf_uc_IJJ{<~|kb%gK=WX2mg{Mpp0wp_gDEIAM0cz-klH_WlaKB8c^0@-t1Z_%`D;!kP&hh zWi z4Uu3>tY8WvW&#%Hv~YW)^Z|?$de<WAi?@nB7{R zeWF=Lbk%845|OsJWruXbe#nf$4b&F!vVc6koEMF$r}Sb^Vs2{#iYNkbMjrUwDUiaJ z5+k*3h^G5gO;=qR(KVZx$0-EU5zQJXNCK}4YC`b(+JwSmP4$kF%r57w)*Sa(?aGKD zg5JCNe3&6w>y+;R=D@wW#>u)!MDM!Tm3tp+$NvpMs*boJlKp&Ee+T$+mvE@2}SO|9oxF zbM1jY&DFr1FK38i?1$ZnMxt&x6G!+NSB4lGUSWiQ+Skl3C`RlCk=nb$3!UJpU6cS4 zAOoY{Xb6!T!51_bmCD?=uNGES>{vEu}c z0Ui=&0rrd>%rTE59u^d<5FpVccUHwUY{w}C2zmOar5jXUDY4F5AG7AY;9NPFtW2Z{O&pg}CprpKkuYJEj%8V}_SIIc_%wtU zxe8+*ZrMwAwh(ANH#>l(Ek%3G_EcaQAppcqk(903Dz*`E+;`@rXUG>5o8avCq-N%R z5=@J&wf-*fnhL(R7$P2}X3ZttJnWGS4a4+bA!vBK>EZsEI1szLR)#DUWo!`{- z5q444z{Hrn&;m4db{odPJ@-1f%^ZvlCB7aq7d~Wc0+kopnQX@t$OU$ehY1Q8add&1Yel=1$D$y`%Ya;-Ss(`g+RJK^37P+6+NCm~;mJ-cEKPmbgk7C#9q$VP&<*Kd%viuUIo#4Y$D%V4SnZwFTUNo_P9rZLi4kS=wT`i z<^p>h^**EV4d)XVi+P*ETMpT!F`p0hoT0sN0-Ws_;KWY~0lfVk?gTV@e?IKpm%iKD z)?S>M$KB=U{&TlBmmG7R34^K<&h>rnVQCbW>WC>f0>xAqc`z%$ZH>iZjHqo;C2zNx z`h%iGFW5p-_061`3gwKK8ic9Xg~hE>#8ORXQ&P4FXCn`Bg+G#nsnHkbdK3|dUJFm zBFoU{{r0is`Da5jJV$~?wNj~!h!Aenx4@A%J43O~674N_5oz6^4`Ih!H@N4yWZJN? z@o@h47_-Lms;`&vjv2Og^xr9h6wN}%^v^pY3AN~ z$nb?hgX(8`V8Xkd@?6F}JKBFZy`7DuL#T%Y9t?luxDqY8FewgAL&L?$oUPEI5F#fd zG3XfNVnx;bW(3RM=gcQy*U4rZb8h1Wv@9BaMlBXdJXI?d9HlOve-;9+ty19FVqtk>V(c=7 zpN(YD#o^F3@qkNx%zm%`tO-zjMnls~59Zt}o84VZR&_oRTCmd!r_CPiVbHWsyxX$@ zs~+ahF*3&5+8eVVJly{6wq78F-K5Lrr!helMH}@PtKtI^CoGUeRa40-vQat9>^5vK zMpO=B2$qrJmm)VmN5()d!X8Hf9Hy4zpn-K2+?@11j~^fL_Pf_;bU+(JeG|w;UUIWg zwfFb3&+J@Fo*%N<{E4=o(T+l2T+oQ~H`zRj;6;(kA`k&`A&RkV3HM%JZmDnHc8fM2 zxoG>6c+8}EPm zj?MGc6C|MU+#_e1-QZfL-j<1C(v~#|0LZ;o1yq&N)kZE}yOpH$P;&nlccrtr-7wd1 z;sOw!C}!IZhnvuBeGUpFThF4y0g@UM%`b;TxITZC;9{5fs6DUq3OqSsd9*@S#_3&U z(ky}z$zm!4@zD|>p03przO+{0b!F^D)@Dl5UK6sLgx~ zBeDXKZcc0?n7yx`qZkRzTe}{jQ{iAY^9T|#Dt7yg8Zt?Dx7CfPjQwIbamij5w0Db6 z!6G1x;cIMkhJ986A^Tt_Ce9Y%fm;>DQd2}Zd%iXxi~+4aNG@oV^@*{&uS3#9PYM%0 zPJs`3hC?s7Qkc5!2M(>>P@gJ5K>Y~Fd{72fy!-A`eE+TY@cJ7c!xz8&3;5BGuhHbF zC6TEID&|aG;qgTUGOyQAj4Td?2VEjhKC{O|1w+g^a59YNd~E3vK%ka!@h*@PG}SL) zHuNoF-eqWen`%;*k}O2$UYPm#V78yhh3{VMdi6zBX!^a{yaQ6JI$%=8)D%7hj((OQ zZ#yp)O}O{4tI+7u`gTt0D}o$t4ns=%)hscU1(hTnj&7w* zOOAZD`7mfbv+vnGf$w{UIX{FIm30*(6$du|5H=HcB65g!2)sc?N|GUBjL|I)ea5zD zjomlmj)E@R&F4Q;l*5Z&Qv!QOlU^Ltzj(cR*xq5e3-5b?dE-@GyVJYbBl+v?{SEjx zn0E?dyRqdJA)1)^WZjJKEAX7QL`>xH67*Y@hjwr~VYynt)bQ+s z6GW~c!ftQ3FCF|%yXZy(6! zxwP9;5YzsQ@a*l5akB!r(ubLn@rA(P8nxxdCI%J_Kcl8ho5FkD;dM5_z{_6}JP&37 zB31^qzC#(aYTX#CX6KR)lA&JI!&A2UTDrra{yt@ZD;Ehs!}pkk<5Sq=H>-M{;3_9H z_O^wrRgKiwuF^LBIBSdhXEj`r$iu$Mjxx>@4p&G)SH$0`nbS1Hkz930n0%|BhxZ-k z3}(+%Uln&A~J~7lb&n;D=OjRCanqX8gcqgxCeVWO)@R;tZ@#x zTf#uF$uKGes>Qqa72o>S_wkv}J;s0dw|)b!z5WQ-Z+`%hffFk-(_o>Da6g#=_g~Qs zF#>qeJrn-%8+3U?ugtvR=UoL4LnmtVlP?F2^dl&!ZRWYxwY^9A)UD^cHo zodlOKlZ!I-k!f1P`A;$dj5`K;JXg%95v{QA`oA)VzL{$#!i??bvvVDgwdY6=&9oVF z$!9X#n27>1|IBlQ_}pWe5gH^-_wnovrqFvIXp2P)^lpsBlcDUygd-XHa3Mqt68HA4 zJgc9T!{hohLLZwZT#g{=fVgo!Ecd{Q*5);$QIrdz5FO9h*L2-|(ayu{oD z3gLf;Ux&=~*{A2(e8~t)O#o0g$_ri3$<622zCtzp);`SR-})2WUR2<6o97dB*FzU4 z=7H~e;KjVVbbH>guGQWC&UyZ7KW`TVsy+IA&MwBhJ0lOcszRRLosst#GZ)vu0e}{R zwN|)8m|xe=7W~1o>VtJh2{upR$^An#yT)$1zXI%YU<|WKpY`x%d(*NF`dt<6`u9ay}g+%GoUdV7bz6#DXq{6b#J;&j40g0T(!mv!o8d=U)#{V~)&kaoSkN zurOm?yP*gLa<~1jvZ=s*H^@sY7vZSRB~UbUDTL(ZUHgRoE}p|&b2-n~tJ0pIpAp{g zIVuZvo3vBRul3<*W3Qik5dgQ%Q+n@i?(7)$N+CQ1x=F5g>F*uq z=QYPE@lHj03aTTHRAgFJ8K!v?Lvs)Kido_pF6qk5A0pezFvQws90!Mk4F(p`-0-9K z7)vnB@axct@HDeBs!%C$&IoQlml$K399-}9{bosJ#GVwMZ07l|p|uu}d?JY2JsJdR zE1GC&RJuUS{*#zxC?tAgpyD&^H`yJQqjCt#SmAmh9+pmhLUt&6Y?;AyX zbP4pf8E_(7Chx*^4rSMcyANZWKHurs#n;CvzO-i8e0P6_e)ngV@^hk^<-Q9Z!Ic1n zZ(C>vsb$6MI3;&gT1!U5YM(Dzx$3tPbD)y*DE9ser+dg2JU97}(%+oFgnSVUdCa%u zoZK?q&hRrd$^?eZE}b?d@0FvZcjPO87nk!L=ISlKx2Su!NLv4LqlVrciop> zmq7QPMG{blS4m_r26=jO&2TuVQkygq4o8GQK#5Tw&CD$ZzeI@Sb05<*@m zV+6CgtH3ic^h*p(Hd>Ky?%Vvg=$5}Oe+IlFUVX3YQuyB9oa7K@NcQ3-<+i(S54`jF zCS$vf8x+t7RJ*phW4r$QbM#zxm0K@YpD{5ox-g#9CdeSixv9{0Hrl!8S|8=RIc=;H zM`JXoj0b>ahMMKGvbZ8qbVWR7s1C@ToTf>J7AvuLMCPeUd)+@(XM&c@?u7YQq7li+ zZBD6-nah~SW9k*YR&U`l7u?Lj?Fh63s5Q{iKo*uJ7Lq1r8i>%fvepFmTG!759dW#9 zblZd@>a=5Mu64DUyVmaY=$vsUGp{+;Bd>;ttuMv4^Jy%2N?8L(Nhs&C?nBy9ehNAy zHj8BHnF<@8V?6sNGBDz^Twia$ZUblP=Z?(!pNK23hB7jV$X0LW0(Ov zbD^%4kXZN{(otI6v&HANtamw(US2ejG{3KUvumhY+n-qdFj{jsz9pmgb|!`{ zpw18mnsjsCTX1}I#MN=Zv-hv@!Fx83o=e_R!RoFWPH8meouPFcEFL`_RZU6thy#!% zqVbf(=J?h}da_rzJ?1Eh3;7vTi6r)G_-NRf5fP0;zw4Tl9n93^emrMKOO);Jl8gDk z>%m}ok$mQlcLbMvw@HvYdr}C;<*wa2sdsAR7v^PR!xtDs{#BFuY=yiJLE;MWx-h;9 z+e;QCsf0izOFkyUNX^BWP|d%sA8(nT6Yk>Wo~l{CBfQSF&0v~T_O~D6b-$~8yOei&|*tCjXWU?0}ij#5wsj2mL!InF?ug+ zYK+`7h(#`QQhX)i>>U~wtI#EygFXwVsLh-$#@sb>v7hb)2N_m!awK7wSfS9wbM74B z$bFy+d)@!3F>jgqZ9XAk{&1mwqSgYyvXn(=mNCe(`DWTElMqqYbo!I6gs(&6w$ak& zt83A?MM0MIO^E^K`yGC^@4RRYyRb3b#KjZ6pxQax%Vb*o(l{K^@|+u-ZVxaoXlUT3 z_VUIHTGM^DEC&!Z)y&%x483SBc2o%jt&XDC&+qBrZz_4UEzMk$0?_qu=jX5RC0Z?STb5w%L+QFPU-Mif*JJ=m~ z)us{q7QpS9;6<3v?Dud~^1~mYGk_DF@FWf>J(+qX$Q1}cv3Dk{34|B4Grh9T?wsN} z+?fY55H{M{Zrn3V6j??T^ZIR>ZE2z0ODM}`edWSD1n14T0GjDF#U`Q>90{^&f15|T zr)Z^R0hWV?qwyi~vAd^x3IK|BOna)rCq@fJMr3po3omoebrFp(S8i9**^1c|qwJ|I zV0Bh1YXg+I_I)5Z3lloFIS+yzT2c4LEoVfR!icg7ivt=7LBhuPMW<^dgqsmPq?+Lw zl9~&@EyYT0feZn+Hko|_2^xT~YPfx*J=o#p+Wa|f(xNV4*APo`m=udr@b-0vF)xwu zm*T(zTr+RRvwxAsjXna6x4IDp9fU5Uh7{WIY3@pNEqHz5${^BR_ z@lU*lKl-I|lpUu|7!P zLV>(CeWndzzvZo{A%QQ-#P8$8S6*FynUihQtcA2`BeOv{*+_WLnVQqmgtGH}6wGZ4 zyDP)XZYBJhsbFW%Qz`e+G$zcDB(g6P);q1{{C8-UIdBBBSiaEO&lO@VG3If9GS)5p zo{Wb}e+llnF72T8+}i>in%T||AUb6DSrI5G(zcq;vPB%k88ozLjA`M{*wq2f3e(h( zGD)T$S~JE8sN|m~E-Gr98Vnw-*m5F5pTRD%hcZvWP+VuYRc7cE)B__K;B(sHe zva+pwFpc+I6AvNN9hq&9ilY!g@YOlZbL(qa-4tehANSF5I2@t?O+8~Z+F{S^nt!XX zG5Q=rSCyP9cr*FoL@<-chK39sV=%ZiG&+EJ!O{*mEJyI7+W4{@(Ut`ZFW`2-aj^tN zBBs3BITpb%2iHX!nk;zy=!j2!@-ZGgYWUt;KSMhZ+GrS9FsMgGJw@GyC3T@fA4`&| zKy!+K>gQTiLwD20$bkPi`dpC^qey;EB2vb-E=T1&lxOtbJLAynru9pzj{kt`Wu;WT zPvx)s6jQlx1+jZ+$>$x&$qOLu;dAsnectcgtZiSu=!Kd1S&Nfr(9SHJ*#^$n?98L{ zY!1?5SD9u?y{277*jOIF%G^~TPocn-y}7TVq0S^$XM8i9(rDzkB8T%aIpS4QK*iB( z%y|Vbr|_3RCyK?~dD*1B(PWeNl#y9HV?%byT?$JG86Qj|KZ4o;vZV7H$i3H>nE!ac zY3IUAjwWoJGQrmhuXl{m!M#VB*I@QWTtjP@B-ZW-NA5ph+Gz7cG}FYDT~p+^Vi}nj znAGocnxpQx3tQWKGZS<@ZAi;F2!LhLutfkMi(?kCcCF+ffWYs@VNk%g{ourVn8?9phb6a<)iVQH8I70yzSvzdmUR;>0kxlM6#;j0! zYt00eS|w4QKgGvFMA$b>#feK&2ia3Kr7^S)(& zQoH7W5V$Sq?Fv|q39q*WbkN%6n$Bkd!WLfUk)6R?Aq}g?%^tWXP))pRmN!+XQzIv5 z&JhRZkh#ZTBkQ~uO?uyqhDkkAW(+R0vgY-aKm9d#-~5^I-Wdvgm>Q=7~>I^wnEuHFwoGcV*pq! zquatAkQER{9}Pl769ieVDsl6V30Cf_;7EzD9g_-J0O{D4lBU*d=UNG(>D&$rc_RT+ z!-_~>gV`T!+>fD;ppoYr=DydIxZnkt#WvYNg&!ZFEGmIbntul{K)j% z9W3w8wq9-?0`JqNXR^HmY0BO7G>KS=IWNvlPIx@jt6?6c6a5Hpi`Z9K!6b4DnGhLZ z6p2%-cFoo3+VIGzO@CeEOOoUe=ZFDS0nlN4W_L0&Mu?&z0_X+%3=IGtSFNa?=CfBj zUO>REWDOst_gFJoX+bO1j(nT~Em?}?0B%RMnIEozb}&u-pqbvHDi>iOD_IGV5$mje z1)^~F_gHa^8`Z!?3|>EBbj!aEQN`RQ+C$1Px+U!p(CFcerS|!pA7-_8O5ao$gWqGZ z&JJe^q6B7a0Z&=VG%>)iH=45&6K8SEjYhpKK@4bIHw`WFLFkqyl_5A_?Pfxt8#Gpj zH7D6zgvU4&fED>j;n(}X%s3nlxH+A0Yzx+P#j>dnHhH*KNT~x(P;`Pld`vypxiz$9!QpVk zak)Y>=zUop;dr>hay;O8xWe&xH3=kRhK(|q zEvJP}LJ_>3#U#IS+T6s{1EW_OuBq#EKMft&hFkvJu5AJwscKX0uQB~m^Wtt8!fjru zh|Qe$c<$X|<-qeG7I)e9@7YS>;GB>jJzR`2G8(-`5sO67kE z`oU@`*-)~It`XJH`*SMb|u)+ZZc zGIC4&2+yE4{0k}&t0T7L)yUGrHndG=yrI==5|csZaqcmE&1~X)1eS);4xqykd^iB@ z2qH}kBLNfokd_c2IL19kCbTgyddKJkY#H4&i<{?MFHUV`$fo6Q=@lzkm{9pP-o}YKFj;#zLh0>W8 zw06Y8N7KUf-*LIZ;rIy0;}s5v1CGlT9$h^~qh@=`oGV8|?;Wxn!RsSj9gcY8@fBWs zo$&Y(<0Bt=jE{ffO?+^DgF}CW)A|g<_GFiioAnyPgjH71Siz%%$AI7zLe)(198!0o zMbsn(Lky@U^~p5Vyg@9&&Q6;5eT+23>5=D@D~$%+o1s6?^(y$p8O(5rK;G8IJ)GUb zai@^(8dfj^l@FPdJowvUBoChq3JqhcDc-jqx6R9guUmX1lKIW|cu2;1Kjz%l`HuXl ziW2ANLCj{NP1u3!{lJCIq?!!fc3y;xcr5MFuEBVj)oN3Pm^0EDVw+z0@aJGhR9sLD z5;%<~`al^Pml1wdbjmT+IQEJf5eY!eNP{uI))Z22#=Ms!dRtU8Z>%2E=EOzR>@~(* zEm=N$krkUbG0g>C?c=g)(G>I%+P7AJw~zpOSpAl!$tBbm9wbPhUOp%kK)A!we}?RW)l>bagG40}}@GeLMZt;4il4w0)PdIx$3`U%vl zXL|P=wEts`nQ@KWo!xX=lTpTe7EiPk1g$+ugNeV>69R=EP#6`&m#T7aTUQ{`nVxNa zM~$4uT*^sd?R6NjX2+EPKtsUf^d_!ZT|L30t1CP@KEd(opl=yE?!FF47Yykb zW5q!heBx6_eDyCh{K79Zy!U~iwF5r>u_HcsM!4!7>$>7}y209Su=W$4ULUZI6;E$I zz|DG%wKwo!H1j7QkH2T%YDQ}s&^`UGAw3-E1){98YvG=+9%ERN8Kdr&$*)BUUZ)I+ z9I{oQTF#T4U8L0KpKPF|y^2F98~xI?g`iU^z6bk!4n{T+#GSDK?%agug0Sz^gB@l* zhs4}&5u9{jp5I2 zvNoY=0N#lHtQuHFefDyv*PfQMWcy@`3=lNY34;067FnfCnv~t1Fjd~ zLS-+k=c$xa~^~WQSlew5EfbhvD%Q=MPKt!)RGv zHRh_Ms-^rVUd|ZhLn>#?!DnfJ0PCtkqKtvj^`CToFA31DfUo}Qg3tT{VZrx}x_JPUD1iT`@fB!kB{RBuq10MPGcr+$Tfxu-CN1DgfBZ zGtfZlGvUy?kd~hHl*G(vnF%S4D#fP^1T0&6o*S%uCaLY55x0$9+8OwkVcnN;$kf)| z4Ci^6dG>Sf^Vq>j3|<09zYWotLhCRe$_tIfeX_arMeX7F_+C4;pL3|x?H6qxr`>zb zGPvz9u9zj)X2}6+KivLZxGBL)qecKt>9T9Lsiin!j@g|YF#{=+8{@~ zFZa5_${kn)3mQge)!?}U=J>}$vj?(P(t#zzEw<<)jJq%e4IOizs6NyOgNH@|^tI!} z9mFay4pBi;oS6@`|2Jy*=$jp%|0Tlp&A`{ceuHm*Yv3nu3*LKrjhoX6>#5^rUC~EJ z?yA&mhz0IEqp`m%Z65}Xg@*WbcD7P?UA;!X_CM^*RCJb&W zFf!@6pyF@j`^aqUz@81g?AxsdzMCa!Vj$-kv-URH_Nv~zcQOBa3kEwv!IXkCAoz=K zdeHmsx^^M`J;6mT5sv-&nijBr#j+$mNAj??;nP%O6IBM5|m{wt6RmLtZR4l1gTRBs=k;`Hk-`vSsZnzW@TbuaTyz$zAkrkWoWO`C&xL*aHKhtA59Ntd~H6azBtGf$#I z)83ak-D}vb$1L4M&#;qXRB zTL`D8jQ)&4(yWl;YCselJ|UA9K4g?|FrE7$Y8!9Tjk#;yf<9KPU6Gc7q0tS-%vI)R zFb{nBOAL~MuYcnjfBeTM{N%@ir>7MkoIb#*pRk@zSl5m&18eW-SaDjP;dHvj+D{l` zg-G8Zr0JX(%WUhY#bjuTh)F*J9os^xoi9vSA}Nl>_BR5=3hT@ZlQdMr;xyCXu2bB3 z@%g&n&Y|LYb6=JiFJ0Rl>JQJ*cAxL(L%8khtG;54l+5iM)|K5~ZyRec#xn-?yh4#| zrivy(XzoX*q{Pl051X5;c2%9XQcIruKKo`AZ*B-+B~6L^##*BplPO18_tv~I7r@vi zRhn>vjcs~LzbkdUm8c2}+EMW63FGF5aD8npI;<{0RBE%PrM$kq221{z>VmaEu>MW-j-Yndx9aLMF1vE6zwP1x{1)VzN&tAhj-+v0M z5_z);?#VtvQ}=x9p4K�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%0ARoHk^CqzI(C3hB7 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%0D4=-r;7@3fM4V&)lV$F>^u!}k}N`~0O0 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 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/HMC-FAIRFriday_Bertuch_Garcia-Castro-md.jpg b/survey_dashboard/hmc_layout/static/en_files/HMC-FAIRFriday_Bertuch_Garcia-Castro-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3672051ca3ee6b556cf15b9970c56b777bbcd8f4 GIT binary patch literal 53376 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 E081@L)&Kwi literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/HMC-FAIRFriday_huang-md.jpg b/survey_dashboard/hmc_layout/static/en_files/HMC-FAIRFriday_huang-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d5d8058322ebd12636f86d645257b132c1666268 GIT binary patch literal 45795 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 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/HMC-HEC-2023-md.PNG b/survey_dashboard/hmc_layout/static/en_files/HMC-HEC-2023-md.PNG new file mode 100644 index 0000000000000000000000000000000000000000..7704d34c4672dea14f2b919e01b55a645896c21c GIT binary patch literal 75794 zcmb@tbyQs6vM$=VyCuPs#vOvYyEX0vx5nLFgS)$1fW{@kgS#~n0>Og^f(2e@?|Xh{ zpL@r6t<^|Jc14Zu~9k(U9$!2tkpus^`dIzSSD01yB72YV1< zKO|HnBt%3cbQBb1R7`YCObm1k3@mJXTr6xnYzz!sVq81|LLwp}OdOI|#DuT#35f{* zDuF|Qy@QB^hJ=Jhh=qYg`2YHO`2xU0h4X^@g#d>KfX9PFz=L}k0#E<|@Nh_Qf9?IR z2LT=l83h#$5e^+DH^&9QA;2RdBElmhq9S2qBBH>-Bj6!Y1CWsMQ3&30po*)1Aml`Q z2XYBXCVC~I1?2KfO)H*XAbxMw+(N_6!^>K2+((gc|!c}>T!5{stPSA+hmt2q~|hI(#4ak(s7JnBGHsV{fPJKt-Tnr z2@OjMAhZ>?IB1O^$S2n?p-?;|-?LD0Tjq*V~Qv)&F#9h>$W#^T~8fAx6FPK79Qm&MXYn;Ewi> z+z7xkL<30)qd^DOR_kb{qKqTzB9H)Q?fmbU^5L~vtNwl%yVOcBbdd(~%Os;jzO$<= zOJsnBGM=>8@JwNN>9095sJUtYX9)wj)wuU1#aD3~xdlIb@TIUUKF#9h~MJ8W_M=rO3!A*4Zo% zOg@ws^%;JBoEV#F2Ws-rgbgNPcIYt>7(XzaZ`i+ZP>Di_x**=(_)>G`rnB$jcI@zv%+6xwje;n{q`70GXgn*VAi%|r&d+AH!P8V`{h#cIF9*r^mEF4y-#mj z+oE*^ZphJ;_4Z?L&1KnzdEJ)W-N*HJ6?9*Pj(4w|-i+dxcTftSOlv-teP_90(DZVX zeE~EFn1r$DH@E9fT~T+W(1Nn!CB`08>T5(Zcu0f9*LPbzPCI>qH?p`Dah~^+Pw|Co zUmxgUTURn+(K=;n?RiN4*>6gJU-}yfj&IW*ddb4sTi?3Wdn)3&T;%5s2^EeTjH@rF zHP4Abd*__gEa7)Qyo>7&;+AZhGDHmN_##dt{8TvsS3<7o!uYSfUy8=<5JA&QmzAwJ z&LB>(CWR|@6N*iLt~t=+fhO6;TKtM$1~q4T%SbA6Y?bGnR(zu{LukhjN;l2%zX6tk z51X7r7G6jG<_o}z<@ZbmqX+t+M0yvu&k6SR1XdSi&sZzX#7)?DB7I^Q?X9%CR z8-)LE*T38T|6v=0K%#D#p0ZG{AzGkk!X?RlNwzzC^ zzXI5J4s^SvE2ftqPXkc>nQkodxKj^X!U9=(-C-0YE#y8*BOX=xel%YV;T#~V@HN#U z3QD24VLwXHL?ozobKU%*Qcc-~rY6jLbgVWXR6NR~iI^~BL~2zLD4LEDWv)BHkqNVJ zyu{R>cnZfEnrEe0e$cs12Ms!pr{86b!k6nTab7cR>n5-T#fj$i)tD@Gc)qs~BI^IB zm=y92(FE^`*2>f$HeqC9b5`|aC%0z6BaVinX+9N~n#UMjokaez=0DP6U1ZCVxm+bC zo{Uh%=*R6iN*8~<1~*JdzX8t*_s?VW4~t}O z%ICXGBIlqnb*^JU^A@LQO6~Tj)Q_*C+zip!Wc zO#i*4p?29@qzwDCl0)HA&x^*wW-xr#K%ZJv_%}ckC^EnW8LmOY>!fb*W#!skc(0m1 z+m_uh$?B)5Mb6)CO{C1r(YcQ?0BOImcW~LDCo)T=zB#$R4Im1J*VG)>_KB|RPbjJHgk)rPRsFVyLWgmQ99XG z=p+FasP8Fx$XhU)PkGlIsL0<|wG2*9`Ls9=BDtB$uTmINRp($JJ_F8V%7=mlH5Na? z6rT%Tya0xb_)B!^+=Quf=8nWBi0G${DnPb*;5}*<2F7>IanUg`$?@98Y5^*?4M)!3 zQl`N*k*CQil{OTF$6IvTR%vepRkeg{(1(_q@^vM<|ZJmjmlKq|m*$Y*h%= z1v0%U$^4$F79kMYX0!(;EjM8Y%KhZI@9${$hGJ(;Xa%>?y%)e8Bzl)MkxM3rP3O4> zAfe$w0EbUKoA`?XliU_&e6tpjl3Oz)0~u_vgtM%nQ4$Aw+KOTjn&aaf>GC4N`WVAx zi*&-c{uAQ!H))OaCwv@!wpvKh62QcWolX0#P%v@?B-9;U`ntz>)7sZ0=et!gb5a zrH5+(f5vMqHFnT6bZAMd>^IN1LTziH3Cdje=>qHfX%R#Y!qI?Guqd<7u5tPjUU)T|E1+~79jG|V-ewq&$K9I4mxw=Nc&U3z`J?u=oxUzx2BE;;(;-+_0H>kv$C9tb`GP$bp!ID%Vy7kMJp!~VxLMW`C>nZZ8 z?dHDCU%L)cwIUhuT230A^YfK86qx0Zas|y406QG5{3m<|Fw_&Hh~Nz~b+ zworYCVwE@I;N!z0r9Pv~S~T%O~f?fjcM6?XDXP2^6ot4?lj zZ#?R)=R8E>hJ1cA1<{N#wqo07SZ4c*(6LY2CCjQRK{d;(hpQRHg4sG108+W6o_Xiq zrmO;ROa!ltC1uJDAFe}lb|b3BSL%K(e<)AR=4(4j&tb0^xOLd_dyU|IO1J)s!3fmY z`k%wVUjh1VL6Yl~RbN&USSQz2!V4huRATE0 z-Q-Jy^keM{;C}C;adIoZPydc^9Np@P1y8_nc9jszrM3JI5B_DJBCg3&`ZAkdEdw1H zNUKt?X|)xz(5dica^~%f=O6{z>5}?#dJeFiW_R|PoG)`IFSbx%9?Hf$Y)HVh_W z%(G<@rji-8UHd2Y^I;);JviXYrq95*0(!l9c?%1dvY~b)`DFDXR2veifYfIL zfNo0s*%fB$;vOT+=jb;N=v6Q?rq+uHCjC=_ylmpW;j zSi8|ReWi|B7GB_Hrly1JATCV1If;7J@!323SSta73<9N2J1C&<+)^b&);nWL71o|y$u#eEl+M~LNTSjFM*T>I>#IXSYm`Sj=A41uuxYr8TC zwQ^ZRb7)2dL6@eN6siBL^*l-gBN~!CJfRQ6sw#gcD#p4Gt=Rhd=fX1VXWi^$^}o8H zrPGdIG2+I6ArOO(>|eo7#|32}!$@htrPB_lVuom)K&+C@p_%gdM+M)sQGJa6c1;B2 zZ#Rdsua0T{XM^H`P6?8JBk-aW{CHp1*FW;2vA=Va)2OI(Dr1Evd{) z!sEt{_6GIa=`{r z8geCGI||HK-~N96gg@Dc{ui`%dE!DQzYp}bA*~Cq|1H7$`;xh#zrJ%tK{vCLq2*7S z8mJa^&!nv4WIAH`u=H2{(rm7VipK4zMN1ZuH*sw8r1@nENmm5C zpzKbLjc_7L2X}@Y!ot*WO2KF6zOV?_bF?8IJ)@0@v1m{5X2GJ=>v6H!8x=BXK2Wa0 zrX(XP@p2}7h~3*`ybo?rRrz+?!%Ta)05X6W)yckQHE!@J=VF^2YjBxxw+$e!0r8a6 z&v+F2VW=P8{hHiPA2GCO0?jRzt!?Y(r{F=6LA9Eo$@pI_DQ2!>a9jDZ5}O**_6_Cb zR3oG9XCi+X-g4*&LB80EP;y&+;*)3Z$r^8>ZT`>|(hC^}M3lPdfE-d$Y_GG45tX-R zkA?29p>><+^7pE+b9NwW5o?Ec%nM*jUHmCy!Q`mQWt&BGYUW&UL#L+k9`Mtt-&N?T z^6Ulhnc|O+TG!H!`3r#CO6MtWf%53kW!nMvX5zWvPA^#XYtShMVgRmu0lR%z(~^wb zHxLP$>PljkT%rA|+fmgt${1^>ewz;>E$MSRf=Bc_?P%Ty_3s@O(~d~4>T_BMg6l1Rjt-}m944P|WQUv-Gg-6~#aAP1?+M7g+l9NeZGL$et8tNw8 zmuQVtjknCAIAaAO3-+n8so)I*`rF7g<6EdqR$6Q$=9;VQG=NzjcYkzdl1z74_2ae& z9frRE-1ZxuwS`{*W~HOqqb~pkr?W&KqdKlj@{=^5%7+7k7XZb<{V2s{UzM>Iw2@o< z1pulUxgF|XRc8`r>d@51;dpDw!(k)RUMQj(aO67(VVjM@`*o%`l>a*0`GD7%_Zk03 zx-wl6efg&}o=SY2Q)<=s#pfs!lnf=m)x8_OO}*Q_GTz)4?pfO+Id~LWxX;L$X(5Z_ zs4&nQG1A^cQf9|5>w`|%OZnuFy`$7Jhu)&ZW7`jQMl>=r_4JXt6Hy}ZDVE%U8D974 zrIu9mwk;AOoH1A5%#;)ubO)paa9o{?SM0Q$oYApGHIuXz&B@1C_8<)_<2)Z32|qz9 zUI062qjSg4a_5^Odq1iJL4w-9|8T&jcaV%tQ0H0LWRYlIq`nr9S0s`JdBL#x0KRf!!R5G&YO@a%FjmQE{sBoOODYUS9ZSVA*R#@jIcH4z zk7C-Lo>#Fj{Ju#o(uh*Ce`VimwnZDj^rSTH5qExQCsa!{|Fb6$Kc_ElhY_}6`U9k$ zQyLx(;#)}5@x5$JHAz*UK#Dy_H~Kdm0=WTjDbNDUlRq@S5}k3>e?SL;PHZ!WgYM~v zAEd!^eG-eqmQL|TUGR(cqNJ-BFRbaCp+jzpsI-nAfNRC_hiGHBcVa@ zyNbC7Yi=(mop%)TUnTYpZ8;jPwjWTRW1yq|^o+5qb}V)6%E*^5?(h>kuk;)X-43ay z)fO5T1*fQVW~o-C>e7CH(t1URD%T_`noW;(+Ams}ZTVEbD;!A0`gseyrv-NFq3tqj z@pQh9FkkVnG#7(7Ly;MXqgR?Ov1{Fa*8C#Bq_}3(e5g1Lx|TvP&$aw$LvfhFY!$Sg zR@QpV=OHWgnEeyPswxlV@_Hx{hfLI7*4Dp8Ny`$v*sV!EWm7}$Up;^x0%F9?YV8Ul z_?W4yh0&FFDq&qCqL9XcGHdn4p?>3ncc(qRfW9@u;@KzNR;gX$$JA*}EuCAAUe%mWXGgS5`Bh=Hcs<8u&@6#XAw#PvI@MmbtGi@vihwFat127rLbE5{lz+^#Q-8H9y2k@{LoRSGeFLj zz5kFXu)|aO0ZO*ZEni$Zk!L$HMMG*2Cm0sO#HjtnQtZ{|JS^zBy=_~BFtT+!v@dMj z@5GnTc3T_A{zya6(a(%heO}(iN$R~k05dW-Lwh9Ue&??N&>eFJ!NCCCoRsjE2ga(D z9WNm6d^AuznaYREs#Iv#JlbdjX~vPw#`a{I>(4+y9NrzP%aV<-R60FFaV~yJ+JHhF z%Ndne!WhIn2rt2^ozp7r@%wr2#?$=pG*FlXMK<=Jyu>^G*cDTBPy1 zo@Wi1b4iU>B=r>4$9=lmej{tAON1^Ui&1?I6N#coOWK#4#OvPvtUg<-(qf>W8e$(z zb&Sd%w%TLf#>rHQgxXJ_$G$1H1+@f7%yraO^T>xm?kD5mX)kO%DEuD-8Bb@+3e*04 z`@M5q>^Zl5=YAdxep|pSa3TOWa)|qtvqpp@S&^AaiH-4j2oH`L|YydHytH6Z3 zVF7tXY?Cc9nhpHb#=6HWH2 zbI94d$Q^>@nilal)Xr&tobMyQi!5`TCd|-_;!l4I-G{m644vI!x=@9QMD?&w#x+7|_tIVcLw(D-(06=D>2RD7rnTk|$q;Uw zMTR$kfZcn?3-rqFY7*BO_ie3QKTIPOcra$S^ z3(_|jnNhNmIdfknqby$^jGd1uszXWad-N{_3d7!%26qiq+gioE$=c&SXC6?O)sovG z2bEleW^5*EM~b+7klNsErLL1ZMPbYIy}8+9$PhZat9v~ZgdRQhoGQ$b^)|tM*>1oi z!gx{f!?6VJ4(j#BT?MGu=iqEqCH{7rcz;ID-9IpO8)5?H4Owr!KJc9>Am__SV7kk? zM9R|Wo|Nn{^?D;k)Y-l5Eaz~VNp4hA`%ljN-{Qg9?{K2G51jF^?pTyEH zkK;cgMS^>L4|!*QojR}NNrTIITo*DH7>lWKAzkEZHtEv+^1g^!&|0+PE1@E+)eqke zX7YN8iG*1(2wj@Gc=}S2DuTfV3o3U;j7yyFPBInxI~k~!beWU{GGWO*IwvrR z{(&WIPpo0*k=<+2Yg?CzALsQt-8*gn;aXq4JrTu8rIcp1^KqlYH864_u&Qb_Cd6V#bSeRjtEX*hUbF=XbA|o zy{06}>LgF6#iJc6LQ}NB!LgOUt6yu>S)vfT|D#jUaJ#0qzt))dY$81tDi2K2EU~DT zkr%0?xbcy&sPS+#&xrj}(h?~d(@n>0jEPh;KjX93>dhJ+Uyf~hglGJpT zBL2j|LS2SWW8Gc_<<8bRgkgrlcmce?&aS@SjW-Zy8({hGNFNU*eR!@nmxZy=osA2I2 zH9e;wnJ}7tAEVfIk`cz0GRyeG_m^0Cqw2uqMQw?Zn#(HKae_tq(ys(M<}s~UUEQhl zj#rhn+_Am))1&k|-&ACAaiwUkPpi{{%S1fY0@>3-ZYCvt`>VMP%*VQ#{PR?8LDCDE zrc`$WD<9U$%WsBEi($9)w4_y}>qkXU=N-9{Q>bzX*?&!8 zo8@-Im;jbq;UI(1da`U6J3ELC|8x;l;F+vczYyCFRA|@sI9_74cRjnWgB!*{?lIk7 zPOM%M7?r1^|9Z3mu4MeI01`!a5l1B@^-$oH`^m9f91YzN%`H0;j`{&%)LSmwtZYN^ znsPdyLwi1$GJ2}>dmffeq)U7TecPnC>b|qy5nRq$`9s!6z13Lb@&=AkVoN_INO|u! zl2q7JpfIUR9MwCg)j-8q=f}_h6Px=w<&i$X$rc>9s(2%(l`CW2RJo|a=r9MUT`hhL z6q*CN4c3H1lJKG(b*S{*I*wmsEwK`m{XE@!H`S|v4PW$e;LvIQXHp;1A!2S=At0#u zb0S@EpxX0H?AeYCeJIi8;5o66>anou@W+9ggi-?wevCDFZVZJQ66190v@qHa2Y4#S z!B=V`y34hu=k2+s0>By1zR7`ZR)bqd_q;EOA_Gm_H{VprO}UC*#rucW46*iD z8!kKjF3;F?Q8s>T7Mj)&I2oQ9GZ$#_N9bY&x%xVt_mo@B_ocAuFG=lA11|t{&ToJ% zN)MT`9daYV*Y#>LmN|)P@B1}#%=$gho}9bHsWeaKmU>?sGy zXi|S8z5biXmJ%~gxtCaNezUwG~B0;qz z%~H5~9+Wm=Nf$plT!cSSKRTjtI^Y!pO7~l*fDBX#Ln?w81yUAWbAvxd=M-}a460!{ z3lA38^dQQs7>@G~bSr&ANG8`ok5T=;52DGxDSZsHMn4-1dw3lVUyLVET-;1ETWiWx zg6sq|9vJh$-&Dmp^UI|G?XePNS!h@r3QgOWwOhW8YfxX7mG;A4V{)e{t~`PCMq?^} zI`7g;q+!DY{R2Z$EXHc_97eWKA!};of7b;?J(lPkmAXW%zjNUE4t0Fad@1|80Mdfgk!47?Mwfq#i5c{qiom0wy z+}`iRuabDqJ$W&Z+U*#sRkB$bO+G=~W7&p$yM+1{H@;k0q4^`$=(NRZnR1aMug1aP z%;X9AfAE4nmKU_Gphkm96&xLE(L{bXkwDOSeLFY);Ro=PojH@AcK!|aO#dD1nf)N3 zwV3PjzQ1Sc*8E=t6dy9eilzJ|Rai?VIzRf=u)rf+DqmE*Lk^jTV)?~-$VLAs^SQoO zK3)E&p5bGCEskh=%ZB2z6*c{ANw~gwtosxNmq($4bTAmz}kC^aFBbW*_t@x<+Y#CQmB37*}(9J*l=(yWMSAzNb~64;N}`!5vm| zfC)vujkgNoJK+B~#$-k7XcaJ-=OS^J`o}!$?5h_lu3(fI2jjn7r{`s^_JSU9LM4-B z`${(7mG+faIfDW~9b`IgX`75}-ZW}}gKzBH&erm0V)eXjV{zln+!D>a@UR{IX`)o$ z^G8A$)68Pf1HsQW4Gu$Qr=e_(7JK17l_bb0 z-1i3inh5P!X&P>H*O@s;bfoPfG+UK}1w}wnuJ4G$)nd0`0>jRu^R~S_J6rJP8wPmA z!$>uUu5`20j1OX7)JklyxW(*UU&p8Y4XY&{#Q4j=eGy_aAu2Qf_D!XRou$wsgzSjr z?PJ;SzS8yD2e3-J$nviiF1o<(?~bHh)0I;M8M)~=VH>V1WN1@v%Akxn(G>@Ua9V%4o7Gzbqdsx^YbbF7FpUH~J!J4`PCxx&X())&Ch zW%;2WLLU3Tj7Sc(^#v&fx}TmWKRxy=M2tC-i!zu0@09L4-~d%Da&#>7XNm7l5nf zw?87>y*RLtP!$?z&s)YCq_`@g`06q)vFkcmV@r=QI3&P#SBTt zM-25}FehGbhm_jMm8=%DT#5wT*%>a?=1vY&UHT3467roU&;EEg(ZD=sDXzZjvO+de zRGsiLOBT$w1^%j+K#O}cWBCa_H8hg`q^ft9PUKjt?Km-+uoug3yV-5Cc|tPsu$+q9 z9E2jIaQQBn*{aTlT!xdlMY?UzED%1YG{*oPwR@^A8binCJf1X;kwogvRMv?od>j47 zQW0u}nO2;5wHtH0#)2KAUGSERs$t%6O#%p2S;7#T#t#+h`-NS~N6Or0!Pc6`Jz7^< zmV4b?EATREs1Tp;Q8J^ou~lq_bUa`GaP^LuAr>PTsrFc3PAe&S(L%X9&(2^?4dU|N zfM7Y0#zCvI)~HE+fx?^FFUdL`YA!OtRoC=gIoq;TS$n=mE&XZ>{J}+8Wo%>Etgr++flg+kI_81`K&5 zpl?`wC3hoI13vnf72?;n2I7^^sRhCoJ_hcp);=n(WmqMRoa0Hxi3}`h@Bv?e_tSid z{mdH{@Pg<^?f7+bDBO{EqdOSULcVpC4J!iWPlxQS{vw8Hk+#)-9@J?-Dttnjle+L5 ziiO!WNU{CfxOa`Qi}fy`j-yW92S1U9`L$=A{5_{whZ*KttFGj$@H(DrMGUZkCc4(Y z$AJvo8(%d)YU8T^)2Cv4|CqQR7FLp861@P*RR4@+2K~DG@jx;9TnKquhW+c`YjP^< z4|O0_i$hmUXp2cyHDite4v(v0kCQ}*LU<%lj;@WSLpoKCx?tg2&FKs^^xX8G#X~^? z1o+94FmRB{#5Q}^xYM99pydor)h-rbI3{V{+Y>FdO#=<)hRzyl7z~}!N|dQOyG-#A zPuJOJxK~)Ib_HU%8!dxH90XbWuXLlxX-Zo5Q!aN$jj$>>^97Snk|k63H0$T)7BCA! z{40`@(&${X7})`xz(TGB#fw^j1U5flLCduf@;5_ve_j#u*sd-ZkE*dc-etPV>o9(4@55_ExY-ZnHBZ zNmXHyDyxsD{y%n4Wc{Yy)S6H7Mw8o!fvFQ%EtDD+sl1( zaZW2`(#pibl>bR~Q{U9Q+*(g*_G*rg)_N>wr}7hfY=Ze*#a1RR?|_}YjPKaWg;sj0 z9v=6ne$gLt1Z4WO)p!;1G-;?6bUx4(8IV185wVgjw^NZ!`WOY92^BX=Y;CkcbkSJp zt`qd&fM3WZg>@=*?>;ZT-oJH~VR*m9@yWoJhzQZI-rVL1U*t(zNFbFf&>oxgx8T6( zhm{2G{FOPU;F;mkrgy4$eJj}t5=;vj2UWzITFT>Y&u-H1u2Jxx1mb%2D14U}J_f65BJorK$sv;i;)r-lkagVS<=gPq_?hXeTDN#t zw-h2Hur-)Fw+>bKJkZ&Qb^*5+RQr(>hP>xr>e zeezdljJ=L;%q~JU0;%~^^P)Mho68;#;cV>k)G=Bsd6CG2H%!ACV}tC1Zsa@IMx#ys z~@DU(U4Y^XTIQPDT@9^J!&(T zb#kIJ_0aJ7ae&E1A*;L&{6kjVlH z@B3Y72?`;x8t<^AwH5P5DFnf8rH?5-T$GW>_U!A ztxXL^o@nbWWPmf#bGaH1A{}*r8&G(|xi?FJcbBli^q^DB^C#K`yV zeCFdJYC4IYr2lCvR`-PQ&`5mW~GV|7SjH( z%KV4>bSev}D(W=)t>Ur(Q>iMprh;y|Pl~#zT{1tAuicpZpQe>07zHo(mW~ATnDr zFMkQEwbzfRhv|5@D;;xn9%0r^`E5WMG^NKZy)yM`YHX9}Q@~TDn zGE2Iunq)Cl961$yU}uHv@ejdtxw0lMv=V%inqvek9S-yVwq=aNdr179^x+b6Y`h+y*M#VYys zLM0fhEg7g{Vj^qWM?yA%~4`jBDN ze@Se&QZ(gs#8b@|w!T26M|*$=DPlX}%@M?Vw+p>AQb`SAbY;JOvtyNcpuN3FEr4;2 z@W*jjO~8Ulnifn&@w2H58(*T67lCmDDn3ALMky5Bam_~tG+UXkRAr&f_$34$l7 z<$!ST$QsEjdn=VItXy?gC9lQNo^e;xh>|3_Q4&jU#UT;K(MgzB!a!6JhGTPY?_NWLzDg`yB#Qd{myA(BSa)gdS6Wb`$4SOPJ#l@ zj4+H6=D=uW?liH*c7)Qijs{zrL^Q#>wwY^(b`qda%rjwH0uN)0tT?50TuuRU|MP`O zhPN^`LTL`Lo8qM4-cQvqa@f`HxCgOwcFeKuzu=nKQs8Qm>9XEGvMTx zBX;F-x~KvMgxJ(-Yz8~FW(9q5Qd|(>hj%t`@2Y%K0cllZKqbuPu8V5sr{QzH4X7zqYDofF23X%PXYpFx&ymnq+q#(4z;M@*!s ztaHdMv98+|c@9|oG$2eTNDrwD#GlSlstkJIXgMVSd9x?2{+h`n_1!w>j zI}b4yXeR&9js{zVw9w#f8mVdo8#o?fK~=SoE$U7H)zQA&%4n-%q{VnxU2&f$G;(9L zHwH>{6Z*$CEl$@fY>{Q_FJKE1;v()htD^8NWnYAGRDs*ob1ZO#kYtRQRGxPW#u$#M zfJ!4rN^uJAXDq{C@To5EU}Yx5SD7ixJw@d->qNP!MoV8Vogj`|*!wodbhZUR4VF#O z>~>vumL**#BS=^qXVw;d?4RMl;_qh{(ek{Z%nZDv9W<6_ zL*Sb|FGz|!S%ZrzC+ve{XZN{FVtX$zGZrIo z^T*N=f91~tqe(i11(92q(412cBVfflotyFLTfl>j!GeCsto2(|QYk5NU;Y$SV8|Su zQZ;_YW`(oJgx!7!c2PY`uoQ!&EfOyeX`*~GG=QK5QG8+2x-qh=aXxHAJZ`aKtvm*r69=@uO4j;x#Z zN58z`r=Cj0mY5!KT@rV?VU!^P27F)?u z7nstVf2`2ukx@`t<Wh8iiqou1u=sJPdJH*XnC zVmP!+Y-tl_!5hk*bM1ZP;R|zWYtxsK>9uE>DsBU$a(~>m ztggym=l&-7RqFMl!HfR={5Oxb9Ep67VMJ;Ouv$T#9RU9$buW$pUZDyx3*+s+@teDr z)E|xq+#B3Wn%M@**Ll@m^!_vusWTm=GB{Vc_Ja58ehvo?=%IcRYdo#NP!jma>SQyG_s0bMt734s2&}#Dj^NoWKNXSBysM^PUr1aHo0(V67ef z1@!4@PKQBNt8{)VdvUGwea+gv&$9aC**@jI4CsKeKf9zXZpJ)07R=bTwoyo~HY(Wm z({${Q53(``IVt%O4jxZ!A)`4GePg<$;%!HK*!nG`KUu7JG!I zf@JLSMYE44zm#U>ILs%zpMV1ACBAG34}CPw*Y}8A;)NGTHu3K9R-t)Vo*O1<>kmqZ%OofKpLHpJ-cl0rrKD)tyj zKbgZB!MX@5OsncAU^pf(t#T~MKVUc}6->HjqSwc%QtKGPMGdXFpw-Z zsOR5NjqZe3`?6@&mnXKK!6poH$Iv**4sg-F8WDBP6XVm~TJrLkFpFPLwZ{H9?YNz9+4Nut^7)=AsqmiT<4kMGB~ocO&06=51~eZzP(X`5azv>` z!tK!S@$QbW9eIDX@59;!(^s_BZYplvm^x~FJ~TF3d#z(iWp#4uou~1>Jch$YZGx<= z2cHt8+YyFI1FQ|Z^7x25tPvfZgWcl$Kh$tL)L4p-tVQAbBviT*1RmpB;BcUv@f`{- z@hd#o|3}s0c&tGc+#)YMpj;lszs7YZ;Fzvhxs&G%$_T50i(A|GK3whLe=~b_cw!NdRL)g#zx&s{Z>QwnFiKQSM zLxjaGpm&IJxqFwMt!1f}UY3^X+k`w^jJOzDc?D3itK3gu!p-KUpn&0?%314pvBSd+ z5B$32A5tN{nc5aC>ax3=OshQk2`H#wJRckyx?}CCx!_1b3W(NPR^P`l|MF|f6HdDv z|CLumdug>`(aNt3h_m7#t^KenZh~_n|71eawC#%Sl$J!b2v^FffTm`C$dDrnIE@Hb zoPh7=##UQIJ5-k^(n!AnjA88B{{9++Pb+}(K`@eV?WD(O=MQpv0Gl@5+lgF77OnmF zKhwiC3Hf%~u08QH7KG<{&je-J&6eyl;_M9c6DBQ@eSgBi)dNS41m?b{EgEv=7=(X;^FOdU%?e&pKe ztDY9FKirvW%e{)K*{=NX^$-?$#p2#^C?WDTFDYL9g9@9eTR8sv?N&zadLMhGh*=ys zU$1xDIFb3_9RiVYf~BUo5_~!KHuvxr{AQaRmW(c@jAxj_vpdGe;Yi5Oe*l%9Ki?WZ-#b5xDV< zJW)#T+J9>_5XSJOcI%}!vA5F}opNJw+gV7M2uk|6qIcZ29eH%LFk$(L08Q?^wIWA) zQJ$I}m|~IN7RwjVW3!wn{G&cFf77wKZBA5X6brKFXi#<&MbZ4!7~L6X`@_ zkK=I?x$j>9iVuw6E=jkUXugjk=I(sP_ITi4PPbKlQb@~)W?|XV;jT0g)M_0_t45SN z{w)7$#yYmIHK;|$YN97Sc4xS*e0sq>qQyk|Hzp_eUH_(Pb@Qf6rj=e><^RRkTSmpT zY+ayea0wb95L|-<_aK4b?v@Z-8g~n>!D-y-1{$}?>*;xsm&3eoE>j(a13vWDP<^LJk+c?czUu5;b_OwSSnWl&6uz0^ z%skwhT^{)WUH-*qAW|*L%HAnp6Gw5e6G^5^>WU+&e3=KXJB^*0X$H59AFwp#A={GU zvS@M@U@$UGPj&utHETP14d&-C26 zSTLiWZzX)CW6Psma++MWwWI9?r^w$VtBr zk%>-s*Nua@sPG)w)OHD}%PCXQVySIB)7XYh*~jY$qEQDyP~%P+P64#p@T^XONKPWy zTl$s1C0_kBhsR}w)l!F}kI*;g14?91gMnNsQPPEnZ}a5Pu{B|*UGcumaxQZg zRem6IBmSbAyuRa?1vU%`J#KU44$mkoSze*KD_$&vur%=ld>=ZW;mo1K>*>rGNvoeB z!G8LM+hPHElEwjQ!K39)2ud`AS{|jLY}OibM)*XuKeaA>zLm}!PcP!ac_v?J?BC;= zr5npkOZBf4h*Hl!R2I#jsoMsXz}$9ttDq__TiTapUhmOYP3#3w1L@Bme;59`e+aC7 zn>sOj9vKN^)0v(;rJ4Rh;)dk{N1|6>H4%%XOMJvy^k``a(h5Jp9c!x1COqjKK z5}5OUensI?aRoCY*5|XC5?zH^iLH%#r~C8#aQ&&M-VE$9mhUrpx9`VOTC8JLdSthf zU*Zspk~t^RP-7_HHP2v8?d|Z*hg^X7SdA2!hHz=9$AwkRUAQ>)w(({|_AF{^K4%0b zI5k*NFLRKQ{<@+CShGx_)g$M;r&vqp60DJ+>O!51ILM~sOwynjxbc*xWqNZIG%1cb z-^uLd<*9IaE6P4)5FH;mcgs;Tz@TFgyr>yh;sh8?K}tygv#@DMv`~jJh-H#ydHjW7 zUA7h|Smmw=$XuP{VY4#)$?xR}HW}p|Y}=X8PrAh8kx~}dR!JoYgzB>>Q0h5%YoRN| zbNWdj>_i=9o@b_5C-rnq&bftC%6E`oQL}e=F*#)Z;ISP-{{6_lO@}>G z7qU`ZtuB>r5TQ8Mg1J|Lk0_#4pePqy+2d<>*J?CB<(LCww~O&SO=Pq8qw%`ic|+8$ zZ1W@lVLRf<4p9^SrDEZ>zn^5b=To85{gIO?f%Y<`Mly!nWioj-gtkz8CPqoQL?uBlz7 zDennn;9fBWHwIF$P=^F`yI!1d$Idm0843uy<_LHLo7}yA@`VX|BUF9@waM;gcoc2L zzq)#xnk@iP121Uk@&oJj8b_#19Z}e-)0bzHojho02m8b^)c1e@6Mti|dMeKfy$}N| zwg%gZ9QT_-XFKl6LsmBz?hdRH?mGiOdvI=SS#b`A1SYOv11d@qx`N@}smhd#0A-mT zL%o5FzH8i*=>15qGi#O8BEF#a^|`*lk=n@B$uF4;&rk1xMND(vApF#hCyoG)Sh#imG;4^#{6=sYNn;5#!mJi7nfDPJ*-Sajuq)GEP!~ zyyah2&7;a8mDD+)Izxk(EXW?S3cwZ(#uvXX+Ekox%QJErY3yb?D)g4!!ecB%t@gdc zi+_sW3_1?9cV@9}$H42>LB)oWX#l(~J32MZ_zD%;nmc~JwgRQ3< zW2_>mEsmqU>kEjCJ128%L=^mFGcBd->V%Guw<><(tFt?m!pU_C*O)2$qj-a77M!vS zC7u&6guP)HAUL*RQ8;rzL(oy}L>>Skh5su& zGBWZfg>NJZgx~eH%qBPJ^)#%DYksAZ;vr*el4_rOvkfnm>v@0sTA%C~bupeSfR!&T z9GCo}w_Z-e9O`g@!;+bpJtKUjk-7+wUmA69<}Tt9e7vJHQ@E>?R7-p(>(JSB#2~^Z+mO8m+)$TPypS8pjuOwnpde56IlURA13*wES)wC{yfO2O0(KV||4ByY>6CTCyH}4QTFq7$`dkm09bJ51oJzDG z#JBKXa*F|yV4X}YITpm2rPjJLjUaXzR;j*xZXg+^8m(j}e|bG&Gss4$GGygik0r}M zo4@bdJzGSek>bp6)1Mk_?zyR)uOs-t?rwtxNu!}fY&InrlBXQ}_2)bX-*mO!IEG$p z^P#~jM@)b^IvvZFc~UiK)5sax`vIKIv6t?+T9~%cwwB1z2{ZCw>ri@c3|21DSyk7# zMuH!2GbjVDS{{NJTMWf|$MP62F>bufe;OKCVPAB{FpX({Otl@`yCWYxeht{(CWJar zm*RsPzm8Zapd@H`X|EnDDb0e`>SRCLio#k`0;{dbuj?1KD;=1RIY=K|E_Aambd@iW z*|!5Av(PV)pV=959!XiQl`M9vVeBjziYy8mIyzdFKTv||rE&)LE?RxD$k&C>A1nAi zDXcF}*)>L8UufkOPDW{2&o8%yW<28U3Qux6+s_E9%yq8VDptm;n4!9SeM4otYUc5n4ZSQ<>)dcKjLR;Gna?uw9GM)9{cXsD^ z)PFTw1t$irs~3x@A;$-NrEWZGF!)h=dQIiuiIt5mCnfdqGac2icar(Y5hr@xz-7^4 z?c_~v9@wK=^!rK`pq~#@-B3ToJD!==LczH%wg#t%kP9!S`Mu@9iuTV}cd@`D<~Wb; z+O|%Ymii760lAfR=aYr35UwVXLlCIIae)8xx8a5+lfj${qDU`*!5chHumtjwQQaZk zwDW=Ubij9hx5Uez91|q8wYgYmUOlDbbK75|tM*5{g@XA|)oc{G8ft)v%8jBeG?krG zaj8@kcM7Jh5|s#o z`;E8C+lv&E%@t;2K&20fG9eRt_dzg@C&J${3d08KKjo-dx9BoM92JuSuEQqu+C(~8 zXL;(xy_o$8cm6_PdJVrO36g&fE+LG2=8yl{V+JRrl4`bXSKt*QW_S2`&&GJEe2L|# zS3<9mgp}aEG5f9eE4miWwOh&}SM4q{t)1-ZS}!A?qav)s6|idA_bwNNs8+f>eK>VD z-E9zZAD;!5;n8}86)&VCJ%BmoIs?tf$CKy+5|=BsY?r|ku8k!S{{FZg)I<(v-LdU|`RlK|4HpAdF&Pun#@QuPe zK;L-VKbi?fa>{@!t-zAbR8V>Bu1M{ZOyHYN6+-cfPy$MCE3fA)<^6-C`@yB?$H-64 z0c+i)q+oAQiCApy%Kunz)Zib|&6L5Caj3;%eb z*vqmSKcUOEUe9o2({x)g7Km4fw&o`ZmwU*yEw5mYq%#)Qa(IGg@G3GR(wyCj!_~^m zj?WgdO>{zE-GkRFE8AkycTRMfrYO|qb;}+O)YA%-e~7842`Po4vskwnsi7zTqBp}8 zMYq5|b$wvl3MJIw9o>e0?VhmH&+wYw8uvH$1w!YD=O)B|MQh|}q$z9ZcF4k5SLL~_ zJOeg8oEgIxXAvYuH(Tyh7`H&cI`2_l)u(rEHdTrW)+DKcCm(suLmNLlo@2y&4bgzU z5e?-gt-m|M{=ExFsn5^0_c5nB-^NwTfMp6O76nI(EV0}edw(G$PjF5?8rIJDNC)(< z<+hwAUxXDzrn&CB4QmC&c+@S3>RS{yS&6mhwrCN2-MGttb-zd2g)u1VXm?^|B~N(J zNPO}Apw-~)t|Dt&lkoz+$*FGJmhUw$#gh#J5?HHc=#*VD^%T7Ql8Q0*LhYAaXBr=- z>pZC07nz}1?-ouc2LP-5D>;lsIbx$kUcW9$^M8MU0p6q^=1r@xu@;qrDj{P>Eo?qo z%UN`#lfQNsjk3&qG3qNV7oux3RvXTbXR7OKTWm+MMYMh?am~bc!lqXMyh-5ZI#U(} znPEl*c0 zaIFYFf0RaJ_0Z~k0K1-{+U<*y$@=IUEt|&PjB?F+VbrR1oaPL}?1zJ?*u0;hx$n9VwX8n@fgnML8FDT23Ib>tLI--bf^cf=DK*Hc!&8d~!s&-KS?MWD7t zCztz9bsb2+Xbx4GJMx*YE7+pt&DDxQz)iL+_NIOMi=%s>y~LY_44D4G(cAZ%T@la7 zHkgCD%2}EXP~njsr(I3{>DkZes47TkUz>x`*iet)CH1_INjm#9Kk^4D&DOBUyr?dc z2I~!)JNmoV6?-eiTA^6ghbq=PvX*!r_#ZX80McJP-A~v1&&S_C@pZaWrW$s3Z8RB- z{CIO&wmWq&89cbo{aabfD{FhSWkA@tc)G=aSSCqB#NtJh!n)p-VJOnaB#qDw`2U%;btvS%EsotY)+Ry)I^ePgMFwJ+Hv?${l* z``zjoEE+yZ5?Gj_BZbcaQR!eZ;6_dDD~)R|3Y|&K#1HC zH1L`M^r2ipEk)!D>}7E1=ctMbaBCnHP8c;(`g{HPU}uQ3A-&OwVR5<&x=M?)f4v_)xex`LfwK$7YS88#_%JhS{fLQxB76Avon9312lL zSyl+&rIF*3Njr)?Hl!%ylZNSXAba?U=ycePXtSj9C8*|N4H)l`a#mcZEFgxbmC3ju zo82sxe`G6pNBni*G%Kj7%T=(sFPpP-37Up`s5h`9)AWwY;$zRpnD+AC^45-%Atfza zCfB^s-QQvw`fsmVdW@$nrHf{oI$xzy9iIj+d{Bkzj2s#Q)_gQ$#Vz*%N+**rZZSn5!Xny3O9?R&7~T6?|49tYsjNtA?~RHZw{zYzN# z>#@^mzrOV1m!@G=_Ro7lO;tJOHNOltgAfplVfENsVydUhUI&UuURSAho=(481sKJs z^vvRs_jTcdIEq)ysF%pL%o%fuJXpwOt;)IbeVW#ZjIb?Iuc6c|o`Qfxlj&aEz+6@p zd%z?#S3eg#UgWZ@J+3zz+~6m9DIdq@oTYu7Y2Pvi{st>(n)z^DFaU+72%t(Jm>2qb z?|hjEYRq4zAA2jnC*u>g$$mKfv-+bGPxk?K-NgGNQogE>d9z}gNop3nk%kQ^@GKw0 zz7^WKh)W4mV^I^)ZoDXpXIBS0)MCcfdki~(E@!I08^atd*mR<|C1)mcbjyQ;33~-% zM#d$xT#}@rJXG9P+WFkLvT%C@qBbz)&?tn8tUef#4Dq@{t5($Mm}|(NYI6X z)+vWYh0W=>^$85zdD2WzU7Q9g>?Dqny?m{1$fo{bjevj)dEAb}nSK|RBq8hOGSedQmf+OTs{3GSP zWMg&0KnMmzf<6Mj29Le}4l~4}2gPG=L$B_(pz3=p9^UzdF_vM8p**>hA@h0c8?29F z!o%Wkj!7#I@)qP}S#Um_t*Z*gu2hfho?Q>UGHni@2B{aY5`y;JZ&oPMS$f?fIuc{jG0a%m!ldd7}pm9!h;@=9OX3r37 zYiA4%0LG_uLK`>u|Bdo~zJZ^mZw}AiY<^&Mjw@I7hXC`fl~^+j7n?F;?l@wGzn(zi)pb2!niLhw6)2 zjuwk?sX>)hhE*vqd2qrfAEn8)DmX{8CUw6%W&d)!8(D$4+md@X%Co(BV7LsD+!7i$ zFMHzAhTsNd(id%&O%E|8PG+no^Gv+)m^qGZSHyF_>ldc=uYp#atpK337G`*OzYWZS z+bySsVP#QcusjCm0~M3ec5v^CcTMc>%eC>fc(3yn7uf3R5C4}b=NHeTCSj(sMJBFc z`a46BWmU{x-^#Zq;3t{sr3m5ZvM4NfP9c?B5%vPs1O#18SCkL01i_W*1%Nx-aM^%q zt@)Of%1Hr+`p5Wbv>tJuU2ny9`^^DaPe|Qt;F^lo3SM0yhpCF_N+dub$$-IZw*0kZ z=-P^4j6u!J(62YI)3iRRl`+=4Vi`PuxAohv`*95ia9`Wls!(*L6y~(D3iF|Y;nHhJ zYYI|oNSVt^QW7ftL9X<7*7_tX6h3DwdGQ%WBTW)>#4M3vb!3xq=Xk4MN(v?fo4R2Y zlX}wyaE61O+j+y;)Mq2Z@wbKY;qxRYk2~L1Iqp_dS9HQ}|3WakFy?Gr(WcNg%$%LX z|E)Ij+hkk1HAd-h83zkUE^A{r;&O|WIJ%kuMMwcLmsp3QBhu~;iyaH&HGyl@2L|2Q z5?!^S#tnnr+K!l__A~2mnyYfGN_o>BK3z1Z@JXsrD%yg}G*(vBlJY-`@2?Uw^VW4z%L-EZ1$6Z1jS@^*0zSoE?-3wB4gn6g%JA`kTS zVB8cVAdW$ld_Quj!~{(%J{p#-ko+yx`@*1Y8#z{3H&RskJD&g!FFPmZ6i}n#r82M} zbsvIP**}Y`K_Gx3k;Ig5)?{O+5zC}m_0+P{7rbv|#1EGE`Gp2R{AQZ6=ZsqOXk2oj z6YqVlGaAKxm8alyFn2Li7>YPJL31yZd zjg5nirWl>jE&R+5@Xhrp^$nMHSss_UDR5fRv9WX;bno7n&U8*#HXLCsXIYR-X?opo z6=P`11t*8STzM_~8MyfD3@uo7&GR|(QAnf^A@hdmk@llSnczE1hu;dlh}~3C&37GQ z)%PYFryznjqf3G1QkNA9q^91*fx)rkm~HNMq_||i=fS=s+WK|#lKLWVFXwvV7;Uh# z-tK%weVMiIEym;K23>Xz_qa;D0%K@avZv_z8|Z-UPuOFbOg*(sjBRufcXlUjCq|cv zDTr4}P;DMEFZU$-CrF!mC&&(G50w#+ZM?B?3huL{?!z@^G{O^fkbs!#T1HViCwF?9 zrE|Yz`tI@$2EMd(;#u*@Nf^Xv)$J*caAF4Q$h#p4tghd8iXngCMGqT-r?Y=3M*pL3 zi~?0l+gUNQ)j1Jlf9001Kb=SON=rokpQ}wKyHXmzIwIlc@n74x(XypZXU^u;=?d|m zmb?63M7pc@m;ULEKh~nPSc{5ikAprUlF8!PPQJD2ZJ0NJfbPz$hXAdK8orfTCKWx)wQ4@A!Cd5S^IP zqt4DR&QO?i`Dz1_GKEQmN%GWZKtM<%v`j%i+fBg;P)?~K(M3|av%sS6oJ(8 zNNAXyM9WZbn8RD=c~S%5U7rUl!X{~xfj`Z1p!M%b#fv_fqd5FM_lVT%OxrAgVib)8 z1PaCh*W(M=uL~&1(K-{=q@G5n;c|-k`1xM|IVXkz)D)tkPufuU#rzOQ{l!dQ_UK2O zx8*8xn(s|czu38ATFqajHZV|ZaWHTtOjExUpxhy=su}SFq8fm{HNCt#)+=4_3{f;h z;|t+ytQvm^DZ9>5EUk~=`F8!TY=2fqG+nd*`zitg0}F;6IsyVU0>Y3+GKEZR@nAQi zjh1)m_5m4d%!q^T?GD(K*07HtHwDk0IA&U)(uW^dv)!j(b-}|_h(Ka(r^>_&LKcXjZd1H4vVP&{ za#y9H(!Wx^(Nfb6e3r|V@?~bLF8|x+xf|2I1 z80b$3IL{w^zF-J-OP~>7Hm<0fvup-JrH182YsH?P06|QmNod4e(vT8>J68~OFa{1r zNBa{Xa$4?55Y^()W3ZO;wD4Gt)5ubq1)L380a8Uy@x#t5e zGfoqfz8#U7mCmLP)cX}5F?Zqbt%%ro7cxPhrX@RLAy4s@8vCx3z>6N2d9SI4%4x0M zW36OAedW=__dgpw>AQ2o8Dv#$K|zjDI9}rS@XdhiN1B7^+A&KlZ@?*2Ha#^nlT9lK zrwe!ht>yND^YLFd-j!a5hN2=itZ5*sZqiTM!MlP}$PFEcZ@Uu$^ZBz%6Ip)KZ$N93 zd*F<3n;KF)WORiHd6;^X>K5Zu<=MMG<6%`vxJm`jUTdW$Hl$>A%PpNY7|xWO^$WZW zTn`Ys|E-mM%gRtZ_~7+QN-H;cwum6FSNWIQuWw4Zn14F0?WB5v4`c$}GWVBUBnPaC z@BB@lzf^8S+S)9Y(h~loc?}=L3IWC+>No(7Hvd55xcGNnB_=$ zikZ2&(?z|FasoKMNg|8>Xv~uLwA=DE!cc`j3#fMJ9lY-*OqGW}F|a44x$tNvfKOl6 z2z)S}e+L2^j39hoD>D#C(;r$&zxt_qncA@9g8PFMf?$y?rNL}#5JR^BQF1dG@R_8^ zcZ(h!k(yHBVMVz#b~4oev+9Vd^4_P265mbv%|KjcK=G`>>Vxb7|G!A^S$>ip!TJAZ zzk_H>Y;u%U`(T%&P7LNKwxFiYah9HVA)FUx)tWY`le`7@yeN_G(8tWHc!j{*|1&XB4Ap3jb4em< zU`t}M-DE3$luL)}Tlrtg9U)`x%nAK#4DjEEm_MrIa>@g%iuP#Dp4IOM=s?(TQsRVZB^Wo9^S+M+ridu!I&efLz7rfsuAJlLY z-)-z1bbPx0@^9PbEzP$j*-c1@h)^W~SJSWJe<8#%qY#)H9rFC^`Encvs7bn7TMSMJ zQxcdhYWM`b_cs1`&TAd)^0OQsF8>tWnm9G5N_sn-$}Va>8BfY}*-dUc%SL7w80Gxi zTbA0Edw1d0MktNQ0yhmcpoL!vK-KN>ClbgX&*zMna{f2Ezui=}e82}-qou6+q6bVa zXFlJOUGKMJ@qf?~wQ<9zaZVjB)+BiXw_VZM@`2oAjY8O*6*z@NvXlO_+~~?>?!iH# zgDBS1kNtn*;pSfX*Eh*AxNFXUqx*jXqP(?wb(0(Y-@LeYT$2MWO-XHAi^{=M0Bs*% zW+>pHD6}Cm7i_sJnf~A8nCrv5@m`Z!uFLXzU{2}DfriT5Cuo!R8r?Y&t=zD2G2;K( zDJu3BH?FDc7vh!g+0vfMA3d+(nD9y$jrQLy%rU@Ld7YX7E-*^GVnu3={S_7DxGX^B z7MH+Ph4&%q-*rj*@=riK5+fSRKJ|Auol?XLf0ToFSoq;z@X)MRSn=B{Ww6zy{~ZMf zg@AqPl{=Y9Ai@zLmFx9-wb-kae=w^`s3TQ z-Fp76k#NGW4PO)Lp9P17tgKI-z}bLn32L}o$L{XGi=BDh-d`sK*G}N?{Y85KGFrag zTiuR*Da1zjF7v-k4fvgVgm)FHYbqQWRu7ypkR4sk2d7TZ92Sc{;LuGDX?v0h|L;E; zMP-4iT$h;3lFf*DjGH zRgEeK->lrHOIdX5mzYN8$=vW6|6}q38fg>`bJTRo_bh64T=m^XPsEfhbvIlyXo3u@ zi_ER*p7~!Oq5S)WK){<=shP?pyP&^3dtqYrO@;s|<-pKJw4kC~l#Wd>cJIH2O6TmY zq1V}t>HmugBZHX(w-|5ui^9BW2H_8Cv(8%;LSHB*^JG~HG5EhQxGo5X*`{mS7<^a9 zpa>MeQ2&j~0durYLpLCDJmp}c@ZmE!2%<;A#u4xc6BEk6WvsS1s1uFu;m{s|jhhN1eSTmSGp3aNq;<=#i90=8R~SL&jfy1|;iTb! z%Pjc=k?RPH@jxf5vS;I86x*TlFGxPSzyWw9x+(F{ zUk1WrQZ`a2{PU-d2Dn9STsTG^xywkVs9}fBreoWH+ko^{SWZz0+q|8T8%oqY-K89{sJ(a^`%37S7>aiCaCmSN0Q%oGa;d|P>~SZNy(R6!Our>t13aBOry*kY~A z>=zwa&wl*n|;o+Y*1b7kbT;&?#@}o)(zTu>u zyPUb+23Qz*PYX-*Hj5E3FsBpvVjF84nT5AD?0zl&+EX!eV>015@SFC|9)3%0Z{jP< zeXUJrXWqN#V~BU~JU-dvD7Q=~D&hm=p`K}}f>aaf^BZesYNr6NY{)s7nP!Jf&-|Tb zp_5!wY!3UdupDuZy{*`7CC-eAys?`z^NKd!(XzUqH$xY;sVQL@ClX=@BV4XVGr0I# zE2}?ROD5I4T)jeQba*)xh>Wwqag*tFeNVGePf$|m|J=CnKUrIx${U9w@6O|SSc``-?hsKo^vpR- zLsaTf6;yC-euPlpm-Q`6?z<6ll<~1=dR_}~_b3r~hS7jpT74xkBL^CVDg2!pviuf4 z3HMoXbw945BvK7r%N{EIg%C-WycCc#8fT$5%2+U%bPWL5v#?}1%fEp0wk#hq-8QsN zYUCA9WM{C8*-YM>g1Wg>oK!YFk;)DFLNa$`%ug4rGH?J4SQuX0BOg_AS8uM6}6mkOi;(27@-O$zi0bmA}MFA)tM`dbfsh{sF4JhN?b z*h_A4oQj_)b7>Sodtxo!gl~ku%+uA1UjV*Uf%bVt4|N$Ah#K2kE3Fhfj$~&e$u8cn zKny3^#sF|gaZL@*DL>iJh?`x7L#jLLx|MMu#^|KMNisD&<0@l)*35T*;alt~CM}s- zR>C-nLF_K?UON&RMirc2gedVH?PnY}fAZ?|1vgffAwfe^ONo^ZNb74E^+5)CzpVfe zKEtAaW;kS4*q2?$LTl9M06)#po`C=*2}`{{qHwGl}Igx>BRRI5lvUSN#Jl(B{{8&4x<@7Q`UY( z+j8>;k4Kzl*0N$ReyXOuF>I`7M{j$YXc--a$(ES4xi;whTgp*u0Zm&P)5`&7Q$R6n zGpFfAB*MOxKHb~aU&K|(E!01E2#yoUSf4FG^~G-ExFtRrbGz;_L=CqStxIgq7%a@_ zSgeQ)Pqgr$E^OlB-tU2wYb& zyCj&=bkI+Jo0}(^$}(lm7=`&jU>OC5R1nwFnFa^vw=obb)-|hbvd4wfS%HMORILny zFQ;x12{C~uiF?e3mG8IMSltd*RJ`U6tL2@cEjqzu|*Mi-DQodTZZ1ZH+t(jG^F(_lwtWO;9C;y=fQA1hrhCyHX zx}54;6ntM3xj~N^*Gv;$ta#U`9}R5HDJS;?gCF`D)UYry);jj_opKKb@~ z0>XEMGp4t5H6wR_QVd@$+#A2)E@$VjL$A;iv1<6F%hq9Y9V!Y@>s=YTXAX{m_%#2X zM%uoYS**LJ{Ccg>GWPp0Ckil}QE;-jtLeL`w|??XM~La_4TveIwNA^f&J#xW94C{-QAu8o_e>1L`n}iZ3y#VKV_L zN0x@MJ3?5wl_`o{$Hqe&&fuFwm4%&MK_SO5jGkcGXRIvXtaPx06}VQf9Pzc*$6j^@ zy#e5-aFwhSL>~nw;0O~I8*7w_(_gRFt2tWNY#d9a9T_4ts{Zd)#m^q4B z>vE`w`wKyQ05Ys-wY;SSkS9>g#4RY1pX3lb9xCy|s)nk3$eyA5ZfnP#IeS*@xnpC1 zNB=es1ljz6?;E#sIr!p7*#%|2xF>sgmFDL=zC3`lbKM<`-^AyhH{M34vwljbDrh2a z#C4g;<>y6c`wW|*3c+$z7;_^WV9-9RFz;)bB5q^C7B`l^Xeke5-DS`#y)02jsF>Nx z(%FZmwAQO+_bk+5zRraFRM4f6@?cSsd` z#zt3_{!sA_6gWVZ%-3_3v?V`@;WZZ5EQRsz=%|4`f)}m$lHnRjipu0KdasYk{n_iB9Q0{KiRV zLE_5<%je!9Ujs%tD~~ZuHA&hJ&ATtQ5_aa8 zK9Rui=cJWIFKtx7asVlU8i7DCf_(x1`8Dfvdp>rsMEdn=X8|obs+AZ(Syj86icoVj z#&mUResHS+f;xnl&i{_Gg$Ls)4-nS13YJ(aCl!Z-1b!q;@C?8UN%*TTKS(qyfW;gROl)mcI`bL^0O zxoNW|D4q1hq`akm3F&(k&3Wv4MNz2cZwlCc<1|$%Dy@B?#t(w(E<%MEBU`|?ta+cC zo#NYMf`Mx0>AueITTO87fd%M+@~Uj#qK)!E)6u=Ew{)$WdP4| z=fd8)&Q2puyj3>ScB-;jp)KQt^f~=|b@VFUu__(5Pdv}4cGw$q0R;nMsq?AGr4ld+ zrs}S73MJp%sW^aaOB_2z+6>pPRzf5f(5NBi7=RYeftq%k>FfU z5tK14Ysmw9CpV>^_+1dO3i?8$$)z>BPsH+Dm%J6^C_2G|KA3ib*^?4(+^8Ps$_^9D zkRNt5nJRIQU(#Q5TOJhZe@UG#belmI((TMO{t4oW8HeXoq(7_O%7;qqdow&HhbvmV z;@pj~cAY@3>gu8`cFAY*YytdO!FzYSDR_Qf{Km$9^%H)d1e|wsYQz2m17{ldsi>Fw zqqcOoaF(!ZMC)G&I>$=eoz*mBHm~ymt@UHXd~9`!80v9JTIl$R$I~9;v`^8%ympD4 z1JO2di_ZgqO7S(baMKeUi1IHht84v~I79YEy;F>mVHX1I1aO`!UnW? zUwfu3&x08?fT)bu&As1nx6OA@NwwT+-kam_e0w$#i|P8N-q_^1f*LP=e`Aa!XT3po zXMv{lpw+H6CuBH~)Oylc!Y*Z_Z^KcYTCeq;H99n<41>FX5D;uu({bcn=JKuXc+S>^ zKZn&zQQ8*%X^0wmicwhTAhn%mG`dI}-%oV6U>XE{CYg0$Cde0m*%J5#exJjY@k`t& z0M&73@z*9K=wXbnxV;CG9dQC=+G{^4m^@t?-mN8z)Jeq$uLm31m5<-SZE5rwev91 z+QT^&K?^6OP;p8pll`83!0tA#!ABZ#(pdei^{a}xHZgAoiF0ZZpT>sHz0C@$?=;SC z4kYSC_uy6ku}x*oIIjPw$4ttkC7#aJg|dOeTP|wC=Hp`TqvGh=ybB7tsu}X!!U%a= zf5vA;g*^*#)6C{HgKFu|K%|UAMvRvvU8MtSl2vwhhA1{npHkuEzt2<9JxU)>{;^7T zm6|eXqj~hV^m}Mt)AAd1LHxOZK7TQJ(1WxQ%ZBuFcmcaEgtOA9TW2v8l>XuDJ?F+Z z6%evb0w0|>EO-34$uW9WtF0eGY9Rw^D&(dXe(z<@cYNNtGWD7dwDW4K?q<%zyfu{5 z!AvuwSscYh>jD76-4-BY+W++BU0z1v@_wYHmEW!RomJnY2;rdJ=RPt4nu3ryY)ZNY z!we>Q+RxY}0nKR)o3fR2+AVzso-Dos#Usa`M%VqcG@*DjvM7Quc~lXMcl*8rH{Q{p zHgmZbqNEMgBX2n-a+pl!7Tsmhg6eh>Ptf-34kCQY#Dx0Z-en0XB+K0us%WP3*5p7q z`NPo@K7)wqlke|I#VDlo<$LqNzTKh{o7jMD{?(U!yUYCeE?eCA?|0Z6b zV=G&A{dO-FD*?QJpkbmG7Z?@al}>w||UuN>jz4&GpUj{*k6x z*Lj(&!I>k}bPYM*ZDv_o{kT%6Y`G$jy@Ho{v^0gnr7*wr)y8hZYe#s&Cg&tr8C+Ga zDPihcp-9jQn8Rl6Mb)jhZt0ak^7Nz<3ALj6IT`Wu6aSAE#>N?UzDE_MCtH`YvbO^h z-i7Mw$C!*Bh|P&#(>;>WrYyV5l_Z$nXI93s`KsJik@^#}@?`F>Ov=F z_gjCG~zp3^ihi;9u!M**=IIZO>;7n>We;*{y3i|xp(pL zx}mmrvW1PasUs86LJmeKXxBfG|0MEH+!}+<1c;%X%isqVlESio8HvVGb zl%@CCYWwmA^(gN`*zS}?#uI-LnC$0k;#ok7z!-bZ_h3~sfoo_un|d2bHxlmG_l2Er zTz++Gwt{UrKxel6#)PGl?907**v*n)K1=Onv`vsoEqRCm{i=ZbH#-zq#@ zEVrO+)}q13F3jr3YVE=D7$>-2;NM693Tg*xBM|#~-VabV*dN7p!3^)J?1VIZ4qk^b z=KY00-WeYu8XY45`p$MUUp`p|lOS%b%lMILX`+{*fB};vG=-$5QWmInIw;l+1wLNeYIIyoa(!?WjP~VThvL zVGxnXw$q9Jn%el#^Mb#Zq#sigMem#bli(`0GWEk(Qk2rptrE2hd*# zC=QN~-ezBR>~fCW6|R40EZ%%xWaNi^xLEOMJ=m_>z;Oy=FV%1CT8>tV-7n51X|)`~Z(i*01tIoxVw-ONooNj(U6W zQtgZTs4Ue}GdAWayMcDe*pE~QP8W|Z8__$K1B;8UI6{d^P29t@ilBs3?&S8f5Dwy9 zZ;z}W+14X`gg_OG41gx@Ty*|2l@tF?RH#D@D+|*8Ojz@hh3wo9yEk|=n&i#?b9ZFU zX2r`Nv~t`^I8m#a=es?LrC=Vyq4@EOQV0_~N8S(tN^o)-3M#4P!{U%_(Ampkv`}03bmJ#`=3LLMX+y z+K^oof~ryxMIH1xWf4erDp+74%$t6-ln5FO_*!?Q(b|70s&bB3WTXiYK= z(wZ!dH>OADB7*yq_C<@MMsUcR>+V~0U*zY%ZOa3g|!68 zp7Z>*ZZgPZ6Byhk=Of?p}6y8|@rfHAlX>P&x;A zMA&~#!I>T|*@RCBQ(Y3w3g^xd&B7J7^zZb#(Bq;wQFy+}hZt!UC?y!ZB7gl^c6fn~ z{wz>%FN_?UO7)!sqb{z!99N97K+9tVJjFYm5Yg}biLkU@A_rqxD|QpT#x3{(7W@vg zM?%-Yj)UZ>^gbtS8leY%@XiIZQ{!7mCiLuHlWbc!?e~YELuY*RG6zMl)~7w{!;r0f z+{u}VrubB~8h01DM|9}USzndM${z;p+-Usxenh-Gs2$6f)}lPil)fz#iLo4l=r7Mz zZKt~ag84&&nT$s|swxLBWQ;}G$HW-j`$YloE*h3$_(fFXX+5FMH*gWHMDPSUunqp`fa6AIzBCCC8jQ(_qdLhs~c;AX8%#rt`3J8{Oa&Acm~7IzzPe zLqy!};N~LRKkcl)Wm$A5tIppmiX44`ro+40cpw(7fR|54=T5&geX^cpb}ayqis^LL zTZC?7XOs_!U&^4r52g+C;f16on4iDA*023WO5E?5WejD>RHBB)El0%~A&gC$WH{?> z7>X3FLE!L%d{kd%`+2#O+Dg$zku8hJW%woK0lDKooE1$JCon=hs{I}r zyo$Q6Lo$o|E`9u38J<+nxE+^4qRge-fQ3$uv7bvZFDXoG_g+o|aXZyR2#iv4NUfXh#!Lhv-pN2l@s&Mo9 zdy}8>ZN~VuRwypC(llhTN2fL4VN@Cs2U(oUGmlcct8ZKebd~A19ieOPo&lxw zF~sSRcpidi1s8Q7DqyfS6Zj$(EGD@BKg7LdR9xG(E(!@AT!RMJKnU*c?pnd!-Q7L7 z1a~bgNTCVt?ohaE5*z~L70KFboxSh5x1Dxhd+q(LKF642R`27hn!SJH>xZ8V*S9#F z7CcTaKun!f_7NJ^#Ni9b&T{dajBd(V=Ba0E%oXhlTX7%C&L3G54}Izj=>{9l)hd($ zwar-$@iGey31F|B{bn08pmsHi?R)6T>=1)1AUy!Eu7*~rJHIptMMEY?EIPEGoi*;@fhpw=1`e$}hF_gd9ijT{3xiyUM0S znSjD8$qjV?l{Jb9L=o(3Z)P%%7Q_Ares*#2IC7kw~DW3Rp$WgA5!(03h1@ipnhmsd23kAh=l4P`m zZxv2gTG37hthZlRz`m6A0j6!LXUBfVS|)_fe4jP$tBn6cQ^30K^i@rILG}EyJ-4@@ zVac+uC%=(SY4vUpdMUIL9ltCHCUz28%f!`69q>lIgOr`b<(|p36D4XhwpCUZ{CEanRX2wn6>;>^&v*wD-1LFQh|U6%L^s zJURfb4k~?~6A}Em$+xc)d*4eUT$X}n?#@hDs?(rwB$mPO>W*<5psby_KK>-aqgZ3N zhh%`x+iAVeu}IBt@AmUTBl@U~&TT2Mt9BCV)_p9$h%2SU<)==Kyv8nfj&a^asmiuo zny$PnPrR_mo?D7GtH&Y9#_>#5KfI~~RcW95yV}f^Bztd}LS0uWVP-evBd-3o=9C-t zADnVWHa${1J zYqTCU?+Y(sN@I&di}t6W%pC9qCG!pj31-cPa}}fve5hKp21(O_zQ}= zL#FA9n5qbYi0A%+SJ9uT2Y#S-o^Ki`J>ebc=(a>{TxN~G3$kt&$IAyzDA z^^Ysk`2`ieb0v*bDXgr7Mr8$sj_h*x3L{3&6(zlF2HfVZ0a~#m*682OWQvPk_M${M z_{!gMQ-g*FO6AHdE4Mm4{{=PWX$nhETUIV}17s{pq8BLf_7wUCp6{^poQ8q=yy2l( zz_=>^ZN(c5*v?DpDfhPa2OlQ;(;xYt#6lRH6kj$pko=DbzC1Agg7Va}L;LaS-R7QQ zS?(xZMTK&P$;2)YNi8oK)JPi2;FL5o?Ja%X9V#r9c)Eov=Zst#rCKn|^wOwM&&&m@ z=w*)eOd;rFva&-P-xN<&YEyM$QB+mOy%Tn%D%I@{ZN8fTh1Ae1*z@% z^e}u!O^p0V?RIz$osBJnhBI_je+5LIfrur)f=%4GX_w16TQhu?Rn>1i$#9x&E;eLh zPm^bG)WsUD(U!3y(a21VC5)~S{9o&QiOyLiHYjg;xTiqAIAeDTPes zB{c{_WyHb;*yS2z8r0{!#;nznT+1;8u!D^|BcY*lu`2_&HB7Me#!|D$=M$A)dDg^| z-47#7VVdaO_Onh7>MrWwPn1bR)@AMA~#lmg*`c zjII!-wCxek-40eyG<=#$YnHmmiH7=W=XcYA*0kmv=Pl5=fH+gB9?dRcG4EM^ku??q zT48UjQELh(rK8i0Qi4Oof^p-T^A64daHbsF9JH=2r@&`0S_^N+hdS>PJcToDjOR}s z8P!~-8fSb4jENfZB~uZ#An`WCy1WkXsfih@AQYZ$P7?T*xTDcyHP=tOMH(B%+z_*q z)L8P2MSjX#?91Z}hmRl14sz55G}E}nzu$SjQkgG$OSh;k1uLUOeGTOvV`e)$XDb;C zx7!08(wOP4G`zOQQtp~Q34qiCU*_S{!=rH(rrlg5=g1y&y452GO>b)}_Ay0h__BZ8NJqkx_~-cP@eGiwIT6x2 zj&z#GpR&qWYB$9QxW`IRUItW4R4Jwwfd+TZIH45cxN6u$+1Rk*AMZ1KK@3>8Cy{ck`&vc4MU+zeTpQiL;StsT^!6iCx)H-obU$z~+m?OJ4 z$^ZRrV9Ztr$NTPd?z7czfMx@(?A^iV4k6$iH&9nn>IQaR{0#~*5YT*Za1b}2(F zW4`fwbDm)`UyF-Jog^nic?Kgy15HrBB4X`{N%z58xxOaU!YAxrc0-HQT7@W>%^Pd0 zo#M7*LT{1234h5j(4$9dIrgf?71m?$_|pt`BUZzU$f%Gur5-Cf4wXTqJ4I*9Go!0Q z?c^go332m22k%jd?pQ4Cl15H=TXa!rERdXamoa@nw-pI?`;-iBwIUDaNm8?mjuAEA zK0$_p?0mCLgL6V7et%LYPpZ%?k<7@2->bthq80U{(fZ{%wKkPoNjfRMK^ZA+_2vM| zSI9Qbz@NMpz39?Mvkw<~UUB$3Iqdpa&Vj~@^MN_D0aH23#6+K^-6OQUvtALr*{tX{ zzX$BS!Du9k&6J8)a+5r-QW(Ja$H#3|r;)-3DQolk_-DS7>EL%=*@91m%O}Pcqk^t7@3p?_6!U zC8=d*3rCY$g&ZLVicbYVrF=6&Dy*b~7i+CA5vxZnZ&>%f9|38yS|A4q8Qbnz%28A`Y|s-sfrHIUFo7@RjKp=`S-dkr=d}C|IMR|} zHEb5?K5U<&4Eip+Lanfcc*>o%@s>fSWY$V;7<)#Bo>n9URLo+4^+JloW14rBps~wyl#I z@!Ic$4g@G%rjKbXCD1Wy&0NlB%NAG}q!%G_U8~hzKUp;TL$}#c71NGoKN%#^GffEq}Srx5y{B^ozsTW^=K@H(zxXO&O-BpOd>vR%^e5ik1ETK}a zsU&1Xfnh`aj&{SH@?qAG^pL)>^W(g>gs#TCd+Ou^s;Qzz5t3Kfx9KHm>RmfP;7{DT ze$CaF%8fd#_&DPYJAD$`9}Jsupi6FSnIo6sBhv{p)Ll%78=Y=T>jZWjJ^=(N#nu`D;jx=+&&t>eKN*!e*j$A8Q+> zxz7>3vWkcN+R^|DzovNX^I1=|hDUS5m5dr^*=1(L zGsczzRD8{59hl)YIvE1(haMyr5P^mgl`oI@GSVc5pfHX`(_`7zfNS)}jIh;spZ2kl zt@^B@2GARLKXEHq#%Fh{Ho}xa1&9b;-$1&Y(y65(CiM$gmj`PZEjpCis?WBh1}BL& zHE<7Mux4nr5m+yjOm%#}l4~1v%+Zb1e4q463_m$nc{hs7mMtR1ESk5`6Q$(+}w6FIi*O_@0ykY|DuNmQ8!%o>r32VwL>?;VV1GZ@;!F^ z5TOjJMt<=RQ!)RvA+E7ws->5~fFJHfNWu}P?l;Y9I$~1wu7h|8Q-%UU0F1$4N3$(* z1)iadh%~*NG@z9xDX_h%T)>Wtr{B1g`iE~>T9VMz-0B8uw4uq9l8Wb7Sb2mTWq^<< z_x*EmJP*1xn95=>)WLJ=p4Lk5i6+ph%L;y!v3RK$^}$_jF7)s zo{jIEvyj$~ApJ*Hp#ky0Vu z`Z1%AfJ5g+8a`!FId7thH^%Gw0E1zm)v2~V(0PE>W+agwY+0g628+17j2W^lze#~l za(-irlW%M)iglj{)e_pL~E7Ln@zZaNY*ICZ^>MvY`50t z`(A`H6q5e4 z$^RyP!V!vGJI1@SM4CcfUL&=Tm|Y7TT|p@PND%JeKOqaDZxh-(*QVhDi$A;&v4c-& zoXArwp@o;q<$buVoRIkEO3>N0w!}W_SDQu|u|zM)v@e|y>bCmOAl2V}l03Vc(iQd< z`iF%aRp51EXeRZW%QdSZ5p(zB-vg$&mjx+HqyPHRsOVJ^;w9ecF;}6OObaXG-`s91 z022sX?I^#2`h!{~CBjdj;SQ-$EIOa>lR5B1Kg3ax*nds_hk5%>oo`dMf?iu=Kc&TN ze^0>tH!c56Wt$ShGlnp}n8ip2VCm-U(kj*+%I9!E#df~BSuFCiJ`?3v&%zzv$)F)B z8ob{}9pzJOT#-@`lvsk1@XiH5<(LQ>cH%zI{M=#*HMy$c*a?3hsXx3KwdW%XWvdXr z&4y|@ts_0Sy9q>kBYiFqzJTqJ8ecU7gPrg%62GB0LD?JK*+ux&vNt@6nq|%QS^}E+ zm?CX1bbmjxjR`x~PA?`tn> zf@)OcbHpzY^w{D4hEKTw@nY-WYqlnRgC_Gq z2Y$ZNv0mum-(0=)FRJ+O&0E!?c0R$OR`1uMn+AU9pN5cC5iCe{nMVK3(bi1rvu`g< z6=)y5nz?^prdSPO=D%DmNHddB_@mgDNxyuArk;!@9P0k;=E+LrsM|yE(S-V}qIQZq z1QSrOiPaoUpHFJgC|5OVbPsT-Y$?t%SVNBao2;SbU-gkt6?tE3*ojfb{r(+cD~|wh zZ16(Kf!?-YD#~{e=IKxZ`VObJfK#d>33X38Zx9(NUpsw0lS^?~r3`*CDPw2!1D=jT zKezd)3Sk@@Dg8JMj2RL#5;W4ZCh@H?Yqt zXTx<2+`NivL6JsmBcO;cb&gPyMMP}Hhtlq;a*K4AQZXAR#2krUlBB@$L&z2&Q0 zsd9M)F+dG3d1jK$?^l&Yfq zC2&b2>1QPZ(L;;Y#O!f988N08`4mohi!1d&Zy`9bg!lVx6ShUdvDp92a`tbtBs3?)Jf6ZkBdG<$snZklpZ@_2W(%1`gf$4;D_dBV`C0c(6g0?4|}o!F8jWD zglgwCs!QOU@eHi;;+;s9*`#nndiuo|MV2BA_@E%{ul%+kNoJBXsDt*8X+2h#CA9A)1 zuoJJ!NrK;N$=R9*g`2}qe9WQcaL+Gh=H}}Mt1$rfk$lx)7K z+DakMge%=Iqn6cJXR72Q#%D+`m*H`qW$JtvPJ)s1xjMS9d_k%Zg+doN^n4$uy@40* z^r?Tx{M%H1P+l&zIPe}VIV>5b#mJor$&^FtJ^8svHT5L) zIKsm2>Am}COtBDMgj~}mFM=8uQwy`Ce28s=XRsqjP8yD0)Qj}x4BKuubVcS5P6lzA z2+`hzkpa(p4`QHd=CmQrF$ij5Hnz72B&jzaG_#EzakF^;W}dj@e%c&sdwN&@#s zP}8sv%7GS)d5q1aDG2mVGaxX^m$y~T~cfmdr`ats0>;G<}f=^ac>+6uW@PzjmRO475Ten*<06N8x% zlga1|a<{x^xZ0hJ&v4Lo14H2ID9|Fo8P5e&DlWfETCO|!bf#)ZG(Z`Q*mBq^1qYjl z4er~_JEKtjasA_)2Bp0cy>T_6gA67X(<`Qx$U_jn5@ce7(Mt#-A$4Hmyk9^8Q6VCx z!KT^vUV=%=a3k@UO{cf_M`^^7RM9(tzm3qLx_@ICfTKBuADdAQ^SU1lIYEdAL7w&N zRxjY;Ysj8X9#SZ_P`whD^581YZeO61ZpnV6aD1qVYIuz_`VucIS=)=^l~3HV%>9qff;cfE63aU`wQfgpk^1QG1`tDg}8f@unI4&iED`w%Nj zi?ws0?lWH42{QJ&adZS*)6u%Qlg%-Ef}6l{)w*jN8ndq2A1ECEzzJlqYU(aeBblN) zX881>OTxIbQzDfL-ZUq@+uCYujfDj>D+Y;ixokvY-cdT=nJ%RM#t7-nTn0f!%0IC8 z2e7iptPIlJIJSfxaBsL!XZ3^+G5-yac=*tZ-!qBYAv$2o1`W0c&p4ky&u_x}8%-`f z=r~G(#^={o$E5A8gGB5pc3V5OI3hS$4dGY?e0+WgNo;i5S$o~ZwyKZ9ok6*+JMar~ zTHguRWs>$wLg0js!s~W99Uj<>%^h$Ff*0TMDS52fJ#ZlVjaCpiL9ms3F@FtKLY2(~ zymVqToHlq^VEG5Hg3UcdWel8JVnJnqmb$OmgN;egSFnP07Kp1h$_RO94`xF;Zi z3s=7~I<0u&HRGx^cpeQX8q1qJ;=6^Y;r{b>Q?vEvz)?d{1qV?cqGT=pSp6=FSOhd# z?AAA@=3aV=HUZ15+#zV;=Rrw=u$J2($cKprv%@HeaGEOlp4iY|<837d9?jrjgnGK_!`d3PZ}Pz+oI#E8$WQM^A+46v3g+Jl}<#}!NE!M zK97s8==xs{SQ<_J445H6ULXnZ)`)}9Zb@bZ{t~xV66s2^{Jr6v+Q!}rX z18L@aaLg+NkjCK@{#rf#f46#rtKZJ!sk6LojYQ?}j5_f3&XONXHjutzez)^p!mDtz z;B(+IH|4iycQaM~zTQh_U^V??qCwXsv;B^vdqTN29%FyRWCdL9*S}49EXhK+jVc-) zRuWH?G=0yfP3X{@AfbUwU=%28%{witxy#dg824hrv!JJ_E%QzHTCOMY(Vb#w zD=E|?N-=!%*V3O^miLOlH9_^ZTee-x(XF>nw)O}5{_4Z;!1t>6FZ^42rhV;x+?Q@l zq~tbeKP<0Lew=fPz7a@%3dlWkxXjgU_bb0;-8DbO_2+25qsRdJo%zx|LJNX7 z4+WY%9yJ92nf1+o8dd+_KSwCWW>^SN-Y9t>&t(7vdTYLK*Mj!{KDE_MDRb2|3f!ZTmcFm zCyE90Xntn#)69Q+Q{jhvr~OkrcMSN5MEAW%q>=GW!DV6;DK--JF!jxSjlk`*Prc_w zZ`gJ<24AAveES83+g$7H>5=m8Kf|f=&HpUc{!fcQc+{l*;(VxhfcUhJCVka5aFqMc zQ%CtQPl7ZOQV!YA$Ga265|s_g1l5f_A0)S4L&Eu{D(>eer?Rkq0_Z29n^b04Iz_7rx(FjAHE63y~H|(_&s6W zSwS70!x8Q<_qH+4SAR2tSo^yC|F?DSq#)Nwx4{%TStJTA%NXBQZ&7?B(Lz?>eBMrw zwuq(lkq1U7FW?%jiM9prjLA&`L8jD{BmYY{4g3Qs|JQDY5B=X`SiwRVJwe#?!SH2L zmjOfiC+zt03;1M**I6hZ_B%}c!80l1JD<@l2upxQ##I+a-Ue=T|6hHlLf@Ok6z~6c zn~Cw(D8I~>DW*ocok&XmJsTaFvnPDAjrYx0BM0*5wU$G6l(OgBUILh1c55ucq6;HM z0OAJr|8d(#Jups|Qxav$(0chG+5dvNbrkpuIJ1Cu#_0CX&@ccH{%%@wTu$OtDg9pB z{>MoqpqK(&%<&5w{Y#hf29lr?fk3rT^%aK_nRn*XD$GdG_YJz)Jz^Iug5DgYBLlq`xHOGfVh+ zr7W}jmFJ6Qy*1v%;uD)gGt2l)bP`TWr?RYcSnjimlB*WLN$dM{No#KnOz?Ket`yv9 zKjNp<2LBoNdOT{*4E4I%yG_2OP_#0M_dwaPefSeIha-Y8lV<4#wh8zI#t#RZ(0BY7vA$ z?0tia3sK^KmVAMXuCFbnhjMM^sCtpwH#MDv)Y7p(4EY;DDF-n=k5BEsDc>Bk9jlD6 z`GAY7KKAfXCEh;t_wbpwT+0!z&H2`ZpU`0#MR+qKg3nOGZ7^7W-`bbBA0bJq_LPAE zwdqy`das`BRkKSZ2jpn#cQNqu^NC-&zb2O_?`ep6%QLenUv#039=ft8m1GPCtc-3% zf}XE3smGr>m$r<;(lPkf)yq}D5DN#LBWr~%#>9)V}YS{u=IyY?0kM@pFzavxr!fH{- za$z`2##8RKqx+qT0r(o1^PH=2lr1A}73ULWz(4;L8%OQ(0cpgG0l=|L&e?<(4UWHK z)9MrxRr*f~I=YYqvB4O7`Hf^lQ`hadnYFTgR-#(u)uF8M*+6t(MRsXI8`Td&FO-3& z1C&BYN6@&;f+X3(+An%@Elj+Pdr9NAghL0Kqit9-jw>y7O?*L*#ua7||38OO{f-($ zAGWULl_qUmqp+gV7ei(nKNGNL!XH1qCf9bO*R}t&2o9}G_H+I3F3<>n!KZj{V7v_qycMZj-rI_KrB4Qk)^NHh7oh42iYJ{l8{Wb`)mhl8KMUlvWK=VFZJ9e{_)&MZ`C} z89iB#8l%%rD3OR3L#j=^KH7$PXFxUUr`gomJw211;qsi0?jQq0UNJA#5|@c(kL)ofaeKq_i&je>iJOD{N5K%z#Q#_l^F!^Md8_aEXG$k8XLJd+a z!L;sg1Oz-BT$GC6FEl=zS^ldzIv=u%QgK(pZYbgDd)kJ?urFQL2r}cl{+J(`;UBfW zYZZ;`Uv@HVKg2BZb#%o0?@M^OgteF_$AZlrm&8C#a|+@5=3Yf~WK>k$@Fy=Sy`JX2 z@Ok##2TC`zzvKnmiqRaqHA!p}&&;|-p0nO;L|DG_mC}u^3h8OnrSJl}ToD(T^?OGz z?`Uo7{>Xg5DU&DBPAOHlC9`I zjyLh%PR{@&Z)~^!=_5fHBGh-vc$zXfdyS{yAJpv^8oNB!YL+sqtm$SdV^r$&koG4^ zb9|5vG<*RmY;MIpjlIGz_v*5=KGK}k?h#azjS*gcAVNz?H{(0An}f77o8o!&E(Mxt zfOUurOZ80^aHCQ~ii#D`Rge;qVkqVNo=nykM&FqfP3sX=8^=~-pxW4!I-NqEsBo_? zXY0BJeOV70Vkip{&D5oEI(3{i@R?_^duN@1Ap4cGrGE$|9%K_c(tt5%c%ifRd_CqR zIlxW?K@%czJ}IdlK4zU69npd<8wZVsHjs? zHa566x=Hav-ot9!N=t$x9yxYegtK*QkkSjPdbjFxMfHIZYeK@cUeQR8a8EmqL}ipS zo9xPt38jSdMd7-R6EU8&kG!edmdT>tkV;P5>IYGWI~xj!K^%`r2V7=Zx+EJ|RF0q` zle|P_Kqo{iBd4XitNw^kbfgDK#Ko38&sS;n&D9m9XSX)4|E3OoOONc2!46`mc{VhL zxN&wwgk%0da{}8^mJooKc-7&KjtE2gLOjC8Z3J$iTTS>9~(W39@b1Le|BtGb47fJp{!=aX;eP zyuxyOA3oFR%;9z;H{dlCCELr}*xyBSG-Q+A^oCPYeZRr~g3okaB=HC8sbj4)QN1pW zMIh-FU=%C~$>2w*=!J8D7O8{^bZM*%r1&*iTd&XF8tsTA-pq@}MrzTf4uvcWAGPo+ z*9=7v zdw(;>@iGQEG6~$_^-g2@c$Fd9>TUVqh%*O4l_?r~<7~sWp{2-IZA5()=`+$^&uxif zR_U{nQ{m7>Q2H4YdtdJ>YBksQN+dTxp>UWNXL54M9H^9Vz1w@ z$8_Zyk;(;;QXR^HmI#NKms&}`5^JzF-K2A18U6L?tU5{p?NIxsqdLqC&OdXVMLK?d zrL4vwR#<64tU<0yeF4{PBS!V^MeI0-(C_Oxm*LeT4wF2C4LQi~m-UE%;~WKoG0RJY zz@PjdIInV_Jjg>?qkSvfMNc|IDd`m0a?&WZiq4oJ5-ZlO@x5&J7qK1dtOgFDdNm93JsX zdgGr#Fu~XK-HVC${R;@;92}}GZmnK%Qx{qjj^2PM>}tmA^40y%O3R;+RAWg`9pVvG z6t)q+S>NC_4*mr`6&R?t{{cQB?JvC{@cVot!Zho+!k2rEW6ukq^MJ8)tQSE5aESdE zl)>x6)0Noen-stOh30>9jfGfvxM z(ky)0KZa1KOk96PJv4f02^=!P;OB5lE{*OIWaW)(@0c^WaFjqFTh5^x@G}KYdA_0h zVtq{#PBIy8_nb=CHznllCRBxL>?l=x8Pd}Dm6LoD{<&y22x_b*Tk3i)FQQ~`{#91n z{JX5S{HHqiyTJF*rUa?b71^S-$xW7e<-ZV1^L+tTt2=nt)Oooq_15|@o3>G6xK zKk0Fc+z1Uz17cDizv!WuN=$1;XTmZ~ssT?6Byh0VeIh4)|H4OxGX25HbP9I2NO;OW ztR3;$8u;83SusdQD2PU+Sd0g<=DhzmAI*U~{HKogQ{dBX{jt087)91HXVR6`T^dwQ zX58veLO+iS>nj2?1fU<4x!sPA2=K@iTi`rNAvErAl|{01((&Mq-)sad9#cjIOvC7B zyN+*v$5Y8#h%8?1hDC8YbL( zD|MHA1eO+vEnV{ll=oJ)9-qvvi4t(WPV~68({qj|%KqHkgYT~s@2ZnA62QFrRUxUI z2htHoBU*MB-_K&tST;?yPG`$4O;-$^?>cTMmn-sOAQ2ex0jzA>VHG-RXMnYnQ$FK# zPgU|NTx(UWy)kcI%>yc)f+;^Q`#a3njL_KI&|abuc8J`&mpQXDV^z7Q+E7Kw?!}rT zoyvUb@N`~$a=u9gr7a}}-c`CEqNT{9zR}091hSUHWTMYJ2V9X1`>&fB9C_P?ZzXAH zPdmbhF7n7t%%LpHxx1k3wQ`lM^{5??I!3oDc}K6@#Km6THRX;+E4EkIuXrWq)5!!) z$E!DrOEZhd@ubws@uD|yayfn^UWpL!a6Q;;ukNdTkOyhf(uK85!MGl_2O$okixEpA zXAFEWEnA!M^ql0$yrj6gl<16RwMxyt>}Y{c#(iViHO!k>*hr>TdVeM zxhb`#B(Xh(63pIs8Gx>pNOYibE3QG@Kxf zl|U7lq-m9cWGHCmGg4$W2OpC010C=etHMkCui5aH$NVeftNZjVvi=qiib&1KO(m?( zc`Z}rk@#qjg>l}Ysy*T8jRJ<*)+iK}tRH9m^D>G@Eu6hlRieTV>0gy2dK6kAeNyKQ zc3VpZqpp#ec8#SC5|%;JKumy#U%bJl;};4_7Ub$w7l^h_C-`x zu8djZ5<5Su@KsFXe1@+DJ)O+_*0hW}x?3@&!VD`M?^xIOcJgTbs7}4prLQM1wbm4! zzUTS^3U-P6B+i`zu(j{TBl##3p|Z#DV|R7i8MYMar`okg8klOwj$Enba~?#wZT(DU zEP;7drj0z7tGbp_uCGLh=#&YI-%}ck5_JK~bepb6FOl8Q-D_lrnA8AUjaHkt(Hsnr z|FothUPN)`%CAi1LK2hD)U;VKeQ8Lr#r8TsnV}9QZPdE?Cp(Jj7L)+nC%6V_mDGxq zcNxs$i%B1gDO(GPht#kZQ5WyodF>Rm6J&rNnu+fcP6oZD);g(FadyYa7XUiTNNWAD z?;DJ7A}Ft+P!!5IS^T`JHLW{ep z5#bxQGlt)i$B~HZIw#C}>@Ou=5k>7X`YEk^Ih6~q?V=DZN%|6zgl|VmJ9|9l*U3O@ zdUvEhbmT`JivO~~Wrfhk1{pNmV>m4&I->u+k6O_n;bqgA!e)al9eBah+5iuKbbPbT z$3n5gW0L%wz5H20U8P}?Zy5`%vY$RDOTQ~dQf=fZETm<4Lj@dXO7W? z)Ydr(>x8{1!y}zmmRjQ76I;IGki?>L7(_XA30AeP+O}gXy{SIBUT|Hjo zSWct?0;;JZQyvVMAk-}L+TdCu)h%*14V@xYp0J};OGTt`V^dxc({7ZKe(HJ3L3E!c z=pZlU@k*^ej^o}O;t%FOKN^mpYLF<_rC!INfy(o^1E?tjGbneaR(OPawEI6T%+i&N zUD&GUYVM?zzw!%aRzMy7R;u)JCqZUT4`V715 z>-4_3mfJ2uMd#Cyw=m&&1eo5dz?{e7If8wP%9CGwz*yoOHjSMn5bH zFr4s(|AHcIKGB5a(=XabD5qt()x9%5n2HV=vX3^*#MP3Pl_R<`*yN-ii-cfV1~H%e zqZUT4@}zhMER8U3m~Xderg~g0(9~Xk&~7Xq_pq13Bqwsm$dN^xT=>DN{(U~fGpxKu zw#Cq~s)uW^2Rm-Ir9%dZR~MrWNkqz)PA-^qjo+1z2DYi~CzZLpqy=l>SGR0u{^YjZ z9oK5{N~D3T#lvq2md7JNP3Zjbn$8JC6|Dor8uPcPj#)4YoiJh~r>vPbde%Ps{Il@V zhcQDg>9(R;gJNSziVw z)*ACw^x3^<4L z(B7xpFPppwY*4RL3Mm$+Q%Pz;qKD-zKxjHWLknMBA{=EqTxoNJAAiJQR!}o)c&{(A zJ=oxb@9jMWtH=feND8^tB_L1=iMX}j3g4v{9FkiDp*~-mu?7#)L zwN&ObXn{ZC#Id537)`CHBl7C(ZYL*w_4OMYs%&>U{CL^dW(gN|(gknjR&+lez{5?_ zK7JZFkfb;FoIo>(bUC}wRcRUPc%|dcz9rTc*zsj+?VjyE8Xdxf`D(5!!?`O3wRt4j>iuEI=TL!n&F#Y zP(K(~zufLRCy^}qraZ59nWN>(hI7cN&^uC*D(RJzoUMH9grTuNMHE+$bh?H`B8<#G zy0weCt}uCJ`Ore4A*V-Di3msE&?(xl;Vk6PtEUqFqM3h?`R@7D+n{*v`SDU}AfV6J z{OP98$3G$Wcx8Nf{Y;ms8R>&1nIW0jungj=y>{U3_E^oVWjgt1P-7Zzxdxh+pZ4@cxJUD?$oTqKMIz;fP+vd?$3CeUUg^4C zhc)Sll#(#g9`Im>SDBLAjhAaRqK91oxPg#o%#heQ}uZ5-#DDHdGlE8I`OnN{$;$M_ZQR^ir|`ghn*qh zSFP0lJMUwtF45Z(r^1MK5oFS|xk5{o6v{}!ZmUz`QAiuzb&`*7ye+ph?EErC*Z4=e zomjGEz^T25Q&$l_{%^hKa)lL3Sq130lgvC%?P5nS-gczXIlr4}%y8u}9nlTm>@wY( z2H1NgV^+p*e&lm;98R5mWd+dvl%0?kwR6t2{<^);9;?KuSFt>RnG8%U)i|2pTPlm~Oi*h)sZ6&e zHlFhEi}+=Z;OfDU;#*xaR#8eUc6^;!$@P~ev4lq2E1VUv1|6(aWX2O=Ee~8?05}fmVC-_ffNs$J05(QwUsQ-H9rSuJ}77(S;W$~^SO^5 zEnj}}qZ_aXhqbN5Z%Ok@^YNROvO`q3ah07q2i`hsY=`=GK*TEO(7Ocue6H9-5m-+e^)KB^>lYuWome;Hs zb>~=*(!R?Ozoi`iNPFsc6+E|{_yx5Zb*}$VQuB@X5vaMFd*3~9WBCi}j-K+irLd-a zW;q2r7Pt={bZ42i14zk4O~RK$iwF+)E6DfMx;K70T(0M^b1Oa>dBgX)d7?cD z+b`A#u>XRpv(kTbe6#nC5FGc=@e2y%yXjfe8h*$A?n(G*f#FpEWiIM+a-!n>=d!k6 zP^U;SD%+0|>+AL%u8Q}Rzn~uce?e7I-a?+)){o{F)CrLHDT$+h{p~NPH~;cH&EFC-j4t*=~~n(?+5KuSyY#saSfd?#X@Qjeb?-)*HepoV6o zv0rJvbd`YBzYY2)=ZrZMTw_jQ1*;@3mbha9t)J6*E`_jQbVugnq<|P#`6^h zic@I2CZW0=RdIRC7zfn!xfS;+Zhp!EbvYdLdN6^ozjS^^c7$!CDSPxFa|Y)@qS$Bu z7+JNXl*j#jrAPmFe0EfjH)%ZdM}d73fbsPu&}zD|(OJ^vh0~ZXO4i4|%B`lG+w7n8 zJQ)NWh!&Jz1K=JX)L->d>FK6eRa88ReY&P4oKQN>h=oz>~0rE+k~ z89ue~;uRIYCC1clnw(0*WD%I#q@Vd>|JLqM7l4EVxDm~Q9*h^lLZ)%@|wsgzrBWz+u05jkGo-6k_i)N$t&Dk z&Emwrm)Gi#IkVl+vhIo-rqR>C@#f^MdhRQpOonj}#GYj@zU8>4{Ftbe(3$#;J+_ih zwy62iiKYy7%;Z8ppS*nr>i0?}VfMDSdla{;o@jqX314DIxlgSZ@Np1u=5|=r8CbIiAC@qr!%Fz!2=F zYyW8D1%5MMx_-uR4vFvwS$F#;p7%wAX(m^F52S$f@jrVhUZyKIT7(}8ii*%X^cv{8OSP5yj*8%Nibmy$L{wQ#xkccZ*WRK zz02&DjcAo^m(o>(r%_IX%1n5k9wHBQDyc#l(&(wGH3ZP_@onBDy8Ua=11Nqc&sp|xNWr3Yxf4V z5;mAF;&8s!!VcqsI~3Cxuy1Ruq*ApJV2ZSNPnVHb$Bc+=lvvHh9*%{Pw8Axn9H`by zpIzInZJ3B0l-e5!w>=347%k?~F}+99H#ZjNAQoa#RNGBcLSjZ0)!t|pv;(Rw09(e< zp|@Z_6WTQ8-dEp{&(FwU3R%j0n_j&taV(J{>Sc0AJfp;Z{f{z>KUAIhw3Bw_a9{km;2d?^{Um{)*&78e0$cPlc=ECf(h9%E~WN zcwBQmbv)^=Q*G9wS;F{N8Q@l~rTq%Nv>>M}V0I8jOqWu!XL=Uo=uBx(OiO!tT+Y_TN5rKp~p+bfRxbaI-I z8j_ezGvxwyMh$zJD{%=^Z86$a#|?w}mCQ*Pmmut|$MJ7s+X4(R8hu#{Y8DoRLDM_y zG_+23!3RoZ`VEI^l28;(eKKfEt|MW;`n zs@MpyPq_)Fm$3|R8ssLd#eWR-IS$|EAv=p|aUwq`PQFtd8kbetIVjtkbJh76TW}Jl znl}+wlvop0p;2k6SKAspfP;D*v>D8_E zT}KjK3}d6tIfs~qplUbjzIjIT`~Hz^vTfQ57IT_h!Y%YiuP3c}SH3bf!ONn75J&>p z;&IgWP>qqZW>0!7K$s%XZI)y;6fnpoov`CImi1UX+x9_hrMyUF2&Fc>s*XR+1lp6F zW`YP=lBlKkpefXVRKi$A;-U=%d+Ibd->YUG{oeU$vjVp!@C?()#fy+fVQrw6^C#5KNFBa+N;O>7y=bsO!8ORhx*mgwyLRVgMmQ4#A~5_P_kNsEzA6svubNmoZcS>lfpZgvSE%tQ` zrLWa@%Sl300wu}$!;FfK2^^I~rMB(GPSfQO?49y%OYJ$46B<@&BThZ5&hSX*-b4*?PfV&1^0W846@Jsl+v9u5KFJQm>3TLyYnGo-IcFZV&FIjUk*Nt( zJ{|Juo5gp~Rj*=t*9dIjf(PJs)3Oi-l6S~z?|nv0W6#^8jhV6o zzJgsN_|#GCYhUEcPHPp-tEe=$g`5!SzTl?KrF42=-Bb&iK5EaP?%M=6Jcy9t1iIO# zJm!+qSgWODV9bK%-q4EW9EUYA^dI&*mQtujT08&H4$V_qu~Y0gfuqqZpt9{qDm7n{ zU4D*ilo(0+lxh`!P+mGAvAQJP(|Ld^(=c6)0(V7zhhR8`9NrLzJL2{E&QT%hrOUA1 z`c=yXSh$sp$j^}%?JS=2@kw`j)1PMNX7)L-U4GyIkv~MHVS}44kdYB4f)aJ%R$YhG zuM*YDr(FoJgL&sBr_YAbJ+}>Y{fdp<6fe&%t}~V9G3#F1wC+abhz;`%NrR6Ga8rEd zX9Y&Xzxvct!!HG(+rRg8#oIFEY1E}DFH<2D?M39@k-x>f&@WENNNd_-s<+T^FitV8 z!tXyqkgrKq)RSKDLGk>uj&gND?s9K#@WP(MhIVt#Lz3@PXO6$MP(ya<$-7a z3EfE^1$u$R{VgsJ#h<{V2mZ}4)pBMPj>fkss(zp*Y=!nyZc>N;<`Ict2G=#d^-Ktg z>kK9=a9g&`Zn^nO09%)ui7~JtD4rjb|DE{x1k1NDAh+7UP=mkR$_e) z!~2d`UGWRBepelR$^PW!v7+-dX1b6^Y=MA*E${c0Y-Rj;YG?1Vz0WA`aO;7=bDCA=Ic zti*vo3iVPLGwuF4o1k&a-%G_J(WI`bk07a|0jonFfr513b#Y9h`PVwKNVK`&H07zP zx>bPE|F>oSgb7SE9W_fR#7QK>D&*3kSsPUt`XBaf-5vuF96lNor&=^-veGKs_F?Dm z(Y>=jwKTy(lx4$GmEPokTB4ZsB{In(Ii8WF%JUnMmH!1mEV>lzI{CjFxGC0~U-PJcVR9)9BdS7}>^5`(!OI;58#hst3ZJ1^FsmkLOLmpt@TY@^mfEKN=|~6j%Hxktkn(zw%gZBToXP zvKMv8@4^og183FGLM?4t_l* zeJ3;|G0{qQ#q*u#H*6|-=pI++_TGo`xxvLb(u{?_dUl!6JNbn><^1$GfV^o{#@wrYR0V|}dM`FunOwi4Ab|9LbnEQ4BKHB< zskL>s6MFr8A8Of*Il6w!ZpBrdgL>4g(QItzYlA7-`OP7SX`Ux%deuWYF$T33}I9AAzs{|4t-2`v@5vatd$4rz;7xNSa_n*RaQsSYij3pqaxAV+P3 z^U7LltV?u;Y5n^l8P^HraSe@Je?zjN}6QYA+#ZFG!pxGbcjs<4IsD%Dp6 zf&E}e1$Jo0nOwS=7+#L@a|J&A)eX-|IQs8y#Q0#A%CDhV*_$4W9a}oV+O=TWAmW@8 z7Q6F3I~zm{N7)LSX@Nx68b`&<%&x42sO{?Tvw5?mu&2Z6XSpWUubycZp=N>{|9`@4!GOkT!fkPNm z37a?acuJcxe?Fp5HbF?lY|vvd6b_1jZZy;uWYfdoQt2E$8bhIJif8DCjL=yH$+##d zYqUG*fc$_kEZ)Jn`tRYEet+b}h^L_yxa}oa@taZu$nKzd171wW-X^&Sw~)ktF23#c zDOURZ>1_{20uM4be;f2^>|8uccCX~)&IY`J4&*aFvYUg_q^p0|#{6*roeECN1mAs` z@-lLBV@=h%B@5W6Mv`X5fjslvj$WSe1kNMsdBd#>@ngCJOy=47a*BZr;Y9W6ft@!d zD%Uoz&iX#&zwhm&RatYd)rpejE)-=OqlM z0KX_i%fZ-@^(l6%+qV^whs{dO#?VcP$lD_#jld@oLR@6ikXX{jjnh5`>LG(q#6!Xr zR5>^eb@r1#yIL-+k-^9ri=>V3%rcul`&Z9#X!feG+(ActX;K9AA0oCj*xpQn-+PJ- z=q|=jJG;?ar@b)J_@mq^7<)~0IT#{BR{A+Es~}7!D}V1zE$yfz(l|qd3HZ;lgm1td zB)+vwuOG9FpRiarSq-#)0Xi=XNO#R2Zmyb|e-Q(xi>X4t$9qH32NV}lKW-PYwUt180|PeFC4rKOrO|h=g3--$JO2JCga8wznC1QfbaLmKu-azn`0uTgkWq1F`)=< z0pT~pHUCN!Py>b^ro96%fM8a)k+C7G1^zFWU;K^kF8&>WQ`G>Ch9}kO$o>{6ccUkR z2z{aa+lOY~C O#0_%8yhRsohu5~-`80*@au`=Ci7=N5r42?bIurPRurVUZUE~XL z(O|cw7Ku;`HBMAzmIZgE=yp)4wOV5m{>ct4c5-=XgwqHZU2G^Wr2tde3cd5fGMb@t zK|cNR7oiMup*s{2XrxVhFEI2#W&GKwtNs_jYr9K$cwC7(=bm#@`Rpv^l{;KT z3X&?XDESmyef|ONWG#)>ev!1wV&W)_kp8N=0hQJ@xb-3Vow6k{f0}t~e$S_7 zrXt6x+yT5Ga%$;|CV@&WzVZRCBS*bv9@fGkk(T=YETDOY%p&hiaYbdR;mKsF-A5e{ zQ6{piAOLEB3f%Xh`O4F;cCrnA)9z`FnQD8xM{QfZos!~(k38QxZh6Y?yt@Krzo^Zt zR;e{29<7$LY5CK0JqHc?hzM~Wsl-VN%W4s5o6BV zR@j4D@zVlTE~1CQ0zp94daec}GWplhPKF^fwT5perc?P)of#%|*Hl|r*0>oszN>r^ z!E#P2Z}u(mBJ{Pcf8zRyi)OU*12jtV>QUqZPW*xpYZtcog_o8?7#X+ot_!|Lmm!ZkEl#dNeh??NyUf;x81 zlz7@jX}eM`9m2Ydd9mppd^*^+rrN10Gn0AiW~RakE}fIkuh2vR zhio$rUuoMU72Zre;en$?%~FTrghZaXj>NytNbi@m?@;f@S#==(!~a6FmO`q>E)$pi0=)%78USZcaNSHOnZp3LsanOMUSgF=P1J(MR5KDrFbG#m^c#`?F z@;*|LmOZ%5vQ_0B+{EqV@F;}0(nlud2@TiPMI|aY)J$h|%ZZ4u`Bm*`@gS8}D@&_R z6M-;hvrBAU^?h#M8h5eViD)(R`1823`D<1YOk;I!ee^Q#l;g?8JdtRq5>Y;X zQW9cW;Zw}}tot|uL=bLVbocti9dKb^%sItjrm?LORDF=>u`gNZsC`g3nl>+YDrYS# zqmdh9TtgO%<>2+PuDsH=&v1&uSTZU-V{joy{$n5t?;RaO<@e@%kI9zpOtV6u1BhC~ zr)PC1I(*Ly%-1=VYns{a^|WVA`lf@{@(F$L4A&CKq*7)ucI_hxV$d(Cv}s%DjpLQz z0^CQBqX{BZQ9}(^t4U+iSbVQ5-L|GkQ*{=ezNF#(E;Q#Pm)?$e3I5V+1unI?g*o+R zZD6`2Xs#7ypI$*!BtQZ{u^EfES&v<|JXwV|qf0^g*7Go(i!Y^^26nL;NtdcqlI4E3NW~LSI<^3Yq=R8&m@}gX{7dTjVVH z*Q5r@*5*vbV6;2J+8_Dtn%+ ze4e+v88&_a`kP&U0d^86<-JUA{@|ChnC&V}IDw2fP#Tvn1sl{QQd(TIQO+4fjTHVj zg< zkM%y{;pWf4nMB&?Alrk6m6e{{D~`-D2QyeHwwXe3`v6c(*BRwE;d0>{U0oi|NG#be z2mw67{ms`Z5qbH*;!))f`lH?tu=o|h0_?f2paliJ&gTGM!JbZJzt-e!T0Q2tCR`;tG zqDk2@PBpG~X~nFi?z4QeWy)fq%ck@c7ssD88zUzMMto>w{OUAUrwgS`&jPB1ak^+* zbgI3DtzQKy9V6?GaaUOSD}h$tQY&K5apWV&!0pKjKGp`J_MMfi)fGyKyzcYYVm>RG z%h_-Ix_7Q={H^a7z&29pG0W+BRw=5QPMF5-+#7kix0~Q>Dpc`70jhrzAp)*k#?ztG2i?9f@_41Bx$46WWT3 zcq<8cqVHvZq)~zh6E9Y$UEbK~mf{^)O~I2vv$6nQnAXX`dN!17=?tUe}-L-cSrdy(0Sd%%u*PlETt` zR9NVDm`M?&zW~X`r2M(hHoiXVmz~DH0OX`?LMU27k={umtUE%6Y8v8l%1>yjKj*4j zksXS8KNZ`{3bG_(;K1#xjq7wqbxt$`UZ;|h3K2h!v*yglagzodW?jQwkxr2ydAdMM zbh&39%vBFOlYzq}-`H1=?S>ZFLLHtuJA-an2|vB2#oy4cl;TQXW3u%(hH!BXOyDOv zx(*`58lzEuujCE&;vCUNEN{yyknuY&i~m+_C)`ac{+U*E04_L~aR4 z*`HdXDF;B(GG83OviwGEy;Qq=OWf}(+%v(#e&yD>_E|BSM(G>XSm48;9$g4Vw?^1+MWcWYg@MH5cz-XFXI1bU3^?bLN>B=JvV~Yn78^ zUHI=MY$?>UJ}*t#IIlQJwv?~xlQke^6gOI>DRGJB7Kv(E2Y9RGn}wp3wT2`UfzriUzj2rewF396jIhL*!pR3y z-71^nf<{Q`;@y=tA|nLUZr@<9X!;TIUW|DT>5nv&3eH{82p%u?pAng9C+oTE7FWz% zYgN6IPMl>;=Xiq6GK@vF72hl<6#NwIX_R!mqL}#AdNa;g;PzXhwP*fGh<_+ZJ?hj9NKcrlpHKPxU!W z=uoR{+L~dOV};mXg`H?h-(CL#u3z^)io}fXO7WPpSwl%JJ~(bPPSuqeqq4wOnJnTn z+3lcu(?@bk#ndvqebcl-1v%|0d$TWY2tw%JhxDvEs-rU`Cx7g;`LZl(shKis6|coA z%ZhD_`B5m&g@whvM@*a;Kvsk@BsK-JA@Rp00;6%PT14$xa8UX-LVc`zKM-1GqQC1= zP}44>rcc}_D%O!+5Gb@=)@CK_^sY_28H}7TU&L9ab=BopV>+_}zb~@Pwj&ux>JUGy zSY*54{S0~_s;g^$J9c!epr`YS9>Rjoj64TV^Zs^-Z7`qE)P4~I30AP&ajRezOy;1! zLD2f#H0DmLlKjoPm~En*J-kp^UG<$xsSdrxD7iP*w}atulJJEJ58QzjM6{`AgIIR~?nnZewe$nq6q% zNi9fS|8^JrC7=Zr(2-rgqm<87XO#u@vd8hE0wxeHTJxq>>5dD8;_2yBhfZpqrVgHAp~w}Y?+a* z%v3fWUKr&!-~y3<%!jsr1)tc~M3Tpe`%fAJV-g`^R# z%E!+nfYgky{%t-;N-gldPK#I2Fdc76%JiT#N@5}#CmkcUp}A;!({QFw#jW{W-zh;; zPu?84(onXi%6&MK3V*5zw`C5jZaKb@sHR9zUSv<#aOp7@1Nvr`2 zy=Ah8Dt!E#o=?ZuA=LM3)}wqF>%nGG@j^`78?M)muOF%5yaJT-Ybca$(-S431_X_o zF_ys5EsB^>LiNQnv2Q;VW|XlYm`&?kzILJSg)^Ud{L8<;4#%*; zSY3Zpvrc}Ai(&~=e-l^05;2r)+(FL9XnnsV@3YB8bXr;yjzvvpzlcWhbjb4>LH51C z!BbH-G==dowY&2>gAmT$qN}%<+l-CyWo2>H;J7Jjto09fC2XP(EGazy8&j@lkZb~Xw-WRJ4VY+j1!};f9f9aqGfl0kZz=_xKGczuM;5| zoTag>VV z2t&1pIG;U;z6y4^4eIon&Q!m_wU{Cm6ve2kG;hsTlj!@$KorAva%;0C18>s!jEI{$21WJhj zR437iHA zz}p!`kNt7)Nh5ns{gF+_Gxt6DN6b5zm6C@HpPAnI6i72`lZR*L~6)N)TtcLC%{{K6rq z9-75_c4Sb z1W*(xK^TO6peI8DW)^UMl&h{I(7jSLOSqqeA|>%F3g6Lbfx=bc*ZVOe<_jK}8B555 zUwK)f7NsI-tLPm*Ier?L5YA9BI~*%p6jv`Z#lrVtZEe(Pi47~2D#SY=#G-PgvtGXc z%8pz&GgsKf8&!o1&%`h0sbZ2s7?Ls4N&$hxYLn7SpWZL^{<2W7nMAC{UzGW!W+m)C zri4rR3a>s*=NM6$2rbyN;2}c|RDAU6AzID^KglJfrKs+)Ep@ak1S6m>=}TN8My!+J zlF^A_$>!jZqncA7fvz*M)(a20(mKX=p0QPx2K-2N7w2Oz2$b$XE=xrifoUALWZ6=? z{3*5y&BFdmbz^EJjgq96j)8U>hz5tbTT@)2flqo|gwP~nq&4gkV<9;+DGVr!ilxzj z#ZNkh1CE$PEb*MuKYS)JiI}(+a+8hS3saWvSLbh_m#zczm;3}Mvd!d!+neaQK>0^p zDak1)LpeN!1ugl>-8u!0sJNbe$cSEBy2qCKq82O#&y;T}?bKIK=52tpWutkTrn(8E z0*x5WZx(8qDGl!<^AJMbdw+f*KLXXPk?9VLGA6BNRn-;aKum=~YwfWsHDnEJZI6j| zteBU&=L!SWk%L+bUMUNo)~U>UtlPHJ#lwWE%AN+(mQ-Jz>zX`xfi1^Bw%!tsU&B?i zCpRS}6tonjGc&6)rpVAdU8E-H-|*!-+bDgIBP`>rLgmZE@pi-Ot)m8K&P@=X@zTBY z8eVGDX^V$NZ$NMV;|lwhiOWu0OGQUXNxScSnvSBhC<9psKJqgoN^5|`; zq2yZRZd0HHx(db1l=E=@dEms{UwBiFjsRR<)G#q+MBpJ=nDWut`|;r;7KFSQ_wwP* zf;cG8iQKCK>Vk-hfH-7Pu&4$&I;AC;&JU0<7@N9yFkd_?#KxYHAUDjvomtz8Boa^+ zN1@8sp2E(?_X)-#MZo*})zv%70F?jzz39$CNOqdcX0SkD7aoK^CILDK3TpB4)R?BQs3k?%xu9S z=9pQblLN{@#IGCyGo@dOv+Tp*6JUv&Ph(P)Dw{SghcXOGu+uGNUIqM&a-2m5B0cmG z&m+CLI`oIe92*K=xC!l~ziyNi71n(30>pZ2_toFY_KT$L(G!Af`8O@w+=JJeeWpzF z5UD*vJ^NU}$RMf1ovmKqtR@F>rWg{PMxC;E52yV`OO+h zSDkbVn`=ZDMX-iS=0r!-BznUuUA)sXQM{i_jXJve3)-$r5*Wt%9d(ptFMML}&2CmO zcNfUCZ?4p|UDLK$@gEA}nb@H$*2Nof5WGvOc_% zW;!Ek$e+|Ssy<#cO<@W&MCur6j)8Jkh{rFh3Px0iXRCCYLTe4RTzL!B9SP;ur!DlM zuEDCuF_)_4`1`W6y^k{EqBx^*MDaprm-3vS-Z5;X?A>n$df3_&7l8tLKh!+x5QF^Q z8ZWV4l^%glZl8F3_(AYW(swr?|KswsSQ}bAPM}R z2Skig<=>3ZN}*&%XL=0kT$LrpAEo!Jq6pnZwvv%K#kHdqODFqL%_hYGsJu$dx=&?XY{tj8C~eIy~426Vj+5(WN(rg^+H3%-Ednjadh;V zq&877X}{G+f+knthphxq3_A`srm{wUa+Ay}z1+$@m^kL7j_hnB+d%Wy?kd5mZTeFB zosPx%OmI>1KXs=A?~@5)46B$ljx>~&P>BEfTUIi_Qhf4 zwKRcl&Lms2lYH_!Rb(sOdO8c{d}f+q75<8xSZbZ%GbM|``E^vO%%)jlgnX6UtJe3; z{Xn4#9vKFoYek!iK+16^KzQ;!76OP1&tBZs66b>xw zD{c(yNYA#I5WlE6CAp3&E;;LM==RHyE!iZ6xh4TOBf2&#OdmyZ2$^=jQ+p&{@KYVZ zfvIn#7-3(H^HJMt>1?z9c!1kp@@lBgWxZw2>M1M2f1S#0?_5oYXyHJg=ni!tjj`}> zj)R~snt^j5QMDy4{No|kaZmddZro8*0a|5gLp;xMWAdV^Tqk(OSl*x41&-u8E@{5= z<;NrB;WSG2h559bXwdW#3EBMN>H)CiF5{>0tWe;+sB_^r^Z|_Ugvh1pAG&z^VEIUDNE4UzLX<^~3*I0#Fv zgI!=L76$0AxaLcyU&Xn~^6!2>Qg<&^&jbZeW08BZGwEq09Y*2ox)6z%&ERwD#}@ct z^SQlsZ09R0+2ervtDBN*6Z{nS+`yrod6fl35Az6uc|=ZBFZ{D#m%Zu3oB4*pFgJaI_^g1#@ky7^Ul`66c!*((7!a8so78 zE~3I>&@L8FHLZBBA-V^|Y^PJ6W2>sZ{97MmXYWl7aE{oHbf;Lq>SQg$Q8<|a*s1X&7aGH(`;9?^H?G7ENzJ(SwwYzDVE_0c zndzs_={+B)Z5rmOQQV^#wcw(Xb&N7dL9xgR*8xRF2bX~8t zE6{Aq13(fy8dc1j^EMmv=34B`>Ws4zm?`FhoE)X465T}D@FiBF#?}J0F;wQ_n;ir{ z^Q`P2E~gjyGZFH-X7cRTK=@knWCP4O_2&eX7BcXQcW@;trEW^R6!+%vKgt@Uv2AeX zYZh{jx%)39%_F~TjTmlXnQh8woSG(p#u$Pn%>lTb1 zX55*!gs|P}T9TbA*D2o1z~AzGEw||bVH{`Z>-XCQ<~S! zc(HY3KSXp_v$g245;o>^gfrJv^!3wc0=*Tz!x(_Fl02SFw?~hMR<~Im*9(Z?*ZT`C zro;BE(ivXNjkrQfAu4MZ3p7Y80Qtx!&JIp=f3R+qxysy{(fH#eWJ0&CW8 zYM~3iMS1Z7UBF6Q*gH`rSyjAut4N`hKo09P94<=Be$1A^Yomuxob;#i0BO%w0$&~b zK7ISaVwOd4*pENUd=TI5w3na=U8aBl3fkM$-sG6GJY zo@2{8<_d1G4wrfFY;}-7s{1Xyh1M@y0tN6*w2jktMq3a|t>uTd(aABu0J^rDa&qD@ z=O7E7UZH|HE`+WDZ^I!n63J^pTm>m0*>}yV@Q=N(U~Kb^EhuKYiICw$jtf&=$ay{J zogo`geB3@P;v;33pcw}B_*8UD^_WH#+*#ayQh>Sc!KG7D?aL1;F#+sZ&Rmxmc8asv zns1O9PH6K+afLH0qy%#<`5&RIQ}C$6kX^Dyj#J*+-bMtRr}xdG!6=$Hfpd1t2nCZL z+;hlm#g)jr6pvijmO781hE~8D@NWV5*Sz=Je*UeBF~RNf8|2B@#PG=(baH+%u5&dp}{)IMkHFP^VWuw;xZ(d}*gRWSbigqVKx4mhW(i;ve~5o#(py>$wh@8RBr@oX_#1xY(fP z8YeMOb%0-ip3W5Up#^~(7y-Qb?fJupDj^EgG2(#3P2=9yer!rk_wtcR!5#3ev z%}KS-nXe+5;x*Lwv1Lmg3k3Dwjjz@r5!1sm9e#S_rqoeB*#YoO(#F;iO-BXh4@40v zJGh0k4n|J^%PdORIhv9U@>eEg#KY_%tB7ZQ{2n{Rjc(e|TFGIQ;f>m2^{Cty>fD=e z0-u<+j@8WYZzMC#@e>tHu}ahz-fPn#GLt)Uk<*yg-e=gYZztMWhTB;H@JBr#78*O# znKE=|-VIG5K4|pg7UxdXudeWn9UYb;u>0osa=}wu(STwk;x=o$721JDW3S6iW(EPG z+eMw}%xJUWBSf@YRR+CGFo~8#Rb@p@A*2Yunvgd;mdtLN_reY2Do)w#yxj6&-OWT0VON=Kv?8NrLyhJj;k zFt+9wksbb@i+qideCKZ+*y^jIW>p{9Wp=eaV;2nyN9pJ0BpQNGR_T_vthb(>F%iUBC}mEO#(hB)Y1q5ti(Lt>7|U{CJB4?_7$JuyP}h zblcj!7sYmsltOW7j&JO-ppar(%>-%Gkv|1O!*3wkO|2 zw{&FYr@yylmF2Y+{{p=HDG3)rmsU_vC@Z(d4I=}}ksRa`$E-j#f*#b@`c(sE5KULw zD>ZA5O|;C(OFf^&#koacg$j1j3@Ffu8V#&U%>+-M2xAmCij9=M^V-geb#CuJunBijV zbSLnNSQ$Nu*)B_a#A4Y-ym>E24R-?JYl`#mT3YU&{+px$+em##KSU|(izRZIXGS(t z3HE8tA{9hT9czh!Uqo@+Oz6?fm7M%1b{a}geAe@IWK)e!!=lMe!u6_=H zqc`1b-@h%hqY9^4R+?rWG9J^95X_EN=gKhyp;gkBh(KS!*oZL&{u|hk8%0y~m$0rm zlM)xV0Sijv;%-_0Ju(eJ%)tZo-$4N|_lmxFk$lAByV!@D$QejX? zZ-nzbLDrgY0@lU_P6NKX1t2&`wlNvcuSp6t*=wf3QAPa<~fqnQCmji zbP&PF@VW{U7q06cTQ3yanE{v?rWr>$E5pR>lwxHWjGOra9CM!O%7Ud=nJSr9m`WG( z9<5fgCUmqGVy|IwFqZ(VQQIf0tOVIQ19W(lplZrz%;F)8B!3J}8le^#!iuLR2SqGTf2!&y5)XZk z=32FpU8}6!Ai_+A3R`Z4j+RQ2H_{TA)Sb4k&{%QB=RH#kxx8iB(d{g?Rk_U4%$%`x zh|0V=Y)khe28f(zr`4aP-E=Nhz-VWt&S)L+Q?L_^^)HRJ`QM zW>HJy=q6SveZMuoRPKx;D4`+920%t3i^0D`cz*PTb?&o;nV*-p+KSB=A|hGlxl)Ts zIQ9gqlVVkK?Y?)%$e(hHG2>OttwB0tePKx805P$I&P@I!*=TP`x!xnGcs#ULTE{YzRn%d}uGjmk0xl1uC-LlRhCV|{37wDB^KCWABCy!)I>U;rZD;xA z5qV^Zn9#kv2>c|}L8O)qu^VEp3a6)MM z3PE-NiYk-S-pHg3wze_n&+`ttvn6U9uV1&@JYKtklNg52XFhLoq8%HpBy>zE|#6K1eTCd6Pd{iJz&?({Fdc^dU!0G04y zKiC<7638YVRyt#SH>?5npRP^fiVGEiZ%8#$V0%YuV$Z!MzW)g>{7v??ucKNza9tr+ ziX_b1AX0MzO-mR#llhxS{*FS8o+dRnsNlcnbFy?(k10`nWQ6S(ONC zB>pTJ&_sI)fb1cZ)c)_p*L3!3bA~aiav-jb>BrHz0zaiKp$}Pi_1gNbV$)rnxgVG3 zI$Bu3o|5){0qnJXqNTdF|9~XmA(xwAH*GRgK|Cbs)PB+h zKz9_W`vCD#qS+Bvh>e~Yi!!uEb_|LOR?^%bJBlyfxu^9qkc+C{PZBLx?>Cz99h78D z(#^g+jPsFk{>S|qmd8o#NO=Jbf;d$`!BlJw**%6pk<0Nk#1$9kp!Nheir?rMRD@RP z_N_&pWTM|*KtsNTVaU6hclg^;iM2+m2^w9G1)3>eXd7<4!;7DgBPzl>{xRB@M7itN zS0Cgq+iqhM^#YAQ35+?B7JTKa|B<3-@@mk;oa~^YnkRQM743@}r)p?+x!TY=fAkO$ zH|sdd7Rrw|h(AdMmX2lv9GhIT8J}@Sn7o18fB!xmZ8P$BKRwV_gg7@! zdjU550a~DEYPe%#=th6>d!xtrz^|(r!y?t3A*KHg(SF%ER5Tu%TDyZzle|+gbx=m?u7NF9kdfF2Jsf+=o(S?f&1# z?H4R__{Yp4sg`OLS(K;c*MPD_{y1O&VSBva!#??+bNkO2$mvw0{#?C8P-2=osI{EB zVf63TLOC$EzZW+BKPF9ebw}CsD+=}KL(}1=j3`^9&&V5|;ZeI1!(0z&WT}sZ&#Z`C zui_}x;lyJ6j>&CjBb2C`Y(z~XRrtG>k*XvU$<|bbD zHVL##=#vLiB-@cR(?xjmH?P=Hn|qmb%x%akZ{kbOZ$(sJnd@=D1Sb;5pn4qw&M{i=M)}i_%N!@#wq?-v`3Ll>&| zl-J*ep@rY9NDtDU*GZslvfbU!k9Y3Myc0sA33JY4c@ZgjqyP04ooU5wT;UD@x9f{XH6fWNPd4Gz<&gXR z?&grZ%zoN+68?v~XJ9tfhfaLbGl=-(mBC6Mw8`spbmAv(UmT@4Vf|0hw_Lf;9@VE? z--;cOZbPD;yM)7wM`YZ0M^LPsekgw6ipdPVDHu<^f6RZlAPINw4~}tsO|(RLTA7M} zs+xI^`o(QHBM38N=-)q*_W!?u-R+LR8BR^}-mijOot9%U%nz=zj|P0O55rK%cWHQQ zUash{IW3p+*KNVF`JtJ~j#57SF!60|M8BH&KdoJRJkxC(pF=3okV7HEwwxBJ=pmXp zZp~xO$e}W)avBxtK}1DNPP1uJA#-le%DIDzD3eoc4r}$4G!GplQ9`}Hp`Lo*_xU`Z z&-;1*zJK?1AHLt~yU*_XzOL&;n9>L5hQy42UcLs)&VDWrcu2HkVCEgr=k(mY2PeEV zTAfES28y&7I?GO63ZDBdT9=QCeg5JOD`U9)J>K#cW1C{V1%>>|*qJjtYAb6XS;d(l zP_hTM3jx|+W4BO|JM;EkqQ(yv@@>tN-M;)YPqKbk!y2HE(?4Pkg8>MFsg(eNjV~f3 zc~pL~MO@#f;i6SXqY|_DryocKC^Nx3F(pwKI=W)25g$ssHd^*v1`4{LV5RFO=>)=KZ+Qum)As z5jm`yxIKaHu6IOPEu3%r3VBhi0y(Y#H%D6p{WLMl{{xI8z!N!kt)$R@e&QbHSAb&c z4wHb1MHEn{=m#Dz`eN#L zU;M;UUk6oTM~ciC)@qC{sZnCOTH3qG6Kwa-MfdCc2@li87PhyFk->V^%AKe)4Jee& z!brv)Rl>Xb8TtEj%P}je?IY;?Yl9u}hn*B_~O&S+;+Ak&exfh& zU7V6QoVfVLA*0P)l1X3aLvcfxc(fvcJpV^#CRcJz~@A-z6Vu#~TVq#{U za*Sn^Cb{am!)0efTOR&D#uEwG7k^ zY^8MVDor|%ZG0e4(<`7H_Hz3Iy3*p-fLkZqvb1!C%%)Xn|;puGCH-G(yhx||y#ekk%o8ho%-oI)` zzNH3~r{jX*0;higyAG^(sPfV3x6pF9ek&X934uRCjk{J&YTjYa7BZ3>ER4)zqU=Np z?C~TuPHIKeHhmbC)`X7=M4K+eOFBJQYC(dxOtoUOVYcnG1}~_|FYSCL6J~TEni!Ef zgI(m~llrgjgt0WmVKSK;5r;`Sf^IG)(ju86Na2SddACft*O zSSfgmF(Nl)iPc_#tQSAteQCitpnz*YyU2`eP}@mJ9}YoyhxZzgf~7(-ptBQP6Y}{D zIMM@Hd{o)~=3c)`zxP+gIuEvo5N(i|Tl%`AGqLgoWLiPnF8fzv7abvd$KTDpfTZzS z_vAg+e4H9C*nV28JOGiIlYwY{mXqnnyKmci2A%bEz!)JBcd$FIyauIO?rF;`4|#t< zurb_$kaxV-?2N;hu{lp*ThQ(0mA_V2Q-;92^lQ)1OGEm_H!wVD%3)kxo6$BAoU3%o zA~w#qRxBA$dTG=rjrO;#Col>fi!oXC0w8CFs7RK-hkzK5UN<(?$NG~Y57I;YoheGs zK_>FBR8Vn9&e5c9-I%`Xj0(rwf$FH{`}Q$=+iKRmn&Kz4Xjaf_+;wFx;p*5IBWdCAD=kO~?wsyTmq`1rLmPO*VuQ7Fxs!^-GPG3OW0^ zFQ9OnzSAbLSScO(UhJtG2L>$cR#FfW&8pg4d3v^7lOzX&%5iG441;>oRd!6-g)K26 zO4kxcg1TiR zpic)(E$oKxs))&C=GlrCdutl;#g)E-$Usv^Ye#>;sX4tc+KO%U#Wn4A!=b){`nY#KK7;Wh=F?Mr21PWZh2@PkT_JL3 zINL}H=$irbC1#}ztuGU*h%Vl{0WWrN9B4CM?e`%Ewk|| z$FR&!|3ggyOLKzY5oZqxRXO_eP3@!%kHHC|rb1VNiBX8*m%= z+VOHCfoVld`rJcR3DjaIr@9TIl}#yr5BI{PsF`(cQMu+CaW@~sbd$L6&`J=*0a zDKxz^E?wo=s@pzYFKc)xtU`nvNh87&+ye<7CCmGjUB}n&8cBNaizf|GrPbMeVhpb; zqK`E)on^5wRulfpnx-Fdi=e8;op9}^&`oCn;mea!XL>K`nsf@)9A6*x_>}MY+dg$Q z)TTQR0ijS8ejptFTQg-)z@jtLU`Ekb=<3>KACi~aD_PxSl7b%alK&?Dq`RAEF~$>7 ztP525= zGG^NG^6kroLGBv(i*JjY3B5!)%YMfWh8xc+LG+P^m!*!{?n!Aat`tQirx>Tkle04-p394$D! zDk8ni92@VBv@Wy?GoeA{?Ykjw9TU$2X6T2&zeY!;*COCZ+<{`!k)y@c?OvPBc^C$- zJ&R*zFBH%%Frhrs-{90xksAW0rETjTVHnxQ)7rmQPI;i`H}7!{?#O)$jUiVDDJ9E2 zQwB1=K>N`$9GT7%oQO1*771q?5obHENaZyZa`fv_wA6~FDyG{mTN|czriebomhV}} zmauq>AgiMqzGc(U7I#GNT=ge^a7BPQCc2Y$$mecUih}O7ROhKP`Ia$nE%1LC+W1Mo zD2Aw5@J9EQiAz(ehyaQ$xsPz_TJ?gvTqPDbcO^JA*O>wpztVdyUGwA98#a5xHC+{= z*SO8T7;W;JT0T_;YC1Nc{j;uDgNAeyrSs{nUqM?!a6v9z3E%%a2QzX8Rr3gtWE8Qq z+!ZKz-xcN~&RZc-+I{kR56ov=ScDar{cr8=<0M@2Y3)E|6PMHZF@vd(*M?5}Ue{N4 z5oYfNLW!(kO?sqQDf!-`8B`C*-lH2kPWpA6H?=($w7j$ogS91Gb*`a&T|cJVS%-Hj zJR%uvQ4l*=6pJh7QI}$nZKBkGuP7CzY|mg=tNQDrAjR;Ac=0t>VsBN7EOt-g-(Xxf zRR{;nhO1jqOUw58;0*N`m;w9{skqzS|T}ecT3q~a^bU7t}*Jd zl`M23N;Na@BM#Cw4UT*9(^GF1tzf;Rafz8e9Z{vU)_v$Ita@pepPQWNDd0Y2v^%Ie z0@!_F*1}gmXwRx;453S4 zzg`4enEMYn$As(dReda9qn;>V?Y~#gK$4zioTrlI)FpiU&@c_GwM@8HBO9*%CwQRkiJAB^62uhNx?TH76>?yn36($O zDjz%HV;H>qCbw&3>4X(Hhpt`4fA(q-b(5b_KPW_9xW)|alT=>Ojq(?b^)SB)e^qeh zM+^};lkm|eja|5__S>EU8SWf`%eK<48UDGo$EwnjT{?T|U<+}n4=P2*0iR~|_LT#r z9(Ds%#QWVVGpEpTH(D58*TH^qlHdsZHtUjo?$ioGXu?X<_h3kziVS$XC!aUM*`3mxD%7{sg9zKKned}M&i7x|QD*Y#_*00v=*zxHj zKf3t4_XWAkE4EC8V0w^Mc6Eb~O?!xs?auAO(7K--p7o4b`ga6aaiVj%5Qd%9 z`QhRmyBRL%Lm`;)$IRK4^PShM%J&FS-7O>%G3ECPPhuG9&`C`xo-?OX=Pv)az>#dj zTcKJ$^?Xzi8nJSz-Y-D&{jMXm{k-RLGDU%ooiy)_PiM4%5ZsA9=9Hx@SHNy?^uyyP zNL{B_2CCY(Bfy)?6_g>lTi|JZ54zd=c+0tjATt2E6|Upw(r5Uz>_%z`iuFV{L5wH) zglx)P^&MHO*N5|b;jLcS#l>=HaGI%5f@H$ nrY3yhZi3jmy8&lh)G&*sx@&?S6WZ(RIRA|X--10(d>#51nI@&0 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/HMC_Events_deRSE-20223-preview-md.jpg b/survey_dashboard/hmc_layout/static/en_files/HMC_Events_deRSE-20223-preview-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0b9b9a78d03aad49c819fad0764da6f1ba7f9d60 GIT binary patch literal 21686 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 A2mk;8 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/HMC_TEACH-2-2022-lg-md.jpg b/survey_dashboard/hmc_layout/static/en_files/HMC_TEACH-2-2022-lg-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..40686276ad71cbe12a306c3738aa0f85749ee950 GIT binary patch literal 54927 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 z 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 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/MC2023_Header-md.jpg b/survey_dashboard/hmc_layout/static/en_files/MC2023_Header-md.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5ebf64effa5bd2997b0fa9e15c98104fbbef90d6 GIT binary patch literal 52345 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 Z1!^=&Sk59W8KY?y&lnkX(yaaen*ene)~^5n literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/Marble_Workshop_visual-md.png b/survey_dashboard/hmc_layout/static/en_files/Marble_Workshop_visual-md.png new file mode 100644 index 0000000000000000000000000000000000000000..8995f78fc6119f07c9635a98523ebb795dd411e8 GIT binary patch literal 103510 zcmXt<1ymIM`}Qg625ISTY3Xh$1CUsd66tQ1M!FjbDS1GUZk7}&NonauIu_V^$KQMY zoU>=n&N3S_^S$G`K6jjfo(2&<9X=Ww8j+T!su3C*`VH{#!^H;nyt?Xu0~;&{rI$)* zXbmX@aO)?)b7mV&qnBuCft+Y)VNqykkHD_5Lo_rWVKlU3D>O9eTr@Ol_rgv?S>Ofi z_c|J?z!vy>Q0W2#8$5SSQ!g|$Mv8wAbfGl8V_+wax7JHFoPB&cLJ21Cx!iEz9nrK@ zm0tU={Qc}>#W<3OQfRt}m`-=e%d?S5rifkc=X^O^S*SebGG^R)w{ntI-!o9}-_!gu zk41~#-gzncxvjlU_VBDumkdO~*TL`ZiK(aai0^vU8-4*oSy7pd*nQf5^zz}6&ofe+ zww+iQc(`~*VJIA2_(nzQ{nXFRm!*g{?Kl4!W&QWMB1iN|iRl$=)Are4PL?;Xwl|67 zzt)_1=Gqxo|NnpSzL~u+yevWQ#6^|0>0?8KnRDLI)c@~T-Z#~s?xrk548(6j#M?i< z|G&LQeW{m5k|qE55xhPA|6v6F`!L`DZ4LA{=so}UQd!u4VV3{f{pP<}Lq#EL1M{r^ zyuC9rLs_x$J~3mw{uBb)ks^*(MxVNV4<#JzYjoxx9}-Sf8pj>?V6gRgQ5olAYLXp2 zO}87sn7p$oMO>8VTY}pstn0iwAU3%qqp}xy(n-mHGv#9wWJbvHJ))Ixp=!3bqrEQj zAmAx<>{HlQwqwj53FP#?Lp0`j4DdB{)ZQopzl0b=3on^XQTdu%E2yAP?;eVciM8lM zD;G$a1MI~%-g%W#M3}dqHBZ-bcrWLX{;fEc9=o%I z>-xY>ih>qz7F{#L)b&q$FWV+l`>CSQ?VV(fEZrYt35KZ?AFs1rWG{C`@S|nL#l<-y zCN^zwW5RyeYX#LXr>#M07zwXrNfv$3!yV~Vs6z3hG4ga{%;nDsa>GcmM^z)drQe8T z7agRH@BOrGq%2hS|I^4nTP;@onw$kIIzmOFfF);qN6W3)#n2Wv(pE%E`tv{||J8$R zo^E!qyuwcj`*0SlO2_xhRIM&HW)hXAyd@8(QyG)?+?0jiby=`RRYm?b5Jy49)IH)QP7tOL{n$<^A?LX$C;zScXLcaL6DlJ zgj;%v_p5g$r`(hbzh1y}k*8%$+(BT(BT;kfnbM%w)dPlc?<4*?ymB!sRLsNAKmY$vv zYtvhwmf+nD`NVk8Q>eCqAyT)aaf)+OU!{e%c?s=+w>{1{J$w(r{8dKPb9*A(vkG1kuef{Ky_stPn z#;Y(}{Pp0Z@%cRRDzy5*rtxC1Yh#F4`Cb+qPY6*Nxt-TTJVCJ@aKlX6@y)Z09uRQk zN1b%oisZ%H^*+Xr-}k4~RkXGFX};^6{Y6iA9Pr1f{_DLtXGXtIPrO}worZkz&HzF7 z)parQ17u`xhY7}w%R$?8+xU zSg>w&IR{aQ`_(niRg2*JGL*cnB2+eIS=3>Aex9;R((X!i{qnBuVvxx~=VcNZM#@Pb zJ2{?ulC%B%&;2Et>eu*-x8M1h?yL0jhKBHVpfy4d-BaMH>s-;G{nYgW83*5c*sJlP z{c#YCQ@`FsO*#v|#d*rB!6m`*RaItpu`av2oRIhGeEBO}mQ*8RN5QT-9WO76GL59| z2kM0^w9f6e%=EV2ha}ni3pVGC-ksEshrdhO|DD&=%*}w*DWy5?(N%?QVXwL&ALioN zG=X$U(2kLr5YKM4GpBrFb+sc_J@~X{)V4{*70j8j*J#`u;bAjE+$H~HKLinC?y+29 z4rT2#pEB#{P>@%g%Z`kwe#O@pe24iINk(hr80QCdIS%u{t_> zCKE(pgj`io@Owj8|2Ko89V!1JRqYLtCMQ3>Xk}}IzBkOL1eHXEzQ?19jB@|&((-WB z9i-`UzPQcIFl`NfBI9?qB82Rw`CriYM$^;=i55$}@w|<-;Cz|0+sIsj#>2g9J)}AM z=G@EC`KslPjcmR?bhUs+!Ii(R%T2UqM=FtFc_L`t=Qi|W4y1Ygz)&A>`!pE33R1Az zA7AXb`yC7`0I_6g8*&c`Jk^#_xHZr567uk{VA@nFalg!RyJ zTcuKfgMsA%uH9wHPmQz17!SO(am?pjM4m$(9ciO?2><*g5t*;U3DTcb3VY#Y-*mY2 zu;y_fCwp_j#<9|I(kUh(=6)5&^*t1UT@-NFG5t4`yZWOikR~j#wD4vl&8ax2QoHvs z1&P?$qcQpoBjd56yKV1+M^Dy|E^C-*7~&2H^k@`{5`;R#K*(di$j8}=OxvaxmH|h@ zy2TPc3irIUG&EL6FB~or=b<}cFjkP)-cvC#iHjm=h?IRGRMx~heS~jgbL+__V^T(+ z4|@!cX<+q`&(?auOW9aqsx8fguwpSsjKyx$ANb*jLPh0%-=SYI{N{j$=BHI|#a>>e zmcG7Fww>q_e9Be?zUL;%iltj994K4%~pokXznfb;;qDe#4x5yK{Y`&-nR?Rh{Z7hGng(MT1Z=hB!HE<9 zzm$H;6>WdVU+AAbV&~?q#a@ycE5b!#oj<+7WpZ2I$D@kwsX-TVCR)7P34&uU`i#5U z%uQw%qyARiMb`JGfM>o$!{Jp5(jUUrNM!Hp$&-w6ml~D6JNDpOaQ5!gg?An(%X4*H z;HNZDY@S2Je(qsqzqI-#hZX6Lb|hN-DETjro&{~P={m7;_I&0@J6m?nj%8|(q@AnsfNMF-{`o@@(R|fa^P<R;G}R5kzEvWdE_O;;mZvEBApTMq~56_qq4^0kpzx&hGdT3W$l_ zXmN~dT;$d45v8$tC{NpZD3|z=_p*a$hb*DWE z5Xj)dw^eP-%+%b<(Vz{4?=Q1Jz0s~>#2WR(PBDs6y^BwT@Nk9ietha>U2ws8?F0G9 zPDJ9(O;+ItpQ={3A|B&&tXwY=kk0m5@?^Ym--cNfC@C#3vN9 zD<8Ix7WR!_`an)1HC&W`?I4JfwQD|52s=O>o22_gsMGD6|E|$~b5$^j?NM4CRR|t{ z&bTBm|BR_UA<$w)|MNKlf|%WnZi%oRD<&R(_5L0aa&#EE*j*6I7#J1I!X&B`;Wn#V zksf@HdA>R|izunl5$gE9jpzxI@rTLOZ&mN{4SdrXe(lbXEdc7*2VaF`7X|tHBQlDS zvS8%NhE|1*){BT)nPN|R_1BU)4CHr;C_1Ee&_?~n|vab^B zmVz&P$iiHe{*GudQWA-1LbZUDK1%oO|29sYwAG2KRUNQDZJKKE{7pwbm>L{Ky3{(k zo=kiOzOb$f{%yxGrCT7)v1ZgJ)W}~T?o_-dU<}E5Zu3qG`d2hyd(_SwdiVaxLWwGC z|A9QpPFki*8PvYg0@QVy?Bs=>+jxK1|BS^X6%0U`G2CfuwTghu9m7K0$%EX(Ib@l} z{}W#+kSf1~-vLPo#hMqPk;-cLFW750we=-~VR_%1s2-@+r%wvXK_=*@$VSEEh(Uwn zYti;o{g98~M#p}RdaoCI&O%uC+ZfRo;@1n$dhORc=WwpmPiwY<@#1N;yM~x^Lvi~; zw;~=2L+*~&ysuB^7xMG%*+%!S3fpd+^`tMO5WHsE-*q$c^Ya^_et*|S&DQT2C7f4u zZERGA+4;11M)y2}|56lhIf0NEIXzcTgC9jfoBH4fnIrE2VPo(+iT32pm<>AbvuH1& zOLvBgwR_bpqttQje?j@+l_2E4XmtJU28Nduc=jWzL+`x(QD80jy zfNvc(Tpv2pXs1s?Fx`)H8|896KyWg)&ZH1(-d^DfvZcXT)l1jqJUlwu>W^jG%FNKW zJ!w64UTLPC6MOzLa;8k9sVcgl>TdtxOto+IKCE zSLH@p&Z|GfUJ|Nob_!A#LNd=o&oyptZWupp)o2O{DGFg+FX3=~0ohuK(8+%t-$b#R z=SIIS5o~sd7}nRs+UCZ(MVpAeFctJ3)k2gNlu~|$;7*$EeC9U;y{u)&O@Lw z?Uzh#n*o-%|ApEH7WtiP*mnKO?4RgGbstGA)P`j5?^h~8Xo0%kHodnuR35P`e7zcWI{P5AlC{ygeVT=cqE_{TsBRLy`4ZV4$!E_Yj|>PAQn;eCkF zhALbL6Hxzh&>cTqx)nkmINYDRg+fbyn{iX}?lUY=;|-Q{VK6T&E|Mr{D`>)vxGDW_ z8n{f_t>k@LT53%?;+-X8UCg|Fe4_LAK7RZtbuuxRCl#ukif=M4_C5+C{(Vz;dgI(d zWMVHOv~#PqTZ1ViBDntr^9&+#W*lewX)aE+Q1mB7iHH@=0s^HFDO`$(j<>y^7AyLG z37H8@;Ln31Qovxuq$h}44J|hwN17`OJgGl%NFYjBSAE_j579F%l`khuAd!-n`Z_)f zB6y-%#bFE{vKu1=D+KW5>O`TBveyRAZvNOPAiOb@S24%k7a>J*fAoufK2vlnS%eQF z{4Ky1YsL7i?E_1WxhIR?A0MHkzMP_|1Jl4W)56f>QJimNnD@6>nt<%Q>vOEDF zm**z(#Uo$oNvCOLaCr`F@0c`rice0y)GZPtTW##PJ`3u2$X-PU!z{jhV2mdPsmjt> zjztphUwEQ>TZoeMG0+l5a2st#RVu}n$kRM|^1*%sw+|srcoVG=gSH>p4v~hb5zd^+ zz9!99;kN}L?Qf?H;PYG8$`r2g)CO^V2~WAw@+<48d+Q(PiO6zmF@>qsYvoZR*Rhgl zQ1X{Ox+wNAoal&>facwJ$u8YIp3xe)eKlQCLMAMrSGzx{CoIHR!n3f7Gb3WhpTO+b z#~qSikSR5lQ=N7XUx*F)wT!9Z)^nV+7IcnTy730Le|7!pN8~#P?oT9mnFt5?u=hQ| zf23BqshR_GDfIq==Dt}PyaFEh6(yG3n*0Qo*AgkV16B`Z7{nfLJ4y|tKkv_s6m8}} zka9p|nQRF1I+zGFv_tK#ocyG|VsXdW<{VW`*I*0@E+#w6`gtvzU!b1Zv$(pNU`?3R zUw6V9wfW60fNg}yk3kZ?!wFpL(AjFrDLJUDThMsH{S~zWj-o}#GZ5;BpFa@pcdrAm z3_u`gx_Tpty2kSozUwwtqHa4Z5p--)MSP>c=Q8%G9e%0O&Dxp1G!Y%)EQx5-Wq!73 z_Tl}{Nd2u{B7GdCn7dQL#1NIqpTFpalYdLOgvLJ?4%Ec)!nDTSKz_Z}9hc>(n9j5_ zef^=~s5^MEfBIN7qTyn$npIAL-NL3KC1Kcx<_)D4TQo*})B zi#u%NrTG+eIHs!L)BDi)p8{o3WY}%^dyS@#kB{zhl7(H$Om0U`m>~#Em2pD>KDfN9 zG^}>c6u3G0-qxw(=E?<8QG>kuF9>)m2S0qHWJR;JS}wyzATCz?+UF$|SwQ&5$!rZ2 z>Bf-Xc0-!&JJ^x7ywi6i8KHSG;J$I{-Wzd2$X{}GDz#(?&Eq-=GN-QYjND~Km=I-^ zW&Jpnr6ZCv-f*}SZCAY9;3fHkv`uoK;E83>-qYY)9TZ+)R()^cBIM{}6sc6QP6nq~ zc{<+J1B0=-FJ3+PPu80I&P({^6qwIx&x3($;$4m|XN=FMneZ=V)YIZ1->+$yxNIi4 znBSGGu=r{z8H?OVNW>0UUA``TsSOz)tBMeb<`t&Kjv;5b=pTOd?oBiHd6UpP&0rJo ziwVBS-iCWL6dCP@=^+APZhT?3Lw!St%}msg*w`lyvJzO0HCp3+o{92!EOoq5I0j!? zgOOb}i7&Uc;)h4_%g3l6d+x zfLCv+NdD{z@n=8#lfwa@r+fNgKjxxsp0M(RIdV)VSH__g@`o4gI+u@)TRLaF$#(P- zR%G&6zCP7*V8hfA)7FdV@i)3kBRK=x!nSmk!-QF~Vs58s;PBN5zughxTW?DkKbr!%Agmu4ylYduzX-V>O=TU5RX)lWD>~?Dzlw-7D8GrY zYUaP*pWK=d)e{Jq_h684U5T=LN2tb|RWvd|J7=?qjVO6wSvDG_P0vP?P!_5XH$f5P--_HK7(`k z=-c2<_D242Y=rv&vVa_&Fi ziA`1`y6}}Hm~w29%*2uq^oj)IY2PcPJzkM29XZ*rr?dxegt^Rd?4RviuGBm`rn4Q6 zdY--KcbiNvz}P)F(A+N;Y5CMtci?Au-|y|I4$aNg91=uUj-_lSUXvSnxItA%#Cw|; zgwlxSSGkgIf`Gv1HTB}8ulZb&E-=LI(9u@)w1!-TQK3#j5tpS5M~`7Oe6)>rOmQr$R@g@OB(GR+V7u z#J+8Z>y+`-yb*P zVvoR_C$~v>>XW^eJ#!)YNrKqbFV(No9o8I4Xpb zlJISZ$9Y9xslHk!fNxb={w5_ngGLb!N7Ftj{veL@%5`t3a<_m6XL*Lrg8&FE&DkWtn?o>SD%*^wBfI-j< ze!|&HCGeip<90(6?bF1*J=%O_{U01VHFVHQ*9vUwoMV5c|FIMi=9!6Y#h91HRzeu6 z;goixzXRG9 zN~J$5`WHpvG~h_R-8Z$CKbUr?)5M(hAXyD<8<{3>8QExx~2!FY&5OqxE&J4irG3hW2s6o?OZ9 zv$1$6>Y@sA2KB8NOpDq&H~(pi1%btlb~+^Dt8iV&UYKtk@JhW7!jDyIb=e7I1l?~h z5-+0*;KbKKwu)tMrXiYDMk-WML@Zyms#>alW-mp);7Xw9KpezEbK zpWb@enl|P*(wcSRYq9iK{Fi3(l(}b%Hc=a~^2YzFEkQA_8No*bL)DA|HaO@5eK|Kqj{AE*TJGPmrtNNNFz&=E^!k3XBd+g7 z2z?Vl+@4Ebje=3#kYuNKZfd2O+yk^Aefsp!$)WBd?6^sq9esyn%wZ`|wNJBD6d z>LYP5RVE}M5bqnbKr(LLNpr_(Z50w~eXTgoZw31{?>aseOsQ-!PoHHe^ul%HVR|ZH zm)cRILD_0VNw_uP5#HpobS^#F5}iFfUzklGOL1IW3{3^|ZK9Nw5kG$}og%F|p$S_N zIf=wvv#a(>Ev7l?!8fr06>0%5#|1&?_%gb38Ts9B)BOF zqYM%}^3=@w(qCPh-=2jY(?6+mdYY7gXaCxsh8=fEKn8YK%w&N7jkAAB{>~d#&G)hw zHHf13eWP%H+2TJtD20XtW*{!_Tl2CWrDZe*FDY2p2mg4j@Z(D=HKC93aX4!Wb?4f; z%ihSFRU#r417ow176lZCzI;o!bAWy7xH$)TBOg}J!|H6zf}w&?-*k}=Lr$w)kU#vg z%75^U+rdBAfLEaQ>|Jy1mEldcouRR@jvI)nqY#2)!EqxZknxo7t*FNUmb!%GPC$Ba zX_#ZaZh=JBKSPNIyY6JWQ?RPgla*p51qy8pV&pUa_Jj1%?Xa2X3H zB3Xnf(AG8C>5n#MoCbKLw_cGT8Qy~2q$%C6DoViE4PLAvySMd8TE*G@kkCY}`YWh( zV=;rQJvQ87r91mlVL&jBIaTX$MY>l3q`5bjV-52uNo>;Y;*1Y7=y40XkQHY4SLzu; z%qeMV?oJWIABkYxKan{|o<7^KL!FiM2qy zK|ttzjTk(XHoSf%%L*4v|5>DGq}71&i;!9qVHDFKGm$1|hV0$_t6B>f|MdaMh>;tr_s zA9fw&Q&P&fo@;v*a&>%x`$~{ECZjXS=p>FyZzdmOXGQO<%)()=yaZmhli zWa;EZ)ObZZrWd_V^D(l}3@RW*df4(}7Z(`JP|EMIan9wOVnbUb^Y-ke=$`}iqByrS z!{^n|$u>V5a#rPk!pVIy`>E=_61|w|X`Q8**ahDG_7k(2i)bn^#E%r_hP9_yURvXk z-Y@bxoZgG-E32nT`?gT>BvoV4G|TC8V(+dwHTbYps@1b&A+ujDy85xpPu znKXYqX3Q>JNx}Dy5AV#R#E+o$^6Z8U20e_ssZY0#4>TfbJxT$w1X-~aUXyG+UpY&9 z_)q_o-0x zShDUTnGj4sg$J1D^TI10_i1!oiesom5T6 zN8iQYzpu*9Lu%ez28WG`?#I6K6Uno>-WMng`ehn?I*u~E{0u=Nu;p*k^wadJ-XA?D zoa+-2(DmB*0rJ@#weZ_WI0;bFtu5cA{7Gn=EAGU39tbm|5p9izf;Vk|QuTd4=k2Y} zQvvN5>*>YCkyF3*Na`VHpy=Ia63xM*_<TK8kE*nOmT+dd0A+qoPme!qD3 z78@Je|Kl>gp2F$tqL8~6g54LgZ);7|`~dBcdg7p^KH#Y&^4dOIz$`p7y{O2tDC8`l zwY<$^Y*goii;K4ZStckmCAH8xd)t~tDag#S#QvNoy4P3^#xBi?5)4O#X6D88j{1j+gCsW@!cVgs`jKp{~ zb^Y{f&H7De<5chd@0{y=W6-Z6xzpNrJ=wrLrp_36Pqt8NI==Rz$;A6FhJ?$?#9ofY zKzSSmV0wotJ2+4udB-aQNhS}7Dm-$QCT&11(wSFm9`tMxaV4pFL_jfi6Dr(R!y|o+ zW&wQ;LnTp7aC0lm@ebUI{n$6)6Iw}s$~$J+&a8ces_0uCQLZS3@%rFi9KWy-APA+F z1{4ze`6_p(EF|H5s|^{3ciQ@YS@M1cSK`a#}bNU#s zHzd1cUuC!COX&YIpCZr;q*K+w@5QVp@K!YfXgty(A(Va zKAg5XXz}_@YoD#M=iYBdf}F$HtDz!Q8f12>z{wNveYKXKhLyd-kd-_`=S4(??B%>j z`e?aOE)~AHJF+9T``NO2G8A&~EEv8aulp_m+mFEkB7vjH<6#l|*eSh!|F}r_`1f$8 z>|fvi&ts6~>45g)4rt7k+D70JC^kPs9y=iGX-$R8y z>J))Pp8+d*q>;Zo^9D-ty{#;AVWBnBHBjLZt!I1XE9=aBua(_LoM#t&PkFPdE!8>K za0|&_xrK;EWayR`^(s?P(o?Od-qnu-nBp=ot4Y@O}>Z z6viLSKEz8l*Qvs649|?GX7smQ60GRgz%NmyF*JKGfkPGtimK3%57>}(BQ>?m{A!I= zN0p`i_Cr~9R^_qrc}&&``0zQ_@f*~$xtl*g_4F>1YYT3ClUm0kUp|OxGe~7mPnU$$ zWXQPIISjuN$L$kqDSX1pFE2Pk6wAwf*Lb+0WZ&}Rp!By;7|;MFp}M2}dahk`^JJ_4 zdVTxm0>5>W?Byn^BM&GIa@gRa{Lih~fIC&Y;vY()H(I?Lrp%bHu5F2U3x))gqBR!U zyN22IAKPIsqJFUFTzd5})hX5;mQ(1Lv{rI1iJprDEhJJuk<|{nk`;o>^;$H^s$6w2 z0Cr9~P43`@0>S6T4hDed@%-rI6bnyg!8#C<Rra#1)GXVB)I@ti5e5z{26OVwZ>cw+p~>I-TJ z1A;Nwf$yGhG{S2dZ%{FOGh*_`@ZxpaQ3q}&H;)RRBFx}@(1;CF>bk3G;!5Z(YrNX0 zd%9bJ#$zY@72)TE*05WoHYUIqV7YRu-PG%XY!MYTKyf>_(K)NtEC2E z92SluQS(xxwvQl)L2nf>Mf&F#AlE;gJem$_kEm`^GUsIjx!02Fb81B@b$cBr|KZz6 za_V0OJc2zx_z7^xRQbo>yJ_HnAxNgD#T|RCGD{6M2k1In|IPi}=WNX5uR9^`bbI$i z^%e>h4Z7n;UV9b)5GM5O2-YHfd(?&0m&X~U?0&!$6x|P?yxQwCe@~O{P4`yb+Fb>0 zf8oL?o5{$tCGlgE{VywT(zWcV1F3^r(f3nS_|YNRRmqd5M@OZAX|QtFjA>wItYcyE zEQS>5Lz{y9_?|uc4A4IAaDXI}lap&cKjjXxyjMh_0hxVyt~Y0Wy_dx<@|BASQAW-B zeJ6_?C@vHRQvi~4$Kk*=Anz72p%+hO=&}RJK_$d_OdwU8Q;QwWW-OY=&-b==yC~>f z;VYgUdD@M;;Vn~ECI$A$m4bshS<-sP3*FV2a3gb?K0d*Xojh$a^DJLwim z7O7q}v6FpG_#)w`rV+vuoySIuB9p z471mvA3_Xf=IE@jVIk!UlC=3lS#Gp9-rrfMVyTE|lg&uisank>ZMna{-C-Nhy!%RD z8uvn$(>SKuAo@q)MLybKDy~_huUf>fJ5Ecm56NU11O5|b_qUkG(!C_e7Pq7Fj^(Y= zASZYUFG~xVkqx)9@klC{jFF_tko|9it4Q3*mR%ef!Vb@mvyPz`gQ%T-GuM&CJwU{k zde;`pR{h!&5*z~yJorztr^tt1;vbQq!)k3zNoT%jXT?0Ym(vqb48Q1N#@ZQ4+CwnG zB^#F+Ms2^;X4gkP+`M3{2Um@JN((*SFzUq)fQQ3e#XByc*hkGI`>{3lMWePRJ@x`z zpWCs*e*D0;pUm&SdnQAw?B4%G$7X8752a#m;w`Y9MHt36I21?WzjFctI<7#uGIa^# zS`@%E?kz$PY|wQmvyV>;KqZETo|3%S+s&YP{-t5}VX^1zcOiKFg_wkd`#%}b54k}C z<>A%_EZpnDkOfBu!103V0GS)Ul>)W{7I&>>Pj(yy*Y}&XCK?)=K4GG(4%y^axC_`! z-#;rFG(_BosXW~x*2s&oyfKYg9s1(W{W60N=V0QT}v6JuJ{Rdm_zcTHoJ-GF_d@AQJh-@e*N)#7gfcN)F zWuoF!e)2SGYGP9pH@kS2j=F#PR2Pw#Ud?XSc51a#`OzmDl1Ld>;jh(O7caspF!nqH z0Lp=^%l9tNA$8Q=(bd)dt~ReBh(r$lvt)1YI$(byZxV#OcapylB_uKGd8ZWlg()SYr4(Yi6a-9dBkOjb(q63e$1_J-n8lkGnN_)+PBup)h zoS3?TAaSPFEAyBHG19z0JLgA5+duCZNC*>ps4N9=mXsz}c4^rR?Xcv&hzu zO^P4PllkxI^qc!+KIeqWt-wS+Brs*ua)bq=e1 zbfuXR+Gw;oc$)wMtq%P@KYzb=FB=`f-Dv-5(+XQ^Z-TJ$qWu)R!2AbEs|sGwtiql5 z&)5APj1)e*@|KI&G+*(ARoU^qrkL18zqqWzALlbDGCjVPV3OKi`gd!#e*6OX*O$1&l}o5S#+5#cZO&Te}S(ZP;ERE z*FS&0L?!Y1aV$rE>Z;1P(RJ_#i)khresUxQoKQV^$H3Sa_Ju@L7z|x`vo>0uC4dm4(S&|)2d-VU(;9pRIB;*nnd^JIq3|V2Fs#5LzT9U zad^P&^ zHLd&M;dViUHwExBhso~kH*ymov9MQE^DH^h990L7G+S2%sFuMOWW)P$Ea9Dk<}qM` zmT~0`!56wyaQ9*b&GOa}#A6%S_Nc?5$tsmy;-DyGKlJxye1Z^jp6*4mA6W5;GFmU$ z$r6Q}B}$KauzVXYsFkQkE`lEHaPe0gbDTJZTk7Y{_6WE2sw{Ho)pNhWaxIEHF%JxY z&SVI>1Nu-P47}wZ&q&wG;PeD2D-LKKKx8}|O@(TKzzU)cx(ADpZi&jC6h^stwFIi~ zp@Zs=FxaTA2#oLx!|Lont*PkO>gvm;wc!PPTFdc$&&VPH;n(EKsd^Pl?T!S1gZ-tY zMPMz|K995Lq_|k#<(Lv^Yrm}{r+E61M;7|JmW)%fR8uKg zx2~x2o0d72dg_M??oG|bCGSa1UQOfEi#9P$*6CE1uOyN0nXB0~yzpK1{OLn5)PgORSOf0B5eB2Gvzx$$D;J`*+pl1!@*;2 zL)|Jr{b9uZ=RpF+^iTJ>{z@O@OnwYiU)8u@q;;SNe--)n%s91U1YLQdyWqzz3D(lR z3a-^wM>n?visDBS9z%5UIW0N0{Ph>b%yRx3D1bBl#JHWw8R7vFfw0=ilW${rW_^2);RXKzIr`2$I)) z`7+;-P2-=iOzs;Y6#8%?m7K66<-FXP1kmK!xjQL&k~#F>%=#O-)68TgpFPLv`SX@D5%RA&YS2%|J(R5k(IFzGA8ZHPk2w?on=3E&Ruc zHLs+_hXk+$BwqmC5kS#guRF;PNJ4H=Qv109YhF``e}>M>g4U>2&8(mks~JPA!+zyj z*aW8VMT)vlNMHIFTf;>7wAv9)R+{B8r{9~;j6Tm0eDU&~UuB61S?Lc*G1D?zYuW@f z^FI1-(i#5u+9lqp)a5z2VdXy#($EEw=8Q3A?>MpRpKJUWUah!MPX~OX-*t8w zD|K$h8<2kLBttKvR12B^vaGgym{f!V7yUcKkNa*aOSl56W#ivnpHIL{>RkRQT~U=a z_qZnCja8S+$&@j=yPqlac?1E-(Q{=Pkz1*)xZ~s&$G&zUQ=oA!`Q7=YFmAX7_K#$i zx)l~MTWSB?KB}Yw%WLu4R!oPOLfAMH5F__H4mvi!@ZIm6gZpnBUy+zBp9fFjw4dH} zRo5iAIf&?f_``C)+$OLN>jH>K>s>&6$>RfnZW*f1)p?nRF(EJmJ%aQXr@_fag6`yL5VN=8=7vpIZxreKz;Jj9&9PMUcP4sqAk@p8CaG#Hy@RD9` zk}g;EMFEa?8_J&pLAOVop~E>J61mCJZReYrc@+}Bq(`l;oOQ{mfl_v#*3;oG+vHv> zMRKOdSy`Eqt32y1Yf^$j&uX4$c%R%KeLqaUH?gUwa`)j%=kinlv&FVa=t26xT&P6vml`alIx!!-t z*k5T-rJiJJa{3=`Y1o8??$roZm9jKO{SylSl2bQd%6hsnGsqE`%P{wNs3`P1TLwt^ zc*8RFGx~0fCdk;?TR~2?G))7u6E`qDFO4=&1BC?B<6+hP22)7RoW5Lu=K0eMfK1-< zgKWy3!-NqC%!&LLk&m74-hQ(rz_@EZ-_rp-aGr-ut(x`(5&^?2|H$&z1v%%X_B3zl zNxTuJ5ejs#F3$AzX)X$CS zf$^Z?`I8~Jto)`>R#+T@W6~u-VXpC{|)QVDuk}F92JvB*s?15`H`H5 zpfv91fN(MJNNE0(W$AvKjo51du3M-oCyb zzf2uc>$KRSnA3C{{oHT>;0wd=PY6?)MvDDT<)l<{qzh9a`x6bO5)~`_B(e7`elI?? z2nL#b^k>{)sy$lRV%%rtj?3)I-7r3l5mGmyeTRLg(=0^(HtKC_X4ajs23?7(B5{{s zW?naZ)#E`!lq~04rKUUXm!2<)!CCOmONrr=RFv{^gL7jfz2jBiVza>eq(*!W zkCM5Tzka)I>!+!t{dKfN_zI))=2f%3sOcjN2at@mykbZx@t?>}snNW%m*;8V2kOUA z0O+x8T0VTHvPsDhwFEJrM@mQnTAKvFE@djJXY+oi>ysQgkZiy1 z(@FEtWsZ5t`SZd0O-S*iy=l`OZSP&u`FHC<&g0_~VH|WXJ7xXK&E+g?NhG@cGq#Q+ zee8}zl6?->F1AG7Ia@y``YAaS1~8iS`YuoAyjODK+%gU@*K;6O>5W#QP&w#z!3H7? zFcBL9P9$6(MnHEY)6gRYUVYy7MzB>Fz(9q-k&+4y$md?MH+3hSYk{ku3qx0U{;vEP z(+A)20q^w$uoYHE01O5wx+V%lWi$mb0fe&F5=O`+@FLu0)laywBku80n)7ARPa!Zu z26?f1(tef$*{cDeig`;&3NS8SF@IzJUTgZC>MQ4tVs&Uu$?$(p9;pjPMcER6hXt_I zO6sztaYscUKPGpcZtxho+U=$ARX*kG)&9yaL|8q#wapXnN_ky6pCbZ)j1Mqk2(J$i zhjW9U{8e3GI%0{6wbLpBVV)E>xf76&XTDn|D-KqPbCVMxjBIf#wmW)K&(8EWw*T^g!_x-D;;N+myfC%=cAUQK5qg89oY)V zqw@wACFT3&t-HUD8}%+90h$4NT(LsGbG@50SS%dlZgk&Ams&ywRtrwGi}p=3&B>NK1^TGQF`e_1_yO zK8(k)H%^*=G5vud2|zz=Og}*d-gsDofB&9em}ibEX(%oFH%hh6X^0%m(tLb`k;X8m zX{LTZwdQcr;>-Y6;#Gf2aKr{`|3G&s*1!XWNpk(qu<~06$|PiQ{bh?=Fg@Q-jDt?W zDOnf;6KW~;gCp(aWZU{LIp~_c^k?GeOARJ)M8!eTcY!KiLSkYkz-8E~N9mkKgLem~}IPMx;=w+n3-etAbj}fs1qxc3BgsDPh1&~@L z+u!wRL>b6&omcnw&D;)-+brIAf)O!Ez~md|936uchp^}F&-4@IdT1ojBj)F;UsMRe zaYP;TgkZg_gTjY@{|W}|4_r^}(Su;*uQ>I$e}LBqfZQu=t7v@9nWfzTxVVDB!!`aF z>#Jk~JgV>8XtDiYclQj%?X3rZ^B> z*#NpoI!@(l3C|ahxCST53{XQ1wFq_mf_mqWh zgos*L$mteJ75y76bW?F>rM<`4yB4bbH(5GTRQ>JS9x$jf;((yEB@YwxzE znJ=Haxw*RnI_NMb6co0V-E+F?chMmf96iDaP6y_+Ud`bG6UZy`)y{t!iA1HW4blWv z>0)qSMPP189t1D~Xhq=^5;kJ${a1PY;f%x}LyzgTaBLa>Eg$J3ZkdIike*Wz&kE@1 zKrKfgPIqe-N?lVt9nV6d8=@i3(-yDYMqkl%3to)C@-bHB;4P{dwyE2wzbWMJ1XZYL z;Oqp+p|LmjEXPwS=2a?zP-teY&H@4Fy)Y)iWcbriW_z-SkwazPyxo;zWaD4orf7nF zuYKC87%OC7B#Jk{exkgWi~4aq3t1;)33b)9XEgAkkw@R=5%0JhLA zk|i67BgQuZ@NLSaZaexev(=w_*S`l%?+R{)^$oSY|F+ibBr!Qpz=)!r!*OZIQG4=1 zY1Wz{oWx}N;$XS|OMF6uryrK=f0V>R-lZR;Rw+m>yl@goVC0#@!sE+}Ua;*!WUn9d zgOTIpi_}}j9X_kRo)5Bm{rzG@fUyA-H{O~Y8Sv9b@I`BzrJ8-8-xM(9IMf4` z(-gU&b--=}F7NEq+(9QNr$gMZU#%{!uB*SY>VpVCuw&|6NeALM)m=X(nxEetuc_o= z9jN!?3#V&-i<7;#TH5^M_Gg8ri45Z8M*&OOlXLp@yZ@YefPCe0{77dwTcs*O`_6%3 zq_niO=(R}n&f{|?c^VyQY3Z?#gGS0WDwtJ;!L0R=HafO#+fF*}*#7o=&K>u=|7+CPqxMs))|zW#W%+=s#z7>__el;M ztF#|gk-=mj_!dAV={5tsuR zd`qkE^@u+*0KiY%zm4WBQzW4?&%`+Io6oM}iOF$uXOOj);fZA%&s}yx@f^Cd#z{rE ztVuJ^_syNDtLM@vl7?p&y6_ARi(lxQiKveF1Ghrg!qb=i&I zwQgED_PAbL{XAmzpjcqJ!A1Nk;;~MeU5GPe?v5+<6-7lwt0HIpZYzoM_t0MY*su5hu`0%Ge_s-Q z4K*Cmf{r&?skYrMr;T3m)&S`Q+kj9(uQQzQ;`$QMxvp$ zad5V^CKErsY&p{uHmND2w6Zg6G|z*@Tb&z|tjLzD7l%?mo zyxnUcQ=OSd48a9Io>*6nEEs!h;NwmSBUx0{BYuf2wOa;Pi+ZWYLoZ1=Lh0d$rP}x~ zPcWD>db4hY=o>Ms+3~U=9e?7Dfle{8JwEj)u3veOGMcu&*QY-&#JLd|VcEPg=Qt>|!QH1{z5sjvO z^TDS%n0@p6-IgoMktP~;4Qy2dXAsq3;1X|0ngMc>@jA4U}2vupvrXN*A#} zK5A%$+FIEa)NU#k+-Dh;g_8eU~G_vIpW4&(W6q<5%uN$&hXVhHCFP$<%Tgk2Eh z-&vy4)m2YbHgn4O9byckH^}oE{&`+(<@DyyG1Zblc4lZx%ILQUA9gb{TYphOsCp(H z=R8@-?`p*5n@b11iRps7xu{d2)VB7YI&>*5Wf(5a+j#CBt^0mg##{xP-S}}AuRCm z(8#g|@L55PzGb(N^6EroC&+n=vLj2S-FOazVodN{nO=q7FW3v&`R2ew$EmIMY_~h< zk6p#u!3H_NVt7meX!obU1pmE%l1^t;<}d(<`8LPFBlqHk4f57sYOLB0W_nqq5mnF7 zG%2-70q%^U0d6`GWIq!- z(OmvPP)W}Rk3iO>X;urr(&Nr zxcgqcT05;fEGX>(owUP3_!92VzZ9L)ha-ntk%^uggLSVCLp%_El-^Ptqxez{*{5Rb zAHD;%j}B+ef-({%4Fjptm`{D9(z?7WzN=3*CuTx!oEZAlW~AhZ&(Lwx)o^e4Usr(3 znOeQsk`nVvWBxi6IXe5K)}rPDC=RZpiz-EqF)D2r=`Zlh&&%D>ms=#ssQdIpk1jZo z#+wy~7Njiz>N4g3y^0WQHUPPMY*KNtGYpV#tWsw`vuCVrfxa_9tOB^8GU!wE^}^@( z;nz%!q18ii1EcO|BMsP6&;+925q{rWn3lG-dn0~ZVg|b)|Lyut3-8bV{VQy)PT8)u zBSYnQ6#*56rknihu(~V6l969+hd5r0PosFA1>2}i1Il$wvV2@oDh0o~uLbddDQXnj z-sV2K6{HZJ1lx5O=+81ujrxZNTueT(TmX3yCFNCbrz5 zjao#t-kzpV>;)P&VwwZ{s0-%)_&uK?6of(;Ian^z%D(Up?roxGj>X%7@DV14m^l4H zJ@J(EK2BRBFmX1ECrLKfujQT(Zx>2eVd92QS~dzu?9_$ve=Ks-|e&x@mK_WrBtdM zUCd&NcAb?6Cf*VV@6XHSi{bdB7i#=*#)$;MFI=Irqh`A;BDTJXtH|1B-|VIcp*o^% zxMz2 z_0;_XgXBrm`&3MEB)GI2uo%q58g(;8{p zdQ%FK+S!}g-<*LVt-3xP=IEJzkB?+?S7I4K(#h&Ia{i&#Wiw-z%uy`smD1RBx$j`E zT1}RPT0bC*=FC6_=9W&0vof2-^+1!6VWc97GmrSTENYrK3qEI==6aQvU}#jteA&%X zV}ip#s*LSa0u8}~C!j8DB!`Kxqdn`m#IJ^h!M0@9kea2jdX)YM$xbJ|SlP?as>>rm zD;{76A0k;dWl~gzZr+eBM9I~X)Imd&6NNm%1{y9%v8LsQqr9IEsDYR&r=qk_wjdrZT4HZfcb$b%) zD^r4o-8g~gc;z2yiauNuMxblo>nhoL@B1_}vl4tw*u_|V#2Pcx^mNT{}t?Ip-=sWF0fB;4DZys()o1#^WwVH+FTR68TBx#eYXNeie8&YF`)Ai=iV$EV->QT`@pJI5EJL#h52 zv6ST_u0je%6{FTL)+>Nr@ziGM=JZsSPBme|(g&(=32ABRz&anHo(V7*_W=0%N$5A# zL?EtWwa^xh(b!;sN<~TZ^>+}=#3(Z9?8q#Q$QRnddHVd zFrQ4_HDo^`$kS0exl-qwiv*Msc70LIs|_BZ3#a{MRD2H^+{|a@-BG zovyJ+FfvFvaTCun*Z9I0F3Fb(4HIBzvA-vM_2`S?I0Sjw!ZJd_Wo{J$Fep^n3q1u- zA}pPC=*w&NS7fgFt1!o34^;9}Evh}%FX?J{HpymPQGUXrr{q}GAI)U>{#p_Kc^nKPb>+jO_+8G|DKj7W{;5l>Q*%H0#C;_zxZiOQPxP>${ zINf)D`+1ZKGq=~s_EX>Vxc9PxJLkCl+Zyhyc?Dst(vf6IfNpl4Y{BBR$J1%CA5>Ta za>!VX#YF#J0U;}b3&Co zw*nD)9GB_!9`F~m8vw$u=IXOqMxyng8etkj_>--`jIPHKOX8;B734ao3K>){fw}>F zY7m0$OE^qh?dJD`a9V{T3<`5njrsY{Om%S}B1;+7zce?&36DDX5G41p8{~`A5h4Qq zD**DG{6lT8!s?jjk6Mbxok{3pKB&+?XkGlJFrMSLsrfCeiEU*Cy1Q@qoY91OVVz{s z{#j_Y2`(bsb4jt@i#~#x1Qev0ULDx{(*ZO8`Y;3H3KH_rT^&tABYxEp9d*AbZ&&EZso_0CU?oZG% zkY-InK&1XEvloIJAt8r^k$HEY}%{I|4xdUCsa^==Dw*ysuYVy*GBC-J!_K`jp=X5&abvw}(EOr<3u!1H9hywQx; z=?Dt{=&JjDB6!pP+&#KWjbx=3kYB;*xdAfzg-_5eCwH|$LlHU0)ux+DlX1dsqm7nA zWlWgUBXnl|3!6dZmT#}VLG)5W8a6XxE=myRTdz{F+2BRscC3<$w9(KM1mSr^1Kh3o zB_uGSg{YGAhejY9-|mJ5Ri_ zluiwM_<8UK$Ig`aTO#I|UYZTvYYXq+#fZ5D0LmtU5jx_8Fy`c0Xk9#isw=g6aW&|akq5`PBbR#%KSs!pqJQaO*8>E3Zx}S;Y zmn^TtVUmeaxmraGWS-O``Y=6h{oc8Ct9f5`NC^7N<*t|Z{Wh@*98ov9?dR+Ho(FhT zD$rt2Qhj`{wz$17Weup>zXm#7y{LYYnufi0-snTedtMW!l*Z4mev)RW#1hG6pR2#LITgj zjPv!atXL#=^ojcuxZ6?F0iTNt29P)%3Yjeg>90>#sqlG=zAn>&?Lr)_GEdb7qtj zgdeo~I?-uEC9jF4kqh%x`A%s|wHbw^JMV(^V-GSwy-bHrcp;+DZ-&woG<3C*V!XU; z79^y=Tcq>r#2KVrd1P=48Dy9+Bp+GvyX|;sqtd6`xm2|7CF6ifKE|lkRN|$$8;#5K zb$q+8G-Us%u}8kdakQw&kLAkMnxk>EN#oU6ou=}t(3(<90Rx~j*h%u1L95XZ#aiu% zupw7CDeCsK6&=~!EG8p9AmY?wj{s%!K<}dTOg9HC$kuI-AEY=qj(VJbo0AiPNJqBkh7}MijdN=pJj}k=<#xC=}`-A3v9jBL$J$)S$OWVD8(!=^W2`02K zl0c1Z;`+WLx9i|(thHo(0+B8i;iu17zM@y6q)_RPqoAAX=E(71u!^P>I-ZJpaUlq? zMfO51+@k7^ZpoD~nGR@+hA>Ig>eQ+P_)n?AQ`7~_u@~)Wqx0^P$~nq|5ovyD!$(09 z(L`>2Q^AZfHQYqqWbVeQWYhAJyV7g2mz56_D+Y?NxAGX>6mZPw%av1YbD*aX)E0En z5f8#pKW0rxA>W9Pz05!(@7ZqUAxy9o^WZg0JQrOMF|zwFg_wT6FHp3usTMxh_m|(h<%S<&%f zHDmVI*Fp*K2n4yfx*Kh}or0m9s5y4>ze~#*|#vZWJ$60=MM-?aswD�Dnu zAE*IsWsm)@?5sHoW5Ua2L@(ysGeKqqY|-@$M{;thxHX0n;BiXythbI5zeG ztjPXmJOkP3@{V;5+qjw#@XCs+q4I{uQ{_eULqwh9|HXz3;t&XBT*Z z8iq+?HrlI(goJE0-AIb*FuUdNCuoh}x*QQSYzpuaz6dz8dV|1ra*^jy0PcYSE$b9c z`o$p``sUy}%M0~ejo3INaqpm?+P7>v&&w}CGzc%h)TKl%iYInT@z>sE8iSDm6v#=M zC~+!kh!6B2_bMev%`Qvje;YvjmZ}Y5v&DfjYdUllb0$yGPF~&&!Lz~EICFn4d}QS2 zokkLu!=4OTtT0TyKt|dOl6bM{Wi3oVjw0((b;A0(_qJN&e(g}vI6d`AUix@8YxD;q z|Jc6_R`2N@w$DKgIlnmI_NT2zlqTaLfIq?TtIN}@*o$!i z`d~olpTlx0Ryv|ny-H-2IT)u1(8P>_C0v9V+&9vPPy5W5>5N^guTod)b(Nj9afu8S zeV0gfA{@M|4^0@^r1_*M3ca}}-pUIFW_ngvO(ngO5=2>R=}XF=4CnWDT@#`k<|)GoLKN6?E!qWD?7jv*ETy; z>g{RH5+a^lw9Wyim>f*)!B^A9RHhO-IuZyJ6elSFd8-#YzbmB6fTW7U5Z8;rOOsS9 zAQLj==$)pYN$%<2y6Ugm=IR*1@6YQVCZZeGQBPsAHZt2*)%@mJGGn z68Jta&lq&Dx$QCmdhUa+O>iiH#cme8jy}qzBXlQMpCyBC-8MEjr?{mBW07s}17!Gb z$AaRrH5N0dYaO%a$9S2?ER?`dt)H)51p9%Yz$^96+g9h<*=0XVwG^pRU=HQ@u``gx zZg^6o7j1_*{&!)m7Z3mRzfa2<*JDKw^}9a2ZMYV;L|kDW$A|LU_fKj(xq28luMv=mh?u-Zz;BI4 z_}&La{rCRi5k6ju>}5owci{57Eg`Gso0jwADDLAiuJ@?7_su}FMT;Z40&m#aCE2gE zx;iG#=X~+s%-#>)~wCWBDj;epe7FRwMaYXs->SEaw+cxxf4SKa4TneQk5T zABZVoZ7!f>{0s6l+vM!8}loz7&J zQ}e?H?dY+#e&W$GqOeL#tce)uReP)xTNe~1nv)Mis<9+_>I}$(7mV8Ua(8YGe{w%QCyM@?^!@rTY?ogCkEC%18b|yEE8Y{(X zXo}7nxc{hjJgPs`@S#?eD2AspzvMn0Y0{GTG7`S(rHup0n65Ef4AwD+D($u@9*aN zd4rWvQ4~$p0}=WZYx-A=!s;?Jh-NH7h};K1mwjP=B-}}1%jHb=f9s2(!~omLgB~b? ztrK2r=_a`(y-v5=oa@j0zZrh-H(^?jLrGh1hwxo>*5tLG%avGyzIbrQvnNZc3de!> zsSQ?>7<{X6qjkURjMDoA4hezsv&Hp!aZx?~)?FXYIDs#ZaiFW~+9*)MiPJ6u+B^b_ zjNiiIbFNmZkTJEa(H+tIwePO$dqysYCz$(g-(g-a`xfDdyS6HGRoy?#(b_A^-~ide=4sdR8DO}@mFTD8|)fPo!u z1X-9Ht*|tbrE7_SYB4Fgt8GEGE8-%NMqfCNC$3#*((2$y0Qf=S^HMbt`V*5-Q;qA7 z`!YP~g>wKFYOKuVIsr@Y=DoNfgeHecfJtLk*Q&kvHsPXqS-{}L6~;XsQkqc7j4s^Z zh3uIvV#k3+Iz6pK;!o2L+)5-UReci)<1eGb+v%3$EWxvqRfeDxf(dw8Bav)&uC40A zgtX>sSL8VP9|SerB)?;zZHA0s1X#Uh=F*^&WUx%JA>U25Vgbzb1>e?CeXUhMAt8a4 zm0qWn#q93y1S5n^JW`+-$vC6mJ8vw%8!;x@rJaK{j2k`bZ_(ky5{ZYYB?y_9n%*HR#Yd^uf*V*r(`>wq%H@_>*)ZtzGwgesf z-W;I614Ew0(K$O8M3{rm^*hk9=st5!+Ok?DmF&Cf!NoKKFyc8U7XTHmPl3}>8Uy7w zg7Mg&t~1Vpv4dI=Sfhbi-4Wm?21gTBqCoG=s4~S4m5YE;D7C2c7}E3l%WQ=Gor}=r z>lC{!a7uT$l(=h5x2mx=ngqK+1Wk|bCxWtehrNADz)0K)vEJ(UZJ^(_kj{PAO*@sb zQ0mG(Ny%=aXF-grr8)w8m*tem4%%(ATK?RgR_jf2dZp__TFJD^?=N2FKS)!bi3Cm_rAx}R5GG*}Ip-mAgnL55?}@)rBfu+H1tw#2 zc!Si6oT9>JNryQH+Jr>0iW4h@%kH`nx8ic7f*&bS$9=5NfTr*m5=``PtM@Oblmf$; zy-%)BZZ3egtH_^~FKcCAISzi`?^vY)X4=HisYPmz###1G!jOF#G?zN^@-c*-jKoHi zKx@cfx%%}Df7>urAMpMrzYBpjUJqhs&KZ!WZy4EQ zk4$TIHUSYT)HeNi@pvzYQdt(a%#Fh4IbfFWZ&A*Hr(yrZe0fPg z8QplaZenp-kv;;4C7c@Y6it^1&1&KZ6AuLcTKS*bO!Zd;hK5hCznl;cK9$dsyTD-G zawD@EdTbbD%ZIiZg3mt_92$`IkG=R6a#!~dM!sW5XHcL?Laov8?5v9rfXfWGTS&_Qv24})x+anLA3 zXv0Pzz)h(IP!aNzM*!&B4RL?s{HU`NeAt!%blUP|8Sh5q8xyqe3CVQfSu)pMCC{4# zA~}O&$L7F7Dx!J_EjMWl^kfEF?W{G};R02@Xn3Q7B0sUDz-xdTHXz9gDC6Ac1d_-8 z^Ma2sC28me_om}Qcyx6ov)xpzrXdeya^#n`NbB9jC3egPYi?#QR;^%iiUv}pS%iVY zq;_ut)tquPc#()@MXed*8}yF+huat|Z>pwWIr_f-zD$2o=b_45TtkGYAMOi%2t2$D zl?_V)nC2-E^3MUGDs2d-nW1@asnTO}|WdA0sZ5 zi%%g9YU_!WknjUch&EZrUU@*hKbVIJ7Y)n4>D?5KK5>2MXb$ zQX5fbqfwuh2UymVzTwRBkLhBLRg-*$q3E&EKhv!I9lF@;U0gyTN7y`D5RK{q9)`WK zQ)?_QBT?r|V8TWDl8j&>KA;_cHf}CrEdoi=Fq)BJ;Gnl@wfP}N$@6Bzpz^7NRF*eV zurG)vwGNu43viWx!!m8 z^kLrn8SDl%qz5~5pB9yQ;GLN1hDL1IVpFK|RBJK1+{EQRZ@s*%wa5>&L^y=c+s!fpTktdNV2X|uh(Qnb$8FI(zl=MFm$&!wSupr8o-~j%j9^1{QQDzo zgqRpS&C< zPhqIFT{OkJ(yea&Afqhcn*Vb5T0IEq`4HKpWQxhXnSbMd})z^|j19`a|-c*-K zd38A#@Tk9*=!h9i_vInufzu*g?x1jMkcvp4c5UOc5J@wyB0HL`)OPn%r#fr6*vij| zp1(^?MgCSB`A6N2=p!4Pl2xAZKq!@QK80>b5mKs-y)k-9Su?rIXR?AovYrHr#z?u2gEc0ek~T&L1w_h=u%EIQ(^YiTWJsghifM)9O?%N9z4 z7vdV_SP*rrycLHv!wVl?v^S=Cafn@qbo&f;S;5vNBB7pdsEkkz1*;ahn{>;O3R{Zf z;L@+&tIp!4taH#Exn(5qn>07Mrwk4sU^kDrn#9sY<0UsuGyQ@wEqxilRLxeh%QdV~ z@?=2=@3f_i*={NmGVh41rz|+w*Uj>Y`KC)7cJ7tI;>);W;d6zVQUKYvcywdzUW(I8 zaz;`|M}0iXb&nFntp@tpm#7jY`rSmepx$&6C{0;}X`*6uOKYjEOXWLh>4|2Y=UDO< zS#0yO6otgtE+(O6&T6;2>ZV@0Ti}6%z)TJDLkRWD?5t3$YmKVE8jYz*lFdf^cAv&* zDpcQH8uv$xEmu^`xyG7=JRPm}dx+9XXgMSKFty{tuBx;CW|qO7ITda>2urm*S~uoJ z9++Ru^Ld8xwBA@@N7U}J4Ap^A)l^%yPLKX0Jz!X+u(=+u)>!mstOaw7Q#?1yQ<>#9 zmB7qT+};Id##M}M`7#sZxm4JDCHHZ(ij#TQ?wQ12ViATqu?}8EGAVh59S@$AQ4=)! zyZQ$uHL#~pnUO*{zu>H-9W~k}#NQN3;slmz9>JcK4X6q*zRM5kE(2*D1QnXf6(m0+WjZcH2TZ=PAIN@2A=!$ zN6_juvm6e|Zs<_j=f!;|KB=R@Jc&ozUvxo~K_c?jk_qkKLK52N7=sP24$PwG^k1y9-|6Dd<5(DyEdA zq~(U*(c?w85L|gJ%*h5TuauqTC;aEZ~>_^74xyj<^#! z@(Tlf0_(gLc;Sbm!?o;k&tMMYAh0;~ZdDAeDkuw0$!_Wk0va?$5m%zTKcFq7*1Z;AE0$B&(e2ny%wGTTnzypCajb;R?!@(8I<(5XWN`%Dx|BQ!E9IZ<;rNli+T%H zXBG#?`L?m4#w`BT;m?vZcHmOJ+l6&pxL{3^!1fo?QI#i4B!i!H^G-Rab)3giw$y3- zThG1d@{X~gdRVB^fHB%!)PcK!3~cMlY1v_l8%s81m}u7XGU4a0j7!@CDdaKeHGJ=n zlCr8lE}a|CN=@uC=r}OFjIL0XY9)6rE?^yWuSewelF0ZZ*d|Jm(l|Y(DAmqpPYu0l zmx~+3G*;nZq-QFj(Gkx3jA4UpSZ;Z-Xf!gAC^Ppm@1K+1?!`~tGGUzX?hNpv2|RBg zpC#b;EUp^1!?IQli|Xe)iQY>#@k1bnA!d@>w>ebdE!1`{dk-4~Fy0EI&jFw7|6Ib8Os1yWKuARS5S_g+ z`?Eptwd?5Ru9&KBNxHyKU*5y6#|m+{_oAHUrSxEOC!;YLXA>RWdZO1 zp8ubpZ7KaaK^`1Y9nvrub$V^Ikbroqf=_&qMJb{xQ8L7tX8O?_-0tsIcFdz zV|iF|wH836=tYJ~Y9&+rW3O@u0mh?EC0-`hSY*(YVkK@Q^@`Z0(uIHOjk#Ddb~ygE zTq~&OZ-AEUA2V3Dg@wLP@fHY2eyI{$98`%l1nPno2I(U5n@Gj@%KuF}?6yoSNYwCh z@OvyHMVb5j^y)_>Kyb}wx)|&-xx!|{X3)QhrsU^4vzmS1Gf?Ht<0Saj+UY)}*X21x z%bWmB)gKk` z#V17k5lkN~ve?Bq$@WT69x6;;az9uXAk=LyNl~XrW?emgY2ijY9Hdj=jwXqBt@i7x zKR*A@A;W>BCTxfPI6m6Sc^8$;Mbrx%62~Fc^4iH~mCfefW}Q+QO0PV?6k?EPW0j-T2=D3$~{Jh7F9oWeL0a zxvfX#f@aFJ{61%W`um;xaYoaf1;&Qji>$s^XV+S+e>C6M7%oU#0Lhdj_uDS?-|$&! z{Gxk~1#VC1Q#s!ZD#r{h+xIrwoVJ7Z+!?nPU6J!iqeRZX*Y1Z5*4n+uUpH8}pXh7D zb}s+^)wH&OI`w|Lw)Og_Tr3q}#pEo5s;)?x^_yRAGwnJUsVzs(c1qs#t62MLHzz9A z=~?&Ro4Evg$>~N@8O{tNwtq*O+mCjfUG?N5=(MClR-!2_PnGLtvqlj>{8g;Z6+Apj_m#r>QvBT4G;4&`L`89xlV; zEs6cIo861y3(BbV#w3+kK5S=jTV%^LXDkrNDKeQ3aeB0#?q;o3bO*W*jCl(j6ingL* z3EyOa$n7`uH1?e;vq#U#U*(z}43u;|;yal8Iv3*5Y5Y(18Kgk(vICbkD`ay@2zfE9 zN7N$`&pht1B5S18+Udx44WY=j-kgC^F~*`ckL6FXa;?VWd{v2MPm;6Flu%KyNN!Hl zvrp}!M3#cJPtCS5j*?=PUs_s>ccl6^y?>x`x-B%roh$YUBiC`4Q}+nE@yxdOXj@ZWhVmtX9_`mqXxVdOwjcI*rdL%* zMsy}`>J(h>-mBmzmV*ptnePMql45pjd!@gN*QuU6X4cm&szf+8ovs%jJZk3pyDnjA z9aaLGiu^wGjOWb--jJG<_aM!j@ zw}3ttV6Qtk!bI;^7aWmwR^}+%V-@4Es?Mvfy-2;w8qQ)G)F+X2HgTkI$mcGcf_RFL*6Vyksx_R_f&?$eE2iZrP$mdX1J62 zVmsIW^R0!CNiHPs7Q%fd%cT^}-`&L*lgtlgU=mB!+n)5Gc$Q#De`mlrqEBUGU*wD* zO4M4h>&>ydXFSZIR4DDUmv8{S?t@}V(TmLsX>#T2PGhz$#SD~N9R4`9=V4sYZOO2jF&z`bL8B>z< zlo;%wf-yzETxmkcqiyd=-ctA7Pr5F1>VdJv_J7Y!)xQa5vtet(w!HzYyPg7%F$~+Z zev0q0put*EI*y+9vWS$^H5SId5Ounla+vHJV%tf{aPuxn9bgw;?t&@FQ8BZpvlw#5 zFL%Ev6q_*rr2lW6@*pv_Q@0fB^EdoecR1DIkMl2G?=9?@9}1+{&tMc+0e5nSowvT$60Hrr3N7$NN4^MK*AF-1xHvVUj+7{K^_e@ zhk^>g-b@RNzy~9jn#LJ$BAaOwiCKA)Pg8F?nkb#gVr|F<5E=jFXt=1)8gp2WO^^yQ z8%4#EiU=@0Y+>LaVFI|~)s8yPUfz1zo?gPWG-oUe0Yh+sZO4W|VqstrOV__q?N{ri zx#kj5gt@^h$b_?>f8+}NAaK9i2fnxe5b1`iC>Lxs|L<6_5JeeKjweUgR8;~HJbnoW zcNt`>x6ShAct3#S#gT^o4&NrZ5JxZkp9$uSDZf8ndrXQJv)}1Vv>4c*LjXS{cw$5G{}+tdx37bA$hQF?l*Lwt)(CfC;k zT!v;+nfs_Vfp$->s5?^S3gs%MSRktgQfYt{CDPFBF7> zXjl?v#Nsj$CthtK{$;5FNWR@Fuz*R2C>MUb(Opc74T{hkQm&l9u#;C%-%u+6+Hl%M}sfb_mjFsFye}$b?Lua`rH} zzI)5>T>}k0V#aonY4KY-45Q z4^&HI(YDDz`>5l<8UXRbj?@RZ?{|lzX82e_p;!_jW@MHdgTgOf2LbU1kroqHbAP-U zBV4KboevO|Pin8_3r)>cC+KwNgjj?cc*q{lc2db)=5!$AGzUb@VvUx21${j+tn4&K zOm{+zc7Jlk=4BFPx!&M~RV&(>PB$QU;pza*m7M-{+yiH;i8-$3kfa&H*}n5>zGh%I zT_$hQUzo5|3b^z6wDq+lt>oaI(&9(N3_Di7X#WdG(4MQ$!C#+}qWDaNLgCDiDJiQ+%ZoZ4?abqG zKJNzmvr`_J`2L~-SPD~=d^t;xOXm>ERx>|~L zI-T0Ys*1YK#01yJ^GuQ?Q(7X1a5UViF`c73v}N6H$@I-~vpykD zgW9Jo?8H-(T|Sy{S&qkQVRv2PwF<%55xCct7!0=eAHcCq$_SiyCQC^@KtC#T) z)RP!f71>0qO*|GKY`5!E(zLXa%T!;m+>ZK~Xyn9tR2AEa(hPy_TnVVe(4paCa}ieg z$9P;-MUx)!EDwmVndo0LLye4{HDVNY+9RdR zOxUa@;O#36VygJ0xZq1mzdp=Z%Xiy@juhdUUZbt(xEU2U3Eh=g8&U^msy{9aa>z!T z9N$73ssHKq7wKV;jxNtz2^&vIVKC;%`IH8gtv(sbV@*d`G}XBebFA1-Ds#3_KntK1 zORg}hAP^M~CB<9U6mj$Wr^vA!9%RThb!z%vyGhXHP9#Q^&Ancntdr2;WXiQ{Kk?bL z{GQ^ofgCm93&OX_HIB20C(xRNrh6*2#@c^)(;=&@NeAYn-;`qB1`y2q*YKUvbZWK9 z^J+Ff(r-csloL*VK-_1r*`tx_jt?AOoNagJ;_J8_X1dB{cZZvt(3?iL=kDsg>$v>| z=?uMHJZ83-5j)4VGpEy?O4sF8d~%sjqc_BYG_SknwM|P77lSL)y@5S$A)`hEpqL9* z{(k`GKpDRuyn}x|--aQ~pnY1TzBEZqm`n4bnV5K?m?BJ*o~Q4jw>5)QCUvd9iUF=~ z8bCTOp6a`4u=}YkY){wIxNr=nI>pD6h*S;7UOta`<4pRp5|Qua$-ix+O(vK)e;m`N z)ly$)GW$f6!rl)0ItIw)+*tZq8N?k3DRHxT_IC7AH@TS^EhBI@?4fhCocGshCXBKZxg2bdTT={Q{ z*m2tmmSrunUA@TqMy8B%_}8B{vFZhfJ^4Cb^}11vYTkugaOv66$@5R{f<~7UFCEXw zaVfmLzPrL2(a32ROrxo*l?QIzL`R>;n0a;7dKvD2a2s3qI9RrcWeG^9@f?qWAP%wM#Cwa?u}A0~z{ zs2|aY%=Xit$@__LoGeZ@6ZoZNz|C+x(gJX98X=pip||N7ftTuj^A&lKQA#Y z-v#rKdNGha}rIfNR&#a!eBgn>)$ZH!v<` zlj+VOYU{Cbd2$(-ge`CfGPvewl4%=Elc{IU<&9IPQ8T)M`kqdn-q?>JQdqS$EVyhg zf((z`xr2^=iD?VEw|1~AGluity@Wm6d)T$1ooz34(e3(kX5z^+xV*WAf)tFLH4^8U zcFe{!Y2lLTDPSiJM0S9@Ymur;20nFzR6~lUaW-=nHIsC*ba(mgluZw;V^?l~exsju z@)W3Q=T)lG(M(4;pc81wqUIu`C2pBVUIuj3)iD-dyoH4f2Wp;wepcYUhe>{yYtMta4xkA z`q=esD;s*dCo?_UB8~po^IAJ-%iG^VeV-Y zXqi$^a$ROn|B@^e=-AT6GpqYZBm_<-LsqKeXTb>Ct}Hv*{sCNX1e50_*|l~zxzSBb z9A&WQsRFJmh%_3U-eWv-nowONsOrAfAjazn* z@dSmv-8}N=)r_6h%-AU-IQEibm_2DdH{I2V^gMcZb+Uf>ZU&^q)Y>{mdBJ3oehz(o zjs=PPNRFuI_}46E<^Y(Yz~+ZHvU9+|u%T~PH@yH9$b`Y4RYFs8K zGQnEXrKfW*C7I#0KRoG0X8Q09+8{NoI_*ZCkQOY4>VAA-lu^1_&o{s4a~Za*xSyZ@ z;XZmL7#J9~O)`Ge^E|0zybU&tl5LQPkJjob=8UFiex$F(xyoB-n_+?dfCvQ#)wO zv~%j~=dtLd(QLnU4^l$5y_*eB?O=D8L9eXm+>=Jpv}|t}iSN07df!0#_WSHOw6AMp z)q@=*lLkf@neL~o?bYUlFd*BPBP-I(oh7*IpPkg7F`mVfJ=WcxC6mkGCYwp7OdQ9- ztVv;d1#(%3Mj`3lzL(Ya?PAs1EN^`GWTtNDVPzKb{e3)h!?QeNkKyvGrg1{+01rLh z2}pW&bBFYv?Gr-H{O$Mpk@41;WcAF_5l)5nZtTlWA1nH;-bcnB~NxPk_U%vx4 zXqTZm`K2f@uX9tkd2!LzoIozZtF>8}Fj!9FSFcHnQG>^@)w=IL$ zT+g(LNqTqpviep@*eDDf`xBf%#=kV*}fy#j+QjBd$VlY*voO3Ok&*QT^IuF z+7!(b8W3ZXOkdat18o%Yk|Y8vouGMQJ*G4aHpvX+8R+SzEi;2@OUBV@_A~wD(Kx$0=p(q`2%eP#o_ySnJf z7)+f#0;i`RM{P$leSg1=ffcw(+}>`s?e>_oWD--l_b~GK@mQVRwCyVRZV(}Gvjqw{ zK_X?v=6{u{RHc^}9nhrQp2gYcPokwcMSV*>W2e;7wXT(ZyOuepO=i*Q zlbExxiOi;5JpII8`g6XOH`kG6KHxVCRc!wje_-$x6;w$B3&1^uAZrrl618>G>(AH&vcN>OS1hQjdR9PKhVp7 znP&Rpv5cQFiW$d_B9-Z3^#fbk(dJM$VI*@FjAX)`7G^CT&z$31NIN-NH)WZ4+C(Ny zYGV4rvCKJsEK_GT(7SOr-I?--$jAaTj&Gng)6McHIxuT%7~Sab?6NkxIb;9Vmqp@GI2&7*{&RoEh)O!?P2%c9DVIs8fLXH zzOKOTJ(4LWPG;WmW0`eq3w1KXin}(kqurxvN)z?@em1V_p&(3}r!-S1``NUnown8j zBWI6c;R$0HS?93&jty+ykw>O#m@&7OJ4-aRO-dPPGAXi| zzTmX6A;0V=LXluvR^W^F$mI$M%f>bhKX^_OoZRM-FSrOH@LU(evM~)m@rhF?;019OE=il=)js(eT2==qPxRMINbO)ZK>w#s42~H#e!vr_y;1QUj1NM227i7Hi zTjsgaPXJ>G3?V7xy)Z#Z@pwoN%%lkg2RF!cY8^6E)tPPUC2s}?ElMz@63&V3MxL^p0#J~#Pvl1q<;NnVuJA#=o zF{DR6FEJBAVjuiIIY9y+(>5`Ye)1iBOM@vSg}jSOkYgJD&H}gKkA*~#NE*0#7q7_r zP^BtW>E%Z+FF=p&Lk_?pzQV`ittuygDX%Gq$Ku5=Mn|EB5oCu(i;A|yMR8=BF(7tN zZ+0w{F7{%1KtFgL(5e@e@5SyHUyt>-Xxxg%d$4v8E9b!UYuKN`KH5>u09U0dy}ap& z_Ms1-^aZ^PC=UT>2U|4D-g^m6pi$u5AXrr4o+$Zc`22YB9T$=-l^<|zt{P=qG(aTChSNpQPASI^y`$d+D zl!8P$jcp9kCp%dCf<5D2nBMe2v=Kjk!8A$LSV&iv_?izCZ{>k(v(|JgWn)_+Ot2$8 zu+ugPOK>pV_*II?4}P>rCX6BRJo?`Bd2xu_jKEG?1P9QSYoz~Oq|F2SK^(vyAVIqR zH;}MIc&cLAr}gjfK=520G!+D_rt1>a)LNk0Ai+r3B$M$uVb|Ey)F()#j2F#UeI6a~ zb@3>pmldFcKR*+-^)%Gj{$VNyfXw80PI<+pES`{laSW9bX_(YEComDzG$bo8M8hrG z^=Mo;h4WuGjkM#v_yS8R04WS=8lyHJ5bjEli3AH?y?_&r8-WpwnMaBRm(Jn%1+|z} z!T*qu#Af>GGdSanW^7|jT-OT@Ff%bNGdP<% zI72;_Le>c0DUi$W=DFp|k$W<@(%$?7-C7xx|*ld9#kqL=( z{IkRTv$>;fZ0elw;xY}>Kc&reWq3}xI?vlJxFjaFaL((dv+?FtJpJqdcG3*CT6uVm zIw#yuCS!(Wc~M`~yCOB+;0rrp5%FqNGrm4JbJ?@9>d@$V8fucDMe05av2B1R`cAy?-U$5Htk`} zfJHXvAq_uWacuqzjYmko-L8ix39lQ9e~PyOe+lMJ*nUHsBf+>-vP4T6jJ+$#X9dZI zG<7wm|2;b{uA_a2rvL4Fu7^yF;LH!7NY|ep;<42koZ#C=@zqs*3f2ixbWl`m{&_eB zL1W8kW{%Hu-wz&RW4niCTbKgeoXhSt+i|Td1xF%GL9U~Z$A0}d88WdWC+zL+Vqrqbfp@T%Hb^mc6IzWbjhZ`zEWv6w~krc;}cv~OI+if1>`o3WWZ z{}kqqPf##xskJ40Hm+vPy6t3Ko3S&OaNKcIsgeCW)4iG2jK6Q#b6pbkW0-gBA|{O) zf$ZPIrsvo2%$9b9Q8WvKV=X2AWH01g3VHuWfoFQ$2o{~t%=*W+(bnNHVeur!B>Q=8 z`Cb~2oyPPQNnR!x(PYuOW;@TV=^^h(8mG4~XVEAc%mTfsS`4!OVk)6wS_|_Qk7h)T zLHD*jJpa@lx&~Z?nP%>VlSy@UV~uKLU0(@9?hhwjWk?3pNS`B*tKRmt5)|=KXo*-7K~!_NSi`mh8?SRvVMIZazrCb zPHVyGcWG#;MP&MT{?Q$@wihr`NhY5-p6OF-$PW}~Ncv4V5p@n158B2rZvHjr; zZF~JxxlT<3r(QUQLPvqRadpt$&GV1#Vs}RYGnHi8DdU+owT^820A5niv0^(LcIGKB z=L>4akL0+;H9Y^oHrjhV5+mxEf65s4JiV3e?UE^{OkvuzIueG*wq;vczoDO8!D8-t zlS#JiX65QWQsYN)-0}5nx^EkMvo7}7Q7k#NkSpQrPPHip6oIH+NTav6zVPpr`@YD`=wiN(qm@8vbs;d}wx_GsTf%)U{G-4n{1@!B6jFv@e9Syu!k*0xIA+7&)_pZ84i#I7(-`#oQaWxOTIQ zqfEA)9AI=TMJy~RE@im-#uBSJ7qq5r?+}N!n=BU%21gQvojhk>&e40Yoy_@JrWYMV zILzLMw_(mMarH_8wRH_Fo-5dX|4v3nqBu)sI#Wht!Ic{CVb76X+LI<;wZg48W|^9) z(z$zx!>0!s?Tym^#D3aOx-6ZYB0iZ?YkN2 zFaj|T1!jVKp7;XC#}cF`ud!TIB-)Y~O${`y5T+p;HzNdS+h%2^K&9rASu9bi`8@Gf zTb#Ye`$;Awo(CN}df2%;g=Gjj_K$GlbPtu;G*ioh1CNg}*e-BHl#^fH&2WceZoWp} zXp(r;KzWMrKsTqK8X;aTGI42@$et1Id#DFfXtW41bbOSPpBg~s3S2+GM!D)DA|duY zxr?!G&Gf}3(peWXDp8&yH?hpsv&*EHi%g%J=lmP<%q>&_2&F=pZkdVmi==BIj(lz> zgK>e5M%nw&4tDn|rY@{vj`VTzWG4oKuDydi_~Za8TcqTL7~K)aXQtKEvIVZamc|^t0!a22UTV?6L^FK#-|V$R^i7x%JfTNvAx3?A-h z`$!ni(a2PS-6wiTnZEIp9c>(WY!`?2MM>XSW@5VN8}eXYlBP+>G8!XX7$&3l?cm`4 z1S^-9Sn(2^{OlM#37?iYlxpYjlRMbEJ;c)0WoD*}cqlp#4s+jQgSaahmJ5QTpWn^i z;V>c;V(j#Gj_(RFb77fcB*}fB*+xg)CjpA}wsYjEUF;e#n7y#b{6YyOz^j(I`Nk5p zNSv`%t+1A@FneyEi?1y(w_L^3 zl2Fvb@G8t)T4u2zIR3faj1GrTu11DJY(F#3i4*NqX46bu&r)_gsz!{14-V295jc)y z?8Fe;`z)$%(>oRN38I=|#AECnkCL0up}V?x=yRiVB?WHHVgCFab9uq&{!T(N*hNZ7 z@8J=Soa~}Jo#s}?;-1fr(wi{wv_UK(DJ^BVad8!Mbb!+j_hK5p(MgPSbK>b;jP+<{ zFE25_TEo$r)b?KX9cV+knowJU!}s+Qvov+@xsZ}j$fV8F&=!R7m+Vgj3wyn zjS~vQdcsKyRW37mVTlzx!ilH1(~~kNE@rs)))Fh3D)Sc?xb*5A(=%lp4OYk^Y}(9U zU1n~@W%uK|Ij|?X?!p(rJJ3u}WLKFvzr>A8S#m3RuD>zM)k|4wp2jVdnK-{hF51S< zUCEzlOtjLN$rSf|aW~siE>o9RsiwL({p2>KUaTQ_njLm-SmQPQzl zohwpxU9t;B$~E76)WSyG-8*zN(Rh+%Sd&R-uyvEmU793PZG=^cm0ZPlE7!72O=-IJ z9A(H}Wb)=MYGDhI^o{PM)1Bqde*6+EMUB@r#-|VM<(ao$!*yLU6K6Sl`67AN7&vf@ z9ou@iI=f0Kzr<8IPNsAS6(nj1Bvx_{`?pz~``Y(-^+p~m6vB`K6)2@iX_D&bfJ%;R zq3TcSQsS;_S^bc6TQ|2Q}K0^zH9uxZC91dCzCQQx-c;_LKb45^Ij{%3QuM&Dqyh zsk#Ew0M7~&ids-ql+z{VZmvO7RrKCwl9mdAOAd z^OrT5tV2v`bYnLL)m({*x97Qap+xfF5GRM?C>>^aPbZZdv%K-#G!=j99A zXc?xhJ%Llsku5n0G^Lds*C!qNcXSZHT%-R`H`al%uS{|2mWOExJlo>vW8E;fz-!ME5%uDvqPt@l<*J0=JI;dZ*aW=XGAP)cL3WjXi4EZ1kMSfN0TP%9R$uF-#V zkZ!Ran-$%i>^azuxHgY6OmtZh8SGJi-f{tC7fgaB!T zNVT^?r9h@oZ7lA!TjS9;xN(c-DRwPbG!XUSrr|K$%HW@oPh<9s(H=dm(>m?c7 z-$59S){50z8B8}tGHT*hJrX1B^d(G|rc2CKEs9l-+-!!Kt2K162q>j<+`3@U7B>mG z9x@WAe<%*;%3ul%Geoj2i7w~JmK{Izs9e%hSyZx<+JP_&*H>9~41eKUb6C2$O5$*p zAxk%6+V8{EL42%_eLEuDde%lriI?~2I^9QK-!#RLiD`meuvweSFqNrdJ0J~#%4B%u zZ?bqb#mv`=jd`(!jkwDVy1=egDSFzc*=#4haeV- zpepl}JdG4J3dItkkrb8?fTB{!`IEnEQ>wsFGUCVlR3OLMEKMmuCR2n|iCm_HFifBF zw27HcX+@<}0>|AL`4j$5Ng94EZ!2OwT_@dJpMj{=Ts$d}0d=im2&_~D!>v&)`bN8u zb#PpP#vqmqV_636Lv6GRfiC1Y`+AO=Tj#Y?WS1%^EilZW0X3T|Kb|CZriXjKd;sZG zx$@jiE?h1(Vy*R9yI~511kaJ|xPJ%7k0!~_mv9IZ37HrcPyyw*RGi>`mGa}ihCwK1 zP|enG9D#CO@>z#)&@v(YmAjCemGu0$tyA&!+dZYh5MY^vqmuG`6;Fd#wkemIUECV9 zOSxRev%PioINN6K@*3NZw$rn%#^{jA{I}B7gheuGP?@V>BM_=ev8-r|TgYHmtH%sC zDE9$EXtY};mv)hXp;~|T8oOFS;h~yB!pe22lx;jipsO`(&ma^v8|SQ*Y7}!0(ln7$ z<4KEf%%Hkf^CotN=#G@uoGS1-62!?>UcNdsTdyROvl);Y?#P&lbBPYkW|GrLg^98EfBoq;t zhG@|CQnShCY?Lq>`2|L4jmbHW;a!~!#}Y(JIVNYTjpIsrTfS4C?T{_FC?Tj-9BR@a z5;A-RKI0B?Yz zgY7lq|KL+q*WYc!lt>lil&IjCyuJULnH%er+d>LL9bw{ONu(>qP>(?1a{ZMBG6h9t zVV3ikB8=R>gYl?A`rH(6KEFi4@#ULU5TkF*i?kf>n{ihgbOE@v3T|zyql#LkOl|WQ z@VVTjWuo!$JOOwVN+pbVHyvSvMLR)fM~X^*3Om?wxAhJY37J&FDZ1JXO7mqLSD}4CZquwtpYHGIt<#<$bHA1NnmZh;xleP}af0kOKJc4AQAZgCk2!r+Q!G>=1 zjYGNM(cc}#G6l3nNTn=RoAVO2OS$AWe+y~A(v@Y>N5(n!)Huof3a?(TAPtjT#$l*C zLDXEM7>U!7lvHloXrXZ(jTtsjt|lB05vr&Eu3yZbWND@m_9wksrP3I)^)QkQTco;T zgk^?MM}nB?QYkq8_e9{IXvhEygtn;^9im-vLYBZbBXqPGlrC4$T46@Q^mIosWu90n zLPR^1s=mQnSIHGLW_p5MYmm(35hRUCjan3>qd(y}885r?>IMUd7By*U1)K zWIZQEdsIs0+uk1|9HO^7j46eGzn(+6;^Nhl&Vpo0&r>L`p`EnlVmdM2&rqAoD}Q#L z^ViDk{n9adkICj2(SLD-@Vig=JIwN2jvI5Dy^ri9lyZ6btE+g@XsBa(j!n^Nyzro1 zGAmU^+oJwE9}5$YnMk1_m1JyRiiH=h@a%JoSmUGgkB>LC(oedx!qWG4XfR?S1`hS1 zZ%p#7Kb^o%qPm@p zI?UDQE^%ST#t554A_l7FVTEeE`}GUFXBxB}*~!z753%iHh70qxmV5mJ;;uI6TZx7E zpWlH^UJHchP*}?oJ-Uyh$11F3R=G7f&&td#R*pQxBM&{uR4K}V0fQ^A-o*2izq0Jz z$C;D&QHTyR7Fp%x=@n|)qGx1`j?^HX$rQ2Cz3i0*7U$>47jAHQ;$HTjd<;4!@ys}0 zewueLOrgyWC(a^-z%JHEmnCC|`|;Ww#&#wM-IR^&Wr@F1)>8WaHUuoJFtakm_S1tD zQ&sltZ6g}ZVOWs9o+f)_2ao*PZl>pIggfIzi)mhaaTQ0eukM=@z>Qdl@dw99Rg2`S z2JJCTcD{n6{TJ9NI}~byv10?2%pAGpJPQjo@)?`qp$>K)hwg)e^!G+sG3&c|83@3L zz-uBgJcrer8N4TY*n6r%A(UdIKg2D=-)SU=Iv9?}=<0|N+SbL+Oo+@#OW~bdY-=6I5dDbaj}lHVvdLhmj%i)^g0vIvjYii^;#aM9u*r6*FfSIrMZt zr=NgaxQ!kPyn7>$X}PQ}RoK2`kiADWUHdyoBurnBC6L=SjQXx-V^;RYHmvXJwY2El zJHYYBOrqlhh^1AQ7HvPfI>_X2_Et#gvT$RKy-)RV>T!=kC`o^~%Gt{$l!_q?i;+{q z92IdocJ)!2NVB%)Z1l3ojq9RFPa8uWF?#!BBy|U42OJ7(CFbWcOx~z+=+WIg(6Nkb zg-CWtCci()e8EFIHP+S?BgaR`MRH`Oa;(){3Yi-HgPrU;A&BkhXQ<0$F{l7(w4-rP zqVYTmb7|I`?HnAc@cL8BjAq>_5^?GL~ypOGsI-o&dc1Bn85) za`SwKBfq&5b^SUs8I6o?J@)*}dd2kHa~%0>FZ=ItSqrt%-xnn>1=uz68Hc`+4#rQI zwC^9HyF0XAv7sDR=4$LZI?P^?B|Vd4HS1AWtzhhGXZ+{@Vz8SXgJIIwg74gE&J1p$ zJ|EP_h1P;-SCa0*I0JoA5=I+i2dk7;@~o`6AOyvQJhl6VIP$;<@#`6uCbE>L(@d}S zar9UBlenD6NJfe13RiwSkG*qQt7TXR&QhH|=qQ{9KmV$s)EEDA#6nsYd&77sEReuv%nk zzC>=NOn9)1p>1&}6u5ag$I5h;Y|izwq@^Y^lcP{-ij`_j&4$=u2kn(L-hO47nwE`H z&aFF8A{?99*&5v=ZIH?E=J%&b=QOdN4vv3zJKZ5op{VHD-bPzQu`-p%ktWrpEK4gk z2tg#QSf0qTI-95H81#*I(c7k3n#iy;m1BOUga;TQ6J0DbH&xnDwV*vkzGkuWK$69m zC%C>~`w6jk)KbvY7BXa2l!5UM+NrWIm1FT%hL!mu`8AvPa2MM~V^mi1%-_ngG?AlF zQ=nazXNt59buqLfg;lAtGFv3OP(XBdFuWs$SuJt%QkK=ZEGuc-4`IE}{3E4+T8)Lt zBDNBMLD#N+j@;i*%&Ag#P5MStgv%usmuqOiP8TS+23=#FbVW7U#XPxem4zEwR4U2H zSUYjkW9jB9ON&)pX)>^*lfID@aj(qTA51W_WCx4Y)(-uH;1j8~rj_S24rv|4zk}GG z4qWK?J4PV|o*m)X)4#z3Z5Q~%zkY?Pwi>H-zs6WK>R};}MwPOu2HCH(CfJ?TzO&tYj{4(jX0r2l7WH^tebvr} z8rPh+0(^ie}4yS z&tBy9^F<6xHrh^8t+1b~z{dVoy1`P`h}8xzoVshbe$0P=SRc1aqXUmx{W}|;Vet22 z)7WjeS~V!spnp$4M;{uX@Y+>geKQZ%ohSQs=E7#)1{wxJtLE{%{to!}AcLHOP47c6 z&Kq^+jd314`z`uU1EFM$v4_SvJYe(EUtec#&BHX`Zx&eZ@A^H^c-OY*zk02^tk>wT z&lUCi6`P+^Q=i{ylTp8)+s4@zV*KRIih7 z;V0wncDa9O1TkCbYlLrhPe@@PW%H*RZ3vudkwVDB423Y2(WFEsFpwB`@^b{oH(%(( z4ox8h8XZjH;wSy_ZNH5>#Gi!WXV`v_b2K!@4d2Ci+Z_eLS7`{gqHeoW8=9}P@tqAy zR<`V0@%|+;n=<8u))=uUJMY`Ua9 zMbHmh+-u#~ZQZoyd;LiZJAX2(=Bo%-_(VOMjDTob)HU3)lv%meETeV$mxWET#T??D>3EQE6=!>DWhvx>ncEe{r zYpoXVE;P#goPdR@&dwIZI&XiT0n=ab9i_oAePjA}w_8|mgKBE?Pc{TUxgV5Az%C{} zeE8=V03DQd)Ds{P0>i2^w|)EtV*D~V4^IWmazXZcYaVIgqsAv}hyEcU1X7sD&@Z|n zE&MDHLIei+gQkr5M&iSL=Ob@*QyV_&e)t95($COtguo0*%)9Je6b-MI#QH@yriEX6 z+-0x!M+ZN?6!4e}+2H4Ht(jYBp@kM&xEt}wHR#GC)G@?JhhXx?EXuq?R|NI2b^ddo zoaXP=?OrD*aXpP!7fX=>vniRxQwmSv*ZE3`5hNseO5-*qMwwFJ>!SD~EA{gk(*L=m zD=^T*_5yL1dcC<>Pr|96uP)FLxO62y;e6hD`}!Uiq)&O~Y3s((7tmbrmkH6mD_{r+0_BarPRkuELP(wdC~} zAIT$^0u!eW_{ClH2YbXzwkx5W{aDsNp)bF-+> zLb88n7khf5v{^8}kmIe%0&A`&9*=NfTN~qDVGQMQWg^Xci#0s_B%!Trrb5u$*T#{d z7#$(Wts9HH9SEaXkqD=EwlmTe!c-m?Zme)=#l{P=ySM5KPYbq$#C0@OnW~!n;+FHvNT4ab#XlpjUXIL(AM5TM|(Tbu!&X*PfMc74!X7<<)H`fWvs84&W;ozDH^+W zD-tKw-a#rECnWtc9X4IR@uNJH>!KW|>BmKR-qwE~FgtX0#5uk_PSkU;Wr*MS^f+JI zox)Twx^s|!c7Goc+rjMV-8h(RdE*IQ1B3j*Jkp3zV&1qSg#rD`aYzlMkF>=RiA#))bbm^2!S@ zGhIuv@7_;wVBZ0H`YldB72$}z%uCPynCtU7lH2xk^4?>NcE!P|Fni?~ttSzgKW*Dn;PR$VGa2zvLjr#nJ2ROaB$c4~_& z{Q1lC&{^hx_`(RgdzQI4S?236&tTgg#j0W@yocW!OwgfWaW2bWEoQ0IT*@LsTkjtB z_lNlQYz3FCchpiTu3ukb@>Yz0*b_$vHLX%`Wja&9%0kKh<0=OQ%{hZe~v%@ z>U&t*PVvPj?`O~KH1Expeb%-}gs7n?lzjFi(KbTQgAWty2nWh+0&uC^c!lb-@8M~U zFf8Ix3%gpyb_3RLr6^YYn6nH?x+Ab$uyKURXuHYEHHWcX-Gqx3R&7adDuStN6e}J| zYaC(H6O|M)RSH5NJddIauBS1RCJ9q9yXN3201wY~(MlnNfiO*k;fEMJSK$QW2y(rv z!SyHvF?$h#ZIW$hYJIAY+ zrzouDxO8orJqJBM{_T3i`}T3{_=89-=<*&9ndKV89+AVM*c_dY`Z{(h9RO8U9y$uDKmLT)K4BL%FiWcZ_J@|3F{ z(T*hl^vN+E-d<$pd=Bp?60)?=LJKW?@^F_MbV0RL#Hnzbd@9{zx|mH(n~B@~p?|1qcIOS>p2BFY?}O1=ElS z?UG+x^OdW#N41p4M&tSXpcuV-7=H3$;+^aI%V>|v^;ekr{yTogw_72bt@=CK0KOEc z$;m@~+}q~yH_tCHQSvY&ikbrvug0JJI89iZ{O(bxI-W1PC=B-R?BVG#ix*y-<;`Uq zQ~J`PW+cp+V}tAu*?i^I73ON%&yIIW6mMR_U5%ovMQR0Sy<4FX+ph~fYVE7RneiwO zAL?bFTi`#QU1h$eF{D7d4%OLfq@7u`Tc%Pd2O4X->6}fnuz+oQEY&=;(xjat*NYxI z;uc}8`CxibEws==3!g09)dt;nMSHkv<7YlJ%5&ER612csn4n}lLSJu;_tG_n2fB%c zGiV*6t+NZ6z0RBOzDGGU#;HUbq1rm-qpsD^p*XRq#Le0WLsG4*FrP8$>}aEMc7lqL zprVo9Xd2tyJz1yxrgV^O~Rz$n{m zIsW*iMV4(%$PhRR4DI5~U1M!F;DDDBq)#a+g&=Gh7)m3hAZmgGw6{g+F%`=Nmqd4p z!|jrrS8b}Ty?F~Qw9vwq_$5$VlZ_HH(G-Lg%|F3qsbtV?+u5~mH#^35GCVXyO04nL zYiB82afZeZuxIBEcJ3IZZ=e?|bCq|lub{NRt5oRUdk=fY$Jw*9kMdfYOg2xxCK=sx zh(m`Cv;V*W#`|L|FU*jys3y?{5y(0RWpGXnW!q@S|51*O_8NlL8`~~GDap`q7r*`R z0PP3{2HSaXe>dAgnyHmC%O#J&;SL_$-N`-MV=UZU=EoByN|7Z0^2t%Q#|#qf3GUzD z&EbJC6E}18j`#Ch_x2IjFf`K11A96dGaat1IeucAVIT~{7pVx^uY_dp?m>RzR6j?C z67+XQ+0hYZaji^rWPsm4*+s~ZY#-_5;oTi{8y;7eYhd_-HNr6bSbQM3;3{37rP z;c~C5mAQy;8(p34L=BHp&BGElOABd~8K$kPoA!7Zw_2v+Nh~+dO0F6tX({4e{d6Q_ zL=1=Jg=LD>8cLWX+q-B_#*w(xN;%dt1zhT?QX3jyZ_S+_#umKdNcQu}2GTsxx5_PR+P@c=gTnQr)VIUR43>2CqV3#UP z=6%r#h8tTV`|rg%5_ z@`cTEsH(~BFPcbTwle6N^)@tz3mOudnyt?#wl?UEdr})J_!@oQsKXCFlg)}^Ews== z3oUHGT^&RJuvlk*+gdzU$OXrYA`T4dAH7+Pjh(V z&i(k4&bRfkRt&v`7FuXwE3|g#P0$)bktmj2PZs+b&W1)33WqVo#>6zxXkikGh7ela zX5fu*7iXUM410$X=%2kIA5q_|C%uJ228oDC#1uhdoyK!L%y5)cDn)x+im)m0Jaxy6 ze(L8#5Dpo{BPJ0;_z8CeNq#~Q4I9KG29~HN+`+QD{QJmvW;YV|fN-INXP z6SPPv5%u;djYjyp`k;LuRi-`oY;}D@oqXm<590}g%xa$RzP-TN)f!gYD1ZN-|5Khg zG(3;x~z^nc@j|IQ75{a^np_Sjc>_T4!w)7<1v56TA;(9LVFO!Dn1n=d>xN~%=k;D?+(1|Dvp_A_s$_+d+u4Tull_0ATSJp@|4d+Z+6ge z?{V6~qK)hHPlQYYovzlx<;`XKrMfTy-!nW;uRuoPoH(z+f+H z*Pr3d^S8K}yUN=$|7T9`*+aYcIu$}lweG<{NR-m_bVcb=RsQqU9P=5MHz$icxHm~S zoTa-nhO@lJJF`Wmyecm)_H&>+Mp70iv;7BOz^x*r$MUUntjwk9-91YFz32JCxjfOZh0u!h z%r#!i&EnZL%&uL0;fwdPt@|zJ(gkd#P+C%)J;#rqdo-P6blmOt_1m;T)7Z9?#2jc!V>dR0YP(yco}k5!g1^6h__?(wIzwcg4)tT4ZPUcUF{Y z<&%j!u50*lD&6}+n7mw>eD5)bDBdsKOt&W};^C?s;FHqLMdxVsj3$oEgj2$UnV&>0 z@1WhJmfyiGr;9$_qg&CtcQ3ij0yz4@C|I<**OpB5+z^NcMvX&Ofn&GyCXL8tsxYpwt+k@W}qFS1)-{5iHIxUc)7{fBIf~B_nN^9Hl`;2NA@#mZpMqu~b zpC6;fn?j-mgEA6C28yF<9G=eDa+$1&;HmEGB-YS3r=^(G|E`bT_5aK;_<9fAV8Mye zBa21{zacCrY_+@oPRA3!b?a@(*t(>Qn{DmfXu}OKGl1gaP4v|WDJBM3V1UNsRo}Ew z01i<|G_euPV+(@~R?l52f;k-kM0HV-GgR+s$S?>2?bw)Otf%=o-as$?EuSR~Y3`^A_s2sIcGvU@;I`^`5|ADb!n7>VKi>9bEY!Wg2tS4JNBo|pih;O4 zSQ8lF*6GVzSP%q2bcyP?!%PTD{(&rJ=X3AjNi%kpG~J9!!wi-%yXeiJ63iA;RK>6Qr|8Q%#^jQmy~BBH)tiI}^oLw6 zdk#Y8g9MpXdj;IdRqN`F{|QD@kkZy6C{u};r_#ci$2$=84V*8DOVG0Oj%**#2CeXR zx*UkTb)Vfi_4D}(^9_+A!lGI}CzO?hYIb0=5K^3`cQn`M9US`F;o}eA>!b^=9e>_+ z5*&r>`%?|tcfY;PH=bhp1y2Hv?~b*Sl%HZRtJ`BsPO!X>$QRDYCN+y6qrq2~dRCc| zw!yb_pDcdQyC@|lL_ve}@4-R-!%h{(j0+{DIRq_x2s&Obh&h79Z^qrHL{k&Ywlg9W zq27xt_OZ6rqB5GwOyD1<=U85kv4$RaDr$R&wYQg_rJp%`(3r|j5ubCp-oZJ|%$NrJ z)xsCawMyPndCj2}t59DLDgqhz>bi#U^&B}|EFn8o3wNo;NMoP!{sbSDwjG2#P13>0 zPj=30KOtE6{;xQ}0R3b>x1!Y=Ha1QM?Vk!sfu%8sRG^m4s1ikTIfrfC5i*4ep;`)z z_13`Eu1rNSjs{?;W`%-O!Wou<+c%1*FuM!6EN-E3lg(-DC!swM%Y<%Xsy=%HrKC(b zGO41*4yTf1eVS0z^_+k$wJ>R1X^t#hIl{gvyUqkj4wRn&E!*yBix8IFIf51M72RlF zV)wI1m&^6c5IbLz;4*meT9p83Zy#zKMD`#+nI$))_fD-wG9y6+%caFUaO-U*t-ZFW zQ(fz7I(^1}tq5lvi78S~*D)v|2`hHJJ)y%Jp-`5H;p;;WVe&qp+bq)86b}AU8=

~S2G)M%X@Ka9q<#@5XZzF_0TnHYmErDdr_f4gILMc@%&< z-w>(b9GTdqD$Xb}I|9ANB1H{wq#W;`#HKNpER#?8=s`JnrbKfzL1=K`sWsipkZ8H& zn()^t6h3E8lmaiBZnMQiDm>rljJ>X>g-%2^p1`~CyR(?W*%G5d%P+LUetm>*VC9I# zlvC8oB(b}TyPI9tRJtRBIV`oK=j;59ePZ^xGZ1BmPgDRs0hoyU)#;BhZc4eb#<8^` z4AyA!*^U?K5t`G(Dr+mXTXXPq(T;jpks;p{3fcQa&FynYzo&z@^)crMVB1U^%5X`ved_MMgW)9Lvk(sp0 zz}FfSy}P%fH)%-@0De3{X&jznp4ODwbMwjZL;IUROMe}6smi+M2Os`Ba-WJ=f8+d7 zp|^GbFjaB>&CgB)eN7k6w&C@`xdUu1_h&XPl!sX z7Wy^}mPIf|ng*b-Y>>DD-O1XEySm`hk(bv0j(TQu5#oOrEl3Tge)&H&15$~--=Bio z*7>liDg#gM+pci;+RvbEHET;@y_wff|5s#yM9;6lkNi9Z43Y$gb;tOOQiN@$j`|!C zUA9$Cs|oukckx8(BtCtxt*t*g$DRp!xls`M;xl=^YMO=0+3Q1czPGV9llhzMxg~@I z#KHVy_f_MERuYtN`z;q{oiqkIbSc~V)sV`!*B1{Q6!CavQcd34AUoDqotG3FF_MtwT0@ZIG; zgiv5(v&XZ14cGIxjN#cC+c|pLoOj)(SP1IwM82;qTx&fS_~+UZ?RDWZmMKmD$v_}C zBTmUVKQl>Mdr#lzWjD)BPOp-kZ3LjI6j3xxZmTt0DLWy-450??>6F^3E579f`+WCu zgRKt(&vu!Ft+gM&yFps_CtrBs|FtW9P1iRdy34C{&Pvpq7h69q@>&*I+j5N_aZH=Y zC93NNqaSsTCM%sthGnn!(;Xbsv9fu1{`SM_dVi=KHmDF~ud%xx@(y?YawLuINi!Dx zX^6|^ese3===I9#SEd&Is_jLrS|c3YI~D(m-fdyXkic%*mKOIvM79lg|&3E*|e z3SzD=i>KoxStDNee%9(@P$1xF3*8%urC(i((bC=-?yS9em?UjdUqaC%_nRXSR4-4H zTh`^H1+P&3Q@z}ZIsPIz;$*Gs?UnLaWA@w~3hBe+#?J+3r7N^8q_C2-53$ zDfaPp`W|LW)78SE@n1#d&HUv4-NjGRoIe9CKW>5k$0W>|DupmR%6h?KTSsTM42Dc(=qDOuB#bKs59u;vb?gXb$( zJvzBP-XP|#7~uy79?#3qd&bmkhUx( z(p|;=PPCFFNsKbLu;AMd6=qSI5Cb)eeJN!tJlPj$zK=tW5;bs6!s(jGUobFA5EovU z@*S2`sKN9!Ns9BPyYjza7p3qL*YIpJ-&Z(@d;9~Z` z6N)_x85YiZoBYz;)v$YgyRhwSgL`|@3WNGk?r){&!NWYyb(P9I(i|p(e>P?*vGRy1 zx!3eN@KofG?X-KJiLr@$CXk18lrMJa-ECX^yS|J2L6sl4FU|7WT{=7;W%YAdst%zV zuBpo*SgnrMP+h;U5n@*YK|RMJOpkWCQlYlPiR=)6ds;sIwYm95E8X{+S$%bLz|f37 z7Lt6MY~1!CXIo6*S-9xz#^D1rVR_QeeQJ>gtlyr%B(x5ZPv zW3nE}?F&-6Cmp4pln;_OF#dL1}wetkd%C?%L z`tSd039+!w)x0IQ(N;fNRue71H%k5Q=90_RI-)FJ|FL~v%%w)5p}sB>JWc^f?^?4(rA}$3^^V|Lko`_YPDLW z=*#`(aNm%}i#(^i;dIEcDq4U&C2m-cdI}v#Af$~AHVCpyv3Xn$E8xA~*fB6OMB0#H zN+&-*YXcVB-L`_q$XEqGd0$ItE``_sxsJJ*(CT%m7CI)R_ZFR<(mTcRM=^+!mN58D@GhPDMMr-{%$xAE9 zUaP3|dUFOEneFs+4yNCI-qrxYSBgIj(_#2@?KNp1|NR$Y5qiJhl=D4n3(gNIa?dt0 z75zkonJY@sGk7_4DN(y=lT;Wj)lioG z$NGrrQg|dQ)@#SD>wUf{#5$nNMU-tMG`%3Nt<6;u4<{_novBey+^;a71>Y)YsIetz z7Nvx*-Q=ZwXNCTXAAfyYtmAW{$mg+9`^W?)%3kfG+WK;&2n9pwtugm&b($hriBtT` zUHUG}KrPc_ZSi%$k)60dV%^{7GW&nxkr!)ygZ2>~d@BxmfFBEgkfGiopWq<7xAC4M zI(PM*5O(kAMgKGsnzA)6;++xz*<;$?UYt}V_qeJk_&>4!yi-#n^#9w-Xx>dQp8pBJ z|Aq%h+sONlpBMsGxoWzv-WP(BgF5-#B};odRAHTvV-{{kG}@3jPiJrMyGL(0V2*^< z*W5rgGVySOFwxyZIxNjs;Ej_AFE7v%)V8K*5(Rh4g?5ab;LpF=yYp>=d7*=n48mA$ z4i)yu314BB^eqC1b4nMV5rd#|Cgs?EyZ|3^%X_y8$_~FA zc)Hu#>-~AhDDji_%3LLUH=2#hL&?GB7JKFe)ajB{Fy|C50?@TbpoU$Ir02s+VcL0% zPA~a=1YiUdFBbb1$0}PVKf5+f`HmKQm-OYPF>deOE=qBcIWf@t%`Gd>S_^aLXkgnv zXqR5I?u|^RFYhlAyE^XyY<#c7?+|{M9%q-z-FDy|*WPD<+L|?m&lpvLsF%i#7oR18 zo5q{BOUiCuyf-t93F2r5Ex7=QYVb}s=Y~#dX$GbJjc_n<-d)A|T*O?8zzcYs?ivI% z;}o`c5uv+5y`XBdlqJZ3PdGM{tTPwQCD>dx;PB~|g|-xR67w;}x~yBv-#clnh54FE zXvedyHTP;*8@sw*@TEfEx55nvDmi-I#eqcPfZATAK?Rpz&JtTZ127t-rc{)!E-3Ct z%_@?zL0&!V5m^~Kz{iL*)wFMNF^D=ErZdO8Ie}!CzV{GH7DLD73^9E3e^x%dW6D`l zkM+sDVjt(`Fh$)_$+ohN4x_;==aLqVt3P~m+$ly6F6HP`9m4M(?ca86O?&;)PqO&K z$U?fdIjET?mVFAH<^Mf#{TA3XbMq_yyhUf!h1-|{)qwFpWK@`f{OQnwYJ9|Ul_nGx`|}{)1u21 z0P2B{eONsAfR3b?1ZzqAV^O7MlLiSBxO4SB;7khZTs-)Esbk_egs!~a0K3CJ!pAqJ zua{)6zN@OkxMb#WKV;G`w2|fE7=@v06qQmdcRl7s>Y9T&l#zaP-p);gdhnN+E9_`C z%a2-SXwz_^h>&}Eaz@Fgsgx$jF^!K6&^*pj5hJRp0LQ~B3|u8LY=Y-#{@a{c+z00K z)RSxwDU^#x3W-E#l33I3kTimmm9BhevkR6`4K{e{K45g|c2uvlG&{-rt^#K!gd?NavTMQA_tS z{0X)~MF^PBF~IZ83w&;5>Uvd$koHG|_$C&*;!~F>tS(QT)b{*B!_>%fr|Jm6Voqq` z@xkdS&2)!47tC=2Ox9^mn-?E*Gxz78z5eSmpXsb#T#eGq6 z5uxIGK9RReM`b53#?U+N5iv2ctE;Qx#jEC#q3^dIc$%7jniYkAeLL{zIn%Y65KpQz zL5wLex3eQhYdO{GUwaXx4tB)l?hH}W0!_ICALfC6Tl1|{ZMvQeI_ zaL2}B_dPob7-k@T)ikvIp&Rld^g*SWc6R|Zai^DD6X)6^7h7gQ6D8?~!N=R5PXD^v z4_3)DI!bqGbd>k!bRH``gDeBu;JLDFZLNfbGhYAAoUOc0*aMmg&+N_~o^t%#I0+V; zyyTt-f@BaB<`do*xjhR3W>m5nQ-iU-%0Ao|0cuMt4>H>W`c5S0bQROuQc$ufskGh@ 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 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/Partner-Website_900x675px-md.png b/survey_dashboard/hmc_layout/static/en_files/Partner-Website_900x675px-md.png new file mode 100644 index 0000000000000000000000000000000000000000..8a203fde309ec8cf905d5d5328519b2c5080df0f GIT binary patch literal 318627 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

wIY+UEW>Oo@ozFNo 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�%csVjIi4SZ==@ya9;bGgh+tx_9BO!@f?A~?z= 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

3xYa_jfr8}NR~>E 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 U58|Yo%K!iX07*qoM6N<$f~=AK2LJ#7 literal 0 HcmV?d00001 diff --git a/survey_dashboard/hmc_layout/static/en_files/Teaser_M4R_20230327-md.PNG b/survey_dashboard/hmc_layout/static/en_files/Teaser_M4R_20230327-md.PNG new file mode 100644 index 0000000000000000000000000000000000000000..749f64ed89d590f66048141bbffcede1d6799286 GIT binary patch literal 61438 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

  • DT&IL=GGSh)|7sgbWe!Q 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

    ;)@M6X9=+sVd(8@77TXpL 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%0Abo_HER1rVInz-o;rfelHp$TBsej7b}oJM zLH?e=Ok*Q9#lL;Vu{$>T*G|+|o|xe6SOcJmWtv|7mbeTj|3EpX3~aS+oxb?49>@i? z!)*3-tbxE^HITHusDoU9meNV8=fy@8{KJp^#jCn4dDtNKt4mFkqWtm7*8Ef8v-EDS znd_4kbV&PE@x{8yXXk5nb`5G!_qKuW01gDo6qmL)PMmseOCL79R8Q>^G*&I9xNznx z8^;Il4Mq^aNkg(6K3wt}p2ciGZ()JP%|U8=FRKf|+9~=FUvKn27YZKLpMo36g7^IF zH{xcD5do~D1Pm~e^u}pIBLpo9ST59G7X0j1`56g$@8tJ&MYWIYt!Uv=NY$-;AW4KsitOeppwlK?S-WeU9olQ zhikqPlqayVr%x508f4|<6ayT1ie50R)?C|NokZz#d&6Mdfd}daYKtCLgk3ABzNDBPDmVXbO1YpWv#X!UJaLF| zm|hg-;#~1saJ<3)XP<=i{Xn9^63Ujt*E>Z&J%T&4O!b~KG|wb}tw1RF5#-~odFprT zHxh~Cx`RM|uQLmnK|FnFamYE<;c<|blX-QHt*ZxXl*y7lI!_0~lIGO_z_!-%@fGv& zeTQytjtoE6CWV9}1NPqdf3PSFm$?`@@V#6Pkx9R~J#ZtjjzMXVm0w2bsD9M7CLu-3 z;4WW1{3uc=x|rK;jYFlMNktH%xFeuE=Y=l&iyt0(4<_-2wtn*<>^}3$%=@4^Pwtca zx{@8J4Xi>`y?2uqOP8+254?3#-P+c+@$fPPW|x@j>j0T7r1_o@ zoaI-eVD1r*F5$p? zn@30Q$0(|~&){PSP$NRCS;x}8n@zvz{p|LCa@|1Xhh((|@wjfYG6_pTY2f6X6cjrpz;EM{+3 zyo8nh0|js_GqT1A{fle;j~aAu{^sRr?SHkPH6x>g*usfxBRrN zKp`^3`^k-e2-;hjk_bqLlRDONl%rc!_1~Z#&k&}Og:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-hidden{overflow-y:hidden}.overflow-x-visible{overflow-x:visible}.whitespace-nowrap{white-space:nowrap}.break-words{overflow-wrap:break-word}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-\[10px\]{border-radius:10px}.rounded{border-radius:.25rem}.rounded-r-\[10px\]{border-bottom-right-radius:10px;border-top-right-radius:10px}.border{border-width:1px}.border-0{border-width:0}.border-t{border-top-width:1px}.border-b{border-bottom-width:1px}.border-t-4{border-top-width:4px}.border-b-4{border-bottom-width:4px}.border-gray-300{--tw-border-opacity:1;border-color:rgb(200 212 216/var(--tw-border-opacity))}.border-blue{--tw-border-opacity:1;border-color:rgb(0 40 100/var(--tw-border-opacity))}.border-blue-light{--tw-border-opacity:1;border-color:rgb(20 200 255/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(235 236 236/var(--tw-border-opacity))}.border-green{--tw-border-opacity:1;border-color:rgb(140 214 0/var(--tw-border-opacity))}.border-purple{--tw-border-opacity:1;border-color:rgb(157 17 96/var(--tw-border-opacity))}.border-red{--tw-border-opacity:1;border-color:rgb(210 50 100/var(--tw-border-opacity))}.border-orange{--tw-border-opacity:1;border-color:rgb(240 120 30/var(--tw-border-opacity))}.border-yellow{--tw-border-opacity:1;border-color:rgb(255 210 40/var(--tw-border-opacity))}.border-turquoise{--tw-border-opacity:1;border-color:rgb(80 200 170/var(--tw-border-opacity))}.border-darkgreen{--tw-border-opacity:1;border-color:rgb(50 100 105/var(--tw-border-opacity))}.border-blueviolet{--tw-border-opacity:1;border-color:rgb(150 120 210/var(--tw-border-opacity))}.border-lightblue{--tw-border-opacity:1;border-color:rgb(20 200 255/var(--tw-border-opacity))}.border-violet{--tw-border-opacity:1;border-color:rgb(94 59 153/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(123 135 139/var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity:1;border-color:rgb(88 102 106/var(--tw-border-opacity))}.bg-blue,.bg-blue-dark{--tw-bg-opacity:1;background-color:rgb(0 40 100/var(--tw-bg-opacity))}.bg-blue-light{--tw-bg-opacity:1;background-color:rgb(20 200 255/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(247 247 247/var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgb(88 102 106/var(--tw-bg-opacity))}.bg-purple{--tw-bg-opacity:1;background-color:rgb(157 17 96/var(--tw-bg-opacity))}.bg-red{--tw-bg-opacity:1;background-color:rgb(210 50 100/var(--tw-bg-opacity))}.bg-orange{--tw-bg-opacity:1;background-color:rgb(240 120 30/var(--tw-bg-opacity))}.bg-yellow{--tw-bg-opacity:1;background-color:rgb(255 210 40/var(--tw-bg-opacity))}.bg-turquoise{--tw-bg-opacity:1;background-color:rgb(80 200 170/var(--tw-bg-opacity))}.bg-darkgreen{--tw-bg-opacity:1;background-color:rgb(50 100 105/var(--tw-bg-opacity))}.bg-green{--tw-bg-opacity:1;background-color:rgb(140 214 0/var(--tw-bg-opacity))}.bg-blueviolet{--tw-bg-opacity:1;background-color:rgb(150 120 210/var(--tw-bg-opacity))}.bg-violet{--tw-bg-opacity:1;background-color:rgb(94 59 153/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(235 236 236/var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(200 212 216/var(--tw-bg-opacity))}.bg-opacity-20{--tw-bg-opacity:0.2}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-white{--tw-gradient-from:#fff;--tw-gradient-to:hsla(0,0%,100%,0);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.fill-blue{fill:#002864}.object-contain{-o-object-fit:contain;object-fit:contain}.object-right-top{-o-object-position:right top;object-position:right top}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-\[50px\]{padding:50px}.p-2{padding:.5rem}.p-8{padding:2rem}.p-6{padding:1.5rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-20{padding-bottom:5rem;padding-top:5rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.pr-4{padding-right:1rem}.pb-4{padding-bottom:1rem}.pl-4{padding-left:1rem}.pb-20{padding-bottom:5rem}.pt-10{padding-top:2.5rem}.pt-3{padding-top:.75rem}.pt-6{padding-top:1.5rem}.pt-8{padding-top:2rem}.pb-0{padding-bottom:0}.pb-3{padding-bottom:.75rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pb-10{padding-bottom:2.5rem}.pt-1\.5{padding-top:.375rem}.pb-2{padding-bottom:.5rem}.pt-1{padding-top:.25rem}.pb-\[30px\]{padding-bottom:30px}.pl-3{padding-left:.75rem}.pt-2{padding-top:.5rem}.pr-6{padding-right:1.5rem}.pb-3\.5{padding-bottom:.875rem}.pl-1{padding-left:.25rem}.pb-16{padding-bottom:4rem}.pr-12{padding-right:3rem}.pb-12{padding-bottom:3rem}.pt-4{padding-top:1rem}.pr-8{padding-right:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-body{font-family:halvar,sans-serif}.font-display{font-family:hermann,sans-serif}.text-sm{font-size:15px;line-height:22px}.text-2xl{font-size:32px;line-height:46px}.text-xl{font-size:25px;line-height:34px}.text-lg{font-size:20px;line-height:28px}.text-base{font-size:17px;line-height:24px}.text-xs{font-size:13px;line-height:16px}.font-bold{font-weight:700}.uppercase{text-transform:uppercase}.leading-9{line-height:2.25rem}.leading-8{line-height:2rem}.leading-7{line-height:1.75rem}.leading-none{line-height:1}.leading-6{line-height:1.5rem}.tracking-widest{letter-spacing:.1em}.text-blue{--tw-text-opacity:1;color:rgb(0 40 100/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-blue-dark{--tw-text-opacity:1;color:rgb(0 40 100/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(22 22 22/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(123 135 139/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(88 102 106/var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity:1;color:rgb(247 247 247/var(--tw-text-opacity))}.text-purple{--tw-text-opacity:1;color:rgb(157 17 96/var(--tw-text-opacity))}.text-red{--tw-text-opacity:1;color:rgb(210 50 100/var(--tw-text-opacity))}.text-orange{--tw-text-opacity:1;color:rgb(240 120 30/var(--tw-text-opacity))}.text-yellow{--tw-text-opacity:1;color:rgb(255 210 40/var(--tw-text-opacity))}.text-turquoise{--tw-text-opacity:1;color:rgb(80 200 170/var(--tw-text-opacity))}.text-darkgreen{--tw-text-opacity:1;color:rgb(50 100 105/var(--tw-text-opacity))}.text-blue-light{--tw-text-opacity:1;color:rgb(20 200 255/var(--tw-text-opacity))}.text-green{--tw-text-opacity:1;color:rgb(140 214 0/var(--tw-text-opacity))}.text-blueviolet{--tw-text-opacity:1;color:rgb(150 120 210/var(--tw-text-opacity))}.text-violet{--tw-text-opacity:1;color:rgb(94 59 153/var(--tw-text-opacity))}.\!text-blue-light{--tw-text-opacity:1!important;color:rgb(20 200 255/var(--tw-text-opacity))!important}.\!text-blue{--tw-text-opacity:1!important;color:rgb(0 40 100/var(--tw-text-opacity))!important}.text-gray-800{--tw-text-opacity:1;color:rgb(63 69 71/var(--tw-text-opacity))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-50{opacity:.5}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-75{opacity:.75}.opacity-25{opacity:.25}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.grayscale{--tw-grayscale:grayscale(100%)}.filter,.grayscale{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-150{transition-duration:.15s}.duration-500{transition-duration:.5s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.will-change-transform{will-change:transform}@font-face{font-display:auto;font-family:hermann;font-style:normal;font-weight:700;src:url(/fonts/hermann-bold.woff) format("woff")}@font-face{font-display:auto;font-family:halvar;font-stretch:normal;font-style:normal;font-weight:300;src:url(/fonts/HelmholtzHalvarMittel-Lt.woff2) format("woff2"),url(/fonts/HelmholtzHalvarMittel-Lt.woff) format("woff"),url(/fonts/HelmholtzHalvarMittel-Lt.eot) format("eot")}@font-face{font-display:auto;font-family:halvar;font-stretch:normal;font-style:normal;font-weight:400;src:url(/fonts/HelmholtzHalvarMittel-Rg.woff2) format("woff2"),url(/fonts/HelmholtzHalvarMittel-Rg.woff) format("woff"),url(/fonts/HelmholtzHalvarMittel-Rg.eot) format("eot")}@font-face{font-display:auto;font-family:halvar;font-stretch:normal;font-style:normal;font-weight:700;src:url(/fonts/HelmholtzHalvarMittel-Bd.woff2) format("woff2"),url(/fonts/HelmholtzHalvarMittel-Bd.woff) format("woff"),url(/fonts/HelmholtzHalvarMittel-Bd.eot) format("eot")}@font-face{font-display:auto;font-family:halvar;font-stretch:normal;font-style:italic;font-weight:400;src:url(/fonts/HelmholtzHalvarMittel-LtSlanted.woff2) format("woff2"),url(/fonts/HelmholtzHalvarMittel-LtSlanted.woff) format("woff"),url(/fonts/HelmholtzHalvarMittel-LtSlanted.eot) format("eot")}@font-face{font-display:auto;font-family:halvar;font-stretch:normal;font-style:italic;font-weight:400;src:url(/fonts/HelmholtzHalvarMittel-RgSlanted.woff2) format("woff2"),url(/fonts/HelmholtzHalvarMittel-RgSlanted.woff) format("woff"),url(/fonts/HelmholtzHalvarMittel-RgSlanted.eot) format("eot")}@font-face{font-display:auto;font-family:halvar;font-stretch:normal;font-style:italic;font-weight:700;src:url(/fonts/HelmholtzHalvarMittel-BdSlanted.woff2) format("woff2"),url(/fonts/HelmholtzHalvarMittel-BdSlanted.woff) format("woff"),url(/fonts/HelmholtzHalvarMittel-BdSlanted.eot) format("eot")}.h1,h1{font-size:32px;line-height:46px;line-height:2.25rem;margin-bottom:3rem;max-width:700px}.h1,.h2,h1,h2{--tw-text-opacity:1;color:rgb(0 40 100/var(--tw-text-opacity))}.h2,h2{font-size:25px;line-height:34px;line-height:2rem;margin-bottom:2rem;padding-right:5rem}.h3,h3{--tw-text-opacity:1;color:rgb(0 40 100/var(--tw-text-opacity));font-size:20px;line-height:28px;line-height:1.75rem;margin-bottom:1rem;padding-right:1rem}.h4,h4{color:rgb(0 40 100/var(--tw-text-opacity))}.h4,h4,p{--tw-text-opacity:1;font-size:17px;line-height:24px;margin-bottom:1rem}p{color:rgb(22 22 22/var(--tw-text-opacity));line-height:1.75rem}p a{--tw-border-opacity:1;background-repeat:no-repeat;border-bottom-width:1px;border-color:rgb(20 200 255/var(--tw-border-opacity));color:rgb(0 40 100/var(--tw-text-opacity));font-size:17px;line-height:24px;padding-bottom:2px;transition:all .3s}p a,p a:hover{--tw-text-opacity:1}p a:hover{color:rgb(20 200 255/var(--tw-text-opacity));padding-bottom:0}.contact-wysiwyg p{--tw-text-opacity:1;color:rgb(123 135 139/var(--tw-text-opacity));font-size:15px;line-height:22px}.contact-wysiwyg ul{margin-bottom:10px}.contact-wysiwyg ul li p{display:inline-block;margin-bottom:10px;padding-left:16px;position:relative}.contact-wysiwyg ul li p:before{--tw-bg-opacity:1;background-color:rgb(20 200 255/var(--tw-bg-opacity));border-radius:4px;content:"";display:block;height:5px;left:2px;position:absolute;top:8px;width:5px}a.hmc-link{--tw-border-opacity:1;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='10' height='7' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m6 1 3 2.684-3 2.684M1 1l3 2.647-3 2.647' stroke='%2314C8FF'/%3E%3C/svg%3E");background-position:0;background-repeat:no-repeat;border-bottom-width:1px;border-color:rgb(20 200 255/var(--tw-border-opacity));color:rgb(0 40 100/var(--tw-text-opacity));font-size:17px;line-height:24px;max-width:-moz-max-content;max-width:max-content;padding-bottom:2px;padding-left:1rem;transition:all .3s}a.hmc-link,a.hmc-link:hover{--tw-text-opacity:1}a.hmc-link:hover{color:rgb(20 200 255/var(--tw-text-opacity));padding-bottom:0}a.hmc-link_sm{--tw-border-opacity:1;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='10' height='7' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m6 1 3 2.684-3 2.684M1 1l3 2.647-3 2.647' stroke='%2314C8FF'/%3E%3C/svg%3E");background-position:0;background-repeat:no-repeat;border-bottom-width:1px;border-color:rgb(20 200 255/var(--tw-border-opacity));color:rgb(0 40 100/var(--tw-text-opacity));font-size:15px;line-height:22px;max-width:-moz-max-content;max-width:max-content;padding-bottom:2px;padding-left:1rem;transition:all .3s}a.hmc-link_sm,a.hmc-link_sm:hover{--tw-text-opacity:1}a.hmc-link_sm:hover{color:rgb(20 200 255/var(--tw-text-opacity))}article ul,section ul{margin-bottom:10px}article ul li p,section ul li p{display:inline-block;line-height:1.75rem;margin-bottom:10px;padding-left:24px;position:relative}article ul li p:before,section ul li p:before{--tw-bg-opacity:1;background-color:rgb(20 200 255/var(--tw-bg-opacity));border-radius:4px;content:"";display:block;height:6px;left:0;position:absolute;top:11px;width:6px}.hmc-btn-outline{--tw-border-opacity:1;--tw-text-opacity:1;border-color:rgb(0 40 100/var(--tw-border-opacity));border-radius:9999px;border-width:1px;color:rgb(0 40 100/var(--tw-text-opacity));display:inline-block;font-size:15px;letter-spacing:.05em;line-height:22px;padding:.5rem 1rem;text-transform:uppercase;transition:all .3s}.hmc-btn-outline:hover{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(0 40 100/var(--tw-bg-opacity))}.hmc-btn,.hmc-btn-outline:hover{color:rgb(255 255 255/var(--tw-text-opacity))}.hmc-btn{background-color:rgb(20 200 255/var(--tw-bg-opacity));border-color:rgb(20 200 255/var(--tw-border-opacity));border-radius:9999px;border-width:1px;display:inline-block;font-size:15px;letter-spacing:.05em;line-height:22px;padding:.5rem 1rem;transition:all .3s}.hmc-btn,.hmc-btn:hover{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1}.hmc-btn:hover{background-color:rgb(0 40 100/var(--tw-bg-opacity));border-color:rgb(0 40 100/var(--tw-border-opacity));color:rgb(255 255 255/var(--tw-text-opacity))}.test{color:#000}.modal{font-family:-apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica neue,helvetica,ubuntu,roboto,noto,segoe ui,arial,sans-serif}.modal__overlay{align-items:center;background:rgba(0,0,0,.6);bottom:0;display:flex;justify-content:center;left:0;position:fixed;right:0;top:0;z-index:2001}.modal__container{background-color:#fff;border-radius:4px;box-sizing:border-box;max-height:100vh;overflow-y:auto;padding:30px}.modal__header{align-items:center;display:flex;justify-content:space-between}.modal__title{box-sizing:border-box;color:#00449e;font-size:1.25rem;font-weight:600;line-height:1.25;margin-bottom:0;margin-top:0}.modal__close{background:transparent;border:0;min-width:25px}.modal__header .modal__close:before{content:"✕"}.modal__content{color:rgba(0,0,0,.8);line-height:1.5;margin-bottom:2rem;margin-top:2rem}.modal__btn{-moz-osx-font-smoothing:grayscale;-webkit-appearance:button;-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:#e6e6e6;border-radius:.25rem;border-style:none;border-width:0;color:rgba(0,0,0,.8);cursor:pointer;font-size:.875rem;line-height:1.15;margin:0;overflow:visible;padding:.5rem 1rem;text-transform:none;transform:translateZ(0);transition:transform .25s ease-out;will-change:transform}.modal__btn:focus,.modal__btn:hover{transform:scale(1.05)}.modal__btn-primary{background-color:#00449e;color:#fff}@keyframes mmfadeIn{0%{opacity:0}to{opacity:1}}@keyframes mmfadeOut{0%{opacity:1}to{opacity:0}}@keyframes mmslideIn{0%{transform:translateY(15%)}to{transform:translateY(0)}}@keyframes mmslideOut{0%{transform:translateY(0)}to{transform:translateY(-10%)}}.micromodal-slide{display:none}.micromodal-slide.is-open{display:block}.micromodal-slide[aria-hidden=false] .modal__overlay{animation:mmfadeIn .3s cubic-bezier(0,0,.2,1)}.micromodal-slide[aria-hidden=false] .modal__container{animation:mmslideIn .3s cubic-bezier(0,0,.2,1)}.micromodal-slide[aria-hidden=true] .modal__overlay{animation:mmfadeOut .3s cubic-bezier(0,0,.2,1)}.micromodal-slide[aria-hidden=true] .modal__container{animation:mmslideOut .3s cubic-bezier(0,0,.2,1)}.micromodal-slide .modal__container,.micromodal-slide .modal__overlay{will-change:transform}.embla{overflow:hidden}.embla__container{display:flex;margin-left:-24px}.embla__slide{cursor:grab;flex:0 0 100%;padding-left:24px;position:relative}@media (min-width:1024px){.embla{overflow:hidden}.embla__container{display:flex;margin-left:-24px;transform:none!important}.embla__slide{cursor:grab;flex:0 0 100%;left:0;opacity:0;padding-left:24px;position:absolute;right:0;top:0;transition:opacity 1s}.embla__slide.is-selected{opacity:1;transition:opacity 1s;transition-delay:1s;z-index:1}}.embla__viewport:before{content:'{ "loop": false, "align": "start", "slidesToScroll": 1, }';display:none}.embla__button{fill:#005799;align-items:center;background-color:#fff;border:0;border-radius:100%;cursor:pointer;display:flex;height:40px;justify-content:center;outline:0;padding:0;touch-action:manipulation;transform:translateY(-50%);width:40px;z-index:1}.embla__button:disabled{cursor:default;opacity:.5}.embla__button--prev .embla__button__svg{height:30%;margin-right:2px;width:30%}.embla__button--next .embla__button__svg{height:30%;margin-left:2px;width:30%}.embla__dots{align-items:center;display:flex;height:100%;justify-content:center;list-style:none}@media (min-width:768px){.embla__dots{justify-content:flex-end}}.embla__dot{align-items:center;background-color:transparent;border:0;border-radius:999px;cursor:pointer;display:flex;height:8px;margin-left:7.5px;margin-right:7.5px;outline:0;padding:0;position:relative;width:8px}.embla__dot:after{background-color:#fff;border-radius:999px;content:"";height:100%;opacity:.3;width:100%}.embla__dot.is-selected:after{background-color:#fff;opacity:1}body{--tw-bg-opacity:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:rgb(0 40 100/var(--tw-bg-opacity));box-sizing:border-box;font-family:halvar,sans-serif}@media (min-width:768px){.container{padding-left:3rem;padding-right:3rem}}.multiply{mix-blend-mode:multiply}.greyscale-100{filter:grayscale(100%)}.bg-gradient{background-image:linear-gradient(270deg,rgba(0,147,202,0) 1%,currentColor)}.embed-responsive{height:auto;overflow:hidden;padding-bottom:56.25%;position:relative}.embed-responsive iframe{height:100%;left:0;position:absolute;top:0;width:100%}.line-clamp{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box}.card-text>p{margin-bottom:6px!important}section table th{padding-bottom:1rem;padding-right:1rem;vertical-align:top}section table td{--tw-border-opacity:1;border-color:rgb(200 212 216/var(--tw-border-opacity));border-top-width:1px;padding-bottom:.5rem;padding-right:1rem;padding-top:.5rem;vertical-align:top}.placeholder\:text-gray-400::-moz-placeholder{--tw-text-opacity:1;color:rgb(123 135 139/var(--tw-text-opacity))}.placeholder\:text-gray-400::placeholder{--tw-text-opacity:1;color:rgb(123 135 139/var(--tw-text-opacity))}.last\:border-white:last-child{--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity))}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(200 212 216/var(--tw-bg-opacity))}.hover\:text-blue-light:hover{--tw-text-opacity:1;color:rgb(20 200 255/var(--tw-text-opacity))}.hover\:opacity-100:hover{opacity:1}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-blue:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(0 40 100/var(--tw-ring-opacity))}.focus\:ring-opacity-30:focus{--tw-ring-opacity:0.3}@media (min-width:640px){.sm\:max-h-75{max-height:75vh}.sm\:text-3xl{font-size:40px;line-height:54px}}@media (min-width:768px){.md\:right-12{right:3rem}.md\:m-3{margin:.75rem}.md\:flex{display:flex}.md\:hidden{display:none}.md\:h-\[500px\]{height:500px}.md\:w-2\/3{width:66.666667%}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:justify-between{justify-content:space-between}.md\:p-16{padding:4rem}}@media (min-width:1024px){.lg\:order-1{order:1}.lg\:order-2{order:2}.lg\:col-span-7{grid-column:span 7/span 7}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-start-9{grid-column-start:9}.lg\:-ml-3{margin-left:-.75rem}.lg\:mb-0{margin-bottom:0}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:w-min{width:-moz-min-content;width:min-content}.lg\:w-2\/3{width:66.666667%}.lg\:w-1\/2{width:50%}.lg\:w-1\/3{width:33.333333%}.lg\:w-1\/4{width:25%}.lg\:w-2\/4{width:50%}.lg\:w-full{width:100%}.lg\:w-3\/12{width:25%}.lg\:w-9\/12{width:75%}.lg\:w-8\/12{width:66.666667%}.lg\:w-56{width:14rem}.lg\:w-\[223px\]{width:223px}.lg\:max-w-\[200px\]{max-width:200px}.lg\:scale-90{--tw-scale-x:.9;--tw-scale-y:.9;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:items-center{align-items:center}.lg\:justify-between{justify-content:space-between}.lg\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.lg\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2rem*var(--tw-space-x-reverse))}.lg\:pt-20{padding-top:5rem}.lg\:pt-0{padding-top:0}.lg\:pt-6{padding-top:1.5rem}.lg\:pt-12{padding-top:3rem}.lg\:pt-16{padding-top:4rem}.lg\:pb-0{padding-bottom:0}.lg\:pb-6{padding-bottom:1.5rem}.lg\:pb-12{padding-bottom:3rem}.lg\:pb-16{padding-bottom:4rem}.lg\:pb-20{padding-bottom:5rem}.lg\:pr-20{padding-right:5rem}.lg\:pr-12{padding-right:3rem}.lg\:pr-4{padding-right:1rem}.lg\:pl-4{padding-left:1rem}}@media (min-width:1280px){.xl\:block{display:block}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:items-center{align-items:center}.xl\:justify-end{justify-content:flex-end}.xl\:pr-20{padding-right:5rem}} diff --git a/survey_dashboard/hmc_layout/static/en_files/app.js b/survey_dashboard/hmc_layout/static/en_files/app.js new file mode 100644 index 0000000..0e7f3bc --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/app.js @@ -0,0 +1,2 @@ +/*! For license information please see app.js.LICENSE.txt */ +(()=>{var e,t={669:(e,t,n)=>{e.exports=n(609)},448:(e,t,n)=>{"use strict";var r=n(867),o=n(26),i=n(372),l=n(327),a=n(97),s=n(109),c=n(985),u=n(61),f=n(655),p=n(263);e.exports=function(e){return new Promise((function(t,n){var d,h=e.data,m=e.headers,v=e.responseType;function g(){e.cancelToken&&e.cancelToken.unsubscribe(d),e.signal&&e.signal.removeEventListener("abort",d)}r.isFormData(h)&&delete m["Content-Type"];var y=new XMLHttpRequest;if(e.auth){var _=e.auth.username||"",b=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";m.Authorization="Basic "+btoa(_+":"+b)}var w=a(e.baseURL,e.url);function x(){if(y){var r="getAllResponseHeaders"in y?s(y.getAllResponseHeaders()):null,i={data:v&&"text"!==v&&"json"!==v?y.response:y.responseText,status:y.status,statusText:y.statusText,headers:r,config:e,request:y};o((function(e){t(e),g()}),(function(e){n(e),g()}),i),y=null}}if(y.open(e.method.toUpperCase(),l(w,e.params,e.paramsSerializer),!0),y.timeout=e.timeout,"onloadend"in y?y.onloadend=x:y.onreadystatechange=function(){y&&4===y.readyState&&(0!==y.status||y.responseURL&&0===y.responseURL.indexOf("file:"))&&setTimeout(x)},y.onabort=function(){y&&(n(u("Request aborted",e,"ECONNABORTED",y)),y=null)},y.onerror=function(){n(u("Network Error",e,null,y)),y=null},y.ontimeout=function(){var t=e.timeout?"timeout of "+e.timeout+"ms exceeded":"timeout exceeded",r=e.transitional||f.transitional;e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),n(u(t,e,r.clarifyTimeoutError?"ETIMEDOUT":"ECONNABORTED",y)),y=null},r.isStandardBrowserEnv()){var k=(e.withCredentials||c(w))&&e.xsrfCookieName?i.read(e.xsrfCookieName):void 0;k&&(m[e.xsrfHeaderName]=k)}"setRequestHeader"in y&&r.forEach(m,(function(e,t){void 0===h&&"content-type"===t.toLowerCase()?delete m[t]:y.setRequestHeader(t,e)})),r.isUndefined(e.withCredentials)||(y.withCredentials=!!e.withCredentials),v&&"json"!==v&&(y.responseType=e.responseType),"function"==typeof e.onDownloadProgress&&y.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&y.upload&&y.upload.addEventListener("progress",e.onUploadProgress),(e.cancelToken||e.signal)&&(d=function(e){y&&(n(!e||e&&e.type?new p("canceled"):e),y.abort(),y=null)},e.cancelToken&&e.cancelToken.subscribe(d),e.signal&&(e.signal.aborted?d():e.signal.addEventListener("abort",d))),h||(h=null),y.send(h)}))}},609:(e,t,n)=>{"use strict";var r=n(867),o=n(849),i=n(321),l=n(185);var a=function e(t){var n=new i(t),a=o(i.prototype.request,n);return r.extend(a,i.prototype,n),r.extend(a,n),a.create=function(n){return e(l(t,n))},a}(n(655));a.Axios=i,a.Cancel=n(263),a.CancelToken=n(972),a.isCancel=n(502),a.VERSION=n(288).version,a.all=function(e){return Promise.all(e)},a.spread=n(713),a.isAxiosError=n(268),e.exports=a,e.exports.default=a},263:e=>{"use strict";function t(e){this.message=e}t.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},t.prototype.__CANCEL__=!0,e.exports=t},972:(e,t,n)=>{"use strict";var r=n(263);function o(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var n=this;this.promise.then((function(e){if(n._listeners){var t,r=n._listeners.length;for(t=0;t{"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},321:(e,t,n)=>{"use strict";var r=n(867),o=n(327),i=n(782),l=n(572),a=n(185),s=n(875),c=s.validators;function u(e){this.defaults=e,this.interceptors={request:new i,response:new i}}u.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{}).url=arguments[0]:e=e||{},(e=a(this.defaults,e)).method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=e.transitional;void 0!==t&&s.assertOptions(t,{silentJSONParsing:c.transitional(c.boolean),forcedJSONParsing:c.transitional(c.boolean),clarifyTimeoutError:c.transitional(c.boolean)},!1);var n=[],r=!0;this.interceptors.request.forEach((function(t){"function"==typeof t.runWhen&&!1===t.runWhen(e)||(r=r&&t.synchronous,n.unshift(t.fulfilled,t.rejected))}));var o,i=[];if(this.interceptors.response.forEach((function(e){i.push(e.fulfilled,e.rejected)})),!r){var u=[l,void 0];for(Array.prototype.unshift.apply(u,n),u=u.concat(i),o=Promise.resolve(e);u.length;)o=o.then(u.shift(),u.shift());return o}for(var f=e;n.length;){var p=n.shift(),d=n.shift();try{f=p(f)}catch(e){d(e);break}}try{o=l(f)}catch(e){return Promise.reject(e)}for(;i.length;)o=o.then(i.shift(),i.shift());return o},u.prototype.getUri=function(e){return e=a(this.defaults,e),o(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},r.forEach(["delete","get","head","options"],(function(e){u.prototype[e]=function(t,n){return this.request(a(n||{},{method:e,url:t,data:(n||{}).data}))}})),r.forEach(["post","put","patch"],(function(e){u.prototype[e]=function(t,n,r){return this.request(a(r||{},{method:e,url:t,data:n}))}})),e.exports=u},782:(e,t,n)=>{"use strict";var r=n(867);function o(){this.handlers=[]}o.prototype.use=function(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!n&&n.synchronous,runWhen:n?n.runWhen:null}),this.handlers.length-1},o.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},o.prototype.forEach=function(e){r.forEach(this.handlers,(function(t){null!==t&&e(t)}))},e.exports=o},97:(e,t,n)=>{"use strict";var r=n(793),o=n(303);e.exports=function(e,t){return e&&!r(t)?o(e,t):t}},61:(e,t,n)=>{"use strict";var r=n(481);e.exports=function(e,t,n,o,i){var l=new Error(e);return r(l,t,n,o,i)}},572:(e,t,n)=>{"use strict";var r=n(867),o=n(527),i=n(502),l=n(655),a=n(263);function s(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new a("canceled")}e.exports=function(e){return s(e),e.headers=e.headers||{},e.data=o.call(e,e.data,e.headers,e.transformRequest),e.headers=r.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),r.forEach(["delete","get","head","post","put","patch","common"],(function(t){delete e.headers[t]})),(e.adapter||l.adapter)(e).then((function(t){return s(e),t.data=o.call(e,t.data,t.headers,e.transformResponse),t}),(function(t){return i(t)||(s(e),t&&t.response&&(t.response.data=o.call(e,t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))}},481:e=>{"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code,status:this.response&&this.response.status?this.response.status:null}},e}},185:(e,t,n)=>{"use strict";var r=n(867);e.exports=function(e,t){t=t||{};var n={};function o(e,t){return r.isPlainObject(e)&&r.isPlainObject(t)?r.merge(e,t):r.isPlainObject(t)?r.merge({},t):r.isArray(t)?t.slice():t}function i(n){return r.isUndefined(t[n])?r.isUndefined(e[n])?void 0:o(void 0,e[n]):o(e[n],t[n])}function l(e){if(!r.isUndefined(t[e]))return o(void 0,t[e])}function a(n){return r.isUndefined(t[n])?r.isUndefined(e[n])?void 0:o(void 0,e[n]):o(void 0,t[n])}function s(n){return n in t?o(e[n],t[n]):n in e?o(void 0,e[n]):void 0}var c={url:l,method:l,data:l,baseURL:a,transformRequest:a,transformResponse:a,paramsSerializer:a,timeout:a,timeoutMessage:a,withCredentials:a,adapter:a,responseType:a,xsrfCookieName:a,xsrfHeaderName:a,onUploadProgress:a,onDownloadProgress:a,decompress:a,maxContentLength:a,maxBodyLength:a,transport:a,httpAgent:a,httpsAgent:a,cancelToken:a,socketPath:a,responseEncoding:a,validateStatus:s};return r.forEach(Object.keys(e).concat(Object.keys(t)),(function(e){var t=c[e]||i,o=t(e);r.isUndefined(o)&&t!==s||(n[e]=o)})),n}},26:(e,t,n)=>{"use strict";var r=n(61);e.exports=function(e,t,n){var o=n.config.validateStatus;n.status&&o&&!o(n.status)?t(r("Request failed with status code "+n.status,n.config,null,n.request,n)):e(n)}},527:(e,t,n)=>{"use strict";var r=n(867),o=n(655);e.exports=function(e,t,n){var i=this||o;return r.forEach(n,(function(n){e=n.call(i,e,t)})),e}},655:(e,t,n)=>{"use strict";var r=n(155),o=n(867),i=n(16),l=n(481),a={"Content-Type":"application/x-www-form-urlencoded"};function s(e,t){!o.isUndefined(e)&&o.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}var c,u={transitional:{silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},adapter:(("undefined"!=typeof XMLHttpRequest||void 0!==r&&"[object process]"===Object.prototype.toString.call(r))&&(c=n(448)),c),transformRequest:[function(e,t){return i(t,"Accept"),i(t,"Content-Type"),o.isFormData(e)||o.isArrayBuffer(e)||o.isBuffer(e)||o.isStream(e)||o.isFile(e)||o.isBlob(e)?e:o.isArrayBufferView(e)?e.buffer:o.isURLSearchParams(e)?(s(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):o.isObject(e)||t&&"application/json"===t["Content-Type"]?(s(t,"application/json"),function(e,t,n){if(o.isString(e))try{return(t||JSON.parse)(e),o.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(n||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||u.transitional,n=t&&t.silentJSONParsing,r=t&&t.forcedJSONParsing,i=!n&&"json"===this.responseType;if(i||r&&o.isString(e)&&e.length)try{return JSON.parse(e)}catch(e){if(i){if("SyntaxError"===e.name)throw l(e,this,"E_JSON_PARSE");throw e}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};o.forEach(["delete","get","head"],(function(e){u.headers[e]={}})),o.forEach(["post","put","patch"],(function(e){u.headers[e]=o.merge(a)})),e.exports=u},288:e=>{e.exports={version:"0.24.0"}},849:e=>{"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r{"use strict";var r=n(867);function o(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}e.exports=function(e,t,n){if(!t)return e;var i;if(n)i=n(t);else if(r.isURLSearchParams(t))i=t.toString();else{var l=[];r.forEach(t,(function(e,t){null!=e&&(r.isArray(e)?t+="[]":e=[e],r.forEach(e,(function(e){r.isDate(e)?e=e.toISOString():r.isObject(e)&&(e=JSON.stringify(e)),l.push(o(t)+"="+o(e))})))})),i=l.join("&")}if(i){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+i}return e}},303:e=>{"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},372:(e,t,n)=>{"use strict";var r=n(867);e.exports=r.isStandardBrowserEnv()?{write:function(e,t,n,o,i,l){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(i)&&a.push("domain="+i),!0===l&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},793:e=>{"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},268:e=>{"use strict";e.exports=function(e){return"object"==typeof e&&!0===e.isAxiosError}},985:(e,t,n)=>{"use strict";var r=n(867);e.exports=r.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement("a");function o(e){var r=e;return t&&(n.setAttribute("href",r),r=n.href),n.setAttribute("href",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,""):"",host:n.host,search:n.search?n.search.replace(/^\?/,""):"",hash:n.hash?n.hash.replace(/^#/,""):"",hostname:n.hostname,port:n.port,pathname:"/"===n.pathname.charAt(0)?n.pathname:"/"+n.pathname}}return e=o(window.location.href),function(t){var n=r.isString(t)?o(t):t;return n.protocol===e.protocol&&n.host===e.host}}():function(){return!0}},16:(e,t,n)=>{"use strict";var r=n(867);e.exports=function(e,t){r.forEach(e,(function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])}))}},109:(e,t,n)=>{"use strict";var r=n(867),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,i,l={};return e?(r.forEach(e.split("\n"),(function(e){if(i=e.indexOf(":"),t=r.trim(e.substr(0,i)).toLowerCase(),n=r.trim(e.substr(i+1)),t){if(l[t]&&o.indexOf(t)>=0)return;l[t]="set-cookie"===t?(l[t]?l[t]:[]).concat([n]):l[t]?l[t]+", "+n:n}})),l):l}},713:e=>{"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},875:(e,t,n)=>{"use strict";var r=n(288).version,o={};["object","boolean","number","function","string","symbol"].forEach((function(e,t){o[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}}));var i={};o.transitional=function(e,t,n){function o(e,t){return"[Axios v"+r+"] Transitional option '"+e+"'"+t+(n?". "+n:"")}return function(n,r,l){if(!1===e)throw new Error(o(r," has been removed"+(t?" in "+t:"")));return t&&!i[r]&&(i[r]=!0,console.warn(o(r," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(n,r,l)}},e.exports={assertOptions:function(e,t,n){if("object"!=typeof e)throw new TypeError("options must be an object");for(var r=Object.keys(e),o=r.length;o-- >0;){var i=r[o],l=t[i];if(l){var a=e[i],s=void 0===a||l(a,i,e);if(!0!==s)throw new TypeError("option "+i+" must be "+s)}else if(!0!==n)throw Error("Unknown option "+i)}},validators:o}},867:(e,t,n)=>{"use strict";var r=n(849),o=Object.prototype.toString;function i(e){return"[object Array]"===o.call(e)}function l(e){return void 0===e}function a(e){return null!==e&&"object"==typeof e}function s(e){if("[object Object]"!==o.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function c(e){return"[object Function]"===o.call(e)}function u(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),i(e))for(var n=0,r=e.length;n{"use strict";var r=n(821);const o={itemsToShow:1,itemsToScroll:1,modelValue:0,transition:300,autoplay:0,snapAlign:"center",wrapAround:!1,pauseAutoplayOnHover:!1,mouseDrag:!0,touchDrag:!0,dir:"ltr",breakpoints:void 0},i={itemsToShow:{default:o.itemsToShow,type:Number},itemsToScroll:{default:o.itemsToScroll,type:Number},wrapAround:{default:o.wrapAround,type:Boolean},snapAlign:{default:o.snapAlign,validator:e=>["start","end","center","center-even","center-odd"].includes(e)},transition:{default:o.transition,type:Number},breakpoints:{default:o.breakpoints,type:Object},autoplay:{default:o.autoplay,type:Number},pauseAutoplayOnHover:{default:o.pauseAutoplayOnHover,type:Boolean},modelValue:{default:void 0,type:Number},mouseDrag:{default:o.mouseDrag,type:Boolean},touchDrag:{default:o.touchDrag,type:Boolean},dir:{default:o.dir,validator:e=>["rtl","ltr"].includes(e)},settings:{default:()=>({}),type:Object}};function l(e,t,n,r){return e.wrapAround?t:Math.min(Math.max(t,r),n)}var a=(0,r.defineComponent)({name:"Carousel",props:i,setup(e,{slots:t,emit:n,expose:a}){var s;const c=(0,r.ref)(null),u=(0,r.ref)([]),f=(0,r.ref)([]),p=(0,r.ref)(0),d=(0,r.ref)(1);let h,m,v=(0,r.ref)({}),g=Object.assign({},o);const y=(0,r.reactive)(Object.assign({},g)),_=(0,r.ref)(null!==(s=y.modelValue)&&void 0!==s?s:0),b=(0,r.ref)(0),w=(0,r.ref)(0),x=(0,r.ref)(0),k=(0,r.ref)(0);function S(){const t=Object.assign(Object.assign({},e),e.settings);v=(0,r.ref)(Object.assign({},t.breakpoints)),g=Object.assign(Object.assign({},t),{settings:void 0,breakpoints:void 0}),C(g)}function E(){const e=Object.keys(v.value).map((e=>Number(e))).sort(((e,t)=>+t-+e));let t=Object.assign({},g);e.some((e=>!!window.matchMedia(`(min-width: ${e}px)`).matches&&(t=Object.assign(Object.assign({},t),v.value[e]),!0))),C(t)}function C(e){for(let t in e)y[t]=e[t]}(0,r.provide)("config",y),(0,r.provide)("slidesBuffer",f),(0,r.provide)("slidesCount",d),(0,r.provide)("currentSlide",_),(0,r.provide)("maxSlide",x),(0,r.provide)("minSlide",k);const T=function(e,t){let n;return function(...r){n&&clearTimeout(n),n=setTimeout((()=>{e(...r),n=null}),t)}}((()=>{v.value&&(E(),L()),N()}),16);function N(){if(!c.value)return;const e=c.value.getBoundingClientRect();p.value=e.width/y.itemsToShow}function L(){d.value=Math.max(u.value.length,1),d.value<=0||(w.value=Math.ceil((d.value-1)/2),x.value=function(e,t){if(e.wrapAround)return t-1;switch(e.snapAlign){case"start":return t-e.itemsToShow;case"end":return t-1;case"center":case"center-odd":case"center-even":return t-Math.ceil(e.itemsToShow/2);default:return 0}}(y,d.value),k.value=function(e){if(e.wrapAround)return 0;switch(e.snapAlign){case"start":default:return 0;case"end":return e.itemsToShow-1;case"center":case"center-odd":return Math.floor((e.itemsToShow-1)/2);case"center-even":return Math.floor((e.itemsToShow-2)/2)}}(y),_.value=l(y,_.value,x.value,k.value))}function O(){const e=[...Array(d.value).keys()];if(y.wrapAround&&y.itemsToShow+1<=d.value){let t=(1!==y.itemsToShow?Math.round((d.value-y.itemsToShow)/2):0)-_.value;if("end"===y.snapAlign?t+=Math.floor(y.itemsToShow-1):"center"!==y.snapAlign&&"center-odd"!==y.snapAlign||t++,t<0)for(let n=t;n<0;n++)e.push(Number(e.shift()));else for(let n=0;n{v.value&&(E(),L()),(0,r.nextTick)((()=>setTimeout(N,16))),$(),window.addEventListener("resize",T,{passive:!0})})),(0,r.onUnmounted)((()=>{m&&clearTimeout(m),h&&clearInterval(h)}));let A=!1;const P={x:0,y:0},I={x:0,y:0},R=(0,r.reactive)({x:0,y:0}),F=(0,r.ref)(!1),M=()=>{F.value=!0},B=()=>{F.value=!1};function j(e){A="touchstart"===e.type,!A&&0!==e.button||W.value||(A||e.preventDefault(),P.x=A?e.touches[0].clientX:e.clientX,P.y=A?e.touches[0].clientY:e.clientY,document.addEventListener(A?"touchmove":"mousemove",V,!0),document.addEventListener(A?"touchend":"mouseup",D,!0))}const V=function(e,t){let n;return function(...r){const o=this;n||(e.apply(o,r),n=!0,setTimeout((()=>n=!1),t))}}((e=>{I.x=A?e.touches[0].clientX:e.clientX,I.y=A?e.touches[0].clientY:e.clientY;const t=I.x-P.x,n=I.y-P.y;R.y=n,R.x=t}),16);function D(){const e="rtl"===y.dir?-1:1,t=.4*Math.sign(R.x),n=Math.round(R.x/p.value+t)*e;let r=l(y,_.value-n,x.value,k.value);if(n&&!A){const e=t=>{t.stopPropagation(),window.removeEventListener("click",e,!0)};window.addEventListener("click",e,!0)}H(r),R.x=0,R.y=0,document.removeEventListener(A?"touchmove":"mousemove",V,!0),document.removeEventListener(A?"touchend":"mouseup",D,!0)}function $(){!y.autoplay||y.autoplay<=0||(h=setInterval((()=>{y.pauseAutoplayOnHover&&F.value||z()}),y.autoplay))}function U(){h&&(clearInterval(h),h=null),$()}const W=(0,r.ref)(!1);function H(e,t=!1){if(_.value===e||W.value)return;U();return e>d.value-1?H(e-d.value):e<0?H(e+d.value):(W.value=!0,b.value=_.value,_.value=e,t||n("update:modelValue",_.value),void(m=setTimeout((()=>{y.wrapAround&&O(),W.value=!1}),y.transition)))}function z(){let e=_.value+y.itemsToScroll;y.wrapAround||(e=Math.min(e,x.value)),H(e)}function G(){let e=_.value-y.itemsToScroll;y.wrapAround||(e=Math.max(e,k.value)),H(e)}const q={slideTo:H,next:z,prev:G};(0,r.provide)("nav",q);const J=(0,r.computed)((()=>function({slidesBuffer:e,currentSlide:t,snapAlign:n,itemsToShow:r,wrapAround:o,slidesCount:i}){let l=e.indexOf(t);if(-1===l&&(l=e.indexOf(Math.ceil(t))),"center"===n||"center-odd"===n?l-=(r-1)/2:"center-even"===n?l-=(r-2)/2:"end"===n&&(l-=r-1),!o){const e=i-r,t=0;l=Math.max(Math.min(l,e),t)}return l}({slidesBuffer:f.value,itemsToShow:y.itemsToShow,snapAlign:y.snapAlign,wrapAround:Boolean(y.wrapAround),currentSlide:_.value,slidesCount:d.value})));(0,r.provide)("slidesToScroll",J);const K=(0,r.computed)((()=>{const e="rtl"===y.dir?-1:1,t=J.value*p.value*e;return{transform:`translateX(${R.x-t}px)`,transition:`${W.value?y.transition:0}ms`}}));function Y(){S()}function Z(){S(),E(),L(),O(),N(),U()}function X(){L(),O()}Object.keys(i).forEach((t=>{["modelValue"].includes(t)||(0,r.watch)((()=>e[t]),Z)})),Y(),(0,r.watchEffect)((()=>{const t=d.value!==u.value.length;void 0!==e.modelValue&&_.value!==e.modelValue&&H(Number(e.modelValue),!0),t&&X()}));const Q={config:y,slidesBuffer:f,slidesCount:d,slideWidth:p,currentSlide:_,maxSlide:x,minSlide:k,middleSlide:w};a({updateBreakpointsConfigs:E,updateSlidesData:L,updateSlideWidth:N,updateSlidesBuffer:O,initCarousel:Y,restartCarousel:Z,updateCarousel:X,slideTo:H,next:z,prev:G,nav:q,data:Q});const ee=t.default||t.slides,te=t.addons,ne=(0,r.reactive)(Q);return()=>{const e=function(e){var t,n,r;return e?"CarouselSlide"===(null===(n=null===(t=e[0])||void 0===t?void 0:t.type)||void 0===n?void 0:n.name)?e:(null===(r=e[0])||void 0===r?void 0:r.children)||[]:[]}(null==ee?void 0:ee(ne)),t=(null==te?void 0:te(ne))||[];u.value=e,e.forEach(((e,t)=>e.props.index=t));const n=(0,r.h)("ol",{class:"carousel__track",style:K.value,onMousedown:y.mouseDrag?(0,r.withModifiers)(j,["capture"]):null,onTouchstart:y.touchDrag?(0,r.withModifiers)(j,["capture"]):null},e),o=(0,r.h)("div",{class:"carousel__viewport"},n);return(0,r.h)("section",{ref:c,class:{carousel:!0,"carousel--rtl":"rtl"===y.dir},dir:y.dir,"aria-label":"Gallery",onMouseenter:M,onMouseleave:B},[o,t])}}});const s={arrowUp:"M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z",arrowDown:"M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z",arrowRight:"M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z",arrowLeft:"M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z"},c=e=>{const t=e.name;if(!t||"string"!=typeof t)return;const n=s[t],o=(0,r.h)("path",{d:n}),i=e.title||t,l=(0,r.h)("title",i);return(0,r.h)("svg",{class:"carousel__icon",viewBox:"0 0 24 24",role:"img",ariaLabel:i},[l,o])};c.props={name:String,title:String};const u=(e,{slots:t,attrs:n})=>{const{next:i,prev:l}=t||{},a=(0,r.inject)("config",(0,r.reactive)(Object.assign({},o))),s=(0,r.inject)("maxSlide",(0,r.ref)(1)),u=(0,r.inject)("minSlide",(0,r.ref)(1)),f=(0,r.inject)("currentSlide",(0,r.ref)(1)),p=(0,r.inject)("nav",{}),d="rtl"===a.dir;return[(0,r.h)("button",{type:"button",class:["carousel__prev",!a.wrapAround&&f.value<=u.value&&"carousel__prev--in-active",null==n?void 0:n.class],"aria-label":"Navigate to previous slide",onClick:p.prev},(null==l?void 0:l())||(0,r.h)(c,{name:d?"arrowRight":"arrowLeft"})),(0,r.h)("button",{type:"button",class:["carousel__next",!a.wrapAround&&f.value>=s.value&&"carousel__next--in-active",null==n?void 0:n.class],"aria-label":"Navigate to next slide",onClick:p.next},(null==i?void 0:i())||(0,r.h)(c,{name:d?"arrowLeft":"arrowRight"}))]};var f=(0,r.defineComponent)({name:"CarouselSlide",props:{index:{type:Number,default:1}},setup(e,{slots:t}){const n=(0,r.inject)("config",(0,r.reactive)(Object.assign({},o))),i=(0,r.inject)("slidesBuffer",(0,r.ref)([])),l=(0,r.inject)("currentSlide",(0,r.ref)(0)),a=(0,r.inject)("slidesToScroll",(0,r.ref)(0)),s=(0,r.ref)(e.index);function c(){s.value=i.value.indexOf(e.index)}n.wrapAround&&(c(),(0,r.watch)(i,c));const u=(0,r.computed)((()=>({width:1/n.itemsToShow*100+"%",order:s.value.toString()}))),f=()=>{const t=Math.ceil(a.value),r=Math.floor(a.value+n.itemsToShow);return i.value.slice(t,r).includes(e.index)};return()=>{var o;return(0,r.h)("li",{style:u.value,class:{carousel__slide:!0,"carousel__slide--active":e.index===l.value,"carousel__slide--visible":f(),"carousel__slide--prev":e.index===i.value[Math.ceil(a.value)-1],"carousel__slide--next":e.index===i.value[Math.floor(a.value+n.itemsToShow)]}},null===(o=t.default)||void 0===o?void 0:o.call(t))}}});const p=()=>{const e=(0,r.inject)("maxSlide",(0,r.ref)(1)),t=(0,r.inject)("minSlide",(0,r.ref)(1)),n=(0,r.inject)("currentSlide",(0,r.ref)(1)),o=(0,r.inject)("nav",{});const i=r=>{const o=n.value;return o===r||o>e.value&&r>=e.value||o{return e=n,void o.slideTo(e);var e}}),t=(0,r.h)("li",{class:"carousel__pagination-item",key:n},e);l.push(t)}return(0,r.h)("ol",{class:"carousel__pagination"},l)};var d={key:0,class:"w-full"},h={class:"flex flex-col w-full h-full bg-white shadow md:m-3"},m={key:0},v=["href"],g=["src"],y=["href"],_=["src"],b={class:"flex flex-col items-start w-full h-full p-4"},w={key:0,class:"flex flex-wrap gap-2 mt-0 mb-4 -ml-1"},x=["href"],k={class:"inline-block mb-1 text-sm text-gray-500"},S={class:"-mb-2"},E={key:0},C=["href"],T=["href"];const N={__name:"NewsCarouselNext",props:{slides:{type:Array,required:!0}},setup:function(e){var t=function(e){return e.filter((function(e){return 1==e.visible}))},n={itemsToShow:1,wrapAround:!1,touchDrag:!0,mouseDrag:!0,itemsToScroll:1,transition:400,snapAlign:"start"},o={768:{itemsToShow:2,itemsToScroll:2,snapAlign:"start"},1024:{itemsToShow:3,itemsToScroll:3,snapAlign:"start"},1280:{itemsToShow:4,itemsToScroll:4,snapAlign:"start"}};return function(i,l){return e.slides.length>0?((0,r.openBlock)(),(0,r.createElementBlock)("div",d,[(0,r.createVNode)((0,r.unref)(a),{snapAlign:"start",settings:n,breakpoints:o},{addons:(0,r.withCtx)((function(e){return[e.slidesCount>1?((0,r.openBlock)(),(0,r.createBlock)((0,r.unref)(u),{key:0})):(0,r.createCommentVNode)("",!0),(0,r.createVNode)((0,r.unref)(p))]})),default:(0,r.withCtx)((function(){return[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(e.slides,(function(e){return(0,r.openBlock)(),(0,r.createBlock)((0,r.unref)(f),{key:e},{default:(0,r.withCtx)((function(){return[(0,r.createElementVNode)("div",h,[e.image?((0,r.openBlock)(),(0,r.createElementBlock)("div",m,[e.url?((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:1,class:"w-full",href:e.url},[(0,r.createElementVNode)("img",{src:e.image.conversion_urls.md,alt:"",class:"w-full"},null,8,_)],8,y)):((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:0,class:"w-full",href:e.route_url},[(0,r.createElementVNode)("img",{src:e.image.conversion_urls.md,alt:"",class:"w-full"},null,8,g)],8,v))])):(0,r.createCommentVNode)("",!0),(0,r.createElementVNode)("div",b,[t(e.tags).length>0?((0,r.openBlock)(),(0,r.createElementBlock)("div",w,[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(t(e.tags),(function(e){return(0,r.openBlock)(),(0,r.createElementBlock)("div",null,[e.visible?((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:0,href:e.route,class:(0,r.normalizeClass)(["inline-flex items-center justify-center px-3 pt-1.5 pb-2 text-sm leading-none rounded-lg bg-opacity-20",e.color?"bg-".concat(e.color," text-").concat(e.color):"bg-blue text-blue"])},(0,r.toDisplayString)(e.title),11,x)):(0,r.createCommentVNode)("",!0)])})),256))])):(0,r.createCommentVNode)("",!0),(0,r.createElementVNode)("span",k,(0,r.toDisplayString)(e.published_at),1),(0,r.createElementVNode)("h3",S,[(0,r.createTextVNode)((0,r.toDisplayString)(e.title.replace(/^(.{60}[^\s]*).*/,"$1"))+" ",1),e.title.length>60?((0,r.openBlock)(),(0,r.createElementBlock)("span",E," …")):(0,r.createCommentVNode)("",!0)]),e.url?((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:2,class:"mt-auto hmc-link",href:e.url},(0,r.toDisplayString)(i.$t("app.read-more")),9,T)):((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:1,class:"mt-auto hmc-link",href:e.route_url},(0,r.toDisplayString)(i.$t("app.read-more")),9,C))])])]})),_:2},1024)})),128))]})),_:1})])):(0,r.createCommentVNode)("",!0)}}};var L=n(379),O=n.n(L),A=n(236),P={insert:"head",singleton:!1};O()(A.Z,P);A.Z.locals;const I=N;var R={key:0,class:"w-full"},F={key:0},M=["href"],B=["src"],j=["href"],V=["src"],D={class:"flex flex-col w-full h-full p-4"},$={key:0,class:"flex flex-wrap gap-2 mt-0 mb-4 -ml-1"},U=["href"],W={key:1,class:"flex mb-1 space-x-1"},H={key:0,class:"text-sm text-gray-500"},z={key:1,class:"text-sm text-gray-500"},G={key:2,class:"flex mb-1 space-x-1"},q={key:0,class:"text-sm text-gray-500"},J={key:1,class:"text-sm text-gray-500"},K={class:"-mb-2"},Y={key:0},Z=["href"],X=["href"];const Q={__name:"EventsCarouselNext",props:{slides:{type:Array,required:!0}},setup:function(e){var t=function(e){return e.filter((function(e){return 1==e.visible}))},n={itemsToShow:1,wrapAround:!1,touchDrag:!0,mouseDrag:!0,itemsToScroll:1,transition:400,snapAlign:"start"},o={768:{itemsToShow:2,itemsToScroll:2,snapAlign:"start"},1024:{itemsToShow:3,itemsToScroll:3,snapAlign:"start"},1280:{itemsToShow:4,itemsToScroll:4,snapAlign:"start"}};return function(i,l){return e.slides.length>0?((0,r.openBlock)(),(0,r.createElementBlock)("div",R,[(0,r.createVNode)((0,r.unref)(a),{snapAlign:"start",settings:n,breakpoints:o},{addons:(0,r.withCtx)((function(e){return[e.slidesCount>1?((0,r.openBlock)(),(0,r.createBlock)((0,r.unref)(u),{key:0})):(0,r.createCommentVNode)("",!0),(0,r.createVNode)((0,r.unref)(p))]})),default:(0,r.withCtx)((function(){return[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(e.slides,(function(e){return(0,r.openBlock)(),(0,r.createBlock)((0,r.unref)(f),{key:e},{default:(0,r.withCtx)((function(){return[(0,r.createElementVNode)("div",{class:(0,r.normalizeClass)(["flex flex-col w-full h-full bg-white shadow md:m-3",{"opacity-50":e.in_past}])},[e.image?((0,r.openBlock)(),(0,r.createElementBlock)("div",F,[e.url?((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:1,class:"w-full",href:e.url},[(0,r.createElementVNode)("img",{src:e.image.conversion_urls.md,alt:"",class:"w-full"},null,8,V)],8,j)):((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:0,class:"w-full",href:e.route_url},[(0,r.createElementVNode)("img",{src:e.image.conversion_urls.md,alt:"",class:"w-full"},null,8,B)],8,M))])):(0,r.createCommentVNode)("",!0),(0,r.createElementVNode)("div",D,[t(e.tags).length>0?((0,r.openBlock)(),(0,r.createElementBlock)("div",$,[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(t(e.tags),(function(e){return(0,r.openBlock)(),(0,r.createElementBlock)("div",null,[e.visible?((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:0,href:e.route,class:(0,r.normalizeClass)(["inline-flex items-center justify-center px-3 pt-1.5 pb-2 text-sm leading-none rounded-lg bg-opacity-20",e.color?"bg-".concat(e.color," text-").concat(e.color):"bg-blue text-blue"])},(0,r.toDisplayString)(e.title),11,U)):(0,r.createCommentVNode)("",!0)])})),256))])):(0,r.createCommentVNode)("",!0),e.starts_at!=e.ends_at?((0,r.openBlock)(),(0,r.createElementBlock)("div",W,[e.starts_at?((0,r.openBlock)(),(0,r.createElementBlock)("span",H,(0,r.toDisplayString)(e.starts_at),1)):(0,r.createCommentVNode)("",!0),e.ends_at?((0,r.openBlock)(),(0,r.createElementBlock)("span",z," - "+(0,r.toDisplayString)(e.ends_at),1)):(0,r.createCommentVNode)("",!0)])):(0,r.createCommentVNode)("",!0),e.starts_at==e.ends_at?((0,r.openBlock)(),(0,r.createElementBlock)("div",G,[e.starts_at?((0,r.openBlock)(),(0,r.createElementBlock)("span",q,(0,r.toDisplayString)(e.starts_at),1)):(0,r.createCommentVNode)("",!0),e.time?((0,r.openBlock)(),(0,r.createElementBlock)("span",J," | "+(0,r.toDisplayString)(e.time),1)):(0,r.createCommentVNode)("",!0)])):(0,r.createCommentVNode)("",!0),(0,r.createElementVNode)("h3",K,[(0,r.createTextVNode)((0,r.toDisplayString)(e.title.replace(/^(.{60}[^\s]*).*/,"$1"))+" ",1),e.title.length>60?((0,r.openBlock)(),(0,r.createElementBlock)("span",Y," …")):(0,r.createCommentVNode)("",!0)]),e.url?((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:4,class:"mt-auto hmc-link",href:e.url},(0,r.toDisplayString)(i.$t("app.read-more")),9,X)):((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:3,class:"mt-auto hmc-link",href:e.route_url},(0,r.toDisplayString)(i.$t("app.read-more")),9,Z))])],2)]})),_:2},1024)})),128))]})),_:1})])):(0,r.createCommentVNode)("",!0)}}};var ee=n(563),te={insert:"head",singleton:!1};O()(ee.Z,te);ee.Z.locals;const ne=Q;function re(e){return function(e){if(Array.isArray(e))return oe(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return oe(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return oe(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function oe(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0){var r=a(t.dataobjects),o=s(t.dataobjects),i=c(t.dataobjects),l=u(t.dataobjects);e=function(e){for(var t=new Set,n=0;n0&&(e=e.filter((function(e){var t=e.tags.map((function(e){return e.id}));return f.value.every((function(e){return t.includes(e)}))}))),e}))),a=function(e){return e.filter((function(e){var t;return null===(t=e.keywords)||void 0===t?void 0:t.toLowerCase().includes(n.value.toLocaleLowerCase())}))},s=function(e){return e.filter((function(e){var t;return null===(t=e.title)||void 0===t?void 0:t.toLowerCase().includes(n.value.toLocaleLowerCase())}))},c=function(e){return e.filter((function(e){var t;return null===(t=e.text)||void 0===t?void 0:t.toLowerCase().includes(n.value.toLocaleLowerCase())}))},u=function(e){return e.filter((function(e){var t;return null===(t=e.excerpt)||void 0===t?void 0:t.toLowerCase().includes(n.value.toLocaleLowerCase())}))},f=(0,r.ref)([]);return function(t,i){var a;return(0,r.openBlock)(),(0,r.createElementBlock)(r.Fragment,null,[(0,r.createElementVNode)("div",ie,[(0,r.createElementVNode)("div",le,[(0,r.createElementVNode)("div",ae,[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(e.filtertags,(function(e){return(0,r.openBlock)(),(0,r.createElementBlock)("div",{onClick:function(t){return n=e.id,void(f.value.includes(n)?f.value.splice(f.value.indexOf(n),1):f.value.push(n));var n},class:(0,r.normalizeClass)(["rounded-md px-3 py-1 cursor-pointer mr-2 transition flex items-center text-sm",[{"bg-blue text-white":f.value.includes(e.id),"bg-gray-200 hover:bg-gray-300 text-gray-500":!f.value.includes(e.id)}]])},[(0,r.createTextVNode)((0,r.toDisplayString)(e.title)+" ",1),f.value.includes(e.id)?((0,r.openBlock)(),(0,r.createElementBlock)("span",ce,[((0,r.openBlock)(),(0,r.createElementBlock)("svg",ue,fe))])):(0,r.createCommentVNode)("",!0)],10,se)})),256))])]),(0,r.createElementVNode)("div",pe,[(0,r.withDirectives)((0,r.createElementVNode)("input",{"onUpdate:modelValue":i[0]||(i[0]=function(e){return n.value=e}),type:"text",placeholder:t.$t("app.search"),class:"w-full px-3 py-3 text-sm bg-white rounded-[10px] outline-none placeholder:text-gray-400 focus:ring focus:ring-blue focus:ring-opacity-30"},null,8,de),[[r.vModelText,n.value]]),he])]),(0,r.createElementVNode)("div",null,(0,r.toDisplayString)(null===(a=o.value)||void 0===a?void 0:a.id),1),((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)((0,r.unref)(l),(function(e){return(0,r.openBlock)(),(0,r.createElementBlock)("div",me,[e.image?((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:0,class:"max-w-full lg:max-w-[200px] mb-auto",href:e.route_url},[(0,r.createElementVNode)("img",{src:e.image.conversion_urls.md,alt:"",class:"w-full"},null,8,ge)],8,ve)):(0,r.createCommentVNode)("",!0),(0,r.createElementVNode)("div",ye,[(0,r.createElementVNode)("div",_e,[(0,r.createElementVNode)("a",{href:e.route_url},(0,r.toDisplayString)(e.title),9,be)]),(0,r.createElementVNode)("div",null,(0,r.toDisplayString)(e.excerpt),1)]),(0,r.createElementVNode)("div",we,[(0,r.createElementVNode)("a",{class:"hmc-link_sm",href:e.route_url},(0,r.toDisplayString)(t.$t("app.detailsLinkText")),9,xe)])])})),256)),0==(0,r.unref)(l).length?((0,r.openBlock)(),(0,r.createElementBlock)("div",ke,[(0,r.createElementVNode)("p",null,(0,r.toDisplayString)(t.$t("app.no-hits")),1)])):(0,r.createCommentVNode)("",!0)],64)}}};function Ee(e,t,...n){if(e in t){let r=t[e];return"function"==typeof r?r(...n):r}let r=new Error(`Tried to handle "${e}" but there is no handler defined. Only defined handlers are: ${Object.keys(t).map((e=>`"${e}"`)).join(", ")}.`);throw Error.captureStackTrace&&Error.captureStackTrace(r,Ee),r}var Ce,Te=((Ce=Te||{})[Ce.None=0]="None",Ce[Ce.RenderStrategy=1]="RenderStrategy",Ce[Ce.Static=2]="Static",Ce),Ne=(e=>(e[e.Unmount=0]="Unmount",e[e.Hidden=1]="Hidden",e))(Ne||{});function Le({visible:e=!0,features:t=0,ourProps:n,theirProps:r,...o}){var i;let l=function(...e){if(0===e.length)return{};if(1===e.length)return e[0];let t={},n={};for(let r of e)for(let e in r)e.startsWith("on")&&"function"==typeof r[e]?(null!=n[e]||(n[e]=[]),n[e].push(r[e])):t[e]=r[e];if(t.disabled||t["aria-disabled"])return Object.assign(t,Object.fromEntries(Object.keys(n).map((e=>[e,void 0]))));for(let e in n)Object.assign(t,{[e](t,...r){let o=n[e];for(let e of o){if(t instanceof Event&&t.defaultPrevented)return;e(t,...r)}}});return t}(r,n),a=Object.assign(o,{props:l});if(e||2&t&&l.static)return Oe(a);if(1&t){return Ee(null==(i=l.unmount)||i?0:1,{0:()=>null,1:()=>Oe({...o,props:{...l,hidden:!0,style:{display:"none"}}})})}return Oe(a)}function Oe({props:e,attrs:t,slots:n,slot:o,name:i}){var l;let{as:a,...s}=Pe(e,["unmount","static"]),c=null==(l=n.default)?void 0:l.call(n,o),u={};if(o){let e=!1,t=[];for(let[n,r]of Object.entries(o))"boolean"==typeof r&&(e=!0),!0===r&&t.push(n);e&&(u["data-headlessui-state"]=t.join(" "))}if("template"===a){if(c=Ae(null!=c?c:[]),Object.keys(s).length>0||Object.keys(t).length>0){let[e,...n]=null!=c?c:[];if(!function(e){return null!=e&&("string"==typeof e.type||"object"==typeof e.type||"function"==typeof e.type)}(e)||n.length>0)throw new Error(['Passing props on "template"!',"",`The current component <${i} /> is rendering a "template".`,"However we need to passthrough the following props:",Object.keys(s).concat(Object.keys(t)).sort(((e,t)=>e.localeCompare(t))).map((e=>` - ${e}`)).join("\n"),"","You can apply a few solutions:",['Add an `as="..."` prop, to ensure that we render an actual element instead of a "template".',"Render a single element as the child so that we can forward the props onto that element."].map((e=>` - ${e}`)).join("\n")].join("\n"));return(0,r.cloneVNode)(e,Object.assign({},s,u))}return Array.isArray(c)&&1===c.length?c[0]:c}return(0,r.h)(a,Object.assign({},s,u),{default:()=>c})}function Ae(e){return e.flatMap((e=>e.type===r.Fragment?Ae(e.children):[e]))}function Pe(e,t=[]){let n=Object.assign({},e);for(let e of t)e in n&&delete n[e];return n}let Ie=0;function Re(){return++Ie}var Fe=(e=>(e.Space=" ",e.Enter="Enter",e.Escape="Escape",e.Backspace="Backspace",e.Delete="Delete",e.ArrowLeft="ArrowLeft",e.ArrowUp="ArrowUp",e.ArrowRight="ArrowRight",e.ArrowDown="ArrowDown",e.Home="Home",e.End="End",e.PageUp="PageUp",e.PageDown="PageDown",e.Tab="Tab",e))(Fe||{});function Me(e){var t;return null==e||null==e.value?null:null!=(t=e.value.$el)?t:e.value}const Be="undefined"==typeof window||"undefined"==typeof document;function je(e){if(Be)return null;if(e instanceof Node)return e.ownerDocument;if(null!=e&&e.hasOwnProperty("value")){let t=Me(e);if(t)return t.ownerDocument}return document}let Ve=["[contentEditable=true]","[tabindex]","a[href]","area[href]","button:not([disabled])","iframe","input:not([disabled])","select:not([disabled])","textarea:not([disabled])"].map((e=>`${e}:not([tabindex='-1'])`)).join(",");var De=(e=>(e[e.First=1]="First",e[e.Previous=2]="Previous",e[e.Next=4]="Next",e[e.Last=8]="Last",e[e.WrapAround=16]="WrapAround",e[e.NoScroll=32]="NoScroll",e))(De||{}),$e=(e=>(e[e.Error=0]="Error",e[e.Overflow=1]="Overflow",e[e.Success=2]="Success",e[e.Underflow=3]="Underflow",e))($e||{}),Ue=(e=>(e[e.Previous=-1]="Previous",e[e.Next=1]="Next",e))(Ue||{});function We(e=document.body){return null==e?[]:Array.from(e.querySelectorAll(Ve))}var He=(e=>(e[e.Strict=0]="Strict",e[e.Loose=1]="Loose",e))(He||{});function ze(e,t=0){var n;return e!==(null==(n=je(e))?void 0:n.body)&&Ee(t,{0:()=>e.matches(Ve),1(){let t=e;for(;null!==t;){if(t.matches(Ve))return!0;t=t.parentElement}return!1}})}let Ge=["textarea","input"].join(",");function qe(e,t,n=!0,r=null){var o;let i=null!=(o=Array.isArray(e)?e.length>0?e[0].ownerDocument:document:null==e?void 0:e.ownerDocument)?o:document,l=Array.isArray(e)?n?function(e,t=(e=>e)){return e.slice().sort(((e,n)=>{let r=t(e),o=t(n);if(null===r||null===o)return 0;let i=r.compareDocumentPosition(o);return i&Node.DOCUMENT_POSITION_FOLLOWING?-1:i&Node.DOCUMENT_POSITION_PRECEDING?1:0}))}(e):e:We(e);r=null!=r?r:i.activeElement;let a,s=(()=>{if(5&t)return 1;if(10&t)return-1;throw new Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),c=(()=>{if(1&t)return 0;if(2&t)return Math.max(0,l.indexOf(r))-1;if(4&t)return Math.max(0,l.indexOf(r))+1;if(8&t)return l.length-1;throw new Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),u=32&t?{preventScroll:!0}:{},f=0,p=l.length;do{if(f>=p||f+p<=0)return 0;let e=c+f;if(16&t)e=(e+p)%p;else{if(e<0)return 3;if(e>=p)return 1}a=l[e],null==a||a.focus(u),f+=s}while(a!==i.activeElement);return 6&t&&function(e){var t,n;return null!=(n=null==(t=null==e?void 0:e.matches)?void 0:t.call(e,Ge))&&n}(a)&&a.select(),a.hasAttribute("tabindex")||a.setAttribute("tabindex","0"),2}let Je=Symbol("Context");var Ke=(e=>(e[e.Open=0]="Open",e[e.Closed=1]="Closed",e))(Ke||{});function Ye(){return(0,r.inject)(Je,null)}function Ze(e,t){if(e)return e;let n=null!=t?t:"button";return"string"==typeof n&&"button"===n.toLowerCase()?"button":void 0}function Xe(e,t,n){Be||(0,r.watchEffect)((r=>{document.addEventListener(e,t,n),r((()=>document.removeEventListener(e,t,n)))}))}var Qe=(e=>(e[e.None=1]="None",e[e.Focusable=2]="Focusable",e[e.Hidden=4]="Hidden",e))(Qe||{});let et=(0,r.defineComponent)({name:"Hidden",props:{as:{type:[Object,String],default:"div"},features:{type:Number,default:1}},setup:(e,{slots:t,attrs:n})=>()=>{let{features:r,...o}=e;return Le({ourProps:{"aria-hidden":2==(2&r)||void 0,style:{position:"fixed",top:1,left:1,width:1,height:0,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:"0",...4==(4&r)&&2!=(2&r)&&{display:"none"}}},theirProps:o,slot:{},attrs:n,slots:t,name:"Hidden"})}});var tt=(e=>(e[e.Forwards=0]="Forwards",e[e.Backwards=1]="Backwards",e))(tt||{});function nt(){let e=(0,r.ref)(0);return function(e,t,n){Be||(0,r.watchEffect)((r=>{window.addEventListener(e,t,n),r((()=>window.removeEventListener(e,t,n)))}))}("keydown",(t=>{"Tab"===t.key&&(e.value=t.shiftKey?1:0)})),e}var rt=(e=>(e[e.Open=0]="Open",e[e.Closed=1]="Closed",e))(rt||{});let ot=Symbol("PopoverContext");function it(e){let t=(0,r.inject)(ot,null);if(null===t){let t=new Error(`<${e} /> is missing a parent <${ct.name} /> component.`);throw Error.captureStackTrace&&Error.captureStackTrace(t,it),t}return t}let lt=Symbol("PopoverGroupContext");function at(){return(0,r.inject)(lt,null)}let st=Symbol("PopoverPanelContext");let ct=(0,r.defineComponent)({name:"Popover",props:{as:{type:[Object,String],default:"div"}},setup(e,{slots:t,attrs:n,expose:o}){var i;let l=`headlessui-popover-button-${Re()}`,a=`headlessui-popover-panel-${Re()}`,s=(0,r.ref)(null);o({el:s,$el:s});let c=(0,r.ref)(1),u=(0,r.ref)(null),f=(0,r.ref)(null),p=(0,r.ref)(null),d=(0,r.ref)(null),h=(0,r.computed)((()=>je(s))),m=(0,r.computed)((()=>{var e,t;if(!Me(u)||!Me(d))return!1;for(let e of document.querySelectorAll("body > *"))if(Number(null==e?void 0:e.contains(Me(u)))^Number(null==e?void 0:e.contains(Me(d))))return!0;let n=We(),r=n.indexOf(Me(u)),o=(r+n.length-1)%n.length,i=(r+1)%n.length,l=n[o],a=n[i];return!(null!=(e=Me(d))&&e.contains(l)||null!=(t=Me(d))&&t.contains(a))})),v={popoverState:c,buttonId:l,panelId:a,panel:d,button:u,isPortalled:m,beforePanelSentinel:f,afterPanelSentinel:p,togglePopover(){c.value=Ee(c.value,{0:1,1:0})},closePopover(){1!==c.value&&(c.value=1)},close(e){v.closePopover();let t=e?e instanceof HTMLElement?e:e.value instanceof HTMLElement?Me(e):Me(v.button):Me(v.button);null==t||t.focus()}};(0,r.provide)(ot,v),function(e){(0,r.provide)(Je,e)}((0,r.computed)((()=>Ee(c.value,{0:Ke.Open,1:Ke.Closed}))));let g={buttonId:l,panelId:a,close(){v.closePopover()}},y=at(),_=null==y?void 0:y.registerPopover;return(0,r.watchEffect)((()=>null==_?void 0:_(g))),function(e,t,n,o){Be||(0,r.watchEffect)((r=>{(e=null!=e?e:window).addEventListener(t,n,o),r((()=>e.removeEventListener(t,n,o)))}))}(null==(i=h.value)?void 0:i.defaultView,"focus",(e=>{var t,n;0===c.value&&(function(){var e,t,n,r;return null!=(r=null==y?void 0:y.isFocusWithinPopoverGroup())?r:(null==(e=h.value)?void 0:e.activeElement)&&((null==(t=Me(u))?void 0:t.contains(h.value.activeElement))||(null==(n=Me(d))?void 0:n.contains(h.value.activeElement)))}()||!u||!d||null!=(t=Me(v.beforePanelSentinel))&&t.contains(e.target)||null!=(n=Me(v.afterPanelSentinel))&&n.contains(e.target)||v.closePopover())}),!0),function(e,t,n=(0,r.computed)((()=>!0))){function o(r,o){if(!n.value||r.defaultPrevented)return;let i=o(r);if(null===i||!i.getRootNode().contains(i))return;let l=function e(t){return"function"==typeof t?e(t()):Array.isArray(t)||t instanceof Set?t:[t]}(e);for(let e of l){if(null===e)continue;let t=e instanceof HTMLElement?e:Me(e);if(null!=t&&t.contains(i))return}return!ze(i,He.Loose)&&-1!==i.tabIndex&&r.preventDefault(),t(r,i)}let i=(0,r.ref)(null);Xe("mousedown",(e=>{var t,r;n.value&&(i.value=(null==(r=null==(t=e.composedPath)?void 0:t.call(e))?void 0:r[0])||e.target)}),!0),Xe("click",(e=>{!i.value||(o(e,(()=>i.value)),i.value=null)}),!0),Xe("blur",(e=>o(e,(()=>window.document.activeElement instanceof HTMLIFrameElement?window.document.activeElement:null))),!0)}([u,d],((e,t)=>{var n;v.closePopover(),ze(t,He.Loose)||(e.preventDefault(),null==(n=Me(u))||n.focus())}),(0,r.computed)((()=>0===c.value))),()=>{let r={open:0===c.value,close:v.close};return Le({theirProps:e,ourProps:{ref:s},slot:r,slots:t,attrs:n,name:"Popover"})}}}),ut=(0,r.defineComponent)({name:"PopoverButton",props:{as:{type:[Object,String],default:"button"},disabled:{type:[Boolean],default:!1}},inheritAttrs:!1,setup(e,{attrs:t,slots:n,expose:o}){let i=it("PopoverButton"),l=(0,r.computed)((()=>je(i.button)));o({el:i.button,$el:i.button});let a=at(),s=null==a?void 0:a.closeOthers,c=(0,r.inject)(st,null),u=null!==c&&c===i.panelId,f=(0,r.ref)(null),p=`headlessui-focus-sentinel-${Re()}`;u||(0,r.watchEffect)((()=>{i.button.value=f.value}));let d=function(e,t){let n=(0,r.ref)(Ze(e.value.type,e.value.as));return(0,r.onMounted)((()=>{n.value=Ze(e.value.type,e.value.as)})),(0,r.watchEffect)((()=>{var e;n.value||!Me(t)||Me(t)instanceof HTMLButtonElement&&(null==(e=Me(t))||!e.hasAttribute("type"))&&(n.value="button")})),n}((0,r.computed)((()=>({as:e.as,type:t.type}))),f);function h(e){var t,n,r,o,a;if(u){if(1===i.popoverState.value)return;switch(e.key){case Fe.Space:case Fe.Enter:e.preventDefault(),null==(n=(t=e.target).click)||n.call(t),i.closePopover(),null==(r=Me(i.button))||r.focus()}}else switch(e.key){case Fe.Space:case Fe.Enter:e.preventDefault(),e.stopPropagation(),1===i.popoverState.value&&(null==s||s(i.buttonId)),i.togglePopover();break;case Fe.Escape:if(0!==i.popoverState.value)return null==s?void 0:s(i.buttonId);if(!Me(i.button)||(null==(o=l.value)?void 0:o.activeElement)&&(null==(a=Me(i.button))||!a.contains(l.value.activeElement)))return;e.preventDefault(),e.stopPropagation(),i.closePopover()}}function m(e){u||e.key===Fe.Space&&e.preventDefault()}function v(t){var n,r;e.disabled||(u?(i.closePopover(),null==(n=Me(i.button))||n.focus()):(t.preventDefault(),t.stopPropagation(),1===i.popoverState.value&&(null==s||s(i.buttonId)),i.togglePopover(),null==(r=Me(i.button))||r.focus()))}function g(e){e.preventDefault(),e.stopPropagation()}return()=>{let o=0===i.popoverState.value,l={open:o},a=u?{ref:f,type:d.value,onKeydown:h,onClick:v}:{ref:f,id:i.buttonId,type:d.value,"aria-expanded":e.disabled?void 0:0===i.popoverState.value,"aria-controls":Me(i.panel)?i.panelId:void 0,disabled:!!e.disabled||void 0,onKeydown:h,onKeyup:m,onClick:v,onMousedown:g},s=nt();return(0,r.h)(r.Fragment,[Le({ourProps:a,theirProps:{...t,...e},slot:l,attrs:t,slots:n,name:"PopoverButton"}),o&&!u&&i.isPortalled.value&&(0,r.h)(et,{id:p,features:Qe.Focusable,as:"button",type:"button",onFocus:function(){let e=Me(i.panel);e&&Ee(s.value,{[tt.Forwards]:()=>qe(e,De.First),[tt.Backwards]:()=>qe(e,De.Last)})}})])}}}),ft=((0,r.defineComponent)({name:"PopoverOverlay",props:{as:{type:[Object,String],default:"div"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},setup(e,{attrs:t,slots:n}){let o=it("PopoverOverlay"),i=`headlessui-popover-overlay-${Re()}`,l=Ye(),a=(0,r.computed)((()=>null!==l?l.value===Ke.Open:0===o.popoverState.value));function s(){o.closePopover()}return()=>{let r={open:0===o.popoverState.value};return Le({ourProps:{id:i,"aria-hidden":!0,onClick:s},theirProps:e,slot:r,attrs:t,slots:n,features:Te.RenderStrategy|Te.Static,visible:a.value,name:"PopoverOverlay"})}}}),(0,r.defineComponent)({name:"PopoverPanel",props:{as:{type:[Object,String],default:"div"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0},focus:{type:Boolean,default:!1}},inheritAttrs:!1,setup(e,{attrs:t,slots:n,expose:o}){let{focus:i}=e,l=it("PopoverPanel"),a=(0,r.computed)((()=>je(l.panel))),s=`headlessui-focus-sentinel-before-${Re()}`,c=`headlessui-focus-sentinel-after-${Re()}`;o({el:l.panel,$el:l.panel}),(0,r.provide)(st,l.panelId),(0,r.watchEffect)((()=>{var e,t;if(!i||0!==l.popoverState.value||!l.panel)return;let n=null==(e=a.value)?void 0:e.activeElement;null!=(t=Me(l.panel))&&t.contains(n)||qe(Me(l.panel),De.First)}));let u=Ye(),f=(0,r.computed)((()=>null!==u?u.value===Ke.Open:0===l.popoverState.value));function p(e){var t,n;if(e.key===Fe.Escape){if(0!==l.popoverState.value||!Me(l.panel)||a.value&&(null==(t=Me(l.panel))||!t.contains(a.value.activeElement)))return;e.preventDefault(),e.stopPropagation(),l.closePopover(),null==(n=Me(l.button))||n.focus()}}function d(e){var t,n,r,o,i;let a=e.relatedTarget;!a||!Me(l.panel)||null!=(t=Me(l.panel))&&t.contains(a)||(l.closePopover(),((null==(r=null==(n=Me(l.beforePanelSentinel))?void 0:n.contains)?void 0:r.call(n,a))||(null==(i=null==(o=Me(l.afterPanelSentinel))?void 0:o.contains)?void 0:i.call(o,a)))&&a.focus({preventScroll:!0}))}let h=nt();function m(){let e=Me(l.panel);e&&Ee(h.value,{[tt.Forwards]:()=>{qe(e,De.Next)},[tt.Backwards]:()=>{var e;null==(e=Me(l.button))||e.focus({preventScroll:!0})}})}function v(){let e=Me(l.panel);e&&Ee(h.value,{[tt.Forwards]:()=>{var e,t;let n=Me(l.button),r=Me(l.panel);if(!n)return;let o=We(),i=o.indexOf(n),a=o.slice(0,i+1),s=[...o.slice(i+1),...a];for(let n of s.slice())if((null==(t=null==(e=null==n?void 0:n.id)?void 0:e.startsWith)?void 0:t.call(e,"headlessui-focus-sentinel-"))||(null==r?void 0:r.contains(n))){let e=s.indexOf(n);-1!==e&&s.splice(e,1)}qe(s,De.First,!1)},[tt.Backwards]:()=>qe(e,De.Previous)})}return()=>{let o={open:0===l.popoverState.value,close:l.close};return Le({ourProps:{ref:l.panel,id:l.panelId,onKeydown:p,onFocusout:i&&0===l.popoverState.value?d:void 0,tabIndex:-1},theirProps:{...t,...Pe(e,["focus"])},attrs:t,slot:o,slots:{...n,default:(...e)=>{var t;return[(0,r.h)(r.Fragment,[f.value&&l.isPortalled.value&&(0,r.h)(et,{id:s,ref:l.beforePanelSentinel,features:Qe.Focusable,as:"button",type:"button",onFocus:m}),null==(t=n.default)?void 0:t.call(n,...e),f.value&&l.isPortalled.value&&(0,r.h)(et,{id:c,ref:l.afterPanelSentinel,features:Qe.Focusable,as:"button",type:"button",onFocus:v})])]}},features:Te.RenderStrategy|Te.Static,visible:f.value,name:"PopoverPanel"})}}}));(0,r.defineComponent)({name:"PopoverGroup",props:{as:{type:[Object,String],default:"div"}},setup(e,{attrs:t,slots:n,expose:o}){let i=(0,r.ref)(null),l=(0,r.ref)([]),a=(0,r.computed)((()=>je(i)));function s(e){let t=l.value.indexOf(e);-1!==t&&l.value.splice(t,1)}return o({el:i,$el:i}),(0,r.provide)(lt,{registerPopover:function(e){return l.value.push(e),()=>{s(e)}},unregisterPopover:s,isFocusWithinPopoverGroup:function(){var e;let t=a.value;if(!t)return!1;let n=t.activeElement;return!(null==(e=Me(i))||!e.contains(n))||l.value.some((e=>{var r,o;return(null==(r=t.getElementById(e.buttonId))?void 0:r.contains(n))||(null==(o=t.getElementById(e.panelId))?void 0:o.contains(n))}))},closeOthers:function(e){for(let t of l.value)t.buttonId!==e&&t.close()}}),()=>Le({ourProps:{ref:i},theirProps:e,slot:{},attrs:t,slots:n,name:"PopoverGroup"})}});var pt={class:"items-center hidden h-full xl:flex"},dt={key:0,class:"relative"},ht=["id"],mt=[(0,r.createElementVNode)("g",{stroke:"none","stroke-width":"1",fill:"none","fill-rule":"evenodd"},[(0,r.createElementVNode)("g",{transform:"translate(-766.000000, -50.000000)",fill:"#005799","fill-rule":"nonzero"},[(0,r.createElementVNode)("polygon",{points:"772 50 772.707107 50.7071068 769.353553 54.0606602 766 50.7071068 766.707107 50 767.544606 50.8375328 769.353553 52.6465534"})])],-1)],vt={key:0,class:"flex flex-col px-5 pb-2 shadow-md"},gt=["href"],yt=["onClick"],_t={class:"!text-blue"},bt=["id"],wt=[(0,r.createElementVNode)("path",{d:"m9 6 6 6-6 6",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round"},null,-1)],xt=["id"],kt=["href"],St=["href"],Et={class:"flex items-center"},Ct={class:"overflow-y-auto h-full"},Tt={class:"p-[50px] grid grid-cols-3 gap-x-[60px] gap-y-[70px]"},Nt={class:""},Lt={class:"flex flex-col w-full"},Ot=["href"],At=["onClick"],Pt={class:"!text-blue"},It=["id"],Rt=[(0,r.createElementVNode)("path",{d:"m9 6 6 6-6 6",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round"},null,-1)],Ft=["id"],Mt=["href"],Bt=(0,r.createElementVNode)("div",{class:"sticky bg-gradient-to-t from-white bottom-0 left-0 w-fill h-32 pointer-events-none mr-12"},null,-1);const jt={__name:"MainNavigation",props:{menu:{type:Array,required:!0}},setup:function(e){var t,n=function(e){clearTimeout(t)},o=function(e){t=setTimeout((function(){i()}),500)},i=function(){document.querySelectorAll(".popover-button-level-1").forEach((function(e){"open"==e.getAttribute("data-headlessui-state")&&e.click()}))};return function(l,a){return(0,r.openBlock)(),(0,r.createElementBlock)("div",pt,[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(e.menu,(function(e,l){return(0,r.openBlock)(),(0,r.createBlock)((0,r.unref)(ct),{key:l},{default:(0,r.withCtx)((function(s){var c=s.open;return[JSON.parse(e.children).length>0?((0,r.openBlock)(),(0,r.createElementBlock)("div",dt,[(0,r.createVNode)((0,r.unref)(ut),{as:"div",class:(0,r.normalizeClass)(["popover-button-level-1 px-2 mx-0.5 text-blue hover:text-blue-light min-h-[60px] inline-flex items-center focus:outline-none transition cursor-pointer",{"!text-blue-light":e.is_active}]),onMouseover:a[0]||(a[0]=function(e){return n=e,i(),clearTimeout(t),void("open"!=n.target.getAttribute("data-headlessui-state")&&n.target.click());var n}),onMouseout:a[1]||(a[1]=function(e){t=setTimeout((function(){i()}),500)})},{default:(0,r.withCtx)((function(){return[(0,r.createElementVNode)("div",{id:"main-nav-btn-".concat(l),class:"flex items-center gap-2 uppercase rounded group focus:outline-none pointer-events-none"},[(0,r.createElementVNode)("span",null,(0,r.toDisplayString)(e.title),1),((0,r.openBlock)(),(0,r.createElementBlock)("svg",{width:"7px",height:"4px",viewBox:"0 0 7 4",version:"1.1",xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",class:(0,r.normalizeClass)(["transition duration-200",{"rotate-180":c}])},mt,2))],8,ht)]})),_:2},1032,["class"]),(0,r.createVNode)(r.TransitionGroup,{name:"panels","enter-active-class":"transition duration-300 ease-out","enter-from-class":"opacity-0","enter-to-class":"opacity-100","leave-active-class":"transition duration-150 ease-in","leave-from-class":"opacity-100","leave-to-class":"opacity-0"},{default:(0,r.withCtx)((function(){return[e.mega_menu?(0,r.createCommentVNode)("",!0):(0,r.withDirectives)(((0,r.openBlock)(),(0,r.createBlock)((0,r.unref)(ft),{key:0,static:"",class:"absolute left-1/2 -translate-x-1/2 top-full z-40 w-[258px] hidden overflow-y-auto shadow-xl transform bg-white border-t-4 xl:block border-blue-light pt-2",onMouseover:a[2]||(a[2]=function(e){return n()}),onMouseout:a[3]||(a[3]=function(e){return o()})},{default:(0,r.withCtx)((function(){return[e.mega_menu?(0,r.createCommentVNode)("",!0):((0,r.openBlock)(),(0,r.createElementBlock)("div",vt,[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(JSON.parse(e.children),(function(e,t){return(0,r.openBlock)(),(0,r.createElementBlock)("div",{key:t,class:"flex flex-col w-full pb-2 border-b border-gray-200 last:border-white"},[0==JSON.parse(e.children).length?((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:0,class:(0,r.normalizeClass)(["flex w-full pt-3 text-sm text-left text-blue hover:text-blue-light transition",{"!text-blue-light":e.is_active}]),href:e.route},(0,r.toDisplayString)(e.title),11,gt)):(0,r.createCommentVNode)("",!0),JSON.parse(e.children).length>0?((0,r.openBlock)(),(0,r.createElementBlock)("button",{key:1,onClick:function(e){return function(e,t){var n=document.getElementById("sub-level-two-".concat(e,"-").concat(t)),r=document.getElementById("sub-level-two-indicator-".concat(e,"-").concat(t));if(n.classList.contains("hidden"))return n.classList.replace("hidden","flex"),void r.classList.replace("rotate-90","-rotate-90");n.classList.replace("flex","hidden"),r.classList.replace("-rotate-90","rotate-90")}(l,t)},class:(0,r.normalizeClass)(["flex justify-between w-full pt-2 text-sm text-left text-blue hover:text-blue-light transition",{"!text-blue-light":e.is_active}])},[(0,r.createElementVNode)("div",null,(0,r.toDisplayString)(e.title),1),(0,r.createElementVNode)("div",_t,[((0,r.openBlock)(),(0,r.createElementBlock)("svg",{id:"sub-level-two-indicator-".concat(l,"-").concat(t),width:"24",height:"24","stroke-width":"1.5",class:"transition-transform duration-300 origin-center scale-50 rotate-90",fill:"none",xmlns:"http://www.w3.org/2000/svg"},wt,8,bt))])],10,yt)):(0,r.createCommentVNode)("",!0),JSON.parse(e.children).length>0?((0,r.openBlock)(),(0,r.createElementBlock)("div",{key:2,id:"sub-level-two-".concat(l,"-").concat(t),class:"flex-col hidden w-full gap-3 pt-2"},[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(JSON.parse(e.children),(function(e,t){return(0,r.openBlock)(),(0,r.createElementBlock)("div",{key:t,class:"flex"},[(0,r.createElementVNode)("a",{class:(0,r.normalizeClass)(["flex pl-3 text-sm text-blue hover:text-blue-light transition",{"!text-blue-light":e.is_active}]),href:e.route},(0,r.toDisplayString)(e.title),11,kt)])})),128))],8,xt)):(0,r.createCommentVNode)("",!0)])})),128))]))]})),_:2},1536)),[[r.vShow,c]])]})),_:2},1024)])):((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:1,href:e.route,class:(0,r.normalizeClass)(["relative min-h-[60px] uppercase px-2 mx-2 text-blue hover:text-blue-light inline-flex items-center font-medium rounded focus:outline-none",{"!text-blue-light":e.is_active}])},[(0,r.createElementVNode)("div",Et,(0,r.toDisplayString)(e.title),1)],10,St)),e.mega_menu?((0,r.openBlock)(),(0,r.createBlock)(r.Teleport,{key:2,to:"#app-header"},[(0,r.createVNode)(r.TransitionGroup,{name:"megapanels","enter-active-class":"transition duration-300 ease-out","enter-from-class":"opacity-0","enter-to-class":"opacity-100","leave-active-class":"transition duration-150 ease-in","leave-from-class":"opacity-100","leave-to-class":"opacity-0"},{default:(0,r.withCtx)((function(){return[(0,r.withDirectives)((0,r.createVNode)((0,r.unref)(ft),{static:"",class:(0,r.normalizeClass)(["absolute right-0 pr-6 pt-6 pb-6 z-40 hidden overflow-x-visible transform bg-white border-t-4 shadow-xl xl:block border-blue-light",{"w-[1000px] mega-height":e.mega_menu,"w-[258px] h-fit":!e.mega_menu}]),onMouseover:a[4]||(a[4]=function(e){return n()}),onMouseout:a[5]||(a[5]=function(e){return o()})},{default:(0,r.withCtx)((function(){return[(0,r.createElementVNode)("div",Ct,[(0,r.createElementVNode)("div",Tt,[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(JSON.parse(e.children),(function(e,t){return(0,r.openBlock)(),(0,r.createElementBlock)("div",{key:t},[(0,r.createElementVNode)("div",Nt,[(0,r.createElementVNode)("div",{class:(0,r.normalizeClass)(["text-base flex border-b-4 pb-3.5 text-blue mb-3",{"border-blue":!e.item_color||"blue"==e.item_color,"border-green":"green"==e.item_color,"border-purple":"purple"==e.item_color,"border-red":"red"==e.item_color,"border-orange":"orange"==e.item_color,"border-yellow":"yellow"==e.item_color,"border-turquoise":"turquoise"==e.item_color,"border-darkgreen":"darkgreen"==e.item_color,"border-monochrome":"monochrome"==e.item_color,"border-blueviolet":"blueviolet"==e.item_color,"border-lightblue":"lightblue"==e.item_color,"border-violet":"violet"==e.item_color,"border-gray-400":"gray-400"==e.item_color}])},(0,r.toDisplayString)(e.title),3),(0,r.createElementVNode)("div",Lt,[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(JSON.parse(e.children),(function(e,n){return(0,r.openBlock)(),(0,r.createElementBlock)("div",{key:n,class:"pb-2 border-b border-gray-200"},[0==JSON.parse(e.children).length?((0,r.openBlock)(),(0,r.createElementBlock)("a",{key:0,class:(0,r.normalizeClass)(["flex w-full pt-2 text-sm text-left text-blue hover:text-blue-light transition",{"!text-blue-light":e.is_active}]),href:e.route},(0,r.toDisplayString)(e.title),11,Ot)):(0,r.createCommentVNode)("",!0),JSON.parse(e.children).length>0?((0,r.openBlock)(),(0,r.createElementBlock)("button",{key:1,onClick:function(e){return function(e,t){var n=document.getElementById("sub-level-three-".concat(e,"-").concat(t)),r=document.getElementById("sub-level-three-indicator-".concat(e,"-").concat(t));if(n.classList.contains("hidden"))return n.classList.replace("hidden","flex"),void r.classList.replace("rotate-90","-rotate-90");n.classList.replace("flex","hidden"),r.classList.replace("-rotate-90","rotate-90")}(t,n)},class:(0,r.normalizeClass)(["flex justify-between w-full pt-2 text-sm text-left text-blue hover:text-blue-light transition",{"!text-blue-light":e.is_active}])},[(0,r.createElementVNode)("div",null,(0,r.toDisplayString)(e.title),1),(0,r.createElementVNode)("div",Pt,[((0,r.openBlock)(),(0,r.createElementBlock)("svg",{id:"sub-level-three-indicator-".concat(t,"-").concat(n),width:"24",height:"24","stroke-width":"1.5",class:"transition-transform duration-300 origin-center scale-50 rotate-90",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Rt,8,It))])],10,At)):(0,r.createCommentVNode)("",!0),JSON.parse(e.children).length>0?((0,r.openBlock)(),(0,r.createElementBlock)("div",{key:2,id:"sub-level-three-".concat(t,"-").concat(n),class:"flex-col hidden w-full gap-3 pt-2"},[((0,r.openBlock)(!0),(0,r.createElementBlock)(r.Fragment,null,(0,r.renderList)(JSON.parse(e.children),(function(e,t){return(0,r.openBlock)(),(0,r.createElementBlock)("div",{key:t},[(0,r.createElementVNode)("a",{class:(0,r.normalizeClass)(["flex pl-3 text-sm text-blue hover:text-blue-light transition",{"!text-blue-light":e.is_active}]),href:e.route},(0,r.toDisplayString)(e.title),11,Mt)])})),128))],8,Ft)):(0,r.createCommentVNode)("",!0)])})),128))])])])})),128))])]),Bt]})),_:2},1032,["class"]),[[r.vShow,c]])]})),_:2},1024)])):(0,r.createCommentVNode)("",!0)]})),_:2},1024)})),128))])}}};var Vt=n(284),Dt={insert:"head",singleton:!1};O()(Vt.Z,Dt);Vt.Z.locals;const $t=jt,Ut="undefined"!=typeof window;const Wt="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag,Ht=e=>Wt?Symbol(e):e,zt=e=>JSON.stringify(e).replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029").replace(/\u0027/g,"\\u0027"),Gt=e=>"number"==typeof e&&isFinite(e),qt=e=>"[object RegExp]"===cn(e),Jt=e=>un(e)&&0===Object.keys(e).length;function Kt(e,t){"undefined"!=typeof console&&(console.warn("[intlify] "+e),t&&console.warn(t.stack))}const Yt=Object.assign;let Zt;const Xt=()=>Zt||(Zt="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==n.g?n.g:{});function Qt(e){return e.replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}const en=Object.prototype.hasOwnProperty;function tn(e,t){return en.call(e,t)}const nn=Array.isArray,rn=e=>"function"==typeof e,on=e=>"string"==typeof e,ln=e=>"boolean"==typeof e,an=e=>null!==e&&"object"==typeof e,sn=Object.prototype.toString,cn=e=>sn.call(e),un=e=>"[object Object]"===cn(e);const fn=1,pn=2,dn=3,hn=4,mn=5,vn=6,gn=7,yn=8,_n=9,bn=10,wn=11,xn=12,kn=13,Sn=14,En=15;function Cn(e,t,n={}){const{domain:r,messages:o,args:i}=n,l=new SyntaxError(String(e));return l.code=e,t&&(l.location=t),l.domain=r,l}function Tn(e){throw e}function Nn(e,t,n){const r={start:e,end:t};return null!=n&&(r.source=n),r}const Ln=" ",On="\n",An=String.fromCharCode(8232),Pn=String.fromCharCode(8233);function In(e){const t=e;let n=0,r=1,o=1,i=0;const l=e=>"\r"===t[e]&&t[e+1]===On,a=e=>t[e]===Pn,s=e=>t[e]===An,c=e=>l(e)||(e=>t[e]===On)(e)||a(e)||s(e),u=e=>l(e)||a(e)||s(e)?On:t[e];function f(){return i=0,c(n)&&(r++,o=0),l(n)&&n++,n++,o++,t[n]}return{index:()=>n,line:()=>r,column:()=>o,peekOffset:()=>i,charAt:u,currentChar:()=>u(n),currentPeek:()=>u(n+i),next:f,peek:function(){return l(n+i)&&i++,i++,t[n+i]},reset:function(){n=0,r=1,o=1,i=0},resetPeek:function(e=0){i=e},skipToPeek:function(){const e=n+i;for(;e!==n;)f();i=0}}}const Rn=void 0;function Fn(e,t={}){const n=!1!==t.location,r=In(e),o=()=>r.index(),i=()=>{return e=r.line(),t=r.column(),n=r.index(),{line:e,column:t,offset:n};var e,t,n},l=i(),a=o(),s={currentType:14,offset:a,startLoc:l,endLoc:l,lastType:14,lastOffset:a,lastStartLoc:l,lastEndLoc:l,braceNest:0,inLinked:!1,text:""},c=()=>s,{onError:u}=t;function f(e,t,n,...r){const o=c();if(t.column+=n,t.offset+=n,u){const n=Cn(e,Nn(o.startLoc,t),{domain:"tokenizer",args:r});u(n)}}function p(e,t,r){e.endLoc=i(),e.currentType=t;const o={type:t};return n&&(o.loc=Nn(e.startLoc,e.endLoc)),null!=r&&(o.value=r),o}const d=e=>p(e,14);function h(e,t){return e.currentChar()===t?(e.next(),t):(f(fn,i(),0,t),"")}function m(e){let t="";for(;e.currentPeek()===Ln||e.currentPeek()===On;)t+=e.currentPeek(),e.peek();return t}function v(e){const t=m(e);return e.skipToPeek(),t}function g(e){if(e===Rn)return!1;const t=e.charCodeAt(0);return t>=97&&t<=122||t>=65&&t<=90||95===t}function y(e,t){const{currentType:n}=t;if(2!==n)return!1;m(e);const r=function(e){if(e===Rn)return!1;const t=e.charCodeAt(0);return t>=48&&t<=57}("-"===e.currentPeek()?e.peek():e.currentPeek());return e.resetPeek(),r}function _(e){m(e);const t="|"===e.currentPeek();return e.resetPeek(),t}function b(e,t=!0){const n=(t=!1,r="",o=!1)=>{const i=e.currentPeek();return"{"===i?"%"!==r&&t:"@"!==i&&i?"%"===i?(e.peek(),n(t,"%",!0)):"|"===i?!("%"!==r&&!o)||!(r===Ln||r===On):i===Ln?(e.peek(),n(!0,Ln,o)):i!==On||(e.peek(),n(!0,On,o)):"%"===r||t},r=n();return t&&e.resetPeek(),r}function w(e,t){const n=e.currentChar();return n===Rn?Rn:t(n)?(e.next(),n):null}function x(e){return w(e,(e=>{const t=e.charCodeAt(0);return t>=97&&t<=122||t>=65&&t<=90||t>=48&&t<=57||95===t||36===t}))}function k(e){return w(e,(e=>{const t=e.charCodeAt(0);return t>=48&&t<=57}))}function S(e){return w(e,(e=>{const t=e.charCodeAt(0);return t>=48&&t<=57||t>=65&&t<=70||t>=97&&t<=102}))}function E(e){let t="",n="";for(;t=k(e);)n+=t;return n}function C(e){let t="";for(;;){const n=e.currentChar();if("{"===n||"}"===n||"@"===n||"|"===n||!n)break;if("%"===n){if(!b(e))break;t+=n,e.next()}else if(n===Ln||n===On)if(b(e))t+=n,e.next();else{if(_(e))break;t+=n,e.next()}else t+=n,e.next()}return t}function T(e){const t=e.currentChar();switch(t){case"\\":case"'":return e.next(),`\\${t}`;case"u":return N(e,t,4);case"U":return N(e,t,6);default:return f(hn,i(),0,t),""}}function N(e,t,n){h(e,t);let r="";for(let o=0;o=1&&f(_n,i(),0),e.next(),n=p(t,2,"{"),v(e),t.braceNest++,n;case"}":return t.braceNest>0&&2===t.currentType&&f(yn,i(),0),e.next(),n=p(t,3,"}"),t.braceNest--,t.braceNest>0&&v(e),t.inLinked&&0===t.braceNest&&(t.inLinked=!1),n;case"@":return t.braceNest>0&&f(gn,i(),0),n=A(e,t)||d(t),t.braceNest=0,n;default:let r=!0,o=!0,l=!0;if(_(e))return t.braceNest>0&&f(gn,i(),0),n=p(t,1,L(e)),t.braceNest=0,t.inLinked=!1,n;if(t.braceNest>0&&(5===t.currentType||6===t.currentType||7===t.currentType))return f(gn,i(),0),t.braceNest=0,P(e,t);if(r=function(e,t){const{currentType:n}=t;if(2!==n)return!1;m(e);const r=g(e.currentPeek());return e.resetPeek(),r}(e,t))return n=p(t,5,function(e){v(e);let t="",n="";for(;t=x(e);)n+=t;return e.currentChar()===Rn&&f(gn,i(),0),n}(e)),v(e),n;if(o=y(e,t))return n=p(t,6,function(e){v(e);let t="";return"-"===e.currentChar()?(e.next(),t+=`-${E(e)}`):t+=E(e),e.currentChar()===Rn&&f(gn,i(),0),t}(e)),v(e),n;if(l=function(e,t){const{currentType:n}=t;if(2!==n)return!1;m(e);const r="'"===e.currentPeek();return e.resetPeek(),r}(e,t))return n=p(t,7,function(e){v(e),h(e,"'");let t="",n="";const r=e=>"'"!==e&&e!==On;for(;t=w(e,r);)n+="\\"===t?T(e):t;const o=e.currentChar();return o===On||o===Rn?(f(dn,i(),0),o===On&&(e.next(),h(e,"'")),n):(h(e,"'"),n)}(e)),v(e),n;if(!r&&!o&&!l)return n=p(t,13,function(e){v(e);let t="",n="";const r=e=>"{"!==e&&"}"!==e&&e!==Ln&&e!==On;for(;t=w(e,r);)n+=t;return n}(e)),f(pn,i(),0,n.value),v(e),n}return n}function A(e,t){const{currentType:n}=t;let r=null;const o=e.currentChar();switch(8!==n&&9!==n&&12!==n&&10!==n||o!==On&&o!==Ln||f(bn,i(),0),o){case"@":return e.next(),r=p(t,8,"@"),t.inLinked=!0,r;case".":return v(e),e.next(),p(t,9,".");case":":return v(e),e.next(),p(t,10,":");default:return _(e)?(r=p(t,1,L(e)),t.braceNest=0,t.inLinked=!1,r):function(e,t){const{currentType:n}=t;if(8!==n)return!1;m(e);const r="."===e.currentPeek();return e.resetPeek(),r}(e,t)||function(e,t){const{currentType:n}=t;if(8!==n&&12!==n)return!1;m(e);const r=":"===e.currentPeek();return e.resetPeek(),r}(e,t)?(v(e),A(e,t)):function(e,t){const{currentType:n}=t;if(9!==n)return!1;m(e);const r=g(e.currentPeek());return e.resetPeek(),r}(e,t)?(v(e),p(t,12,function(e){let t="",n="";for(;t=x(e);)n+=t;return n}(e))):function(e,t){const{currentType:n}=t;if(10!==n)return!1;const r=()=>{const t=e.currentPeek();return"{"===t?g(e.peek()):!("@"===t||"%"===t||"|"===t||":"===t||"."===t||t===Ln||!t)&&(t===On?(e.peek(),r()):g(t))},o=r();return e.resetPeek(),o}(e,t)?(v(e),"{"===o?O(e,t)||r:p(t,11,function(e){const t=(n=!1,r)=>{const o=e.currentChar();return"{"!==o&&"%"!==o&&"@"!==o&&"|"!==o&&o?o===Ln?r:o===On?(r+=o,e.next(),t(n,r)):(r+=o,e.next(),t(!0,r)):r};return t(!1,"")}(e))):(8===n&&f(bn,i(),0),t.braceNest=0,t.inLinked=!1,P(e,t))}}function P(e,t){let n={type:14};if(t.braceNest>0)return O(e,t)||d(t);if(t.inLinked)return A(e,t)||d(t);switch(e.currentChar()){case"{":return O(e,t)||d(t);case"}":return f(vn,i(),0),e.next(),p(t,3,"}");case"@":return A(e,t)||d(t);default:if(_(e))return n=p(t,1,L(e)),t.braceNest=0,t.inLinked=!1,n;const{isModulo:r,hasSpace:o}=function(e){const t=m(e),n="%"===e.currentPeek()&&"{"===e.peek();return e.resetPeek(),{isModulo:n,hasSpace:t.length>0}}(e);if(r)return o?p(t,0,C(e)):p(t,4,function(e){v(e);const t=e.currentChar();return"%"!==t&&f(fn,i(),0,t),e.next(),"%"}(e));if(b(e))return p(t,0,C(e))}return n}return{nextToken:function(){const{currentType:e,offset:t,startLoc:n,endLoc:l}=s;return s.lastType=e,s.lastOffset=t,s.lastStartLoc=n,s.lastEndLoc=l,s.offset=o(),s.startLoc=i(),r.currentChar()===Rn?p(s,14):P(r,s)},currentOffset:o,currentPosition:i,context:c}}const Mn=/(?:\\\\|\\'|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;function Bn(e,t,n){switch(e){case"\\\\":return"\\";case"\\'":return"'";default:{const e=parseInt(t||n,16);return e<=55295||e>=57344?String.fromCodePoint(e):"�"}}}function jn(e={}){const t=!1!==e.location,{onError:n}=e;function r(e,t,r,o,...i){const l=e.currentPosition();if(l.offset+=o,l.column+=o,n){const e=Cn(t,Nn(r,l),{domain:"parser",args:i});n(e)}}function o(e,n,r){const o={type:e,start:n,end:n};return t&&(o.loc={start:r,end:r}),o}function i(e,n,r,o){e.end=n,o&&(e.type=o),t&&e.loc&&(e.loc.end=r)}function l(e,t){const n=e.context(),r=o(3,n.offset,n.startLoc);return r.value=t,i(r,e.currentOffset(),e.currentPosition()),r}function a(e,t){const n=e.context(),{lastOffset:r,lastStartLoc:l}=n,a=o(5,r,l);return a.index=parseInt(t,10),e.nextToken(),i(a,e.currentOffset(),e.currentPosition()),a}function s(e,t){const n=e.context(),{lastOffset:r,lastStartLoc:l}=n,a=o(4,r,l);return a.key=t,e.nextToken(),i(a,e.currentOffset(),e.currentPosition()),a}function c(e,t){const n=e.context(),{lastOffset:r,lastStartLoc:l}=n,a=o(9,r,l);return a.value=t.replace(Mn,Bn),e.nextToken(),i(a,e.currentOffset(),e.currentPosition()),a}function u(e){const t=e.context(),n=o(6,t.offset,t.startLoc);let l=e.nextToken();if(9===l.type){const t=function(e){const t=e.nextToken(),n=e.context(),{lastOffset:l,lastStartLoc:a}=n,s=o(8,l,a);return 12!==t.type?(r(e,xn,n.lastStartLoc,0),s.value="",i(s,l,a),{nextConsumeToken:t,node:s}):(null==t.value&&r(e,Sn,n.lastStartLoc,0,Vn(t)),s.value=t.value||"",i(s,e.currentOffset(),e.currentPosition()),{node:s})}(e);n.modifier=t.node,l=t.nextConsumeToken||e.nextToken()}switch(10!==l.type&&r(e,Sn,t.lastStartLoc,0,Vn(l)),l=e.nextToken(),2===l.type&&(l=e.nextToken()),l.type){case 11:null==l.value&&r(e,Sn,t.lastStartLoc,0,Vn(l)),n.key=function(e,t){const n=e.context(),r=o(7,n.offset,n.startLoc);return r.value=t,i(r,e.currentOffset(),e.currentPosition()),r}(e,l.value||"");break;case 5:null==l.value&&r(e,Sn,t.lastStartLoc,0,Vn(l)),n.key=s(e,l.value||"");break;case 6:null==l.value&&r(e,Sn,t.lastStartLoc,0,Vn(l)),n.key=a(e,l.value||"");break;case 7:null==l.value&&r(e,Sn,t.lastStartLoc,0,Vn(l)),n.key=c(e,l.value||"");break;default:r(e,kn,t.lastStartLoc,0);const u=e.context(),f=o(7,u.offset,u.startLoc);return f.value="",i(f,u.offset,u.startLoc),n.key=f,i(n,u.offset,u.startLoc),{nextConsumeToken:l,node:n}}return i(n,e.currentOffset(),e.currentPosition()),{node:n}}function f(e){const t=e.context(),n=o(2,1===t.currentType?e.currentOffset():t.offset,1===t.currentType?t.endLoc:t.startLoc);n.items=[];let f=null;do{const o=f||e.nextToken();switch(f=null,o.type){case 0:null==o.value&&r(e,Sn,t.lastStartLoc,0,Vn(o)),n.items.push(l(e,o.value||""));break;case 6:null==o.value&&r(e,Sn,t.lastStartLoc,0,Vn(o)),n.items.push(a(e,o.value||""));break;case 5:null==o.value&&r(e,Sn,t.lastStartLoc,0,Vn(o)),n.items.push(s(e,o.value||""));break;case 7:null==o.value&&r(e,Sn,t.lastStartLoc,0,Vn(o)),n.items.push(c(e,o.value||""));break;case 8:const i=u(e);n.items.push(i.node),f=i.nextConsumeToken||null}}while(14!==t.currentType&&1!==t.currentType);return i(n,1===t.currentType?t.lastOffset:e.currentOffset(),1===t.currentType?t.lastEndLoc:e.currentPosition()),n}function p(e){const t=e.context(),{offset:n,startLoc:l}=t,a=f(e);return 14===t.currentType?a:function(e,t,n,l){const a=e.context();let s=0===l.items.length;const c=o(1,t,n);c.cases=[],c.cases.push(l);do{const t=f(e);s||(s=0===t.items.length),c.cases.push(t)}while(14!==a.currentType);return s&&r(e,wn,n,0),i(c,e.currentOffset(),e.currentPosition()),c}(e,n,l,a)}return{parse:function(n){const l=Fn(n,Yt({},e)),a=l.context(),s=o(0,a.offset,a.startLoc);return t&&s.loc&&(s.loc.source=n),s.body=p(l),14!==a.currentType&&r(l,Sn,a.lastStartLoc,0,n[a.offset]||""),i(s,l.currentOffset(),l.currentPosition()),s}}}function Vn(e){if(14===e.type)return"EOF";const t=(e.value||"").replace(/\r?\n/gu,"\\n");return t.length>10?t.slice(0,9)+"…":t}function Dn(e,t){for(let n=0;nn,helper:e=>(n.helpers.add(e),e)}}(e);n.helper("normalize"),e.body&&$n(e.body,n);const r=n.context();e.helpers=Array.from(r.helpers)}function Wn(e,t){const{helper:n}=e;switch(t.type){case 0:!function(e,t){t.body?Wn(e,t.body):e.push("null")}(e,t);break;case 1:!function(e,t){const{helper:n,needIndent:r}=e;if(t.cases.length>1){e.push(`${n("plural")}([`),e.indent(r());const o=t.cases.length;for(let n=0;n{const n=on(t.mode)?t.mode:"normal",r=on(t.filename)?t.filename:"message.intl",o=!!t.sourceMap,i=null!=t.breakLineCode?t.breakLineCode:"arrow"===n?";":"\n",l=t.needIndent?t.needIndent:"arrow"!==n,a=e.helpers||[],s=function(e,t){const{sourceMap:n,filename:r,breakLineCode:o,needIndent:i}=t,l={source:e.loc.source,filename:r,code:"",column:1,line:1,offset:0,map:void 0,breakLineCode:o,needIndent:i,indentLevel:0};function a(e,t){l.code+=e}function s(e,t=!0){const n=t?o:"";a(i?n+" ".repeat(e):n)}return{context:()=>l,push:a,indent:function(e=!0){const t=++l.indentLevel;e&&s(t)},deindent:function(e=!0){const t=--l.indentLevel;e&&s(t)},newline:function(){s(l.indentLevel)},helper:e=>`_${e}`,needIndent:()=>l.needIndent}}(e,{mode:n,filename:r,sourceMap:o,breakLineCode:i,needIndent:l});s.push("normal"===n?"function __msg__ (ctx) {":"(ctx) => {"),s.indent(l),a.length>0&&(s.push(`const { ${a.map((e=>`${e}: _${e}`)).join(", ")} } = ctx`),s.newline()),s.push("return "),Wn(s,e),s.deindent(l),s.push("}");const{code:c,map:u}=s.context();return{ast:e,code:c,map:u?u.toJSON():void 0}})(r,n)}const zn="i18n:init",Gn="function:translate",qn=[];qn[0]={w:[0],i:[3,0],"[":[4],o:[7]},qn[1]={w:[1],".":[2],"[":[4],o:[7]},qn[2]={w:[2],i:[3,0],0:[3,0]},qn[3]={i:[3,0],0:[3,0],w:[1,1],".":[2,1],"[":[4,1],o:[7,1]},qn[4]={"'":[5,0],'"':[6,0],"[":[4,2],"]":[1,3],o:8,l:[4,0]},qn[5]={"'":[4,0],o:8,l:[5,0]},qn[6]={'"':[4,0],o:8,l:[6,0]};const Jn=/^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;function Kn(e){if(null==e)return"o";switch(e.charCodeAt(0)){case 91:case 93:case 46:case 34:case 39:return e;case 95:case 36:case 45:return"i";case 9:case 10:case 13:case 160:case 65279:case 8232:case 8233:return"w"}return"i"}function Yn(e){const t=e.trim();return("0"!==e.charAt(0)||!isNaN(parseInt(e)))&&(n=t,Jn.test(n)?function(e){const t=e.charCodeAt(0);return t!==e.charCodeAt(e.length-1)||34!==t&&39!==t?e:e.slice(1,-1)}(t):"*"+t);var n}const Zn=new Map;function Xn(e,t){return an(e)?e[t]:null}const Qn=e=>e,er=e=>"",tr=e=>0===e.length?"":e.join(""),nr=e=>null==e?"":nn(e)||un(e)&&e.toString===sn?JSON.stringify(e,null,2):String(e);function rr(e,t){return e=Math.abs(e),2===t?e?e>1?1:0:1:e?Math.min(e,2):0}function or(e={}){const t=e.locale,n=function(e){const t=Gt(e.pluralIndex)?e.pluralIndex:-1;return e.named&&(Gt(e.named.count)||Gt(e.named.n))?Gt(e.named.count)?e.named.count:Gt(e.named.n)?e.named.n:t:t}(e),r=an(e.pluralRules)&&on(t)&&rn(e.pluralRules[t])?e.pluralRules[t]:rr,o=an(e.pluralRules)&&on(t)&&rn(e.pluralRules[t])?rr:void 0,i=e.list||[],l=e.named||{};Gt(e.pluralIndex)&&function(e,t){t.count||(t.count=e),t.n||(t.n=e)}(n,l);function a(t){const n=rn(e.messages)?e.messages(t):!!an(e.messages)&&e.messages[t];return n||(e.parent?e.parent.message(t):er)}const s=un(e.processor)&&rn(e.processor.normalize)?e.processor.normalize:tr,c=un(e.processor)&&rn(e.processor.interpolate)?e.processor.interpolate:nr,u={list:e=>i[e],named:e=>l[e],plural:e=>e[r(n,e.length,o)],linked:(t,...n)=>{const[r,o]=n;let i="text",l="";1===n.length?an(r)?(l=r.modifier||l,i=r.type||i):on(r)&&(l=r||l):2===n.length&&(on(r)&&(l=r||l),on(o)&&(i=o||i));let s=a(t)(u);return"vnode"===i&&nn(s)&&l&&(s=s[0]),l?(c=l,e.modifiers?e.modifiers[c]:Qn)(s,i):s;var c},message:a,type:un(e.processor)&&on(e.processor.type)?e.processor.type:"text",interpolate:c,normalize:s};return u}let ir=null;const lr=ar(Gn);function ar(e){return t=>ir&&ir.emit(e,t)}const sr=7;function cr(e,t,n){return[...new Set([n,...nn(t)?t:an(t)?Object.keys(t):on(t)?[t]:[n]])]}function ur(e,t,n){const r=on(n)?n:hr,o=e;o.__localeChainCache||(o.__localeChainCache=new Map);let i=o.__localeChainCache.get(r);if(!i){i=[];let e=[n];for(;nn(e);)e=fr(i,e,t);const l=nn(t)||!un(t)?t:t.default?t.default:null;e=on(l)?[l]:l,nn(e)&&fr(i,e,!1),o.__localeChainCache.set(r,i)}return i}function fr(e,t,n){let r=!0;for(let o=0;o`${e.charAt(0).toLocaleUpperCase()}${e.substr(1)}`;let vr,gr,yr;let _r=null;const br=e=>{_r=e};let wr=null;const xr=e=>{wr=e};let kr=0;function Sr(e={}){const t=on(e.version)?e.version:"9.2.2",n=on(e.locale)?e.locale:hr,r=nn(e.fallbackLocale)||un(e.fallbackLocale)||on(e.fallbackLocale)||!1===e.fallbackLocale?e.fallbackLocale:n,o=un(e.messages)?e.messages:{[n]:{}},i=un(e.datetimeFormats)?e.datetimeFormats:{[n]:{}},l=un(e.numberFormats)?e.numberFormats:{[n]:{}},a=Yt({},e.modifiers||{},{upper:(e,t)=>"text"===t&&on(e)?e.toUpperCase():"vnode"===t&&an(e)&&"__v_isVNode"in e?e.children.toUpperCase():e,lower:(e,t)=>"text"===t&&on(e)?e.toLowerCase():"vnode"===t&&an(e)&&"__v_isVNode"in e?e.children.toLowerCase():e,capitalize:(e,t)=>"text"===t&&on(e)?mr(e):"vnode"===t&&an(e)&&"__v_isVNode"in e?mr(e.children):e}),s=e.pluralRules||{},c=rn(e.missing)?e.missing:null,u=!ln(e.missingWarn)&&!qt(e.missingWarn)||e.missingWarn,f=!ln(e.fallbackWarn)&&!qt(e.fallbackWarn)||e.fallbackWarn,p=!!e.fallbackFormat,d=!!e.unresolving,h=rn(e.postTranslation)?e.postTranslation:null,m=un(e.processor)?e.processor:null,v=!ln(e.warnHtmlMessage)||e.warnHtmlMessage,g=!!e.escapeParameter,y=rn(e.messageCompiler)?e.messageCompiler:vr,_=rn(e.messageResolver)?e.messageResolver:gr||Xn,b=rn(e.localeFallbacker)?e.localeFallbacker:yr||cr,w=an(e.fallbackContext)?e.fallbackContext:void 0,x=rn(e.onWarn)?e.onWarn:Kt,k=e,S=an(k.__datetimeFormatters)?k.__datetimeFormatters:new Map,E=an(k.__numberFormatters)?k.__numberFormatters:new Map,C=an(k.__meta)?k.__meta:{};kr++;const T={version:t,cid:kr,locale:n,fallbackLocale:r,messages:o,modifiers:a,pluralRules:s,missing:c,missingWarn:u,fallbackWarn:f,fallbackFormat:p,unresolving:d,postTranslation:h,processor:m,warnHtmlMessage:v,escapeParameter:g,messageCompiler:y,messageResolver:_,localeFallbacker:b,fallbackContext:w,onWarn:x,__meta:C};return T.datetimeFormats=i,T.numberFormats=l,T.__datetimeFormatters=S,T.__numberFormatters=E,__INTLIFY_PROD_DEVTOOLS__&&function(e,t,n){ir&&ir.emit(zn,{timestamp:Date.now(),i18n:e,version:t,meta:n})}(T,t,C),T}function Er(e,t,n,r,o){const{missing:i,onWarn:l}=e;if(null!==i){const r=i(e,n,t,o);return on(r)?r:t}return t}function Cr(e,t,n){e.__localeChainCache=new Map,e.localeFallbacker(e,n,t)}const Tr=e=>e;let Nr=Object.create(null);let Lr=En;const Or=()=>++Lr,Ar={INVALID_ARGUMENT:Lr,INVALID_DATE_ARGUMENT:Or(),INVALID_ISO_DATE_ARGUMENT:Or(),__EXTEND_POINT__:Or()};function Pr(e){return Cn(e,null,void 0)}const Ir=()=>"",Rr=e=>rn(e);function Fr(e,...t){const{fallbackFormat:n,postTranslation:r,unresolving:o,messageCompiler:i,fallbackLocale:l,messages:a}=e,[s,c]=jr(...t),u=ln(c.missingWarn)?c.missingWarn:e.missingWarn,f=ln(c.fallbackWarn)?c.fallbackWarn:e.fallbackWarn,p=ln(c.escapeParameter)?c.escapeParameter:e.escapeParameter,d=!!c.resolvedMessage,h=on(c.default)||ln(c.default)?ln(c.default)?i?s:()=>s:c.default:n?i?s:()=>s:"",m=n||""!==h,v=on(c.locale)?c.locale:e.locale;p&&function(e){nn(e.list)?e.list=e.list.map((e=>on(e)?Qt(e):e)):an(e.named)&&Object.keys(e.named).forEach((t=>{on(e.named[t])&&(e.named[t]=Qt(e.named[t]))}))}(c);let[g,y,_]=d?[s,v,a[v]||{}]:Mr(e,s,v,l,f,u),b=g,w=s;if(d||on(b)||Rr(b)||m&&(b=h,w=b),!(d||(on(b)||Rr(b))&&on(y)))return o?-1:s;let x=!1;const k=Rr(b)?b:Br(e,s,y,b,w,(()=>{x=!0}));if(x)return b;const S=function(e,t,n,r){const{modifiers:o,pluralRules:i,messageResolver:l,fallbackLocale:a,fallbackWarn:s,missingWarn:c,fallbackContext:u}=e,f=r=>{let o=l(n,r);if(null==o&&u){const[,,e]=Mr(u,r,t,a,s,c);o=l(e,r)}if(on(o)){let n=!1;const i=Br(e,r,t,o,r,(()=>{n=!0}));return n?Ir:i}return Rr(o)?o:Ir},p={locale:t,modifiers:o,pluralRules:i,messages:f};e.processor&&(p.processor=e.processor);r.list&&(p.list=r.list);r.named&&(p.named=r.named);Gt(r.plural)&&(p.pluralIndex=r.plural);return p}(e,y,_,c),E=function(e,t,n){0;0;return t(n)}(0,k,or(S)),C=r?r(E,s):E;if(__INTLIFY_PROD_DEVTOOLS__){const t={timestamp:Date.now(),key:on(s)?s:Rr(b)?b.key:"",locale:y||(Rr(b)?b.locale:""),format:on(b)?b:Rr(b)?b.source:"",message:C};t.meta=Yt({},e.__meta,_r||{}),lr(t)}return C}function Mr(e,t,n,r,o,i){const{messages:l,onWarn:a,messageResolver:s,localeFallbacker:c}=e,u=c(e,r,n);let f,p={},d=null,h=n,m=null;for(let n=0;nr;return e.locale=n,e.key=t,e}const s=l(r,function(e,t,n,r,o,i){return{warnHtmlMessage:o,onError:e=>{throw i&&i(e),e},onCacheKey:e=>((e,t,n)=>zt({l:e,k:t,s:n}))(t,n,e)}}(0,n,o,0,a,i));return s.locale=n,s.key=t,s.source=r,s}function jr(...e){const[t,n,r]=e,o={};if(!on(t)&&!Gt(t)&&!Rr(t))throw Pr(Ar.INVALID_ARGUMENT);const i=Gt(t)?String(t):(Rr(t),t);return Gt(n)?o.plural=n:on(n)?o.default=n:un(n)&&!Jt(n)?o.named=n:nn(n)&&(o.list=n),Gt(r)?o.plural=r:on(r)?o.default=r:un(r)&&Yt(o,r),[i,o]}const Vr="undefined"!=typeof Intl;Vr&&Intl.DateTimeFormat,Vr&&Intl.NumberFormat;function Dr(e,...t){const{datetimeFormats:n,unresolving:r,fallbackLocale:o,onWarn:i,localeFallbacker:l}=e,{__datetimeFormatters:a}=e;const[s,c,u,f]=Ur(...t),p=(ln(u.missingWarn)?u.missingWarn:e.missingWarn,ln(u.fallbackWarn)?u.fallbackWarn:e.fallbackWarn,!!u.part),d=on(u.locale)?u.locale:e.locale,h=l(e,o,d);if(!on(s)||""===s)return new Intl.DateTimeFormat(d,f).format(c);let m,v={},g=null,y=d,_=null;for(let t=0;t{$r.includes(e)?a[e]=n[e]:i[e]=n[e]})),on(r)?i.locale=r:un(r)&&(a=r),un(o)&&(a=o),[i.key||"",l,i,a]}function Wr(e,t,n){const r=e;for(const e in n){const n=`${t}__${e}`;r.__datetimeFormatters.has(n)&&r.__datetimeFormatters.delete(n)}}function Hr(e,...t){const{numberFormats:n,unresolving:r,fallbackLocale:o,onWarn:i,localeFallbacker:l}=e,{__numberFormatters:a}=e;const[s,c,u,f]=Gr(...t),p=(ln(u.missingWarn)?u.missingWarn:e.missingWarn,ln(u.fallbackWarn)?u.fallbackWarn:e.fallbackWarn,!!u.part),d=on(u.locale)?u.locale:e.locale,h=l(e,o,d);if(!on(s)||""===s)return new Intl.NumberFormat(d,f).format(c);let m,v={},g=null,y=d,_=null;for(let t=0;t{zr.includes(e)?l[e]=n[e]:i[e]=n[e]})),on(r)?i.locale=r:un(r)&&(l=r),un(o)&&(l=o),[i.key||"",a,i,l]}function qr(e,t,n){const r=e;for(const e in n){const n=`${t}__${e}`;r.__numberFormatters.has(n)&&r.__numberFormatters.delete(n)}}"boolean"!=typeof __INTLIFY_PROD_DEVTOOLS__&&(Xt().__INTLIFY_PROD_DEVTOOLS__=!1);let Jr=sr;const Kr=()=>++Jr;Kr(),Kr(),Kr(),Kr(),Kr(),Kr();let Yr=En;const Zr=()=>++Yr,Xr={UNEXPECTED_RETURN_TYPE:Yr,INVALID_ARGUMENT:Zr(),MUST_BE_CALL_SETUP_TOP:Zr(),NOT_INSLALLED:Zr(),NOT_AVAILABLE_IN_LEGACY_MODE:Zr(),REQUIRED_VALUE:Zr(),INVALID_VALUE:Zr(),CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN:Zr(),NOT_INSLALLED_WITH_PROVIDE:Zr(),UNEXPECTED_ERROR:Zr(),NOT_COMPATIBLE_LEGACY_VUE_I18N:Zr(),BRIDGE_SUPPORT_VUE_2_ONLY:Zr(),MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION:Zr(),NOT_AVAILABLE_COMPOSITION_IN_LEGACY:Zr(),__EXTEND_POINT__:Zr()};function Qr(e,...t){return Cn(e,null,void 0)}const eo=Ht("__transrateVNode"),to=Ht("__datetimeParts"),no=Ht("__numberParts"),ro=Ht("__setPluralRules");Ht("__intlifyMeta");const oo=Ht("__injectWithOption");function io(e){if(!an(e))return e;for(const t in e)if(tn(e,t))if(t.includes(".")){const n=t.split("."),r=n.length-1;let o=e;for(let e=0;e{if("locale"in e&&"resource"in e){const{locale:t,resource:n}=e;t?(l[t]=l[t]||{},so(n,l[t])):so(n,l)}else on(e)&&so(JSON.parse(e),l)})),null==o&&i)for(const e in l)tn(l,e)&&io(l[e]);return l}const ao=e=>!an(e)||nn(e);function so(e,t){if(ao(e)||ao(t))throw Qr(Xr.INVALID_VALUE);for(const n in e)tn(e,n)&&(ao(e[n])||ao(t[n])?t[n]=e[n]:so(e[n],t[n]))}function co(e){return e.type}function uo(e,t,n){let r=an(t.messages)?t.messages:{};"__i18nGlobal"in n&&(r=lo(e.locale.value,{messages:r,__i18n:n.__i18nGlobal}));const o=Object.keys(r);if(o.length&&o.forEach((t=>{e.mergeLocaleMessage(t,r[t])})),an(t.datetimeFormats)){const n=Object.keys(t.datetimeFormats);n.length&&n.forEach((n=>{e.mergeDateTimeFormat(n,t.datetimeFormats[n])}))}if(an(t.numberFormats)){const n=Object.keys(t.numberFormats);n.length&&n.forEach((n=>{e.mergeNumberFormat(n,t.numberFormats[n])}))}}function fo(e){return(0,r.createVNode)(r.Text,null,e,0)}const po="__INTLIFY_META__";let ho=0;function mo(e){return(t,n,o,i)=>e(n,o,(0,r.getCurrentInstance)()||void 0,i)}function vo(e={},t){const{__root:n}=e,o=void 0===n;let i=!ln(e.inheritLocale)||e.inheritLocale;const l=(0,r.ref)(n&&i?n.locale.value:on(e.locale)?e.locale:hr),a=(0,r.ref)(n&&i?n.fallbackLocale.value:on(e.fallbackLocale)||nn(e.fallbackLocale)||un(e.fallbackLocale)||!1===e.fallbackLocale?e.fallbackLocale:l.value),s=(0,r.ref)(lo(l.value,e)),c=(0,r.ref)(un(e.datetimeFormats)?e.datetimeFormats:{[l.value]:{}}),u=(0,r.ref)(un(e.numberFormats)?e.numberFormats:{[l.value]:{}});let f=n?n.missingWarn:!ln(e.missingWarn)&&!qt(e.missingWarn)||e.missingWarn,p=n?n.fallbackWarn:!ln(e.fallbackWarn)&&!qt(e.fallbackWarn)||e.fallbackWarn,d=n?n.fallbackRoot:!ln(e.fallbackRoot)||e.fallbackRoot,h=!!e.fallbackFormat,m=rn(e.missing)?e.missing:null,v=rn(e.missing)?mo(e.missing):null,g=rn(e.postTranslation)?e.postTranslation:null,y=n?n.warnHtmlMessage:!ln(e.warnHtmlMessage)||e.warnHtmlMessage,_=!!e.escapeParameter;const b=n?n.modifiers:un(e.modifiers)?e.modifiers:{};let w,x=e.pluralRules||n&&n.pluralRules;w=(()=>{o&&xr(null);const t={version:"9.2.2",locale:l.value,fallbackLocale:a.value,messages:s.value,modifiers:b,pluralRules:x,missing:null===v?void 0:v,missingWarn:f,fallbackWarn:p,fallbackFormat:h,unresolving:!0,postTranslation:null===g?void 0:g,warnHtmlMessage:y,escapeParameter:_,messageResolver:e.messageResolver,__meta:{framework:"vue"}};t.datetimeFormats=c.value,t.numberFormats=u.value,t.__datetimeFormatters=un(w)?w.__datetimeFormatters:void 0,t.__numberFormatters=un(w)?w.__numberFormatters:void 0;const n=Sr(t);return o&&xr(n),n})(),Cr(w,l.value,a.value);const k=(0,r.computed)({get:()=>l.value,set:e=>{l.value=e,w.locale=l.value}}),S=(0,r.computed)({get:()=>a.value,set:e=>{a.value=e,w.fallbackLocale=a.value,Cr(w,l.value,e)}}),E=(0,r.computed)((()=>s.value)),C=(0,r.computed)((()=>c.value)),T=(0,r.computed)((()=>u.value));const N=(e,t,i,f,p,h)=>{let m;if(l.value,a.value,s.value,c.value,u.value,__INTLIFY_PROD_DEVTOOLS__)try{br((()=>{const e=(0,r.getCurrentInstance)();let t=null;return e&&(t=co(e)[po])?{[po]:t}:null})()),o||(w.fallbackContext=n?wr:void 0),m=e(w)}finally{br(null),o||(w.fallbackContext=void 0)}else m=e(w);if(Gt(m)&&-1===m){const[e,r]=t();return n&&d?f(n):p(e)}if(h(m))return m;throw Qr(Xr.UNEXPECTED_RETURN_TYPE)};function L(...e){return N((t=>Reflect.apply(Fr,null,[t,...e])),(()=>jr(...e)),0,(t=>Reflect.apply(t.t,t,[...e])),(e=>e),(e=>on(e)))}const O={normalize:function(e){return e.map((e=>on(e)||Gt(e)||ln(e)?fo(String(e)):e))},interpolate:e=>e,type:"vnode"};function A(e){return s.value[e]||{}}ho++,n&&Ut&&((0,r.watch)(n.locale,(e=>{i&&(l.value=e,w.locale=e,Cr(w,l.value,a.value))})),(0,r.watch)(n.fallbackLocale,(e=>{i&&(a.value=e,w.fallbackLocale=e,Cr(w,l.value,a.value))})));const P={id:ho,locale:k,fallbackLocale:S,get inheritLocale(){return i},set inheritLocale(e){i=e,e&&n&&(l.value=n.locale.value,a.value=n.fallbackLocale.value,Cr(w,l.value,a.value))},get availableLocales(){return Object.keys(s.value).sort()},messages:E,get modifiers(){return b},get pluralRules(){return x||{}},get isGlobal(){return o},get missingWarn(){return f},set missingWarn(e){f=e,w.missingWarn=f},get fallbackWarn(){return p},set fallbackWarn(e){p=e,w.fallbackWarn=p},get fallbackRoot(){return d},set fallbackRoot(e){d=e},get fallbackFormat(){return h},set fallbackFormat(e){h=e,w.fallbackFormat=h},get warnHtmlMessage(){return y},set warnHtmlMessage(e){y=e,w.warnHtmlMessage=e},get escapeParameter(){return _},set escapeParameter(e){_=e,w.escapeParameter=e},t:L,getLocaleMessage:A,setLocaleMessage:function(e,t){s.value[e]=t,w.messages=s.value},mergeLocaleMessage:function(e,t){s.value[e]=s.value[e]||{},so(t,s.value[e]),w.messages=s.value},getPostTranslationHandler:function(){return rn(g)?g:null},setPostTranslationHandler:function(e){g=e,w.postTranslation=e},getMissingHandler:function(){return m},setMissingHandler:function(e){null!==e&&(v=mo(e)),m=e,w.missing=v},[ro]:function(e){x=e,w.pluralRules=x}};return P.datetimeFormats=C,P.numberFormats=T,P.rt=function(...e){const[t,n,r]=e;if(r&&!an(r))throw Qr(Xr.INVALID_ARGUMENT);return L(t,n,Yt({resolvedMessage:!0},r||{}))},P.te=function(e,t){const n=A(on(t)?t:l.value);return null!==w.messageResolver(n,e)},P.tm=function(e){const t=function(e){let t=null;const n=ur(w,a.value,l.value);for(let r=0;rReflect.apply(Dr,null,[t,...e])),(()=>Ur(...e)),0,(t=>Reflect.apply(t.d,t,[...e])),(()=>""),(e=>on(e)))},P.n=function(...e){return N((t=>Reflect.apply(Hr,null,[t,...e])),(()=>Gr(...e)),0,(t=>Reflect.apply(t.n,t,[...e])),(()=>""),(e=>on(e)))},P.getDateTimeFormat=function(e){return c.value[e]||{}},P.setDateTimeFormat=function(e,t){c.value[e]=t,w.datetimeFormats=c.value,Wr(w,e,t)},P.mergeDateTimeFormat=function(e,t){c.value[e]=Yt(c.value[e]||{},t),w.datetimeFormats=c.value,Wr(w,e,t)},P.getNumberFormat=function(e){return u.value[e]||{}},P.setNumberFormat=function(e,t){u.value[e]=t,w.numberFormats=u.value,qr(w,e,t)},P.mergeNumberFormat=function(e,t){u.value[e]=Yt(u.value[e]||{},t),w.numberFormats=u.value,qr(w,e,t)},P[oo]=e.__injectWithOption,P[eo]=function(...e){return N((t=>{let n;const r=t;try{r.processor=O,n=Reflect.apply(Fr,null,[r,...e])}finally{r.processor=null}return n}),(()=>jr(...e)),0,(t=>t[eo](...e)),(e=>[fo(e)]),(e=>nn(e)))},P[to]=function(...e){return N((t=>Reflect.apply(Dr,null,[t,...e])),(()=>Ur(...e)),0,(t=>t[to](...e)),(()=>[]),(e=>on(e)||nn(e)))},P[no]=function(...e){return N((t=>Reflect.apply(Hr,null,[t,...e])),(()=>Gr(...e)),0,(t=>t[no](...e)),(()=>[]),(e=>on(e)||nn(e)))},P}function go(e={},t){{const t=vo(function(e){const t=on(e.locale)?e.locale:hr,n=on(e.fallbackLocale)||nn(e.fallbackLocale)||un(e.fallbackLocale)||!1===e.fallbackLocale?e.fallbackLocale:t,r=rn(e.missing)?e.missing:void 0,o=!ln(e.silentTranslationWarn)&&!qt(e.silentTranslationWarn)||!e.silentTranslationWarn,i=!ln(e.silentFallbackWarn)&&!qt(e.silentFallbackWarn)||!e.silentFallbackWarn,l=!ln(e.fallbackRoot)||e.fallbackRoot,a=!!e.formatFallbackMessages,s=un(e.modifiers)?e.modifiers:{},c=e.pluralizationRules,u=rn(e.postTranslation)?e.postTranslation:void 0,f=!on(e.warnHtmlInMessage)||"off"!==e.warnHtmlInMessage,p=!!e.escapeParameterHtml,d=!ln(e.sync)||e.sync;let h=e.messages;if(un(e.sharedMessages)){const t=e.sharedMessages;h=Object.keys(t).reduce(((e,n)=>{const r=e[n]||(e[n]={});return Yt(r,t[n]),e}),h||{})}const{__i18n:m,__root:v,__injectWithOption:g}=e,y=e.datetimeFormats,_=e.numberFormats;return{locale:t,fallbackLocale:n,messages:h,flatJson:e.flatJson,datetimeFormats:y,numberFormats:_,missing:r,missingWarn:o,fallbackWarn:i,fallbackRoot:l,fallbackFormat:a,modifiers:s,pluralRules:c,postTranslation:u,warnHtmlMessage:f,escapeParameter:p,messageResolver:e.messageResolver,inheritLocale:d,__i18n:m,__root:v,__injectWithOption:g}}(e)),n={id:t.id,get locale(){return t.locale.value},set locale(e){t.locale.value=e},get fallbackLocale(){return t.fallbackLocale.value},set fallbackLocale(e){t.fallbackLocale.value=e},get messages(){return t.messages.value},get datetimeFormats(){return t.datetimeFormats.value},get numberFormats(){return t.numberFormats.value},get availableLocales(){return t.availableLocales},get formatter(){return{interpolate:()=>[]}},set formatter(e){},get missing(){return t.getMissingHandler()},set missing(e){t.setMissingHandler(e)},get silentTranslationWarn(){return ln(t.missingWarn)?!t.missingWarn:t.missingWarn},set silentTranslationWarn(e){t.missingWarn=ln(e)?!e:e},get silentFallbackWarn(){return ln(t.fallbackWarn)?!t.fallbackWarn:t.fallbackWarn},set silentFallbackWarn(e){t.fallbackWarn=ln(e)?!e:e},get modifiers(){return t.modifiers},get formatFallbackMessages(){return t.fallbackFormat},set formatFallbackMessages(e){t.fallbackFormat=e},get postTranslation(){return t.getPostTranslationHandler()},set postTranslation(e){t.setPostTranslationHandler(e)},get sync(){return t.inheritLocale},set sync(e){t.inheritLocale=e},get warnHtmlInMessage(){return t.warnHtmlMessage?"warn":"off"},set warnHtmlInMessage(e){t.warnHtmlMessage="off"!==e},get escapeParameterHtml(){return t.escapeParameter},set escapeParameterHtml(e){t.escapeParameter=e},get preserveDirectiveContent(){return!0},set preserveDirectiveContent(e){},get pluralizationRules(){return t.pluralRules||{}},__composer:t,t(...e){const[n,r,o]=e,i={};let l=null,a=null;if(!on(n))throw Qr(Xr.INVALID_ARGUMENT);const s=n;return on(r)?i.locale=r:nn(r)?l=r:un(r)&&(a=r),nn(o)?l=o:un(o)&&(a=o),Reflect.apply(t.t,t,[s,l||a||{},i])},rt:(...e)=>Reflect.apply(t.rt,t,[...e]),tc(...e){const[n,r,o]=e,i={plural:1};let l=null,a=null;if(!on(n))throw Qr(Xr.INVALID_ARGUMENT);const s=n;return on(r)?i.locale=r:Gt(r)?i.plural=r:nn(r)?l=r:un(r)&&(a=r),on(o)?i.locale=o:nn(o)?l=o:un(o)&&(a=o),Reflect.apply(t.t,t,[s,l||a||{},i])},te:(e,n)=>t.te(e,n),tm:e=>t.tm(e),getLocaleMessage:e=>t.getLocaleMessage(e),setLocaleMessage(e,n){t.setLocaleMessage(e,n)},mergeLocaleMessage(e,n){t.mergeLocaleMessage(e,n)},d:(...e)=>Reflect.apply(t.d,t,[...e]),getDateTimeFormat:e=>t.getDateTimeFormat(e),setDateTimeFormat(e,n){t.setDateTimeFormat(e,n)},mergeDateTimeFormat(e,n){t.mergeDateTimeFormat(e,n)},n:(...e)=>Reflect.apply(t.n,t,[...e]),getNumberFormat:e=>t.getNumberFormat(e),setNumberFormat(e,n){t.setNumberFormat(e,n)},mergeNumberFormat(e,n){t.mergeNumberFormat(e,n)},getChoiceIndex:(e,t)=>-1,__onComponentInstanceCreated(t){const{componentInstanceCreatedListener:r}=e;r&&r(t,n)}};return n}}const yo={tag:{type:[String,Object]},locale:{type:String},scope:{type:String,validator:e=>"parent"===e||"global"===e,default:"parent"},i18n:{type:Object}};function _o(e){return r.Fragment}const bo={name:"i18n-t",props:Yt({keypath:{type:String,required:!0},plural:{type:[Number,String],validator:e=>Gt(e)||!isNaN(e)}},yo),setup(e,t){const{slots:n,attrs:o}=t,i=e.i18n||Lo({useScope:e.scope,__useComponent:!0});return()=>{const l=Object.keys(n).filter((e=>"_"!==e)),a={};e.locale&&(a.locale=e.locale),void 0!==e.plural&&(a.plural=on(e.plural)?+e.plural:e.plural);const s=function({slots:e},t){if(1===t.length&&"default"===t[0])return(e.default?e.default():[]).reduce(((e,t)=>[...e,...nn(t.children)?t.children:[t]]),[]);return t.reduce(((t,n)=>{const r=e[n];return r&&(t[n]=r()),t}),{})}(t,l),c=i[eo](e.keypath,s,a),u=Yt({},o),f=on(e.tag)||an(e.tag)?e.tag:_o();return(0,r.h)(f,u,c)}}};function wo(e,t,n,o){const{slots:i,attrs:l}=t;return()=>{const t={part:!0};let a={};e.locale&&(t.locale=e.locale),on(e.format)?t.key=e.format:an(e.format)&&(on(e.format.key)&&(t.key=e.format.key),a=Object.keys(e.format).reduce(((t,r)=>n.includes(r)?Yt({},t,{[r]:e.format[r]}):t),{}));const s=o(e.value,t,a);let c=[t.key];nn(s)?c=s.map(((e,t)=>{const n=i[e.type],r=n?n({[e.type]:e.value,index:t,parts:s}):[e.value];var o;return nn(o=r)&&!on(o[0])&&(r[0].key=`${e.type}-${t}`),r})):on(s)&&(c=[s]);const u=Yt({},l),f=on(e.tag)||an(e.tag)?e.tag:_o();return(0,r.h)(f,u,c)}}const xo={name:"i18n-n",props:Yt({value:{type:Number,required:!0},format:{type:[String,Object]}},yo),setup(e,t){const n=e.i18n||Lo({useScope:"parent",__useComponent:!0});return wo(e,t,zr,((...e)=>n[no](...e)))}},ko={name:"i18n-d",props:Yt({value:{type:[Number,Date],required:!0},format:{type:[String,Object]}},yo),setup(e,t){const n=e.i18n||Lo({useScope:"parent",__useComponent:!0});return wo(e,t,$r,((...e)=>n[to](...e)))}};function So(e){if(on(e))return{path:e};if(un(e)){if(!("path"in e))throw Qr(Xr.REQUIRED_VALUE);return e}throw Qr(Xr.INVALID_VALUE)}function Eo(e){const{path:t,locale:n,args:r,choice:o,plural:i}=e,l={},a=r||{};return on(n)&&(l.locale=n),Gt(o)&&(l.plural=o),Gt(i)&&(l.plural=i),[t,a,l]}function Co(e,t,...n){const o=un(n[0])?n[0]:{},i=!!o.useI18nComponentName;(!ln(o.globalInstall)||o.globalInstall)&&(e.component(i?"i18n":bo.name,bo),e.component(xo.name,xo),e.component(ko.name,ko)),e.directive("t",function(e){const t=t=>{const{instance:n,modifiers:r,value:o}=t;if(!n||!n.$)throw Qr(Xr.UNEXPECTED_ERROR);const i=function(e,t){const n=e;if("composition"===e.mode)return n.__getInstance(t)||e.global;{const r=n.__getInstance(t);return null!=r?r.__composer:e.global.__composer}}(e,n.$),l=So(o);return[Reflect.apply(i.t,i,[...Eo(l)]),i]};return{created:(n,o)=>{const[i,l]=t(o);Ut&&e.global===l&&(n.__i18nWatcher=(0,r.watch)(l.locale,(()=>{o.instance&&o.instance.$forceUpdate()}))),n.__composer=l,n.textContent=i},unmounted:e=>{Ut&&e.__i18nWatcher&&(e.__i18nWatcher(),e.__i18nWatcher=void 0,delete e.__i18nWatcher),e.__composer&&(e.__composer=void 0,delete e.__composer)},beforeUpdate:(e,{value:t})=>{if(e.__composer){const n=e.__composer,r=So(t);e.textContent=Reflect.apply(n.t,n,[...Eo(r)])}},getSSRProps:e=>{const[n]=t(e);return{textContent:n}}}}(t))}function To(e,t){e.locale=t.locale||e.locale,e.fallbackLocale=t.fallbackLocale||e.fallbackLocale,e.missing=t.missing||e.missing,e.silentTranslationWarn=t.silentTranslationWarn||e.silentFallbackWarn,e.silentFallbackWarn=t.silentFallbackWarn||e.silentFallbackWarn,e.formatFallbackMessages=t.formatFallbackMessages||e.formatFallbackMessages,e.postTranslation=t.postTranslation||e.postTranslation,e.warnHtmlInMessage=t.warnHtmlInMessage||e.warnHtmlInMessage,e.escapeParameterHtml=t.escapeParameterHtml||e.escapeParameterHtml,e.sync=t.sync||e.sync,e.__composer[ro](t.pluralizationRules||e.pluralizationRules);const n=lo(e.locale,{messages:t.messages,__i18n:t.__i18n});return Object.keys(n).forEach((t=>e.mergeLocaleMessage(t,n[t]))),t.datetimeFormats&&Object.keys(t.datetimeFormats).forEach((n=>e.mergeDateTimeFormat(n,t.datetimeFormats[n]))),t.numberFormats&&Object.keys(t.numberFormats).forEach((n=>e.mergeNumberFormat(n,t.numberFormats[n]))),e}const No=Ht("global-vue-i18n");function Lo(e={}){const t=(0,r.getCurrentInstance)();if(null==t)throw Qr(Xr.MUST_BE_CALL_SETUP_TOP);if(!t.isCE&&null!=t.appContext.app&&!t.appContext.app.__VUE_I18N_SYMBOL__)throw Qr(Xr.NOT_INSLALLED);const n=function(e){{const t=(0,r.inject)(e.isCE?No:e.appContext.app.__VUE_I18N_SYMBOL__);if(!t)throw Qr(e.isCE?Xr.NOT_INSLALLED_WITH_PROVIDE:Xr.UNEXPECTED_ERROR);return t}}(t),o=function(e){return"composition"===e.mode?e.global:e.global.__composer}(n),i=co(t),l=function(e,t){return Jt(e)?"__i18n"in t?"local":"global":e.useScope?e.useScope:"local"}(e,i);if(__VUE_I18N_LEGACY_API__&&"legacy"===n.mode&&!e.__useComponent){if(!n.allowComposition)throw Qr(Xr.NOT_AVAILABLE_IN_LEGACY_MODE);return function(e,t,n,o={}){const i="local"===t,l=(0,r.shallowRef)(null);if(i&&e.proxy&&!e.proxy.$options.i18n&&!e.proxy.$options.__i18n)throw Qr(Xr.MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION);const a=!ln(o.inheritLocale)||o.inheritLocale,s=(0,r.ref)(i&&a?n.locale.value:on(o.locale)?o.locale:hr),c=(0,r.ref)(i&&a?n.fallbackLocale.value:on(o.fallbackLocale)||nn(o.fallbackLocale)||un(o.fallbackLocale)||!1===o.fallbackLocale?o.fallbackLocale:s.value),u=(0,r.ref)(lo(s.value,o)),f=(0,r.ref)(un(o.datetimeFormats)?o.datetimeFormats:{[s.value]:{}}),p=(0,r.ref)(un(o.numberFormats)?o.numberFormats:{[s.value]:{}}),d=i?n.missingWarn:!ln(o.missingWarn)&&!qt(o.missingWarn)||o.missingWarn,h=i?n.fallbackWarn:!ln(o.fallbackWarn)&&!qt(o.fallbackWarn)||o.fallbackWarn,m=i?n.fallbackRoot:!ln(o.fallbackRoot)||o.fallbackRoot,v=!!o.fallbackFormat,g=rn(o.missing)?o.missing:null,y=rn(o.postTranslation)?o.postTranslation:null,_=i?n.warnHtmlMessage:!ln(o.warnHtmlMessage)||o.warnHtmlMessage,b=!!o.escapeParameter,w=i?n.modifiers:un(o.modifiers)?o.modifiers:{},x=o.pluralRules||i&&n.pluralRules;function k(){return[s.value,c.value,u.value,f.value,p.value]}const S=(0,r.computed)({get:()=>l.value?l.value.locale.value:s.value,set:e=>{l.value&&(l.value.locale.value=e),s.value=e}}),E=(0,r.computed)({get:()=>l.value?l.value.fallbackLocale.value:c.value,set:e=>{l.value&&(l.value.fallbackLocale.value=e),c.value=e}}),C=(0,r.computed)((()=>l.value?l.value.messages.value:u.value)),T=(0,r.computed)((()=>f.value)),N=(0,r.computed)((()=>p.value));function L(){return l.value?l.value.getPostTranslationHandler():y}function O(e){l.value&&l.value.setPostTranslationHandler(e)}function A(){return l.value?l.value.getMissingHandler():g}function P(e){l.value&&l.value.setMissingHandler(e)}function I(e){return k(),e()}function R(...e){return l.value?I((()=>Reflect.apply(l.value.t,null,[...e]))):I((()=>""))}function F(...e){return l.value?Reflect.apply(l.value.rt,null,[...e]):""}function M(...e){return l.value?I((()=>Reflect.apply(l.value.d,null,[...e]))):I((()=>""))}function B(...e){return l.value?I((()=>Reflect.apply(l.value.n,null,[...e]))):I((()=>""))}function j(e){return l.value?l.value.tm(e):{}}function V(e,t){return!!l.value&&l.value.te(e,t)}function D(e){return l.value?l.value.getLocaleMessage(e):{}}function $(e,t){l.value&&(l.value.setLocaleMessage(e,t),u.value[e]=t)}function U(e,t){l.value&&l.value.mergeLocaleMessage(e,t)}function W(e){return l.value?l.value.getDateTimeFormat(e):{}}function H(e,t){l.value&&(l.value.setDateTimeFormat(e,t),f.value[e]=t)}function z(e,t){l.value&&l.value.mergeDateTimeFormat(e,t)}function G(e){return l.value?l.value.getNumberFormat(e):{}}function q(e,t){l.value&&(l.value.setNumberFormat(e,t),p.value[e]=t)}function J(e,t){l.value&&l.value.mergeNumberFormat(e,t)}const K={get id(){return l.value?l.value.id:-1},locale:S,fallbackLocale:E,messages:C,datetimeFormats:T,numberFormats:N,get inheritLocale(){return l.value?l.value.inheritLocale:a},set inheritLocale(e){l.value&&(l.value.inheritLocale=e)},get availableLocales(){return l.value?l.value.availableLocales:Object.keys(u.value)},get modifiers(){return l.value?l.value.modifiers:w},get pluralRules(){return l.value?l.value.pluralRules:x},get isGlobal(){return!!l.value&&l.value.isGlobal},get missingWarn(){return l.value?l.value.missingWarn:d},set missingWarn(e){l.value&&(l.value.missingWarn=e)},get fallbackWarn(){return l.value?l.value.fallbackWarn:h},set fallbackWarn(e){l.value&&(l.value.missingWarn=e)},get fallbackRoot(){return l.value?l.value.fallbackRoot:m},set fallbackRoot(e){l.value&&(l.value.fallbackRoot=e)},get fallbackFormat(){return l.value?l.value.fallbackFormat:v},set fallbackFormat(e){l.value&&(l.value.fallbackFormat=e)},get warnHtmlMessage(){return l.value?l.value.warnHtmlMessage:_},set warnHtmlMessage(e){l.value&&(l.value.warnHtmlMessage=e)},get escapeParameter(){return l.value?l.value.escapeParameter:b},set escapeParameter(e){l.value&&(l.value.escapeParameter=e)},t:R,getPostTranslationHandler:L,setPostTranslationHandler:O,getMissingHandler:A,setMissingHandler:P,rt:F,d:M,n:B,tm:j,te:V,getLocaleMessage:D,setLocaleMessage:$,mergeLocaleMessage:U,getDateTimeFormat:W,setDateTimeFormat:H,mergeDateTimeFormat:z,getNumberFormat:G,setNumberFormat:q,mergeNumberFormat:J};function Y(e){e.locale.value=s.value,e.fallbackLocale.value=c.value,Object.keys(u.value).forEach((t=>{e.mergeLocaleMessage(t,u.value[t])})),Object.keys(f.value).forEach((t=>{e.mergeDateTimeFormat(t,f.value[t])})),Object.keys(p.value).forEach((t=>{e.mergeNumberFormat(t,p.value[t])})),e.escapeParameter=b,e.fallbackFormat=v,e.fallbackRoot=m,e.fallbackWarn=h,e.missingWarn=d,e.warnHtmlMessage=_}return(0,r.onBeforeMount)((()=>{if(null==e.proxy||null==e.proxy.$i18n)throw Qr(Xr.NOT_AVAILABLE_COMPOSITION_IN_LEGACY);const n=l.value=e.proxy.$i18n.__composer;"global"===t?(s.value=n.locale.value,c.value=n.fallbackLocale.value,u.value=n.messages.value,f.value=n.datetimeFormats.value,p.value=n.numberFormats.value):i&&Y(n)})),K}(t,l,o,e)}if("global"===l)return uo(o,e,i),o;if("parent"===l){let r=function(e,t,n=!1){let r=null;const o=t.root;let i=t.parent;for(;null!=i;){const t=e;if("composition"===e.mode)r=t.__getInstance(i);else if(__VUE_I18N_LEGACY_API__){const e=t.__getInstance(i);null!=e&&(r=e.__composer,n&&r&&!r[oo]&&(r=null))}if(null!=r)break;if(o===i)break;i=i.parent}return r}(n,t,e.__useComponent);return null==r&&(r=o),r}const a=n;let s=a.__getInstance(t);if(null==s){const n=Yt({},e);"__i18n"in i&&(n.__i18n=i.__i18n),o&&(n.__root=o),s=vo(n),function(e,t,n){(0,r.onMounted)((()=>{0}),t),(0,r.onUnmounted)((()=>{e.__deleteInstance(t)}),t)}(a,t),a.__setInstance(t,s)}return s}const Oo=["locale","fallbackLocale","availableLocales"],Ao=["t","rt","d","n","tm"];var Po,Io,Ro;if(Po=function(e,t={}){{const n=(t.onCacheKey||Tr)(e),r=Nr[n];if(r)return r;let o=!1;const i=t.onError||Tn;t.onError=e=>{o=!0,i(e)};const{code:l}=Hn(e,t),a=new Function(`return ${l}`)();return o?a:Nr[n]=a}},vr=Po,gr=function(e,t){if(!an(e))return null;let n=Zn.get(t);if(n||(n=function(e){const t=[];let n,r,o,i,l,a,s,c=-1,u=0,f=0;const p=[];function d(){const t=e[c+1];if(5===u&&"'"===t||6===u&&'"'===t)return c++,o="\\"+t,p[0](),!0}for(p[0]=()=>{void 0===r?r=o:r+=o},p[1]=()=>{void 0!==r&&(t.push(r),r=void 0)},p[2]=()=>{p[0](),f++},p[3]=()=>{if(f>0)f--,u=4,p[0]();else{if(f=0,void 0===r)return!1;if(r=Yn(r),!1===r)return!1;p[1]()}};null!==u;)if(c++,n=e[c],"\\"!==n||!d()){if(i=Kn(n),s=qn[u],l=s[i]||s.l||8,8===l)return;if(u=l[0],void 0!==l[1]&&(a=p[l[1]],a&&(o=n,!1===a())))return;if(7===u)return t}}(t),n&&Zn.set(t,n)),!n)return null;const r=n.length;let o=e,i=0;for(;igo(e))):o.run((()=>vo(e)));if(null==n)throw Qr(Xr.UNEXPECTED_ERROR);return[o,n]}}(e,n),c=Ht("");{const e={get mode(){return __VUE_I18N_LEGACY_API__&&n?"legacy":"composition"},get allowComposition(){return i},async install(t,...i){t.__VUE_I18N_SYMBOL__=c,t.provide(t.__VUE_I18N_SYMBOL__,e),!n&&o&&function(e,t){const n=Object.create(null);Oo.forEach((e=>{const o=Object.getOwnPropertyDescriptor(t,e);if(!o)throw Qr(Xr.UNEXPECTED_ERROR);const i=(0,r.isRef)(o.value)?{get:()=>o.value.value,set(e){o.value.value=e}}:{get:()=>o.get&&o.get()};Object.defineProperty(n,e,i)})),e.config.globalProperties.$i18n=n,Ao.forEach((n=>{const r=Object.getOwnPropertyDescriptor(t,n);if(!r||!r.value)throw Qr(Xr.UNEXPECTED_ERROR);Object.defineProperty(e.config.globalProperties,`$${n}`,r)}))}(t,e.global),__VUE_I18N_FULL_INSTALL__&&Co(t,e,...i),__VUE_I18N_LEGACY_API__&&n&&t.mixin(function(e,t,n){return{beforeCreate(){const o=(0,r.getCurrentInstance)();if(!o)throw Qr(Xr.UNEXPECTED_ERROR);const i=this.$options;if(i.i18n){const n=i.i18n;i.__i18n&&(n.__i18n=i.__i18n),n.__root=t,this===this.$root?this.$i18n=To(e,n):(n.__injectWithOption=!0,this.$i18n=go(n))}else i.__i18n?this===this.$root?this.$i18n=To(e,i):this.$i18n=go({__i18n:i.__i18n,__injectWithOption:!0,__root:t}):this.$i18n=e;i.__i18nGlobal&&uo(t,i,i),e.__onComponentInstanceCreated(this.$i18n),n.__setInstance(o,this.$i18n),this.$t=(...e)=>this.$i18n.t(...e),this.$rt=(...e)=>this.$i18n.rt(...e),this.$tc=(...e)=>this.$i18n.tc(...e),this.$te=(e,t)=>this.$i18n.te(e,t),this.$d=(...e)=>this.$i18n.d(...e),this.$n=(...e)=>this.$i18n.n(...e),this.$tm=e=>this.$i18n.tm(e)},mounted(){},unmounted(){const e=(0,r.getCurrentInstance)();if(!e)throw Qr(Xr.UNEXPECTED_ERROR);delete this.$t,delete this.$rt,delete this.$tc,delete this.$te,delete this.$d,delete this.$n,delete this.$tm,n.__deleteInstance(e),delete this.$i18n}}}(s,s.__composer,e));const l=t.unmount;t.unmount=()=>{e.dispose(),l()}},get global(){return s},dispose(){a.stop()},__instances:l,__getInstance:function(e){return l.get(e)||null},__setInstance:function(e,t){l.set(e,t)},__deleteInstance:function(e){l.delete(e)}};return e}}({locale:null!==(Ro=window.location.href.split("/")[3])&&void 0!==Ro?Ro:"en",fallbackLocale:"en",messages:Fo});const Bo=Mo;n(333),n(85);var jo=(0,r.createApp)({});jo.component("news-carousel-next",I),jo.component("events-carousel-next",ne),jo.use(Bo),jo.mount("#slider");var Vo=(0,r.createApp)({});Vo.component("list-overview",Se),Vo.use(Bo),Vo.mount("#list-overview-search");var Do=(0,r.createApp)({});Do.component("main-navigation",$t),Do.use(Bo),Do.mount("#main-navigation")},333:(e,t,n)=>{window._=n(486),window.Vue=n(821),window.axios=n(669),window.axios.defaults.headers.common["X-Requested-With"]="XMLHttpRequest"},85:(e,t,n)=>{"use strict";function r(e,t){var n={start:function(){return 0},center:function(e){return r(e)/2},end:r};function r(e){return t-e}return{measure:function(r){return"number"==typeof e?t*Number(e):n[e](r)}}}function o(e){return Math.abs(e)}function i(e){return e?e/o(e):0}function l(e,t){return o(e-t)}function a(e,t){for(var n=[],r=0;rt}function l(e){return r(e)||i(e)}return{length:n,max:t,min:e,constrain:function(n){return l(n)?r(n)?e:t:n},reachedAny:l,reachedMax:i,reachedMin:r,removeOffset:function(e){return n?e-n*Math.ceil((e-t)/n):e}}}function p(e,t,n){var r=f(0,e),i=r.min,l=r.constrain,a=e+1,s=c(t);function c(e){return n?o((a+e)%a):l(e)}function u(){return s}function d(e){return s=c(e),h}var h={add:function(e){return d(u()+e)},clone:function(){return p(e,u(),n)},get:u,set:d,min:i,max:e};return h}function d(){var e=[];var t={add:function(n,r,o,i){return void 0===i&&(i=!1),n.addEventListener(r,o,i),e.push((function(){return n.removeEventListener(r,o,i)})),t},removeAll:function(){return e=e.filter((function(e){return e()})),t}};return t}function h(e){var t=e;function n(e){return t/=e,o}function r(e){return"number"==typeof e?e:e.get()}var o={add:function(e){return t+=r(e),o},divide:n,get:function(){return t},multiply:function(e){return t*=e,o},normalize:function(){return 0!==t&&n(t),o},set:function(e){return t=r(e),o},subtract:function(e){return t-=r(e),o}};return o}function m(e,t,n,r,a,s,c,u,f,p,m,v,g,y,_){var b=e.cross,w=["INPUT","SELECT","TEXTAREA"],x=h(0),k=d(),S=d(),E={mouse:300,touch:400},C={mouse:500,touch:600},T=a?5:16,N=0,L=0,O=!1,A=!1,P=!1,I=!1;function R(e){if(!(I="mousedown"===e.type)||0===e.button){var t,o,i=l(r.get(),c.get())>=2,a=I||!i,u=(t=e.target,o=t.nodeName||"",!(w.indexOf(o)>-1)),f=i||I&&u;O=!0,s.pointerDown(e),x.set(r),r.set(c),p.useBaseMass().useSpeed(80),function(){var e=I?document:n;S.add(e,"touchmove",F).add(e,"touchend",M).add(e,"mousemove",F).add(e,"mouseup",M)}(),N=s.readPoint(e),L=s.readPoint(e,b),g.emit("pointerDown"),a&&(P=!1),f&&e.preventDefault()}}function F(e){if(!A&&!I){if(!e.cancelable)return M(e);var n=s.readPoint(e),o=s.readPoint(e,b),i=l(n,N),a=l(o,L);if(!(A=i>a)&&!P)return M(e)}var c=s.pointerMove(e);!P&&c&&(P=!0),u.start(),r.add(t.apply(c)),e.preventDefault()}function M(e){var n=m.byDistance(0,!1).index!==v.get(),c=s.pointerUp(e)*(a?C:E)[I?"mouse":"touch"],u=function(e,t){var n=v.clone().add(-1*i(e)),r=n.get()===v.min||n.get()===v.max,l=m.byDistance(e,!a).distance;return a||o(e)<20?l:!y&&r?.4*l:_&&t?.5*l:m.byIndex(n.get(),0).distance}(t.apply(c),n),d=function(e,t){if(0===e||0===t)return 0;if(o(e)<=o(t))return 0;var n=l(o(e),o(t));return o(n/e)}(c,u),h=l(r.get(),x.get())>=.5,b=n&&d>.75,w=o(c)<20,k=b?10:T,N=b?1+2.5*d:1;h&&!I&&(P=!0),A=!1,O=!1,S.removeAll(),p.useSpeed(w?9:k).useMass(N),f.distance(u,!a),I=!1,g.emit("pointerUp")}function B(e){P&&e.preventDefault()}return{addActivationEvents:function(){var e=n;k.add(e,"touchmove",(function(){})).add(e,"touchend",(function(){})).add(e,"touchstart",R).add(e,"mousedown",R).add(e,"touchcancel",M).add(e,"contextmenu",M).add(e,"click",B)},clickAllowed:function(){return!P},pointerDown:function(){return O},removeAllEvents:function(){k.removeAll(),S.removeAll()}}}function v(e,t,n){var r,o,l=(r=2,o=Math.pow(10,r),function(e){return Math.round(e*o)/o}),a=h(0),s=h(0),c=h(0),u=0,f=t,p=n;function d(e){return f=e,v}function m(e){return p=e,v}var v={direction:function(){return u},seek:function(t){c.set(t).subtract(e);var n,r,o,l,d=(n=c.get(),(o=0)+(n-(r=0))/(100-r)*(f-o));return u=i(c.get()),c.normalize().multiply(d).subtract(a),(l=c).divide(p),s.add(l),v},settle:function(t){var n=t.get()-e.get(),r=!l(n);return r&&e.set(t),r},update:function(){a.add(s),e.add(a),s.multiply(0)},useBaseMass:function(){return m(n)},useBaseSpeed:function(){return d(t)},useMass:m,useSpeed:d};return v}function g(e,t,n,r){var i=!1;return{constrain:function(l){if(!i&&e.reachedAny(n.get())&&e.reachedAny(t.get())){var a=e.reachedMin(t.get())?"min":"max",s=o(e[a]-t.get()),c=n.get()-t.get(),u=Math.min(s/50,.85);n.subtract(c*u),!l&&o(c)<10&&(n.set(e.constrain(n.get())),r.useSpeed(10).useMass(3))}},toggleActive:function(e){i=!e}}}function y(e,t,n,r,o){var i=f(-t+e,n[0]),l=r.map(i.constrain);return{snapsContained:function(){if(t<=e)return[i.max];if("keepSnaps"===o)return l;var n=function(){var e=l[0],t=c(l),n=l.lastIndexOf(e),r=l.indexOf(t)+1;return f(n,r)}(),r=n.min,a=n.max;return l.slice(r,a)}()}}function _(e,t,n,r,o){var i=f(n.min+t.measure(.1),n.max+t.measure(.1)),l=i.reachedMin,a=i.reachedMax;return{loop:function(t){if(function(e){return 1===e?a(r.get()):-1===e&&l(r.get())}(t)){var n=e*(-1*t);o.forEach((function(e){return e.add(n)}))}}}}function b(e){var t=e.max,n=e.length;return{get:function(e){return(e-t)/-n}}}function w(e,t,n,r,i,l){var s,u,f=e.startEdge,p=e.endEdge,d=i.map((function(e){return r[f]-e[f]})).map(n.measure).map((function(e){return-o(e)})),h=(s=a(d,l).map((function(e){return e[0]})),u=a(i,l).map((function(e){return c(e)[p]-e[0][f]})).map(n.measure).map(o).map(t.measure),s.map((function(e,t){return e+u[t]})));return{snaps:d,snapsAligned:h}}function x(e,t,n,r,i){var l=r.reachedAny,a=r.removeOffset,s=r.constrain;function c(e,t){return o(e)0?e.concat([n]):e}),[])}function m(e,t){var r="start"===t,o=r?-n:n,a=i.findSlideBounds([o]);return e.map((function(e){var t=r?0:-n,o=r?n:0,i=a.filter((function(t){return t.index===e}))[0][r?"end":"start"];return{point:i,getTarget:function(){return l.get()>i?t:o},index:e,location:-1}}))}return{canLoop:function(){return p.every((function(e){var n=e.index;return d(u.filter((function(e){return e!==n})),t)<=0}))},clear:function(){p.forEach((function(t){var n=t.index;a[n].style[e.startEdge]=""}))},loop:function(){p.forEach((function(t){var n=t.getTarget,r=t.location,o=t.index,i=n();i!==r&&(a[o].style[e.startEdge]=i+"%",t.location=i)}))},loopPoints:p}}function S(e,t,n){var r="x"===e.scroll?function(e){return"translate3d("+e+"%,0px,0px)"}:function(e){return"translate3d(0px,"+e+"%,0px)"},o=n.style,i=!1;return{clear:function(){o.transform=""},to:function(e){i||(o.transform=r(t.apply(e.get())))},toggleActive:function(e){i=!e}}}function E(e,t,n,i,l){var a,E=i.align,C=i.axis,T=i.direction,N=i.startIndex,L=i.inViewThreshold,O=i.loop,A=i.speed,P=i.dragFree,I=i.slidesToScroll,R=i.skipSnaps,F=i.containScroll,M=t.getBoundingClientRect(),B=n.map((function(e){return e.getBoundingClientRect()})),j=function(e){var t="rtl"===e?-1:1;return{apply:function(e){return e*t}}}(T),V=function(e,t){var n="y"===e?"y":"x";return{scroll:n,cross:"y"===e?"x":"y",startEdge:"y"===n?"top":"rtl"===t?"right":"left",endEdge:"y"===n?"bottom":"rtl"===t?"left":"right",measureSize:function(e){var t=e.width,r=e.height;return"x"===n?t:r}}}(C,T),D=(a=V.measureSize(M),{measure:function(e){return 0===a?0:e/a*100},totalPercent:100}),$=D.totalPercent,U=r(E,$),W=function(e,t,n,r,i){var l=e.measureSize,a=e.startEdge,s=e.endEdge,f=r.map(l);return{slideSizes:f.map(t.measure),slideSizesWithGaps:r.map((function(e,t,r){var o=t===u(r),l=window.getComputedStyle(c(n)),p=parseFloat(l.getPropertyValue("margin-"+s));return o?f[t]+(i?p:0):r[t+1][a]-e[a]})).map(t.measure).map(o)}}(V,D,n,B,O),H=W.slideSizes,z=W.slideSizesWithGaps,G=w(V,U,D,M,B,I),q=G.snaps,J=G.snapsAligned,K=-c(q)+c(z),Y=y($,K,q,J,F).snapsContained,Z=!O&&""!==F?Y:J,X=function(e,t,n){var r,o;return{limit:(r=t[0],o=c(t),f(n?r-e:o,r))}}(K,Z,O).limit,Q=p(u(Z),N,O),ee=Q.clone(),te=s(n),ne=function(e){var t=0;function n(e,n){return function(){e===!!t&&n()}}function r(){t=window.requestAnimationFrame(e)}return{proceed:n(!0,r),start:n(!1,r),stop:n(!0,(function(){window.cancelAnimationFrame(t),t=0}))}}((function(){O||fe.scrollBounds.constrain(fe.dragHandler.pointerDown()),fe.scrollBody.seek(ie).update();var e=fe.scrollBody.settle(ie);e&&!fe.dragHandler.pointerDown()&&(fe.animation.stop(),l.emit("settle")),e||l.emit("scroll"),O&&(fe.scrollLooper.loop(fe.scrollBody.direction()),fe.slideLooper.loop()),fe.translate.to(oe),fe.animation.proceed()})),re=Z[Q.get()],oe=h(re),ie=h(re),le=v(oe,A,1),ae=x(O,Z,K,X,ie),se=function(e,t,n,r,o,i){function l(r){var l=r.distance,a=r.index!==t.get();l&&(e.start(),o.add(l)),a&&(n.set(t.get()),t.set(r.index),i.emit("select"))}return{distance:function(e,t){l(r.byDistance(e,t))},index:function(e,n){var o=t.clone().set(e);l(r.byIndex(o.get(),n))}}}(ne,Q,ee,ae,ie,l),ce=function(e,t,n,r,o,i,l){var a=o.removeOffset,s=o.constrain,c=Math.min(Math.max(l,.01),.99),u=i?[0,t,-t]:[0],f=p(u,c);function p(t,o){var i=t||u,l=o||0,a=n.map((function(e){return e*l}));return i.reduce((function(t,o){var i=r.map((function(t,r){return{start:t-n[r]+a[r]+o,end:t+e-a[r]+o,index:r}}));return t.concat(i)}),[])}return{check:function(e,t){var n=i?a(e):s(e);return(t||f).reduce((function(e,t){var r=t.index,o=t.start,i=t.end;return-1===e.indexOf(r)&&on?e.concat([r]):e}),[])},findSlideBounds:p}}($,K,H,q,X,O,L),ue=m(V,j,e,ie,P,function(e,t){var n,r;function i(e){return"undefined"!=typeof TouchEvent&&e instanceof TouchEvent}function l(e){return e.timeStamp}function a(t,n){var r="client"+("x"===(n||e.scroll)?"X":"Y");return(i(t)?t.touches[0]:t)[r]}return{isTouchEvent:i,pointerDown:function(e){return n=e,r=e,t.measure(a(e))},pointerMove:function(e){var o=a(e)-a(r),i=l(e)-l(n)>170;return r=e,i&&(n=e),t.measure(o)},pointerUp:function(e){if(!n||!r)return 0;var i=a(r)-a(n),s=l(e)-l(n),c=l(e)-l(r)>170,u=i/s;return s&&!c&&o(u)>.1?t.measure(u):0},readPoint:a}}(V,D),oe,ne,se,le,ae,Q,l,O,R),fe={containerRect:M,slideRects:B,animation:ne,axis:V,direction:j,dragHandler:ue,eventStore:d(),pxToPercent:D,index:Q,indexPrevious:ee,limit:X,location:oe,options:i,scrollBody:le,scrollBounds:g(X,oe,ie,le),scrollLooper:_(K,D,X,oe,[oe,ie]),scrollProgress:b(X),scrollSnaps:Z,scrollTarget:ae,scrollTo:se,slideLooper:k(V,$,K,z,Z,ce,oe,n),slidesInView:ce,slideIndexes:te,target:ie,translate:S(V,j,t)};return fe}n.r(t);var C={align:"center",axis:"x",containScroll:"",direction:"ltr",dragFree:!1,draggable:!0,inViewThreshold:0,loop:!1,skipSnaps:!1,slidesToScroll:1,speed:10,startIndex:0};function T(e,t,n){var r,o,i,l,a,s,c,u,f,p=function(){var e={};function t(t){return e[t]||[]}var n={emit:function(e){return t(e).forEach((function(t){return t(e)})),n},off:function(r,o){return e[r]=t(r).filter((function(e){return e!==o})),n},on:function(r,o){return e[r]=t(r).concat([o]),n}};return n}(),d=(r=function(){if(g){var e=l.axis.measureSize(c.getBoundingClientRect());b!==e&&k(),p.emit("resize")}},o=500,i=0,function(){window.clearTimeout(i),i=window.setTimeout(r,o)||0}),h=k,m=p.on,v=p.off,g=!1,y=Object.assign({},C,T.globalOptions),_=Object.assign({},y),b=0;function w(){var t,n="container"in e&&e.container,r="slides"in e&&e.slides;c="root"in e?e.root:e,u=n||c.children[0],f=r||[].slice.call(u.children),t=getComputedStyle(c,":before").content,a={get:function(){try{return JSON.parse(t.slice(1,-1).replace(/\\/g,""))}catch(e){}return{}}}}function x(e,t){if(w(),y=Object.assign({},y,e),_=Object.assign({},y,a.get()),s=Object.assign([],t),(l=E(c,u,f,_,p)).eventStore.add(window,"resize",d),l.translate.to(l.location),b=l.axis.measureSize(c.getBoundingClientRect()),s.forEach((function(e){return e.init(A)})),_.loop){if(!l.slideLooper.canLoop())return S(),x({loop:!1},t);l.slideLooper.loop()}_.draggable&&u.offsetParent&&f.length&&l.dragHandler.addActivationEvents(),g||(setTimeout((function(){return p.emit("init")}),0),g=!0)}function k(e,t){if(g){var n=O(),r=Object.assign({startIndex:n},e);S(),x(r,t||s),p.emit("reInit")}}function S(){l.dragHandler.removeAllEvents(),l.animation.stop(),l.eventStore.removeAll(),l.translate.clear(),l.slideLooper.clear(),s.forEach((function(e){return e.destroy()}))}function N(e){var t=l[e?"target":"location"].get(),n=_.loop?"removeOffset":"constrain";return l.slidesInView.check(l.limit[n](t))}function L(e,t,n){l.scrollBody.useBaseMass().useSpeed(t?100:_.speed),g&&l.scrollTo.index(e,n||0)}function O(){return l.index.get()}var A={canScrollNext:function(){return l.index.clone().add(1).get()!==O()},canScrollPrev:function(){return l.index.clone().add(-1).get()!==O()},clickAllowed:function(){return l.dragHandler.clickAllowed()},containerNode:function(){return u},internalEngine:function(){return l},destroy:function(){g&&(S(),g=!1,p.emit("destroy"))},off:v,on:m,previousScrollSnap:function(){return l.indexPrevious.get()},reInit:h,rootNode:function(){return c},scrollNext:function(e){L(l.index.clone().add(1).get(),!0===e,-1)},scrollPrev:function(e){L(l.index.clone().add(-1).get(),!0===e,1)},scrollProgress:function(){return l.scrollProgress.get(l.location.get())},scrollSnapList:function(){return l.scrollSnaps.map(l.scrollProgress.get)},scrollTo:L,selectedScrollSnap:O,slideNodes:function(){return f},slidesInView:N,slidesNotInView:function(e){var t=N(e);return l.slideIndexes.filter((function(e){return-1===t.indexOf(e)}))}};return x(t,n),A}T.globalOptions=void 0;const N=T;var L={delay:4e3,playOnInit:!0,stopOnInteraction:!0,stopOnMouseEnter:!1,stopOnLastSnap:!1};function O(e,t){var n,r=Object.assign({},L,O.globalOptions,e),o=r.playOnInit,i=r.stopOnInteraction,l=r.stopOnMouseEnter,a=r.stopOnLastSnap,s=r.delay,c=i?f:d,u=0;function f(){n.off("pointerDown",c),i||n.off("pointerUp",h),d(),u=0}function p(){d(),u=window.setTimeout(m,s)}function d(){u&&window.clearTimeout(u)}function h(){u&&(d(),p())}function m(){var e=n.internalEngine().index;if(a&&e.get()===e.max)return f();n.canScrollNext()?n.scrollNext():n.scrollTo(0),p()}var v={name:"Autoplay",options:r,init:function(e){var r=(n=e).internalEngine().eventStore,a=n.rootNode(),s=t&&t(a)||a;n.on("pointerDown",c),i||n.on("pointerUp",h),l&&(r.add(s,"mouseenter",c),i||r.add(s,"mouseleave",h)),r.add(document,"visibilitychange",(function(){if("hidden"===document.visibilityState)return d();h()})),r.add(window,"pagehide",(function(e){e.persisted&&d()})),o&&p()},destroy:f,play:p,stop:d,reset:h};return v}O.globalOptions=void 0;const A=O;var P=document.querySelector(".embla");if(P){var I=null==P?void 0:P.querySelector(".embla__viewport"),R=null==P?void 0:P.querySelector(".embla__button--prev"),F=null==P?void 0:P.querySelector(".embla__button--next"),M=document.querySelector(".embla__dots"),B=A({delay:1e4,stopOnInteraction:!1},(function(e){return e.parentElement})),j=N(I,{loop:!1,skipSnaps:!1,speed:2},[B]),V=function(e,t){var n=t.scrollSnapList(),r=document.createDocumentFragment(),o=n.map((function(){return document.createElement("button")}));return o.forEach((function(e){return r.appendChild(e)})),null==e||e.appendChild(r),o}(M,j),D=function(e,t){return function(){var n=t.previousScrollSnap(),r=t.selectedScrollSnap();t.slideNodes()[n].classList.remove("is-selected"),t.slideNodes()[r].classList.add("is-selected"),e[n].classList.remove("is-selected"),e[r].classList.add("is-selected")}}(V,j),$=function(e,t,n){return function(){n.canScrollPrev()?null==e||e.removeAttribute("disabled"):null==e||e.setAttribute("disabled","disabled"),n.canScrollNext()?null==t||t.removeAttribute("disabled"):null==t||t.setAttribute("disabled","disabled")}}(R,F,j);!function(e,t,n){null==e||e.addEventListener("click",(function(){n.reset(),t.scrollPrev(),t.slideNodes()[t.previousScrollSnap()].classList.remove("is-selected"),t.slideNodes()[t.selectedScrollSnap()].classList.add("is-selected")}),!1)}(R,j,B),function(e,t,n){null==e||e.addEventListener("click",(function(){n.reset(),t.scrollNext(),t.slideNodes()[t.previousScrollSnap()].classList.remove("is-selected"),t.slideNodes()[t.selectedScrollSnap()].classList.add("is-selected")}),!1)}(F,j,B),function(e,t){e.forEach((function(e,n){e.classList.add("embla__dot"),e.addEventListener("click",(function(){return t.scrollTo(n)}),!1)}))}(V,j);var U=function(e){var t="resize"===e,n=t?"remove":"add";I.classList[n]("embla--is-ready"),t&&j.reInit()};j.on("select",D),j.on("select",$),j.on("init",D),j.on("init",$),j.on("init",U),j.on("resize",U),j.on("reInit",U),j.internalEngine().translate.toggleActive(!1),j.internalEngine().translate.clear()}},563:(e,t,n)=>{"use strict";n.d(t,{Z:()=>i});var r=n(645),o=n.n(r)()((function(e){return e[1]}));o.push([e.id,".carousel__slide{align-items:center;display:flex;flex-shrink:0;justify-content:center;margin:0;position:relative;scroll-snap-stop:auto}.carousel{margin-left:-.75rem;margin-right:-.75rem;position:relative}.carousel,.carousel *{box-sizing:border-box}.carousel__track{display:flex;margin:0;padding:0;position:relative}.carousel__viewport{overflow:hidden}:root{--vc-clr-primary:#57666a;--vc-clr-secondary:#bfc3c5;--vc-clr-white:#fff;--vc-icn-width:1em;--vc-nav-width:40px;--vc-nav-color:#fff;--vc-nav-background-color:var(--vc-clr-primary);--vc-pgn-width:8px;--vc-pgn-height:8px;--vc-pgn-margin:7px;--vc-pgn-border-radius:0;--vc-pgn-background-color:var(--vc-clr-secondary);--vc-pgn-active-color:var(--vc-clr-primary)}.carousel__next,.carousel__prev{align-items:center;background-color:#fff;border:0;border-radius:100%;color:#005a9a;cursor:pointer;display:none;font-size:calc(var(--vc-nav-width)*2/3);height:var(--vc-nav-width);justify-content:center;padding:0;position:absolute;text-align:center;width:var(--vc-nav-width)}.carousel__prev{right:54px;top:calc(100% - 35px)}.carousel__next{right:0;top:calc(100% - 35px)}.carousel__icon{fill:currentColor;height:var(--vc-icn-width);width:var(--vc-icn-width)}.carousel__pagination{display:flex;flex-wrap:wrap;justify-content:center;list-style:none;margin-left:0;margin-top:30px}@media (min-width:768px){.carousel__pagination{display:flex;justify-content:flex-start;list-style:none;margin-left:8px;margin-top:30px;max-width:300px}.carousel__next,.carousel__prev{display:flex}.carousel__prev{right:66px;top:calc(100% - 35px)}.carousel__next{right:12px;top:calc(100% - 35px)}}.carousel__pagination-button{background-color:var(--vc-pgn-background-color);border:0;border-radius:var(--vc-pgn-height);cursor:pointer;height:var(--vc-pgn-height);margin:var(--vc-pgn-margin);width:var(--vc-pgn-width)}.carousel__pagination-button--active{background-color:var(--vc-pgn-active-color)}",""]);const i=o},284:(e,t,n)=>{"use strict";n.d(t,{Z:()=>i});var r=n(645),o=n.n(r)()((function(e){return e[1]}));o.push([e.id,".mega-height{height:calc(100vh - 93px)}.scrolled .mega-height{height:calc(100vh - 62px)}",""]);const i=o},236:(e,t,n)=>{"use strict";n.d(t,{Z:()=>i});var r=n(645),o=n.n(r)()((function(e){return e[1]}));o.push([e.id,".carousel__slide{align-items:center;display:flex;flex-shrink:0;justify-content:center;margin:0;position:relative;scroll-snap-stop:auto}.carousel{position:relative}.carousel,.carousel *{box-sizing:border-box}.carousel__track{display:flex;margin:0;padding:0;position:relative}.carousel__viewport{overflow:hidden}:root{--vc-clr-primary:#57666a;--vc-clr-secondary:#bfc3c5;--vc-clr-white:#fff;--vc-icn-width:1em;--vc-nav-width:40px;--vc-nav-color:#fff;--vc-nav-background-color:var(--vc-clr-primary);--vc-pgn-width:8px;--vc-pgn-height:8px;--vc-pgn-margin:7px;--vc-pgn-border-radius:0;--vc-pgn-background-color:var(--vc-clr-secondary);--vc-pgn-active-color:var(--vc-clr-primary)}.carousel__next,.carousel__prev{align-items:center;background-color:#fff;border:0;border-radius:9px;color:#005a9a;cursor:pointer;display:none;font-size:calc(var(--vc-nav-width)*2/3);height:var(--vc-nav-width);justify-content:center;padding:0;position:absolute;text-align:center;width:var(--vc-nav-width)}.carousel__prev{right:54px;top:calc(100% - 35px)}.carousel__next{right:0;top:calc(100% - 35px)}.carousel__icon{fill:currentColor;height:var(--vc-icn-width);width:var(--vc-icn-width)}.carousel__pagination{display:flex;flex-wrap:wrap;justify-content:center;list-style:none;margin-left:0;margin-top:30px}@media (min-width:768px){.carousel__pagination{display:flex;justify-content:flex-start;list-style:none;margin-left:8px;margin-top:30px;max-width:300px}.carousel__next,.carousel__prev{display:flex}.carousel__prev{right:66px;top:calc(100% - 35px)}.carousel__next{right:12px;top:calc(100% - 35px)}}.carousel__pagination-button{background-color:var(--vc-pgn-background-color);border:0;border-radius:var(--vc-pgn-height);cursor:pointer;height:var(--vc-pgn-height);margin:var(--vc-pgn-margin);width:var(--vc-pgn-width)}.carousel__pagination-button--active{background-color:var(--vc-pgn-active-color)}",""]);const i=o},645:e=>{"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n=e(t);return t[2]?"@media ".concat(t[2]," {").concat(n,"}"):n})).join("")},t.i=function(e,n,r){"string"==typeof e&&(e=[[null,e,""]]);var o={};if(r)for(var i=0;i"']/g,Y=RegExp(J.source),Z=RegExp(K.source),X=/<%-([\s\S]+?)%>/g,Q=/<%([\s\S]+?)%>/g,ee=/<%=([\s\S]+?)%>/g,te=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,ne=/^\w*$/,re=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,oe=/[\\^$.*+?()[\]{}|]/g,ie=RegExp(oe.source),le=/^\s+/,ae=/\s/,se=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,ce=/\{\n\/\* \[wrapped with (.+)\] \*/,ue=/,? & /,fe=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,pe=/[()=,{}\[\]\/\s]/,de=/\\(\\)?/g,he=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,me=/\w*$/,ve=/^[-+]0x[0-9a-f]+$/i,ge=/^0b[01]+$/i,ye=/^\[object .+?Constructor\]$/,_e=/^0o[0-7]+$/i,be=/^(?:0|[1-9]\d*)$/,we=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,xe=/($^)/,ke=/['\n\r\u2028\u2029\\]/g,Se="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",Ee="\\u2700-\\u27bf",Ce="a-z\\xdf-\\xf6\\xf8-\\xff",Te="A-Z\\xc0-\\xd6\\xd8-\\xde",Ne="\\ufe0e\\ufe0f",Le="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",Oe="['’]",Ae="[\\ud800-\\udfff]",Pe="["+Le+"]",Ie="["+Se+"]",Re="\\d+",Fe="[\\u2700-\\u27bf]",Me="["+Ce+"]",Be="[^\\ud800-\\udfff"+Le+Re+Ee+Ce+Te+"]",je="\\ud83c[\\udffb-\\udfff]",Ve="[^\\ud800-\\udfff]",De="(?:\\ud83c[\\udde6-\\uddff]){2}",$e="[\\ud800-\\udbff][\\udc00-\\udfff]",Ue="["+Te+"]",We="(?:"+Me+"|"+Be+")",He="(?:"+Ue+"|"+Be+")",ze="(?:['’](?:d|ll|m|re|s|t|ve))?",Ge="(?:['’](?:D|LL|M|RE|S|T|VE))?",qe="(?:"+Ie+"|"+je+")"+"?",Je="[\\ufe0e\\ufe0f]?",Ke=Je+qe+("(?:\\u200d(?:"+[Ve,De,$e].join("|")+")"+Je+qe+")*"),Ye="(?:"+[Fe,De,$e].join("|")+")"+Ke,Ze="(?:"+[Ve+Ie+"?",Ie,De,$e,Ae].join("|")+")",Xe=RegExp(Oe,"g"),Qe=RegExp(Ie,"g"),et=RegExp(je+"(?="+je+")|"+Ze+Ke,"g"),tt=RegExp([Ue+"?"+Me+"+"+ze+"(?="+[Pe,Ue,"$"].join("|")+")",He+"+"+Ge+"(?="+[Pe,Ue+We,"$"].join("|")+")",Ue+"?"+We+"+"+ze,Ue+"+"+Ge,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",Re,Ye].join("|"),"g"),nt=RegExp("[\\u200d\\ud800-\\udfff"+Se+Ne+"]"),rt=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,ot=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],it=-1,lt={};lt[M]=lt[B]=lt[j]=lt[V]=lt[D]=lt[$]=lt[U]=lt[W]=lt[H]=!0,lt[y]=lt[_]=lt[R]=lt[b]=lt[F]=lt[w]=lt[x]=lt[k]=lt[E]=lt[C]=lt[T]=lt[L]=lt[O]=lt[A]=lt[I]=!1;var at={};at[y]=at[_]=at[R]=at[F]=at[b]=at[w]=at[M]=at[B]=at[j]=at[V]=at[D]=at[E]=at[C]=at[T]=at[L]=at[O]=at[A]=at[P]=at[$]=at[U]=at[W]=at[H]=!0,at[x]=at[k]=at[I]=!1;var st={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},ct=parseFloat,ut=parseInt,ft="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,pt="object"==typeof self&&self&&self.Object===Object&&self,dt=ft||pt||Function("return this")(),ht=t&&!t.nodeType&&t,mt=ht&&e&&!e.nodeType&&e,vt=mt&&mt.exports===ht,gt=vt&&ft.process,yt=function(){try{var e=mt&&mt.require&&mt.require("util").types;return e||gt&>.binding&>.binding("util")}catch(e){}}(),_t=yt&&yt.isArrayBuffer,bt=yt&&yt.isDate,wt=yt&&yt.isMap,xt=yt&&yt.isRegExp,kt=yt&&yt.isSet,St=yt&&yt.isTypedArray;function Et(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function Ct(e,t,n,r){for(var o=-1,i=null==e?0:e.length;++o-1}function Pt(e,t,n){for(var r=-1,o=null==e?0:e.length;++r-1;);return n}function tn(e,t){for(var n=e.length;n--&&$t(t,e[n],0)>-1;);return n}function nn(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&++r;return r}var rn=Gt({À:"A",Á:"A",Â:"A",Ã:"A",Ä:"A",Å:"A",à:"a",á:"a",â:"a",ã:"a",ä:"a",å:"a",Ç:"C",ç:"c",Ð:"D",ð:"d",È:"E",É:"E",Ê:"E",Ë:"E",è:"e",é:"e",ê:"e",ë:"e",Ì:"I",Í:"I",Î:"I",Ï:"I",ì:"i",í:"i",î:"i",ï:"i",Ñ:"N",ñ:"n",Ò:"O",Ó:"O",Ô:"O",Õ:"O",Ö:"O",Ø:"O",ò:"o",ó:"o",ô:"o",õ:"o",ö:"o",ø:"o",Ù:"U",Ú:"U",Û:"U",Ü:"U",ù:"u",ú:"u",û:"u",ü:"u",Ý:"Y",ý:"y",ÿ:"y",Æ:"Ae",æ:"ae",Þ:"Th",þ:"th",ß:"ss",Ā:"A",Ă:"A",Ą:"A",ā:"a",ă:"a",ą:"a",Ć:"C",Ĉ:"C",Ċ:"C",Č:"C",ć:"c",ĉ:"c",ċ:"c",č:"c",Ď:"D",Đ:"D",ď:"d",đ:"d",Ē:"E",Ĕ:"E",Ė:"E",Ę:"E",Ě:"E",ē:"e",ĕ:"e",ė:"e",ę:"e",ě:"e",Ĝ:"G",Ğ:"G",Ġ:"G",Ģ:"G",ĝ:"g",ğ:"g",ġ:"g",ģ:"g",Ĥ:"H",Ħ:"H",ĥ:"h",ħ:"h",Ĩ:"I",Ī:"I",Ĭ:"I",Į:"I",İ:"I",ĩ:"i",ī:"i",ĭ:"i",į:"i",ı:"i",Ĵ:"J",ĵ:"j",Ķ:"K",ķ:"k",ĸ:"k",Ĺ:"L",Ļ:"L",Ľ:"L",Ŀ:"L",Ł:"L",ĺ:"l",ļ:"l",ľ:"l",ŀ:"l",ł:"l",Ń:"N",Ņ:"N",Ň:"N",Ŋ:"N",ń:"n",ņ:"n",ň:"n",ŋ:"n",Ō:"O",Ŏ:"O",Ő:"O",ō:"o",ŏ:"o",ő:"o",Ŕ:"R",Ŗ:"R",Ř:"R",ŕ:"r",ŗ:"r",ř:"r",Ś:"S",Ŝ:"S",Ş:"S",Š:"S",ś:"s",ŝ:"s",ş:"s",š:"s",Ţ:"T",Ť:"T",Ŧ:"T",ţ:"t",ť:"t",ŧ:"t",Ũ:"U",Ū:"U",Ŭ:"U",Ů:"U",Ű:"U",Ų:"U",ũ:"u",ū:"u",ŭ:"u",ů:"u",ű:"u",ų:"u",Ŵ:"W",ŵ:"w",Ŷ:"Y",ŷ:"y",Ÿ:"Y",Ź:"Z",Ż:"Z",Ž:"Z",ź:"z",ż:"z",ž:"z",IJ:"IJ",ij:"ij",Œ:"Oe",œ:"oe",ʼn:"'n",ſ:"s"}),on=Gt({"&":"&","<":"<",">":">",'"':""","'":"'"});function ln(e){return"\\"+st[e]}function an(e){return nt.test(e)}function sn(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}function cn(e,t){return function(n){return e(t(n))}}function un(e,t){for(var n=-1,r=e.length,o=0,i=[];++n",""":'"',"'":"'"});var gn=function e(t){var n,r=(t=null==t?dt:gn.defaults(dt.Object(),t,gn.pick(dt,ot))).Array,ae=t.Date,Se=t.Error,Ee=t.Function,Ce=t.Math,Te=t.Object,Ne=t.RegExp,Le=t.String,Oe=t.TypeError,Ae=r.prototype,Pe=Ee.prototype,Ie=Te.prototype,Re=t["__core-js_shared__"],Fe=Pe.toString,Me=Ie.hasOwnProperty,Be=0,je=(n=/[^.]+$/.exec(Re&&Re.keys&&Re.keys.IE_PROTO||""))?"Symbol(src)_1."+n:"",Ve=Ie.toString,De=Fe.call(Te),$e=dt._,Ue=Ne("^"+Fe.call(Me).replace(oe,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),We=vt?t.Buffer:o,He=t.Symbol,ze=t.Uint8Array,Ge=We?We.allocUnsafe:o,qe=cn(Te.getPrototypeOf,Te),Je=Te.create,Ke=Ie.propertyIsEnumerable,Ye=Ae.splice,Ze=He?He.isConcatSpreadable:o,et=He?He.iterator:o,nt=He?He.toStringTag:o,st=function(){try{var e=hi(Te,"defineProperty");return e({},"",{}),e}catch(e){}}(),ft=t.clearTimeout!==dt.clearTimeout&&t.clearTimeout,pt=ae&&ae.now!==dt.Date.now&&ae.now,ht=t.setTimeout!==dt.setTimeout&&t.setTimeout,mt=Ce.ceil,gt=Ce.floor,yt=Te.getOwnPropertySymbols,jt=We?We.isBuffer:o,Gt=t.isFinite,yn=Ae.join,_n=cn(Te.keys,Te),bn=Ce.max,wn=Ce.min,xn=ae.now,kn=t.parseInt,Sn=Ce.random,En=Ae.reverse,Cn=hi(t,"DataView"),Tn=hi(t,"Map"),Nn=hi(t,"Promise"),Ln=hi(t,"Set"),On=hi(t,"WeakMap"),An=hi(Te,"create"),Pn=On&&new On,In={},Rn=$i(Cn),Fn=$i(Tn),Mn=$i(Nn),Bn=$i(Ln),jn=$i(On),Vn=He?He.prototype:o,Dn=Vn?Vn.valueOf:o,$n=Vn?Vn.toString:o;function Un(e){if(oa(e)&&!ql(e)&&!(e instanceof Gn)){if(e instanceof zn)return e;if(Me.call(e,"__wrapped__"))return Ui(e)}return new zn(e)}var Wn=function(){function e(){}return function(t){if(!ra(t))return{};if(Je)return Je(t);e.prototype=t;var n=new e;return e.prototype=o,n}}();function Hn(){}function zn(e,t){this.__wrapped__=e,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=o}function Gn(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=v,this.__views__=[]}function qn(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t=t?e:t)),e}function ur(e,t,n,r,i,l){var a,s=1&t,c=2&t,u=4&t;if(n&&(a=i?n(e,r,i,l):n(e)),a!==o)return a;if(!ra(e))return e;var f=ql(e);if(f){if(a=function(e){var t=e.length,n=new e.constructor(t);t&&"string"==typeof e[0]&&Me.call(e,"index")&&(n.index=e.index,n.input=e.input);return n}(e),!s)return Po(e,a)}else{var p=gi(e),d=p==k||p==S;if(Zl(e))return Co(e,s);if(p==T||p==y||d&&!i){if(a=c||d?{}:_i(e),!s)return c?function(e,t){return Io(e,vi(e),t)}(e,function(e,t){return e&&Io(t,Fa(t),e)}(a,e)):function(e,t){return Io(e,mi(e),t)}(e,lr(a,e))}else{if(!at[p])return i?e:{};a=function(e,t,n){var r=e.constructor;switch(t){case R:return To(e);case b:case w:return new r(+e);case F:return function(e,t){var n=t?To(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}(e,n);case M:case B:case j:case V:case D:case $:case U:case W:case H:return No(e,n);case E:return new r;case C:case A:return new r(e);case L:return function(e){var t=new e.constructor(e.source,me.exec(e));return t.lastIndex=e.lastIndex,t}(e);case O:return new r;case P:return o=e,Dn?Te(Dn.call(o)):{}}var o}(e,p,s)}}l||(l=new Zn);var h=l.get(e);if(h)return h;l.set(e,a),ca(e)?e.forEach((function(r){a.add(ur(r,t,n,r,e,l))})):ia(e)&&e.forEach((function(r,o){a.set(o,ur(r,t,n,o,e,l))}));var m=f?o:(u?c?ai:li:c?Fa:Ra)(e);return Tt(m||e,(function(r,o){m&&(r=e[o=r]),rr(a,o,ur(r,t,n,o,e,l))})),a}function fr(e,t,n){var r=n.length;if(null==e)return!r;for(e=Te(e);r--;){var i=n[r],l=t[i],a=e[i];if(a===o&&!(i in e)||!l(a))return!1}return!0}function pr(e,t,n){if("function"!=typeof e)throw new Oe(i);return Ri((function(){e.apply(o,n)}),t)}function dr(e,t,n,r){var o=-1,i=At,l=!0,a=e.length,s=[],c=t.length;if(!a)return s;n&&(t=It(t,Zt(n))),r?(i=Pt,l=!1):t.length>=200&&(i=Qt,l=!1,t=new Yn(t));e:for(;++o-1},Jn.prototype.set=function(e,t){var n=this.__data__,r=or(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},Kn.prototype.clear=function(){this.size=0,this.__data__={hash:new qn,map:new(Tn||Jn),string:new qn}},Kn.prototype.delete=function(e){var t=pi(this,e).delete(e);return this.size-=t?1:0,t},Kn.prototype.get=function(e){return pi(this,e).get(e)},Kn.prototype.has=function(e){return pi(this,e).has(e)},Kn.prototype.set=function(e,t){var n=pi(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},Yn.prototype.add=Yn.prototype.push=function(e){return this.__data__.set(e,l),this},Yn.prototype.has=function(e){return this.__data__.has(e)},Zn.prototype.clear=function(){this.__data__=new Jn,this.size=0},Zn.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},Zn.prototype.get=function(e){return this.__data__.get(e)},Zn.prototype.has=function(e){return this.__data__.has(e)},Zn.prototype.set=function(e,t){var n=this.__data__;if(n instanceof Jn){var r=n.__data__;if(!Tn||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new Kn(r)}return n.set(e,t),this.size=n.size,this};var hr=Mo(xr),mr=Mo(kr,!0);function vr(e,t){var n=!0;return hr(e,(function(e,r,o){return n=!!t(e,r,o)})),n}function gr(e,t,n){for(var r=-1,i=e.length;++r0&&n(a)?t>1?_r(a,t-1,n,r,o):Rt(o,a):r||(o[o.length]=a)}return o}var br=Bo(),wr=Bo(!0);function xr(e,t){return e&&br(e,t,Ra)}function kr(e,t){return e&&wr(e,t,Ra)}function Sr(e,t){return Ot(t,(function(t){return ea(e[t])}))}function Er(e,t){for(var n=0,r=(t=xo(t,e)).length;null!=e&&nt}function Lr(e,t){return null!=e&&Me.call(e,t)}function Or(e,t){return null!=e&&t in Te(e)}function Ar(e,t,n){for(var i=n?Pt:At,l=e[0].length,a=e.length,s=a,c=r(a),u=1/0,f=[];s--;){var p=e[s];s&&t&&(p=It(p,Zt(t))),u=wn(p.length,u),c[s]=!n&&(t||l>=120&&p.length>=120)?new Yn(s&&p):o}p=e[0];var d=-1,h=c[0];e:for(;++d=a?s:s*("desc"==n[r]?-1:1)}return e.index-t.index}(e,t,n)}))}function qr(e,t,n){for(var r=-1,o=t.length,i={};++r-1;)a!==e&&Ye.call(a,s,1),Ye.call(e,s,1);return e}function Kr(e,t){for(var n=e?t.length:0,r=n-1;n--;){var o=t[n];if(n==r||o!==i){var i=o;wi(o)?Ye.call(e,o,1):ho(e,o)}}return e}function Yr(e,t){return e+gt(Sn()*(t-e+1))}function Zr(e,t){var n="";if(!e||t<1||t>h)return n;do{t%2&&(n+=e),(t=gt(t/2))&&(e+=e)}while(t);return n}function Xr(e,t){return Fi(Li(e,t,ls),e+"")}function Qr(e){return Qn(Wa(e))}function eo(e,t){var n=Wa(e);return ji(n,cr(t,0,n.length))}function to(e,t,n,r){if(!ra(e))return e;for(var i=-1,l=(t=xo(t,e)).length,a=l-1,s=e;null!=s&&++ii?0:i+t),(n=n>i?i:n)<0&&(n+=i),i=t>n?0:n-t>>>0,t>>>=0;for(var l=r(i);++o>>1,l=e[i];null!==l&&!fa(l)&&(n?l<=t:l=200){var c=t?null:Xo(e);if(c)return fn(c);l=!1,o=Qt,s=new Yn}else s=t?[]:a;e:for(;++r=r?e:io(e,t,n)}var Eo=ft||function(e){return dt.clearTimeout(e)};function Co(e,t){if(t)return e.slice();var n=e.length,r=Ge?Ge(n):new e.constructor(n);return e.copy(r),r}function To(e){var t=new e.constructor(e.byteLength);return new ze(t).set(new ze(e)),t}function No(e,t){var n=t?To(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function Lo(e,t){if(e!==t){var n=e!==o,r=null===e,i=e==e,l=fa(e),a=t!==o,s=null===t,c=t==t,u=fa(t);if(!s&&!u&&!l&&e>t||l&&a&&c&&!s&&!u||r&&a&&c||!n&&c||!i)return 1;if(!r&&!l&&!u&&e1?n[i-1]:o,a=i>2?n[2]:o;for(l=e.length>3&&"function"==typeof l?(i--,l):o,a&&xi(n[0],n[1],a)&&(l=i<3?o:l,i=1),t=Te(t);++r-1?i[l?t[a]:a]:o}}function Uo(e){return ii((function(t){var n=t.length,r=n,l=zn.prototype.thru;for(e&&t.reverse();r--;){var a=t[r];if("function"!=typeof a)throw new Oe(i);if(l&&!s&&"wrapper"==ci(a))var s=new zn([],!0)}for(r=s?r:n;++r1&&_.reverse(),d&&us))return!1;var u=l.get(e),f=l.get(t);if(u&&f)return u==t&&f==e;var p=-1,d=!0,h=2&n?new Yn:o;for(l.set(e,t),l.set(t,e);++p-1&&e%1==0&&e1?"& ":"")+t[r],t=t.join(n>2?", ":" "),e.replace(se,"{\n/* [wrapped with "+t+"] */\n")}(r,function(e,t){return Tt(g,(function(n){var r="_."+n[0];t&n[1]&&!At(e,r)&&e.push(r)})),e.sort()}(function(e){var t=e.match(ce);return t?t[1].split(ue):[]}(r),n)))}function Bi(e){var t=0,n=0;return function(){var r=xn(),i=16-(r-n);if(n=r,i>0){if(++t>=800)return arguments[0]}else t=0;return e.apply(o,arguments)}}function ji(e,t){var n=-1,r=e.length,i=r-1;for(t=t===o?r:t;++n1?e[t-1]:o;return n="function"==typeof n?(e.pop(),n):o,sl(e,n)}));function ml(e){var t=Un(e);return t.__chain__=!0,t}function vl(e,t){return t(e)}var gl=ii((function(e){var t=e.length,n=t?e[0]:0,r=this.__wrapped__,i=function(t){return sr(t,e)};return!(t>1||this.__actions__.length)&&r instanceof Gn&&wi(n)?((r=r.slice(n,+n+(t?1:0))).__actions__.push({func:vl,args:[i],thisArg:o}),new zn(r,this.__chain__).thru((function(e){return t&&!e.length&&e.push(o),e}))):this.thru(i)}));var yl=Ro((function(e,t,n){Me.call(e,n)?++e[n]:ar(e,n,1)}));var _l=$o(Gi),bl=$o(qi);function wl(e,t){return(ql(e)?Tt:hr)(e,fi(t,3))}function xl(e,t){return(ql(e)?Nt:mr)(e,fi(t,3))}var kl=Ro((function(e,t,n){Me.call(e,n)?e[n].push(t):ar(e,n,[t])}));var Sl=Xr((function(e,t,n){var o=-1,i="function"==typeof t,l=Kl(e)?r(e.length):[];return hr(e,(function(e){l[++o]=i?Et(t,e,n):Pr(e,t,n)})),l})),El=Ro((function(e,t,n){ar(e,n,t)}));function Cl(e,t){return(ql(e)?It:$r)(e,fi(t,3))}var Tl=Ro((function(e,t,n){e[n?0:1].push(t)}),(function(){return[[],[]]}));var Nl=Xr((function(e,t){if(null==e)return[];var n=t.length;return n>1&&xi(e,t[0],t[1])?t=[]:n>2&&xi(t[0],t[1],t[2])&&(t=[t[0]]),Gr(e,_r(t,1),[])})),Ll=pt||function(){return dt.Date.now()};function Ol(e,t,n){return t=n?o:t,t=e&&null==t?e.length:t,ei(e,f,o,o,o,o,t)}function Al(e,t){var n;if("function"!=typeof t)throw new Oe(i);return e=ga(e),function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=o),n}}var Pl=Xr((function(e,t,n){var r=1;if(n.length){var o=un(n,ui(Pl));r|=c}return ei(e,r,t,n,o)})),Il=Xr((function(e,t,n){var r=3;if(n.length){var o=un(n,ui(Il));r|=c}return ei(t,r,e,n,o)}));function Rl(e,t,n){var r,l,a,s,c,u,f=0,p=!1,d=!1,h=!0;if("function"!=typeof e)throw new Oe(i);function m(t){var n=r,i=l;return r=l=o,f=t,s=e.apply(i,n)}function v(e){return f=e,c=Ri(y,t),p?m(e):s}function g(e){var n=e-u;return u===o||n>=t||n<0||d&&e-f>=a}function y(){var e=Ll();if(g(e))return _(e);c=Ri(y,function(e){var n=t-(e-u);return d?wn(n,a-(e-f)):n}(e))}function _(e){return c=o,h&&r?m(e):(r=l=o,s)}function b(){var e=Ll(),n=g(e);if(r=arguments,l=this,u=e,n){if(c===o)return v(u);if(d)return Eo(c),c=Ri(y,t),m(u)}return c===o&&(c=Ri(y,t)),s}return t=_a(t)||0,ra(n)&&(p=!!n.leading,a=(d="maxWait"in n)?bn(_a(n.maxWait)||0,t):a,h="trailing"in n?!!n.trailing:h),b.cancel=function(){c!==o&&Eo(c),f=0,r=u=l=c=o},b.flush=function(){return c===o?s:_(Ll())},b}var Fl=Xr((function(e,t){return pr(e,1,t)})),Ml=Xr((function(e,t,n){return pr(e,_a(t)||0,n)}));function Bl(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new Oe(i);var n=function(){var r=arguments,o=t?t.apply(this,r):r[0],i=n.cache;if(i.has(o))return i.get(o);var l=e.apply(this,r);return n.cache=i.set(o,l)||i,l};return n.cache=new(Bl.Cache||Kn),n}function jl(e){if("function"!=typeof e)throw new Oe(i);return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}Bl.Cache=Kn;var Vl=ko((function(e,t){var n=(t=1==t.length&&ql(t[0])?It(t[0],Zt(fi())):It(_r(t,1),Zt(fi()))).length;return Xr((function(r){for(var o=-1,i=wn(r.length,n);++o=t})),Gl=Ir(function(){return arguments}())?Ir:function(e){return oa(e)&&Me.call(e,"callee")&&!Ke.call(e,"callee")},ql=r.isArray,Jl=_t?Zt(_t):function(e){return oa(e)&&Tr(e)==R};function Kl(e){return null!=e&&na(e.length)&&!ea(e)}function Yl(e){return oa(e)&&Kl(e)}var Zl=jt||_s,Xl=bt?Zt(bt):function(e){return oa(e)&&Tr(e)==w};function Ql(e){if(!oa(e))return!1;var t=Tr(e);return t==x||"[object DOMException]"==t||"string"==typeof e.message&&"string"==typeof e.name&&!aa(e)}function ea(e){if(!ra(e))return!1;var t=Tr(e);return t==k||t==S||"[object AsyncFunction]"==t||"[object Proxy]"==t}function ta(e){return"number"==typeof e&&e==ga(e)}function na(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=h}function ra(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function oa(e){return null!=e&&"object"==typeof e}var ia=wt?Zt(wt):function(e){return oa(e)&&gi(e)==E};function la(e){return"number"==typeof e||oa(e)&&Tr(e)==C}function aa(e){if(!oa(e)||Tr(e)!=T)return!1;var t=qe(e);if(null===t)return!0;var n=Me.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&Fe.call(n)==De}var sa=xt?Zt(xt):function(e){return oa(e)&&Tr(e)==L};var ca=kt?Zt(kt):function(e){return oa(e)&&gi(e)==O};function ua(e){return"string"==typeof e||!ql(e)&&oa(e)&&Tr(e)==A}function fa(e){return"symbol"==typeof e||oa(e)&&Tr(e)==P}var pa=St?Zt(St):function(e){return oa(e)&&na(e.length)&&!!lt[Tr(e)]};var da=Ko(Dr),ha=Ko((function(e,t){return e<=t}));function ma(e){if(!e)return[];if(Kl(e))return ua(e)?hn(e):Po(e);if(et&&e[et])return function(e){for(var t,n=[];!(t=e.next()).done;)n.push(t.value);return n}(e[et]());var t=gi(e);return(t==E?sn:t==O?fn:Wa)(e)}function va(e){return e?(e=_a(e))===d||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0}function ga(e){var t=va(e),n=t%1;return t==t?n?t-n:t:0}function ya(e){return e?cr(ga(e),0,v):0}function _a(e){if("number"==typeof e)return e;if(fa(e))return m;if(ra(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=ra(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=Yt(e);var n=ge.test(e);return n||_e.test(e)?ut(e.slice(2),n?2:8):ve.test(e)?m:+e}function ba(e){return Io(e,Fa(e))}function wa(e){return null==e?"":fo(e)}var xa=Fo((function(e,t){if(Ci(t)||Kl(t))Io(t,Ra(t),e);else for(var n in t)Me.call(t,n)&&rr(e,n,t[n])})),ka=Fo((function(e,t){Io(t,Fa(t),e)})),Sa=Fo((function(e,t,n,r){Io(t,Fa(t),e,r)})),Ea=Fo((function(e,t,n,r){Io(t,Ra(t),e,r)})),Ca=ii(sr);var Ta=Xr((function(e,t){e=Te(e);var n=-1,r=t.length,i=r>2?t[2]:o;for(i&&xi(t[0],t[1],i)&&(r=1);++n1),t})),Io(e,ai(e),n),r&&(n=ur(n,7,ri));for(var o=t.length;o--;)ho(n,t[o]);return n}));var Va=ii((function(e,t){return null==e?{}:function(e,t){return qr(e,t,(function(t,n){return Oa(e,n)}))}(e,t)}));function Da(e,t){if(null==e)return{};var n=It(ai(e),(function(e){return[e]}));return t=fi(t),qr(e,n,(function(e,n){return t(e,n[0])}))}var $a=Qo(Ra),Ua=Qo(Fa);function Wa(e){return null==e?[]:Xt(e,Ra(e))}var Ha=Vo((function(e,t,n){return t=t.toLowerCase(),e+(n?za(t):t)}));function za(e){return Qa(wa(e).toLowerCase())}function Ga(e){return(e=wa(e))&&e.replace(we,rn).replace(Qe,"")}var qa=Vo((function(e,t,n){return e+(n?"-":"")+t.toLowerCase()})),Ja=Vo((function(e,t,n){return e+(n?" ":"")+t.toLowerCase()})),Ka=jo("toLowerCase");var Ya=Vo((function(e,t,n){return e+(n?"_":"")+t.toLowerCase()}));var Za=Vo((function(e,t,n){return e+(n?" ":"")+Qa(t)}));var Xa=Vo((function(e,t,n){return e+(n?" ":"")+t.toUpperCase()})),Qa=jo("toUpperCase");function es(e,t,n){return e=wa(e),(t=n?o:t)===o?function(e){return rt.test(e)}(e)?function(e){return e.match(tt)||[]}(e):function(e){return e.match(fe)||[]}(e):e.match(t)||[]}var ts=Xr((function(e,t){try{return Et(e,o,t)}catch(e){return Ql(e)?e:new Se(e)}})),ns=ii((function(e,t){return Tt(t,(function(t){t=Di(t),ar(e,t,Pl(e[t],e))})),e}));function rs(e){return function(){return e}}var os=Uo(),is=Uo(!0);function ls(e){return e}function as(e){return Br("function"==typeof e?e:ur(e,1))}var ss=Xr((function(e,t){return function(n){return Pr(n,e,t)}})),cs=Xr((function(e,t){return function(n){return Pr(e,n,t)}}));function us(e,t,n){var r=Ra(t),o=Sr(t,r);null!=n||ra(t)&&(o.length||!r.length)||(n=t,t=e,e=this,o=Sr(t,Ra(t)));var i=!(ra(n)&&"chain"in n&&!n.chain),l=ea(e);return Tt(o,(function(n){var r=t[n];e[n]=r,l&&(e.prototype[n]=function(){var t=this.__chain__;if(i||t){var n=e(this.__wrapped__),o=n.__actions__=Po(this.__actions__);return o.push({func:r,args:arguments,thisArg:e}),n.__chain__=t,n}return r.apply(e,Rt([this.value()],arguments))})})),e}function fs(){}var ps=Go(It),ds=Go(Lt),hs=Go(Bt);function ms(e){return ki(e)?zt(Di(e)):function(e){return function(t){return Er(t,e)}}(e)}var vs=Jo(),gs=Jo(!0);function ys(){return[]}function _s(){return!1}var bs=zo((function(e,t){return e+t}),0),ws=Zo("ceil"),xs=zo((function(e,t){return e/t}),1),ks=Zo("floor");var Ss,Es=zo((function(e,t){return e*t}),1),Cs=Zo("round"),Ts=zo((function(e,t){return e-t}),0);return Un.after=function(e,t){if("function"!=typeof t)throw new Oe(i);return e=ga(e),function(){if(--e<1)return t.apply(this,arguments)}},Un.ary=Ol,Un.assign=xa,Un.assignIn=ka,Un.assignInWith=Sa,Un.assignWith=Ea,Un.at=Ca,Un.before=Al,Un.bind=Pl,Un.bindAll=ns,Un.bindKey=Il,Un.castArray=function(){if(!arguments.length)return[];var e=arguments[0];return ql(e)?e:[e]},Un.chain=ml,Un.chunk=function(e,t,n){t=(n?xi(e,t,n):t===o)?1:bn(ga(t),0);var i=null==e?0:e.length;if(!i||t<1)return[];for(var l=0,a=0,s=r(mt(i/t));li?0:i+n),(r=r===o||r>i?i:ga(r))<0&&(r+=i),r=n>r?0:ya(r);n>>0)?(e=wa(e))&&("string"==typeof t||null!=t&&!sa(t))&&!(t=fo(t))&&an(e)?So(hn(e),0,n):e.split(t,n):[]},Un.spread=function(e,t){if("function"!=typeof e)throw new Oe(i);return t=null==t?0:bn(ga(t),0),Xr((function(n){var r=n[t],o=So(n,0,t);return r&&Rt(o,r),Et(e,this,o)}))},Un.tail=function(e){var t=null==e?0:e.length;return t?io(e,1,t):[]},Un.take=function(e,t,n){return e&&e.length?io(e,0,(t=n||t===o?1:ga(t))<0?0:t):[]},Un.takeRight=function(e,t,n){var r=null==e?0:e.length;return r?io(e,(t=r-(t=n||t===o?1:ga(t)))<0?0:t,r):[]},Un.takeRightWhile=function(e,t){return e&&e.length?vo(e,fi(t,3),!1,!0):[]},Un.takeWhile=function(e,t){return e&&e.length?vo(e,fi(t,3)):[]},Un.tap=function(e,t){return t(e),e},Un.throttle=function(e,t,n){var r=!0,o=!0;if("function"!=typeof e)throw new Oe(i);return ra(n)&&(r="leading"in n?!!n.leading:r,o="trailing"in n?!!n.trailing:o),Rl(e,t,{leading:r,maxWait:t,trailing:o})},Un.thru=vl,Un.toArray=ma,Un.toPairs=$a,Un.toPairsIn=Ua,Un.toPath=function(e){return ql(e)?It(e,Di):fa(e)?[e]:Po(Vi(wa(e)))},Un.toPlainObject=ba,Un.transform=function(e,t,n){var r=ql(e),o=r||Zl(e)||pa(e);if(t=fi(t,4),null==n){var i=e&&e.constructor;n=o?r?new i:[]:ra(e)&&ea(i)?Wn(qe(e)):{}}return(o?Tt:xr)(e,(function(e,r,o){return t(n,e,r,o)})),n},Un.unary=function(e){return Ol(e,1)},Un.union=ol,Un.unionBy=il,Un.unionWith=ll,Un.uniq=function(e){return e&&e.length?po(e):[]},Un.uniqBy=function(e,t){return e&&e.length?po(e,fi(t,2)):[]},Un.uniqWith=function(e,t){return t="function"==typeof t?t:o,e&&e.length?po(e,o,t):[]},Un.unset=function(e,t){return null==e||ho(e,t)},Un.unzip=al,Un.unzipWith=sl,Un.update=function(e,t,n){return null==e?e:mo(e,t,wo(n))},Un.updateWith=function(e,t,n,r){return r="function"==typeof r?r:o,null==e?e:mo(e,t,wo(n),r)},Un.values=Wa,Un.valuesIn=function(e){return null==e?[]:Xt(e,Fa(e))},Un.without=cl,Un.words=es,Un.wrap=function(e,t){return Dl(wo(t),e)},Un.xor=ul,Un.xorBy=fl,Un.xorWith=pl,Un.zip=dl,Un.zipObject=function(e,t){return _o(e||[],t||[],rr)},Un.zipObjectDeep=function(e,t){return _o(e||[],t||[],to)},Un.zipWith=hl,Un.entries=$a,Un.entriesIn=Ua,Un.extend=ka,Un.extendWith=Sa,us(Un,Un),Un.add=bs,Un.attempt=ts,Un.camelCase=Ha,Un.capitalize=za,Un.ceil=ws,Un.clamp=function(e,t,n){return n===o&&(n=t,t=o),n!==o&&(n=(n=_a(n))==n?n:0),t!==o&&(t=(t=_a(t))==t?t:0),cr(_a(e),t,n)},Un.clone=function(e){return ur(e,4)},Un.cloneDeep=function(e){return ur(e,5)},Un.cloneDeepWith=function(e,t){return ur(e,5,t="function"==typeof t?t:o)},Un.cloneWith=function(e,t){return ur(e,4,t="function"==typeof t?t:o)},Un.conformsTo=function(e,t){return null==t||fr(e,t,Ra(t))},Un.deburr=Ga,Un.defaultTo=function(e,t){return null==e||e!=e?t:e},Un.divide=xs,Un.endsWith=function(e,t,n){e=wa(e),t=fo(t);var r=e.length,i=n=n===o?r:cr(ga(n),0,r);return(n-=t.length)>=0&&e.slice(n,i)==t},Un.eq=Wl,Un.escape=function(e){return(e=wa(e))&&Z.test(e)?e.replace(K,on):e},Un.escapeRegExp=function(e){return(e=wa(e))&&ie.test(e)?e.replace(oe,"\\$&"):e},Un.every=function(e,t,n){var r=ql(e)?Lt:vr;return n&&xi(e,t,n)&&(t=o),r(e,fi(t,3))},Un.find=_l,Un.findIndex=Gi,Un.findKey=function(e,t){return Vt(e,fi(t,3),xr)},Un.findLast=bl,Un.findLastIndex=qi,Un.findLastKey=function(e,t){return Vt(e,fi(t,3),kr)},Un.floor=ks,Un.forEach=wl,Un.forEachRight=xl,Un.forIn=function(e,t){return null==e?e:br(e,fi(t,3),Fa)},Un.forInRight=function(e,t){return null==e?e:wr(e,fi(t,3),Fa)},Un.forOwn=function(e,t){return e&&xr(e,fi(t,3))},Un.forOwnRight=function(e,t){return e&&kr(e,fi(t,3))},Un.get=La,Un.gt=Hl,Un.gte=zl,Un.has=function(e,t){return null!=e&&yi(e,t,Lr)},Un.hasIn=Oa,Un.head=Ki,Un.identity=ls,Un.includes=function(e,t,n,r){e=Kl(e)?e:Wa(e),n=n&&!r?ga(n):0;var o=e.length;return n<0&&(n=bn(o+n,0)),ua(e)?n<=o&&e.indexOf(t,n)>-1:!!o&&$t(e,t,n)>-1},Un.indexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var o=null==n?0:ga(n);return o<0&&(o=bn(r+o,0)),$t(e,t,o)},Un.inRange=function(e,t,n){return t=va(t),n===o?(n=t,t=0):n=va(n),function(e,t,n){return e>=wn(t,n)&&e=-9007199254740991&&e<=h},Un.isSet=ca,Un.isString=ua,Un.isSymbol=fa,Un.isTypedArray=pa,Un.isUndefined=function(e){return e===o},Un.isWeakMap=function(e){return oa(e)&&gi(e)==I},Un.isWeakSet=function(e){return oa(e)&&"[object WeakSet]"==Tr(e)},Un.join=function(e,t){return null==e?"":yn.call(e,t)},Un.kebabCase=qa,Un.last=Qi,Un.lastIndexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var i=r;return n!==o&&(i=(i=ga(n))<0?bn(r+i,0):wn(i,r-1)),t==t?function(e,t,n){for(var r=n+1;r--;)if(e[r]===t)return r;return r}(e,t,i):Dt(e,Wt,i,!0)},Un.lowerCase=Ja,Un.lowerFirst=Ka,Un.lt=da,Un.lte=ha,Un.max=function(e){return e&&e.length?gr(e,ls,Nr):o},Un.maxBy=function(e,t){return e&&e.length?gr(e,fi(t,2),Nr):o},Un.mean=function(e){return Ht(e,ls)},Un.meanBy=function(e,t){return Ht(e,fi(t,2))},Un.min=function(e){return e&&e.length?gr(e,ls,Dr):o},Un.minBy=function(e,t){return e&&e.length?gr(e,fi(t,2),Dr):o},Un.stubArray=ys,Un.stubFalse=_s,Un.stubObject=function(){return{}},Un.stubString=function(){return""},Un.stubTrue=function(){return!0},Un.multiply=Es,Un.nth=function(e,t){return e&&e.length?zr(e,ga(t)):o},Un.noConflict=function(){return dt._===this&&(dt._=$e),this},Un.noop=fs,Un.now=Ll,Un.pad=function(e,t,n){e=wa(e);var r=(t=ga(t))?dn(e):0;if(!t||r>=t)return e;var o=(t-r)/2;return qo(gt(o),n)+e+qo(mt(o),n)},Un.padEnd=function(e,t,n){e=wa(e);var r=(t=ga(t))?dn(e):0;return t&&rt){var r=e;e=t,t=r}if(n||e%1||t%1){var i=Sn();return wn(e+i*(t-e+ct("1e-"+((i+"").length-1))),t)}return Yr(e,t)},Un.reduce=function(e,t,n){var r=ql(e)?Ft:qt,o=arguments.length<3;return r(e,fi(t,4),n,o,hr)},Un.reduceRight=function(e,t,n){var r=ql(e)?Mt:qt,o=arguments.length<3;return r(e,fi(t,4),n,o,mr)},Un.repeat=function(e,t,n){return t=(n?xi(e,t,n):t===o)?1:ga(t),Zr(wa(e),t)},Un.replace=function(){var e=arguments,t=wa(e[0]);return e.length<3?t:t.replace(e[1],e[2])},Un.result=function(e,t,n){var r=-1,i=(t=xo(t,e)).length;for(i||(i=1,e=o);++rh)return[];var n=v,r=wn(e,v);t=fi(t),e-=v;for(var o=Kt(r,t);++n=l)return e;var s=n-dn(r);if(s<1)return r;var c=a?So(a,0,s).join(""):e.slice(0,s);if(i===o)return c+r;if(a&&(s+=c.length-s),sa(i)){if(e.slice(s).search(i)){var u,f=c;for(i.global||(i=Ne(i.source,wa(me.exec(i))+"g")),i.lastIndex=0;u=i.exec(f);)var p=u.index;c=c.slice(0,p===o?s:p)}}else if(e.indexOf(fo(i),s)!=s){var d=c.lastIndexOf(i);d>-1&&(c=c.slice(0,d))}return c+r},Un.unescape=function(e){return(e=wa(e))&&Y.test(e)?e.replace(J,vn):e},Un.uniqueId=function(e){var t=++Be;return wa(e)+t},Un.upperCase=Xa,Un.upperFirst=Qa,Un.each=wl,Un.eachRight=xl,Un.first=Ki,us(Un,(Ss={},xr(Un,(function(e,t){Me.call(Un.prototype,t)||(Ss[t]=e)})),Ss),{chain:!1}),Un.VERSION="4.17.21",Tt(["bind","bindKey","curry","curryRight","partial","partialRight"],(function(e){Un[e].placeholder=Un})),Tt(["drop","take"],(function(e,t){Gn.prototype[e]=function(n){n=n===o?1:bn(ga(n),0);var r=this.__filtered__&&!t?new Gn(this):this.clone();return r.__filtered__?r.__takeCount__=wn(n,r.__takeCount__):r.__views__.push({size:wn(n,v),type:e+(r.__dir__<0?"Right":"")}),r},Gn.prototype[e+"Right"]=function(t){return this.reverse()[e](t).reverse()}})),Tt(["filter","map","takeWhile"],(function(e,t){var n=t+1,r=1==n||3==n;Gn.prototype[e]=function(e){var t=this.clone();return t.__iteratees__.push({iteratee:fi(e,3),type:n}),t.__filtered__=t.__filtered__||r,t}})),Tt(["head","last"],(function(e,t){var n="take"+(t?"Right":"");Gn.prototype[e]=function(){return this[n](1).value()[0]}})),Tt(["initial","tail"],(function(e,t){var n="drop"+(t?"":"Right");Gn.prototype[e]=function(){return this.__filtered__?new Gn(this):this[n](1)}})),Gn.prototype.compact=function(){return this.filter(ls)},Gn.prototype.find=function(e){return this.filter(e).head()},Gn.prototype.findLast=function(e){return this.reverse().find(e)},Gn.prototype.invokeMap=Xr((function(e,t){return"function"==typeof e?new Gn(this):this.map((function(n){return Pr(n,e,t)}))})),Gn.prototype.reject=function(e){return this.filter(jl(fi(e)))},Gn.prototype.slice=function(e,t){e=ga(e);var n=this;return n.__filtered__&&(e>0||t<0)?new Gn(n):(e<0?n=n.takeRight(-e):e&&(n=n.drop(e)),t!==o&&(n=(t=ga(t))<0?n.dropRight(-t):n.take(t-e)),n)},Gn.prototype.takeRightWhile=function(e){return this.reverse().takeWhile(e).reverse()},Gn.prototype.toArray=function(){return this.take(v)},xr(Gn.prototype,(function(e,t){var n=/^(?:filter|find|map|reject)|While$/.test(t),r=/^(?:head|last)$/.test(t),i=Un[r?"take"+("last"==t?"Right":""):t],l=r||/^find/.test(t);i&&(Un.prototype[t]=function(){var t=this.__wrapped__,a=r?[1]:arguments,s=t instanceof Gn,c=a[0],u=s||ql(t),f=function(e){var t=i.apply(Un,Rt([e],a));return r&&p?t[0]:t};u&&n&&"function"==typeof c&&1!=c.length&&(s=u=!1);var p=this.__chain__,d=!!this.__actions__.length,h=l&&!p,m=s&&!d;if(!l&&u){t=m?t:new Gn(this);var v=e.apply(t,a);return v.__actions__.push({func:vl,args:[f],thisArg:o}),new zn(v,p)}return h&&m?e.apply(this,a):(v=this.thru(f),h?r?v.value()[0]:v.value():v)})})),Tt(["pop","push","shift","sort","splice","unshift"],(function(e){var t=Ae[e],n=/^(?:push|sort|unshift)$/.test(e)?"tap":"thru",r=/^(?:pop|shift)$/.test(e);Un.prototype[e]=function(){var e=arguments;if(r&&!this.__chain__){var o=this.value();return t.apply(ql(o)?o:[],e)}return this[n]((function(n){return t.apply(ql(n)?n:[],e)}))}})),xr(Gn.prototype,(function(e,t){var n=Un[t];if(n){var r=n.name+"";Me.call(In,r)||(In[r]=[]),In[r].push({name:t,func:n})}})),In[Wo(o,2).name]=[{name:"wrapper",func:o}],Gn.prototype.clone=function(){var e=new Gn(this.__wrapped__);return e.__actions__=Po(this.__actions__),e.__dir__=this.__dir__,e.__filtered__=this.__filtered__,e.__iteratees__=Po(this.__iteratees__),e.__takeCount__=this.__takeCount__,e.__views__=Po(this.__views__),e},Gn.prototype.reverse=function(){if(this.__filtered__){var e=new Gn(this);e.__dir__=-1,e.__filtered__=!0}else(e=this.clone()).__dir__*=-1;return e},Gn.prototype.value=function(){var e=this.__wrapped__.value(),t=this.__dir__,n=ql(e),r=t<0,o=n?e.length:0,i=function(e,t,n){var r=-1,o=n.length;for(;++r=this.__values__.length;return{done:e,value:e?o:this.__values__[this.__index__++]}},Un.prototype.plant=function(e){for(var t,n=this;n instanceof Hn;){var r=Ui(n);r.__index__=0,r.__values__=o,t?i.__wrapped__=r:t=r;var i=r;n=n.__wrapped__}return i.__wrapped__=e,t},Un.prototype.reverse=function(){var e=this.__wrapped__;if(e instanceof Gn){var t=e;return this.__actions__.length&&(t=new Gn(this)),(t=t.reverse()).__actions__.push({func:vl,args:[rl],thisArg:o}),new zn(t,this.__chain__)}return this.thru(rl)},Un.prototype.toJSON=Un.prototype.valueOf=Un.prototype.value=function(){return go(this.__wrapped__,this.__actions__)},Un.prototype.first=Un.prototype.head,et&&(Un.prototype[et]=function(){return this}),Un}();dt._=gn,(r=function(){return gn}.call(t,n,t,e))===o||(e.exports=r)}.call(this)},423:()=>{},155:e=>{var t,n,r=e.exports={};function o(){throw new Error("setTimeout has not been defined")}function i(){throw new Error("clearTimeout has not been defined")}function l(e){if(t===setTimeout)return setTimeout(e,0);if((t===o||!t)&&setTimeout)return t=setTimeout,setTimeout(e,0);try{return t(e,0)}catch(n){try{return t.call(null,e,0)}catch(n){return t.call(this,e,0)}}}!function(){try{t="function"==typeof setTimeout?setTimeout:o}catch(e){t=o}try{n="function"==typeof clearTimeout?clearTimeout:i}catch(e){n=i}}();var a,s=[],c=!1,u=-1;function f(){c&&a&&(c=!1,a.length?s=a.concat(s):u=-1,s.length&&p())}function p(){if(!c){var e=l(f);c=!0;for(var t=s.length;t;){for(a=s,s=[];++u1)for(var n=1;n{"use strict";var r,o=function(){return void 0===r&&(r=Boolean(window&&document&&document.all&&!window.atob)),r},i=function(){var e={};return function(t){if(void 0===e[t]){var n=document.querySelector(t);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}e[t]=n}return e[t]}}(),l=[];function a(e){for(var t=-1,n=0;n{"use strict";n.r(t),n.d(t,{BaseTransition:()=>sr,Comment:()=>Zo,EffectScope:()=>se,Fragment:()=>Ko,KeepAlive:()=>br,ReactiveEffect:()=>xe,Static:()=>Xo,Suspense:()=>Wn,Teleport:()=>qo,Text:()=>Yo,Transition:()=>Vl,TransitionGroup:()=>ra,VueElement:()=>Pl,callWithAsyncErrorHandling:()=>tn,callWithErrorHandling:()=>en,camelize:()=>Z,capitalize:()=>ee,cloneVNode:()=>_i,compatUtils:()=>hl,compile:()=>Tf,computed:()=>Ji,createApp:()=>Fa,createBlock:()=>si,createCommentVNode:()=>xi,createElementBlock:()=>ai,createElementVNode:()=>mi,createHydrationRenderer:()=>Vo,createPropsRestProxy:()=>rl,createRenderer:()=>jo,createSSRApp:()=>Ma,createSlots:()=>Yr,createStaticVNode:()=>wi,createTextVNode:()=>bi,createVNode:()=>vi,customRef:()=>qt,defineAsyncComponent:()=>gr,defineComponent:()=>mr,defineCustomElement:()=>Ll,defineEmits:()=>Yi,defineExpose:()=>Zi,defineProps:()=>Ki,defineSSRCustomElement:()=>Ol,devtools:()=>xn,effect:()=>Se,effectScope:()=>ce,getCurrentInstance:()=>Pi,getCurrentScope:()=>fe,getTransitionRawChildren:()=>hr,guardReactiveProps:()=>yi,h:()=>il,handleError:()=>nn,hydrate:()=>Ra,initCustomFormatter:()=>sl,initDirectivesForSSR:()=>Va,inject:()=>Yn,isMemoSame:()=>ul,isProxy:()=>Ot,isReactive:()=>Tt,isReadonly:()=>Nt,isRef:()=>Bt,isRuntimeOnly:()=>Ui,isShallow:()=>Lt,isVNode:()=>ci,markRaw:()=>Pt,mergeDefaults:()=>nl,mergeProps:()=>Ci,nextTick:()=>dn,normalizeClass:()=>f,normalizeProps:()=>p,normalizeStyle:()=>l,onActivated:()=>xr,onBeforeMount:()=>Or,onBeforeUnmount:()=>Rr,onBeforeUpdate:()=>Pr,onDeactivated:()=>kr,onErrorCaptured:()=>Vr,onMounted:()=>Ar,onRenderTracked:()=>jr,onRenderTriggered:()=>Br,onScopeDispose:()=>pe,onServerPrefetch:()=>Mr,onUnmounted:()=>Fr,onUpdated:()=>Ir,openBlock:()=>ti,popScopeId:()=>In,provide:()=>Kn,proxyRefs:()=>zt,pushScopeId:()=>Pn,queuePostFlushCb:()=>vn,reactive:()=>xt,readonly:()=>St,ref:()=>jt,registerRuntimeCompiler:()=>$i,render:()=>Ia,renderList:()=>Kr,renderSlot:()=>Zr,resolveComponent:()=>Wr,resolveDirective:()=>Gr,resolveDynamicComponent:()=>zr,resolveFilter:()=>dl,resolveTransitionHooks:()=>ur,setBlockTracking:()=>ii,setDevtoolsHook:()=>En,setTransitionHooks:()=>dr,shallowReactive:()=>kt,shallowReadonly:()=>Et,shallowRef:()=>Vt,ssrContextKey:()=>ll,ssrUtils:()=>pl,stop:()=>Ee,toDisplayString:()=>w,toHandlerKey:()=>te,toHandlers:()=>Qr,toRaw:()=>At,toRef:()=>Yt,toRefs:()=>Jt,transformVNodeArgs:()=>fi,triggerRef:()=>Ut,unref:()=>Wt,useAttrs:()=>el,useCssModule:()=>Il,useCssVars:()=>Rl,useSSRContext:()=>al,useSlots:()=>Qi,useTransitionState:()=>lr,vModelCheckbox:()=>fa,vModelDynamic:()=>ya,vModelRadio:()=>da,vModelSelect:()=>ha,vModelText:()=>ua,vShow:()=>Ca,version:()=>fl,warn:()=>Qt,watch:()=>tr,watchEffect:()=>Zn,watchPostEffect:()=>Xn,watchSyncEffect:()=>Qn,withAsyncContext:()=>ol,withCtx:()=>Fn,withDefaults:()=>Xi,withDirectives:()=>Dr,withKeys:()=>Ea,withMemo:()=>cl,withModifiers:()=>ka,withScopeId:()=>Rn});var r={};function o(e,t){const n=Object.create(null),r=e.split(",");for(let e=0;e!!n[e.toLowerCase()]:e=>!!n[e]}n.r(r),n.d(r,{BaseTransition:()=>sr,Comment:()=>Zo,EffectScope:()=>se,Fragment:()=>Ko,KeepAlive:()=>br,ReactiveEffect:()=>xe,Static:()=>Xo,Suspense:()=>Wn,Teleport:()=>qo,Text:()=>Yo,Transition:()=>Vl,TransitionGroup:()=>ra,VueElement:()=>Pl,callWithAsyncErrorHandling:()=>tn,callWithErrorHandling:()=>en,camelize:()=>Z,capitalize:()=>ee,cloneVNode:()=>_i,compatUtils:()=>hl,computed:()=>Ji,createApp:()=>Fa,createBlock:()=>si,createCommentVNode:()=>xi,createElementBlock:()=>ai,createElementVNode:()=>mi,createHydrationRenderer:()=>Vo,createPropsRestProxy:()=>rl,createRenderer:()=>jo,createSSRApp:()=>Ma,createSlots:()=>Yr,createStaticVNode:()=>wi,createTextVNode:()=>bi,createVNode:()=>vi,customRef:()=>qt,defineAsyncComponent:()=>gr,defineComponent:()=>mr,defineCustomElement:()=>Ll,defineEmits:()=>Yi,defineExpose:()=>Zi,defineProps:()=>Ki,defineSSRCustomElement:()=>Ol,devtools:()=>xn,effect:()=>Se,effectScope:()=>ce,getCurrentInstance:()=>Pi,getCurrentScope:()=>fe,getTransitionRawChildren:()=>hr,guardReactiveProps:()=>yi,h:()=>il,handleError:()=>nn,hydrate:()=>Ra,initCustomFormatter:()=>sl,initDirectivesForSSR:()=>Va,inject:()=>Yn,isMemoSame:()=>ul,isProxy:()=>Ot,isReactive:()=>Tt,isReadonly:()=>Nt,isRef:()=>Bt,isRuntimeOnly:()=>Ui,isShallow:()=>Lt,isVNode:()=>ci,markRaw:()=>Pt,mergeDefaults:()=>nl,mergeProps:()=>Ci,nextTick:()=>dn,normalizeClass:()=>f,normalizeProps:()=>p,normalizeStyle:()=>l,onActivated:()=>xr,onBeforeMount:()=>Or,onBeforeUnmount:()=>Rr,onBeforeUpdate:()=>Pr,onDeactivated:()=>kr,onErrorCaptured:()=>Vr,onMounted:()=>Ar,onRenderTracked:()=>jr,onRenderTriggered:()=>Br,onScopeDispose:()=>pe,onServerPrefetch:()=>Mr,onUnmounted:()=>Fr,onUpdated:()=>Ir,openBlock:()=>ti,popScopeId:()=>In,provide:()=>Kn,proxyRefs:()=>zt,pushScopeId:()=>Pn,queuePostFlushCb:()=>vn,reactive:()=>xt,readonly:()=>St,ref:()=>jt,registerRuntimeCompiler:()=>$i,render:()=>Ia,renderList:()=>Kr,renderSlot:()=>Zr,resolveComponent:()=>Wr,resolveDirective:()=>Gr,resolveDynamicComponent:()=>zr,resolveFilter:()=>dl,resolveTransitionHooks:()=>ur,setBlockTracking:()=>ii,setDevtoolsHook:()=>En,setTransitionHooks:()=>dr,shallowReactive:()=>kt,shallowReadonly:()=>Et,shallowRef:()=>Vt,ssrContextKey:()=>ll,ssrUtils:()=>pl,stop:()=>Ee,toDisplayString:()=>w,toHandlerKey:()=>te,toHandlers:()=>Qr,toRaw:()=>At,toRef:()=>Yt,toRefs:()=>Jt,transformVNodeArgs:()=>fi,triggerRef:()=>Ut,unref:()=>Wt,useAttrs:()=>el,useCssModule:()=>Il,useCssVars:()=>Rl,useSSRContext:()=>al,useSlots:()=>Qi,useTransitionState:()=>lr,vModelCheckbox:()=>fa,vModelDynamic:()=>ya,vModelRadio:()=>da,vModelSelect:()=>ha,vModelText:()=>ua,vShow:()=>Ca,version:()=>fl,warn:()=>Qt,watch:()=>tr,watchEffect:()=>Zn,watchPostEffect:()=>Xn,watchSyncEffect:()=>Qn,withAsyncContext:()=>ol,withCtx:()=>Fn,withDefaults:()=>Xi,withDirectives:()=>Dr,withKeys:()=>Ea,withMemo:()=>cl,withModifiers:()=>ka,withScopeId:()=>Rn});const i=o("Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt");function l(e){if(R(e)){const t={};for(let n=0;n{if(e){const n=e.split(s);n.length>1&&(t[n[0].trim()]=n[1].trim())}})),t}function f(e){let t="";if(V(e))t=e;else if(R(e))for(let n=0;n_(e,t)))}const w=e=>V(e)?e:null==e?"":R(e)||$(e)&&(e.toString===W||!j(e.toString))?JSON.stringify(e,x,2):String(e),x=(e,t)=>t&&t.__v_isRef?x(e,t.value):F(t)?{[`Map(${t.size})`]:[...t.entries()].reduce(((e,[t,n])=>(e[`${t} =>`]=n,e)),{})}:M(t)?{[`Set(${t.size})`]:[...t.values()]}:!$(t)||R(t)||z(t)?t:String(t),k={},S=[],E=()=>{},C=()=>!1,T=/^on[^a-z]/,N=e=>T.test(e),L=e=>e.startsWith("onUpdate:"),O=Object.assign,A=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},P=Object.prototype.hasOwnProperty,I=(e,t)=>P.call(e,t),R=Array.isArray,F=e=>"[object Map]"===H(e),M=e=>"[object Set]"===H(e),B=e=>"[object Date]"===H(e),j=e=>"function"==typeof e,V=e=>"string"==typeof e,D=e=>"symbol"==typeof e,$=e=>null!==e&&"object"==typeof e,U=e=>$(e)&&j(e.then)&&j(e.catch),W=Object.prototype.toString,H=e=>W.call(e),z=e=>"[object Object]"===H(e),G=e=>V(e)&&"NaN"!==e&&"-"!==e[0]&&""+parseInt(e,10)===e,q=o(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),J=o("bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo"),K=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Y=/-(\w)/g,Z=K((e=>e.replace(Y,((e,t)=>t?t.toUpperCase():"")))),X=/\B([A-Z])/g,Q=K((e=>e.replace(X,"-$1").toLowerCase())),ee=K((e=>e.charAt(0).toUpperCase()+e.slice(1))),te=K((e=>e?`on${ee(e)}`:"")),ne=(e,t)=>!Object.is(e,t),re=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},ie=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let le;let ae;class se{constructor(e=!1){this.detached=e,this.active=!0,this.effects=[],this.cleanups=[],this.parent=ae,!e&&ae&&(this.index=(ae.scopes||(ae.scopes=[])).push(this)-1)}run(e){if(this.active){const t=ae;try{return ae=this,e()}finally{ae=t}}else 0}on(){ae=this}off(){ae=this.parent}stop(e){if(this.active){let t,n;for(t=0,n=this.effects.length;t{const t=new Set(e);return t.w=0,t.n=0,t},he=e=>(e.w&ye)>0,me=e=>(e.n&ye)>0,ve=new WeakMap;let ge=0,ye=1;let _e;const be=Symbol(""),we=Symbol("");class xe{constructor(e,t=null,n){this.fn=e,this.scheduler=t,this.active=!0,this.deps=[],this.parent=void 0,ue(this,n)}run(){if(!this.active)return this.fn();let e=_e,t=Ce;for(;e;){if(e===this)return;e=e.parent}try{return this.parent=_e,_e=this,Ce=!0,ye=1<<++ge,ge<=30?(({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let r=0;r{("length"===n||n>=e)&&a.push(t)}))}else switch(void 0!==n&&a.push(l.get(n)),t){case"add":R(e)?G(n)&&a.push(l.get("length")):(a.push(l.get(be)),F(e)&&a.push(l.get(we)));break;case"delete":R(e)||(a.push(l.get(be)),F(e)&&a.push(l.get(we)));break;case"set":F(e)&&a.push(l.get(be))}if(1===a.length)a[0]&&Ie(a[0]);else{const e=[];for(const t of a)t&&e.push(...t);Ie(de(e))}}function Ie(e,t){const n=R(e)?e:[...e];for(const e of n)e.computed&&Re(e,t);for(const e of n)e.computed||Re(e,t)}function Re(e,t){(e!==_e||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}const Fe=o("__proto__,__v_isRef,__isVue"),Me=new Set(Object.getOwnPropertyNames(Symbol).filter((e=>"arguments"!==e&&"caller"!==e)).map((e=>Symbol[e])).filter(D)),Be=We(),je=We(!1,!0),Ve=We(!0),De=We(!0,!0),$e=Ue();function Ue(){const e={};return["includes","indexOf","lastIndexOf"].forEach((t=>{e[t]=function(...e){const n=At(this);for(let e=0,t=this.length;e{e[t]=function(...e){Ne();const n=At(this)[t].apply(this,e);return Le(),n}})),e}function We(e=!1,t=!1){return function(n,r,o){if("__v_isReactive"===r)return!e;if("__v_isReadonly"===r)return e;if("__v_isShallow"===r)return t;if("__v_raw"===r&&o===(e?t?bt:_t:t?yt:gt).get(n))return n;const i=R(n);if(!e&&i&&I($e,r))return Reflect.get($e,r,o);const l=Reflect.get(n,r,o);return(D(r)?Me.has(r):Fe(r))?l:(e||Oe(n,0,r),t?l:Bt(l)?i&&G(r)?l:l.value:$(l)?e?St(l):xt(l):l)}}function He(e=!1){return function(t,n,r,o){let i=t[n];if(Nt(i)&&Bt(i)&&!Bt(r))return!1;if(!e&&(Lt(r)||Nt(r)||(i=At(i),r=At(r)),!R(t)&&Bt(i)&&!Bt(r)))return i.value=r,!0;const l=R(t)&&G(n)?Number(n)!0,deleteProperty:(e,t)=>!0},qe=O({},ze,{get:je,set:He(!0)}),Je=O({},Ge,{get:De}),Ke=e=>e,Ye=e=>Reflect.getPrototypeOf(e);function Ze(e,t,n=!1,r=!1){const o=At(e=e.__v_raw),i=At(t);n||(t!==i&&Oe(o,0,t),Oe(o,0,i));const{has:l}=Ye(o),a=r?Ke:n?Rt:It;return l.call(o,t)?a(e.get(t)):l.call(o,i)?a(e.get(i)):void(e!==o&&e.get(t))}function Xe(e,t=!1){const n=this.__v_raw,r=At(n),o=At(e);return t||(e!==o&&Oe(r,0,e),Oe(r,0,o)),e===o?n.has(e):n.has(e)||n.has(o)}function Qe(e,t=!1){return e=e.__v_raw,!t&&Oe(At(e),0,be),Reflect.get(e,"size",e)}function et(e){e=At(e);const t=At(this);return Ye(t).has.call(t,e)||(t.add(e),Pe(t,"add",e,e)),this}function tt(e,t){t=At(t);const n=At(this),{has:r,get:o}=Ye(n);let i=r.call(n,e);i||(e=At(e),i=r.call(n,e));const l=o.call(n,e);return n.set(e,t),i?ne(t,l)&&Pe(n,"set",e,t):Pe(n,"add",e,t),this}function nt(e){const t=At(this),{has:n,get:r}=Ye(t);let o=n.call(t,e);o||(e=At(e),o=n.call(t,e));r&&r.call(t,e);const i=t.delete(e);return o&&Pe(t,"delete",e,void 0),i}function rt(){const e=At(this),t=0!==e.size,n=e.clear();return t&&Pe(e,"clear",void 0,void 0),n}function ot(e,t){return function(n,r){const o=this,i=o.__v_raw,l=At(i),a=t?Ke:e?Rt:It;return!e&&Oe(l,0,be),i.forEach(((e,t)=>n.call(r,a(e),a(t),o)))}}function it(e,t,n){return function(...r){const o=this.__v_raw,i=At(o),l=F(i),a="entries"===e||e===Symbol.iterator&&l,s="keys"===e&&l,c=o[e](...r),u=n?Ke:t?Rt:It;return!t&&Oe(i,0,s?we:be),{next(){const{value:e,done:t}=c.next();return t?{value:e,done:t}:{value:a?[u(e[0]),u(e[1])]:u(e),done:t}},[Symbol.iterator](){return this}}}}function lt(e){return function(...t){return"delete"!==e&&this}}function at(){const e={get(e){return Ze(this,e)},get size(){return Qe(this)},has:Xe,add:et,set:tt,delete:nt,clear:rt,forEach:ot(!1,!1)},t={get(e){return Ze(this,e,!1,!0)},get size(){return Qe(this)},has:Xe,add:et,set:tt,delete:nt,clear:rt,forEach:ot(!1,!0)},n={get(e){return Ze(this,e,!0)},get size(){return Qe(this,!0)},has(e){return Xe.call(this,e,!0)},add:lt("add"),set:lt("set"),delete:lt("delete"),clear:lt("clear"),forEach:ot(!0,!1)},r={get(e){return Ze(this,e,!0,!0)},get size(){return Qe(this,!0)},has(e){return Xe.call(this,e,!0)},add:lt("add"),set:lt("set"),delete:lt("delete"),clear:lt("clear"),forEach:ot(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach((o=>{e[o]=it(o,!1,!1),n[o]=it(o,!0,!1),t[o]=it(o,!1,!0),r[o]=it(o,!0,!0)})),[e,n,t,r]}const[st,ct,ut,ft]=at();function pt(e,t){const n=t?e?ft:ut:e?ct:st;return(t,r,o)=>"__v_isReactive"===r?!e:"__v_isReadonly"===r?e:"__v_raw"===r?t:Reflect.get(I(n,r)&&r in t?n:t,r,o)}const dt={get:pt(!1,!1)},ht={get:pt(!1,!0)},mt={get:pt(!0,!1)},vt={get:pt(!0,!0)};const gt=new WeakMap,yt=new WeakMap,_t=new WeakMap,bt=new WeakMap;function wt(e){return e.__v_skip||!Object.isExtensible(e)?0:function(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}((e=>H(e).slice(8,-1))(e))}function xt(e){return Nt(e)?e:Ct(e,!1,ze,dt,gt)}function kt(e){return Ct(e,!1,qe,ht,yt)}function St(e){return Ct(e,!0,Ge,mt,_t)}function Et(e){return Ct(e,!0,Je,vt,bt)}function Ct(e,t,n,r,o){if(!$(e))return e;if(e.__v_raw&&(!t||!e.__v_isReactive))return e;const i=o.get(e);if(i)return i;const l=wt(e);if(0===l)return e;const a=new Proxy(e,2===l?r:n);return o.set(e,a),a}function Tt(e){return Nt(e)?Tt(e.__v_raw):!(!e||!e.__v_isReactive)}function Nt(e){return!(!e||!e.__v_isReadonly)}function Lt(e){return!(!e||!e.__v_isShallow)}function Ot(e){return Tt(e)||Nt(e)}function At(e){const t=e&&e.__v_raw;return t?At(t):e}function Pt(e){return oe(e,"__v_skip",!0),e}const It=e=>$(e)?xt(e):e,Rt=e=>$(e)?St(e):e;function Ft(e){Ce&&_e&&Ae((e=At(e)).dep||(e.dep=de()))}function Mt(e,t){(e=At(e)).dep&&Ie(e.dep)}function Bt(e){return!(!e||!0!==e.__v_isRef)}function jt(e){return Dt(e,!1)}function Vt(e){return Dt(e,!0)}function Dt(e,t){return Bt(e)?e:new $t(e,t)}class $t{constructor(e,t){this.__v_isShallow=t,this.dep=void 0,this.__v_isRef=!0,this._rawValue=t?e:At(e),this._value=t?e:It(e)}get value(){return Ft(this),this._value}set value(e){const t=this.__v_isShallow||Lt(e)||Nt(e);e=t?e:At(e),ne(e,this._rawValue)&&(this._rawValue=e,this._value=t?e:It(e),Mt(this))}}function Ut(e){Mt(e)}function Wt(e){return Bt(e)?e.value:e}const Ht={get:(e,t,n)=>Wt(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const o=e[t];return Bt(o)&&!Bt(n)?(o.value=n,!0):Reflect.set(e,t,n,r)}};function zt(e){return Tt(e)?e:new Proxy(e,Ht)}class Gt{constructor(e){this.dep=void 0,this.__v_isRef=!0;const{get:t,set:n}=e((()=>Ft(this)),(()=>Mt(this)));this._get=t,this._set=n}get value(){return this._get()}set value(e){this._set(e)}}function qt(e){return new Gt(e)}function Jt(e){const t=R(e)?new Array(e.length):{};for(const n in e)t[n]=Yt(e,n);return t}class Kt{constructor(e,t,n){this._object=e,this._key=t,this._defaultValue=n,this.__v_isRef=!0}get value(){const e=this._object[this._key];return void 0===e?this._defaultValue:e}set value(e){this._object[this._key]=e}}function Yt(e,t,n){const r=e[t];return Bt(r)?r:new Kt(e,t,n)}var Zt;class Xt{constructor(e,t,n,r){this._setter=t,this.dep=void 0,this.__v_isRef=!0,this[Zt]=!1,this._dirty=!0,this.effect=new xe(e,(()=>{this._dirty||(this._dirty=!0,Mt(this))})),this.effect.computed=this,this.effect.active=this._cacheable=!r,this.__v_isReadonly=n}get value(){const e=At(this);return Ft(e),!e._dirty&&e._cacheable||(e._dirty=!1,e._value=e.effect.run()),e._value}set value(e){this._setter(e)}}Zt="__v_isReadonly";function Qt(e,...t){}function en(e,t,n,r){let o;try{o=r?e(...r):e()}catch(e){nn(e,t,n)}return o}function tn(e,t,n,r){if(j(e)){const o=en(e,t,n,r);return o&&U(o)&&o.catch((e=>{nn(e,t,n)})),o}const o=[];for(let i=0;i>>1;_n(ln[r])_n(e)-_n(t))),un=0;unnull==e.id?1/0:e.id,bn=(e,t)=>{const n=_n(e)-_n(t);if(0===n){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function wn(e){on=!1,rn=!0,ln.sort(bn);try{for(an=0;anxn.emit(e,...t))),kn=[];else if("undefined"!=typeof window&&window.HTMLElement&&!(null===(r=null===(n=window.navigator)||void 0===n?void 0:n.userAgent)||void 0===r?void 0:r.includes("jsdom"))){(t.__VUE_DEVTOOLS_HOOK_REPLAY__=t.__VUE_DEVTOOLS_HOOK_REPLAY__||[]).push((e=>{En(e,t)})),setTimeout((()=>{xn||(t.__VUE_DEVTOOLS_HOOK_REPLAY__=null,Sn=!0,kn=[])}),3e3)}else Sn=!0,kn=[]}function Cn(e,t,...n){if(e.isUnmounted)return;const r=e.vnode.props||k;let o=n;const i=t.startsWith("update:"),l=i&&t.slice(7);if(l&&l in r){const e=`${"modelValue"===l?"model":l}Modifiers`,{number:t,trim:i}=r[e]||k;i&&(o=n.map((e=>V(e)?e.trim():e))),t&&(o=n.map(ie))}let a;let s=r[a=te(t)]||r[a=te(Z(t))];!s&&i&&(s=r[a=te(Q(t))]),s&&tn(s,e,6,o);const c=r[a+"Once"];if(c){if(e.emitted){if(e.emitted[a])return}else e.emitted={};e.emitted[a]=!0,tn(c,e,6,o)}}function Tn(e,t,n=!1){const r=t.emitsCache,o=r.get(e);if(void 0!==o)return o;const i=e.emits;let l={},a=!1;if(!j(e)){const r=e=>{const n=Tn(e,t,!0);n&&(a=!0,O(l,n))};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}return i||a?(R(i)?i.forEach((e=>l[e]=null)):O(l,i),$(e)&&r.set(e,l),l):($(e)&&r.set(e,null),null)}function Nn(e,t){return!(!e||!N(t))&&(t=t.slice(2).replace(/Once$/,""),I(e,t[0].toLowerCase()+t.slice(1))||I(e,Q(t))||I(e,t))}let Ln=null,On=null;function An(e){const t=Ln;return Ln=e,On=e&&e.type.__scopeId||null,t}function Pn(e){On=e}function In(){On=null}const Rn=e=>Fn;function Fn(e,t=Ln,n){if(!t)return e;if(e._n)return e;const r=(...n)=>{r._d&&ii(-1);const o=An(t);let i;try{i=e(...n)}finally{An(o),r._d&&ii(1)}return i};return r._n=!0,r._c=!0,r._d=!0,r}function Mn(e){const{type:t,vnode:n,proxy:r,withProxy:o,props:i,propsOptions:[l],slots:a,attrs:s,emit:c,render:u,renderCache:f,data:p,setupState:d,ctx:h,inheritAttrs:m}=e;let v,g;const y=An(e);try{if(4&n.shapeFlag){const e=o||r;v=ki(u.call(e,e,f,i,d,p,h)),g=s}else{const e=t;0,v=ki(e.length>1?e(i,{attrs:s,slots:a,emit:c}):e(i,null)),g=t.props?s:jn(s)}}catch(t){Qo.length=0,nn(t,e,1),v=vi(Zo)}let _=v;if(g&&!1!==m){const e=Object.keys(g),{shapeFlag:t}=_;e.length&&7&t&&(l&&e.some(L)&&(g=Vn(g,l)),_=_i(_,g))}return n.dirs&&(_=_i(_),_.dirs=_.dirs?_.dirs.concat(n.dirs):n.dirs),n.transition&&(_.transition=n.transition),v=_,An(y),v}function Bn(e){let t;for(let n=0;n{let t;for(const n in e)("class"===n||"style"===n||N(n))&&((t||(t={}))[n]=e[n]);return t},Vn=(e,t)=>{const n={};for(const r in e)L(r)&&r.slice(9)in t||(n[r]=e[r]);return n};function Dn(e,t,n){const r=Object.keys(t);if(r.length!==Object.keys(e).length)return!0;for(let o=0;oe.__isSuspense,Wn={name:"Suspense",__isSuspense:!0,process(e,t,n,r,o,i,l,a,s,c){null==e?function(e,t,n,r,o,i,l,a,s){const{p:c,o:{createElement:u}}=s,f=u("div"),p=e.suspense=zn(e,o,r,t,f,n,i,l,a,s);c(null,p.pendingBranch=e.ssContent,f,null,r,p,i,l),p.deps>0?(Hn(e,"onPending"),Hn(e,"onFallback"),c(null,e.ssFallback,t,n,r,null,i,l),Jn(p,e.ssFallback)):p.resolve()}(t,n,r,o,i,l,a,s,c):function(e,t,n,r,o,i,l,a,{p:s,um:c,o:{createElement:u}}){const f=t.suspense=e.suspense;f.vnode=t,t.el=e.el;const p=t.ssContent,d=t.ssFallback,{activeBranch:h,pendingBranch:m,isInFallback:v,isHydrating:g}=f;if(m)f.pendingBranch=p,ui(p,m)?(s(m,p,f.hiddenContainer,null,o,f,i,l,a),f.deps<=0?f.resolve():v&&(s(h,d,n,r,o,null,i,l,a),Jn(f,d))):(f.pendingId++,g?(f.isHydrating=!1,f.activeBranch=m):c(m,o,f),f.deps=0,f.effects.length=0,f.hiddenContainer=u("div"),v?(s(null,p,f.hiddenContainer,null,o,f,i,l,a),f.deps<=0?f.resolve():(s(h,d,n,r,o,null,i,l,a),Jn(f,d))):h&&ui(p,h)?(s(h,p,n,r,o,f,i,l,a),f.resolve(!0)):(s(null,p,f.hiddenContainer,null,o,f,i,l,a),f.deps<=0&&f.resolve()));else if(h&&ui(p,h))s(h,p,n,r,o,f,i,l,a),Jn(f,p);else if(Hn(t,"onPending"),f.pendingBranch=p,f.pendingId++,s(null,p,f.hiddenContainer,null,o,f,i,l,a),f.deps<=0)f.resolve();else{const{timeout:e,pendingId:t}=f;e>0?setTimeout((()=>{f.pendingId===t&&f.fallback(d)}),e):0===e&&f.fallback(d)}}(e,t,n,r,o,l,a,s,c)},hydrate:function(e,t,n,r,o,i,l,a,s){const c=t.suspense=zn(t,r,n,e.parentNode,document.createElement("div"),null,o,i,l,a,!0),u=s(e,c.pendingBranch=t.ssContent,n,c,i,l);0===c.deps&&c.resolve();return u},create:zn,normalize:function(e){const{shapeFlag:t,children:n}=e,r=32&t;e.ssContent=Gn(r?n.default:n),e.ssFallback=r?Gn(n.fallback):vi(Zo)}};function Hn(e,t){const n=e.props&&e.props[t];j(n)&&n()}function zn(e,t,n,r,o,i,l,a,s,c,u=!1){const{p:f,m:p,um:d,n:h,o:{parentNode:m,remove:v}}=c,g=ie(e.props&&e.props.timeout),y={vnode:e,parent:t,parentComponent:n,isSVG:l,container:r,hiddenContainer:o,anchor:i,deps:0,pendingId:0,timeout:"number"==typeof g?g:-1,activeBranch:null,pendingBranch:null,isInFallback:!0,isHydrating:u,isUnmounted:!1,effects:[],resolve(e=!1){const{vnode:t,activeBranch:n,pendingBranch:r,pendingId:o,effects:i,parentComponent:l,container:a}=y;if(y.isHydrating)y.isHydrating=!1;else if(!e){const e=n&&r.transition&&"out-in"===r.transition.mode;e&&(n.transition.afterLeave=()=>{o===y.pendingId&&p(r,a,t,0)});let{anchor:t}=y;n&&(t=h(n),d(n,l,y,!0)),e||p(r,a,t,0)}Jn(y,r),y.pendingBranch=null,y.isInFallback=!1;let s=y.parent,c=!1;for(;s;){if(s.pendingBranch){s.effects.push(...i),c=!0;break}s=s.parent}c||vn(i),y.effects=[],Hn(t,"onResolve")},fallback(e){if(!y.pendingBranch)return;const{vnode:t,activeBranch:n,parentComponent:r,container:o,isSVG:i}=y;Hn(t,"onFallback");const l=h(n),c=()=>{y.isInFallback&&(f(null,e,o,l,r,null,i,a,s),Jn(y,e))},u=e.transition&&"out-in"===e.transition.mode;u&&(n.transition.afterLeave=c),y.isInFallback=!0,d(n,r,null,!0),u||c()},move(e,t,n){y.activeBranch&&p(y.activeBranch,e,t,n),y.container=e},next:()=>y.activeBranch&&h(y.activeBranch),registerDep(e,t){const n=!!y.pendingBranch;n&&y.deps++;const r=e.vnode.el;e.asyncDep.catch((t=>{nn(t,e,0)})).then((o=>{if(e.isUnmounted||y.isUnmounted||y.pendingId!==e.suspenseId)return;e.asyncResolved=!0;const{vnode:i}=e;Di(e,o,!1),r&&(i.el=r);const a=!r&&e.subTree.el;t(e,i,m(r||e.subTree.el),r?null:h(e.subTree),y,l,s),a&&v(a),$n(e,i.el),n&&0==--y.deps&&y.resolve()}))},unmount(e,t){y.isUnmounted=!0,y.activeBranch&&d(y.activeBranch,n,e,t),y.pendingBranch&&d(y.pendingBranch,n,e,t)}};return y}function Gn(e){let t;if(j(e)){const n=oi&&e._c;n&&(e._d=!1,ti()),e=e(),n&&(e._d=!0,t=ei,ni())}if(R(e)){const t=Bn(e);0,e=t}return e=ki(e),t&&!e.dynamicChildren&&(e.dynamicChildren=t.filter((t=>t!==e))),e}function qn(e,t){t&&t.pendingBranch?R(e)?t.effects.push(...e):t.effects.push(e):vn(e)}function Jn(e,t){e.activeBranch=t;const{vnode:n,parentComponent:r}=e,o=n.el=t.el;r&&r.subTree===n&&(r.vnode.el=o,$n(r,o))}function Kn(e,t){if(Ai){let n=Ai.provides;const r=Ai.parent&&Ai.parent.provides;r===n&&(n=Ai.provides=Object.create(r)),n[e]=t}else 0}function Yn(e,t,n=!1){const r=Ai||Ln;if(r){const o=null==r.parent?r.vnode.appContext&&r.vnode.appContext.provides:r.parent.provides;if(o&&e in o)return o[e];if(arguments.length>1)return n&&j(t)?t.call(r.proxy):t}else 0}function Zn(e,t){return nr(e,null,t)}function Xn(e,t){return nr(e,null,{flush:"post"})}function Qn(e,t){return nr(e,null,{flush:"sync"})}const er={};function tr(e,t,n){return nr(e,t,n)}function nr(e,t,{immediate:n,deep:r,flush:o,onTrack:i,onTrigger:l}=k){const a=Ai;let s,c,u=!1,f=!1;if(Bt(e)?(s=()=>e.value,u=Lt(e)):Tt(e)?(s=()=>e,r=!0):R(e)?(f=!0,u=e.some((e=>Tt(e)||Lt(e))),s=()=>e.map((e=>Bt(e)?e.value:Tt(e)?ir(e):j(e)?en(e,a,2):void 0))):s=j(e)?t?()=>en(e,a,2):()=>{if(!a||!a.isUnmounted)return c&&c(),tn(e,a,3,[d])}:E,t&&r){const e=s;s=()=>ir(e())}let p,d=e=>{c=g.onStop=()=>{en(e,a,4)}};if(ji){if(d=E,t?n&&tn(t,a,3,[s(),f?[]:void 0,d]):s(),"sync"!==o)return E;{const e=al();p=e.__watcherHandles||(e.__watcherHandles=[])}}let h=f?new Array(e.length).fill(er):er;const m=()=>{if(g.active)if(t){const e=g.run();(r||u||(f?e.some(((e,t)=>ne(e,h[t]))):ne(e,h)))&&(c&&c(),tn(t,a,3,[e,h===er?void 0:f&&h[0]===er?[]:h,d]),h=e)}else g.run()};let v;m.allowRecurse=!!t,"sync"===o?v=m:"post"===o?v=()=>Bo(m,a&&a.suspense):(m.pre=!0,a&&(m.id=a.uid),v=()=>hn(m));const g=new xe(s,v);t?n?m():h=g.run():"post"===o?Bo(g.run.bind(g),a&&a.suspense):g.run();const y=()=>{g.stop(),a&&a.scope&&A(a.scope.effects,g)};return p&&p.push(y),y}function rr(e,t,n){const r=this.proxy,o=V(e)?e.includes(".")?or(r,e):()=>r[e]:e.bind(r,r);let i;j(t)?i=t:(i=t.handler,n=t);const l=Ai;Ii(this);const a=nr(o,i.bind(r),n);return l?Ii(l):Ri(),a}function or(e,t){const n=t.split(".");return()=>{let t=e;for(let e=0;e{ir(e,t)}));else if(z(e))for(const n in e)ir(e[n],t);return e}function lr(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return Ar((()=>{e.isMounted=!0})),Rr((()=>{e.isUnmounting=!0})),e}const ar=[Function,Array],sr={name:"BaseTransition",props:{mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:ar,onEnter:ar,onAfterEnter:ar,onEnterCancelled:ar,onBeforeLeave:ar,onLeave:ar,onAfterLeave:ar,onLeaveCancelled:ar,onBeforeAppear:ar,onAppear:ar,onAfterAppear:ar,onAppearCancelled:ar},setup(e,{slots:t}){const n=Pi(),r=lr();let o;return()=>{const i=t.default&&hr(t.default(),!0);if(!i||!i.length)return;let l=i[0];if(i.length>1){let e=!1;for(const t of i)if(t.type!==Zo){0,l=t,e=!0;break}}const a=At(e),{mode:s}=a;if(r.isLeaving)return fr(l);const c=pr(l);if(!c)return fr(l);const u=ur(c,a,r,n);dr(c,u);const f=n.subTree,p=f&&pr(f);let d=!1;const{getTransitionKey:h}=c.type;if(h){const e=h();void 0===o?o=e:e!==o&&(o=e,d=!0)}if(p&&p.type!==Zo&&(!ui(c,p)||d)){const e=ur(p,a,r,n);if(dr(p,e),"out-in"===s)return r.isLeaving=!0,e.afterLeave=()=>{r.isLeaving=!1,!1!==n.update.active&&n.update()},fr(l);"in-out"===s&&c.type!==Zo&&(e.delayLeave=(e,t,n)=>{cr(r,p)[String(p.key)]=p,e._leaveCb=()=>{t(),e._leaveCb=void 0,delete u.delayedLeave},u.delayedLeave=n})}return l}}};function cr(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function ur(e,t,n,r){const{appear:o,mode:i,persisted:l=!1,onBeforeEnter:a,onEnter:s,onAfterEnter:c,onEnterCancelled:u,onBeforeLeave:f,onLeave:p,onAfterLeave:d,onLeaveCancelled:h,onBeforeAppear:m,onAppear:v,onAfterAppear:g,onAppearCancelled:y}=t,_=String(e.key),b=cr(n,e),w=(e,t)=>{e&&tn(e,r,9,t)},x=(e,t)=>{const n=t[1];w(e,t),R(e)?e.every((e=>e.length<=1))&&n():e.length<=1&&n()},k={mode:i,persisted:l,beforeEnter(t){let r=a;if(!n.isMounted){if(!o)return;r=m||a}t._leaveCb&&t._leaveCb(!0);const i=b[_];i&&ui(e,i)&&i.el._leaveCb&&i.el._leaveCb(),w(r,[t])},enter(e){let t=s,r=c,i=u;if(!n.isMounted){if(!o)return;t=v||s,r=g||c,i=y||u}let l=!1;const a=e._enterCb=t=>{l||(l=!0,w(t?i:r,[e]),k.delayedLeave&&k.delayedLeave(),e._enterCb=void 0)};t?x(t,[e,a]):a()},leave(t,r){const o=String(e.key);if(t._enterCb&&t._enterCb(!0),n.isUnmounting)return r();w(f,[t]);let i=!1;const l=t._leaveCb=n=>{i||(i=!0,r(),w(n?h:d,[t]),t._leaveCb=void 0,b[o]===e&&delete b[o])};b[o]=e,p?x(p,[t,l]):l()},clone:e=>ur(e,t,n,r)};return k}function fr(e){if(_r(e))return(e=_i(e)).children=null,e}function pr(e){return _r(e)?e.children?e.children[0]:void 0:e}function dr(e,t){6&e.shapeFlag&&e.component?dr(e.component.subTree,t):128&e.shapeFlag?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function hr(e,t=!1,n){let r=[],o=0;for(let i=0;i1)for(let e=0;e!!e.type.__asyncLoader;function gr(e){j(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:r,delay:o=200,timeout:i,suspensible:l=!0,onError:a}=e;let s,c=null,u=0;const f=()=>{let e;return c||(e=c=t().catch((e=>{if(e=e instanceof Error?e:new Error(String(e)),a)return new Promise(((t,n)=>{a(e,(()=>t((u++,c=null,f()))),(()=>n(e)),u+1)}));throw e})).then((t=>e!==c&&c?c:(t&&(t.__esModule||"Module"===t[Symbol.toStringTag])&&(t=t.default),s=t,t))))};return mr({name:"AsyncComponentWrapper",__asyncLoader:f,get __asyncResolved(){return s},setup(){const e=Ai;if(s)return()=>yr(s,e);const t=t=>{c=null,nn(t,e,13,!r)};if(l&&e.suspense||ji)return f().then((t=>()=>yr(t,e))).catch((e=>(t(e),()=>r?vi(r,{error:e}):null)));const a=jt(!1),u=jt(),p=jt(!!o);return o&&setTimeout((()=>{p.value=!1}),o),null!=i&&setTimeout((()=>{if(!a.value&&!u.value){const e=new Error(`Async component timed out after ${i}ms.`);t(e),u.value=e}}),i),f().then((()=>{a.value=!0,e.parent&&_r(e.parent.vnode)&&hn(e.parent.update)})).catch((e=>{t(e),u.value=e})),()=>a.value&&s?yr(s,e):u.value&&r?vi(r,{error:u.value}):n&&!p.value?vi(n):void 0}})}function yr(e,t){const{ref:n,props:r,children:o,ce:i}=t.vnode,l=vi(e,r,o);return l.ref=n,l.ce=i,delete t.vnode.ce,l}const _r=e=>e.type.__isKeepAlive,br={name:"KeepAlive",__isKeepAlive:!0,props:{include:[String,RegExp,Array],exclude:[String,RegExp,Array],max:[String,Number]},setup(e,{slots:t}){const n=Pi(),r=n.ctx;if(!r.renderer)return()=>{const e=t.default&&t.default();return e&&1===e.length?e[0]:e};const o=new Map,i=new Set;let l=null;const a=n.suspense,{renderer:{p:s,m:c,um:u,o:{createElement:f}}}=r,p=f("div");function d(e){Cr(e),u(e,n,a,!0)}function h(e){o.forEach(((t,n)=>{const r=Gi(t.type);!r||e&&e(r)||m(n)}))}function m(e){const t=o.get(e);l&&t.type===l.type?l&&Cr(l):d(t),o.delete(e),i.delete(e)}r.activate=(e,t,n,r,o)=>{const i=e.component;c(e,t,n,0,a),s(i.vnode,e,t,n,i,a,r,e.slotScopeIds,o),Bo((()=>{i.isDeactivated=!1,i.a&&re(i.a);const t=e.props&&e.props.onVnodeMounted;t&&Ti(t,i.parent,e)}),a)},r.deactivate=e=>{const t=e.component;c(e,p,null,1,a),Bo((()=>{t.da&&re(t.da);const n=e.props&&e.props.onVnodeUnmounted;n&&Ti(n,t.parent,e),t.isDeactivated=!0}),a)},tr((()=>[e.include,e.exclude]),(([e,t])=>{e&&h((t=>wr(e,t))),t&&h((e=>!wr(t,e)))}),{flush:"post",deep:!0});let v=null;const g=()=>{null!=v&&o.set(v,Tr(n.subTree))};return Ar(g),Ir(g),Rr((()=>{o.forEach((e=>{const{subTree:t,suspense:r}=n,o=Tr(t);if(e.type!==o.type)d(e);else{Cr(o);const e=o.component.da;e&&Bo(e,r)}}))})),()=>{if(v=null,!t.default)return null;const n=t.default(),r=n[0];if(n.length>1)return l=null,n;if(!(ci(r)&&(4&r.shapeFlag||128&r.shapeFlag)))return l=null,r;let a=Tr(r);const s=a.type,c=Gi(vr(a)?a.type.__asyncResolved||{}:s),{include:u,exclude:f,max:p}=e;if(u&&(!c||!wr(u,c))||f&&c&&wr(f,c))return l=a,r;const d=null==a.key?s:a.key,h=o.get(d);return a.el&&(a=_i(a),128&r.shapeFlag&&(r.ssContent=a)),v=d,h?(a.el=h.el,a.component=h.component,a.transition&&dr(a,a.transition),a.shapeFlag|=512,i.delete(d),i.add(d)):(i.add(d),p&&i.size>parseInt(p,10)&&m(i.values().next().value)),a.shapeFlag|=256,l=a,Un(r.type)?r:a}}};function wr(e,t){return R(e)?e.some((e=>wr(e,t))):V(e)?e.split(",").includes(t):!!e.test&&e.test(t)}function xr(e,t){Sr(e,"a",t)}function kr(e,t){Sr(e,"da",t)}function Sr(e,t,n=Ai){const r=e.__wdc||(e.__wdc=()=>{let t=n;for(;t;){if(t.isDeactivated)return;t=t.parent}return e()});if(Nr(t,r,n),n){let e=n.parent;for(;e&&e.parent;)_r(e.parent.vnode)&&Er(r,t,n,e),e=e.parent}}function Er(e,t,n,r){const o=Nr(t,e,r,!0);Fr((()=>{A(r[t],o)}),n)}function Cr(e){e.shapeFlag&=-257,e.shapeFlag&=-513}function Tr(e){return 128&e.shapeFlag?e.ssContent:e}function Nr(e,t,n=Ai,r=!1){if(n){const o=n[e]||(n[e]=[]),i=t.__weh||(t.__weh=(...r)=>{if(n.isUnmounted)return;Ne(),Ii(n);const o=tn(t,n,e,r);return Ri(),Le(),o});return r?o.unshift(i):o.push(i),i}}const Lr=e=>(t,n=Ai)=>(!ji||"sp"===e)&&Nr(e,((...e)=>t(...e)),n),Or=Lr("bm"),Ar=Lr("m"),Pr=Lr("bu"),Ir=Lr("u"),Rr=Lr("bum"),Fr=Lr("um"),Mr=Lr("sp"),Br=Lr("rtg"),jr=Lr("rtc");function Vr(e,t=Ai){Nr("ec",e,t)}function Dr(e,t){const n=Ln;if(null===n)return e;const r=zi(n)||n.proxy,o=e.dirs||(e.dirs=[]);for(let e=0;et(e,n,void 0,i&&i[n])));else{const n=Object.keys(e);o=new Array(n.length);for(let r=0,l=n.length;r{const t=r.fn(...e);return t&&(t.key=r.key),t}:r.fn)}return e}function Zr(e,t,n={},r,o){if(Ln.isCE||Ln.parent&&vr(Ln.parent)&&Ln.parent.isCE)return"default"!==t&&(n.name=t),vi("slot",n,r&&r());let i=e[t];i&&i._c&&(i._d=!1),ti();const l=i&&Xr(i(n)),a=si(Ko,{key:n.key||l&&l.key||`_${t}`},l||(r?r():[]),l&&1===e._?64:-2);return!o&&a.scopeId&&(a.slotScopeIds=[a.scopeId+"-s"]),i&&i._c&&(i._d=!0),a}function Xr(e){return e.some((e=>!ci(e)||e.type!==Zo&&!(e.type===Ko&&!Xr(e.children))))?e:null}function Qr(e,t){const n={};for(const r in e)n[t&&/[A-Z]/.test(r)?`on:${r}`:te(r)]=e[r];return n}const eo=e=>e?Fi(e)?zi(e)||e.proxy:eo(e.parent):null,to=O(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>eo(e.parent),$root:e=>eo(e.root),$emit:e=>e.emit,$options:e=>co(e),$forceUpdate:e=>e.f||(e.f=()=>hn(e.update)),$nextTick:e=>e.n||(e.n=dn.bind(e.proxy)),$watch:e=>rr.bind(e)}),no=(e,t)=>e!==k&&!e.__isScriptSetup&&I(e,t),ro={get({_:e},t){const{ctx:n,setupState:r,data:o,props:i,accessCache:l,type:a,appContext:s}=e;let c;if("$"!==t[0]){const a=l[t];if(void 0!==a)switch(a){case 1:return r[t];case 2:return o[t];case 4:return n[t];case 3:return i[t]}else{if(no(r,t))return l[t]=1,r[t];if(o!==k&&I(o,t))return l[t]=2,o[t];if((c=e.propsOptions[0])&&I(c,t))return l[t]=3,i[t];if(n!==k&&I(n,t))return l[t]=4,n[t];io&&(l[t]=0)}}const u=to[t];let f,p;return u?("$attrs"===t&&Oe(e,0,t),u(e)):(f=a.__cssModules)&&(f=f[t])?f:n!==k&&I(n,t)?(l[t]=4,n[t]):(p=s.config.globalProperties,I(p,t)?p[t]:void 0)},set({_:e},t,n){const{data:r,setupState:o,ctx:i}=e;return no(o,t)?(o[t]=n,!0):r!==k&&I(r,t)?(r[t]=n,!0):!I(e.props,t)&&(("$"!==t[0]||!(t.slice(1)in e))&&(i[t]=n,!0))},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:o,propsOptions:i}},l){let a;return!!n[l]||e!==k&&I(e,l)||no(t,l)||(a=i[0])&&I(a,l)||I(r,l)||I(to,l)||I(o.config.globalProperties,l)},defineProperty(e,t,n){return null!=n.get?e._.accessCache[t]=0:I(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};const oo=O({},ro,{get(e,t){if(t!==Symbol.unscopables)return ro.get(e,t,e)},has:(e,t)=>"_"!==t[0]&&!i(t)});let io=!0;function lo(e){const t=co(e),n=e.proxy,r=e.ctx;io=!1,t.beforeCreate&&ao(t.beforeCreate,e,"bc");const{data:o,computed:i,methods:l,watch:a,provide:s,inject:c,created:u,beforeMount:f,mounted:p,beforeUpdate:d,updated:h,activated:m,deactivated:v,beforeDestroy:g,beforeUnmount:y,destroyed:_,unmounted:b,render:w,renderTracked:x,renderTriggered:k,errorCaptured:S,serverPrefetch:C,expose:T,inheritAttrs:N,components:L,directives:O,filters:A}=t;if(c&&function(e,t,n=E,r=!1){R(e)&&(e=ho(e));for(const n in e){const o=e[n];let i;i=$(o)?"default"in o?Yn(o.from||n,o.default,!0):Yn(o.from||n):Yn(o),Bt(i)&&r?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>i.value,set:e=>i.value=e}):t[n]=i}}(c,r,null,e.appContext.config.unwrapInjectedRef),l)for(const e in l){const t=l[e];j(t)&&(r[e]=t.bind(n))}if(o){0;const t=o.call(n,n);0,$(t)&&(e.data=xt(t))}if(io=!0,i)for(const e in i){const t=i[e],o=j(t)?t.bind(n,n):j(t.get)?t.get.bind(n,n):E;0;const l=!j(t)&&j(t.set)?t.set.bind(n):E,a=Ji({get:o,set:l});Object.defineProperty(r,e,{enumerable:!0,configurable:!0,get:()=>a.value,set:e=>a.value=e})}if(a)for(const e in a)so(a[e],r,n,e);if(s){const e=j(s)?s.call(n):s;Reflect.ownKeys(e).forEach((t=>{Kn(t,e[t])}))}function P(e,t){R(t)?t.forEach((t=>e(t.bind(n)))):t&&e(t.bind(n))}if(u&&ao(u,e,"c"),P(Or,f),P(Ar,p),P(Pr,d),P(Ir,h),P(xr,m),P(kr,v),P(Vr,S),P(jr,x),P(Br,k),P(Rr,y),P(Fr,b),P(Mr,C),R(T))if(T.length){const t=e.exposed||(e.exposed={});T.forEach((e=>{Object.defineProperty(t,e,{get:()=>n[e],set:t=>n[e]=t})}))}else e.exposed||(e.exposed={});w&&e.render===E&&(e.render=w),null!=N&&(e.inheritAttrs=N),L&&(e.components=L),O&&(e.directives=O)}function ao(e,t,n){tn(R(e)?e.map((e=>e.bind(t.proxy))):e.bind(t.proxy),t,n)}function so(e,t,n,r){const o=r.includes(".")?or(n,r):()=>n[r];if(V(e)){const n=t[e];j(n)&&tr(o,n)}else if(j(e))tr(o,e.bind(n));else if($(e))if(R(e))e.forEach((e=>so(e,t,n,r)));else{const r=j(e.handler)?e.handler.bind(n):t[e.handler];j(r)&&tr(o,r,e)}else 0}function co(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:o,optionsCache:i,config:{optionMergeStrategies:l}}=e.appContext,a=i.get(t);let s;return a?s=a:o.length||n||r?(s={},o.length&&o.forEach((e=>uo(s,e,l,!0))),uo(s,t,l)):s=t,$(t)&&i.set(t,s),s}function uo(e,t,n,r=!1){const{mixins:o,extends:i}=t;i&&uo(e,i,n,!0),o&&o.forEach((t=>uo(e,t,n,!0)));for(const o in t)if(r&&"expose"===o);else{const r=fo[o]||n&&n[o];e[o]=r?r(e[o],t[o]):t[o]}return e}const fo={data:po,props:vo,emits:vo,methods:vo,computed:vo,beforeCreate:mo,created:mo,beforeMount:mo,mounted:mo,beforeUpdate:mo,updated:mo,beforeDestroy:mo,beforeUnmount:mo,destroyed:mo,unmounted:mo,activated:mo,deactivated:mo,errorCaptured:mo,serverPrefetch:mo,components:vo,directives:vo,watch:function(e,t){if(!e)return t;if(!t)return e;const n=O(Object.create(null),e);for(const r in t)n[r]=mo(e[r],t[r]);return n},provide:po,inject:function(e,t){return vo(ho(e),ho(t))}};function po(e,t){return t?e?function(){return O(j(e)?e.call(this,this):e,j(t)?t.call(this,this):t)}:t:e}function ho(e){if(R(e)){const t={};for(let n=0;n{s=!0;const[n,r]=_o(e,t,!0);O(l,n),r&&a.push(...r)};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}if(!i&&!s)return $(e)&&r.set(e,S),S;if(R(i))for(let e=0;e-1,r[1]=n<0||e-1||I(r,"default"))&&a.push(t)}}}}const c=[l,a];return $(e)&&r.set(e,c),c}function bo(e){return"$"!==e[0]}function wo(e){const t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:null===e?"null":""}function xo(e,t){return wo(e)===wo(t)}function ko(e,t){return R(t)?t.findIndex((t=>xo(t,e))):j(t)&&xo(t,e)?0:-1}const So=e=>"_"===e[0]||"$stable"===e,Eo=e=>R(e)?e.map(ki):[ki(e)],Co=(e,t,n)=>{if(t._n)return t;const r=Fn(((...e)=>Eo(t(...e))),n);return r._c=!1,r},To=(e,t,n)=>{const r=e._ctx;for(const n in e){if(So(n))continue;const o=e[n];if(j(o))t[n]=Co(0,o,r);else if(null!=o){0;const e=Eo(o);t[n]=()=>e}}},No=(e,t)=>{const n=Eo(t);e.slots.default=()=>n};function Lo(){return{app:null,config:{isNativeTag:C,performance:!1,globalProperties:{},optionMergeStrategies:{},errorHandler:void 0,warnHandler:void 0,compilerOptions:{}},mixins:[],components:{},directives:{},provides:Object.create(null),optionsCache:new WeakMap,propsCache:new WeakMap,emitsCache:new WeakMap}}let Oo=0;function Ao(e,t){return function(n,r=null){j(n)||(n=Object.assign({},n)),null==r||$(r)||(r=null);const o=Lo(),i=new Set;let l=!1;const a=o.app={_uid:Oo++,_component:n,_props:r,_container:null,_context:o,_instance:null,version:fl,get config(){return o.config},set config(e){0},use:(e,...t)=>(i.has(e)||(e&&j(e.install)?(i.add(e),e.install(a,...t)):j(e)&&(i.add(e),e(a,...t))),a),mixin:e=>(o.mixins.includes(e)||o.mixins.push(e),a),component:(e,t)=>t?(o.components[e]=t,a):o.components[e],directive:(e,t)=>t?(o.directives[e]=t,a):o.directives[e],mount(i,s,c){if(!l){0;const u=vi(n,r);return u.appContext=o,s&&t?t(u,i):e(u,i,c),l=!0,a._container=i,i.__vue_app__=a,zi(u.component)||u.component.proxy}},unmount(){l&&(e(null,a._container),delete a._container.__vue_app__)},provide:(e,t)=>(o.provides[e]=t,a)};return a}}function Po(e,t,n,r,o=!1){if(R(e))return void e.forEach(((e,i)=>Po(e,t&&(R(t)?t[i]:t),n,r,o)));if(vr(r)&&!o)return;const i=4&r.shapeFlag?zi(r.component)||r.component.proxy:r.el,l=o?null:i,{i:a,r:s}=e;const c=t&&t.r,u=a.refs===k?a.refs={}:a.refs,f=a.setupState;if(null!=c&&c!==s&&(V(c)?(u[c]=null,I(f,c)&&(f[c]=null)):Bt(c)&&(c.value=null)),j(s))en(s,a,12,[l,u]);else{const t=V(s),r=Bt(s);if(t||r){const a=()=>{if(e.f){const n=t?I(f,s)?f[s]:u[s]:s.value;o?R(n)&&A(n,i):R(n)?n.includes(i)||n.push(i):t?(u[s]=[i],I(f,s)&&(f[s]=u[s])):(s.value=[i],e.k&&(u[e.k]=s.value))}else t?(u[s]=l,I(f,s)&&(f[s]=l)):r&&(s.value=l,e.k&&(u[e.k]=l))};l?(a.id=-1,Bo(a,n)):a()}else 0}}let Io=!1;const Ro=e=>/svg/.test(e.namespaceURI)&&"foreignObject"!==e.tagName,Fo=e=>8===e.nodeType;function Mo(e){const{mt:t,p:n,o:{patchProp:r,createText:o,nextSibling:i,parentNode:l,remove:a,insert:s,createComment:c}}=e,u=(n,r,a,c,v,g=!1)=>{const y=Fo(n)&&"["===n.data,_=()=>h(n,r,a,c,v,y),{type:b,ref:w,shapeFlag:x,patchFlag:k}=r;let S=n.nodeType;r.el=n,-2===k&&(g=!1,r.dynamicChildren=null);let E=null;switch(b){case Yo:3!==S?""===r.children?(s(r.el=o(""),l(n),n),E=n):E=_():(n.data!==r.children&&(Io=!0,n.data=r.children),E=i(n));break;case Zo:E=8!==S||y?_():i(n);break;case Xo:if(y&&(S=(n=i(n)).nodeType),1===S||3===S){E=n;const e=!r.children.length;for(let t=0;t{l=l||!!t.dynamicChildren;const{type:s,props:c,patchFlag:u,shapeFlag:f,dirs:d}=t,h="input"===s&&d||"option"===s;if(h||-1!==u){if(d&&$r(t,null,n,"created"),c)if(h||!l||48&u)for(const t in c)(h&&t.endsWith("value")||N(t)&&!q(t))&&r(e,t,null,c[t],!1,void 0,n);else c.onClick&&r(e,"onClick",null,c.onClick,!1,void 0,n);let s;if((s=c&&c.onVnodeBeforeMount)&&Ti(s,n,t),d&&$r(t,null,n,"beforeMount"),((s=c&&c.onVnodeMounted)||d)&&qn((()=>{s&&Ti(s,n,t),d&&$r(t,null,n,"mounted")}),o),16&f&&(!c||!c.innerHTML&&!c.textContent)){let r=p(e.firstChild,t,e,n,o,i,l);for(;r;){Io=!0;const e=r;r=r.nextSibling,a(e)}}else 8&f&&e.textContent!==t.children&&(Io=!0,e.textContent=t.children)}return e.nextSibling},p=(e,t,r,o,i,l,a)=>{a=a||!!t.dynamicChildren;const s=t.children,c=s.length;for(let t=0;t{const{slotScopeIds:u}=t;u&&(o=o?o.concat(u):u);const f=l(e),d=p(i(e),t,f,n,r,o,a);return d&&Fo(d)&&"]"===d.data?i(t.anchor=d):(Io=!0,s(t.anchor=c("]"),f,d),d)},h=(e,t,r,o,s,c)=>{if(Io=!0,t.el=null,c){const t=m(e);for(;;){const n=i(e);if(!n||n===t)break;a(n)}}const u=i(e),f=l(e);return a(e),n(null,t,f,u,r,o,Ro(f),s),u},m=e=>{let t=0;for(;e;)if((e=i(e))&&Fo(e)&&("["===e.data&&t++,"]"===e.data)){if(0===t)return i(e);t--}return e};return[(e,t)=>{if(!t.hasChildNodes())return n(null,e,t),yn(),void(t._vnode=e);Io=!1,u(t.firstChild,e,null,null,null),yn(),t._vnode=e,Io&&console.error("Hydration completed but contains mismatches.")},u]}const Bo=qn;function jo(e){return Do(e)}function Vo(e){return Do(e,Mo)}function Do(e,t){(le||(le="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==n.g?n.g:{})).__VUE__=!0;const{insert:r,remove:o,patchProp:i,createElement:l,createText:a,createComment:s,setText:c,setElementText:u,parentNode:f,nextSibling:p,setScopeId:d=E,insertStaticContent:h}=e,m=(e,t,n,r=null,o=null,i=null,l=!1,a=null,s=!!t.dynamicChildren)=>{if(e===t)return;e&&!ui(e,t)&&(r=J(e),U(e,o,i,!0),e=null),-2===t.patchFlag&&(s=!1,t.dynamicChildren=null);const{type:c,ref:u,shapeFlag:f}=t;switch(c){case Yo:v(e,t,n,r);break;case Zo:g(e,t,n,r);break;case Xo:null==e&&y(t,n,r,l);break;case Ko:A(e,t,n,r,o,i,l,a,s);break;default:1&f?b(e,t,n,r,o,i,l,a,s):6&f?P(e,t,n,r,o,i,l,a,s):(64&f||128&f)&&c.process(e,t,n,r,o,i,l,a,s,Y)}null!=u&&o&&Po(u,e&&e.ref,i,t||e,!t)},v=(e,t,n,o)=>{if(null==e)r(t.el=a(t.children),n,o);else{const n=t.el=e.el;t.children!==e.children&&c(n,t.children)}},g=(e,t,n,o)=>{null==e?r(t.el=s(t.children||""),n,o):t.el=e.el},y=(e,t,n,r)=>{[e.el,e.anchor]=h(e.children,t,n,r,e.el,e.anchor)},_=({el:e,anchor:t})=>{let n;for(;e&&e!==t;)n=p(e),o(e),e=n;o(t)},b=(e,t,n,r,o,i,l,a,s)=>{l=l||"svg"===t.type,null==e?w(t,n,r,o,i,l,a,s):T(e,t,o,i,l,a,s)},w=(e,t,n,o,a,s,c,f)=>{let p,d;const{type:h,props:m,shapeFlag:v,transition:g,dirs:y}=e;if(p=e.el=l(e.type,s,m&&m.is,m),8&v?u(p,e.children):16&v&&C(e.children,p,null,o,a,s&&"foreignObject"!==h,c,f),y&&$r(e,null,o,"created"),m){for(const t in m)"value"===t||q(t)||i(p,t,null,m[t],s,e.children,o,a,G);"value"in m&&i(p,"value",null,m.value),(d=m.onVnodeBeforeMount)&&Ti(d,o,e)}x(p,e,e.scopeId,c,o),y&&$r(e,null,o,"beforeMount");const _=(!a||a&&!a.pendingBranch)&&g&&!g.persisted;_&&g.beforeEnter(p),r(p,t,n),((d=m&&m.onVnodeMounted)||_||y)&&Bo((()=>{d&&Ti(d,o,e),_&&g.enter(p),y&&$r(e,null,o,"mounted")}),a)},x=(e,t,n,r,o)=>{if(n&&d(e,n),r)for(let t=0;t{for(let c=s;c{const s=t.el=e.el;let{patchFlag:c,dynamicChildren:f,dirs:p}=t;c|=16&e.patchFlag;const d=e.props||k,h=t.props||k;let m;n&&$o(n,!1),(m=h.onVnodeBeforeUpdate)&&Ti(m,n,t,e),p&&$r(t,e,n,"beforeUpdate"),n&&$o(n,!0);const v=o&&"foreignObject"!==t.type;if(f?N(e.dynamicChildren,f,s,n,r,v,l):a||j(e,t,s,null,n,r,v,l,!1),c>0){if(16&c)L(s,t,d,h,n,r,o);else if(2&c&&d.class!==h.class&&i(s,"class",null,h.class,o),4&c&&i(s,"style",d.style,h.style,o),8&c){const l=t.dynamicProps;for(let t=0;t{m&&Ti(m,n,t,e),p&&$r(t,e,n,"updated")}),r)},N=(e,t,n,r,o,i,l)=>{for(let a=0;a{if(n!==r){if(n!==k)for(const s in n)q(s)||s in r||i(e,s,n[s],null,a,t.children,o,l,G);for(const s in r){if(q(s))continue;const c=r[s],u=n[s];c!==u&&"value"!==s&&i(e,s,u,c,a,t.children,o,l,G)}"value"in r&&i(e,"value",n.value,r.value)}},A=(e,t,n,o,i,l,s,c,u)=>{const f=t.el=e?e.el:a(""),p=t.anchor=e?e.anchor:a("");let{patchFlag:d,dynamicChildren:h,slotScopeIds:m}=t;m&&(c=c?c.concat(m):m),null==e?(r(f,n,o),r(p,n,o),C(t.children,n,p,i,l,s,c,u)):d>0&&64&d&&h&&e.dynamicChildren?(N(e.dynamicChildren,h,n,i,l,s,c),(null!=t.key||i&&t===i.subTree)&&Uo(e,t,!0)):j(e,t,n,p,i,l,s,c,u)},P=(e,t,n,r,o,i,l,a,s)=>{t.slotScopeIds=a,null==e?512&t.shapeFlag?o.ctx.activate(t,n,r,l,s):R(t,n,r,o,i,l,s):F(e,t,s)},R=(e,t,n,r,o,i,l)=>{const a=e.component=Oi(e,r,o);if(_r(e)&&(a.ctx.renderer=Y),Vi(a),a.asyncDep){if(o&&o.registerDep(a,M),!e.el){const e=a.subTree=vi(Zo);g(null,e,t,n)}}else M(a,e,t,n,o,i,l)},F=(e,t,n)=>{const r=t.component=e.component;if(function(e,t,n){const{props:r,children:o,component:i}=e,{props:l,children:a,patchFlag:s}=t,c=i.emitsOptions;if(t.dirs||t.transition)return!0;if(!(n&&s>=0))return!(!o&&!a||a&&a.$stable)||r!==l&&(r?!l||Dn(r,l,c):!!l);if(1024&s)return!0;if(16&s)return r?Dn(r,l,c):!!l;if(8&s){const e=t.dynamicProps;for(let t=0;tan&&ln.splice(t,1)}(r.update),r.update()}else t.el=e.el,r.vnode=t},M=(e,t,n,r,o,i,l)=>{const a=e.effect=new xe((()=>{if(e.isMounted){let t,{next:n,bu:r,u:a,parent:s,vnode:c}=e,u=n;0,$o(e,!1),n?(n.el=c.el,B(e,n,l)):n=c,r&&re(r),(t=n.props&&n.props.onVnodeBeforeUpdate)&&Ti(t,s,n,c),$o(e,!0);const p=Mn(e);0;const d=e.subTree;e.subTree=p,m(d,p,f(d.el),J(d),e,o,i),n.el=p.el,null===u&&$n(e,p.el),a&&Bo(a,o),(t=n.props&&n.props.onVnodeUpdated)&&Bo((()=>Ti(t,s,n,c)),o)}else{let l;const{el:a,props:s}=t,{bm:c,m:u,parent:f}=e,p=vr(t);if($o(e,!1),c&&re(c),!p&&(l=s&&s.onVnodeBeforeMount)&&Ti(l,f,t),$o(e,!0),a&&ee){const n=()=>{e.subTree=Mn(e),ee(a,e.subTree,e,o,null)};p?t.type.__asyncLoader().then((()=>!e.isUnmounted&&n())):n()}else{0;const l=e.subTree=Mn(e);0,m(null,l,n,r,e,o,i),t.el=l.el}if(u&&Bo(u,o),!p&&(l=s&&s.onVnodeMounted)){const e=t;Bo((()=>Ti(l,f,e)),o)}(256&t.shapeFlag||f&&vr(f.vnode)&&256&f.vnode.shapeFlag)&&e.a&&Bo(e.a,o),e.isMounted=!0,t=n=r=null}}),(()=>hn(s)),e.scope),s=e.update=()=>a.run();s.id=e.uid,$o(e,!0),s()},B=(e,t,n)=>{t.component=e;const r=e.vnode.props;e.vnode=t,e.next=null,function(e,t,n,r){const{props:o,attrs:i,vnode:{patchFlag:l}}=e,a=At(o),[s]=e.propsOptions;let c=!1;if(!(r||l>0)||16&l){let r;go(e,t,o,i)&&(c=!0);for(const i in a)t&&(I(t,i)||(r=Q(i))!==i&&I(t,r))||(s?!n||void 0===n[i]&&void 0===n[r]||(o[i]=yo(s,a,i,void 0,e,!0)):delete o[i]);if(i!==a)for(const e in i)t&&I(t,e)||(delete i[e],c=!0)}else if(8&l){const n=e.vnode.dynamicProps;for(let r=0;r{const{vnode:r,slots:o}=e;let i=!0,l=k;if(32&r.shapeFlag){const e=t._;e?n&&1===e?i=!1:(O(o,t),n||1!==e||delete o._):(i=!t.$stable,To(t,o)),l=t}else t&&(No(e,t),l={default:1});if(i)for(const e in o)So(e)||e in l||delete o[e]})(e,t.children,n),Ne(),gn(),Le()},j=(e,t,n,r,o,i,l,a,s=!1)=>{const c=e&&e.children,f=e?e.shapeFlag:0,p=t.children,{patchFlag:d,shapeFlag:h}=t;if(d>0){if(128&d)return void D(c,p,n,r,o,i,l,a,s);if(256&d)return void V(c,p,n,r,o,i,l,a,s)}8&h?(16&f&&G(c,o,i),p!==c&&u(n,p)):16&f?16&h?D(c,p,n,r,o,i,l,a,s):G(c,o,i,!0):(8&f&&u(n,""),16&h&&C(p,n,r,o,i,l,a,s))},V=(e,t,n,r,o,i,l,a,s)=>{t=t||S;const c=(e=e||S).length,u=t.length,f=Math.min(c,u);let p;for(p=0;pu?G(e,o,i,!0,!1,f):C(t,n,r,o,i,l,a,s,f)},D=(e,t,n,r,o,i,l,a,s)=>{let c=0;const u=t.length;let f=e.length-1,p=u-1;for(;c<=f&&c<=p;){const r=e[c],u=t[c]=s?Si(t[c]):ki(t[c]);if(!ui(r,u))break;m(r,u,n,null,o,i,l,a,s),c++}for(;c<=f&&c<=p;){const r=e[f],c=t[p]=s?Si(t[p]):ki(t[p]);if(!ui(r,c))break;m(r,c,n,null,o,i,l,a,s),f--,p--}if(c>f){if(c<=p){const e=p+1,f=ep)for(;c<=f;)U(e[c],o,i,!0),c++;else{const d=c,h=c,v=new Map;for(c=h;c<=p;c++){const e=t[c]=s?Si(t[c]):ki(t[c]);null!=e.key&&v.set(e.key,c)}let g,y=0;const _=p-h+1;let b=!1,w=0;const x=new Array(_);for(c=0;c<_;c++)x[c]=0;for(c=d;c<=f;c++){const r=e[c];if(y>=_){U(r,o,i,!0);continue}let u;if(null!=r.key)u=v.get(r.key);else for(g=h;g<=p;g++)if(0===x[g-h]&&ui(r,t[g])){u=g;break}void 0===u?U(r,o,i,!0):(x[u-h]=c+1,u>=w?w=u:b=!0,m(r,t[u],n,null,o,i,l,a,s),y++)}const k=b?function(e){const t=e.slice(),n=[0];let r,o,i,l,a;const s=e.length;for(r=0;r>1,e[n[a]]0&&(t[r]=n[i-1]),n[i]=r)}}i=n.length,l=n[i-1];for(;i-- >0;)n[i]=l,l=t[l];return n}(x):S;for(g=k.length-1,c=_-1;c>=0;c--){const e=h+c,f=t[e],p=e+1{const{el:l,type:a,transition:s,children:c,shapeFlag:u}=e;if(6&u)return void $(e.component.subTree,t,n,o);if(128&u)return void e.suspense.move(t,n,o);if(64&u)return void a.move(e,t,n,Y);if(a===Ko){r(l,t,n);for(let e=0;e{let i;for(;e&&e!==t;)i=p(e),r(e,n,o),e=i;r(t,n,o)})(e,t,n);if(2!==o&&1&u&&s)if(0===o)s.beforeEnter(l),r(l,t,n),Bo((()=>s.enter(l)),i);else{const{leave:e,delayLeave:o,afterLeave:i}=s,a=()=>r(l,t,n),c=()=>{e(l,(()=>{a(),i&&i()}))};o?o(l,a,c):c()}else r(l,t,n)},U=(e,t,n,r=!1,o=!1)=>{const{type:i,props:l,ref:a,children:s,dynamicChildren:c,shapeFlag:u,patchFlag:f,dirs:p}=e;if(null!=a&&Po(a,null,n,e,!0),256&u)return void t.ctx.deactivate(e);const d=1&u&&p,h=!vr(e);let m;if(h&&(m=l&&l.onVnodeBeforeUnmount)&&Ti(m,t,e),6&u)z(e.component,n,r);else{if(128&u)return void e.suspense.unmount(n,r);d&&$r(e,null,t,"beforeUnmount"),64&u?e.type.remove(e,t,n,o,Y,r):c&&(i!==Ko||f>0&&64&f)?G(c,t,n,!1,!0):(i===Ko&&384&f||!o&&16&u)&&G(s,t,n),r&&W(e)}(h&&(m=l&&l.onVnodeUnmounted)||d)&&Bo((()=>{m&&Ti(m,t,e),d&&$r(e,null,t,"unmounted")}),n)},W=e=>{const{type:t,el:n,anchor:r,transition:i}=e;if(t===Ko)return void H(n,r);if(t===Xo)return void _(e);const l=()=>{o(n),i&&!i.persisted&&i.afterLeave&&i.afterLeave()};if(1&e.shapeFlag&&i&&!i.persisted){const{leave:t,delayLeave:r}=i,o=()=>t(n,l);r?r(e.el,l,o):o()}else l()},H=(e,t)=>{let n;for(;e!==t;)n=p(e),o(e),e=n;o(t)},z=(e,t,n)=>{const{bum:r,scope:o,update:i,subTree:l,um:a}=e;r&&re(r),o.stop(),i&&(i.active=!1,U(l,e,t,n)),a&&Bo(a,t),Bo((()=>{e.isUnmounted=!0}),t),t&&t.pendingBranch&&!t.isUnmounted&&e.asyncDep&&!e.asyncResolved&&e.suspenseId===t.pendingId&&(t.deps--,0===t.deps&&t.resolve())},G=(e,t,n,r=!1,o=!1,i=0)=>{for(let l=i;l6&e.shapeFlag?J(e.component.subTree):128&e.shapeFlag?e.suspense.next():p(e.anchor||e.el),K=(e,t,n)=>{null==e?t._vnode&&U(t._vnode,null,null,!0):m(t._vnode||null,e,t,null,null,null,n),gn(),yn(),t._vnode=e},Y={p:m,um:U,m:$,r:W,mt:R,mc:C,pc:j,pbc:N,n:J,o:e};let X,ee;return t&&([X,ee]=t(Y)),{render:K,hydrate:X,createApp:Ao(K,X)}}function $o({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function Uo(e,t,n=!1){const r=e.children,o=t.children;if(R(r)&&R(o))for(let e=0;ee&&(e.disabled||""===e.disabled),Ho=e=>"undefined"!=typeof SVGElement&&e instanceof SVGElement,zo=(e,t)=>{const n=e&&e.to;if(V(n)){if(t){const e=t(n);return e}return null}return n};function Go(e,t,n,{o:{insert:r},m:o},i=2){0===i&&r(e.targetAnchor,t,n);const{el:l,anchor:a,shapeFlag:s,children:c,props:u}=e,f=2===i;if(f&&r(l,t,n),(!f||Wo(u))&&16&s)for(let e=0;e{16&y&&u(_,e,t,o,i,l,a,s)};g?v(n,c):f&&v(f,p)}else{t.el=e.el;const r=t.anchor=e.anchor,u=t.target=e.target,d=t.targetAnchor=e.targetAnchor,m=Wo(e.props),v=m?n:u,y=m?r:d;if(l=l||Ho(u),b?(p(e.dynamicChildren,b,v,o,i,l,a),Uo(e,t,!0)):s||f(e,t,v,y,o,i,l,a,!1),g)m||Go(t,n,r,c,1);else if((t.props&&t.props.to)!==(e.props&&e.props.to)){const e=t.target=zo(t.props,h);e&&Go(t,e,null,c,0)}else m&&Go(t,u,d,c,1)}Jo(t)},remove(e,t,n,r,{um:o,o:{remove:i}},l){const{shapeFlag:a,children:s,anchor:c,targetAnchor:u,target:f,props:p}=e;if(f&&i(u),(l||!Wo(p))&&(i(c),16&a))for(let e=0;e0?ei||S:null,ni(),oi>0&&ei&&ei.push(e),e}function ai(e,t,n,r,o,i){return li(mi(e,t,n,r,o,i,!0))}function si(e,t,n,r,o){return li(vi(e,t,n,r,o,!0))}function ci(e){return!!e&&!0===e.__v_isVNode}function ui(e,t){return e.type===t.type&&e.key===t.key}function fi(e){ri=e}const pi="__vInternal",di=({key:e})=>null!=e?e:null,hi=({ref:e,ref_key:t,ref_for:n})=>null!=e?V(e)||Bt(e)||j(e)?{i:Ln,r:e,k:t,f:!!n}:e:null;function mi(e,t=null,n=null,r=0,o=null,i=(e===Ko?0:1),l=!1,a=!1){const s={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&di(t),ref:t&&hi(t),scopeId:On,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:r,dynamicProps:o,dynamicChildren:null,appContext:null,ctx:Ln};return a?(Ei(s,n),128&i&&e.normalize(s)):n&&(s.shapeFlag|=V(n)?8:16),oi>0&&!l&&ei&&(s.patchFlag>0||6&i)&&32!==s.patchFlag&&ei.push(s),s}const vi=gi;function gi(e,t=null,n=null,r=0,o=null,i=!1){if(e&&e!==Hr||(e=Zo),ci(e)){const r=_i(e,t,!0);return n&&Ei(r,n),oi>0&&!i&&ei&&(6&r.shapeFlag?ei[ei.indexOf(e)]=r:ei.push(r)),r.patchFlag|=-2,r}if(qi(e)&&(e=e.__vccOpts),t){t=yi(t);let{class:e,style:n}=t;e&&!V(e)&&(t.class=f(e)),$(n)&&(Ot(n)&&!R(n)&&(n=O({},n)),t.style=l(n))}return mi(e,t,n,r,o,V(e)?1:Un(e)?128:(e=>e.__isTeleport)(e)?64:$(e)?4:j(e)?2:0,i,!0)}function yi(e){return e?Ot(e)||pi in e?O({},e):e:null}function _i(e,t,n=!1){const{props:r,ref:o,patchFlag:i,children:l}=e,a=t?Ci(r||{},t):r;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:a,key:a&&di(a),ref:t&&t.ref?n&&o?R(o)?o.concat(hi(t)):[o,hi(t)]:hi(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:l,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Ko?-1===i?16:16|i:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&_i(e.ssContent),ssFallback:e.ssFallback&&_i(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx}}function bi(e=" ",t=0){return vi(Yo,null,e,t)}function wi(e,t){const n=vi(Xo,null,e);return n.staticCount=t,n}function xi(e="",t=!1){return t?(ti(),si(Zo,null,e)):vi(Zo,null,e)}function ki(e){return null==e||"boolean"==typeof e?vi(Zo):R(e)?vi(Ko,null,e.slice()):"object"==typeof e?Si(e):vi(Yo,null,String(e))}function Si(e){return null===e.el&&-1!==e.patchFlag||e.memo?e:_i(e)}function Ei(e,t){let n=0;const{shapeFlag:r}=e;if(null==t)t=null;else if(R(t))n=16;else if("object"==typeof t){if(65&r){const n=t.default;return void(n&&(n._c&&(n._d=!1),Ei(e,n()),n._c&&(n._d=!0)))}{n=32;const r=t._;r||pi in t?3===r&&Ln&&(1===Ln.slots._?t._=1:(t._=2,e.patchFlag|=1024)):t._ctx=Ln}}else j(t)?(t={default:t,_ctx:Ln},n=32):(t=String(t),64&r?(n=16,t=[bi(t)]):n=8);e.children=t,e.shapeFlag|=n}function Ci(...e){const t={};for(let n=0;nAi||Ln,Ii=e=>{Ai=e,e.scope.on()},Ri=()=>{Ai&&Ai.scope.off(),Ai=null};function Fi(e){return 4&e.vnode.shapeFlag}let Mi,Bi,ji=!1;function Vi(e,t=!1){ji=t;const{props:n,children:r}=e.vnode,o=Fi(e);!function(e,t,n,r=!1){const o={},i={};oe(i,pi,1),e.propsDefaults=Object.create(null),go(e,t,o,i);for(const t in e.propsOptions[0])t in o||(o[t]=void 0);n?e.props=r?o:kt(o):e.type.props?e.props=o:e.props=i,e.attrs=i}(e,n,o,t),((e,t)=>{if(32&e.vnode.shapeFlag){const n=t._;n?(e.slots=At(t),oe(t,"_",n)):To(t,e.slots={})}else e.slots={},t&&No(e,t);oe(e.slots,pi,1)})(e,r);const i=o?function(e,t){const n=e.type;0;e.accessCache=Object.create(null),e.proxy=Pt(new Proxy(e.ctx,ro)),!1;const{setup:r}=n;if(r){const n=e.setupContext=r.length>1?Hi(e):null;Ii(e),Ne();const o=en(r,e,0,[e.props,n]);if(Le(),Ri(),U(o)){if(o.then(Ri,Ri),t)return o.then((n=>{Di(e,n,t)})).catch((t=>{nn(t,e,0)}));e.asyncDep=o}else Di(e,o,t)}else Wi(e,t)}(e,t):void 0;return ji=!1,i}function Di(e,t,n){j(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:$(t)&&(e.setupState=zt(t)),Wi(e,n)}function $i(e){Mi=e,Bi=e=>{e.render._rc&&(e.withProxy=new Proxy(e.ctx,oo))}}const Ui=()=>!Mi;function Wi(e,t,n){const r=e.type;if(!e.render){if(!t&&Mi&&!r.render){const t=r.template||co(e).template;if(t){0;const{isCustomElement:n,compilerOptions:o}=e.appContext.config,{delimiters:i,compilerOptions:l}=r,a=O(O({isCustomElement:n,delimiters:i},o),l);r.render=Mi(t,a)}}e.render=r.render||E,Bi&&Bi(e)}Ii(e),Ne(),lo(e),Le(),Ri()}function Hi(e){const t=t=>{e.exposed=t||{}};let n;return{get attrs(){return n||(n=function(e){return new Proxy(e.attrs,{get:(t,n)=>(Oe(e,0,"$attrs"),t[n])})}(e))},slots:e.slots,emit:e.emit,expose:t}}function zi(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(zt(Pt(e.exposed)),{get:(t,n)=>n in t?t[n]:n in to?to[n](e):void 0,has:(e,t)=>t in e||t in to}))}function Gi(e,t=!0){return j(e)?e.displayName||e.name:e.name||t&&e.__name}function qi(e){return j(e)&&"__vccOpts"in e}const Ji=(e,t)=>function(e,t,n=!1){let r,o;const i=j(e);return i?(r=e,o=E):(r=e.get,o=e.set),new Xt(r,o,i||!o,n)}(e,0,ji);function Ki(){return null}function Yi(){return null}function Zi(e){0}function Xi(e,t){return null}function Qi(){return tl().slots}function el(){return tl().attrs}function tl(){const e=Pi();return e.setupContext||(e.setupContext=Hi(e))}function nl(e,t){const n=R(e)?e.reduce(((e,t)=>(e[t]={},e)),{}):e;for(const e in t){const r=n[e];r?R(r)||j(r)?n[e]={type:r,default:t[e]}:r.default=t[e]:null===r&&(n[e]={default:t[e]})}return n}function rl(e,t){const n={};for(const r in e)t.includes(r)||Object.defineProperty(n,r,{enumerable:!0,get:()=>e[r]});return n}function ol(e){const t=Pi();let n=e();return Ri(),U(n)&&(n=n.catch((e=>{throw Ii(t),e}))),[n,()=>Ii(t)]}function il(e,t,n){const r=arguments.length;return 2===r?$(t)&&!R(t)?ci(t)?vi(e,null,[t]):vi(e,t):vi(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):3===r&&ci(n)&&(n=[n]),vi(e,t,n))}const ll=Symbol(""),al=()=>{{const e=Yn(ll);return e}};function sl(){return void 0}function cl(e,t,n,r){const o=n[r];if(o&&ul(o,e))return o;const i=t();return i.memo=e.slice(),n[r]=i}function ul(e,t){const n=e.memo;if(n.length!=t.length)return!1;for(let e=0;e0&&ei&&ei.push(e),!0}const fl="3.2.45",pl={createComponentInstance:Oi,setupComponent:Vi,renderComponentRoot:Mn,setCurrentRenderingInstance:An,isVNode:ci,normalizeVNode:ki},dl=null,hl=null,ml="undefined"!=typeof document?document:null,vl=ml&&ml.createElement("template"),gl={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const o=t?ml.createElementNS("http://www.w3.org/2000/svg",e):ml.createElement(e,n?{is:n}:void 0);return"select"===e&&r&&null!=r.multiple&&o.setAttribute("multiple",r.multiple),o},createText:e=>ml.createTextNode(e),createComment:e=>ml.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>ml.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,r,o,i){const l=n?n.previousSibling:t.lastChild;if(o&&(o===i||o.nextSibling))for(;t.insertBefore(o.cloneNode(!0),n),o!==i&&(o=o.nextSibling););else{vl.innerHTML=r?`${e}`:e;const o=vl.content;if(r){const e=o.firstChild;for(;e.firstChild;)o.appendChild(e.firstChild);o.removeChild(e)}t.insertBefore(o,n)}return[l?l.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}};const yl=/\s*!important$/;function _l(e,t,n){if(R(n))n.forEach((n=>_l(e,t,n)));else if(null==n&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=function(e,t){const n=wl[t];if(n)return n;let r=Z(t);if("filter"!==r&&r in e)return wl[t]=r;r=ee(r);for(let n=0;n{if(e._vts){if(e._vts<=n.attached)return}else e._vts=Date.now();tn(function(e,t){if(R(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map((e=>t=>!t._stopped&&e&&e(t)))}return t}(e,n.value),t,5,[e])};return n.value=e,n.attached=(()=>Cl||(Tl.then((()=>Cl=0)),Cl=Date.now()))(),n}(r,o);kl(e,n,l,a)}else l&&(!function(e,t,n,r){e.removeEventListener(t,n,r)}(e,n,l,a),i[t]=void 0)}}const El=/(?:Once|Passive|Capture)$/;let Cl=0;const Tl=Promise.resolve();const Nl=/^on[a-z]/;function Ll(e,t){const n=mr(e);class r extends Pl{constructor(e){super(n,e,t)}}return r.def=n,r}const Ol=e=>Ll(e,Ra),Al="undefined"!=typeof HTMLElement?HTMLElement:class{};class Pl extends Al{constructor(e,t={},n){super(),this._def=e,this._props=t,this._instance=null,this._connected=!1,this._resolved=!1,this._numberProps=null,this.shadowRoot&&n?n(this._createVNode(),this.shadowRoot):(this.attachShadow({mode:"open"}),this._def.__asyncLoader||this._resolveProps(this._def))}connectedCallback(){this._connected=!0,this._instance||(this._resolved?this._update():this._resolveDef())}disconnectedCallback(){this._connected=!1,dn((()=>{this._connected||(Ia(null,this.shadowRoot),this._instance=null)}))}_resolveDef(){this._resolved=!0;for(let e=0;e{for(const t of e)this._setAttr(t.attributeName)})).observe(this,{attributes:!0});const e=(e,t=!1)=>{const{props:n,styles:r}=e;let o;if(n&&!R(n))for(const e in n){const t=n[e];(t===Number||t&&t.type===Number)&&(e in this._props&&(this._props[e]=ie(this._props[e])),(o||(o=Object.create(null)))[Z(e)]=!0)}this._numberProps=o,t&&this._resolveProps(e),this._applyStyles(r),this._update()},t=this._def.__asyncLoader;t?t().then((t=>e(t,!0))):e(this._def)}_resolveProps(e){const{props:t}=e,n=R(t)?t:Object.keys(t||{});for(const e of Object.keys(this))"_"!==e[0]&&n.includes(e)&&this._setProp(e,this[e],!0,!1);for(const e of n.map(Z))Object.defineProperty(this,e,{get(){return this._getProp(e)},set(t){this._setProp(e,t)}})}_setAttr(e){let t=this.getAttribute(e);const n=Z(e);this._numberProps&&this._numberProps[n]&&(t=ie(t)),this._setProp(n,t,!1)}_getProp(e){return this._props[e]}_setProp(e,t,n=!0,r=!0){t!==this._props[e]&&(this._props[e]=t,r&&this._instance&&this._update(),n&&(!0===t?this.setAttribute(Q(e),""):"string"==typeof t||"number"==typeof t?this.setAttribute(Q(e),t+""):t||this.removeAttribute(Q(e))))}_update(){Ia(this._createVNode(),this.shadowRoot)}_createVNode(){const e=vi(this._def,O({},this._props));return this._instance||(e.ce=e=>{this._instance=e,e.isCE=!0;const t=(e,t)=>{this.dispatchEvent(new CustomEvent(e,{detail:t}))};e.emit=(e,...n)=>{t(e,n),Q(e)!==e&&t(Q(e),n)};let n=this;for(;n=n&&(n.parentNode||n.host);)if(n instanceof Pl){e.parent=n._instance,e.provides=n._instance.provides;break}}),e}_applyStyles(e){e&&e.forEach((e=>{const t=document.createElement("style");t.textContent=e,this.shadowRoot.appendChild(t)}))}}function Il(e="$style"){{const t=Pi();if(!t)return k;const n=t.type.__cssModules;if(!n)return k;const r=n[e];return r||k}}function Rl(e){const t=Pi();if(!t)return;const n=t.ut=(n=e(t.proxy))=>{Array.from(document.querySelectorAll(`[data-v-owner="${t.uid}"]`)).forEach((e=>Ml(e,n)))},r=()=>{const r=e(t.proxy);Fl(t.subTree,r),n(r)};Xn(r),Ar((()=>{const e=new MutationObserver(r);e.observe(t.subTree.el.parentNode,{childList:!0}),Fr((()=>e.disconnect()))}))}function Fl(e,t){if(128&e.shapeFlag){const n=e.suspense;e=n.activeBranch,n.pendingBranch&&!n.isHydrating&&n.effects.push((()=>{Fl(n.activeBranch,t)}))}for(;e.component;)e=e.component.subTree;if(1&e.shapeFlag&&e.el)Ml(e.el,t);else if(e.type===Ko)e.children.forEach((e=>Fl(e,t)));else if(e.type===Xo){let{el:n,anchor:r}=e;for(;n&&(Ml(n,t),n!==r);)n=n.nextSibling}}function Ml(e,t){if(1===e.nodeType){const n=e.style;for(const e in t)n.setProperty(`--${e}`,t[e])}}const Bl="transition",jl="animation",Vl=(e,{slots:t})=>il(sr,Hl(e),t);Vl.displayName="Transition";const Dl={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},$l=Vl.props=O({},sr.props,Dl),Ul=(e,t=[])=>{R(e)?e.forEach((e=>e(...t))):e&&e(...t)},Wl=e=>!!e&&(R(e)?e.some((e=>e.length>1)):e.length>1);function Hl(e){const t={};for(const n in e)n in Dl||(t[n]=e[n]);if(!1===e.css)return t;const{name:n="v",type:r,duration:o,enterFromClass:i=`${n}-enter-from`,enterActiveClass:l=`${n}-enter-active`,enterToClass:a=`${n}-enter-to`,appearFromClass:s=i,appearActiveClass:c=l,appearToClass:u=a,leaveFromClass:f=`${n}-leave-from`,leaveActiveClass:p=`${n}-leave-active`,leaveToClass:d=`${n}-leave-to`}=e,h=function(e){if(null==e)return null;if($(e))return[zl(e.enter),zl(e.leave)];{const t=zl(e);return[t,t]}}(o),m=h&&h[0],v=h&&h[1],{onBeforeEnter:g,onEnter:y,onEnterCancelled:_,onLeave:b,onLeaveCancelled:w,onBeforeAppear:x=g,onAppear:k=y,onAppearCancelled:S=_}=t,E=(e,t,n)=>{ql(e,t?u:a),ql(e,t?c:l),n&&n()},C=(e,t)=>{e._isLeaving=!1,ql(e,f),ql(e,d),ql(e,p),t&&t()},T=e=>(t,n)=>{const o=e?k:y,l=()=>E(t,e,n);Ul(o,[t,l]),Jl((()=>{ql(t,e?s:i),Gl(t,e?u:a),Wl(o)||Yl(t,r,m,l)}))};return O(t,{onBeforeEnter(e){Ul(g,[e]),Gl(e,i),Gl(e,l)},onBeforeAppear(e){Ul(x,[e]),Gl(e,s),Gl(e,c)},onEnter:T(!1),onAppear:T(!0),onLeave(e,t){e._isLeaving=!0;const n=()=>C(e,t);Gl(e,f),ea(),Gl(e,p),Jl((()=>{e._isLeaving&&(ql(e,f),Gl(e,d),Wl(b)||Yl(e,r,v,n))})),Ul(b,[e,n])},onEnterCancelled(e){E(e,!1),Ul(_,[e])},onAppearCancelled(e){E(e,!0),Ul(S,[e])},onLeaveCancelled(e){C(e),Ul(w,[e])}})}function zl(e){return ie(e)}function Gl(e,t){t.split(/\s+/).forEach((t=>t&&e.classList.add(t))),(e._vtc||(e._vtc=new Set)).add(t)}function ql(e,t){t.split(/\s+/).forEach((t=>t&&e.classList.remove(t)));const{_vtc:n}=e;n&&(n.delete(t),n.size||(e._vtc=void 0))}function Jl(e){requestAnimationFrame((()=>{requestAnimationFrame(e)}))}let Kl=0;function Yl(e,t,n,r){const o=e._endId=++Kl,i=()=>{o===e._endId&&r()};if(n)return setTimeout(i,n);const{type:l,timeout:a,propCount:s}=Zl(e,t);if(!l)return r();const c=l+"end";let u=0;const f=()=>{e.removeEventListener(c,p),i()},p=t=>{t.target===e&&++u>=s&&f()};setTimeout((()=>{u(n[e]||"").split(", "),o=r("transitionDelay"),i=r("transitionDuration"),l=Xl(o,i),a=r("animationDelay"),s=r("animationDuration"),c=Xl(a,s);let u=null,f=0,p=0;t===Bl?l>0&&(u=Bl,f=l,p=i.length):t===jl?c>0&&(u=jl,f=c,p=s.length):(f=Math.max(l,c),u=f>0?l>c?Bl:jl:null,p=u?u===Bl?i.length:s.length:0);return{type:u,timeout:f,propCount:p,hasTransform:u===Bl&&/\b(transform|all)(,|$)/.test(r("transitionProperty").toString())}}function Xl(e,t){for(;e.lengthQl(t)+Ql(e[n]))))}function Ql(e){return 1e3*Number(e.slice(0,-1).replace(",","."))}function ea(){return document.body.offsetHeight}const ta=new WeakMap,na=new WeakMap,ra={name:"TransitionGroup",props:O({},$l,{tag:String,moveClass:String}),setup(e,{slots:t}){const n=Pi(),r=lr();let o,i;return Ir((()=>{if(!o.length)return;const t=e.moveClass||`${e.name||"v"}-move`;if(!function(e,t,n){const r=e.cloneNode();e._vtc&&e._vtc.forEach((e=>{e.split(/\s+/).forEach((e=>e&&r.classList.remove(e)))}));n.split(/\s+/).forEach((e=>e&&r.classList.add(e))),r.style.display="none";const o=1===t.nodeType?t:t.parentNode;o.appendChild(r);const{hasTransform:i}=Zl(r);return o.removeChild(r),i}(o[0].el,n.vnode.el,t))return;o.forEach(oa),o.forEach(ia);const r=o.filter(la);ea(),r.forEach((e=>{const n=e.el,r=n.style;Gl(n,t),r.transform=r.webkitTransform=r.transitionDuration="";const o=n._moveCb=e=>{e&&e.target!==n||e&&!/transform$/.test(e.propertyName)||(n.removeEventListener("transitionend",o),n._moveCb=null,ql(n,t))};n.addEventListener("transitionend",o)}))})),()=>{const l=At(e),a=Hl(l);let s=l.tag||Ko;o=i,i=t.default?hr(t.default()):[];for(let e=0;e{const t=e.props["onUpdate:modelValue"]||!1;return R(t)?e=>re(t,e):t};function sa(e){e.target.composing=!0}function ca(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const ua={created(e,{modifiers:{lazy:t,trim:n,number:r}},o){e._assign=aa(o);const i=r||o.props&&"number"===o.props.type;kl(e,t?"change":"input",(t=>{if(t.target.composing)return;let r=e.value;n&&(r=r.trim()),i&&(r=ie(r)),e._assign(r)})),n&&kl(e,"change",(()=>{e.value=e.value.trim()})),t||(kl(e,"compositionstart",sa),kl(e,"compositionend",ca),kl(e,"change",ca))},mounted(e,{value:t}){e.value=null==t?"":t},beforeUpdate(e,{value:t,modifiers:{lazy:n,trim:r,number:o}},i){if(e._assign=aa(i),e.composing)return;if(document.activeElement===e&&"range"!==e.type){if(n)return;if(r&&e.value.trim()===t)return;if((o||"number"===e.type)&&ie(e.value)===t)return}const l=null==t?"":t;e.value!==l&&(e.value=l)}},fa={deep:!0,created(e,t,n){e._assign=aa(n),kl(e,"change",(()=>{const t=e._modelValue,n=va(e),r=e.checked,o=e._assign;if(R(t)){const e=b(t,n),i=-1!==e;if(r&&!i)o(t.concat(n));else if(!r&&i){const n=[...t];n.splice(e,1),o(n)}}else if(M(t)){const e=new Set(t);r?e.add(n):e.delete(n),o(e)}else o(ga(e,r))}))},mounted:pa,beforeUpdate(e,t,n){e._assign=aa(n),pa(e,t,n)}};function pa(e,{value:t,oldValue:n},r){e._modelValue=t,R(t)?e.checked=b(t,r.props.value)>-1:M(t)?e.checked=t.has(r.props.value):t!==n&&(e.checked=_(t,ga(e,!0)))}const da={created(e,{value:t},n){e.checked=_(t,n.props.value),e._assign=aa(n),kl(e,"change",(()=>{e._assign(va(e))}))},beforeUpdate(e,{value:t,oldValue:n},r){e._assign=aa(r),t!==n&&(e.checked=_(t,r.props.value))}},ha={deep:!0,created(e,{value:t,modifiers:{number:n}},r){const o=M(t);kl(e,"change",(()=>{const t=Array.prototype.filter.call(e.options,(e=>e.selected)).map((e=>n?ie(va(e)):va(e)));e._assign(e.multiple?o?new Set(t):t:t[0])})),e._assign=aa(r)},mounted(e,{value:t}){ma(e,t)},beforeUpdate(e,t,n){e._assign=aa(n)},updated(e,{value:t}){ma(e,t)}};function ma(e,t){const n=e.multiple;if(!n||R(t)||M(t)){for(let r=0,o=e.options.length;r-1:o.selected=t.has(i);else if(_(va(o),t))return void(e.selectedIndex!==r&&(e.selectedIndex=r))}n||-1===e.selectedIndex||(e.selectedIndex=-1)}}function va(e){return"_value"in e?e._value:e.value}function ga(e,t){const n=t?"_trueValue":"_falseValue";return n in e?e[n]:t}const ya={created(e,t,n){ba(e,t,n,null,"created")},mounted(e,t,n){ba(e,t,n,null,"mounted")},beforeUpdate(e,t,n,r){ba(e,t,n,r,"beforeUpdate")},updated(e,t,n,r){ba(e,t,n,r,"updated")}};function _a(e,t){switch(e){case"SELECT":return ha;case"TEXTAREA":return ua;default:switch(t){case"checkbox":return fa;case"radio":return da;default:return ua}}}function ba(e,t,n,r,o){const i=_a(e.tagName,n.props&&n.props.type)[o];i&&i(e,t,n,r)}const wa=["ctrl","shift","alt","meta"],xa={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&0!==e.button,middle:e=>"button"in e&&1!==e.button,right:e=>"button"in e&&2!==e.button,exact:(e,t)=>wa.some((n=>e[`${n}Key`]&&!t.includes(n)))},ka=(e,t)=>(n,...r)=>{for(let e=0;en=>{if(!("key"in n))return;const r=Q(n.key);return t.some((e=>e===r||Sa[e]===r))?e(n):void 0},Ca={beforeMount(e,{value:t},{transition:n}){e._vod="none"===e.style.display?"":e.style.display,n&&t?n.beforeEnter(e):Ta(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:r}){!t!=!n&&(r?t?(r.beforeEnter(e),Ta(e,!0),r.enter(e)):r.leave(e,(()=>{Ta(e,!1)})):Ta(e,t))},beforeUnmount(e,{value:t}){Ta(e,t)}};function Ta(e,t){e.style.display=t?e._vod:"none"}const Na=O({patchProp:(e,t,n,r,o=!1,i,l,a,s)=>{"class"===t?function(e,t,n){const r=e._vtc;r&&(t=(t?[t,...r]:[...r]).join(" ")),null==t?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}(e,r,o):"style"===t?function(e,t,n){const r=e.style,o=V(n);if(n&&!o){for(const e in n)_l(r,e,n[e]);if(t&&!V(t))for(const e in t)null==n[e]&&_l(r,e,"")}else{const i=r.display;o?t!==n&&(r.cssText=n):t&&e.removeAttribute("style"),"_vod"in e&&(r.display=i)}}(e,n,r):N(t)?L(t)||Sl(e,t,0,r,l):("."===t[0]?(t=t.slice(1),1):"^"===t[0]?(t=t.slice(1),0):function(e,t,n,r){if(r)return"innerHTML"===t||"textContent"===t||!!(t in e&&Nl.test(t)&&j(n));if("spellcheck"===t||"draggable"===t||"translate"===t)return!1;if("form"===t)return!1;if("list"===t&&"INPUT"===e.tagName)return!1;if("type"===t&&"TEXTAREA"===e.tagName)return!1;if(Nl.test(t)&&V(n))return!1;return t in e}(e,t,r,o))?function(e,t,n,r,o,i,l){if("innerHTML"===t||"textContent"===t)return r&&l(r,o,i),void(e[t]=null==n?"":n);if("value"===t&&"PROGRESS"!==e.tagName&&!e.tagName.includes("-")){e._value=n;const r=null==n?"":n;return e.value===r&&"OPTION"!==e.tagName||(e.value=r),void(null==n&&e.removeAttribute(t))}let a=!1;if(""===n||null==n){const r=typeof e[t];"boolean"===r?n=y(n):null==n&&"string"===r?(n="",a=!0):"number"===r&&(n=0,a=!0)}try{e[t]=n}catch(e){}a&&e.removeAttribute(t)}(e,t,r,i,l,a,s):("true-value"===t?e._trueValue=r:"false-value"===t&&(e._falseValue=r),function(e,t,n,r,o){if(r&&t.startsWith("xlink:"))null==n?e.removeAttributeNS(xl,t.slice(6,t.length)):e.setAttributeNS(xl,t,n);else{const r=g(t);null==n||r&&!y(n)?e.removeAttribute(t):e.setAttribute(t,r?"":n)}}(e,t,r,o))}},gl);let La,Oa=!1;function Aa(){return La||(La=jo(Na))}function Pa(){return La=Oa?La:Vo(Na),Oa=!0,La}const Ia=(...e)=>{Aa().render(...e)},Ra=(...e)=>{Pa().hydrate(...e)},Fa=(...e)=>{const t=Aa().createApp(...e);const{mount:n}=t;return t.mount=e=>{const r=Ba(e);if(!r)return;const o=t._component;j(o)||o.render||o.template||(o.template=r.innerHTML),r.innerHTML="";const i=n(r,!1,r instanceof SVGElement);return r instanceof Element&&(r.removeAttribute("v-cloak"),r.setAttribute("data-v-app","")),i},t},Ma=(...e)=>{const t=Pa().createApp(...e);const{mount:n}=t;return t.mount=e=>{const t=Ba(e);if(t)return n(t,!0,t instanceof SVGElement)},t};function Ba(e){if(V(e)){return document.querySelector(e)}return e}let ja=!1;const Va=()=>{ja||(ja=!0,ua.getSSRProps=({value:e})=>({value:e}),da.getSSRProps=({value:e},t)=>{if(t.props&&_(t.props.value,e))return{checked:!0}},fa.getSSRProps=({value:e},t)=>{if(R(e)){if(t.props&&b(e,t.props.value)>-1)return{checked:!0}}else if(M(e)){if(t.props&&e.has(t.props.value))return{checked:!0}}else if(e)return{checked:!0}},ya.getSSRProps=(e,t)=>{if("string"!=typeof t.type)return;const n=_a(t.type.toUpperCase(),t.props&&t.props.type);return n.getSSRProps?n.getSSRProps(e,t):void 0},Ca.getSSRProps=({value:e})=>{if(!e)return{style:{display:"none"}}})};function Da(e){throw e}function $a(e){}function Ua(e,t,n,r){const o=new SyntaxError(String(e));return o.code=e,o.loc=t,o}const Wa=Symbol(""),Ha=Symbol(""),za=Symbol(""),Ga=Symbol(""),qa=Symbol(""),Ja=Symbol(""),Ka=Symbol(""),Ya=Symbol(""),Za=Symbol(""),Xa=Symbol(""),Qa=Symbol(""),es=Symbol(""),ts=Symbol(""),ns=Symbol(""),rs=Symbol(""),os=Symbol(""),is=Symbol(""),ls=Symbol(""),as=Symbol(""),ss=Symbol(""),cs=Symbol(""),us=Symbol(""),fs=Symbol(""),ps=Symbol(""),ds=Symbol(""),hs=Symbol(""),ms=Symbol(""),vs=Symbol(""),gs=Symbol(""),ys=Symbol(""),_s=Symbol(""),bs=Symbol(""),ws=Symbol(""),xs=Symbol(""),ks=Symbol(""),Ss=Symbol(""),Es=Symbol(""),Cs=Symbol(""),Ts=Symbol(""),Ns={[Wa]:"Fragment",[Ha]:"Teleport",[za]:"Suspense",[Ga]:"KeepAlive",[qa]:"BaseTransition",[Ja]:"openBlock",[Ka]:"createBlock",[Ya]:"createElementBlock",[Za]:"createVNode",[Xa]:"createElementVNode",[Qa]:"createCommentVNode",[es]:"createTextVNode",[ts]:"createStaticVNode",[ns]:"resolveComponent",[rs]:"resolveDynamicComponent",[os]:"resolveDirective",[is]:"resolveFilter",[ls]:"withDirectives",[as]:"renderList",[ss]:"renderSlot",[cs]:"createSlots",[us]:"toDisplayString",[fs]:"mergeProps",[ps]:"normalizeClass",[ds]:"normalizeStyle",[hs]:"normalizeProps",[ms]:"guardReactiveProps",[vs]:"toHandlers",[gs]:"camelize",[ys]:"capitalize",[_s]:"toHandlerKey",[bs]:"setBlockTracking",[ws]:"pushScopeId",[xs]:"popScopeId",[ks]:"withCtx",[Ss]:"unref",[Es]:"isRef",[Cs]:"withMemo",[Ts]:"isMemoSame"};const Ls={source:"",start:{line:1,column:1,offset:0},end:{line:1,column:1,offset:0}};function Os(e,t,n,r,o,i,l,a=!1,s=!1,c=!1,u=Ls){return e&&(a?(e.helper(Ja),e.helper(ic(e.inSSR,c))):e.helper(oc(e.inSSR,c)),l&&e.helper(ls)),{type:13,tag:t,props:n,children:r,patchFlag:o,dynamicProps:i,directives:l,isBlock:a,disableTracking:s,isComponent:c,loc:u}}function As(e,t=Ls){return{type:17,loc:t,elements:e}}function Ps(e,t=Ls){return{type:15,loc:t,properties:e}}function Is(e,t){return{type:16,loc:Ls,key:V(e)?Rs(e,!0):e,value:t}}function Rs(e,t=!1,n=Ls,r=0){return{type:4,loc:n,content:e,isStatic:t,constType:t?3:r}}function Fs(e,t=Ls){return{type:8,loc:t,children:e}}function Ms(e,t=[],n=Ls){return{type:14,loc:n,callee:e,arguments:t}}function Bs(e,t,n=!1,r=!1,o=Ls){return{type:18,params:e,returns:t,newline:n,isSlot:r,loc:o}}function js(e,t,n,r=!0){return{type:19,test:e,consequent:t,alternate:n,newline:r,loc:Ls}}const Vs=e=>4===e.type&&e.isStatic,Ds=(e,t)=>e===t||e===Q(t);function $s(e){return Ds(e,"Teleport")?Ha:Ds(e,"Suspense")?za:Ds(e,"KeepAlive")?Ga:Ds(e,"BaseTransition")?qa:void 0}const Us=/^\d|[^\$\w]/,Ws=e=>!Us.test(e),Hs=/[A-Za-z_$\xA0-\uFFFF]/,zs=/[\.\?\w$\xA0-\uFFFF]/,Gs=/\s+[.[]\s*|\s*[.[]\s+/g,qs=e=>{e=e.trim().replace(Gs,(e=>e.trim()));let t=0,n=[],r=0,o=0,i=null;for(let l=0;l4===e.key.type&&e.key.content===r))}return n}function uc(e,t){return`_${t}_${e.replace(/[^\w]/g,((t,n)=>"-"===t?"_":e.charCodeAt(n).toString()))}`}function fc(e,{helper:t,removeHelper:n,inSSR:r}){e.isBlock||(e.isBlock=!0,n(oc(r,e.isComponent)),t(Ja),t(ic(r,e.isComponent)))}function pc(e,t){const n=t.options?t.options.compatConfig:t.compatConfig,r=n&&n[e];return"MODE"===e?r||3:r}function dc(e,t){const n=pc("MODE",t),r=pc(e,t);return 3===n?!0===r:!1!==r}function hc(e,t,n,...r){return dc(e,t)}const mc=/&(gt|lt|amp|apos|quot);/g,vc={gt:">",lt:"<",amp:"&",apos:"'",quot:'"'},gc={delimiters:["{{","}}"],getNamespace:()=>0,getTextMode:()=>0,isVoidTag:C,isPreTag:C,isCustomElement:C,decodeEntities:e=>e.replace(mc,((e,t)=>vc[t])),onError:Da,onWarn:$a,comments:!1};function yc(e,t={}){const n=function(e,t){const n=O({},gc);let r;for(r in t)n[r]=void 0===t[r]?gc[r]:t[r];return{options:n,column:1,line:1,offset:0,originalSource:e,source:e,inPre:!1,inVPre:!1,onWarn:n.onWarn}}(e,t),r=Pc(n);return function(e,t=Ls){return{type:0,children:e,helpers:[],components:[],directives:[],hoists:[],imports:[],cached:0,temps:0,codegenNode:void 0,loc:t}}(_c(n,0,[]),Ic(n,r))}function _c(e,t,n){const r=Rc(n),o=r?r.ns:0,i=[];for(;!Dc(e,t,n);){const l=e.source;let a;if(0===t||1===t)if(!e.inVPre&&Fc(l,e.options.delimiters[0]))a=Lc(e,t);else if(0===t&&"<"===l[0])if(1===l.length)Vc(e,5,1);else if("!"===l[1])Fc(l,"\x3c!--")?a=xc(e):Fc(l,""===l[2]){Vc(e,14,2),Mc(e,3);continue}if(/[a-z]/i.test(l[2])){Vc(e,23),Cc(e,1,r);continue}Vc(e,12,2),a=kc(e)}else/[a-z]/i.test(l[1])?(a=Sc(e,n),dc("COMPILER_NATIVE_TEMPLATE",e)&&a&&"template"===a.tag&&!a.props.some((e=>7===e.type&&Ec(e.name)))&&(a=a.children)):"?"===l[1]?(Vc(e,21,1),a=kc(e)):Vc(e,12,1);if(a||(a=Oc(e,t)),R(a))for(let e=0;e/.exec(e.source);if(r){r.index<=3&&Vc(e,0),r[1]&&Vc(e,10),n=e.source.slice(4,r.index);const t=e.source.slice(0,r.index);let o=1,i=0;for(;-1!==(i=t.indexOf("\x3c!--",o));)Mc(e,i-o+1),i+4");return-1===o?(r=e.source.slice(n),Mc(e,e.source.length)):(r=e.source.slice(n,o),Mc(e,o+1)),{type:3,content:r,loc:Ic(e,t)}}function Sc(e,t){const n=e.inPre,r=e.inVPre,o=Rc(t),i=Cc(e,0,o),l=e.inPre&&!n,a=e.inVPre&&!r;if(i.isSelfClosing||e.options.isVoidTag(i.tag))return l&&(e.inPre=!1),a&&(e.inVPre=!1),i;t.push(i);const s=e.options.getTextMode(i,o),c=_c(e,s,t);t.pop();{const t=i.props.find((e=>6===e.type&&"inline-template"===e.name));if(t&&hc("COMPILER_INLINE_TEMPLATE",e,t.loc)){const n=Ic(e,i.loc.end);t.value={type:2,content:n.source,loc:n}}}if(i.children=c,$c(e.source,i.tag))Cc(e,1,o);else if(Vc(e,24,0,i.loc.start),0===e.source.length&&"script"===i.tag.toLowerCase()){const t=c[0];t&&Fc(t.loc.source,"\x3c!--")&&Vc(e,8)}return i.loc=Ic(e,i.loc.start),l&&(e.inPre=!1),a&&(e.inVPre=!1),i}const Ec=o("if,else,else-if,for,slot");function Cc(e,t,n){const r=Pc(e),o=/^<\/?([a-z][^\t\r\n\f />]*)/i.exec(e.source),i=o[1],l=e.options.getNamespace(i,n);Mc(e,o[0].length),Bc(e);const a=Pc(e),s=e.source;e.options.isPreTag(i)&&(e.inPre=!0);let c=Tc(e,t);0===t&&!e.inVPre&&c.some((e=>7===e.type&&"pre"===e.name))&&(e.inVPre=!0,O(e,a),e.source=s,c=Tc(e,t).filter((e=>"v-pre"!==e.name)));let u=!1;if(0===e.source.length?Vc(e,9):(u=Fc(e.source,"/>"),1===t&&u&&Vc(e,4),Mc(e,u?2:1)),1===t)return;let f=0;return e.inVPre||("slot"===i?f=2:"template"===i?c.some((e=>7===e.type&&Ec(e.name)))&&(f=3):function(e,t,n){const r=n.options;if(r.isCustomElement(e))return!1;if("component"===e||/^[A-Z]/.test(e)||$s(e)||r.isBuiltInComponent&&r.isBuiltInComponent(e)||r.isNativeTag&&!r.isNativeTag(e))return!0;for(let e=0;e0&&!Fc(e.source,">")&&!Fc(e.source,"/>");){if(Fc(e.source,"/")){Vc(e,22),Mc(e,1),Bc(e);continue}1===t&&Vc(e,3);const o=Nc(e,r);6===o.type&&o.value&&"class"===o.name&&(o.value.content=o.value.content.replace(/\s+/g," ").trim()),0===t&&n.push(o),/^[^\t\r\n\f />]/.test(e.source)&&Vc(e,15),Bc(e)}return n}function Nc(e,t){const n=Pc(e),r=/^[^\t\r\n\f />][^\t\r\n\f />=]*/.exec(e.source)[0];t.has(r)&&Vc(e,2),t.add(r),"="===r[0]&&Vc(e,19);{const t=/["'<]/g;let n;for(;n=t.exec(r);)Vc(e,17,n.index)}let o;Mc(e,r.length),/^[\t\r\n\f ]*=/.test(e.source)&&(Bc(e),Mc(e,1),Bc(e),o=function(e){const t=Pc(e);let n;const r=e.source[0],o='"'===r||"'"===r;if(o){Mc(e,1);const t=e.source.indexOf(r);-1===t?n=Ac(e,e.source.length,4):(n=Ac(e,t,4),Mc(e,1))}else{const t=/^[^\t\r\n\f >]+/.exec(e.source);if(!t)return;const r=/["'<=`]/g;let o;for(;o=r.exec(t[0]);)Vc(e,18,o.index);n=Ac(e,t[0].length,4)}return{content:n,isQuoted:o,loc:Ic(e,t)}}(e),o||Vc(e,13));const i=Ic(e,n);if(!e.inVPre&&/^(v-[A-Za-z0-9-]|:|\.|@|#)/.test(r)){const t=/(?:^v-([a-z0-9-]+))?(?:(?::|^\.|^@|^#)(\[[^\]]+\]|[^\.]+))?(.+)?$/i.exec(r);let l,a=Fc(r,"."),s=t[1]||(a||Fc(r,":")?"bind":Fc(r,"@")?"on":"slot");if(t[2]){const o="slot"===s,i=r.lastIndexOf(t[2]),a=Ic(e,jc(e,n,i),jc(e,n,i+t[2].length+(o&&t[3]||"").length));let c=t[2],u=!0;c.startsWith("[")?(u=!1,c.endsWith("]")?c=c.slice(1,c.length-1):(Vc(e,27),c=c.slice(1))):o&&(c+=t[3]||""),l={type:4,content:c,isStatic:u,constType:u?3:0,loc:a}}if(o&&o.isQuoted){const e=o.loc;e.start.offset++,e.start.column++,e.end=Ks(e.start,o.content),e.source=e.source.slice(1,-1)}const c=t[3]?t[3].slice(1).split("."):[];return a&&c.push("prop"),"bind"===s&&l&&c.includes("sync")&&hc("COMPILER_V_BIND_SYNC",e,0,l.loc.source)&&(s="model",c.splice(c.indexOf("sync"),1)),{type:7,name:s,exp:o&&{type:4,content:o.content,isStatic:!1,constType:0,loc:o.loc},arg:l,modifiers:c,loc:i}}return!e.inVPre&&Fc(r,"v-")&&Vc(e,26),{type:6,name:r,value:o&&{type:2,content:o.content,loc:o.loc},loc:i}}function Lc(e,t){const[n,r]=e.options.delimiters,o=e.source.indexOf(r,n.length);if(-1===o)return void Vc(e,25);const i=Pc(e);Mc(e,n.length);const l=Pc(e),a=Pc(e),s=o-n.length,c=e.source.slice(0,s),u=Ac(e,s,t),f=u.trim(),p=u.indexOf(f);p>0&&Ys(l,c,p);return Ys(a,c,s-(u.length-f.length-p)),Mc(e,r.length),{type:5,content:{type:4,isStatic:!1,constType:0,content:f,loc:Ic(e,l,a)},loc:Ic(e,i)}}function Oc(e,t){const n=3===t?["]]>"]:["<",e.options.delimiters[0]];let r=e.source.length;for(let t=0;to&&(r=o)}const o=Pc(e);return{type:2,content:Ac(e,r,t),loc:Ic(e,o)}}function Ac(e,t,n){const r=e.source.slice(0,t);return Mc(e,t),2!==n&&3!==n&&r.includes("&")?e.options.decodeEntities(r,4===n):r}function Pc(e){const{column:t,line:n,offset:r}=e;return{column:t,line:n,offset:r}}function Ic(e,t,n){return{start:t,end:n=n||Pc(e),source:e.originalSource.slice(t.offset,n.offset)}}function Rc(e){return e[e.length-1]}function Fc(e,t){return e.startsWith(t)}function Mc(e,t){const{source:n}=e;Ys(e,n,t),e.source=n.slice(t)}function Bc(e){const t=/^[\t\r\n\f ]+/.exec(e.source);t&&Mc(e,t[0].length)}function jc(e,t,n){return Ks(t,e.originalSource.slice(t.offset,n),n)}function Vc(e,t,n,r=Pc(e)){n&&(r.offset+=n,r.column+=n),e.options.onError(Ua(t,{start:r,end:r,source:""}))}function Dc(e,t,n){const r=e.source;switch(t){case 0:if(Fc(r,"=0;--e)if($c(r,n[e].tag))return!0;break;case 1:case 2:{const e=Rc(n);if(e&&$c(r,e.tag))return!0;break}case 3:if(Fc(r,"]]>"))return!0}return!r}function $c(e,t){return Fc(e,"]/.test(e[2+t.length]||">")}function Uc(e,t){Hc(e,t,Wc(e,e.children[0]))}function Wc(e,t){const{children:n}=e;return 1===n.length&&1===t.type&&!rc(t)}function Hc(e,t,n=!1){const{children:r}=e,o=r.length;let i=0;for(let e=0;e0){if(e>=2){o.codegenNode.patchFlag="-1",o.codegenNode=t.hoist(o.codegenNode),i++;continue}}else{const e=o.codegenNode;if(13===e.type){const n=Yc(e);if((!n||512===n||1===n)&&Jc(o,t)>=2){const n=Kc(o);n&&(e.props=t.hoist(n))}e.dynamicProps&&(e.dynamicProps=t.hoist(e.dynamicProps))}}}if(1===o.type){const e=1===o.tagType;e&&t.scopes.vSlot++,Hc(o,t),e&&t.scopes.vSlot--}else if(11===o.type)Hc(o,t,1===o.children.length);else if(9===o.type)for(let e=0;e1)for(let o=0;o`_${Ns[S.helper(e)]}`,replaceNode(e){S.parent.children[S.childIndex]=S.currentNode=e},removeNode(e){const t=S.parent.children,n=e?t.indexOf(e):S.currentNode?S.childIndex:-1;e&&e!==S.currentNode?S.childIndex>n&&(S.childIndex--,S.onNodeRemoved()):(S.currentNode=null,S.onNodeRemoved()),S.parent.children.splice(n,1)},onNodeRemoved:()=>{},addIdentifiers(e){},removeIdentifiers(e){},hoist(e){V(e)&&(e=Rs(e)),S.hoists.push(e);const t=Rs(`_hoisted_${S.hoists.length}`,!1,e.loc,2);return t.hoisted=e,t},cache:(e,t=!1)=>function(e,t,n=!1){return{type:20,index:e,value:t,isVNode:n,loc:Ls}}(S.cached++,e,t)};return S.filters=new Set,S}function Xc(e,t){const n=Zc(e,t);Qc(e,n),t.hoistStatic&&Uc(e,n),t.ssr||function(e,t){const{helper:n}=t,{children:r}=e;if(1===r.length){const n=r[0];if(Wc(e,n)&&n.codegenNode){const r=n.codegenNode;13===r.type&&fc(r,t),e.codegenNode=r}else e.codegenNode=n}else if(r.length>1){let r=64;0,e.codegenNode=Os(t,n(Wa),void 0,e.children,r+"",void 0,void 0,!0,void 0,!1)}}(e,n),e.helpers=[...n.helpers.keys()],e.components=[...n.components],e.directives=[...n.directives],e.imports=n.imports,e.hoists=n.hoists,e.temps=n.temps,e.cached=n.cached,e.filters=[...n.filters]}function Qc(e,t){t.currentNode=e;const{nodeTransforms:n}=t,r=[];for(let o=0;o{n--};for(;nt===e:t=>e.test(t);return(e,r)=>{if(1===e.type){const{props:o}=e;if(3===e.tagType&&o.some(tc))return;const i=[];for(let l=0;l`${Ns[e]}: _${Ns[e]}`;function ru(e,t={}){const n=function(e,{mode:t="function",prefixIdentifiers:n="module"===t,sourceMap:r=!1,filename:o="template.vue.html",scopeId:i=null,optimizeImports:l=!1,runtimeGlobalName:a="Vue",runtimeModuleName:s="vue",ssrRuntimeModuleName:c="vue/server-renderer",ssr:u=!1,isTS:f=!1,inSSR:p=!1}){const d={mode:t,prefixIdentifiers:n,sourceMap:r,filename:o,scopeId:i,optimizeImports:l,runtimeGlobalName:a,runtimeModuleName:s,ssrRuntimeModuleName:c,ssr:u,isTS:f,inSSR:p,source:e.loc.source,code:"",column:1,line:1,offset:0,indentLevel:0,pure:!1,map:void 0,helper:e=>`_${Ns[e]}`,push(e,t){d.code+=e},indent(){h(++d.indentLevel)},deindent(e=!1){e?--d.indentLevel:h(--d.indentLevel)},newline(){h(d.indentLevel)}};function h(e){d.push("\n"+" ".repeat(e))}return d}(e,t);t.onContextCreated&&t.onContextCreated(n);const{mode:r,push:o,prefixIdentifiers:i,indent:l,deindent:a,newline:s,scopeId:c,ssr:u}=n,f=e.helpers.length>0,p=!i&&"module"!==r;!function(e,t){const{ssr:n,prefixIdentifiers:r,push:o,newline:i,runtimeModuleName:l,runtimeGlobalName:a,ssrRuntimeModuleName:s}=t,c=a;if(e.helpers.length>0&&(o(`const _Vue = ${c}\n`),e.hoists.length)){o(`const { ${[Za,Xa,Qa,es,ts].filter((t=>e.helpers.includes(t))).map(nu).join(", ")} } = _Vue\n`)}(function(e,t){if(!e.length)return;t.pure=!0;const{push:n,newline:r,helper:o,scopeId:i,mode:l}=t;r();for(let o=0;o0)&&s()),e.directives.length&&(ou(e.directives,"directive",n),e.temps>0&&s()),e.filters&&e.filters.length&&(s(),ou(e.filters,"filter",n),s()),e.temps>0){o("let ");for(let t=0;t0?", ":""}_temp${t}`)}return(e.components.length||e.directives.length||e.temps)&&(o("\n"),s()),u||o("return "),e.codegenNode?au(e.codegenNode,n):o("null"),p&&(a(),o("}")),a(),o("}"),{ast:e,code:n.code,preamble:"",map:n.map?n.map.toJSON():void 0}}function ou(e,t,{helper:n,push:r,newline:o,isTS:i}){const l=n("filter"===t?is:"component"===t?ns:os);for(let n=0;n3||!1;t.push("["),n&&t.indent(),lu(e,t,n),n&&t.deindent(),t.push("]")}function lu(e,t,n=!1,r=!0){const{push:o,newline:i}=t;for(let l=0;le||"null"))}([i,l,a,s,c]),t),n(")"),f&&n(")");u&&(n(", "),au(u,t),n(")"))}(e,t);break;case 14:!function(e,t){const{push:n,helper:r,pure:o}=t,i=V(e.callee)?e.callee:r(e.callee);o&&n(tu);n(i+"(",e),lu(e.arguments,t),n(")")}(e,t);break;case 15:!function(e,t){const{push:n,indent:r,deindent:o,newline:i}=t,{properties:l}=e;if(!l.length)return void n("{}",e);const a=l.length>1||!1;n(a?"{":"{ "),a&&r();for(let e=0;e "),(s||a)&&(n("{"),r());l?(s&&n("return "),R(l)?iu(l,t):au(l,t)):a&&au(a,t);(s||a)&&(o(),n("}"));c&&(e.isNonScopedSlot&&n(", undefined, true"),n(")"))}(e,t);break;case 19:!function(e,t){const{test:n,consequent:r,alternate:o,newline:i}=e,{push:l,indent:a,deindent:s,newline:c}=t;if(4===n.type){const e=!Ws(n.content);e&&l("("),su(n,t),e&&l(")")}else l("("),au(n,t),l(")");i&&a(),t.indentLevel++,i||l(" "),l("? "),au(r,t),t.indentLevel--,i&&c(),i||l(" "),l(": ");const u=19===o.type;u||t.indentLevel++;au(o,t),u||t.indentLevel--;i&&s(!0)}(e,t);break;case 20:!function(e,t){const{push:n,helper:r,indent:o,deindent:i,newline:l}=t;n(`_cache[${e.index}] || (`),e.isVNode&&(o(),n(`${r(bs)}(-1),`),l());n(`_cache[${e.index}] = `),au(e.value,t),e.isVNode&&(n(","),l(),n(`${r(bs)}(1),`),l(),n(`_cache[${e.index}]`),i());n(")")}(e,t);break;case 21:lu(e.body,t,!0,!1)}}function su(e,t){const{content:n,isStatic:r}=e;t.push(r?JSON.stringify(n):n,e)}function cu(e,t){for(let n=0;nfunction(e,t,n,r){if(!("else"===t.name||t.exp&&t.exp.content.trim())){const r=t.exp?t.exp.loc:e.loc;n.onError(Ua(28,t.loc)),t.exp=Rs("true",!1,r)}0;if("if"===t.name){const o=pu(e,t),i={type:9,loc:e.loc,branches:[o]};if(n.replaceNode(i),r)return r(i,o,!0)}else{const o=n.parent.children;let i=o.indexOf(e);for(;i-- >=-1;){const l=o[i];if(l&&3===l.type)n.removeNode(l);else{if(!l||2!==l.type||l.content.trim().length){if(l&&9===l.type){"else-if"===t.name&&void 0===l.branches[l.branches.length-1].condition&&n.onError(Ua(30,e.loc)),n.removeNode();const o=pu(e,t);0,l.branches.push(o);const i=r&&r(l,o,!1);Qc(o,n),i&&i(),n.currentNode=null}else n.onError(Ua(30,e.loc));break}n.removeNode(l)}}}}(e,t,n,((e,t,r)=>{const o=n.parent.children;let i=o.indexOf(e),l=0;for(;i-- >=0;){const e=o[i];e&&9===e.type&&(l+=e.branches.length)}return()=>{if(r)e.codegenNode=du(t,l,n);else{const r=function(e){for(;;)if(19===e.type){if(19!==e.alternate.type)return e;e=e.alternate}else 20===e.type&&(e=e.value)}(e.codegenNode);r.alternate=du(t,l+e.branches.length-1,n)}}}))));function pu(e,t){const n=3===e.tagType;return{type:10,loc:e.loc,condition:"else"===t.name?void 0:t.exp,children:n&&!Zs(e,"for")?e.children:[e],userKey:Xs(e,"key"),isTemplateIf:n}}function du(e,t,n){return e.condition?js(e.condition,hu(e,t,n),Ms(n.helper(Qa),['""',"true"])):hu(e,t,n)}function hu(e,t,n){const{helper:r}=n,o=Is("key",Rs(`${t}`,!1,Ls,2)),{children:i}=e,l=i[0];if(1!==i.length||1!==l.type){if(1===i.length&&11===l.type){const e=l.codegenNode;return sc(e,o,n),e}{let t=64;return Os(n,r(Wa),Ps([o]),i,t+"",void 0,void 0,!0,!1,!1,e.loc)}}{const e=l.codegenNode,t=14===(a=e).type&&a.callee===Cs?a.arguments[1].returns:a;return 13===t.type&&fc(t,n),sc(t,o,n),e}var a}const mu=eu("for",((e,t,n)=>{const{helper:r,removeHelper:o}=n;return function(e,t,n,r){if(!t.exp)return void n.onError(Ua(31,t.loc));const o=_u(t.exp,n);if(!o)return void n.onError(Ua(32,t.loc));const{addIdentifiers:i,removeIdentifiers:l,scopes:a}=n,{source:s,value:c,key:u,index:f}=o,p={type:11,loc:t.loc,source:s,valueAlias:c,keyAlias:u,objectIndexAlias:f,parseResult:o,children:nc(e)?e.children:[e]};n.replaceNode(p),a.vFor++;const d=r&&r(p);return()=>{a.vFor--,d&&d()}}(e,t,n,(t=>{const i=Ms(r(as),[t.source]),l=nc(e),a=Zs(e,"memo"),s=Xs(e,"key"),c=s&&(6===s.type?Rs(s.value.content,!0):s.exp),u=s?Is("key",c):null,f=4===t.source.type&&t.source.constType>0,p=f?64:s?128:256;return t.codegenNode=Os(n,r(Wa),void 0,i,p+"",void 0,void 0,!0,!f,!1,e.loc),()=>{let s;const{children:p}=t;const d=1!==p.length||1!==p[0].type,h=rc(e)?e:l&&1===e.children.length&&rc(e.children[0])?e.children[0]:null;if(h?(s=h.codegenNode,l&&u&&sc(s,u,n)):d?s=Os(n,r(Wa),u?Ps([u]):void 0,e.children,"64",void 0,void 0,!0,void 0,!1):(s=p[0].codegenNode,l&&u&&sc(s,u,n),s.isBlock!==!f&&(s.isBlock?(o(Ja),o(ic(n.inSSR,s.isComponent))):o(oc(n.inSSR,s.isComponent))),s.isBlock=!f,s.isBlock?(r(Ja),r(ic(n.inSSR,s.isComponent))):r(oc(n.inSSR,s.isComponent))),a){const e=Bs(wu(t.parseResult,[Rs("_cached")]));e.body={type:21,body:[Fs(["const _memo = (",a.exp,")"]),Fs(["if (_cached",...c?[" && _cached.key === ",c]:[],` && ${n.helperString(Ts)}(_cached, _memo)) return _cached`]),Fs(["const _item = ",s]),Rs("_item.memo = _memo"),Rs("return _item")],loc:Ls},i.arguments.push(e,Rs("_cache"),Rs(String(n.cached++)))}else i.arguments.push(Bs(wu(t.parseResult),s,!0))}}))}));const vu=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,gu=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,yu=/^\(|\)$/g;function _u(e,t){const n=e.loc,r=e.content,o=r.match(vu);if(!o)return;const[,i,l]=o,a={source:bu(n,l.trim(),r.indexOf(l,i.length)),value:void 0,key:void 0,index:void 0};let s=i.trim().replace(yu,"").trim();const c=i.indexOf(s),u=s.match(gu);if(u){s=s.replace(gu,"").trim();const e=u[1].trim();let t;if(e&&(t=r.indexOf(e,c+s.length),a.key=bu(n,e,t)),u[2]){const o=u[2].trim();o&&(a.index=bu(n,o,r.indexOf(o,a.key?t+e.length:c+s.length)))}}return s&&(a.value=bu(n,s,c)),a}function bu(e,t,n){return Rs(t,!1,Js(e,n,t.length))}function wu({value:e,key:t,index:n},r=[]){return function(e){let t=e.length;for(;t--&&!e[t];);return e.slice(0,t+1).map(((e,t)=>e||Rs("_".repeat(t+1),!1)))}([e,t,n,...r])}const xu=Rs("undefined",!1),ku=(e,t)=>{if(1===e.type&&(1===e.tagType||3===e.tagType)){const n=Zs(e,"slot");if(n)return n.exp,t.scopes.vSlot++,()=>{t.scopes.vSlot--}}},Su=(e,t,n)=>Bs(e,t,!1,!0,t.length?t[0].loc:n);function Eu(e,t,n=Su){t.helper(ks);const{children:r,loc:o}=e,i=[],l=[];let a=t.scopes.vSlot>0||t.scopes.vFor>0;const s=Zs(e,"slot",!0);if(s){const{arg:e,exp:t}=s;e&&!Vs(e)&&(a=!0),i.push(Is(e||Rs("default",!0),n(t,r,o)))}let c=!1,u=!1;const f=[],p=new Set;let d=0;for(let e=0;e{const i=n(e,r,o);return t.compatConfig&&(i.isNonScopedSlot=!0),Is("default",i)};c?f.length&&f.some((e=>Nu(e)))&&(u?t.onError(Ua(39,f[0].loc)):i.push(e(void 0,f))):i.push(e(void 0,r))}const h=a?2:Tu(e.children)?3:1;let m=Ps(i.concat(Is("_",Rs(h+"",!1))),o);return l.length&&(m=Ms(t.helper(cs),[m,As(l)])),{slots:m,hasDynamicSlots:a}}function Cu(e,t,n){const r=[Is("name",e),Is("fn",t)];return null!=n&&r.push(Is("key",Rs(String(n),!0))),Ps(r)}function Tu(e){for(let t=0;tfunction(){if(1!==(e=t.currentNode).type||0!==e.tagType&&1!==e.tagType)return;const{tag:n,props:r}=e,o=1===e.tagType;let i=o?function(e,t,n=!1){let{tag:r}=e;const o=Ru(r),i=Xs(e,"is");if(i)if(o||dc("COMPILER_IS_ON_ELEMENT",t)){const e=6===i.type?i.value&&Rs(i.value.content,!0):i.exp;if(e)return Ms(t.helper(rs),[e])}else 6===i.type&&i.value.content.startsWith("vue:")&&(r=i.value.content.slice(4));const l=!o&&Zs(e,"is");if(l&&l.exp)return Ms(t.helper(rs),[l.exp]);const a=$s(r)||t.isBuiltInComponent(r);if(a)return n||t.helper(a),a;return t.helper(ns),t.components.add(r),uc(r,"component")}(e,t):`"${n}"`;const l=$(i)&&i.callee===rs;let a,s,c,u,f,p,d=0,h=l||i===Ha||i===za||!o&&("svg"===n||"foreignObject"===n);if(r.length>0){const n=Au(e,t,void 0,o,l);a=n.props,d=n.patchFlag,f=n.dynamicPropNames;const r=n.directives;p=r&&r.length?As(r.map((e=>function(e,t){const n=[],r=Lu.get(e);r?n.push(t.helperString(r)):(t.helper(os),t.directives.add(e.name),n.push(uc(e.name,"directive")));const{loc:o}=e;e.exp&&n.push(e.exp);e.arg&&(e.exp||n.push("void 0"),n.push(e.arg));if(Object.keys(e.modifiers).length){e.arg||(e.exp||n.push("void 0"),n.push("void 0"));const t=Rs("true",!1,o);n.push(Ps(e.modifiers.map((e=>Is(e,t))),o))}return As(n,e.loc)}(e,t)))):void 0,n.shouldUseBlock&&(h=!0)}if(e.children.length>0){i===Ga&&(h=!0,d|=1024);if(o&&i!==Ha&&i!==Ga){const{slots:n,hasDynamicSlots:r}=Eu(e,t);s=n,r&&(d|=1024)}else if(1===e.children.length&&i!==Ha){const n=e.children[0],r=n.type,o=5===r||8===r;o&&0===zc(n,t)&&(d|=1),s=o||2===r?n:e.children}else s=e.children}0!==d&&(c=String(d),f&&f.length&&(u=function(e){let t="[";for(let n=0,r=e.length;n0;let d=!1,h=0,m=!1,v=!1,g=!1,y=!1,_=!1,b=!1;const w=[],x=e=>{c.length&&(u.push(Ps(Pu(c),a)),c=[]),e&&u.push(e)},k=({key:e,value:n})=>{if(Vs(e)){const i=e.content,l=N(i);if(!l||r&&!o||"onclick"===i.toLowerCase()||"onUpdate:modelValue"===i||q(i)||(y=!0),l&&q(i)&&(b=!0),20===n.type||(4===n.type||8===n.type)&&zc(n,t)>0)return;"ref"===i?m=!0:"class"===i?v=!0:"style"===i?g=!0:"key"===i||w.includes(i)||w.push(i),!r||"class"!==i&&"style"!==i||w.includes(i)||w.push(i)}else _=!0};for(let o=0;o0&&c.push(Is(Rs("ref_for",!0),Rs("true")))),"is"===n&&(Ru(l)||r&&r.content.startsWith("vue:")||dc("COMPILER_IS_ON_ELEMENT",t)))continue;c.push(Is(Rs(n,!0,Js(e,0,n.length)),Rs(r?r.content:"",o,r?r.loc:e)))}else{const{name:n,arg:o,exp:h,loc:m}=s,v="bind"===n,g="on"===n;if("slot"===n){r||t.onError(Ua(40,m));continue}if("once"===n||"memo"===n)continue;if("is"===n||v&&Qs(o,"is")&&(Ru(l)||dc("COMPILER_IS_ON_ELEMENT",t)))continue;if(g&&i)continue;if((v&&Qs(o,"key")||g&&p&&Qs(o,"vue:before-update"))&&(d=!0),v&&Qs(o,"ref")&&t.scopes.vFor>0&&c.push(Is(Rs("ref_for",!0),Rs("true"))),!o&&(v||g)){if(_=!0,h)if(v){if(x(),dc("COMPILER_V_BIND_OBJECT_ORDER",t)){u.unshift(h);continue}u.push(h)}else x({type:14,loc:m,callee:t.helper(vs),arguments:r?[h]:[h,"true"]});else t.onError(Ua(v?34:35,m));continue}const y=t.directiveTransforms[n];if(y){const{props:n,needRuntime:r}=y(s,e,t);!i&&n.forEach(k),g&&o&&!Vs(o)?x(Ps(n,a)):c.push(...n),r&&(f.push(s),D(r)&&Lu.set(s,r))}else J(n)||(f.push(s),p&&(d=!0))}}let S;if(u.length?(x(),S=u.length>1?Ms(t.helper(fs),u,a):u[0]):c.length&&(S=Ps(Pu(c),a)),_?h|=16:(v&&!r&&(h|=2),g&&!r&&(h|=4),w.length&&(h|=8),y&&(h|=32)),d||0!==h&&32!==h||!(m||b||f.length>0)||(h|=512),!t.inSSR&&S)switch(S.type){case 15:let e=-1,n=-1,r=!1;for(let t=0;t{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))})((e=>e.replace(Fu,((e,t)=>t?t.toUpperCase():"")))),Bu=(e,t)=>{if(rc(e)){const{children:n,loc:r}=e,{slotName:o,slotProps:i}=function(e,t){let n,r='"default"';const o=[];for(let t=0;t0){const{props:r,directives:i}=Au(e,t,o,!1,!1);n=r,i.length&&t.onError(Ua(36,i[0].loc))}return{slotName:r,slotProps:n}}(e,t),l=[t.prefixIdentifiers?"_ctx.$slots":"$slots",o,"{}","undefined","true"];let a=2;i&&(l[2]=i,a=3),n.length&&(l[3]=Bs([],n,!1,!1,r),a=4),t.scopeId&&!t.slotted&&(a=5),l.splice(a),e.codegenNode=Ms(t.helper(ss),l,r)}};const ju=/^\s*([\w$_]+|(async\s*)?\([^)]*?\))\s*(:[^=]+)?=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/,Vu=(e,t,n,r)=>{const{loc:o,modifiers:i,arg:l}=e;let a;if(e.exp||i.length||n.onError(Ua(35,o)),4===l.type)if(l.isStatic){let e=l.content;e.startsWith("vue:")&&(e=`vnode-${e.slice(4)}`);a=Rs(0!==t.tagType||e.startsWith("vnode")||!/[A-Z]/.test(e)?te(Z(e)):`on:${e}`,!0,l.loc)}else a=Fs([`${n.helperString(_s)}(`,l,")"]);else a=l,a.children.unshift(`${n.helperString(_s)}(`),a.children.push(")");let s=e.exp;s&&!s.content.trim()&&(s=void 0);let c=n.cacheHandlers&&!s&&!n.inVOnce;if(s){const e=qs(s.content),t=!(e||ju.test(s.content)),n=s.content.includes(";");0,(t||c&&e)&&(s=Fs([`${t?"$event":"(...args)"} => ${n?"{":"("}`,s,n?"}":")"]))}let u={props:[Is(a,s||Rs("() => {}",!1,o))]};return r&&(u=r(u)),c&&(u.props[0].value=n.cache(u.props[0].value)),u.props.forEach((e=>e.key.isHandlerKey=!0)),u},Du=(e,t,n)=>{const{exp:r,modifiers:o,loc:i}=e,l=e.arg;return 4!==l.type?(l.children.unshift("("),l.children.push(') || ""')):l.isStatic||(l.content=`${l.content} || ""`),o.includes("camel")&&(4===l.type?l.isStatic?l.content=Z(l.content):l.content=`${n.helperString(gs)}(${l.content})`:(l.children.unshift(`${n.helperString(gs)}(`),l.children.push(")"))),n.inSSR||(o.includes("prop")&&$u(l,"."),o.includes("attr")&&$u(l,"^")),!r||4===r.type&&!r.content.trim()?(n.onError(Ua(34,i)),{props:[Is(l,Rs("",!0,i))]}):{props:[Is(l,r)]}},$u=(e,t)=>{4===e.type?e.isStatic?e.content=t+e.content:e.content=`\`${t}\${${e.content}}\``:(e.children.unshift(`'${t}' + (`),e.children.push(")"))},Uu=(e,t)=>{if(0===e.type||1===e.type||11===e.type||10===e.type)return()=>{const n=e.children;let r,o=!1;for(let e=0;e7===e.type&&!t.directiveTransforms[e.name]))||"template"===e.tag)))for(let e=0;e{if(1===e.type&&Zs(e,"once",!0)){if(Wu.has(e)||t.inVOnce)return;return Wu.add(e),t.inVOnce=!0,t.helper(bs),()=>{t.inVOnce=!1;const e=t.currentNode;e.codegenNode&&(e.codegenNode=t.cache(e.codegenNode,!0))}}},zu=(e,t,n)=>{const{exp:r,arg:o}=e;if(!r)return n.onError(Ua(41,e.loc)),Gu();const i=r.loc.source,l=4===r.type?r.content:i,a=n.bindingMetadata[i];if("props"===a||"props-aliased"===a)return n.onError(Ua(44,r.loc)),Gu();if(!l.trim()||!qs(l))return n.onError(Ua(42,r.loc)),Gu();const s=o||Rs("modelValue",!0),c=o?Vs(o)?`onUpdate:${o.content}`:Fs(['"onUpdate:" + ',o]):"onUpdate:modelValue";let u;u=Fs([`${n.isTS?"($event: any)":"$event"} => ((`,r,") = $event)"]);const f=[Is(s,e.exp),Is(c,u)];if(e.modifiers.length&&1===t.tagType){const t=e.modifiers.map((e=>(Ws(e)?e:JSON.stringify(e))+": true")).join(", "),n=o?Vs(o)?`${o.content}Modifiers`:Fs([o,' + "Modifiers"']):"modelModifiers";f.push(Is(n,Rs(`{ ${t} }`,!1,e.loc,2)))}return Gu(f)};function Gu(e=[]){return{props:e}}const qu=/[\w).+\-_$\]]/,Ju=(e,t)=>{dc("COMPILER_FILTER",t)&&(5===e.type&&Ku(e.content,t),1===e.type&&e.props.forEach((e=>{7===e.type&&"for"!==e.name&&e.exp&&Ku(e.exp,t)})))};function Ku(e,t){if(4===e.type)Yu(e,t);else for(let n=0;n=0&&(e=n.charAt(t)," "===e);t--);e&&qu.test(e)||(u=!0)}}else void 0===l?(h=i+1,l=n.slice(0,i).trim()):v();function v(){m.push(n.slice(h,i).trim()),h=i+1}if(void 0===l?l=n.slice(0,i).trim():0!==h&&v(),m.length){for(i=0;i{if(1===e.type){const n=Zs(e,"memo");if(!n||Xu.has(e))return;return Xu.add(e),()=>{const r=e.codegenNode||t.currentNode.codegenNode;r&&13===r.type&&(1!==e.tagType&&fc(r,t),e.codegenNode=Ms(t.helper(Cs),[n.exp,Bs(void 0,r),"_cache",String(t.cached++)]))}}};function ef(e,t={}){const n=t.onError||Da,r="module"===t.mode;!0===t.prefixIdentifiers?n(Ua(47)):r&&n(Ua(48));t.cacheHandlers&&n(Ua(49)),t.scopeId&&!r&&n(Ua(50));const o=V(e)?yc(e,t):e,[i,l]=[[Hu,fu,Qu,mu,Ju,Bu,Ou,ku,Uu],{on:Vu,bind:Du,model:zu}];return Xc(o,O({},t,{prefixIdentifiers:false,nodeTransforms:[...i,...t.nodeTransforms||[]],directiveTransforms:O({},l,t.directiveTransforms||{})})),ru(o,O({},t,{prefixIdentifiers:false}))}const tf=Symbol(""),nf=Symbol(""),rf=Symbol(""),of=Symbol(""),lf=Symbol(""),af=Symbol(""),sf=Symbol(""),cf=Symbol(""),uf=Symbol(""),ff=Symbol("");var pf;let df;pf={[tf]:"vModelRadio",[nf]:"vModelCheckbox",[rf]:"vModelText",[of]:"vModelSelect",[lf]:"vModelDynamic",[af]:"withModifiers",[sf]:"withKeys",[cf]:"vShow",[uf]:"Transition",[ff]:"TransitionGroup"},Object.getOwnPropertySymbols(pf).forEach((e=>{Ns[e]=pf[e]}));const hf=o("style,iframe,script,noscript",!0),mf={isVoidTag:m,isNativeTag:e=>d(e)||h(e),isPreTag:e=>"pre"===e,decodeEntities:function(e,t=!1){return df||(df=document.createElement("div")),t?(df.innerHTML=`

    `,df.children[0].getAttribute("foo")):(df.innerHTML=e,df.textContent)},isBuiltInComponent:e=>Ds(e,"Transition")?uf:Ds(e,"TransitionGroup")?ff:void 0,getNamespace(e,t){let n=t?t.ns:0;if(t&&2===n)if("annotation-xml"===t.tag){if("svg"===e)return 1;t.props.some((e=>6===e.type&&"encoding"===e.name&&null!=e.value&&("text/html"===e.value.content||"application/xhtml+xml"===e.value.content)))&&(n=0)}else/^m(?:[ions]|text)$/.test(t.tag)&&"mglyph"!==e&&"malignmark"!==e&&(n=0);else t&&1===n&&("foreignObject"!==t.tag&&"desc"!==t.tag&&"title"!==t.tag||(n=0));if(0===n){if("svg"===e)return 1;if("math"===e)return 2}return n},getTextMode({tag:e,ns:t}){if(0===t){if("textarea"===e||"title"===e)return 1;if(hf(e))return 2}return 0}},vf=(e,t)=>{const n=u(e);return Rs(JSON.stringify(n),!1,t,3)};function gf(e,t){return Ua(e,t)}const yf=o("passive,once,capture"),_f=o("stop,prevent,self,ctrl,shift,alt,meta,exact,middle"),bf=o("left,right"),wf=o("onkeyup,onkeydown,onkeypress",!0),xf=(e,t)=>Vs(e)&&"onclick"===e.content.toLowerCase()?Rs(t,!0):4!==e.type?Fs(["(",e,`) === "onClick" ? "${t}" : (`,e,")"]):e;const kf=(e,t)=>{1!==e.type||0!==e.tagType||"script"!==e.tag&&"style"!==e.tag||(t.onError(gf(61,e.loc)),t.removeNode())},Sf=[e=>{1===e.type&&e.props.forEach(((t,n)=>{6===t.type&&"style"===t.name&&t.value&&(e.props[n]={type:7,name:"bind",arg:Rs("style",!0,t.loc),exp:vf(t.value.content,t.loc),modifiers:[],loc:t.loc})}))}],Ef={cloak:()=>({props:[]}),html:(e,t,n)=>{const{exp:r,loc:o}=e;return r||n.onError(gf(51,o)),t.children.length&&(n.onError(gf(52,o)),t.children.length=0),{props:[Is(Rs("innerHTML",!0,o),r||Rs("",!0))]}},text:(e,t,n)=>{const{exp:r,loc:o}=e;return r||n.onError(gf(53,o)),t.children.length&&(n.onError(gf(54,o)),t.children.length=0),{props:[Is(Rs("textContent",!0),r?zc(r,n)>0?r:Ms(n.helperString(us),[r],o):Rs("",!0))]}},model:(e,t,n)=>{const r=zu(e,t,n);if(!r.props.length||1===t.tagType)return r;e.arg&&n.onError(gf(56,e.arg.loc));const{tag:o}=t,i=n.isCustomElement(o);if("input"===o||"textarea"===o||"select"===o||i){let l=rf,a=!1;if("input"===o||i){const r=Xs(t,"type");if(r){if(7===r.type)l=lf;else if(r.value)switch(r.value.content){case"radio":l=tf;break;case"checkbox":l=nf;break;case"file":a=!0,n.onError(gf(57,e.loc))}}else(function(e){return e.props.some((e=>!(7!==e.type||"bind"!==e.name||e.arg&&4===e.arg.type&&e.arg.isStatic)))})(t)&&(l=lf)}else"select"===o&&(l=of);a||(r.needRuntime=n.helper(l))}else n.onError(gf(55,e.loc));return r.props=r.props.filter((e=>!(4===e.key.type&&"modelValue"===e.key.content))),r},on:(e,t,n)=>Vu(e,t,n,(t=>{const{modifiers:r}=e;if(!r.length)return t;let{key:o,value:i}=t.props[0];const{keyModifiers:l,nonKeyModifiers:a,eventOptionModifiers:s}=((e,t,n,r)=>{const o=[],i=[],l=[];for(let r=0;r{const{exp:r,loc:o}=e;return r||n.onError(gf(59,o)),{props:[],needRuntime:n.helper(cf)}}};const Cf=Object.create(null);function Tf(e,t){if(!V(e)){if(!e.nodeType)return E;e=e.innerHTML}const n=e,o=Cf[n];if(o)return o;if("#"===e[0]){const t=document.querySelector(e);0,e=t?t.innerHTML:""}const i=O({hoistStatic:!0,onError:void 0,onWarn:E},t);i.isCustomElement||"undefined"==typeof customElements||(i.isCustomElement=e=>!!customElements.get(e));const{code:l}=function(e,t={}){return ef(e,O({},mf,t,{nodeTransforms:[kf,...Sf,...t.nodeTransforms||[]],directiveTransforms:O({},Ef,t.directiveTransforms||{}),transformHoist:null}))}(e,i);const a=new Function("Vue",l)(r);return a._rc=!0,Cf[n]=a}$i(Tf)}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={id:e,loaded:!1,exports:{}};return t[e].call(i.exports,i,i.exports,r),i.loaded=!0,i.exports}r.m=t,e=[],r.O=(t,n,o,i)=>{if(!n){var l=1/0;for(u=0;u=i)&&Object.keys(r.O).every((e=>r.O[e](n[s])))?n.splice(s--,1):(a=!1,i0&&e[u-1][2]>i;u--)e[u]=e[u-1];e[u]=[n,o,i]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e={773:0,170:0};r.O.j=t=>0===e[t];var t=(t,n)=>{var o,i,[l,a,s]=n,c=0;if(l.some((t=>0!==e[t]))){for(o in a)r.o(a,o)&&(r.m[o]=a[o]);if(s)var u=s(r)}for(t&&t(n);cr(435)));var o=r.O(void 0,[170],(()=>r(423)));o=r.O(o)})(); \ No newline at end of file diff --git a/survey_dashboard/hmc_layout/static/en_files/cables-logo.svg b/survey_dashboard/hmc_layout/static/en_files/cables-logo.svg new file mode 100644 index 0000000..7fbeca9 --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/cables-logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/survey_dashboard/hmc_layout/static/en_files/cables.min(1).js b/survey_dashboard/hmc_layout/static/en_files/cables.min(1).js new file mode 100644 index 0000000..093c973 --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/cables.min(1).js @@ -0,0 +1,4 @@ +var CABLES=function(t){var e={};function i(s){if(e[s])return e[s].exports;var r=e[s]={i:s,l:!1,exports:{}};return t[s].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=t,i.c=e,i.d=function(t,e,s){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:s})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var s=Object.create(null);if(i.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)i.d(s,r,function(e){return t[e]}.bind(null,r));return s},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=0)}([function(t,e,i){t.exports=i(2)},function(module,__webpack_exports__,__webpack_require__){"use strict";let GLOB_EXP=/(?:^|[^\\])\*/,NOT_LINE_ENDING=/[^\r\n]/g;const Preprocessor=function(t,e,i){this.source=""+t,this.baseDir="string"==typeof e?e:".",this.includes="object"==typeof e?e:{},this.preserveLineNumbers="boolean"==typeof i&&i,this.isNode=!("undefined"!=typeof window&&window.window),this.errorSourceAhead=50,this.defines=[]};Preprocessor.EXPR=/([ ]*)\/\/[ ]+#(include_once|include|ifn?def|if|endif|else|elif|put|define)/g,Preprocessor.ALL=/([^\r\n]*)\r?(?:\n|$)/,Preprocessor.INCLUDE=/(include_once|include)[ ]+"([^"\\]*(\\.[^"\\]*)*)"[ ]*\r?(?:\n|$)/g,Preprocessor.IF=/(ifdef|ifndef|if)[ ]*([^\r\n]+)\r?\n/g,Preprocessor.ENDIF=/(endif|else|elif)([ ]+[^\r\n]+)?\r?(?:\n|$)/g,Preprocessor.PUT=/put[ ]+([^\n]+)[ ]*/g,Preprocessor.DEFINE=/define[ ]+([^\n\r]+)\r?(?:\n|$)/g,Preprocessor.VAR=/define[ ]+var[ ]+([a-zA-Z_][a-zA-Z0-9_]*)[ ]*=[ ]*(.+)/g,Preprocessor.BOOLVAR=/define[ ]+([a-zA-Z_][a-zA-Z0-9_]*)[ ]*/g,Preprocessor.FUNCTION=/define[ ]+function[ ]+([a-zA-Z_][a-zA-Z0-9_]*)[ ]*(.+)/g,Preprocessor.stripSlashes=function(t){return(t+"").replace(/\\(.?)/g,(function(t,e){switch(e){case"\\":return"\\";case"0":return"\0";case"":return"";default:return e}}))},Preprocessor.addSlashes=function(t){return(t+"").replace(/([\\"'])/g,"\\$1").replace(/\0/g,"\\0")},Preprocessor.indent=function(t,e){let i=t.split("\n");for(let t=0;t0&&"endif"!==i[2]&&"else"!==i[2]&&"elif"!==i[2]){if(a=h.pop(),e(" pop ("+h.length+"): "+JSON.stringify(a)),Preprocessor.ALL.lastIndex=i.index,null===(s=Preprocessor.ALL.exec(this.source)))throw new Error("Illegal #"+i[2]+": "+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");h.push(o={include:a.include,index:a.index,lastIndex:Preprocessor.ALL.lastIndex}),e(" push ("+h.length+"): "+JSON.stringify(o))}else switch(i[2]){case"ifdef":case"ifndef":case"if":if(Preprocessor.IF.lastIndex=i.index,null===(s=Preprocessor.IF.exec(this.source)))throw new Error("Illegal #"+i[2]+": "+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");e(" test: "+s[2]),e(" defines "+JSON.stringify(t)),n="ifdef"===s[1]?void 0!==t[s[2]]:"ifndef"===s[1]?void 0===t[s[2]]:Preprocessor.evaluate(t,s[2]),l=!n,e(" value: "+n+", isSkip: "+l),h.push(o={include:n,index:i.index,lastIndex:Preprocessor.IF.lastIndex}),e(" push ("+h.length+"): "+JSON.stringify(o));break;case"endif":case"else":case"elif":if(Preprocessor.ENDIF.lastIndex=i.index,null===(s=Preprocessor.ENDIF.exec(this.source)))throw new Error("Illegal #"+i[2]+': "'+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");if(0===h.length)throw new Error("Unexpected #"+s[1]+': "'+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");a=h.pop(),e(" pop ("+h.length+"): "+JSON.stringify(a)),n=this.preserveLineNumbers?this.source.substring(a.index,a.lastIndex).replace(NOT_LINE_ENDING,"")+this.source.substring(a.lastIndex,i.index)+this.source.substring(i.index,Preprocessor.ENDIF.lastIndex).replace(NOT_LINE_ENDING,""):this.source.substring(a.lastIndex,i.index),a.include?(e(" incl: "+Preprocessor.nlToStr(n)+", 0-"+a.index+" + "+n.length+" bytes + "+Preprocessor.ENDIF.lastIndex+"-"+this.source.length),this.source=this.source.substring(0,a.index)+n+this.source.substring(Preprocessor.ENDIF.lastIndex)):this.preserveLineNumbers?(e(" excl(\\n): "+Preprocessor.nlToStr(n)+", 0-"+a.index+" + "+Preprocessor.ENDIF.lastIndex+"-"+this.source.length),n=n.replace(NOT_LINE_ENDING,""),this.source=this.source.substring(0,a.index)+n+this.source.substring(Preprocessor.ENDIF.lastIndex)):(e(" excl: "+Preprocessor.nlToStr(n)+", 0-"+a.index+" + "+Preprocessor.ENDIF.lastIndex+"-"+this.source.length),n="",this.source=this.source.substring(0,a.index)+this.source.substring(Preprocessor.ENDIF.lastIndex)),""===this.source&&e(" result empty"),l=!1,Preprocessor.EXPR.lastIndex=a.index+n.length,e(" continue at "+Preprocessor.EXPR.lastIndex),"else"!==s[1]&&"elif"!==s[1]||(n="else"===s[1]?!a.include:Preprocessor.evaluate(t,s[2]),l=!n,e(" isSkip: "+l),h.push(o={include:n,index:Preprocessor.EXPR.lastIndex,lastIndex:Preprocessor.EXPR.lastIndex}),e(" push ("+h.length+"): "+JSON.stringify(o)));break;case"define":if(Preprocessor.DEFINE.lastIndex=i.index,Preprocessor.VAR.lastIndex=i.index,Preprocessor.FUNCTION.lastIndex=i.index,Preprocessor.BOOLVAR.lastIndex=i.index,null===(s=Preprocessor.DEFINE.exec(this.source)))throw new Error("Illegal #"+i[2]+": "+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");var c,u,g;if(e(' def: "'+s[1]+'"'),null!==(r=Preprocessor.VAR.exec(this.source)))g="var",c=r[1],u=r[2],e(" match3(var): "+JSON.stringify(r));else if(null!==(r=Preprocessor.FUNCTION.exec(this.source)))g="function",c=r[1],u=r[2],e(" match3(function): "+JSON.stringify(r));else{if(null===(r=Preprocessor.BOOLVAR.exec(this.source)))throw new Error("Illegal #"+i[2]+": "+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");g="var",c=r[1],u=!0,e(" match3(boolvar): "+JSON.stringify(r))}e(" type: "+g),e(" identifier: "+c),e(" value: "+u),t[c]={type:g,value:u},e(" defines "+JSON.stringify(t));var p="";this.preserveLineNumbers&&(p=this.source.substring(i.index,Preprocessor.DEFINE.lastIndex).replace(NOT_LINE_ENDING,"")),this.source=this.source.substring(0,i.index)+_+p+this.source.substring(Preprocessor.DEFINE.lastIndex),Preprocessor.EXPR.lastIndex=i.index,e(" continue at "+Preprocessor.EXPR.lastIndex)}}return h.length>0&&e("Still on stack ("+h.length+"): "+JSON.stringify(h.pop())),this.source};var _unused_webpack_default_export=Preprocessor},function(t,e,i){"use strict";i.r(e);var s={};i.r(s),i.d(s,"getShortOpName",(function(){return p})),i.d(s,"shuffleArray",(function(){return _})),i.d(s,"shortId",(function(){return m})),i.d(s,"uuid",(function(){return T})),i.d(s,"generateUUID",(function(){return A})),i.d(s,"simpleId",(function(){return x})),i.d(s,"smoothStep",(function(){return v})),i.d(s,"smootherStep",(function(){return y})),i.d(s,"clamp",(function(){return I})),i.d(s,"map",(function(){return S})),i.d(s,"cacheBust",(function(){return R})),i.d(s,"copyArray",(function(){return P})),i.d(s,"basename",(function(){return O})),i.d(s,"jsonp",(function(){return F})),i.d(s,"ajaxSync",(function(){return C})),i.d(s,"ajax",(function(){return w})),i.d(s,"request",(function(){return M})),i.d(s,"keyCodeToName",(function(){return U})),i.d(s,"UTILS",(function(){return g}));var r={};i.r(r),i.d(r,"base64Chars",(function(){return k})),i.d(r,"base64lookup",(function(){return D})),i.d(r,"b64encTypesArray",(function(){return G})),i.d(r,"b64decTypedArray",(function(){return H}));var n={};i.r(n),i.d(n,"easeExpoIn",(function(){return j})),i.d(n,"easeExpoOut",(function(){return K})),i.d(n,"easeExpoInOut",(function(){return Q})),i.d(n,"easeCubicIn",(function(){return q})),i.d(n,"easeCubicOut",(function(){return J})),i.d(n,"easeCubicInOut",(function(){return Z})),i.d(n,"ANIM",(function(){return X})),i.d(n,"Anim",(function(){return $})),i.d(n,"TL",(function(){return tt}));var o={};i.r(o),i.d(o,"PatchConnectionReceiver",(function(){return Kt})),i.d(o,"PatchConnectionSender",(function(){return Qt})),i.d(o,"PatchConnectorBroadcastChannel",(function(){return qt}));class a{constructor(t){this._logs=[],this.initiator=t}stack(t){console.error("["+this.initiator+"] ",t),console.log((new Error).stack),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"error",t)}groupCollapsed(t){console.groupCollapsed("["+this.initiator+"] "+t)}table(t){console.table(t)}groupEnd(){console.groupEnd()}error(t){console.error("["+this.initiator+"]",...arguments),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"error",arguments)}info(t){console.error("["+this.initiator+"]",...arguments),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"info",arguments)}warn(t){console.warn("["+this.initiator+"]",...arguments),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"warn",arguments)}verbose(){(CABLES.UI&&CABLES.UI.logFilter.shouldPrint(this.initiator,...arguments)||!CABLES.logSilent)&&console.log("["+this.initiator+"]",...arguments),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"verbose",arguments)}log(t){(CABLES.UI&&CABLES.UI.logFilter.shouldPrint(this.initiator,...arguments)||!CABLES.logSilent)&&console.log("["+this.initiator+"]",...arguments),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"log",arguments)}userInteraction(t){}}const h={EASINGS:["linear","absolute","smoothstep","smootherstep","Cubic In","Cubic Out","Cubic In Out","Expo In","Expo Out","Expo In Out","Sin In","Sin Out","Sin In Out","Quart In","Quart Out","Quart In Out","Quint In","Quint Out","Quint In Out","Back In","Back Out","Back In Out","Elastic In","Elastic Out","Bounce In","Bounce Out"],EASING_LINEAR:0,EASING_ABSOLUTE:1,EASING_SMOOTHSTEP:2,EASING_SMOOTHERSTEP:3,EASING_CUBICSPLINE:4,EASING_CUBIC_IN:5,EASING_CUBIC_OUT:6,EASING_CUBIC_INOUT:7,EASING_EXPO_IN:8,EASING_EXPO_OUT:9,EASING_EXPO_INOUT:10,EASING_SIN_IN:11,EASING_SIN_OUT:12,EASING_SIN_INOUT:13,EASING_BACK_IN:14,EASING_BACK_OUT:15,EASING_BACK_INOUT:16,EASING_ELASTIC_IN:17,EASING_ELASTIC_OUT:18,EASING_BOUNCE_IN:19,EASING_BOUNCE_OUT:21,EASING_QUART_IN:22,EASING_QUART_OUT:23,EASING_QUART_INOUT:24,EASING_QUINT_IN:25,EASING_QUINT_OUT:26,EASING_QUINT_INOUT:27},l={OP_PORT_TYPE_VALUE:0,OP_PORT_TYPE_FUNCTION:1,OP_PORT_TYPE_OBJECT:2,OP_PORT_TYPE_TEXTURE:2,OP_PORT_TYPE_ARRAY:3,OP_PORT_TYPE_DYNAMIC:4,OP_PORT_TYPE_STRING:5,OP_VERSION_PREFIX:"_v"},c={PORT_DIR_IN:0,PORT_DIR_OUT:1},u={PACO_CLEAR:0,PACO_VALUECHANGE:1,PACO_OP_DELETE:2,PACO_UNLINK:3,PACO_LINK:4,PACO_LOAD:5,PACO_OP_CREATE:6,PACO_OP_ENABLE:7,PACO_OP_DISABLE:8,PACO_UIATTRIBS:9,PACO_VARIABLES:10,PACO_TRIGGERS:11,PACO_PORT_SETVARIABLE:12,PACO_PORT_SETANIMATED:13,PACO_PORT_ANIM_UPDATED:14,PACO_DESERIALIZE:15},g={float32Concat:function(t,e){t instanceof Float32Array||(t=new Float32Array(t)),e instanceof Float32Array||(e=new Float32Array(e));const i=new Float32Array(t.length+e.length);return i.set(t),i.set(e,t.length),i}},p=function(t){let e=t.split(".")[t.split(".").length-1];if(e.indexOf(l.OP_VERSION_PREFIX)>0){const t=e.split(l.OP_VERSION_PREFIX)[1];e=e.substring(0,e.length-(l.OP_VERSION_PREFIX+t).length)}return e},_=function(t){for(let e=t.length-1;e>0;e--){const i=Math.floor(Math.seededRandom()*(e+1)),s=t[e];t[e]=t[i],t[i]=s}return t},d={},f=function(){let t=Math.random().toString(36).substr(2,9);return d.hasOwnProperty(t)&&(t=f()),d[t]=!0,t},m=f,E=function(){let t=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const i=(t+16*Math.random())%16|0;return t=Math.floor(t/16),("x"==e?i:3&i|8).toString(16)})},T=E,A=E;let b=0;const x=function(){return b++,b},v=function(t){const e=Math.max(0,Math.min(1,(t-0)/1));return t=e*e*(3-2*e)},y=function(t){const e=Math.max(0,Math.min(1,(t-0)/1));return t=e*e*e*(e*(6*e-15)+10)},I=function(t,e,i){return Math.min(Math.max(t,e),i)},S=function(t,e,i,s,r,n){if(t>=i)return r;if(t<=e)return s;let o=!1;const a=Math.min(e,i),h=Math.max(e,i);a!=e&&(o=!0);let l=!1;const c=Math.min(s,r),u=Math.max(s,r);c!=s&&(l=!0);let g=0,p=0;return g=o?(h-t)*(u-c)/(h-a):(t-a)*(u-c)/(h-a),p=l?u-g:g+c,n?1==n?s+(t=Math.max(0,Math.min(1,(p-s)/(r-s))))*t*(3-2*t)*(r-s):2==n?s+(t=Math.max(0,Math.min(1,(p-s)/(r-s))))*t*t*(t*(6*t-15)+10)*(r-s):p:p};Math.randomSeed=1,Math.seededRandom=function(t,e){0===Math.randomSeed&&(Math.randomSeed=999*Math.random()),t=t||1,e=e||0,Math.randomSeed=(9301*Math.randomSeed+49297)%233280;return e+Math.randomSeed/233280*(t-e)},g.arrayWriteToEnd=function(t,e){for(let e=1;e-1?t+="&":t+="?",t+"cb="+CABLES.uuid()},P=function(t,e){if(!t)return null;(e=e||[]).length=t.length;for(let i=0;i0){let t=i[i.length-1].split("?");e=t[0],t=e.split("."),e=t[0]}return e};let N=null;const F=function(t,e){N=N||0,N++;const i=N;CABLES["jsonpFunc"+i]=function(t){e(!1,t)};let s="?";t.indexOf(s)>-1&&(s="&");const r=document.createElement("script");r.setAttribute("src",t+s+"callback=CABLES.jsonpFunc"+i),document.body.appendChild(r)},C=function(t,e,i,s,r){M({url:t,cb:e,method:i,data:s,contenttype:r,sync:!0})},w=function(t,e,i,s,r,n,o={}){M({url:t,cb:e,method:i,data:s,contenttype:r,sync:!1,jsonP:n,headers:o})},M=function(t){let e;t.hasOwnProperty("asynch")||(t.asynch=!0);try{e=new XMLHttpRequest}catch(t){}if(e.onreadystatechange=function(){4==e.readyState&&t.cb&&(200==e.status||0==e.status?t.cb(!1,e.responseText,e):t.cb(!0,e.responseText,e))},e.addEventListener("progress",t=>{}),e.open(t.method?t.method.toUpperCase():"GET",t.url,!t.sync),"object"==typeof t.headers){const i=Object.keys(t.headers);for(let s=0;sthis._cgl.maxTexSize||e>this._cgl.maxTexSize)&&this._log.error("texture size too big! "+t+"x"+e+" / max: "+this._cgl.maxTexSize),t=Math.min(t,this._cgl.maxTexSize),e=Math.min(e,this._cgl.maxTexSize),t=Math.floor(t),e=Math.floor(e),this.width==t&&this.height==e)return;this.width=t,this.height=e,this.deleted=!1,this.shortInfoString=this.getInfoOneLine(),this._cgl.gl.bindTexture(this.texTarget,this.tex),this._cgl.profileData.profileTextureResize++;if(this._cgl.patch.config.canvas.forceTextureNearest&&(this.filter=B.FILTER_NEAREST),1!=this._cgl.glVersion||this.textureType!=B.TYPE_FLOAT||this.filter!=B.FILTER_LINEAR||this._cgl.gl.getExtension("OES_texture_float_linear")||(console.warn("this graphics card does not support floating point texture linear interpolation! using NEAREST"),this.filter=B.FILTER_NEAREST),this.textureType==B.TYPE_FLOAT)if(1==this._cgl.glVersion)if(this._cgl.glUseHalfFloatTex){const i=this._cgl.gl.getExtension("OES_texture_half_float");this._cgl.gl.getExtension("EXT_color_buffer_half_float");if(!i)throw new Error("no half float texture extension");this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,t,e,0,this._cgl.gl.RGBA,i.HALF_FLOAT_OES,null)}else this._cgl.gl.getExtension("OES_texture_float"),this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,t,e,0,this._cgl.gl.RGBA,this._cgl.gl.FLOAT,null);else if(this._cgl.glUseHalfFloatTex){if(!this._cgl.gl.getExtension("EXT_color_buffer_half_float"))throw new Error("no half float texture extension");console.log("half float",this._cgl.gl.RGBA16F,this._cgl.gl.HALF_FLOAT),console.log("half float",this._cgl.gl.HALF_FLOAT),console.log("half float",this._cgl.gl.RGBA16F),this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,t,e,0,this._cgl.gl.RGBA,this._cgl.gl.HALF_FLOAT,null)}else this._cgl.gl.getExtension("EXT_color_buffer_float"),this._cgl.gl.getExtension("EXT_color_buffer_float_linear"),this._cgl.gl.getExtension("OES_texture_float_linear"),this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA32F,t,e,0,this._cgl.gl.RGBA,this._cgl.gl.FLOAT,null);else if(this.textureType==B.TYPE_DEPTH)if(1==this._cgl.glVersion){const i=this._cgl.gl.DEPTH_COMPONENT;this._cgl.gl.texImage2D(this.texTarget,0,i,t,e,0,this._cgl.gl.DEPTH_COMPONENT,this._cgl.gl.UNSIGNED_SHORT,null)}else{const i=this._cgl.gl.DEPTH_COMPONENT32F;this._cgl.gl.texImage2D(this.texTarget,0,i,t,e,0,this._cgl.gl.DEPTH_COMPONENT,this._cgl.gl.FLOAT,null)}else this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,t,e,0,this._cgl.gl.RGBA,this._cgl.gl.UNSIGNED_BYTE,null);this._setFilter(),this.updateMipMap(),this._cgl.gl.bindTexture(this.texTarget,null)},B.prototype.initFromData=function(t,e,i,s,r){if(this.filter=s,this.wrap=r,null==s&&(this.filter=B.FILTER_LINEAR),null==r&&(this.wrap=B.WRAP_CLAMP_TO_EDGE),this.width=e,this.height=i,this._fromData=!0,this.deleted=!1,this.height>this._cgl.maxTexSize||this.width>this._cgl.maxTexSize){const t=CGL.Texture.getTempTexture(this._cgl);return this.width=t.width,this.height=t.height,this.tex=t.tex,void this._log.error("[cgl_texture] texture size to big!!!",this.width,this.height,this._cgl.maxTexSize)}this.flip&&this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_FLIP_Y_WEBGL,this.flip),this._cgl.gl.bindTexture(this.texTarget,this.tex),this.textureType==B.TYPE_FLOAT?this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA32F,e,i,0,this._cgl.gl.RGBA,this._cgl.gl.FLOAT,t):this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,e,i,0,this._cgl.gl.RGBA,this._cgl.gl.UNSIGNED_BYTE,t),this._setFilter(),this.updateMipMap(),this.flip&&this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_FLIP_Y_WEBGL,!1),this._cgl.gl.bindTexture(this.texTarget,null)},B.prototype.updateMipMap=function(){2!=this._cgl.glVersion&&!this.isPowerOfTwo()||this.filter!=B.FILTER_MIPMAP||(this._cgl.gl.generateMipmap(this.texTarget),this._cgl.profileData.profileGenMipMap++)},B.prototype.initTexture=function(t,e){if(this._cgl.printError("before initTexture"),this._cgl.checkFrameStarted("texture inittexture"),this._fromData=!1,this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,this.unpackAlpha),t.width&&(this.width=t.width),t.height&&(this.height=t.height),e&&(this.filter=e),t.height>this._cgl.maxTexSize||t.width>this._cgl.maxTexSize){const e=CGL.Texture.getTempTexture(this._cgl);return this.width=e.width,this.height=e.height,this.tex=e.tex,void this._log.error("[cgl_texture] texture size to big!!!",t.width,t.height,this._cgl.maxTexSize)}this._cgl.gl.bindTexture(this.texTarget,this.tex),this.deleted=!1,this.flipped=!this.flip,this.flipped&&this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_FLIP_Y_WEBGL,this.flipped),this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,this._cgl.gl.RGBA,this._cgl.gl.UNSIGNED_BYTE,t),this._setFilter(),this.updateMipMap(),this._cgl.gl.bindTexture(this.texTarget,null),this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),this.flipped&&this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_FLIP_Y_WEBGL,!1),this.getInfoOneLine(),this._cgl.printError("initTexture")},B.prototype.delete=function(){this.loading||(this.deleted=!0,this.width=0,this.height=0,this._cgl.profileData.profileTextureDelete++,this._cgl.gl.deleteTexture(this.tex),this.image=null,this.tex=null)},B.prototype.isPowerOfTwo=function(){return B.isPowerOfTwo(this.width)&&B.isPowerOfTwo(this.height)},B.prototype.printInfo=function(){console.log(this.getInfo())},B.prototype.getInfoReadable=function(){const t=this.getInfo();let e="";t.name=t.name.substr(0,t.name.indexOf("?rnd="));for(const i in t)e+="* "+i+": **"+t[i]+"**\n";return e},B.prototype.getInfoOneLine=function(){let t=this.width+"x"+this.height;return this.textureType===CGL.Texture.TYPE_FLOAT?t+=" 32bit":t+=" 8bit",this.filter===CGL.Texture.FILTER_NEAREST&&(t+=" nearest"),this.filter===CGL.Texture.FILTER_LINEAR&&(t+=" linear"),this.filter===CGL.Texture.FILTER_MIPMAP&&(t+=" mipmap"),this.wrap===CGL.Texture.WRAP_CLAMP_TO_EDGE&&(t+=" clamp"),this.wrap===CGL.Texture.WRAP_REPEAT&&(t+=" repeat"),this.wrap===CGL.Texture.WRAP_MIRRORED_REPEAT&&(t+=" repeatmir"),this.shortInfoString=t,t},B.prototype.getInfo=function(){return B.getTexInfo(this)},B.prototype._setFilter=function(){if(this._cgl.printError("before _setFilter"),this._fromData||this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,this.unpackAlpha),this.shadowMap&&(this._cgl.gl.texParameteri(this._cgl.gl.TEXTURE_2D,this._cgl.gl.TEXTURE_COMPARE_MODE,this._cgl.gl.COMPARE_REF_TO_TEXTURE),this._cgl.gl.texParameteri(this._cgl.gl.TEXTURE_2D,this._cgl.gl.TEXTURE_COMPARE_FUNC,this._cgl.gl.LEQUAL)),this.textureType==B.TYPE_FLOAT&&this.filter==B.FILTER_MIPMAP&&(this.filter=B.FILTER_LINEAR,this._log.stack("texture: HDR and mipmap filtering at the same time is not possible")),1!=this._cgl.glVersion||this.isPowerOfTwo()){if(this.wrap==B.WRAP_CLAMP_TO_EDGE?(this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_S,this._cgl.gl.CLAMP_TO_EDGE),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_T,this._cgl.gl.CLAMP_TO_EDGE)):this.wrap==B.WRAP_REPEAT?(this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_S,this._cgl.gl.REPEAT),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_T,this._cgl.gl.REPEAT)):this.wrap==B.WRAP_MIRRORED_REPEAT&&(this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_S,this._cgl.gl.MIRRORED_REPEAT),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_T,this._cgl.gl.MIRRORED_REPEAT)),this.filter==B.FILTER_NEAREST)this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MAG_FILTER,this._cgl.gl.NEAREST),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MIN_FILTER,this._cgl.gl.NEAREST);else if(this.filter==B.FILTER_LINEAR)this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MIN_FILTER,this._cgl.gl.LINEAR),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MAG_FILTER,this._cgl.gl.LINEAR);else{if(this.filter!=B.FILTER_MIPMAP)throw this._log.log("unknown texture filter!",this.filter),new Error("unknown texture filter!"+this.filter);this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MAG_FILTER,this._cgl.gl.LINEAR),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MIN_FILTER,this._cgl.gl.LINEAR_MIPMAP_LINEAR)}if(this.anisotropic){const t=this._cgl.gl.getExtension("EXT_texture_filter_anisotropic");if(t){const e=this._cgl.gl.getParameter(t.MAX_TEXTURE_MAX_ANISOTROPY_EXT);this._cgl.gl.texParameterf(this._cgl.gl.TEXTURE_2D,t.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(e,this.anisotropic))}}}else this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MAG_FILTER,this._cgl.gl.NEAREST),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MIN_FILTER,this._cgl.gl.NEAREST),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_S,this._cgl.gl.CLAMP_TO_EDGE),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_T,this._cgl.gl.CLAMP_TO_EDGE),this.filter=B.FILTER_NEAREST,this.wrap=B.WRAP_CLAMP_TO_EDGE;this.getInfoOneLine(),this._cgl.printError("_setFilter")},B.load=function(t,e,i,s){if(!e)return i({error:!0});let r=null;t.patch.loading.existByName(e)||(r=t.patch.loading.start("texture",e));const n=new B(t);return n.name=e,t.patch.isEditorMode()&&gui.jobs().start({id:"loadtexture"+r,title:"loading texture "+CABLES.basename(e)}),n.image=new Image,n.image.crossOrigin="anonymous",n.loading=!0,s&&s.hasOwnProperty("filter")&&(n.filter=s.filter),s&&s.hasOwnProperty("flip")&&(n.flip=s.flip),s&&s.hasOwnProperty("wrap")&&(n.wrap=s.wrap),s&&s.hasOwnProperty("anisotropic")&&(n.anisotropic=s.anisotropic),s&&s.hasOwnProperty("unpackAlpha")&&(n.unpackAlpha=s.unpackAlpha),n.image.onabort=n.image.onerror=s=>{console.warn("[cgl.texture.load] error loading texture",e,s),n.loading=!1,r&&t.patch.loading.finished(r);i&&i({error:!0},n),t.patch.isEditorMode()&&gui.jobs().finish("loadtexture"+r)},n.image.onload=function(e){t.addNextFrameOnceCallback(()=>{n.initTexture(n.image),r&&t.patch.loading.finished(r),n.loading=!1,t.patch.isEditorMode()&&gui.jobs().finish("loadtexture"+r),i&&i(null,n)})},n.image.src=e,n},B.getTempTexture=function(t){return t||console.error("[getTempTexture] no cgl!"),t.tempTexture||(t.tempTexture=B.getTemporaryTexture(t,256,B.FILTER_LINEAR,B.REPEAT)),t.tempTexture},B.getEmptyTexture=function(t,e){if(e)return B.getEmptyTextureFloat(t);if(t||console.error("[getEmptyTexture] no cgl!"),t.tempTextureEmpty)return t.tempTextureEmpty;t.tempTextureEmpty=new B(t,{name:"emptyTexture"});const i=new Uint8Array(256).fill(0);for(let t=0;t<256;t+=4)i[t+3]=0;return t.tempTextureEmpty.initFromData(i,8,8,B.FILTER_NEAREST,B.WRAP_REPEAT),t.tempTextureEmpty},B.getEmptyTextureFloat=function(t){if(t||console.error("[getEmptyTextureFloat] no cgl!"),t.tempTextureEmptyFloat)return t.tempTextureEmptyFloat;t.tempTextureEmptyFloat=new B(t,{name:"emptyTexture",isFloatingPointTexture:!0});const e=new Float32Array(256).fill(1);for(let t=0;t<256;t+=4)e[t+3]=0;return t.tempTextureEmptyFloat.initFromData(e,8,8,B.FILTER_NEAREST,B.WRAP_REPEAT),t.tempTextureEmptyFloat},B.getRandomTexture=function(t){if(t||console.error("[getRandomTexture] no cgl!"),t.randomTexture)return t.randomTexture;const e=new Uint8Array(262144);for(let t=0;t<65536;t++)e[4*t+0]=255*Math.random(),e[4*t+1]=255*Math.random(),e[4*t+2]=255*Math.random(),e[4*t+3]=255;return t.randomTexture=new B(t),t.randomTexture.initFromData(e,256,256,B.FILTER_NEAREST,B.WRAP_REPEAT),t.randomTexture},B.getBlackTexture=function(t){if(t||this._log.error("[getBlackTexture] no cgl!"),t.blackTexture)return t.blackTexture;const e=new Uint8Array(256);for(let t=0;t<64;t++)e[4*t+0]=e[4*t+1]=e[4*t+2]=0,e[4*t+3]=255;return t.blackTexture=new B(t),t.blackTexture.initFromData(e,8,8,B.FILTER_NEAREST,B.WRAP_REPEAT),t.blackTexture},B.getEmptyCubemapTexture=function(t){const e=[t.gl.TEXTURE_CUBE_MAP_POSITIVE_X,t.gl.TEXTURE_CUBE_MAP_NEGATIVE_X,t.gl.TEXTURE_CUBE_MAP_POSITIVE_Y,t.gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,t.gl.TEXTURE_CUBE_MAP_POSITIVE_Z,t.gl.TEXTURE_CUBE_MAP_NEGATIVE_Z],i=t.gl.createTexture(),s=t.gl.TEXTURE_CUBE_MAP,r=B.FILTER_NEAREST,n=B.WRAP_CLAMP_TO_EDGE;t.profileData.profileTextureNew++,t.gl.bindTexture(s,i),t.profileData.profileTextureResize++;for(let i=0;i<6;i+=1){const r=new Uint8Array(256);t.gl.texImage2D(e[i],0,t.gl.RGBA,8,8,0,t.gl.RGBA,t.gl.UNSIGNED_BYTE,r),t.gl.texParameteri(s,t.gl.TEXTURE_MAG_FILTER,t.gl.NEAREST),t.gl.texParameteri(s,t.gl.TEXTURE_MIN_FILTER,t.gl.NEAREST),t.gl.texParameteri(s,t.gl.TEXTURE_WRAP_S,t.gl.CLAMP_TO_EDGE),t.gl.texParameteri(s,t.gl.TEXTURE_WRAP_T,t.gl.CLAMP_TO_EDGE)}return t.gl.bindTexture(s,null),{id:CABLES.uuid(),tex:i,cubemap:i,width:8,height:8,filter:r,wrap:n,unpackAlpha:!0,flip:!0,_fromData:!0,name:"emptyCubemapTexture",anisotropic:0}},B.getTempGradientTexture=function(t){if(t||console.error("[getTempGradientTexture] no cgl!"),t.tempTextureGradient)return t.tempTextureGradient;const e=new B(t),i=new Uint8Array(262144);for(let t=0;t<256;t++)for(let e=0;e<256;e++)i[4*(e+256*t)+0]=i[4*(e+256*t)+1]=i[4*(e+256*t)+2]=255-t,i[4*(e+256*t)+3]=255;return e.initFromData(i,256,256,B.FILTER_NEAREST,B.WRAP_REPEAT),t.tempTextureGradient=e,e},B.getTemporaryTexture=function(t,e,i,s){const r=new B(t),n=[];for(let t=0;t>2],r+=k[(3&i[e])<<4|i[e+1]>>4],r+=k[(15&i[e+1])<<2|i[e+2]>>6],r+=k[63&i[e+2]];return s%3==2?r=r.substring(0,r.length-1)+"=":s%3==1&&(r=r.substring(0,r.length-2)+"=="),r},H=function(t){let e,i,s,r,n,o=.75*t.length,a=t.length,h=0;"="===t[t.length-1]&&(o--,"="===t[t.length-2]&&o--);let l=new ArrayBuffer(o),c=new Uint8Array(l);for(e=0;e>4,c[h++]=(15&s)<<4|r>>2,c[h++]=(3&r)<<6|63&n;return l};class z{constructor(t){this._init(),this._first=!0,this._wireMesh=null,t&&this.apply(t)}_init(){this._max=[-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE],this._min=[Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE],this._center=[0,0,0],this._size=[0,0,0],this._maxAxis=0,this._first=!0}get maxAxis(){return this._maxAxis||1}get size(){return this._size}get center(){return this._center}get x(){return this._center[0]}get y(){return this._center[1]}get z(){return this._center[2]}get minX(){return this._min[0]}get minY(){return this._min[1]}get minZ(){return this._min[2]}get maxX(){return this._max[0]}get maxY(){return this._max[1]}get maxZ(){return this._max[2]}apply(t,e){if(t){if(t instanceof z){const e=t;this.applyPos(e.maxX,e.maxY,e.maxZ),this.applyPos(e.minX,e.minY,e.minZ)}else for(let e=0;e4)&&(console.log("itemsize wrong?",i,t),this._log.stack("itemsize"),i=3),1==i?s="float":2==i?s="vec2":3==i?s="vec3":4==i&&(s="vec4");const r={name:t,data:e,itemSize:i,type:s};this._attributes[t]=r},W.prototype.copyAttribute=function(t,e){const i=this.getAttribute(t);e.setAttribute(t,new Float32Array(i.data),i.itemSize)},W.prototype.setVertices=function(t){t instanceof Float32Array?this._vertices=t:this._vertices=new Float32Array(t)},W.prototype.setTexCoords=function(t){t instanceof Float32Array?this.texCoords=t:this.texCoords=new Float32Array(t)},W.prototype.calcNormals=function(t){t||this.unIndex(),this.calculateNormals({})},W.prototype.flipNormals=function(t,e,i){let s=vec3.create();null==t&&(t=1),null==e&&(e=1),null==i&&(i=1);for(let r=0;r0&&o.push(this.tangents[3*this.verticesIndices[l+0]+0],this.tangents[3*this.verticesIndices[l+0]+1],this.tangents[3*this.verticesIndices[l+0]+2]),this.biTangents.length>0&&a.push(this.biTangents[3*this.verticesIndices[l+0]+0],this.biTangents[3*this.verticesIndices[l+0]+1],this.biTangents[3*this.verticesIndices[l+0]+2]),this.texCoords?r.push(this.texCoords[2*this.verticesIndices[l+0]+0],this.texCoords[2*this.verticesIndices[l+0]+1]):r.push(0,0),s.push(h),h++,i.push(this.vertices[3*this.verticesIndices[l+1]+0],this.vertices[3*this.verticesIndices[l+1]+1],this.vertices[3*this.verticesIndices[l+1]+2]),n.push(this.vertexNormals[3*this.verticesIndices[l+1]+0],this.vertexNormals[3*this.verticesIndices[l+1]+1],this.vertexNormals[3*this.verticesIndices[l+1]+2]),this.tangents.length>0&&o.push(this.tangents[3*this.verticesIndices[l+1]+0],this.tangents[3*this.verticesIndices[l+1]+1],this.tangents[3*this.verticesIndices[l+1]+2]),this.biTangents.length>0&&a.push(this.biTangents[3*this.verticesIndices[l+1]+0],this.biTangents[3*this.verticesIndices[l+1]+1],this.biTangents[3*this.verticesIndices[l+1]+2]),this.texCoords?r.push(this.texCoords[2*this.verticesIndices[l+1]+0],this.texCoords[2*this.verticesIndices[l+1]+1]):r.push(0,0),s.push(h),h++,i.push(this.vertices[3*this.verticesIndices[l+2]+0],this.vertices[3*this.verticesIndices[l+2]+1],this.vertices[3*this.verticesIndices[l+2]+2]),n.push(this.vertexNormals[3*this.verticesIndices[l+2]+0],this.vertexNormals[3*this.verticesIndices[l+2]+1],this.vertexNormals[3*this.verticesIndices[l+2]+2]),this.tangents.length>0&&o.push(this.tangents[3*this.verticesIndices[l+2]+0],this.tangents[3*this.verticesIndices[l+2]+1],this.tangents[3*this.verticesIndices[l+2]+2]),this.biTangents.length>0&&a.push(this.biTangents[3*this.verticesIndices[l+2]+0],this.biTangents[3*this.verticesIndices[l+2]+1],this.biTangents[3*this.verticesIndices[l+2]+2]),this.texCoords?r.push(this.texCoords[2*this.verticesIndices[l+2]+0],this.texCoords[2*this.verticesIndices[l+2]+1]):r.push(0,0),s.push(h),h++;this.vertices=i,this.texCoords=r,this.vertexNormals=n,o.length>0&&(this.tangents=o),a.length>0&&(this.biTangents=a),this.verticesIndices.length=0,t&&(this.verticesIndices=s),e||this.calculateNormals()},W.prototype.calcBarycentric=function(){let t=[];t.length=this.vertices.length;for(let e=0;e1&&this.keys.splice(0,i)},$.prototype.clear=function(t){let e=0;t&&(e=this.getValue(t)),this.keys.length=0,t&&this.setValue(t,e),null!==this.onChange&&this.onChange(),this.emitEvent("onChange",this)},$.prototype.sortKeys=function(){this.keys.sort((t,e)=>parseFloat(t.time)-parseFloat(e.time)),this._needsSort=!1},$.prototype.getLength=function(){return 0===this.keys.length?0:this.keys[this.keys.length-1].time},$.prototype.getKeyIndex=function(t){let e=0;for(let i=0;i=this.keys[i].time&&(e=i),this.keys[i].time>t)return e;return e},$.prototype.setValue=function(t,e,i){let s=null;for(const r in this.keys)if(this.keys[r].time==t){s=this.keys[r],this.keys[r].setValue(e),this.keys[r].cb=i;break}return s||(s=new X.Key({time:t,value:e,e:this.defaultEasing,cb:i}),this.keys.push(s)),this.onChange&&this.onChange(),this.emitEvent("onChange",this),this._needsSort=!0,s},$.prototype.setKeyEasing=function(t,e){this.keys[t]&&(this.keys[t].setEasing(e),this.emitEvent("onChange",this))},$.prototype.getSerialized=function(){const t={keys:[]};t.loop=this.loop;for(const e in this.keys)t.keys.push(this.keys[e].getSerialized());return t},$.prototype.getKey=function(t){const e=this.getKeyIndex(t);return this.keys[e]},$.prototype.getNextKey=function(t){let e=this.getKeyIndex(t)+1;return e>=this.keys.length&&(e=this.keys.length-1),this.keys[e]},$.prototype.isFinished=function(t){return this.keys.length<=0||t>this.keys[this.keys.length-1].time},$.prototype.isStarted=function(t){return!(this.keys.length<=0)&&t>=this.keys[0].time},$.prototype.getValue=function(t){if(0===this.keys.length)return 0;if(this._needsSort&&this.sortKeys(),tthis.keys[e].time){t/this.keys[e].time>this._timesLooped&&(this._timesLooped++,this.onLooped&&this.onLooped()),t=(t-this.keys[0].time)%(this.keys[e].time-this.keys[0].time),t+=this.keys[0].time}const i=this.getKeyIndex(t);if(i>=e)return this.keys[e].cb&&!this.keys[e].cbTriggered&&this.keys[e].trigger(),this.keys[e].value;const s=parseInt(i,10)+1,r=this.keys[i],n=this.keys[s];if(r.cb&&!r.cbTriggered&&r.trigger(),!n)return-1;const o=(t-r.time)/(n.time-r.time);return r.ease||this.log._warn("has no ease",r,n),r.ease(o,n)},$.prototype.addKey=function(t){void 0===t.time?this.log.warn("key time undefined, ignoring!"):(this.keys.push(t),null!==this.onChange&&this.onChange(),this.emitEvent("onChange",this))},$.prototype.easingFromString=function(t){return"linear"==t?h.EASING_LINEAR:"absolute"==t?h.EASING_ABSOLUTE:"smoothstep"==t?h.EASING_SMOOTHSTEP:"smootherstep"==t?h.EASING_SMOOTHERSTEP:"Cubic In"==t?h.EASING_CUBIC_IN:"Cubic Out"==t?h.EASING_CUBIC_OUT:"Cubic In Out"==t?h.EASING_CUBIC_INOUT:"Expo In"==t?h.EASING_EXPO_IN:"Expo Out"==t?h.EASING_EXPO_OUT:"Expo In Out"==t?h.EASING_EXPO_INOUT:"Sin In"==t?h.EASING_SIN_IN:"Sin Out"==t?h.EASING_SIN_OUT:"Sin In Out"==t?h.EASING_SIN_INOUT:"Back In"==t?h.EASING_BACK_IN:"Back Out"==t?h.EASING_BACK_OUT:"Back In Out"==t?h.EASING_BACK_INOUT:"Elastic In"==t?h.EASING_ELASTIC_IN:"Elastic Out"==t?h.EASING_ELASTIC_OUT:"Bounce In"==t?h.EASING_BOUNCE_IN:"Bounce Out"==t?h.EASING_BOUNCE_OUT:"Quart Out"==t?h.EASING_QUART_OUT:"Quart In"==t?h.EASING_QUART_IN:"Quart In Out"==t?h.EASING_QUART_INOUT:"Quint Out"==t?h.EASING_QUINT_OUT:"Quint In"==t?h.EASING_QUINT_IN:"Quint In Out"==t?h.EASING_QUINT_INOUT:void 0},$.prototype.createPort=function(t,e,i){const s=t.inDropDown(e,h.EASINGS);return s.set("linear"),s.defaultValue="linear",s.onChange=function(){this.defaultEasing=this.easingFromString(s.get()),i&&i()}.bind(this),s},$.slerpQuaternion=function(t,e,i,s,r,n){$.slerpQuaternion.q1||($.slerpQuaternion.q1=quat.create(),$.slerpQuaternion.q2=quat.create());const o=i.getKeyIndex(t);let a=o+1;if(a>=i.keys.length&&(a=i.keys.length-1),o==a)quat.set(e,i.keys[o].value,s.keys[o].value,r.keys[o].value,n.keys[o].value);else{const h=i.keys[o].time,l=(t-h)/(i.keys[a].time-h);quat.set($.slerpQuaternion.q1,i.keys[o].value,s.keys[o].value,r.keys[o].value,n.keys[o].value),quat.set($.slerpQuaternion.q2,i.keys[a].value,s.keys[a].value,r.keys[a].value,n.keys[a].value),quat.slerp(e,$.slerpQuaternion.q1,$.slerpQuaternion.q2,l)}return e};const tt=X;tt.Anim=$;const et=function(t,e,i,s){Y.apply(this),this.data={},this._log=new a("core_port"),this.direction=c.PORT_DIR_IN,this.id=CABLES.simpleId(),this.parent=t,this.links=[],this.value=0,this.name=e,this.type=i||l.OP_PORT_TYPE_VALUE,this.uiAttribs=s||{},this.anim=null,this._oldAnimVal=-5711,this.defaultValue=null,this._uiActiveState=!0,this.ignoreValueSerialize=!1,this.onLinkChanged=null,this.crashed=!1,this._valueBeforeLink=null,this._lastAnimFrame=-1,this._animated=!1,this.onValueChanged=null,this.onTriggered=null,this.onUiActiveStateChange=null,this.changeAlways=!1,this._warnedDeprecated=!1,this._useVariableName=null,this.activityCounter=0,this._tempLastUiValue=null,Object.defineProperty(this,"title",{get(){return this.uiAttribs.title||this.name}}),Object.defineProperty(this,"val",{get(){return this._log.warn("val getter deprecated!",this),this._log.stack("val getter deprecated"),this._warnedDeprecated=!0,this.get()},set(t){this._log.warn("val setter deprecated!",this),this._log.stack("val setter deprecated"),this.setValue(t),this._warnedDeprecated=!0}})};et.prototype.copyLinkedUiAttrib=function(t,e){if(!CABLES.UI)return;if(!this.isLinked())return;const i={};i[t]=this.links[0].getOtherPort(this).getUiAttrib(t),e.setUiAttribs(i)},et.prototype.getValueForDisplay=function(){let t=String(this.value);return this.uiAttribs&&"boolnum"==this.uiAttribs.display&&(t+=" - ",this.value?t+="true":t+="false"),t=t.replace(/(<([^>]+)>)/gi,""),t.length>100&&(t=t.substring(0,100)),t},et.prototype.onAnimToggle=function(){},et.prototype._onAnimToggle=function(){this.onAnimToggle()},et.prototype.remove=function(){this.removeLinks(),this.parent.removePort(this)},et.prototype.setUiAttribs=function(t){let e=!1;this.uiAttribs||(this.uiAttribs={});for(const i in t)this.uiAttribs[i]!=t[i]&&(e=!0),this.uiAttribs[i]=t[i],"group"==i&&this.indexPort&&this.indexPort.setUiAttribs({group:t[i]});e&&this.emitEvent("onUiAttrChange",t,this)},et.prototype.getUiAttribs=function(){return this.uiAttribs},et.prototype.getUiAttrib=function(t){return this.uiAttribs&&this.uiAttribs.hasOwnProperty(t)?this.uiAttribs[t]:null},et.prototype.get=function(){return this._animated&&this._lastAnimFrame!=this.parent.patch.getFrameNum()&&(this._lastAnimFrame=this.parent.patch.getFrameNum(),this.value=this.anim.getValue(this.parent.patch.timer.getTime()),this._oldAnimVal=this.value,this.forceChange()),this.value},et.prototype.set=et.prototype.setValue=function(t){if(void 0!==t&&this.parent.enabled&&!this.crashed&&(t!==this.value||this.changeAlways||this.type==l.OP_PORT_TYPE_TEXTURE||this.type==l.OP_PORT_TYPE_ARRAY)){if(this._animated)this.anim.setValue(this.parent.patch.timer.getTime(),t);else{try{this.value=t,this.forceChange()}catch(t){this.crashed=!0,this.setValue=function(t){},this.onTriggered=function(){},this._log.error("onvaluechanged exception cought",t),this._log.error(t.stack),this._log.warn("exception in: "+this.parent.name),this.parent.patch.isEditorMode()&&gui.showOpCrash(this.parent),this.parent.patch.emitEvent("exception",t,this.parent),this.parent.onError&&this.parent.onError(t)}this.parent&&this.parent.patch&&this.parent.patch.isEditorMode()&&this.type==l.OP_PORT_TYPE_TEXTURE&&gui.texturePreview().updateTexturePort(this)}if(this.direction==c.PORT_DIR_OUT)for(let t=0;t{this.parent.patch.emitEvent("portAnimUpdated",this.parent,this,this.anim)}),t.anim.loop&&(this.anim.loop=t.anim.loop);for(const e in t.anim.keys)this.anim.keys.push(new X.Key(t.anim.keys[e]))}},et.prototype.getSerialized=function(){const t={};if(t.name=this.getName(),this.ignoreValueSerialize||0!==this.links.length||this.type==l.OP_PORT_TYPE_OBJECT&&this.value&&this.value.tex||(t.value=this.value),this._useVariableName&&(t.useVariable=this._useVariableName),this._animated&&(t.animated=!0),this.anim&&(t.anim=this.anim.getSerialized()),"file"==this.uiAttribs.display&&(t.display=this.uiAttribs.display),this.direction==c.PORT_DIR_OUT&&this.links.length>0){t.links=[];for(const e in this.links)!this.links[e].ignoreInSerialize&&this.links[e].portIn&&this.links[e].portOut&&t.links.push(this.links[e].getSerialized())}return t},et.prototype.shouldLink=function(){return!0},et.prototype.removeLinks=function(){let t=0;for(;this.links.length>0;){if(t++,t>5e3){this._log.warn("could not delete links... / infinite loop"),this.links.length=0;break}this.links[0].remove()}},et.prototype.removeLink=function(t){for(const e in this.links)this.links[e]==t&&this.links.splice(e,1);this.direction==c.PORT_DIR_IN&&(this.type==l.OP_PORT_TYPE_VALUE?this.setValue(this._valueBeforeLink||0):this.setValue(this._valueBeforeLink||null)),CABLES.UI&&this.parent.checkLinkTimeWarnings&&this.parent.checkLinkTimeWarnings(),this.onLinkChanged&&this.onLinkChanged(),this.emitEvent("onLinkChanged"),this.parent.emitEvent("onLinkChanged")},et.prototype.getName=function(){return this.name},et.prototype.addLink=function(t){this._valueBeforeLink=this.value,this.links.push(t),CABLES.UI&&this.parent.checkLinkTimeWarnings&&this.parent.checkLinkTimeWarnings(),this.onLinkChanged&&this.onLinkChanged(),this.emitEvent("onLinkChanged"),this.parent.emitEvent("onLinkChanged")},et.prototype.getLinkTo=function(t){for(const e in this.links)if(this.links[e].portIn==t||this.links[e].portOut==t)return this.links[e]},et.prototype.removeLinkTo=function(t){for(const e in this.links)if(this.links[e].portIn==t||this.links[e].portOut==t)return this.links[e].remove(),CABLES.UI&&this.parent.checkLinkTimeWarnings&&this.parent.checkLinkTimeWarnings(),this.onLinkChanged&&this.onLinkChanged(),void this.emitEvent("onLinkChanged")},et.prototype.isLinkedTo=function(t){for(const e in this.links)if(this.links[e].portIn==t||this.links[e].portOut==t)return!0;return!1},et.prototype._activity=function(){this.activityCounter++},et.prototype.trigger=function(){if(this._activity(),0===this.links.length)return;if(!this.parent.enabled)return;let t=null;try{for(let e=0;e{this.set(null),this.set(this._variableIn.getValue())}):this._varChangeListenerId=this._variableIn.on("change",this.set.bind(this)),this.set(this._variableIn.getValue())):this._log.warn("PORT VAR NOT FOUND!!!",t),this._useVariableName=t,e.useVariable=!0,e.variableName=this._useVariableName):(e.variableName=this._useVariableName=null,e.useVariable=!1),this.setUiAttribs(e),this.parent.patch.emitEvent("portSetVariable",this.parent,this,t)},et.prototype._handleNoTriggerOpAnimUpdates=function(t){let e=!1;for(let t=0;t{this.updateAnim()}):this.parent.patch.removeEventListener(this._notriggerAnimUpdate))},et.prototype.setAnimated=function(t){this._animated!=t&&(this._animated=t,this._animated&&!this.anim&&(this.anim=new $,this.anim.addEventListener("onChange",()=>{this.parent.patch.emitEvent("portAnimUpdated",this.parent,this,this.anim)})),this._onAnimToggle()),this._handleNoTriggerOpAnimUpdates(t),this.setUiAttribs({isAnimated:this._animated})},et.prototype.toggleAnim=function(){this._animated=!this._animated,this._animated&&!this.anim&&(this.anim=new $,this.anim.addEventListener("onChange",()=>{this.parent.patch.emitEvent("portAnimUpdated",this.parent,this,this.anim)})),this.setAnimated(this._animated),this._onAnimToggle(),this.setUiAttribs({isAnimated:this._animated})},et.prototype.getType=function(){return this.type},et.prototype.isLinked=function(){return this.links.length>0||this._animated||null!=this._useVariableName},et.prototype.isBoundToVar=function(){return null!=this._useVariableName},et.prototype.isAnimated=function(){return this._animated},et.prototype.isHidden=function(){return this.uiAttribs.hidePort},et.prototype._onTriggered=function(t){this._activity(),this.parent.updateAnims(),this.parent.enabled&&this.onTriggered&&this.onTriggered(t)},et.prototype._onSetProfiling=function(t){this.parent.patch.profiler.add("port",this),this.setValue(t),this.parent.patch.profiler.add("port",null)},et.prototype._onTriggeredProfiling=function(){this.parent.enabled&&this.onTriggered&&(this.parent.patch.profiler.add("port",this),this.onTriggered(),this.parent.patch.profiler.add("port",null))},et.prototype.onValueChange=function(t){this.onChange=t},et.prototype.getUiActiveState=function(){return this._uiActiveState},et.prototype.setUiActiveState=function(t){this._uiActiveState=t,this.onUiActiveStateChange&&this.onUiActiveStateChange()},et.prototype.hidePort=function(){this._log.warn("op.hideport() is deprecated, do not use it!")},et.portTypeNumberToString=function(t){return t==l.OP_PORT_TYPE_VALUE?"value":t==l.OP_PORT_TYPE_FUNCTION?"function":t==l.OP_PORT_TYPE_OBJECT?"object":t==l.OP_PORT_TYPE_ARRAY?"array":t==l.OP_PORT_TYPE_STRING?"string":t==l.OP_PORT_TYPE_DYNAMIC?"dynamic":"unknown"};class it extends et{constructor(t,e,i,s,r){super(t,e,i,s),this.indexPort=r,this.indexPort.set=t=>{const e=s.values;if(!e)return;let i=Math.floor(t);i=Math.min(i,e.length-1),i=Math.max(i,0),this.indexPort.setValue(i),this.set(e[i]),this.parent.patch.isEditorMode()&&window.gui&&gui.patchView.isCurrentOp(this.parent)&&gui.opParams.show(this.parent)}}setUiAttribs(t){const e=t.hidePort;t.hidePort=!0,super.setUiAttribs(t),void 0!==e&&this.indexPort.setUiAttribs({hidePort:e})}}class st extends it{setUiAttribs(t){if(this.indexPort.isLinked())for(const e in t)"greyout"!=e||t[e]||(t[e]="true");super.setUiAttribs(t)}}var rt=class{constructor(t,e,i,s,r,n,o,h,l,c){if(this._log=new a("cg_uniform"),this._type=e,this._name=i,this._shader=t,this._value=1e-5,this._oldValue=null,this._port=null,this._structName=l,this._structUniformName=h,this._propertyName=c,this._shader._addUniform(this),this.needsUpdate=!0,this.shaderType=null,this.comment=null,"f"==e)this.set=this.setValue=this.setValueF.bind(this),this.updateValue=this.updateValueF.bind(this);else if("f[]"==e)this.set=this.setValue=this.setValueArrayF.bind(this),this.updateValue=this.updateValueArrayF.bind(this);else if("2f[]"==e)this.set=this.setValue=this.setValueArray2F.bind(this),this.updateValue=this.updateValueArray2F.bind(this);else if("3f[]"==e)this.set=this.setValue=this.setValueArray3F.bind(this),this.updateValue=this.updateValueArray3F.bind(this);else if("4f[]"==e)this.set=this.setValue=this.setValueArray4F.bind(this),this.updateValue=this.updateValueArray4F.bind(this);else if("i"==e)this.set=this.setValue=this.setValueI.bind(this),this.updateValue=this.updateValueI.bind(this);else if("2i"==e)this.set=this.setValue=this.setValue2I.bind(this),this.updateValue=this.updateValue2I.bind(this);else if("3i"==e)this.set=this.setValue=this.setValue3I.bind(this),this.updateValue=this.updateValue3I.bind(this);else if("4i"==e)this.set=this.setValue=this.setValue4I.bind(this),this.updateValue=this.updateValue4I.bind(this);else if("b"==e)this.set=this.setValue=this.setValueBool.bind(this),this.updateValue=this.updateValueBool.bind(this);else if("4f"==e)this.set=this.setValue=this.setValue4F.bind(this),this.updateValue=this.updateValue4F.bind(this);else if("3f"==e)this.set=this.setValue=this.setValue3F.bind(this),this.updateValue=this.updateValue3F.bind(this);else if("2f"==e)this.set=this.setValue=this.setValue2F.bind(this),this.updateValue=this.updateValue2F.bind(this);else if("t"==e)this.set=this.setValue=this.setValueT.bind(this),this.updateValue=this.updateValueT.bind(this);else if("tc"==e)this.set=this.setValue=this.setValueT.bind(this),this.updateValue=this.updateValueT.bind(this);else if("t[]"==e)this.set=this.setValue=this.setValueArrayT.bind(this),this.updateValue=this.updateValueArrayT.bind(this);else{if("m4"!=e&&"m4[]"!=e)throw new Error("Unknown uniform type");this.set=this.setValue=this.setValueM4.bind(this),this.updateValue=this.updateValueM4.bind(this)}"object"==typeof s&&s instanceof et?(this._port=s,this._value=this._port.get(),r&&n&&o?(r instanceof et&&n instanceof et&&o instanceof et||this._log.error("[cgl_uniform] mixed port/value parameter for vec4 ",this._name),this._value=[0,0,0,0],this._port2=r,this._port3=n,this._port4=o,this._port.on("change",this.updateFromPort4f.bind(this)),this._port2.on("change",this.updateFromPort4f.bind(this)),this._port3.on("change",this.updateFromPort4f.bind(this)),this._port4.on("change",this.updateFromPort4f.bind(this)),this.updateFromPort4f()):r&&n?(r instanceof et&&n instanceof et||this._log.error("[cgl_uniform] mixed port/value parameter for vec4 ",this._name),this._value=[0,0,0],this._port2=r,this._port3=n,this._port.on("change",this.updateFromPort3f.bind(this)),this._port2.on("change",this.updateFromPort3f.bind(this)),this._port3.on("change",this.updateFromPort3f.bind(this)),this.updateFromPort3f()):r?(r instanceof et||this._log.error("[cgl_uniform] mixed port/value parameter for vec4 ",this._name),this._value=[0,0],this._port2=r,this._port.on("change",this.updateFromPort2f.bind(this)),this._port2.on("change",this.updateFromPort2f.bind(this)),this.updateFromPort2f()):this._port.on("change",this.updateFromPort.bind(this))):this._value=s,this.setValue(this._value),this.needsUpdate=!0}getType(){return this._type}getName(){return this._name}getValue(){return this._value}getShaderType(){return this.shaderType}isStructMember(){return!!this._structName}updateFromPort4f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this._value[2]=this._port3.get(),this._value[3]=this._port4.get(),this.setValue(this._value)}updateFromPort3f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this._value[2]=this._port3.get(),this.setValue(this._value)}updateFromPort2f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this.setValue(this._value)}updateFromPort(){this.setValue(this._port.get())}};class nt extends rt{constructor(t,e,i,s,r,n,o,a,h,l){super(t,e,i,s,r,n,o,a,h,l),this._loc=-1,this._cgl=t._cgl}copy(t){const e=new nt(t,this._type,this._name,this._value,this._port2,this._port3,this._port4,this._structUniformName,this._structName,this._propertyName);return e.shaderType=this.shaderType,e}getGlslTypeString(){return nt.glslTypeString(this._type)}_isValidLoc(){return-1!=this._loc}resetLoc(){this._loc=-1,this.needsUpdate=!0}bindTextures(){}getLoc(){return this._loc}updateFromPort4f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this._value[2]=this._port3.get(),this._value[3]=this._port4.get(),this.setValue(this._value)}updateFromPort3f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this._value[2]=this._port3.get(),this.setValue(this._value)}updateFromPort2f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this.setValue(this._value)}updateFromPort(){this.setValue(this._port.get())}updateValueF(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._shader.getCgl().gl.uniform1f(this._loc,this._value),this._cgl.profileData.profileUniformCount++}setValueF(t){t!=this._value&&(this.needsUpdate=!0,this._value=t)}updateValueI(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._shader.getCgl().gl.uniform1i(this._loc,this._value),this._cgl.profileData.profileUniformCount++}updateValue2I(){this._value&&(this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._shader.getCgl().gl.uniform2i(this._loc,this._value[0],this._value[1]),this.needsUpdate=!1,this._cgl.profileData.profileUniformCount++)}updateValue3I(){this._value&&(this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._shader.getCgl().gl.uniform3i(this._loc,this._value[0],this._value[1],this._value[2]),this.needsUpdate=!1,this._cgl.profileData.profileUniformCount++)}updateValue4I(){this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._shader.getCgl().gl.uniform4i(this._loc,this._value[0],this._value[1],this._value[2],this._value[3]),this._cgl.profileData.profileUniformCount++}setValueI(t){t!=this._value&&(this.needsUpdate=!0,this._value=t)}setValue2I(t){t&&(this._oldValue?t[0]==this._oldValue[0]&&t[1]==this._oldValue[1]||(this._oldValue[0]=t[0],this._oldValue[1]=t[1],this.needsUpdate=!0):(this._oldValue=[t[0]-1,1],this.needsUpdate=!0),this._value=t)}setValue3I(t){t&&(this._oldValue?t[0]==this._oldValue[0]&&t[1]==this._oldValue[1]&&t[2]==this._oldValue[2]||(this._oldValue[0]=t[0],this._oldValue[1]=t[1],this._oldValue[2]=t[2],this.needsUpdate=!0):(this._oldValue=[t[0]-1,1,2],this.needsUpdate=!0),this._value=t)}setValue4I(t){this.needsUpdate=!0,this._value=t||vec4.create()}updateValueBool(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._shader.getCgl().gl.uniform1i(this._loc,this._value?1:0),this._cgl.profileData.profileUniformCount++}setValueBool(t){t!=this._value&&(this.needsUpdate=!0,this._value=t)}setValueArray4F(t){this.needsUpdate=!0,this._value=t}updateValueArray4F(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._value&&(this._shader.getCgl().gl.uniform4fv(this._loc,this._value),this._cgl.profileData.profileUniformCount++)}setValueArray3F(t){this.needsUpdate=!0,this._value=t}updateValueArray3F(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._value&&(this._shader.getCgl().gl.uniform3fv(this._loc,this._value),this._cgl.profileData.profileUniformCount++)}setValueArray2F(t){this.needsUpdate=!0,this._value=t}updateValueArray2F(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._value&&(this._shader.getCgl().gl.uniform2fv(this._loc,this._value),this._cgl.profileData.profileUniformCount++)}setValueArrayF(t){this.needsUpdate=!0,this._value=t}updateValueArrayF(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._value&&(this._shader.getCgl().gl.uniform1fv(this._loc,this._value),this._cgl.profileData.profileUniformCount++)}setValueArrayT(t){this.needsUpdate=!0,this._value=t}updateValue3F(){this._value&&(this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._shader.getCgl().gl.uniform3f(this._loc,this._value[0],this._value[1],this._value[2]),this.needsUpdate=!1,this._cgl.profileData.profileUniformCount++)}setValue3F(t){t&&(this._oldValue?t[0]==this._oldValue[0]&&t[1]==this._oldValue[1]&&t[2]==this._oldValue[2]||(this._oldValue[0]=t[0],this._oldValue[1]=t[1],this._oldValue[2]=t[2],this.needsUpdate=!0):(this._oldValue=[t[0]-1,1,2],this.needsUpdate=!0),this._value=t)}updateValue2F(){this._value&&(this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._shader.getCgl().gl.uniform2f(this._loc,this._value[0],this._value[1]),this.needsUpdate=!1,this._cgl.profileData.profileUniformCount++)}setValue2F(t){t&&(this._oldValue?t[0]==this._oldValue[0]&&t[1]==this._oldValue[1]||(this._oldValue[0]=t[0],this._oldValue[1]=t[1],this.needsUpdate=!0):(this._oldValue=[t[0]-1,1],this.needsUpdate=!0),this._value=t)}updateValue4F(){this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._value||(this._log.warn("no value for uniform",this._name,this),this._value=[0,0,0,0]),this.needsUpdate=!1,this._shader.getCgl().gl.uniform4f(this._loc,this._value[0],this._value[1],this._value[2],this._value[3]),this._cgl.profileData.profileUniformCount++}setValue4F(t){"number"==typeof this.value&&(this.value=vec4.create()),t&&(this._oldValue?t[0]==this._oldValue[0]&&t[1]==this._oldValue[1]&&t[2]==this._oldValue[2]&&t[3]==this._oldValue[3]||(this._oldValue[0]=t[0],this._oldValue[1]=t[1],this._oldValue[2]=t[2],this.needsUpdate=!0):(this._oldValue=[t[0]-1,1,2,3],this.needsUpdate=!0),this._value=t)}updateValueM4(){if(this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),!this._value||this._value.length%16!=0)return console.log("this.name",this._name,this._value);this._shader.getCgl().gl.uniformMatrix4fv(this._loc,!1,this._value),this._cgl.profileData.profileUniformCount++}setValueM4(t){this.needsUpdate=!0,this._value=t||mat4.create()}updateValueArrayT(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._value&&(this._shader.getCgl().gl.uniform1iv(this._loc,this._value),this._cgl.profileData.profileUniformCount++)}updateValueT(){this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._cgl.profileData.profileUniformCount++,this._shader.getCgl().gl.uniform1i(this._loc,this._value),this.needsUpdate=!1}setValueT(t){this.needsUpdate=!0,this._value=t}}nt.glslTypeString=t=>"f"==t?"float":"b"==t?"bool":"i"==t?"int":"2i"==t?"ivec2":"2f"==t?"vec2":"3f"==t?"vec3":"4f"==t?"vec4":"m4"==t?"mat4":"t"==t?"sampler2D":"tc"==t?"samplerCube":"3f[]"==t||"m4[]"==t||"f[]"==t?null:void(void 0)._log.warn("[CGL UNIFORM] unknown glsl type string ",t);const ot=180/Math.PI,at={MATH:{DEG2RAD:Math.PI/180,RAD2DEG:ot},SHADER:{SHADERVAR_VERTEX_POSITION:"vPosition",SHADERVAR_VERTEX_NUMBER:"attrVertIndex",SHADERVAR_VERTEX_NORMAL:"attrVertNormal",SHADERVAR_VERTEX_TEXCOORD:"attrTexCoord",SHADERVAR_INSTANCE_MMATRIX:"instMat",SHADERVAR_VERTEX_COLOR:"attrVertColor",SHADERVAR_UNI_PROJMAT:"projMatrix",SHADERVAR_UNI_VIEWMAT:"viewMatrix",SHADERVAR_UNI_MODELMAT:"modelMatrix",SHADERVAR_UNI_NORMALMAT:"normalMatrix",SHADERVAR_UNI_INVVIEWMAT:"inverseViewMatrix",SHADERVAR_UNI_INVPROJMAT:"invProjMatrix",SHADERVAR_UNI_VIEWPOS:"camPos"},BLEND_MODES:{BLEND_NONE:0,BLEND_NORMAL:1,BLEND_ADD:2,BLEND_SUB:3,BLEND_MUL:4}};const ht={lastMesh:null},lt=function(t,e,i){this._cgl=t,this._log=new a("cgl_mesh"),this._bufVertexAttrib=null,this._bufVerticesIndizes=this._cgl.gl.createBuffer(),this._indexType=this._cgl.gl.UNSIGNED_SHORT,this._attributes=[],this._attribLocs={},this._geom=null,this._lastShader=null,this._numInstances=0,this._glPrimitive=i,this._preWireframeGeom=null,this.addVertexNumbers=!1,this.feedBackAttributes=[],this.setGeom(e),this._feedBacks=[],this._feedBacksChanged=!1,this._transformFeedBackLoc=-1,this._lastAttrUpdate=0,this._name="unknown",this._cgl.profileData.addHeavyEvent("mesh constructed",this._name),this._queryExt=null,Object.defineProperty(this,"numInstances",{get(){return this._numInstances},set(t){this.setNumInstances(t)}})};var ct;lt.prototype.updateVertices=function(t){this.setAttribute(at.SHADER.SHADERVAR_VERTEX_POSITION,t.vertices,3),this._numVerts=t.vertices.length/3},lt.prototype.setAttributePointer=function(t,e,i,s){for(let r=0;r=e.length-1&&this._log.log(this._cgl.canvas.id+" "+t.name+" buffersubdata out of bounds ?",e.length,s,i,t),1==this._cgl.glVersion?this._cgl.gl.bufferSubData(this._cgl.gl.ARRAY_BUFFER,0,e):this._cgl.gl.bufferSubData(this._cgl.gl.ARRAY_BUFFER,4*i,e,i,s-i))},lt.prototype._resizeAttr=function(t,e){e.buffer&&this._cgl.gl.deleteBuffer(e.buffer),e.buffer=this._cgl.gl.createBuffer(),this._cgl.gl.bindBuffer(this._cgl.gl.ARRAY_BUFFER,e.buffer),this._bufferArray(t,e),e.numItems=t.length/e.itemSize},lt.prototype._bufferArray=function(t,e){let i=null;t&&(this._cgl.debugOneFrame&&console.log("_bufferArray",t.length,e.name),t instanceof Float32Array?i=t:e&&i&&i.length==t.length?i.set(t):(i=new Float32Array(t),this._cgl.debugOneFrame&&console.log("_bufferArray create new float32array",t.length,e.name),this._cgl.profileData.profileNonTypedAttrib++,this._cgl.profileData.profileNonTypedAttribNames="("+this._name+":"+e.name+")"),e.arrayLength=i.length,this._cgl.gl.bufferData(this._cgl.gl.ARRAY_BUFFER,i,this._cgl.gl.DYNAMIC_DRAW))},lt.prototype.addAttribute=lt.prototype.updateAttribute=lt.prototype.setAttribute=function(t,e,i,s){if(!e)throw this._log.error("mesh addAttribute - no array given! "+t),new Error;let r=null,n=!1,o=0;const a=e.length/i;for(this._cgl.profileData.profileMeshAttributes+=a||0,"function"==typeof s&&(r=s),"object"==typeof s&&(s.cb&&(r=s.cb),s.instanced&&(n=s.instanced)),t==at.SHADER.SHADERVAR_INSTANCE_MMATRIX&&(n=!0),o=0;o0)this.setAttribute(at.SHADER.SHADERVAR_VERTEX_TEXCOORD,t.texCoords,2);else{const e=new Float32Array(Math.round(t.vertices.length/3*2));this.setAttribute(at.SHADER.SHADERVAR_VERTEX_TEXCOORD,e,2)}},lt.prototype.updateNormals=function(t){if(t.vertexNormals&&t.vertexNormals.length>0)this.setAttribute(at.SHADER.SHADERVAR_VERTEX_NORMAL,t.vertexNormals,3);else{const e=new Float32Array(Math.round(t.vertices.length));this.setAttribute(at.SHADER.SHADERVAR_VERTEX_NORMAL,e,3)}},lt.prototype._setVertexNumbers=function(t){if(!this._verticesNumbers||this._verticesNumbers.length!=this._numVerts||t){if(t)this._verticesNumbers=t;else{this._verticesNumbers=new Float32Array(this._numVerts);for(let t=0;t{i.uniformNumVertices||(i.uniformNumVertices=new nt(i,"f","numVertices",this._numVerts)),i.uniformNumVertices.setValue(this._numVerts)})}},lt.prototype.setVertexIndices=function(t){if(this._bufVerticesIndizes)if(t.length>0){for(let e=0;e=this._numVerts)return void this._log.warn("invalid index in "+this._name);this._cgl.gl.bindBuffer(this._cgl.gl.ELEMENT_ARRAY_BUFFER,this._bufVerticesIndizes),t instanceof Float32Array&&this._log.warn("vertIndices float32Array: "+this._name),t instanceof Uint32Array?(this.vertIndicesTyped=t,this._indexType=this._cgl.gl.UNSIGNED_INT):t instanceof Uint16Array?this.vertIndicesTyped=t:this.vertIndicesTyped=new Uint16Array(t),this._cgl.gl.bufferData(this._cgl.gl.ELEMENT_ARRAY_BUFFER,this.vertIndicesTyped,this._cgl.gl.DYNAMIC_DRAW),this._bufVerticesIndizes.itemSize=1,this._bufVerticesIndizes.numItems=t.length}else this._bufVerticesIndizes.numItems=0;else this._log.warn("no bufVerticesIndizes: "+this._name)},lt.prototype.setGeom=function(t,e){this._geom=t,null!=t.glPrimitive&&(this._glPrimitive=t.glPrimitive),this._geom&&this._geom.name&&(this._name="mesh "+this._geom.name),ht.lastMesh=null,this._cgl.profileData.profileMeshSetGeom++,this._disposeAttributes(),this.updateVertices(this._geom),this.setVertexIndices(this._geom.verticesIndices),this.addVertexNumbers&&this._setVertexNumbers();const i=this._geom.getAttributes(),s={texCoords:at.SHADER.SHADERVAR_VERTEX_TEXCOORD,vertexNormals:at.SHADER.SHADERVAR_VERTEX_NORMAL,vertexColors:at.SHADER.SHADERVAR_VERTEX_COLOR,tangents:"attrTangent",biTangents:"attrBiTangent"};for(const t in i)i[t].data&&i[t].data.length&&this.setAttribute(s[t]||t,i[t].data,i[t].itemSize);e&&(this._geom=null)},lt.prototype._preBind=function(t){for(let e=0;ethis._lastAttrUpdate||e.length!=this._attributes.length){this._lastAttrUpdate=t.lastCompile;for(let t=0;t=0&&this._cgl.gl.disableVertexAttribArray(e[t])):(this._cgl.gl.vertexAttribDivisor(e[t],0),this._cgl.gl.vertexAttribDivisor(e[t]+1,0),this._cgl.gl.vertexAttribDivisor(e[t]+2,0),this._cgl.gl.vertexAttribDivisor(e[t]+3,0),this._cgl.gl.disableVertexAttribArray(e[t]+1),this._cgl.gl.disableVertexAttribArray(e[t]+2),this._cgl.gl.disableVertexAttribArray(e[t]+3))),-1!=e[t]&&this._cgl.gl.disableVertexAttribArray(e[t])},lt.prototype.meshChanged=function(){return this._cgl.lastMesh&&this._cgl.lastMesh!=this},lt.prototype.printDebug=function(t){console.log("--attributes");for(let t=0;t0},ct.prototype.removeFeedbacks=function(t){this._feedbacks&&(this._feedbacks.length=0,this._feedBacksChanged=!0)},ct.prototype.setAttributeFeedback=function(){},ct.prototype.setFeedback=function(t,e,i){let s={nameOut:e},r=!1;this.unBindFeedbacks();for(let t=0;t=e)return t.setUiError("texeffect",null),!0;e&&t.patch.cgl.currentTextureEffect.imgCompVer="+e+' Downgrade to previous version",1)}return!!t.patch.cgl.currentTextureEffect||(t.patch.cgl.currentTextureEffect||t.uiAttribs.uierrors&&0!=t.uiAttribs.uierrors.length?!!t.patch.cgl.currentTextureEffect:(t.setUiError("texeffect",'This op must be a child of an ImageCompose op! More infos here. ',1),!1))},gt.getBlendCode=function(t){let e="".endl()+"vec3 _blend(vec3 base,vec3 blend)".endl()+"{".endl()+" vec3 colNew=blend;".endl()+" #ifdef BM_MULTIPLY".endl()+" colNew=base*blend;".endl()+" #endif".endl()+" #ifdef BM_MULTIPLY_INV".endl()+" colNew=base* vec3(1.0)-blend;".endl()+" #endif".endl()+" #ifdef BM_AVERAGE".endl()+" colNew=((base + blend) / 2.0);".endl()+" #endif".endl()+" #ifdef BM_ADD".endl()+" colNew=min(base + blend, vec3(1.0));".endl()+" #endif".endl()+" #ifdef BM_SUBTRACT_ONE".endl()+" colNew=max(base + blend - vec3(1.0), vec3(0.0));".endl()+" #endif".endl()+" #ifdef BM_SUBTRACT".endl()+" colNew=base - blend;".endl()+" #endif".endl()+" #ifdef BM_DIFFERENCE".endl()+" colNew=abs(base - blend);".endl()+" #endif".endl()+" #ifdef BM_NEGATION".endl()+" colNew=(vec3(1.0) - abs(vec3(1.0) - base - blend));".endl()+" #endif".endl()+" #ifdef BM_EXCLUSION".endl()+" colNew=(base + blend - 2.0 * base * blend);".endl()+" #endif".endl()+" #ifdef BM_LIGHTEN".endl()+" colNew=max(blend, base);".endl()+" #endif".endl()+" #ifdef BM_DARKEN".endl()+" colNew=min(blend, base);".endl()+" #endif".endl()+" #ifdef BM_OVERLAY".endl()+" #define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))".endl()+" colNew=vec3(BlendOverlayf(base.r, blend.r),BlendOverlayf(base.g, blend.g),BlendOverlayf(base.b, blend.b));".endl()+" #endif".endl()+" #ifdef BM_SCREEN".endl()+" #define BlendScreenf(base, blend) (1.0 - ((1.0 - base) * (1.0 - blend)))".endl()+" colNew=vec3(BlendScreenf(base.r, blend.r),BlendScreenf(base.g, blend.g),BlendScreenf(base.b, blend.b));".endl()+" #endif".endl()+" #ifdef BM_SOFTLIGHT".endl()+" #define BlendSoftLightf(base, blend) ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend)))".endl()+" colNew=vec3(BlendSoftLightf(base.r, blend.r),BlendSoftLightf(base.g, blend.g),BlendSoftLightf(base.b, blend.b));".endl()+" #endif".endl()+" #ifdef BM_HARDLIGHT".endl()+" #define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))".endl()+" colNew=vec3(BlendOverlayf(base.r, blend.r),BlendOverlayf(base.g, blend.g),BlendOverlayf(base.b, blend.b));".endl()+" #endif".endl()+" #ifdef BM_COLORDODGE".endl()+" #define BlendColorDodgef(base, blend) ((blend == 1.0) ? blend : min(base / (1.0 - blend), 1.0))".endl()+" colNew=vec3(BlendColorDodgef(base.r, blend.r),BlendColorDodgef(base.g, blend.g),BlendColorDodgef(base.b, blend.b));".endl()+" #endif".endl()+" #ifdef BM_COLORBURN".endl()+" #define BlendColorBurnf(base, blend) ((blend == 0.0) ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0))".endl()+" colNew=vec3(BlendColorBurnf(base.r, blend.r),BlendColorBurnf(base.g, blend.g),BlendColorBurnf(base.b, blend.b));".endl()+" #endif".endl()+" return colNew;".endl()+"}".endl();return t||(e+="vec4 cgl_blend(vec4 oldColor,vec4 newColor,float amount)".endl()+"{".endl()+"vec4 col=vec4( _blend(oldColor.rgb,newColor.rgb) ,1.0);".endl()+"col=vec4( mix( col.rgb, oldColor.rgb ,1.0-oldColor.a*amount),1.0);".endl()+"return col;".endl()+"}".endl()),t>=3&&(e+="vec4 cgl_blendPixel(vec4 base,vec4 col,float amount)".endl()+"{".endl()+"vec3 colNew=_blend(base.rgb,col.rgb);".endl()+"float newA=clamp(base.a+(col.a*amount),0.,1.);".endl()+"#ifdef BM_ALPHAMASKED".endl()+"newA=base.a;".endl()+"#endif".endl()+"return vec4(".endl()+"mix(colNew,base.rgb,1.0-(amount*col.a)),".endl()+"newA);".endl()+"}".endl()),e},gt.onChangeBlendSelect=function(t,e,i){t.toggleDefine("BM_NORMAL","normal"==e),t.toggleDefine("BM_MULTIPLY","multiply"==e),t.toggleDefine("BM_MULTIPLY_INV","multiply invert"==e),t.toggleDefine("BM_AVERAGE","average"==e),t.toggleDefine("BM_ADD","add"==e),t.toggleDefine("BM_SUBTRACT_ONE","subtract one"==e),t.toggleDefine("BM_SUBTRACT","subtract"==e),t.toggleDefine("BM_DIFFERENCE","difference"==e),t.toggleDefine("BM_NEGATION","negation"==e),t.toggleDefine("BM_EXCLUSION","exclusion"==e),t.toggleDefine("BM_LIGHTEN","lighten"==e),t.toggleDefine("BM_DARKEN","darken"==e),t.toggleDefine("BM_OVERLAY","overlay"==e),t.toggleDefine("BM_SCREEN","screen"==e),t.toggleDefine("BM_SOFTLIGHT","softlight"==e),t.toggleDefine("BM_HARDLIGHT","hardlight"==e),t.toggleDefine("BM_COLORDODGE","color dodge"==e),t.toggleDefine("BM_COLORBURN","color burn"==e),t.toggleDefine("BM_ALPHAMASKED",i)},gt.AddBlendSelect=function(t,e,i){return t.inValueSelect(e||"Blend Mode",["normal","lighten","darken","multiply","multiply invert","average","add","subtract","difference","negation","exclusion","overlay","screen","color dodge","color burn","softlight","hardlight","subtract one"],i||"normal")},gt.AddBlendAlphaMask=function(t,e,i){return t.inSwitch(e||"Alpha Mask",["Off","On"],i||"Off")},gt.setupBlending=function(t,e,i,s,r){const n=()=>{let s=!1;r&&(s="On"==r.get()),gt.onChangeBlendSelect(e,i.get(),s);let n=i.get();"normal"==n?n=null:"multiply"==n?n="mul":"multiply invert"==n?n="mulinv":"lighten"==n?n="light":"darken"==n?n="darken":"average"==n?n="avg":"subtract one"==n?n="sub one":"subtract"==n?n="sub":"difference"==n?n="diff":"negation"==n||"negation"==n||"negation"==n?n="neg":"exclusion"==n?n="exc":"overlay"==n?n="ovl":"color dodge"==n?n="dodge":"color burn"==n?n="burn":"softlight"==n?n="soft":"hardlight"==n&&(n="hard"),t.setUiAttrib({extendTitle:n})};t.setPortGroup("Blending",[i,s,r]);let o=!1;i.onChange=n,r&&(r.onChange=n,o="On"==r.get()),gt.onChangeBlendSelect(e,i.get(),o)};const pt={"CGL.BLENDMODES":function(){this.name="blendmodes",this.srcHeadFrag=gt.getBlendCode()},"CGL.BLENDMODES3":function(){this.name="blendmodes3",this.srcHeadFrag=gt.getBlendCode(3)},"CGL.LUMINANCE":function(){this.name="luminance",this.srcHeadFrag="".endl()+"float cgl_luminance(vec3 c)".endl()+"{".endl()+" return dot(vec3(0.2126,0.7152,0.0722),c);".endl()+"}".endl()},"CGL.RANDOM_OLD":function(){this.name="randomNumber",this.srcHeadFrag="".endl()+"float cgl_random(vec2 co)".endl()+"{".endl()+" return fract(sin(dot(co.xy ,vec2(12.9898,4.1414))) * 432758.5453);".endl()+"}".endl()+"vec3 cgl_random3(vec2 co)".endl()+"{".endl()+" return vec3( cgl_random(co),cgl_random(co+0.5711),cgl_random(co+1.5711));".endl()+"}"},"CGL.RANDOM_LOW":function(){this.name="randomNumber",this.srcHeadFrag="".endl()+"float cgl_random(vec2 co)".endl()+"{".endl()+" return fract(sin(dot(co.xy ,vec2(12.9898,4.1414))) * 358.5453);".endl()+"}".endl()+"vec3 cgl_random3(vec2 co)".endl()+"{".endl()+" return vec3( cgl_random(co),cgl_random(co+0.5711),cgl_random(co+1.5711));".endl()+"}"},"CGL.RANDOM_TEX":function(){this.name="randomNumbertex",this.srcHeadFrag="".endl()+"UNI sampler2D CGLRNDTEX;".endl()+"float cgl_random(vec2 co)".endl()+"{".endl()+" return texture(CGLRNDTEX,co*5711.0).r;".endl()+"}".endl()+"vec3 cgl_random3(vec2 co)".endl()+"{".endl()+" return texture(CGLRNDTEX,co*5711.0).rgb;".endl()+"}",this.initUniforms=function(t){return[new nt(t,"t","CGLRNDTEX",7)]},this.onBind=function(t,e){B.getRandomTexture(t),t.setTexture(7,B.getRandomTexture(t).tex)}}},_t=function(){return window.performance.now()},dt=function(){return _t()},ft=function(){CABLES.EventTarget.apply(this),this._timeStart=_t(),this._timeOffset=0,this._currentTime=0,this._lastTime=0,this._paused=!0,this._delay=0,this.overwriteTime=-1};ft.prototype._getTime=function(){return this._lastTime=(_t()-this._timeStart)/1e3,this._lastTime+this._timeOffset},ft.prototype.setDelay=function(t){this._delay=t,this.emitEvent("timeChange")},ft.prototype.isPlaying=function(){return!this._paused},ft.prototype.update=function(){if(!this._paused)return this._currentTime=this._getTime(),this._currentTime},ft.prototype.getMillis=function(){return 1e3*this.get()},ft.prototype.get=ft.prototype.getTime=function(){return this.overwriteTime>=0?this.overwriteTime-this._delay:this._currentTime-this._delay},ft.prototype.togglePlay=function(){this._paused?this.play():this.pause()},ft.prototype.setTime=function(t){(isNaN(t)||t<0)&&(t=0),this._timeStart=_t(),this._timeOffset=t,this._currentTime=t,this.emitEvent("timeChange")},ft.prototype.setOffset=function(t){this._currentTime+t<0?(this._timeStart=_t(),this._timeOffset=0,this._currentTime=0):(this._timeOffset+=t,this._currentTime=this._lastTime+this._timeOffset),this.emitEvent("timeChange")},ft.prototype.play=function(){this._timeStart=_t(),this._paused=!1,this.emitEvent("playPause")},ft.prototype.pause=function(){this._timeOffset=this._currentTime,this._paused=!0,this.emitEvent("playPause")};const mt=Math.PI/180,Et=(Math.PI,-1!=window.navigator.userAgent.indexOf("Windows")),Tt=function(t){let e;if(t.wheelDelta)e=t.wheelDelta%120-0==-0?t.wheelDelta/120:t.wheelDelta/30,e*=-1.5,Et&&(e*=2);else{let i=t.deltaY;t.shiftKey&&(i=t.deltaX);const s=i||t.detail;e=-(s%3?10*s:s/3),e*=-3}return e>20&&(e=20),e<-20&&(e=-20),e},At=Tt,bt=Tt,xt={"&":"&","<":"<",">":">",'"':""","'":"'"},vt=/[&<>"']/g,yt=RegExp(vt.source);const It=function(t,e){if(!t)throw new Error("shader constructed without cgl "+e);this._log=new a("cgl_shader"),this._cgl=t,e||this._log.stack("no shader name given"),this._name=e||"unknown",this.glslVersion=0,t.glVersion>1&&(this.glslVersion=300),this.id=x(),this._isValid=!0,this._program=null,this._uniforms=[],this._drawBuffers=[!0],this._defines=[],this._needsRecompile=!0,this._compileReason="initial",this._projMatrixUniform=null,this._mvMatrixUniform=null,this._mMatrixUniform=null,this._vMatrixUniform=null,this._camPosUniform=null,this._normalMatrixUniform=null,this._inverseViewMatrixUniform=null,this._attrVertexPos=-1,this.precision=t.patch.config.glslPrecision||"highp",this._pMatrixState=-1,this._vMatrixState=-1,this._modGroupCount=0,this._feedBackNames=[],this._attributes=[],this.glPrimitive=null,this.offScreenPass=!1,this._extensions=[],this.srcVert=this.getDefaultVertexShader(),this.srcFrag=this.getDefaultFragmentShader(),this.lastCompile=0,this._moduleNames=[],this._modules=[],this._moduleNumId=0,this._libs=[],this._structNames=[],this._structUniformNames=[],this._textureStackUni=[],this._textureStackTex=[],this._textureStackType=[],this._textureStackTexCgl=[],this._tempNormalMatrix=mat4.create(),this._tempCamPosMatrix=mat4.create(),this._tempInverseViewMatrix=mat4.create(),this._tempInverseProjMatrix=mat4.create(),this.setModules(["MODULE_VERTEX_POSITION","MODULE_COLOR","MODULE_BEGIN_FRAG"])};It.prototype.isValid=function(){return this._isValid},It.prototype.getCgl=function(){return this._cgl},It.prototype.getName=function(){return this._name},It.prototype.enableExtension=function(t){this.setWhyCompile("enable extension "+t),this._needsRecompile=!0,this._extensions.push(t)},It.prototype.getAttrVertexPos=function(){return this._attrVertexPos},It.prototype.hasTextureUniforms=function(){for(let t=0;t-1){const i=new pt[e];t=t.replace("{{"+e+"}}",i.srcHeadFrag),this._libs.push(i),i.initUniforms&&i.initUniforms(this)}return t},It.prototype.createStructUniforms=function(){let t="",e="";this._structNames=[],this._injectedStringsFrag={},this._injectedStringsVert={},this._structUniformNamesIndicesFrag=[],this._structUniformNamesIndicesVert=[];for(let i=0;it._name),e=[];for(let i=0;i-1&&e.push(i)}for(let t=this._uniforms.length-1;t>=0;t-=1)e.indexOf(t)>-1?this._uniforms.splice(t,1):this._uniforms[t].resetLoc()}this._cgl.printError("uniform resets"),this.hasTextureUniforms()&&(i+="#define HAS_TEXTURES".endl());let r="",n="";this.srcFrag||(this._log.error("[cgl shader] has no fragment source!",this),this.srcVert=this.getDefaultVertexShader(),this.srcFrag=this.getDefaultFragmentShader()),300==this.glslVersion?(r="#version 300 es".endl()+"// ".endl()+"// vertex shader "+this._name.endl()+"// ".endl()+"precision "+this.precision+" float;".endl()+"precision "+this.precision+" sampler2D;".endl()+"".endl()+"#define WEBGL2".endl()+"#define texture2D texture".endl()+"#define UNI uniform".endl()+"#define IN in".endl()+"#define OUT out".endl(),n="#version 300 es".endl()+"// ".endl()+"// fragment shader "+this._name.endl()+"// ".endl()+"precision "+this.precision+" float;".endl()+"precision "+this.precision+" sampler2D;".endl()+"".endl()+"#define WEBGL2".endl()+"#define texture2D texture".endl()+"#define IN in".endl()+"#define UNI uniform".endl()+"{{DRAWBUFFER}}".endl()):(n="".endl()+"// ".endl()+"// fragment shader "+this._name.endl()+"// ".endl()+"#define WEBGL1".endl()+"#define texture texture2D".endl()+"#define outColor gl_FragColor".endl()+"#define IN varying".endl()+"#define UNI uniform".endl(),r="".endl()+"// ".endl()+"// vertex shader "+this._name.endl()+"// ".endl()+"#define WEBGL1".endl()+"#define texture texture2D".endl()+"#define OUT varying".endl()+"#define IN attribute".endl()+"#define UNI uniform".endl());let o="\n// cgl generated".endl(),a="\n// cgl generated".endl();n+="\n// active mods: --------------- ",r+="\n// active mods: --------------- ";let h=!1,l=!1;for(let t=0;t=this._cgl.maxUniformsFrag&&this._log.warn("[cgl_shader] num uniforms frag: "+c+" / "+this._cgl.maxUniformsFrag),u>=this._cgl.maxUniformsVert&&this._log.warn("[cgl_shader] num uniforms vert: "+u+" / "+this._cgl.maxUniformsVert),-1==n.indexOf("precision")&&(n="precision "+this.precision+" float;".endl()+n),-1==r.indexOf("precision")&&(r="precision "+this.precision+" float;".endl()+r),/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)&&(n+="#define MOBILE".endl(),r+="#define MOBILE".endl()),r=e+r+i+s[0]+o+"\n// -- \n"+this.srcVert,n=e+n+i+s[1]+a+"\n// -- \n"+this.srcFrag;let g="",p="";this._modules.sort((function(t,e){return t.group-e.group})),this._modules.sort((function(t,e){return t.priority||0-e.priority||0}));let _=!1;for(let t=0;t-1&&(this._drawBuffers[0]=!0),n.indexOf("outColor1")>-1&&(this._drawBuffers[1]=!0),n.indexOf("outColor2")>-1&&(this._drawBuffers[2]=!0),n.indexOf("outColor3")>-1&&(this._drawBuffers[3]=!0),1==this._drawBuffers.length)d="out vec4 outColor;".endl(),d+="#define gl_FragColor outColor".endl();else{d+="#define MULTI_COLORTARGETS".endl(),d+="vec4 outColor;".endl();let t=0;for(let e=0;e{this.toggleDefine(t,e)},e.changeListener=e.on("change",e.onToggleDefine),e=e.get()),e?this.define(t):this.removeDefine(t)},It.prototype.define=function(t,e){null==e&&(e=""),"object"==typeof e&&(e.removeEventListener("change",e.onDefineChange),e.onDefineChange=e=>{this.define(t,e)},e.on("change",e.onDefineChange),e=e.get());for(let i=0;i0&&this._cgl.gl.transformFeedbackVaryings(t,this._feedBackNames,this._cgl.gl.SEPARATE_ATTRIBS),this._cgl.gl.linkProgram(t),this._cgl.printError("gl.linkprogram"),this._isValid=!0,this._hasErrors=!1,!1!==this._cgl.patch.config.glValidateShader&&(this._cgl.gl.validateProgram(t),this._cgl.gl.getProgramParameter(t,this._cgl.gl.VALIDATE_STATUS)||(console.log("shaderprogram validation failed..."),console.log(this._name+" programinfo: ",this._cgl.gl.getProgramInfoLog(t))),this._cgl.gl.getProgramParameter(t,this._cgl.gl.LINK_STATUS)||(this._hasErrors=!0,this._log.warn(this._cgl.gl.getShaderInfoLog(this.fshader)||"empty shader infolog"),this._log.warn(this._cgl.gl.getShaderInfoLog(this.vshader)||"empty shader infolog"),this._log.error(this._name+" shader linking fail..."),console.log(this._name+" programinfo: ",this._cgl.gl.getProgramInfoLog(t)),console.log("--------------------------------------"),console.log(this),console.log("--------------------------------------"),this._isValid=!1,this._name="errorshader",this.setSource(It.getDefaultVertexShader(),It.getErrorFragmentShader()),this._cgl.printError("shader link err")))},It.prototype.getProgram=function(){return this._program},It.prototype.setFeedbackNames=function(t){this.setWhyCompile("setFeedbackNames"),this._needsRecompile=!0,this._feedBackNames=t},It.prototype.getDefaultVertexShader=It.getDefaultVertexShader=function(){return"{{MODULES_HEAD}}\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\nIN vec3 attrTangent,attrBiTangent;\n\nIN float attrVertIndex;\n\nOUT vec2 texCoord;\nOUT vec3 norm;\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\nvoid main()\n{\n texCoord=attrTexCoord;\n norm=attrVertNormal;\n vec4 pos=vec4(vPosition, 1.0);\n vec3 tangent=attrTangent;\n vec3 bitangent=attrBiTangent;\n mat4 mMatrix=modelMatrix;\n {{MODULE_VERTEX_POSITION}}\n gl_Position = projMatrix * (viewMatrix*mMatrix) * pos;\n}\n"},It.prototype.getDefaultFragmentShader=It.getDefaultFragmentShader=function(t,e,i){return null==t&&(t=.5,e=.5,i=.5),"".endl()+"IN vec2 texCoord;".endl()+"{{MODULES_HEAD}}".endl()+"void main()".endl()+"{".endl()+" vec4 col=vec4("+t+","+e+","+i+",1.0);".endl()+" {{MODULE_COLOR}}".endl()+" outColor = col;".endl()+"}"},It.prototype.addAttribute=function(t){for(let e=0;ethis._cgl.maxTextureUnits&&this._log.warn("[shader._bindTextures] too many textures bound",this._textureStackTex.length+"/"+this._cgl.maxTextureUnits);for(let t=0;t",h+='
    '),h+=(n=i)&&yt.test(n)?n.replace(vt,(function(t){return xt[t]})):n||"",s&&(h+="
    ",h+='
    ')}console.warn(o),o=o.replace(/\n/g,"
    "),h=o+"
    "+h+"

    ",h+="
    ",t.patch.emitEvent("criticalError",{title:"Shader error "+this._name,text:h}),t.patch.isEditorMode()&&console.log("Shader error "+this._name,h),this._name="errorshader",s.setSource(It.getDefaultVertexShader(),It.getErrorFragmentShader())}var n;return r};class St{constructor(t){this._cgl=t,this._lastTime=0,this.pause=!1,this.profileUniformCount=0,this.profileShaderBinds=0,this.profileUniformCount=0,this.profileShaderCompiles=0,this.profileVideosPlaying=0,this.profileMVPMatrixCount=0,this.profileEffectBuffercreate=0,this.profileShaderGetUniform=0,this.profileFrameBuffercreate=0,this.profileMeshSetGeom=0,this.profileTextureNew=0,this.profileGenMipMap=0,this.profileOnAnimFrameOps=0,this.profileMainloopMs=0,this.profileMeshDraw=0,this.profileTextureEffect=0,this.profileTexPreviews=0,this.shaderCompileTime=0,this.profileMeshNumElements=0,this.profileMeshAttributes=0,this.profileSingleMeshAttribute=[],this.heavyEvents=[],this.doProfileGlQuery=!1,this.glQueryData={}}clear(){this.profileSingleMeshAttribute={},this.profileMeshAttributes=0,this.profileUniformCount=0,this.profileShaderGetUniform=0,this.profileShaderCompiles=0,this.profileShaderBinds=0,this.profileTextureResize=0,this.profileFrameBuffercreate=0,this.profileEffectBuffercreate=0,this.profileTextureDelete=0,this.profileMeshSetGeom=0,this.profileVideosPlaying=0,this.profileMVPMatrixCount=0,this.profileNonTypedAttrib=0,this.profileNonTypedAttribNames="",this.profileTextureNew=0,this.profileGenMipMap=0,this.profileFramebuffer=0,this.profileMeshDraw=0,this.profileTextureEffect=0,this.profileTexPreviews=0,this.profileMeshNumElements=0}clearGlQuery(){for(let t in this.glQueryData)(!this.glQueryData[t].lastClear||performance.now()-this.glQueryData[t].lastClear>1e3)&&(this.glQueryData[t].time=this.glQueryData[t]._times/this.glQueryData[t]._numcount,this.glQueryData[t].num=this.glQueryData[t]._numcount,this.glQueryData[t]._times=0,this.glQueryData[t]._numcount=0,this.glQueryData[t].lastClear=performance.now())}addHeavyEvent(t,e,i){const s={event:t,name:e,info:i,date:performance.now()};this.heavyEvents.push(s),this._cgl.emitEvent("heavyEvent",s)}}const Rt={GAPI_WEBGL:0,GAPI_WEBGPU:1,Geometry:W,BoundingBox:z,FpsCounter:class extends Y{constructor(){super(),this._timeStartFrame=0,this._timeStartSecond=0,this._fpsCounter=0,this._msCounter=0,this.stats={ms:0,fps:0}}startFrame(){this._timeStartFrame=CABLES.now()}endFrame(){this._fpsCounter++;const t=CABLES.now()-this._timeStartFrame;this._msCounter+=t,CABLES.now()-this._timeStartSecond>1e3&&this.endSecond()}endSecond(){this.stats.fps=this._fpsCounter,this.stats.ms=Math.round(this._msCounter/this._fpsCounter*100)/100,this.emitEvent("performance",this.stats),this._fpsCounter=0,this._msCounter=0,this._timeStartSecond=CABLES.now()}}},Pt=function(t){Ut.apply(this),this.patch=t,this.gApi=Rt.GAPI_WEBGPU,this._viewport=[0,0,256,256],this._shaderStack=[],this.getViewPort=function(){return[0,0,this.canvasWidth,this.canvasHeight]},this.renderStart=function(t,e,i){this.fpsCounter.startFrame(),this._startMatrixStacks(e,i),this.setViewPort(0,0,this.canvasWidth,this.canvasHeight),this.emitEvent("beginFrame")},this.renderEnd=function(t){this._endMatrixStacks(),this.emitEvent("endFrame"),this.fpsCounter.endFrame()}};Pt.prototype.setViewPort=function(t,e,i,s){this._viewport=[t,e,i,s]},Pt.prototype.getViewPort=function(){return this._viewPort},Pt.prototype.createMesh=function(t,e){return new CGP.Mesh(this,t,e)},Pt.prototype.getShader=function(){return{}},Pt.prototype.pushShader=function(t){this._shaderStack.push(t)},Pt.prototype.popShader=function(){if(0===this._shaderStack.length)throw new Error("Invalid shader stack pop!");this._shaderStack.pop()},Pt.prototype.getShader=function(){return this._shaderStack[this._shaderStack.length-1]},Pt.prototype.pushErrorScope=function(){this.device.pushErrorScope("validation")},Pt.prototype.popErrorScope=function(t,e){this.device.popErrorScope().then(i=>{i&&(this.patch.emitEvent("criticalError",{title:'WebGPU error "'+t+'"',codeText:i.message}),console.warn("[cgp]",t,i.message,i,e),e&&e(i))})};class Ot extends rt{constructor(t,e,i,s,r,n,o,a,h,l){super(t,e,i,s,r,n,o,a,h,l),this._loc=-1,this._cgl=t._cgl}updateValueF(){}setValueF(t){this.needsUpdate=!0,this._value=t}updateValue2F(){}setValue2F(t){this.needsUpdate=!0,this._value=t}updateValue3F(){}setValue3F(t){this.needsUpdate=!0,this._value=t}updateValue4F(){}setValue4F(t){this.needsUpdate=!0,this._value=t}getSizeBytes(){return"f"==this._type||"i"==this._type?4:"2i"==this._type||"2f"==this._type?8:"3f"==this._type?12:"4f"==this._type?16:"m4"==this._type?64:void this._log.warn("unknown type getSizeBytes")}}class Nt{constructor(t,e){this._shaderType=e,this._shader=t,this._cgp=t._cgp,this._gpuBuffer=null,this._values=null,this._sizeBytes=0,this.update()}update(){this._sizeBytes=0;for(let t=0;t1)for(let e=0;e{this._isValid=!1})}}class Ct{constructor(t,e){if(!t)throw new Error("no cgp");this._log=new a("cgp_texture"),this._cgp=t,this.id=CABLES.uuid(),e=e||{},this.name=e.name||"unknown"}initTexture(t,e){this.width=t.width,this.height=t.height,this.textureType="rgba8unorm";const i={size:{width:t.width,height:t.height},format:this.textureType,usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST},s=this._cgp.device.createTexture(i);return this._cgp.device.queue.copyExternalImageToTexture({source:t},{texture:s},i.size),s}getInfo(){const t={};return t.name=this.name,t.size=this.width+" x "+this.height,t.textureType=this.textureType,t}}Ct.load=function(t,e,i,s){fetch(e).then(s=>{s.blob().then(s=>{createImageBitmap(s).then(s=>{const r=new Ct(t,{name:e});r.initTexture(s),i?i(r):console.log("Texture.load no onFinished callback")})})})};const wt=Object.assign({Context:Pt,Shader:class{constructor(t,e){if(!t)throw new Error("shader constructed without cgp "+e);this._log=new a("cgp_shader"),this._cgp=t,this._name=e,this._uniforms=[],e||this._log.stack("no shader name given"),this._name=e||"unknown",this.id=x(),this._isValid=!0,this._compileReason="",this.shaderModule=null,this._needsRecompile=!0,this._src=""}get isValid(){return this._isValid}get uniforms(){return this._uniforms}getName(){return this._name}setWhyCompile(t){this._compileReason=t}setSource(t){this._src=t,this.setWhyCompile("Source changed"),this._needsRecompile=!0}compile(){this._isValid=!0,console.log("compiling shader...",this._compileReason),this._cgp.pushErrorScope(),this.shaderModule=this._cgp.device.createShaderModule({code:this._src}),this._cgp.popErrorScope("cgp_shader "+this._name,this.error.bind(this)),this._needsRecompile=!1}error(t){this._isValid=!1}bind(){for(let t=0;t["WebGL","WebGPU"][this.gApi],this.setCanvas=function(t){this.canvas="string"==typeof t?document.getElementById(t):t,this._setCanvas&&this._setCanvas(t),this.updateSize()},this.updateSize=function(){this.canvas.width=this.canvasWidth=this.canvas.clientWidth*this.pixelDensity,this.canvas.height=this.canvasHeight=this.canvas.clientHeight*this.pixelDensity},this.setSize=function(t,e,i){i||(this.canvas.style.width=t+"px",this.canvas.style.height=e+"px"),this.canvas.width=t*this.pixelDensity,this.canvas.height=e*this.pixelDensity,this.updateSize()},this._resizeToWindowSize=function(){this.setSize(window.innerWidth,window.innerHeight),this.updateSize()},this._resizeToParentSize=function(){const t=this.canvas.parentElement;t?(this.setSize(t.clientWidth,t.clientHeight),this.updateSize()):this._log.error("cables: can not resize to container element")},this.setAutoResize=function(t){window.removeEventListener("resize",this._resizeToWindowSize.bind(this)),window.removeEventListener("resize",this._resizeToParentSize.bind(this)),"window"==t&&(window.addEventListener("resize",this._resizeToWindowSize.bind(this)),window.addEventListener("orientationchange",this._resizeToWindowSize.bind(this)),this._resizeToWindowSize()),"parent"==t&&(window.addEventListener("resize",this._resizeToParentSize.bind(this)),this._resizeToParentSize())},this.pushPMatrix=function(){this.pMatrix=this._pMatrixStack.push(this.pMatrix)},this.popPMatrix=function(){return this.pMatrix=this._pMatrixStack.pop(),this.pMatrix},this.getProjectionMatrixStateCount=function(){return this._pMatrixStack.stateCounter},this.pushModelMatrix=function(){this.mMatrix=this._mMatrixStack.push(this.mMatrix)},this.popModelMatrix=function(){return this.mMatrix=this._mMatrixStack.pop(),this.mMatrix},this.modelMatrix=function(){return this.mMatrix},this.pushViewMatrix=function(){this.vMatrix=this._vMatrixStack.push(this.vMatrix)},this.popViewMatrix=function(){this.vMatrix=this._vMatrixStack.pop()},this.getViewMatrixStateCount=function(){return this._vMatrixStack.stateCounter},this._startMatrixStacks=(t,e)=>{t=t||this._ident,e=e||this._identView,mat4.perspective(this.pMatrix,45,this.canvasWidth/this.canvasHeight,.1,1e3),mat4.identity(this.mMatrix),mat4.identity(this.vMatrix),mat4.translate(this.mMatrix,this.mMatrix,t),mat4.translate(this.vMatrix,this.vMatrix,e),this.pushPMatrix(),this.pushModelMatrix(),this.pushViewMatrix()},this._endMatrixStacks=()=>{this.popViewMatrix(),this.popModelMatrix(),this.popPMatrix()}},Bt=function(t){Ut.apply(this),this.gApi=Rt.GAPI_WEBGL,this.pushMvMatrix=this.pushModelMatrix,this.popMvMatrix=this.popmMatrix=this.popModelMatrix,this.profileData=new St(this),this._log=new a("cgl_context");const e=[0,0,0,0];this.glVersion=0,this.glUseHalfFloatTex=!1,this.clearCanvasTransparent=!0,this.clearCanvasDepth=!0,this.patch=t,this.debugOneFrame=!1,this.checkGlErrors=!0,this.maxTextureUnits=0,this.maxVaryingVectors=0,this.currentProgram=null,this._hadStackError=!1,this.glSlowRenderer=!1,this._isSafariCrap=!1,this.temporaryTexture=null,this.frameStore={},this._onetimeCallbacks=[],this.gl=null,this._cursor="auto",this._currentCursor="",this._glFrameBufferStack=[],this._frameBufferStack=[],this._shaderStack=[],this._stackDepthTest=[],Object.defineProperty(this,"mvMatrix",{get(){return this.mMatrix},set(t){this.mMatrix=t}});const i=new It(this,"simpleshader");i.setModules(["MODULE_VERTEX_POSITION","MODULE_COLOR","MODULE_BEGIN_FRAG"]),i.setSource(It.getDefaultVertexShader(),It.getDefaultFragmentShader());let s=i;this.aborted=!1;const r=[];this.exitError=function(t,e){this.patch.exitError(t,e),this.aborted=!0},this._setCanvas=function(t){if(this.patch.config.canvas||(this.patch.config.canvas={}),this.patch.config.canvas.hasOwnProperty("preserveDrawingBuffer")||(this.patch.config.canvas.preserveDrawingBuffer=!1),this.patch.config.canvas.hasOwnProperty("premultipliedAlpha")||(this.patch.config.canvas.premultipliedAlpha=!1),this.patch.config.canvas.hasOwnProperty("alpha")||(this.patch.config.canvas.alpha=!1),this.patch.config.canvas.stencil=!0,this.patch.config.hasOwnProperty("clearCanvasColor")&&(this.clearCanvasTransparent=this.patch.config.clearCanvasColor),this.patch.config.hasOwnProperty("clearCanvasDepth")&&(this.clearCanvasDepth=this.patch.config.clearCanvasDepth),/^((?!chrome|android).)*safari/i.test(navigator.userAgent)&&(this._isSafariCrap=!0),this.patch.config.canvas.forceWebGl1||(this.gl=this.canvas.getContext("webgl2",this.patch.config.canvas)),this.gl&&"WebGL 1.0"!=this.gl.getParameter(this.gl.VERSION)?this.glVersion=2:(this.gl=this.canvas.getContext("webgl",this.patch.config.canvas)||this.canvas.getContext("experimental-webgl",this.patch.config.canvas),this.glVersion=1,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)&&navigator.userAgent.match(/iPhone/i)&&(this.glUseHalfFloatTex=!0),/iPad|iPhone|iPod/.test(navigator.userAgent)&&!window.MSStream&&(this.patch.config.canvas.hasOwnProperty("powerPreference")||(this.patch.config.canvas.powerPreference="high-performance"))),!this.gl)return void this.exitError("NO_WEBGL","sorry, could not initialize WebGL. Please check if your Browser supports WebGL or try to restart your browser.");const e=this.gl.getExtension("WEBGL_debug_renderer_info");e&&(this.glRenderer=this.gl.getParameter(e.UNMASKED_RENDERER_WEBGL),"Google SwiftShader"===this.glRenderer&&(this.glSlowRenderer=!0)),this.gl.getExtension("OES_standard_derivatives");const i=this.gl.getExtension("ANGLE_instanced_arrays")||this.gl;this.canvas.addEventListener("webglcontextlost",t=>{this._log.error("canvas lost...",t),this.emitEvent("webglcontextlost"),this.aborted=!0}),this.maxVaryingVectors=this.gl.getParameter(this.gl.MAX_VARYING_VECTORS),this.maxTextureUnits=this.gl.getParameter(this.gl.MAX_TEXTURE_IMAGE_UNITS),this.maxTexSize=this.gl.getParameter(this.gl.MAX_TEXTURE_SIZE),this.maxUniformsFrag=this.gl.getParameter(this.gl.MAX_FRAGMENT_UNIFORM_VECTORS),this.maxUniformsVert=this.gl.getParameter(this.gl.MAX_VERTEX_UNIFORM_VECTORS),this.maxSamples=0,this.gl.MAX_SAMPLES&&(this.maxSamples=this.gl.getParameter(this.gl.MAX_SAMPLES)),i.vertexAttribDivisorANGLE&&(this.gl.vertexAttribDivisor=i.vertexAttribDivisorANGLE.bind(i),this.gl.drawElementsInstanced=i.drawElementsInstancedANGLE.bind(i))},this.getInfo=function(){return{glVersion:this.glVersion,glRenderer:this.glRenderer,glUseHalfFloatTex:this.glUseHalfFloatTex,maxVaryingVectors:this.maxVaryingVectors,maxTextureUnits:this.maxTextureUnits,maxTexSize:this.maxTexSize,maxUniformsFrag:this.maxUniformsFrag,maxUniformsVert:this.maxUniformsVert,maxSamples:this.maxSamples}};let n=-1,o=-1;this.getViewPort=function(){return e},this.resetViewPort=function(){this.gl.viewport(e[0],e[1],e[2],e[3])},this.setViewPort=function(t,i,s,r){e[0]=Math.round(t),e[1]=Math.round(i),e[2]=Math.round(s),e[3]=Math.round(r),this.gl.viewport(e[0],e[1],e[2],e[3])},this.screenShot=function(t,e,i,s){e&&(this.gl.clearColor(1,1,1,1),this.gl.colorMask(!1,!1,!1,!0),this.gl.clear(this.gl.COLOR_BUFFER_BIT),this.gl.colorMask(!0,!0,!0,!0)),this.canvas&&this.canvas.toBlob&&this.canvas.toBlob(e=>{t?t(e):this._log.log("no screenshot callback...")},i,s)},this.endFrame=function(){if(this.patch.isEditorMode()&&CABLES.GL_MARKER.drawMarkerLayer(this),this.setPreviousShader(),this._vMatrixStack.length()>0&&this.logStackError("view matrix stack length !=0 at end of rendering..."),this._mMatrixStack.length()>0&&this.logStackError("mvmatrix stack length !=0 at end of rendering..."),this._pMatrixStack.length()>0&&this.logStackError("pmatrix stack length !=0 at end of rendering..."),this._glFrameBufferStack.length>0&&this.logStackError("glFrameBuffer stack length !=0 at end of rendering..."),this._stackDepthTest.length>0&&this.logStackError("depthtest stack length !=0 at end of rendering..."),this._stackDepthWrite.length>0&&this.logStackError("depthwrite stack length !=0 at end of rendering..."),this._stackDepthFunc.length>0&&this.logStackError("depthfunc stack length !=0 at end of rendering..."),this._stackBlend.length>0&&this.logStackError("blend stack length !=0 at end of rendering..."),this._stackBlendMode.length>0&&this.logStackError("blendMode stack length !=0 at end of rendering..."),this._shaderStack.length>0&&this.logStackError("this._shaderStack length !=0 at end of rendering..."),this._stackCullFace.length>0&&this.logStackError("this._stackCullFace length !=0 at end of rendering..."),this._stackCullFaceFacing.length>0&&this.logStackError("this._stackCullFaceFacing length !=0 at end of rendering..."),this._frameStarted=!1,n!=this.canvasWidth||o!=this.canvasHeight){n=this.canvasWidth,o=this.canvasHeight,this.setSize(this.canvasWidth/this.pixelDensity,this.canvasHeight/this.pixelDensity),this.updateSize();for(let t=0;t=0;t--)if(this._shaderStack[t]&&this.frameStore.renderOffscreen==this._shaderStack[t].offScreenPass)return this._shaderStack[t]},this.getDefaultShader=function(){return i},this.pushShader=this.setShader=function(t){this._shaderStack.push(t),s=t},this.popShader=this.setPreviousShader=function(){if(0===this._shaderStack.length)throw new Error("Invalid shader stack pop!");this._shaderStack.pop(),s=this._shaderStack[this._shaderStack.length-1]},this.pushGlFrameBuffer=function(t){this._glFrameBufferStack.push(t)},this.popGlFrameBuffer=function(){return 0==this._glFrameBufferStack.length?null:(this._glFrameBufferStack.pop(),this._glFrameBufferStack[this._glFrameBufferStack.length-1])},this.getCurrentGlFrameBuffer=function(){return 0===this._glFrameBufferStack.length?null:this._glFrameBufferStack[this._glFrameBufferStack.length-1]},this.pushFrameBuffer=function(t){this._frameBufferStack.push(t)},this.popFrameBuffer=function(){return 0==this._frameBufferStack.length?null:(this._frameBufferStack.pop(),this._frameBufferStack[this._frameBufferStack.length-1])},this.getCurrentFrameBuffer=function(){return 0===this._frameBufferStack.length?null:this._frameBufferStack[this._frameBufferStack.length-1]},this.renderStart=function(t,e,s){this.fpsCounter.startFrame(),this.pushDepthTest(!0),this.pushDepthWrite(!0),this.pushDepthFunc(t.gl.LEQUAL),this.pushCullFaceFacing(t.gl.BACK),this.pushCullFace(!1),this.clearCanvasTransparent&&(t.gl.clearColor(0,0,0,0),t.gl.clear(t.gl.COLOR_BUFFER_BIT)),this.clearCanvasDepth&&t.gl.clear(t.gl.DEPTH_BUFFER_BIT),t.setViewPort(0,0,t.canvasWidth,t.canvasHeight),this._startMatrixStacks(e,s),t.pushBlendMode(at.BLEND_MODES.BLEND_NORMAL,!1);for(let t=0;t0){for(let t=0;t0&&this.gl.cullFace(this._stackCullFaceFacing[this._stackCullFaceFacing.length-1])},Bt.prototype._stackDepthFunc=[],Bt.prototype.pushDepthFunc=function(t){this._stackDepthFunc.push(t),this.gl.depthFunc(t)},Bt.prototype.stateDepthFunc=function(){return this._stackDepthFunc.length>0&&this._stackDepthFunc[this._stackDepthFunc.length-1]},Bt.prototype.popDepthFunc=function(){this._stackDepthFunc.pop(),this._stackDepthFunc.length>0&&this.gl.depthFunc(this._stackDepthFunc[this._stackDepthFunc.length-1])},Bt.prototype._stackBlend=[],Bt.prototype.pushBlend=function(t){this._stackBlend.push(t),t?this.gl.enable(this.gl.BLEND):this.gl.disable(this.gl.BLEND)},Bt.prototype.popBlend=function(){this._stackBlend.pop(),this._stackBlend[this._stackBlend.length-1]?this.gl.enable(this.gl.BLEND):this.gl.disable(this.gl.BLEND)},Bt.prototype.stateBlend=function(){return this._stackBlend[this._stackBlend.length-1]};Bt.prototype._stackBlendMode=[],Bt.prototype._stackBlendModePremul=[],Bt.prototype.pushBlendMode=function(t,e){this._stackBlendMode.push(t),this._stackBlendModePremul.push(e);const i=this._stackBlendMode.length-1;this.pushBlend(this._stackBlendMode[i]!==at.BLEND_MODES.BLEND_NONE),this._setBlendMode(this._stackBlendMode[i],this._stackBlendModePremul[i])},Bt.prototype.popBlendMode=function(){this._stackBlendMode.pop(),this._stackBlendModePremul.pop();const t=this._stackBlendMode.length-1;this.popBlend(this._stackBlendMode[t]!==at.BLEND_MODES.BLEND_NONE),t>=0&&this._setBlendMode(this._stackBlendMode[t],this._stackBlendModePremul[t])},Bt.prototype._stackStencil=[],Bt.prototype.pushStencil=function(t){this._stackStencil.push(t),t?this.gl.enable(this.gl.STENCIL_TEST):this.gl.disable(this.gl.STENCIL_TEST)},Bt.prototype.popStencil=function(){this._stackStencil.pop(),this._stackStencil[this._stackStencil.length-1]?this.gl.enable(this.gl.STENCIL_TEST):this.gl.disable(this.gl.STENCIL_TEST)},Bt.prototype.glGetAttribLocation=function(t,e){return this.gl.getAttribLocation(t,e)},Bt.prototype.shouldDrawHelpers=function(t){if(this.frameStore.shadowPass)return!1;if(!t.patch.isEditorMode())return!1;const e=this.getCurrentFrameBuffer();if(e&&e.getWidth){if(!(this.canvasWidth/this.canvasHeight==e.getWidth()/e.getHeight()))return!1}return CABLES.UI.renderHelper||CABLES.UI.renderHelperCurrent&&t.isCurrentUiOp()},Bt.prototype._setBlendMode=function(t,e){const i=this.gl;t==at.BLEND_MODES.BLEND_NONE||(t==at.BLEND_MODES.BLEND_ADD?e?(i.blendEquationSeparate(i.FUNC_ADD,i.FUNC_ADD),i.blendFuncSeparate(i.ONE,i.ONE,i.ONE,i.ONE)):(i.blendEquation(i.FUNC_ADD),i.blendFunc(i.SRC_ALPHA,i.ONE)):t==at.BLEND_MODES.BLEND_SUB?e?(i.blendEquationSeparate(i.FUNC_ADD,i.FUNC_ADD),i.blendFuncSeparate(i.ZERO,i.ZERO,i.ONE_MINUS_SRC_COLOR,i.ONE_MINUS_SRC_ALPHA)):(i.blendEquation(i.FUNC_ADD),i.blendFunc(i.ZERO,i.ONE_MINUS_SRC_COLOR)):t==at.BLEND_MODES.BLEND_MUL?e?(i.blendEquationSeparate(i.FUNC_ADD,i.FUNC_ADD),i.blendFuncSeparate(i.ZERO,i.SRC_COLOR,i.ZERO,i.SRC_ALPHA)):(i.blendEquation(i.FUNC_ADD),i.blendFunc(i.ZERO,i.SRC_COLOR)):t==at.BLEND_MODES.BLEND_NORMAL?e?(i.blendEquationSeparate(i.FUNC_ADD,i.FUNC_ADD),i.blendFuncSeparate(i.ONE,i.ONE_MINUS_SRC_ALPHA,i.ONE,i.ONE_MINUS_SRC_ALPHA)):(i.blendEquationSeparate(i.FUNC_ADD,i.FUNC_ADD),i.blendFuncSeparate(i.SRC_ALPHA,i.ONE_MINUS_SRC_ALPHA,i.ONE,i.ONE_MINUS_SRC_ALPHA)):this._log.log("setblendmode: unknown blendmode"))},Bt.prototype.createMesh=function(t,e){return new CGL.Mesh(this,t,e)},Bt.prototype.setCursor=function(t){this._cursor=t};const Lt=Object.assign({Framebuffer:function(t,e,i,s){const r=t;this._log=new a("Framebuffer");const n=r.gl.getExtension("WEBGL_depth_texture")||r.gl.getExtension("WEBKIT_WEBGL_depth_texture")||r.gl.getExtension("MOZ_WEBGL_depth_texture")||r.gl.DEPTH_TEXTURE;n||r.exitError("NO_DEPTH_TEXTURE","no depth texture support");let o=e||512,h=i||512;(s=s||{isFloatingPointTexture:!1}).hasOwnProperty("clear")||(s.clear=!0),s.hasOwnProperty("filter")||(s.filter=B.FILTER_LINEAR);const l=new B(r,{isFloatingPointTexture:s.isFloatingPointTexture,filter:s.filter,wrap:s.wrap||B.CLAMP_TO_EDGE});let c=null;n&&(c=new B(r,{isDepthTexture:!0})),this._options=s;const u=r.gl.createFramebuffer(),g=r.gl.createRenderbuffer();this.getWidth=function(){return o},this.getHeight=function(){return h},this.getGlFrameBuffer=function(){return u},this.getDepthRenderBuffer=function(){return g},this.getTextureColor=function(){return l},this.getTextureDepth=function(){return c},this.setFilter=function(t){l.filter=t,l.setSize(o,h)},this.setSize=function(t,e){if(t<2&&(t=2),e<2&&(e=2),o=Math.ceil(t),h=Math.ceil(e),r.profileData.profileFrameBuffercreate++,r.gl.bindFramebuffer(r.gl.FRAMEBUFFER,u),r.gl.bindRenderbuffer(r.gl.RENDERBUFFER,g),l.setSize(o,h),c&&c.setSize(o,h),n&&r.gl.renderbufferStorage(r.gl.RENDERBUFFER,r.gl.DEPTH_COMPONENT16,o,h),r.gl.framebufferTexture2D(r.gl.FRAMEBUFFER,r.gl.COLOR_ATTACHMENT0,r.gl.TEXTURE_2D,l.tex,0),n&&(r.gl.framebufferRenderbuffer(r.gl.FRAMEBUFFER,r.gl.DEPTH_ATTACHMENT,r.gl.RENDERBUFFER,g),r.gl.framebufferTexture2D(r.gl.FRAMEBUFFER,r.gl.DEPTH_ATTACHMENT,r.gl.TEXTURE_2D,c.tex,0)),!r.gl.isFramebuffer(u))throw new Error("Invalid framebuffer");const i=r.gl.checkFramebufferStatus(r.gl.FRAMEBUFFER);switch(i){case r.gl.FRAMEBUFFER_COMPLETE:break;case r.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:throw this._log.warn("FRAMEBUFFER_INCOMPLETE_ATTACHMENT...",o,h,l.tex,g),new Error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT");case r.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:throw this._log.warn("FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"),new Error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");case r.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:throw this._log.warn("FRAMEBUFFER_INCOMPLETE_DIMENSIONS"),new Error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS");case r.gl.FRAMEBUFFER_UNSUPPORTED:throw this._log.warn("FRAMEBUFFER_UNSUPPORTED"),new Error("Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED");case 36059:this._log.warn("Incomplete: FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER from ext. Or Safari/iOS undefined behaviour.");break;default:throw this._log.warn("incomplete framebuffer",i),new Error("Incomplete framebuffer: "+i)}r.gl.bindTexture(r.gl.TEXTURE_2D,null),r.gl.bindRenderbuffer(r.gl.RENDERBUFFER,null),r.gl.bindFramebuffer(r.gl.FRAMEBUFFER,null)},this.renderStart=function(){r.pushModelMatrix(),r.gl.bindFramebuffer(r.gl.FRAMEBUFFER,u),r.pushGlFrameBuffer(u),r.pushFrameBuffer(this),r.pushPMatrix(),r.gl.viewport(0,0,o,h),this._options.clear&&(r.gl.clearColor(0,0,0,0),r.gl.clear(r.gl.COLOR_BUFFER_BIT|r.gl.DEPTH_BUFFER_BIT))},this.renderEnd=function(){r.popPMatrix(),r.gl.bindFramebuffer(r.gl.FRAMEBUFFER,r.popGlFrameBuffer()),r.popFrameBuffer(),r.popModelMatrix(),r.resetViewPort()},this.delete=function(){l.delete(),c&&c.delete(),r.gl.deleteRenderbuffer(g),r.gl.deleteFramebuffer(u)},this.setSize(o,h)},Framebuffer2:L,Geometry:W,BoundingBox:z,Marker:function(t){const e=new W("marker");e.setPointVertices([1e-5,0,0,1,0,0,0,1e-5,0,0,1,0,0,0,1e-5,0,0,1]);const i=new lt(t,e,t.gl.LINES);i.setGeom(e);const s=new It(t,"markermaterial"),r="".endl()+"precision highp float;".endl()+"IN vec3 axisColor;".endl()+"void main()".endl()+"{".endl()+" vec4 col=vec4(axisColor,1.0);".endl()+" outColor = col;".endl()+"}",n="".endl()+"IN vec3 vPosition;".endl()+"UNI mat4 projMatrix;".endl()+"UNI mat4 mvMatrix;".endl()+"OUT vec3 axisColor;".endl()+"void main()".endl()+"{".endl()+" vec4 pos=vec4(vPosition, 1.0);".endl()+" if(pos.x!=0.0)axisColor=vec3(1.0,0.3,0.0);".endl()+" if(pos.y!=0.0)axisColor=vec3(0.0,1.0,0.2);".endl()+" if(pos.z!=0.0)axisColor=vec3(0.0,0.5,1.0);".endl()+" gl_Position = projMatrix * mvMatrix * pos;".endl()+"}";s.setSource(n,r),this._vScale=vec3.create(),this.draw=function(t,e,r){const n=e||2;t.pushModelMatrix(),t.pushShader(s),vec3.set(this._vScale,n,n,n),mat4.scale(t.mvMatrix,t.mvMatrix,this._vScale),t.pushDepthTest(1==r),i.render(t.getShader()),t.popDepthTest(),t.popShader(),t.popModelMatrix()}},WirePoint:function(t){const e=t.gl.createBuffer(),i=vec3.create();this.render=function(t,s){t.pushModelMatrix(),vec3.set(i,s,s,s),mat4.scale(t.mvMatrix,t.mvMatrix,i);const r=t.getShader();r&&(r.bind(),t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.vertexAttribPointer(r.getAttrVertexPos(),e.itemSize,t.gl.FLOAT,!1,0,0),t.gl.enableVertexAttribArray(r.getAttrVertexPos()),t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.drawArrays(t.gl.LINE_STRIP,0,e.numItems)),t.popModelMatrix()},function(){const i=[];let s=0,r=0;for(s=0;s<=Math.round(24);s++)r=360/Math.round(24)*s*mt,i.push(.5*Math.cos(r)),i.push(0),i.push(.5*Math.sin(r));for(s=0;s<=Math.round(24);s++)r=360/Math.round(24)*s*mt,i.push(.5*Math.cos(r)),i.push(.5*Math.sin(r)),i.push(0);for(s=0;s<=Math.round(24);s++)r=360/Math.round(24)*s*mt,i.push(0),i.push(.5*Math.cos(r)),i.push(.5*Math.sin(r));t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.bufferData(t.gl.ARRAY_BUFFER,new Float32Array(i),t.gl.STATIC_DRAW),e.itemSize=3,e.numItems=i.length/e.itemSize}()},WireCube:function(t){const e=t.gl.createBuffer(),i=vec3.create();this.render=function(t,s,r,n){t.pushModelMatrix(),vec3.set(i,s||1,r||1,n||1),mat4.scale(t.mvMatrix,t.mvMatrix,i);const o=t.getShader();o&&(o.bind(),t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.vertexAttribPointer(o.getAttrVertexPos(),e.itemSize,t.gl.FLOAT,!1,0,0),t.gl.enableVertexAttribArray(o.getAttrVertexPos()),t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.drawArrays(t.gl.LINE_STRIP,0,e.numItems)),t.popModelMatrix()},function(){const i=[];i.push(-1,-1,1),i.push(1,-1,1),i.push(1,1,1),i.push(-1,1,1),i.push(-1,-1,1),i.push(-1,-1,-1),i.push(1,-1,-1),i.push(1,1,-1),i.push(-1,1,-1),i.push(-1,-1,-1),i.push(-1,-1,-1),i.push(-1,1,-1),i.push(-1,1,1),i.push(-1,-1,1),i.push(-1,-1,-1),i.push(1,-1,-1),i.push(1,1,-1),i.push(1,1,1),i.push(1,-1,1),i.push(1,-1,-1),t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.bufferData(t.gl.ARRAY_BUFFER,new Float32Array(i),t.gl.STATIC_DRAW),e.itemSize=3,e.numItems=i.length/e.itemSize}()},MatrixStack:Mt,Mesh:lt,MESH:ht,ShaderLibMods:pt,Shader:It,Uniform:nt,MESHES:ut,Context:Bt,Texture:B,TextureEffect:gt,isWindows:Et,getWheelSpeed:At,getWheelDelta:bt,onLoadingAssetsFinished:null,ProfileData:St,UniColorShader:class{constructor(t){this.shader=new CGL.Shader(t,"markermaterial");const e="".endl()+"void main()".endl()+"{".endl()+" outColor = vec4(color.rgb,1.0);".endl()+"}",i="".endl()+"IN vec3 vPosition;".endl()+"UNI mat4 projMatrix;".endl()+"UNI mat4 mvMatrix;".endl()+"void main()".endl()+"{".endl()+" gl_Position = projMatrix * mvMatrix * vec4(vPosition,1.0);".endl()+"}";this.shader.setSource(i,e),this.coloruni=this.shader.addUniformFrag("4f","color",[1,.777,1,1])}setColor(t,e,i,s){this.coloruni.set(t,e,i,s)}}},at.BLEND_MODES,at.SHADER,at.MATH,at.BLEND_MODES);window.CGL=Lt;const kt=function(t){Y.apply(this),this.id=CABLES.uuid(),this.portIn=null,this.portOut=null,this.scene=t,this.activityCounter=0,this.ignoreInSerialize=!1};kt.prototype.setValue=function(t){void 0===t?this._setValue():this.portIn.set(t)},kt.prototype.activity=function(){this.activityCounter++},kt.prototype._setValue=function(){if(!this.portOut)return void this.remove();const t=this.portOut.get();t==t&&(this.portIn.type!=l.OP_PORT_TYPE_FUNCTION&&this.activity(),(this.portIn.get()!==t||this.portIn.changeAlways)&&this.portIn.set(t))},kt.prototype.getOtherPort=function(t){return t==this.portIn?this.portOut:this.portIn},kt.prototype.remove=function(){this.portIn&&this.portIn.removeLink(this),this.portOut&&this.portOut.removeLink(this),this.scene&&this.scene.emitEvent("onUnLink",this.portIn,this.portOut,this),!this.portIn||this.portIn.type!=l.OP_PORT_TYPE_OBJECT&&this.portIn.type!=l.OP_PORT_TYPE_ARRAY||(this.portIn.set(null),this.portIn.links.length>0&&this.portIn.set(this.portIn.links[0].getOtherPort(this.portIn).get())),this.portIn&&this.portIn.parent._checkLinksNeededToWork(),this.portOut&&this.portOut.parent._checkLinksNeededToWork(),this.portIn=null,this.portOut=null,this.scene=null},kt.prototype.link=function(t,e){if(!kt.canLink(t,e))return console.warn("[core_link] cannot link ports!",t,e),!1;t.direction==c.PORT_DIR_IN?(this.portIn=t,this.portOut=e):(this.portIn=e,this.portOut=t),t.addLink(this),e.addLink(this),this.setValue(),t.onLink&&t.onLink(this),e.onLink&&e.onLink(this),t.parent._checkLinksNeededToWork(),e.parent._checkLinksNeededToWork()},kt.prototype.getSerialized=function(){const t={};return t.portIn=this.portIn.getName(),t.portOut=this.portOut.getName(),t.objIn=this.portIn.parent.id,t.objOut=this.portOut.parent.id,t},kt.canLinkText=function(t,e){if(t.direction==e.direction){let t="(out)";return e.direction==c.PORT_DIR_IN&&(t="(in)"),"can not link: same direction "+t}return t.parent==e.parent?"can not link: same op":t.type!=l.OP_PORT_TYPE_DYNAMIC&&e.type!=l.OP_PORT_TYPE_DYNAMIC&&t.type!=e.type?"can not link: different type":t?e?t.direction==c.PORT_DIR_IN&&t.isAnimated()||e.direction==c.PORT_DIR_IN&&e.isAnimated()?"can not link: is animated":t.isLinkedTo(e)?"ports already linked":t.canLink&&!t.canLink(e)||e.canLink&&!e.canLink(t)?"Incompatible":"can link":"can not link: port 2 invalid":"can not link: port 1 invalid"},kt.canLink=function(t,e){return!!t&&(!!e&&((t.direction!=c.PORT_DIR_IN||!t.isAnimated())&&((e.direction!=c.PORT_DIR_IN||!e.isAnimated())&&(!t.isHidden()&&!e.isHidden()&&(!t.isLinkedTo(e)&&(t.direction!=e.direction&&((t.type==e.type||t.type==l.OP_PORT_TYPE_DYNAMIC||e.type==l.OP_PORT_TYPE_DYNAMIC)&&(t.type==l.OP_PORT_TYPE_DYNAMIC||e.type==l.OP_PORT_TYPE_DYNAMIC||t.parent!=e.parent&&(!(t.canLink&&!t.canLink(e))&&!(e.canLink&&!e.canLink(t)))))))))))};const Vt=function(){Y.apply(this),this._log=new a("core_op"),this.data={},this.storage={},this.objName="",this.portsOut=[],this.portsIn=[],this.portsInData=[],this.opId="",this.uiAttribs={},this.enabled=!0,this.patch=arguments[0],this.name=arguments[1],this._needsLinkedToWork=[],this._needsParentOp=null,this._shortOpName="",this.hasUiErrors=!1,this._uiErrors={},arguments[1]&&(this._shortOpName=CABLES.getShortOpName(arguments[1]),this.getTitle()),this.id=arguments[2]||T(),this.onAddPort=null,this.onCreate=null,this.onResize=null,this.onLoaded=null,this.onDelete=null,this.onUiAttrChange=null,this.onError=null,this._instances=null,this.preRender=null,this.init=null};{Vt.prototype.clearUiAttrib=function(t){const e={name:null};this.uiAttrib(e)},Vt.prototype.getTitle=function(){return this.uiAttribs?(void 0!==this.uiAttribs.title&&""!==this.uiAttribs.title||-1!=this.objName.indexOf("Ops.Ui.")||(this.uiAttribs.title=this._shortOpName),void 0===this.uiAttribs.title&&(this.uiAttribs.title=this._shortOpName),this.uiAttribs.title):"nouiattribs"+this.name},Vt.prototype.setTitle=function(t){const e=this.name!=t;this.name=t,this.uiAttr({title:t}),e&&this.emitEvent("onTitleChange",t)};const t=function(t){if(!t)return;(t.error||t.warning||t.hint)&&this._log.warn("old ui error/warning attribute in "+this.name+", use op.setUiError !",t),"object"!=typeof t&&this._log.error("op.uiAttrib attribs are not of type object"),this.uiAttribs||(this.uiAttribs={});let e=!1;for(const i in t)this.uiAttribs[i]!=t[i]&&(e=!0),this.uiAttribs[i]=t[i];this.uiAttribs.hasOwnProperty("selected")&&0==this.uiAttribs.selected&&delete this.uiAttribs.selected,t.title&&t.title!=this.name&&this.setTitle(t.title),e&&(this.emitEvent("onUiAttribsChange",t),this.patch.emitEvent("onUiAttribsChange",this,t))};Vt.prototype.setUiAttribs=Vt.prototype.setUiAttrib=Vt.prototype.uiAttr=t,Vt.prototype.getName=function(){return this.uiAttribs.name?this.uiAttribs.name:this.objName.split(".")},Vt.prototype.addOutPort=function(t){return t.direction=c.PORT_DIR_OUT,t.parent=this,this.portsOut.push(t),this.emitEvent("onPortAdd",t),t},Vt.prototype.hasDynamicPort=function(){let t=0;for(t=0;tt==i);n.setValue(t),r.defaultValue=i,n.defaultValue=t}}return r},Vt.prototype.inSwitch=function(t,e,i,s){let r=null;if(s){const i=new et(this,t,l.OP_PORT_TYPE_STRING,{display:"switch",hidePort:!0,type:"string",values:e});r=this.addInPort(i)}else{const s=new et(this,t+" index",l.OP_PORT_TYPE_VALUE,{increment:"integer",hideParam:!0}),n=this.addInPort(s),o=new it(this,t,l.OP_PORT_TYPE_STRING,{display:"switch",hidePort:!0,type:"string",values:e},n);if(s.onLinkChanged=function(){o.setUiAttribs({greyout:s.isLinked()})},r=this.addInPort(o),void 0!==i){r.set(i);const t=e.findIndex(t=>t==i);n.setValue(t),r.defaultValue=i,n.defaultValue=t}}return r},Vt.prototype.inValueInt=Vt.prototype.inInt=function(t,e){const i=this.addInPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{increment:"integer"}));return void 0!==e&&(i.set(e),i.defaultValue=e),i},Vt.prototype.inFile=function(t,e,i){const s=this.addInPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{display:"file",type:"string",filter:e}));return void 0!==i&&(s.set(i),s.defaultValue=i),s},Vt.prototype.inUrl=function(t,e,i){const s=this.addInPort(new et(this,t,l.OP_PORT_TYPE_STRING,{display:"file",type:"string",filter:e}));return void 0!==i&&(s.set(i),s.defaultValue=i),s},Vt.prototype.inTexture=function(t,e){const i=this.addInPort(new et(this,t,l.OP_PORT_TYPE_OBJECT,{display:"texture",objType:"texture",preview:!0}));return void 0!==e&&i.set(e),i},Vt.prototype.inObject=function(t,e,i){const s=this.addInPort(new et(this,t,l.OP_PORT_TYPE_OBJECT,{objType:i}));return void 0!==e&&s.set(e),s},Vt.prototype.inGradient=function(t,e){const i=this.addInPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{display:"gradient",hidePort:!0}));return void 0!==e&&i.set(e),i},Vt.prototype.inArray=function(t,e,i){!i&&CABLES.UTILS.isNumeric(e)&&(i=e);const s=this.addInPort(new et(this,t,l.OP_PORT_TYPE_ARRAY,{stride:i}));return void 0===e||!Array.isArray(e)&&null!=e||s.set(e),s},Vt.prototype.inValueSlider=Vt.prototype.inFloatSlider=function(t,e,i,s){const r={display:"range"};null!=i&&null!=s&&(r.min=i,r.max=s);const n=this.addInPort(new et(this,t,l.OP_PORT_TYPE_VALUE,r));return void 0!==e&&(n.set(e),n.defaultValue=e),n},Vt.prototype.outFunction=Vt.prototype.outTrigger=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_FUNCTION));return void 0!==e&&i.set(e),i},Vt.prototype.outValue=Vt.prototype.outNumber=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_VALUE));return void 0!==e&&i.set(e),i},Vt.prototype.outValueBool=Vt.prototype.outBool=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{display:"bool"}));return void 0!==e?i.set(e):i.set(0),i},Vt.prototype.outBoolNum=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{display:"boolnum"}));return i.set=function(t){this.setValue(t?1:0)}.bind(i),void 0!==e?i.set(e):i.set(0),i},Vt.prototype.outValueString=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{type:"string"}));return void 0!==e&&i.set(e),i},Vt.prototype.outString=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_STRING,{type:"string"}));return void 0!==e?i.set(e):i.set(""),i},Vt.prototype.outObject=function(t,e,i){const s=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_OBJECT,{objType:i||null}));return s.set(e||null),s.ignoreValueSerialize=!0,s},Vt.prototype.outArray=function(t,e,i){!i&&CABLES.UTILS.isNumeric(e)&&(i=e);const s=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_ARRAY,{stride:i}));return void 0===e||!Array.isArray(e)&&null!=e||s.set(e),s.ignoreValueSerialize=!0,s},Vt.prototype.outTexture=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_OBJECT,{preview:!0,objType:"texture"}));return void 0!==e&&i.set(e),i.ignoreValueSerialize=!0,i},Vt.prototype.inDynamic=function(t,e,i,s){const r=new et(this,t,l.OP_PORT_TYPE_DYNAMIC,i);return r.shouldLink=function(t,i){if(e&&g.isArray(e)){for(let s=0;s0&&(t.storage=this.storage),this.uiAttribs.hasOwnProperty("working")&&1==this.uiAttribs.working&&delete this.uiAttribs.working,t.portsIn=[],t.portsOut=[];for(let e=0;e-1&&this._log.warn("setuierror id cant have spaces! ",t),!e&&this._uiErrors.hasOwnProperty(t)?delete this._uiErrors[t]:!e||this._uiErrors.hasOwnProperty(t)&&this._uiErrors[t].txt==e||(null==i&&(i=2),this._uiErrors[t]={txt:e,level:i,id:t});const s=[];for(const t in this._uiErrors)s.push(this._uiErrors[t]);this.uiAttr({uierrors:s}),this.hasUiErrors=Object.keys(this._uiErrors).length,this.emitEvent("uiErrorChange")},Vt.prototype.setError=function(t,e){this._log.warn("old error message op.error() - use op.setUiError()")},Vt.prototype.setEnabled=function(t){this.enabled=t,this.emitEvent("onEnabledChange",t)},Vt.prototype.setPortGroup=function(t,e){for(let i=0;i{e(this._patch),this.emitEvent("finishedAll")},100)}this._wasFinishedPrinted||(this._wasFinishedPrinted=!0,this.print()),this.emitEvent("finishedAll")}},Dt.prototype.getList=function(){let t=[];for(const e in this._loadingAssets)t.push(this._loadingAssets[e]);return t},Dt.prototype.getListJobs=function(){let t=[];for(const e in this._loadingAssets)this._loadingAssets[e].finished||t.push(this._loadingAssets[e].name);return t},Dt.prototype.print=function(){if(this._patch.config.silent)return;const t=[];for(const e in this._loadingAssets)t.push([this._loadingAssets[e].order,this._loadingAssets[e].type,this._loadingAssets[e].name,(this._loadingAssets[e].timeEnd-this._loadingAssets[e].timeStart)/1e3+"s"]);this._log.groupCollapsed("finished loading "+this._order+" assets in "+(Date.now()-this._startTime)/1e3+"s"),this._log.table(t),this._log.groupEnd()},Dt.prototype.finished=function(t){this._loadingAssets[t]&&(this._loadingAssets[t].finished=!0,this._loadingAssets[t].timeEnd=Date.now()),this.checkStatus(),this.emitEvent("finishedTask")},Dt.prototype._startAssetTasks=function(){for(let t=0;t0},Gt.prototype.increment=function(){this._index++},Gt.prototype.index=function(){return this._index};const Ht=function(t){this.startFrame=t.getFrameNum();let e={},i=null,s=0;this.getItems=function(){return e},this.clear=function(){e={}},this.add=function(r,n){null!==i&&(n&&n.id==i||e[i]&&(e[i].timeUsed+=performance.now()-s,(!e[i].peakTime||dt()-e[i].peakTime>5e3)&&(e[i].peak=0,e[i].peakTime=dt()),e[i].peak=Math.max(e[i].peak,performance.now()-s))),null!==n?(e[n.id]||(e[n.id]={numTriggers:0,timeUsed:0}),e[n.id].lastFrame!=t.getFrameNum()&&(e[n.id].numTriggers=0),e[n.id].lastFrame=t.getFrameNum(),e[n.id].numTriggers++,e[n.id].opid=n.parent.id,e[n.id].title=n.parent.name+"."+n.name,e[n.id].subPatch=n.parent.uiAttribs.subPatch,i=n.id,s=performance.now()):i=null},this.print=function(){console.log("--------");for(const t in e)console.log(e[t].title+": "+e[t].numTriggers+" / "+e[t].timeUsed)}};var zt=class extends Y{constructor(t,e,i){super(),this._name=t,this.type=i,this.setValue(e)}addListener(t){this.on("change",t,"var")}getValue(){return this._v}getName(){return this._name}setValue(t){this._v=t,this.emitEvent("change",t,this)}};const Wt=function(t){Y.apply(this),this._log=new a("core_patch"),this.ops=[],this.settings={},this.config=t||{glCanvasResizeToWindow:!1,prefixAssetPath:"",prefixJsPath:"",silent:!0,onError:null,onFinishedLoading:null,onFirstFrameRendered:null,onPatchLoaded:null,fpsLimit:0},this.timer=new ft,this.freeTimer=new ft,this.animFrameOps=[],this.animFrameCallbacks=[],this.gui=!1,CABLES.logSilent=this.silent=!0,this.profiler=null,this.aborted=!1,this._crashedOps=[],this._renderOneFrame=!1,this._animReq=null,this._opIdCache={},this._triggerStack=[],this.loading=new Dt(this),this._volumeListeners=[],this._paused=!1,this._frameNum=0,this.instancing=new Gt,this.onOneFrameRendered=null,this.namedTriggers={},this._origData=null,this._frameNext=0,this._frameInterval=0,this._lastFrameTime=0,this._frameWasdelayed=!0,function(){return!this}()||this._log.warn("not in strict mode: core patch"),this._isLocal=0===document.location.href.indexOf("file:"),this.config.hasOwnProperty("silent")&&(this.silent=CABLES.logSilent=this.config.silent),this.config.hasOwnProperty("doRequestAnimation")||(this.config.doRequestAnimation=!0),this.config.prefixAssetPath||(this.config.prefixAssetPath=""),this.config.prefixJsPath||(this.config.prefixJsPath=""),this.config.masterVolume||(this.config.masterVolume=1),this._variables={},this._variableListeners=[],this.vars={},t&&t.vars&&(this.vars=t.vars),this.cgl=new Bt(this),this.cgp=null,this.cgl.setCanvas(this.config.glCanvasId||this.config.glCanvas||"glcanvas"),!0===this.config.glCanvasResizeToWindow&&this.cgl.setAutoResize("window"),!0===this.config.glCanvasResizeToParent&&this.cgl.setAutoResize("parent"),this.loading.setOnFinishedLoading(this.config.onFinishedLoading),this.cgl.aborted&&(this.aborted=!0),this.cgl.silent&&(this.silent=!0),this.freeTimer.play(),this.exec(),this.aborted||(this.config.patch?this.deSerialize(this.config.patch):this.config.patchFile&&w(this.config.patchFile,(t,e)=>{const i=JSON.parse(e);if(t){return this._log.error("err",t),this._log.error("data",i),void this._log.error("data",i.msg)}this.deSerialize(i)}),this.timer.play()),console.log("made with https://cables.gl")};Wt.prototype.isPlaying=function(){return!this._paused},Wt.prototype.isRenderingOneFrame=function(){return this._renderOneFrame},Wt.prototype.renderOneFrame=function(){this._paused=!0,this._renderOneFrame=!0,this.exec(),this._renderOneFrame=!1},Wt.prototype.getFPS=function(){return console.log("deprecated getfps"),0},Wt.prototype.isEditorMode=function(){return!0===this.config.editorMode},Wt.prototype.pause=function(){cancelAnimationFrame(this._animReq),this.emitEvent("pause"),this._animReq=null,this._paused=!0,this.freeTimer.pause()},Wt.prototype.resume=function(){this._paused&&(cancelAnimationFrame(this._animReq),this._paused=!1,this.freeTimer.play(),this.emitEvent("resume"),this.exec())},Wt.prototype.setVolume=function(t){this.config.masterVolume=t;for(let e=0;e0||document.location.href.indexOf("cables.local")>0){const e=document.location.pathname.split("/");return"/assets/"+(t||e[e.length-1])+"/"}return this.config.hasOwnProperty("assetPath")?this.config.assetPath:"assets/"},Wt.prototype.getJsPath=function(){return this.config.hasOwnProperty("jsPath")?this.config.jsPath:"js/"},Wt.prototype.getFilePath=function(t){return this._isLocal&&!this.config.allowLocalFileAccess&&this.exitError("localAccess","Browser security forbids loading files directly without a webserver. Upload files to a server to work. use allowLocalFileAccess:true to ignore this."),t?0===(t=String(t)).indexOf("https:")||0===t.indexOf("http:")||0===t.indexOf("data:")?t:(t=t.replace("//","/"),this.config.prefixAssetPath+t+(this.config.suffixAssetPath||"")):t},Wt.prototype.clear=function(){for(this.emitEvent("patchClearStart"),this.cgl.TextureEffectMesh=null,this.animFrameOps.length=0,this.timer=new ft;this.ops.length>0;)this.deleteOp(this.ops[0].id);this.emitEvent("patchClearEnd")},Wt.getOpClass=function(t){const e=t.split(".");let i=null;try{return 2==e.length?i=window[e[0]][e[1]]:3==e.length?i=window[e[0]][e[1]][e[2]]:4==e.length?i=window[e[0]][e[1]][e[2]][e[3]]:5==e.length?i=window[e[0]][e[1]][e[2]][e[3]][e[4]]:6==e.length?i=window[e[0]][e[1]][e[2]][e[3]][e[4]][e[5]]:7==e.length?i=window[e[0]][e[1]][e[2]][e[3]][e[4]][e[5]][e[6]]:8==e.length?i=window[e[0]][e[1]][e[2]][e[3]][e[4]][e[5]][e[6]][e[7]]:9==e.length?i=window[e[0]][e[1]][e[2]][e[3]][e[4]][e[5]][e[6]][e[7]][e[8]]:10==e.length&&(i=window[e[0]][e[1]][e[2]][e[3]][e[4]][e[5]][e[6]][e[7]][e[8]][e[9]]),i}catch(t){return null}},Wt.prototype.createOp=function(t,e,i=null){let s=null,r="";try{if(-1===t.indexOf("Ops.")){const n=t;if(CABLES.OPS[n])r=CABLES.OPS[n].objName,s=new CABLES.OPS[n].f(this,r,e,n),s.opId=n;else{if(!i)throw new Error("could not find op by id: "+n);t=i,this._log.warn("could not find op by id: "+n)}}if(!s){r=t;const i=t.split(".");if(!Wt.getOpClass(r))throw this.emitEvent("criticalError",{title:"unknown op",text:"unknown op: "+r}),this._log.error("unknown op: "+r),new Error("unknown op: "+r);if(2==i.length?s=new window[i[0]][i[1]](this,r,e):3==i.length?s=new window[i[0]][i[1]][i[2]](this,r,e):4==i.length?s=new window[i[0]][i[1]][i[2]][i[3]](this,r,e):5==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]](this,r,e):6==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]](this,r,e):7==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]][i[6]](this,r,e):8==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]][i[6]][i[7]](this,r,e):9==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]][i[6]][i[7]][i[8]](this,r,e):10==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]][i[6]][i[7]][i[8]][i[9]](this,r,e):this._log.warn("parts.length",i.length),s){s.opId=null;for(const t in CABLES.OPS)CABLES.OPS[t].objName==r&&(s.opId=t)}}}catch(t){if(this._crashedOps.push(r),this.emitEvent("exceptionOp",t,r,s),!this.isEditorMode())throw this._log.error(t),this._log.error("[instancing error] "+r,t),CABLES.api&&CABLES.api.sendErrorReport(t),this.exitError("INSTANCE_ERR","Instancing Error "+r,t),new Error("instancing error "+r)}return s?(s.objName=r,s.patch=this):this._log.log("no op was created!?",t,e),s},Wt.prototype.addOp=function(t,e,i,s,r){const n=this.createOp(t,i,r);if(n){if((e=e||{}).hasOwnProperty("errors")&&delete e.errors,e.hasOwnProperty("error")&&delete e.error,e.subPatch=e.subPatch||0,n.uiAttr(e),n.onCreate&&n.onCreate(),n.hasOwnProperty("onAnimFrame")&&this.addOnAnimFrame(n),n.hasOwnProperty("onMasterVolumeChanged")&&this._volumeListeners.push(n),this._opIdCache[n.id])return void this._log.warn("opid with id "+n.id+" already exists in patch!");this.ops.push(n),this._opIdCache[n.id]=n,this.emitEvent("onOpAdd",n,s),n.init&&n.init()}else this._log.error("addop: no op.....");return n},Wt.prototype.addOnAnimFrame=function(t){for(let e=0;e0&&n.portsIn[0].isLinked()&&n.portsOut.length>0&&n.portsOut[0].isLinked()&&n.portsIn[0].getType()==n.portsOut[0].getType()&&n.portsIn[0].links[0]&&(o=n.portsIn[0].links[0].getOtherPort(n.portsIn[0]),a=n.portsOut[0].links[0].getOtherPort(n.portsOut[0]));const h=this.ops[r];h.removeLinks(),h.emitEvent("onDelete",this.ops[r]),this.onDelete&&(this._log.warn("deprecated this.onDelete",this.onDelete),this.onDelete(h)),this.ops.splice(r,1),this.emitEvent("onOpDelete",h,i),h.onDelete&&h.onDelete(i),h.cleanUp(),null!==o&&null!==a&&this.link(o.parent,o.getName(),a.parent,a.getName()),delete this._opIdCache[t];break}}s||this._log.warn("core patch deleteop: not found...")},Wt.prototype.getFrameNum=function(){return this._frameNum},Wt.prototype.emitOnAnimFrameEvent=function(t){t=t||this.timer.getTime();for(let e=0;e=500&&0!==this._lastFrameTime&&!this._frameWasdelayed)return this._lastFrameTime=0,setTimeout(this.exec.bind(this),500),this.emitEvent("renderDelayStart"),void(this._frameWasdelayed=!0);if(this._renderOneFrame||0===this.config.fpsLimit||i>this._frameInterval||this._frameWasdelayed){CABLES.now();this.renderFrame(),this._frameInterval&&(this._frameNext=e-i%this._frameInterval)}this._frameWasdelayed&&(this.emitEvent("renderDelayEnd"),this._frameWasdelayed=!1),this._renderOneFrame&&(this.onOneFrameRendered&&this.onOneFrameRendered(),this.emitEvent("renderedOneFrame"),this._renderOneFrame=!1),this.config.doRequestAnimation&&(this._animReq=requestAnimationFrame(this.exec.bind(this)))},Wt.prototype.link=function(t,e,i,s,r,n){if(!t)return void this._log.warn("link: op1 is null ");if(!i)return void this._log.warn("link: op2 is null");const o=t.getPort(e,r),a=i.getPort(s,r);if(o)if(a){if(!o.shouldLink(o,a)||!a.shouldLink(o,a))return!1;if(kt.canLink(o,a)){const t=new kt(this);return t.link(o,a),this.emitEvent("onLink",o,a,t,n),t}}else this._log.warn("port not found! "+s+" of "+i.name+"("+i.objName+")");else this._log.warn("port not found! "+e+"("+t.objName+")")},Wt.prototype.serialize=function(t){const e={};t=t||{},e.ops=[],e.settings=this.settings;for(const t in this.ops){const i=this.ops[t];e.ops.push(i.getSerialized())}return t.asObject?e:JSON.stringify(e)},Wt.prototype.getOpById=function(t){return this._opIdCache[t]},Wt.prototype.getOpsByName=function(t){const e=[];for(const i in this.ops)this.ops[i].name==t&&e.push(this.ops[i]);return e},Wt.prototype.getOpsByObjName=function(t){const e=[];for(const i in this.ops)this.ops[i].objName==t&&e.push(this.ops[i]);return e},Wt.prototype.loadLib=function(t){C("/ui/libs/"+t+".js",(t,e)=>{const i=document.createElement("script");i.type="text/javascript",i.text=e,document.getElementsByTagName("head")[0].appendChild(i)},"GET")},Wt.prototype.reloadOp=function(t,e){let i=0;const s=[],r=[];for(const e in this.ops)this.ops[e].objName==t&&r.push(this.ops[e]);for(let e=0;e200&&this._log.warn("long op init ",t.ops[s].objName,a)}for(const t in this.ops)this.ops[t].onLoadedValueSet&&(this.ops[t].onLoadedValueSet(this.ops[t]._origData),this.ops[t].onLoadedValueSet=null,this.ops[t]._origData=null),this.ops[t].emitEvent("loadedValueSet");if(t.ops)for(let e=0;e{this.loading.finished(s)},100),this.config.onPatchLoaded&&this.config.onPatchLoaded(this),this.emitEvent("patchLoadEnd",i,t,e)},Wt.prototype.profile=function(t){this.profiler=new Ht(this);for(const e in this.ops)this.ops[e].profile(t)},Wt.prototype.setVariable=function(t,e){void 0!==this._variables[t]?this._variables[t].setValue(e):this._log.warn("variable "+t+" not found!")},Wt.prototype._sortVars=function(){if(!this.isEditorMode())return;const t={};Object.keys(this._variables).sort((t,e)=>t.localeCompare(e,"en",{sensitivity:"base"})).forEach(e=>{t[e]=this._variables[e]}),this._variables=t},Wt.prototype.hasVar=function(t){return void 0!==this._variables[t]},Wt.prototype.setVarValue=function(t,e,i){return this.hasVar(t)?this._variables[t].setValue(e):(this._variables[t]=new zt(t,e,i),this._sortVars(),this.emitEvent("variablesChanged")),this._variables[t]},Wt.prototype.getVarValue=function(t,e){if(this._variables.hasOwnProperty(t))return this._variables[t].getValue()},Wt.prototype.getVar=function(t){if(this._variables.hasOwnProperty(t))return this._variables[t]},Wt.prototype.deleteVar=function(t){for(let e=0;e=i._param.minValue&&e<=i._param.maxValue)try{i.setValueAtTime?i.setValueAtTime(e,s.currentTime):i.value=e}catch(e){throw t.log("Possible AudioParam problem with tone.js op: ",e),e.printStackTrace&&e.printStackTrace(),e}else t.log("Warning: The value for an audio parameter is out of range!");else if(i.minValue&&i.maxValue)if(e>=i.minValue&&e<=i.maxValue)try{i.setValueAtTime?i.setValueAtTime(e,s.currentTime):i.value=e}catch(e){throw t.log("AudioParam has minValue / maxValue defined, and value is in range, but setting the value failed! ",e),e.printStackTrace&&e.printStackTrace(),e}else t.log("Warning: The value for an audio parameter is out of range!");else try{i.setValueAtTime?i.setValueAtTime(e,s.currentTime):i.value=e}catch(e){throw t.log("Possible AudioParam problem (without minValue / maxValue): ",e),e.printStackTrace&&e.printStackTrace(),e}if(n.webAudio.previousAudioInNode&&n.webAudio.previousAudioInNode.disconnect){try{n.webAudio.previousAudioInNode.disconnect(i)}catch(e){throw t.log("Could not disconnect previous audio node: ",e),e}n.webAudio.previousAudioInNode=void 0}}else n.webAudio.previousAudioInNode},n},loadAudioFile:function(t,e,i,s,r){const n=jt.createAudioContext();let o=null;(r||void 0===r)&&(o=t.loading.start("audio",e),t.isEditorMode()&&gui.jobs().start({id:"loadaudio"+o,title:" loading audio ("+e+")"}));const a=new XMLHttpRequest;e&&(a.open("GET",e,!0),a.responseType="arraybuffer",a.onload=function(){t.loading.finished(o),t.isEditorMode()&&gui.jobs().finish("loadaudio"+o),n.decodeAudioData(a.response,i,s)},a.send())},isValidToneTime:function(t){try{new Tone.Time(t)}catch(t){return!1}return!0},isValidToneNote:function(t){try{Tone.Frequency(t)}catch(t){return!1}return!0}},Kt=function(t,e,i){this._patch=t,this.connector=i,this._log=new a("PatchConnectionReceiver")};Kt.prototype._addOp=function(t){let e=null;t.vars.uiAttribs&&(e=t.vars.uiAttribs);const i=this._patch.addOp(t.vars.objName,e,t.vars.opId,!0);i&&(i.id=t.vars.opId,t.vars.portsIn&&t.vars.portsIn.forEach(t=>{const e=i.getPortByName(t.name);e&&e.set(t.value)}))},Kt.prototype._receive=function(t){let e={};if(e=t.hasOwnProperty("event")?t:JSON.parse(t.data),e.event==u.PACO_OP_CREATE){if(this._patch.getOpById(e.vars.opId))return;this._log.verbose("op create:",e.vars.objName),window.gui?gui.serverOps.loadOpLibs(e.vars.objName,()=>{this._addOp(e)}):this._addOp(e)}else if(e.event==u.PACO_DESERIALIZE)e.vars.json&&(window.gui?gui.serverOps.loadProjectDependencies(e.vars.json,()=>{this._patch.deSerialize(e.vars.json,e.vars.genIds)}):this._patch.deSerialize(e.vars.json,e.vars.genIds));else if(e.event==u.PACO_LOAD)this._log.verbose("PACO load patch....."),this._patch.clear(),window.gui?gui.serverOps.loadProjectDependencies(JSON.parse(e.vars.patch),()=>{this._patch.deSerialize(e.vars.patch)}):this._patch.deSerialize(e.vars.patch);else if(e.event==u.PACO_CLEAR)this._patch.clear(),this._log.log("clear");else if(e.event==u.PACO_OP_DELETE){this._log.verbose("op delete",e.vars.objName);this._patch.getOpById(e.vars.op);this._patch.deleteOp(e.vars.op,!0)}else if(e.event==u.PACO_OP_ENABLE){const t=this._patch.getOpById(e.vars.op);t&&(t.enabled=!0)}else if(e.event==u.PACO_OP_DISABLE){const t=this._patch.getOpById(e.vars.op);t&&(t.enabled=!1)}else if(e.event==u.PACO_UIATTRIBS){const t=this._patch.getOpById(e.vars.op);t&&t.setUiAttrib(e.vars.uiAttribs)}else if(e.event==u.PACO_UNLINK){const t=this._patch.getOpById(e.vars.op1),i=this._patch.getOpById(e.vars.op2);if(!t||!i)return;const s=t.getPort(e.vars.port1),r=i.getPort(e.vars.port2);s&&r?s.removeLinkTo(r):this._log.warn("paco unlink could not find port...")}else if(e.event==u.PACO_LINK){const t=this._patch.getOpById(e.vars.op1),i=this._patch.getOpById(e.vars.op2);t&&i&&this._patch.link(t,e.vars.port1,i,e.vars.port2)}else if(e.event==u.PACO_VALUECHANGE){if("+ create new one"===e.vars.v)return;const t=this._patch.getOpById(e.vars.op);if(t){const i=t.getPort(e.vars.port);i&&i.set(e.vars.v)}}else if(e.event==u.PACO_VARIABLES){const t=this._patch.getOpById(e.vars.opId);t&&t.varName&&t.varName.set(e.vars.varName)}else if(e.event==u.PACO_TRIGGERS){const t=this._patch.getOpById(e.vars.opId);t&&t.varName&&t.varName.set(e.vars.varName)}else if(e.event==u.PACO_PORT_SETVARIABLE){const t=this._patch.getOpById(e.vars.opId);if(t){const i=t.getPortByName(e.vars.portName);i&&i.setVariable(e.vars.variableName)}}else if(e.event==u.PACO_PORT_SETANIMATED){const t=this._patch.getOpById(e.vars.opId);if(t){t.portsIn[e.vars.portIndex]&&e.vars.hasOwnProperty("targetState")&&this._patch.emitEvent("pacoPortValueSetAnimated",t,e.vars.portIndex,e.vars.targetState,e.vars.defaultValue)}}else if(e.event==u.PACO_PORT_ANIM_UPDATED){const t=this._patch.getOpById(e.vars.opId);if(t){const i=t.getPortByName(e.vars.portName);if(i){const t=i.getSerialized();t.anim=e.vars.anim,i.anim=null,i.deSerializeSettings(t),this._patch.emitEvent("pacoPortAnimUpdated",i)}}}else this._log.warn("unknown patchConnectionEvent!",t)};const Qt=function(t){this.connectors=[],this.paused=!1,t.addEventListener("onOpDelete",t=>{this.send(CABLES.PACO_OP_DELETE,{op:t.id,objName:t.objName})}),t.addEventListener("patchClearStart",()=>{this.paused=!0}),t.addEventListener("patchClearEnd",()=>{this.paused=!1}),t.addEventListener("patchLoadStart",()=>{this.paused=!0}),t.addEventListener("patchLoadEnd",(t,e,i)=>{this.paused=!1,this.send(CABLES.PACO_DESERIALIZE,{json:e,genIds:i})}),t.addEventListener("onOpAdd",t=>{const e=[];t.portsIn.forEach(t=>{const i={id:t.id,name:t.name,value:t.get()};e.push(i)});let i={};t.uiAttribs&&(i={...t.uiAttribs}),this.send(CABLES.PACO_OP_CREATE,{opId:t.id,objName:t.objName,uiAttribs:i,portsIn:e})}),t.addEventListener("onUnLink",(t,e)=>{this.send(CABLES.PACO_UNLINK,{op1:t.parent.id,op2:e.parent.id,port1:t.getName(),port2:e.getName()})}),t.addEventListener("onUiAttribsChange",(t,e)=>{e&&(delete e.extendTitle,delete e.history,delete e.translate,Object.keys(e).length>0&&this.send(CABLES.PACO_UIATTRIBS,{op:t.id,uiAttribs:e}))}),t.addEventListener("opVariableNameChanged",(t,e)=>{const i={opId:t.id,varName:e};this.send(CABLES.PACO_VARIABLES,i)}),t.addEventListener("opTriggerNameChanged",(t,e)=>{const i={opId:t.id,varName:e};this.send(CABLES.PACO_TRIGGERS,i)}),t.addEventListener("onLink",(t,e)=>{this.send(CABLES.PACO_LINK,{op1:t.parent.id,op2:e.parent.id,port1:t.name,port2:e.name})}),t.addEventListener("portSetVariable",(t,e,i)=>{const s={opId:t.id,portName:e.name,variableName:i};this.send(CABLES.PACO_PORT_SETVARIABLE,s)}),t.addEventListener("portAnimUpdated",(t,e,i)=>{if(t&&e){const s={opId:t.id,portName:e.name,anim:i.getSerialized()};this.send(CABLES.PACO_PORT_ANIM_UPDATED,s)}})};Qt.prototype.send=function(t,e){if(!this.paused&&(t!==CABLES.PACO_VALUECHANGE||"+ create new one"!==e.v))for(let i=0;i0&&"endif"!==i[2]&&"else"!==i[2]&&"elif"!==i[2]){if(a=h.pop(),e(" pop ("+h.length+"): "+JSON.stringify(a)),Preprocessor.ALL.lastIndex=i.index,null===(s=Preprocessor.ALL.exec(this.source)))throw new Error("Illegal #"+i[2]+": "+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");h.push(o={include:a.include,index:a.index,lastIndex:Preprocessor.ALL.lastIndex}),e(" push ("+h.length+"): "+JSON.stringify(o))}else switch(i[2]){case"ifdef":case"ifndef":case"if":if(Preprocessor.IF.lastIndex=i.index,null===(s=Preprocessor.IF.exec(this.source)))throw new Error("Illegal #"+i[2]+": "+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");e(" test: "+s[2]),e(" defines "+JSON.stringify(t)),n="ifdef"===s[1]?void 0!==t[s[2]]:"ifndef"===s[1]?void 0===t[s[2]]:Preprocessor.evaluate(t,s[2]),l=!n,e(" value: "+n+", isSkip: "+l),h.push(o={include:n,index:i.index,lastIndex:Preprocessor.IF.lastIndex}),e(" push ("+h.length+"): "+JSON.stringify(o));break;case"endif":case"else":case"elif":if(Preprocessor.ENDIF.lastIndex=i.index,null===(s=Preprocessor.ENDIF.exec(this.source)))throw new Error("Illegal #"+i[2]+': "'+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");if(0===h.length)throw new Error("Unexpected #"+s[1]+': "'+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");a=h.pop(),e(" pop ("+h.length+"): "+JSON.stringify(a)),n=this.preserveLineNumbers?this.source.substring(a.index,a.lastIndex).replace(NOT_LINE_ENDING,"")+this.source.substring(a.lastIndex,i.index)+this.source.substring(i.index,Preprocessor.ENDIF.lastIndex).replace(NOT_LINE_ENDING,""):this.source.substring(a.lastIndex,i.index),a.include?(e(" incl: "+Preprocessor.nlToStr(n)+", 0-"+a.index+" + "+n.length+" bytes + "+Preprocessor.ENDIF.lastIndex+"-"+this.source.length),this.source=this.source.substring(0,a.index)+n+this.source.substring(Preprocessor.ENDIF.lastIndex)):this.preserveLineNumbers?(e(" excl(\\n): "+Preprocessor.nlToStr(n)+", 0-"+a.index+" + "+Preprocessor.ENDIF.lastIndex+"-"+this.source.length),n=n.replace(NOT_LINE_ENDING,""),this.source=this.source.substring(0,a.index)+n+this.source.substring(Preprocessor.ENDIF.lastIndex)):(e(" excl: "+Preprocessor.nlToStr(n)+", 0-"+a.index+" + "+Preprocessor.ENDIF.lastIndex+"-"+this.source.length),n="",this.source=this.source.substring(0,a.index)+this.source.substring(Preprocessor.ENDIF.lastIndex)),""===this.source&&e(" result empty"),l=!1,Preprocessor.EXPR.lastIndex=a.index+n.length,e(" continue at "+Preprocessor.EXPR.lastIndex),"else"!==s[1]&&"elif"!==s[1]||(n="else"===s[1]?!a.include:Preprocessor.evaluate(t,s[2]),l=!n,e(" isSkip: "+l),h.push(o={include:n,index:Preprocessor.EXPR.lastIndex,lastIndex:Preprocessor.EXPR.lastIndex}),e(" push ("+h.length+"): "+JSON.stringify(o)));break;case"define":if(Preprocessor.DEFINE.lastIndex=i.index,Preprocessor.VAR.lastIndex=i.index,Preprocessor.FUNCTION.lastIndex=i.index,Preprocessor.BOOLVAR.lastIndex=i.index,null===(s=Preprocessor.DEFINE.exec(this.source)))throw new Error("Illegal #"+i[2]+": "+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");var c,u,g;if(e(' def: "'+s[1]+'"'),null!==(r=Preprocessor.VAR.exec(this.source)))g="var",c=r[1],u=r[2],e(" match3(var): "+JSON.stringify(r));else if(null!==(r=Preprocessor.FUNCTION.exec(this.source)))g="function",c=r[1],u=r[2],e(" match3(function): "+JSON.stringify(r));else{if(null===(r=Preprocessor.BOOLVAR.exec(this.source)))throw new Error("Illegal #"+i[2]+": "+this.source.substring(i.index,i.index+this.errorSourceAhead)+"...");g="var",c=r[1],u=!0,e(" match3(boolvar): "+JSON.stringify(r))}e(" type: "+g),e(" identifier: "+c),e(" value: "+u),t[c]={type:g,value:u},e(" defines "+JSON.stringify(t));var p="";this.preserveLineNumbers&&(p=this.source.substring(i.index,Preprocessor.DEFINE.lastIndex).replace(NOT_LINE_ENDING,"")),this.source=this.source.substring(0,i.index)+_+p+this.source.substring(Preprocessor.DEFINE.lastIndex),Preprocessor.EXPR.lastIndex=i.index,e(" continue at "+Preprocessor.EXPR.lastIndex)}}return h.length>0&&e("Still on stack ("+h.length+"): "+JSON.stringify(h.pop())),this.source};var _unused_webpack_default_export=Preprocessor},function(t,e,i){"use strict";i.r(e);var s={};i.r(s),i.d(s,"getShortOpName",(function(){return p})),i.d(s,"shuffleArray",(function(){return _})),i.d(s,"shortId",(function(){return m})),i.d(s,"uuid",(function(){return T})),i.d(s,"generateUUID",(function(){return A})),i.d(s,"simpleId",(function(){return x})),i.d(s,"smoothStep",(function(){return v})),i.d(s,"smootherStep",(function(){return y})),i.d(s,"clamp",(function(){return I})),i.d(s,"map",(function(){return S})),i.d(s,"cacheBust",(function(){return R})),i.d(s,"copyArray",(function(){return P})),i.d(s,"basename",(function(){return O})),i.d(s,"jsonp",(function(){return F})),i.d(s,"ajaxSync",(function(){return C})),i.d(s,"ajax",(function(){return w})),i.d(s,"request",(function(){return M})),i.d(s,"keyCodeToName",(function(){return U})),i.d(s,"UTILS",(function(){return g}));var r={};i.r(r),i.d(r,"base64Chars",(function(){return k})),i.d(r,"base64lookup",(function(){return D})),i.d(r,"b64encTypesArray",(function(){return G})),i.d(r,"b64decTypedArray",(function(){return H}));var n={};i.r(n),i.d(n,"easeExpoIn",(function(){return j})),i.d(n,"easeExpoOut",(function(){return K})),i.d(n,"easeExpoInOut",(function(){return Q})),i.d(n,"easeCubicIn",(function(){return q})),i.d(n,"easeCubicOut",(function(){return J})),i.d(n,"easeCubicInOut",(function(){return Z})),i.d(n,"ANIM",(function(){return X})),i.d(n,"Anim",(function(){return $})),i.d(n,"TL",(function(){return tt}));var o={};i.r(o),i.d(o,"PatchConnectionReceiver",(function(){return Kt})),i.d(o,"PatchConnectionSender",(function(){return Qt})),i.d(o,"PatchConnectorBroadcastChannel",(function(){return qt}));class a{constructor(t){this._logs=[],this.initiator=t}stack(t){console.error("["+this.initiator+"] ",t),console.log((new Error).stack),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"error",t)}groupCollapsed(t){console.groupCollapsed("["+this.initiator+"] "+t)}table(t){console.table(t)}groupEnd(){console.groupEnd()}error(t){console.error("["+this.initiator+"]",...arguments),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"error",arguments)}info(t){console.error("["+this.initiator+"]",...arguments),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"info",arguments)}warn(t){console.warn("["+this.initiator+"]",...arguments),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"warn",arguments)}verbose(){(CABLES.UI&&CABLES.UI.logFilter.shouldPrint(this.initiator,...arguments)||!CABLES.logSilent)&&console.log("["+this.initiator+"]",...arguments),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"verbose",arguments)}log(t){(CABLES.UI&&CABLES.UI.logFilter.shouldPrint(this.initiator,...arguments)||!CABLES.logSilent)&&console.log("["+this.initiator+"]",...arguments),window.gui&&window.gui.emitEvent("coreLogEvent",this.initiator,"log",arguments)}userInteraction(t){}}const h={EASINGS:["linear","absolute","smoothstep","smootherstep","Cubic In","Cubic Out","Cubic In Out","Expo In","Expo Out","Expo In Out","Sin In","Sin Out","Sin In Out","Quart In","Quart Out","Quart In Out","Quint In","Quint Out","Quint In Out","Back In","Back Out","Back In Out","Elastic In","Elastic Out","Bounce In","Bounce Out"],EASING_LINEAR:0,EASING_ABSOLUTE:1,EASING_SMOOTHSTEP:2,EASING_SMOOTHERSTEP:3,EASING_CUBICSPLINE:4,EASING_CUBIC_IN:5,EASING_CUBIC_OUT:6,EASING_CUBIC_INOUT:7,EASING_EXPO_IN:8,EASING_EXPO_OUT:9,EASING_EXPO_INOUT:10,EASING_SIN_IN:11,EASING_SIN_OUT:12,EASING_SIN_INOUT:13,EASING_BACK_IN:14,EASING_BACK_OUT:15,EASING_BACK_INOUT:16,EASING_ELASTIC_IN:17,EASING_ELASTIC_OUT:18,EASING_BOUNCE_IN:19,EASING_BOUNCE_OUT:21,EASING_QUART_IN:22,EASING_QUART_OUT:23,EASING_QUART_INOUT:24,EASING_QUINT_IN:25,EASING_QUINT_OUT:26,EASING_QUINT_INOUT:27},l={OP_PORT_TYPE_VALUE:0,OP_PORT_TYPE_FUNCTION:1,OP_PORT_TYPE_OBJECT:2,OP_PORT_TYPE_TEXTURE:2,OP_PORT_TYPE_ARRAY:3,OP_PORT_TYPE_DYNAMIC:4,OP_PORT_TYPE_STRING:5,OP_VERSION_PREFIX:"_v"},c={PORT_DIR_IN:0,PORT_DIR_OUT:1},u={PACO_CLEAR:0,PACO_VALUECHANGE:1,PACO_OP_DELETE:2,PACO_UNLINK:3,PACO_LINK:4,PACO_LOAD:5,PACO_OP_CREATE:6,PACO_OP_ENABLE:7,PACO_OP_DISABLE:8,PACO_UIATTRIBS:9,PACO_VARIABLES:10,PACO_TRIGGERS:11,PACO_PORT_SETVARIABLE:12,PACO_PORT_SETANIMATED:13,PACO_PORT_ANIM_UPDATED:14,PACO_DESERIALIZE:15},g={float32Concat:function(t,e){t instanceof Float32Array||(t=new Float32Array(t)),e instanceof Float32Array||(e=new Float32Array(e));const i=new Float32Array(t.length+e.length);return i.set(t),i.set(e,t.length),i}},p=function(t){let e=t.split(".")[t.split(".").length-1];if(e.indexOf(l.OP_VERSION_PREFIX)>0){const t=e.split(l.OP_VERSION_PREFIX)[1];e=e.substring(0,e.length-(l.OP_VERSION_PREFIX+t).length)}return e},_=function(t){for(let e=t.length-1;e>0;e--){const i=Math.floor(Math.seededRandom()*(e+1)),s=t[e];t[e]=t[i],t[i]=s}return t},d={},f=function(){let t=Math.random().toString(36).substr(2,9);return d.hasOwnProperty(t)&&(t=f()),d[t]=!0,t},m=f,E=function(){let t=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const i=(t+16*Math.random())%16|0;return t=Math.floor(t/16),("x"==e?i:3&i|8).toString(16)})},T=E,A=E;let b=0;const x=function(){return b++,b},v=function(t){const e=Math.max(0,Math.min(1,(t-0)/1));return t=e*e*(3-2*e)},y=function(t){const e=Math.max(0,Math.min(1,(t-0)/1));return t=e*e*e*(e*(6*e-15)+10)},I=function(t,e,i){return Math.min(Math.max(t,e),i)},S=function(t,e,i,s,r,n){if(t>=i)return r;if(t<=e)return s;let o=!1;const a=Math.min(e,i),h=Math.max(e,i);a!=e&&(o=!0);let l=!1;const c=Math.min(s,r),u=Math.max(s,r);c!=s&&(l=!0);let g=0,p=0;return g=o?(h-t)*(u-c)/(h-a):(t-a)*(u-c)/(h-a),p=l?u-g:g+c,n?1==n?s+(t=Math.max(0,Math.min(1,(p-s)/(r-s))))*t*(3-2*t)*(r-s):2==n?s+(t=Math.max(0,Math.min(1,(p-s)/(r-s))))*t*t*(t*(6*t-15)+10)*(r-s):p:p};Math.randomSeed=1,Math.seededRandom=function(t,e){0===Math.randomSeed&&(Math.randomSeed=999*Math.random()),t=t||1,e=e||0,Math.randomSeed=(9301*Math.randomSeed+49297)%233280;return e+Math.randomSeed/233280*(t-e)},g.arrayWriteToEnd=function(t,e){for(let e=1;e-1?t+="&":t+="?",t+"cb="+CABLES.uuid()},P=function(t,e){if(!t)return null;(e=e||[]).length=t.length;for(let i=0;i0){let t=i[i.length-1].split("?");e=t[0],t=e.split("."),e=t[0]}return e};let N=null;const F=function(t,e){N=N||0,N++;const i=N;CABLES["jsonpFunc"+i]=function(t){e(!1,t)};let s="?";t.indexOf(s)>-1&&(s="&");const r=document.createElement("script");r.setAttribute("src",t+s+"callback=CABLES.jsonpFunc"+i),document.body.appendChild(r)},C=function(t,e,i,s,r){M({url:t,cb:e,method:i,data:s,contenttype:r,sync:!0})},w=function(t,e,i,s,r,n,o={}){M({url:t,cb:e,method:i,data:s,contenttype:r,sync:!1,jsonP:n,headers:o})},M=function(t){let e;t.hasOwnProperty("asynch")||(t.asynch=!0);try{e=new XMLHttpRequest}catch(t){}if(e.onreadystatechange=function(){4==e.readyState&&t.cb&&(200==e.status||0==e.status?t.cb(!1,e.responseText,e):t.cb(!0,e.responseText,e))},e.addEventListener("progress",t=>{}),e.open(t.method?t.method.toUpperCase():"GET",t.url,!t.sync),"object"==typeof t.headers){const i=Object.keys(t.headers);for(let s=0;sthis._cgl.maxTexSize||e>this._cgl.maxTexSize)&&this._log.error("texture size too big! "+t+"x"+e+" / max: "+this._cgl.maxTexSize),t=Math.min(t,this._cgl.maxTexSize),e=Math.min(e,this._cgl.maxTexSize),t=Math.floor(t),e=Math.floor(e),this.width==t&&this.height==e)return;this.width=t,this.height=e,this.deleted=!1,this.shortInfoString=this.getInfoOneLine(),this._cgl.gl.bindTexture(this.texTarget,this.tex),this._cgl.profileData.profileTextureResize++;if(this._cgl.patch.config.canvas.forceTextureNearest&&(this.filter=B.FILTER_NEAREST),1!=this._cgl.glVersion||this.textureType!=B.TYPE_FLOAT||this.filter!=B.FILTER_LINEAR||this._cgl.gl.getExtension("OES_texture_float_linear")||(console.warn("this graphics card does not support floating point texture linear interpolation! using NEAREST"),this.filter=B.FILTER_NEAREST),this.textureType==B.TYPE_FLOAT)if(1==this._cgl.glVersion)if(this._cgl.glUseHalfFloatTex){const i=this._cgl.gl.getExtension("OES_texture_half_float");this._cgl.gl.getExtension("EXT_color_buffer_half_float");if(!i)throw new Error("no half float texture extension");this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,t,e,0,this._cgl.gl.RGBA,i.HALF_FLOAT_OES,null)}else this._cgl.gl.getExtension("OES_texture_float"),this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,t,e,0,this._cgl.gl.RGBA,this._cgl.gl.FLOAT,null);else if(this._cgl.glUseHalfFloatTex){if(!this._cgl.gl.getExtension("EXT_color_buffer_half_float"))throw new Error("no half float texture extension");console.log("half float",this._cgl.gl.RGBA16F,this._cgl.gl.HALF_FLOAT),console.log("half float",this._cgl.gl.HALF_FLOAT),console.log("half float",this._cgl.gl.RGBA16F),this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,t,e,0,this._cgl.gl.RGBA,this._cgl.gl.HALF_FLOAT,null)}else this._cgl.gl.getExtension("EXT_color_buffer_float"),this._cgl.gl.getExtension("EXT_color_buffer_float_linear"),this._cgl.gl.getExtension("OES_texture_float_linear"),this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA32F,t,e,0,this._cgl.gl.RGBA,this._cgl.gl.FLOAT,null);else if(this.textureType==B.TYPE_DEPTH)if(1==this._cgl.glVersion){const i=this._cgl.gl.DEPTH_COMPONENT;this._cgl.gl.texImage2D(this.texTarget,0,i,t,e,0,this._cgl.gl.DEPTH_COMPONENT,this._cgl.gl.UNSIGNED_SHORT,null)}else{const i=this._cgl.gl.DEPTH_COMPONENT32F;this._cgl.gl.texImage2D(this.texTarget,0,i,t,e,0,this._cgl.gl.DEPTH_COMPONENT,this._cgl.gl.FLOAT,null)}else this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,t,e,0,this._cgl.gl.RGBA,this._cgl.gl.UNSIGNED_BYTE,null);this._setFilter(),this.updateMipMap(),this._cgl.gl.bindTexture(this.texTarget,null)},B.prototype.initFromData=function(t,e,i,s,r){if(this.filter=s,this.wrap=r,null==s&&(this.filter=B.FILTER_LINEAR),null==r&&(this.wrap=B.WRAP_CLAMP_TO_EDGE),this.width=e,this.height=i,this._fromData=!0,this.deleted=!1,this.height>this._cgl.maxTexSize||this.width>this._cgl.maxTexSize){const t=CGL.Texture.getTempTexture(this._cgl);return this.width=t.width,this.height=t.height,this.tex=t.tex,void this._log.error("[cgl_texture] texture size to big!!!",this.width,this.height,this._cgl.maxTexSize)}this.flip&&this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_FLIP_Y_WEBGL,this.flip),this._cgl.gl.bindTexture(this.texTarget,this.tex),this.textureType==B.TYPE_FLOAT?this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA32F,e,i,0,this._cgl.gl.RGBA,this._cgl.gl.FLOAT,t):this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,e,i,0,this._cgl.gl.RGBA,this._cgl.gl.UNSIGNED_BYTE,t),this._setFilter(),this.updateMipMap(),this.flip&&this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_FLIP_Y_WEBGL,!1),this._cgl.gl.bindTexture(this.texTarget,null)},B.prototype.updateMipMap=function(){2!=this._cgl.glVersion&&!this.isPowerOfTwo()||this.filter!=B.FILTER_MIPMAP||(this._cgl.gl.generateMipmap(this.texTarget),this._cgl.profileData.profileGenMipMap++)},B.prototype.initTexture=function(t,e){if(this._cgl.printError("before initTexture"),this._cgl.checkFrameStarted("texture inittexture"),this._fromData=!1,this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,this.unpackAlpha),t.width&&(this.width=t.width),t.height&&(this.height=t.height),e&&(this.filter=e),t.height>this._cgl.maxTexSize||t.width>this._cgl.maxTexSize){const e=CGL.Texture.getTempTexture(this._cgl);return this.width=e.width,this.height=e.height,this.tex=e.tex,void this._log.error("[cgl_texture] texture size to big!!!",t.width,t.height,this._cgl.maxTexSize)}this._cgl.gl.bindTexture(this.texTarget,this.tex),this.deleted=!1,this.flipped=!this.flip,this.flipped&&this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_FLIP_Y_WEBGL,this.flipped),this._cgl.gl.texImage2D(this.texTarget,0,this._cgl.gl.RGBA,this._cgl.gl.RGBA,this._cgl.gl.UNSIGNED_BYTE,t),this._setFilter(),this.updateMipMap(),this._cgl.gl.bindTexture(this.texTarget,null),this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),this.flipped&&this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_FLIP_Y_WEBGL,!1),this.getInfoOneLine(),this._cgl.printError("initTexture")},B.prototype.delete=function(){this.loading||(this.deleted=!0,this.width=0,this.height=0,this._cgl.profileData.profileTextureDelete++,this._cgl.gl.deleteTexture(this.tex),this.image=null,this.tex=null)},B.prototype.isPowerOfTwo=function(){return B.isPowerOfTwo(this.width)&&B.isPowerOfTwo(this.height)},B.prototype.printInfo=function(){console.log(this.getInfo())},B.prototype.getInfoReadable=function(){const t=this.getInfo();let e="";t.name=t.name.substr(0,t.name.indexOf("?rnd="));for(const i in t)e+="* "+i+": **"+t[i]+"**\n";return e},B.prototype.getInfoOneLine=function(){let t=this.width+"x"+this.height;return this.textureType===CGL.Texture.TYPE_FLOAT?t+=" 32bit":t+=" 8bit",this.filter===CGL.Texture.FILTER_NEAREST&&(t+=" nearest"),this.filter===CGL.Texture.FILTER_LINEAR&&(t+=" linear"),this.filter===CGL.Texture.FILTER_MIPMAP&&(t+=" mipmap"),this.wrap===CGL.Texture.WRAP_CLAMP_TO_EDGE&&(t+=" clamp"),this.wrap===CGL.Texture.WRAP_REPEAT&&(t+=" repeat"),this.wrap===CGL.Texture.WRAP_MIRRORED_REPEAT&&(t+=" repeatmir"),this.shortInfoString=t,t},B.prototype.getInfo=function(){return B.getTexInfo(this)},B.prototype._setFilter=function(){if(this._cgl.printError("before _setFilter"),this._fromData||this._cgl.gl.pixelStorei(this._cgl.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,this.unpackAlpha),this.shadowMap&&(this._cgl.gl.texParameteri(this._cgl.gl.TEXTURE_2D,this._cgl.gl.TEXTURE_COMPARE_MODE,this._cgl.gl.COMPARE_REF_TO_TEXTURE),this._cgl.gl.texParameteri(this._cgl.gl.TEXTURE_2D,this._cgl.gl.TEXTURE_COMPARE_FUNC,this._cgl.gl.LEQUAL)),this.textureType==B.TYPE_FLOAT&&this.filter==B.FILTER_MIPMAP&&(this.filter=B.FILTER_LINEAR,this._log.stack("texture: HDR and mipmap filtering at the same time is not possible")),1!=this._cgl.glVersion||this.isPowerOfTwo()){if(this.wrap==B.WRAP_CLAMP_TO_EDGE?(this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_S,this._cgl.gl.CLAMP_TO_EDGE),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_T,this._cgl.gl.CLAMP_TO_EDGE)):this.wrap==B.WRAP_REPEAT?(this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_S,this._cgl.gl.REPEAT),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_T,this._cgl.gl.REPEAT)):this.wrap==B.WRAP_MIRRORED_REPEAT&&(this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_S,this._cgl.gl.MIRRORED_REPEAT),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_T,this._cgl.gl.MIRRORED_REPEAT)),this.filter==B.FILTER_NEAREST)this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MAG_FILTER,this._cgl.gl.NEAREST),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MIN_FILTER,this._cgl.gl.NEAREST);else if(this.filter==B.FILTER_LINEAR)this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MIN_FILTER,this._cgl.gl.LINEAR),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MAG_FILTER,this._cgl.gl.LINEAR);else{if(this.filter!=B.FILTER_MIPMAP)throw this._log.log("unknown texture filter!",this.filter),new Error("unknown texture filter!"+this.filter);this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MAG_FILTER,this._cgl.gl.LINEAR),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MIN_FILTER,this._cgl.gl.LINEAR_MIPMAP_LINEAR)}if(this.anisotropic){const t=this._cgl.gl.getExtension("EXT_texture_filter_anisotropic");if(t){const e=this._cgl.gl.getParameter(t.MAX_TEXTURE_MAX_ANISOTROPY_EXT);this._cgl.gl.texParameterf(this._cgl.gl.TEXTURE_2D,t.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(e,this.anisotropic))}}}else this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MAG_FILTER,this._cgl.gl.NEAREST),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_MIN_FILTER,this._cgl.gl.NEAREST),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_S,this._cgl.gl.CLAMP_TO_EDGE),this._cgl.gl.texParameteri(this.texTarget,this._cgl.gl.TEXTURE_WRAP_T,this._cgl.gl.CLAMP_TO_EDGE),this.filter=B.FILTER_NEAREST,this.wrap=B.WRAP_CLAMP_TO_EDGE;this.getInfoOneLine(),this._cgl.printError("_setFilter")},B.load=function(t,e,i,s){if(!e)return i({error:!0});let r=null;t.patch.loading.existByName(e)||(r=t.patch.loading.start("texture",e));const n=new B(t);return n.name=e,t.patch.isEditorMode()&&gui.jobs().start({id:"loadtexture"+r,title:"loading texture "+CABLES.basename(e)}),n.image=new Image,n.image.crossOrigin="anonymous",n.loading=!0,s&&s.hasOwnProperty("filter")&&(n.filter=s.filter),s&&s.hasOwnProperty("flip")&&(n.flip=s.flip),s&&s.hasOwnProperty("wrap")&&(n.wrap=s.wrap),s&&s.hasOwnProperty("anisotropic")&&(n.anisotropic=s.anisotropic),s&&s.hasOwnProperty("unpackAlpha")&&(n.unpackAlpha=s.unpackAlpha),n.image.onabort=n.image.onerror=s=>{console.warn("[cgl.texture.load] error loading texture",e,s),n.loading=!1,r&&t.patch.loading.finished(r);i&&i({error:!0},n),t.patch.isEditorMode()&&gui.jobs().finish("loadtexture"+r)},n.image.onload=function(e){t.addNextFrameOnceCallback(()=>{n.initTexture(n.image),r&&t.patch.loading.finished(r),n.loading=!1,t.patch.isEditorMode()&&gui.jobs().finish("loadtexture"+r),i&&i(null,n)})},n.image.src=e,n},B.getTempTexture=function(t){return t||console.error("[getTempTexture] no cgl!"),t.tempTexture||(t.tempTexture=B.getTemporaryTexture(t,256,B.FILTER_LINEAR,B.REPEAT)),t.tempTexture},B.getEmptyTexture=function(t,e){if(e)return B.getEmptyTextureFloat(t);if(t||console.error("[getEmptyTexture] no cgl!"),t.tempTextureEmpty)return t.tempTextureEmpty;t.tempTextureEmpty=new B(t,{name:"emptyTexture"});const i=new Uint8Array(256).fill(0);for(let t=0;t<256;t+=4)i[t+3]=0;return t.tempTextureEmpty.initFromData(i,8,8,B.FILTER_NEAREST,B.WRAP_REPEAT),t.tempTextureEmpty},B.getEmptyTextureFloat=function(t){if(t||console.error("[getEmptyTextureFloat] no cgl!"),t.tempTextureEmptyFloat)return t.tempTextureEmptyFloat;t.tempTextureEmptyFloat=new B(t,{name:"emptyTexture",isFloatingPointTexture:!0});const e=new Float32Array(256).fill(1);for(let t=0;t<256;t+=4)e[t+3]=0;return t.tempTextureEmptyFloat.initFromData(e,8,8,B.FILTER_NEAREST,B.WRAP_REPEAT),t.tempTextureEmptyFloat},B.getRandomTexture=function(t){if(t||console.error("[getRandomTexture] no cgl!"),t.randomTexture)return t.randomTexture;const e=new Uint8Array(262144);for(let t=0;t<65536;t++)e[4*t+0]=255*Math.random(),e[4*t+1]=255*Math.random(),e[4*t+2]=255*Math.random(),e[4*t+3]=255;return t.randomTexture=new B(t),t.randomTexture.initFromData(e,256,256,B.FILTER_NEAREST,B.WRAP_REPEAT),t.randomTexture},B.getBlackTexture=function(t){if(t||this._log.error("[getBlackTexture] no cgl!"),t.blackTexture)return t.blackTexture;const e=new Uint8Array(256);for(let t=0;t<64;t++)e[4*t+0]=e[4*t+1]=e[4*t+2]=0,e[4*t+3]=255;return t.blackTexture=new B(t),t.blackTexture.initFromData(e,8,8,B.FILTER_NEAREST,B.WRAP_REPEAT),t.blackTexture},B.getEmptyCubemapTexture=function(t){const e=[t.gl.TEXTURE_CUBE_MAP_POSITIVE_X,t.gl.TEXTURE_CUBE_MAP_NEGATIVE_X,t.gl.TEXTURE_CUBE_MAP_POSITIVE_Y,t.gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,t.gl.TEXTURE_CUBE_MAP_POSITIVE_Z,t.gl.TEXTURE_CUBE_MAP_NEGATIVE_Z],i=t.gl.createTexture(),s=t.gl.TEXTURE_CUBE_MAP,r=B.FILTER_NEAREST,n=B.WRAP_CLAMP_TO_EDGE;t.profileData.profileTextureNew++,t.gl.bindTexture(s,i),t.profileData.profileTextureResize++;for(let i=0;i<6;i+=1){const r=new Uint8Array(256);t.gl.texImage2D(e[i],0,t.gl.RGBA,8,8,0,t.gl.RGBA,t.gl.UNSIGNED_BYTE,r),t.gl.texParameteri(s,t.gl.TEXTURE_MAG_FILTER,t.gl.NEAREST),t.gl.texParameteri(s,t.gl.TEXTURE_MIN_FILTER,t.gl.NEAREST),t.gl.texParameteri(s,t.gl.TEXTURE_WRAP_S,t.gl.CLAMP_TO_EDGE),t.gl.texParameteri(s,t.gl.TEXTURE_WRAP_T,t.gl.CLAMP_TO_EDGE)}return t.gl.bindTexture(s,null),{id:CABLES.uuid(),tex:i,cubemap:i,width:8,height:8,filter:r,wrap:n,unpackAlpha:!0,flip:!0,_fromData:!0,name:"emptyCubemapTexture",anisotropic:0}},B.getTempGradientTexture=function(t){if(t||console.error("[getTempGradientTexture] no cgl!"),t.tempTextureGradient)return t.tempTextureGradient;const e=new B(t),i=new Uint8Array(262144);for(let t=0;t<256;t++)for(let e=0;e<256;e++)i[4*(e+256*t)+0]=i[4*(e+256*t)+1]=i[4*(e+256*t)+2]=255-t,i[4*(e+256*t)+3]=255;return e.initFromData(i,256,256,B.FILTER_NEAREST,B.WRAP_REPEAT),t.tempTextureGradient=e,e},B.getTemporaryTexture=function(t,e,i,s){const r=new B(t),n=[];for(let t=0;t>2],r+=k[(3&i[e])<<4|i[e+1]>>4],r+=k[(15&i[e+1])<<2|i[e+2]>>6],r+=k[63&i[e+2]];return s%3==2?r=r.substring(0,r.length-1)+"=":s%3==1&&(r=r.substring(0,r.length-2)+"=="),r},H=function(t){let e,i,s,r,n,o=.75*t.length,a=t.length,h=0;"="===t[t.length-1]&&(o--,"="===t[t.length-2]&&o--);let l=new ArrayBuffer(o),c=new Uint8Array(l);for(e=0;e>4,c[h++]=(15&s)<<4|r>>2,c[h++]=(3&r)<<6|63&n;return l};class z{constructor(t){this._init(),this._first=!0,this._wireMesh=null,t&&this.apply(t)}_init(){this._max=[-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE],this._min=[Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE],this._center=[0,0,0],this._size=[0,0,0],this._maxAxis=0,this._first=!0}get maxAxis(){return this._maxAxis||1}get size(){return this._size}get center(){return this._center}get x(){return this._center[0]}get y(){return this._center[1]}get z(){return this._center[2]}get minX(){return this._min[0]}get minY(){return this._min[1]}get minZ(){return this._min[2]}get maxX(){return this._max[0]}get maxY(){return this._max[1]}get maxZ(){return this._max[2]}apply(t,e){if(t){if(t instanceof z){const e=t;this.applyPos(e.maxX,e.maxY,e.maxZ),this.applyPos(e.minX,e.minY,e.minZ)}else for(let e=0;e4)&&(console.log("itemsize wrong?",i,t),this._log.stack("itemsize"),i=3),1==i?s="float":2==i?s="vec2":3==i?s="vec3":4==i&&(s="vec4");const r={name:t,data:e,itemSize:i,type:s};this._attributes[t]=r},W.prototype.copyAttribute=function(t,e){const i=this.getAttribute(t);e.setAttribute(t,new Float32Array(i.data),i.itemSize)},W.prototype.setVertices=function(t){t instanceof Float32Array?this._vertices=t:this._vertices=new Float32Array(t)},W.prototype.setTexCoords=function(t){t instanceof Float32Array?this.texCoords=t:this.texCoords=new Float32Array(t)},W.prototype.calcNormals=function(t){t||this.unIndex(),this.calculateNormals({})},W.prototype.flipNormals=function(t,e,i){let s=vec3.create();null==t&&(t=1),null==e&&(e=1),null==i&&(i=1);for(let r=0;r0&&o.push(this.tangents[3*this.verticesIndices[l+0]+0],this.tangents[3*this.verticesIndices[l+0]+1],this.tangents[3*this.verticesIndices[l+0]+2]),this.biTangents.length>0&&a.push(this.biTangents[3*this.verticesIndices[l+0]+0],this.biTangents[3*this.verticesIndices[l+0]+1],this.biTangents[3*this.verticesIndices[l+0]+2]),this.texCoords?r.push(this.texCoords[2*this.verticesIndices[l+0]+0],this.texCoords[2*this.verticesIndices[l+0]+1]):r.push(0,0),s.push(h),h++,i.push(this.vertices[3*this.verticesIndices[l+1]+0],this.vertices[3*this.verticesIndices[l+1]+1],this.vertices[3*this.verticesIndices[l+1]+2]),n.push(this.vertexNormals[3*this.verticesIndices[l+1]+0],this.vertexNormals[3*this.verticesIndices[l+1]+1],this.vertexNormals[3*this.verticesIndices[l+1]+2]),this.tangents.length>0&&o.push(this.tangents[3*this.verticesIndices[l+1]+0],this.tangents[3*this.verticesIndices[l+1]+1],this.tangents[3*this.verticesIndices[l+1]+2]),this.biTangents.length>0&&a.push(this.biTangents[3*this.verticesIndices[l+1]+0],this.biTangents[3*this.verticesIndices[l+1]+1],this.biTangents[3*this.verticesIndices[l+1]+2]),this.texCoords?r.push(this.texCoords[2*this.verticesIndices[l+1]+0],this.texCoords[2*this.verticesIndices[l+1]+1]):r.push(0,0),s.push(h),h++,i.push(this.vertices[3*this.verticesIndices[l+2]+0],this.vertices[3*this.verticesIndices[l+2]+1],this.vertices[3*this.verticesIndices[l+2]+2]),n.push(this.vertexNormals[3*this.verticesIndices[l+2]+0],this.vertexNormals[3*this.verticesIndices[l+2]+1],this.vertexNormals[3*this.verticesIndices[l+2]+2]),this.tangents.length>0&&o.push(this.tangents[3*this.verticesIndices[l+2]+0],this.tangents[3*this.verticesIndices[l+2]+1],this.tangents[3*this.verticesIndices[l+2]+2]),this.biTangents.length>0&&a.push(this.biTangents[3*this.verticesIndices[l+2]+0],this.biTangents[3*this.verticesIndices[l+2]+1],this.biTangents[3*this.verticesIndices[l+2]+2]),this.texCoords?r.push(this.texCoords[2*this.verticesIndices[l+2]+0],this.texCoords[2*this.verticesIndices[l+2]+1]):r.push(0,0),s.push(h),h++;this.vertices=i,this.texCoords=r,this.vertexNormals=n,o.length>0&&(this.tangents=o),a.length>0&&(this.biTangents=a),this.verticesIndices.length=0,t&&(this.verticesIndices=s),e||this.calculateNormals()},W.prototype.calcBarycentric=function(){let t=[];t.length=this.vertices.length;for(let e=0;e1&&this.keys.splice(0,i)},$.prototype.clear=function(t){let e=0;t&&(e=this.getValue(t)),this.keys.length=0,t&&this.setValue(t,e),null!==this.onChange&&this.onChange(),this.emitEvent("onChange",this)},$.prototype.sortKeys=function(){this.keys.sort((t,e)=>parseFloat(t.time)-parseFloat(e.time)),this._needsSort=!1},$.prototype.getLength=function(){return 0===this.keys.length?0:this.keys[this.keys.length-1].time},$.prototype.getKeyIndex=function(t){let e=0;for(let i=0;i=this.keys[i].time&&(e=i),this.keys[i].time>t)return e;return e},$.prototype.setValue=function(t,e,i){let s=null;for(const r in this.keys)if(this.keys[r].time==t){s=this.keys[r],this.keys[r].setValue(e),this.keys[r].cb=i;break}return s||(s=new X.Key({time:t,value:e,e:this.defaultEasing,cb:i}),this.keys.push(s)),this.onChange&&this.onChange(),this.emitEvent("onChange",this),this._needsSort=!0,s},$.prototype.setKeyEasing=function(t,e){this.keys[t]&&(this.keys[t].setEasing(e),this.emitEvent("onChange",this))},$.prototype.getSerialized=function(){const t={keys:[]};t.loop=this.loop;for(const e in this.keys)t.keys.push(this.keys[e].getSerialized());return t},$.prototype.getKey=function(t){const e=this.getKeyIndex(t);return this.keys[e]},$.prototype.getNextKey=function(t){let e=this.getKeyIndex(t)+1;return e>=this.keys.length&&(e=this.keys.length-1),this.keys[e]},$.prototype.isFinished=function(t){return this.keys.length<=0||t>this.keys[this.keys.length-1].time},$.prototype.isStarted=function(t){return!(this.keys.length<=0)&&t>=this.keys[0].time},$.prototype.getValue=function(t){if(0===this.keys.length)return 0;if(this._needsSort&&this.sortKeys(),tthis.keys[e].time){t/this.keys[e].time>this._timesLooped&&(this._timesLooped++,this.onLooped&&this.onLooped()),t=(t-this.keys[0].time)%(this.keys[e].time-this.keys[0].time),t+=this.keys[0].time}const i=this.getKeyIndex(t);if(i>=e)return this.keys[e].cb&&!this.keys[e].cbTriggered&&this.keys[e].trigger(),this.keys[e].value;const s=parseInt(i,10)+1,r=this.keys[i],n=this.keys[s];if(r.cb&&!r.cbTriggered&&r.trigger(),!n)return-1;const o=(t-r.time)/(n.time-r.time);return r.ease||this.log._warn("has no ease",r,n),r.ease(o,n)},$.prototype.addKey=function(t){void 0===t.time?this.log.warn("key time undefined, ignoring!"):(this.keys.push(t),null!==this.onChange&&this.onChange(),this.emitEvent("onChange",this))},$.prototype.easingFromString=function(t){return"linear"==t?h.EASING_LINEAR:"absolute"==t?h.EASING_ABSOLUTE:"smoothstep"==t?h.EASING_SMOOTHSTEP:"smootherstep"==t?h.EASING_SMOOTHERSTEP:"Cubic In"==t?h.EASING_CUBIC_IN:"Cubic Out"==t?h.EASING_CUBIC_OUT:"Cubic In Out"==t?h.EASING_CUBIC_INOUT:"Expo In"==t?h.EASING_EXPO_IN:"Expo Out"==t?h.EASING_EXPO_OUT:"Expo In Out"==t?h.EASING_EXPO_INOUT:"Sin In"==t?h.EASING_SIN_IN:"Sin Out"==t?h.EASING_SIN_OUT:"Sin In Out"==t?h.EASING_SIN_INOUT:"Back In"==t?h.EASING_BACK_IN:"Back Out"==t?h.EASING_BACK_OUT:"Back In Out"==t?h.EASING_BACK_INOUT:"Elastic In"==t?h.EASING_ELASTIC_IN:"Elastic Out"==t?h.EASING_ELASTIC_OUT:"Bounce In"==t?h.EASING_BOUNCE_IN:"Bounce Out"==t?h.EASING_BOUNCE_OUT:"Quart Out"==t?h.EASING_QUART_OUT:"Quart In"==t?h.EASING_QUART_IN:"Quart In Out"==t?h.EASING_QUART_INOUT:"Quint Out"==t?h.EASING_QUINT_OUT:"Quint In"==t?h.EASING_QUINT_IN:"Quint In Out"==t?h.EASING_QUINT_INOUT:void 0},$.prototype.createPort=function(t,e,i){const s=t.inDropDown(e,h.EASINGS);return s.set("linear"),s.defaultValue="linear",s.onChange=function(){this.defaultEasing=this.easingFromString(s.get()),i&&i()}.bind(this),s},$.slerpQuaternion=function(t,e,i,s,r,n){$.slerpQuaternion.q1||($.slerpQuaternion.q1=quat.create(),$.slerpQuaternion.q2=quat.create());const o=i.getKeyIndex(t);let a=o+1;if(a>=i.keys.length&&(a=i.keys.length-1),o==a)quat.set(e,i.keys[o].value,s.keys[o].value,r.keys[o].value,n.keys[o].value);else{const h=i.keys[o].time,l=(t-h)/(i.keys[a].time-h);quat.set($.slerpQuaternion.q1,i.keys[o].value,s.keys[o].value,r.keys[o].value,n.keys[o].value),quat.set($.slerpQuaternion.q2,i.keys[a].value,s.keys[a].value,r.keys[a].value,n.keys[a].value),quat.slerp(e,$.slerpQuaternion.q1,$.slerpQuaternion.q2,l)}return e};const tt=X;tt.Anim=$;const et=function(t,e,i,s){Y.apply(this),this.data={},this._log=new a("core_port"),this.direction=c.PORT_DIR_IN,this.id=CABLES.simpleId(),this.parent=t,this.links=[],this.value=0,this.name=e,this.type=i||l.OP_PORT_TYPE_VALUE,this.uiAttribs=s||{},this.anim=null,this._oldAnimVal=-5711,this.defaultValue=null,this._uiActiveState=!0,this.ignoreValueSerialize=!1,this.onLinkChanged=null,this.crashed=!1,this._valueBeforeLink=null,this._lastAnimFrame=-1,this._animated=!1,this.onValueChanged=null,this.onTriggered=null,this.onUiActiveStateChange=null,this.changeAlways=!1,this._warnedDeprecated=!1,this._useVariableName=null,this.activityCounter=0,this._tempLastUiValue=null,Object.defineProperty(this,"title",{get(){return this.uiAttribs.title||this.name}}),Object.defineProperty(this,"val",{get(){return this._log.warn("val getter deprecated!",this),this._log.stack("val getter deprecated"),this._warnedDeprecated=!0,this.get()},set(t){this._log.warn("val setter deprecated!",this),this._log.stack("val setter deprecated"),this.setValue(t),this._warnedDeprecated=!0}})};et.prototype.copyLinkedUiAttrib=function(t,e){if(!CABLES.UI)return;if(!this.isLinked())return;const i={};i[t]=this.links[0].getOtherPort(this).getUiAttrib(t),e.setUiAttribs(i)},et.prototype.getValueForDisplay=function(){let t=String(this.value);return this.uiAttribs&&"boolnum"==this.uiAttribs.display&&(t+=" - ",this.value?t+="true":t+="false"),t=t.replace(/(<([^>]+)>)/gi,""),t.length>100&&(t=t.substring(0,100)),t},et.prototype.onAnimToggle=function(){},et.prototype._onAnimToggle=function(){this.onAnimToggle()},et.prototype.remove=function(){this.removeLinks(),this.parent.removePort(this)},et.prototype.setUiAttribs=function(t){let e=!1;this.uiAttribs||(this.uiAttribs={});for(const i in t)this.uiAttribs[i]!=t[i]&&(e=!0),this.uiAttribs[i]=t[i],"group"==i&&this.indexPort&&this.indexPort.setUiAttribs({group:t[i]});e&&this.emitEvent("onUiAttrChange",t,this)},et.prototype.getUiAttribs=function(){return this.uiAttribs},et.prototype.getUiAttrib=function(t){return this.uiAttribs&&this.uiAttribs.hasOwnProperty(t)?this.uiAttribs[t]:null},et.prototype.get=function(){return this._animated&&this._lastAnimFrame!=this.parent.patch.getFrameNum()&&(this._lastAnimFrame=this.parent.patch.getFrameNum(),this.value=this.anim.getValue(this.parent.patch.timer.getTime()),this._oldAnimVal=this.value,this.forceChange()),this.value},et.prototype.set=et.prototype.setValue=function(t){if(void 0!==t&&this.parent.enabled&&!this.crashed&&(t!==this.value||this.changeAlways||this.type==l.OP_PORT_TYPE_TEXTURE||this.type==l.OP_PORT_TYPE_ARRAY)){if(this._animated)this.anim.setValue(this.parent.patch.timer.getTime(),t);else{try{this.value=t,this.forceChange()}catch(t){this.crashed=!0,this.setValue=function(t){},this.onTriggered=function(){},this._log.error("onvaluechanged exception cought",t),this._log.error(t.stack),this._log.warn("exception in: "+this.parent.name),this.parent.patch.isEditorMode()&&gui.showOpCrash(this.parent),this.parent.patch.emitEvent("exception",t,this.parent),this.parent.onError&&this.parent.onError(t)}this.parent&&this.parent.patch&&this.parent.patch.isEditorMode()&&this.type==l.OP_PORT_TYPE_TEXTURE&&gui.texturePreview().updateTexturePort(this)}if(this.direction==c.PORT_DIR_OUT)for(let t=0;t{this.parent.patch.emitEvent("portAnimUpdated",this.parent,this,this.anim)}),t.anim.loop&&(this.anim.loop=t.anim.loop);for(const e in t.anim.keys)this.anim.keys.push(new X.Key(t.anim.keys[e]))}},et.prototype.getSerialized=function(){const t={};if(t.name=this.getName(),this.ignoreValueSerialize||0!==this.links.length||this.type==l.OP_PORT_TYPE_OBJECT&&this.value&&this.value.tex||(t.value=this.value),this._useVariableName&&(t.useVariable=this._useVariableName),this._animated&&(t.animated=!0),this.anim&&(t.anim=this.anim.getSerialized()),"file"==this.uiAttribs.display&&(t.display=this.uiAttribs.display),this.direction==c.PORT_DIR_OUT&&this.links.length>0){t.links=[];for(const e in this.links)!this.links[e].ignoreInSerialize&&this.links[e].portIn&&this.links[e].portOut&&t.links.push(this.links[e].getSerialized())}return t},et.prototype.shouldLink=function(){return!0},et.prototype.removeLinks=function(){let t=0;for(;this.links.length>0;){if(t++,t>5e3){this._log.warn("could not delete links... / infinite loop"),this.links.length=0;break}this.links[0].remove()}},et.prototype.removeLink=function(t){for(const e in this.links)this.links[e]==t&&this.links.splice(e,1);this.direction==c.PORT_DIR_IN&&(this.type==l.OP_PORT_TYPE_VALUE?this.setValue(this._valueBeforeLink||0):this.setValue(this._valueBeforeLink||null)),CABLES.UI&&this.parent.checkLinkTimeWarnings&&this.parent.checkLinkTimeWarnings(),this.onLinkChanged&&this.onLinkChanged(),this.emitEvent("onLinkChanged"),this.parent.emitEvent("onLinkChanged")},et.prototype.getName=function(){return this.name},et.prototype.addLink=function(t){this._valueBeforeLink=this.value,this.links.push(t),CABLES.UI&&this.parent.checkLinkTimeWarnings&&this.parent.checkLinkTimeWarnings(),this.onLinkChanged&&this.onLinkChanged(),this.emitEvent("onLinkChanged"),this.parent.emitEvent("onLinkChanged")},et.prototype.getLinkTo=function(t){for(const e in this.links)if(this.links[e].portIn==t||this.links[e].portOut==t)return this.links[e]},et.prototype.removeLinkTo=function(t){for(const e in this.links)if(this.links[e].portIn==t||this.links[e].portOut==t)return this.links[e].remove(),CABLES.UI&&this.parent.checkLinkTimeWarnings&&this.parent.checkLinkTimeWarnings(),this.onLinkChanged&&this.onLinkChanged(),void this.emitEvent("onLinkChanged")},et.prototype.isLinkedTo=function(t){for(const e in this.links)if(this.links[e].portIn==t||this.links[e].portOut==t)return!0;return!1},et.prototype._activity=function(){this.activityCounter++},et.prototype.trigger=function(){if(this._activity(),0===this.links.length)return;if(!this.parent.enabled)return;let t=null;try{for(let e=0;e{this.set(null),this.set(this._variableIn.getValue())}):this._varChangeListenerId=this._variableIn.on("change",this.set.bind(this)),this.set(this._variableIn.getValue())):this._log.warn("PORT VAR NOT FOUND!!!",t),this._useVariableName=t,e.useVariable=!0,e.variableName=this._useVariableName):(e.variableName=this._useVariableName=null,e.useVariable=!1),this.setUiAttribs(e),this.parent.patch.emitEvent("portSetVariable",this.parent,this,t)},et.prototype._handleNoTriggerOpAnimUpdates=function(t){let e=!1;for(let t=0;t{this.updateAnim()}):this.parent.patch.removeEventListener(this._notriggerAnimUpdate))},et.prototype.setAnimated=function(t){this._animated!=t&&(this._animated=t,this._animated&&!this.anim&&(this.anim=new $,this.anim.addEventListener("onChange",()=>{this.parent.patch.emitEvent("portAnimUpdated",this.parent,this,this.anim)})),this._onAnimToggle()),this._handleNoTriggerOpAnimUpdates(t),this.setUiAttribs({isAnimated:this._animated})},et.prototype.toggleAnim=function(){this._animated=!this._animated,this._animated&&!this.anim&&(this.anim=new $,this.anim.addEventListener("onChange",()=>{this.parent.patch.emitEvent("portAnimUpdated",this.parent,this,this.anim)})),this.setAnimated(this._animated),this._onAnimToggle(),this.setUiAttribs({isAnimated:this._animated})},et.prototype.getType=function(){return this.type},et.prototype.isLinked=function(){return this.links.length>0||this._animated||null!=this._useVariableName},et.prototype.isBoundToVar=function(){return null!=this._useVariableName},et.prototype.isAnimated=function(){return this._animated},et.prototype.isHidden=function(){return this.uiAttribs.hidePort},et.prototype._onTriggered=function(t){this._activity(),this.parent.updateAnims(),this.parent.enabled&&this.onTriggered&&this.onTriggered(t)},et.prototype._onSetProfiling=function(t){this.parent.patch.profiler.add("port",this),this.setValue(t),this.parent.patch.profiler.add("port",null)},et.prototype._onTriggeredProfiling=function(){this.parent.enabled&&this.onTriggered&&(this.parent.patch.profiler.add("port",this),this.onTriggered(),this.parent.patch.profiler.add("port",null))},et.prototype.onValueChange=function(t){this.onChange=t},et.prototype.getUiActiveState=function(){return this._uiActiveState},et.prototype.setUiActiveState=function(t){this._uiActiveState=t,this.onUiActiveStateChange&&this.onUiActiveStateChange()},et.prototype.hidePort=function(){this._log.warn("op.hideport() is deprecated, do not use it!")},et.portTypeNumberToString=function(t){return t==l.OP_PORT_TYPE_VALUE?"value":t==l.OP_PORT_TYPE_FUNCTION?"function":t==l.OP_PORT_TYPE_OBJECT?"object":t==l.OP_PORT_TYPE_ARRAY?"array":t==l.OP_PORT_TYPE_STRING?"string":t==l.OP_PORT_TYPE_DYNAMIC?"dynamic":"unknown"};class it extends et{constructor(t,e,i,s,r){super(t,e,i,s),this.indexPort=r,this.indexPort.set=t=>{const e=s.values;if(!e)return;let i=Math.floor(t);i=Math.min(i,e.length-1),i=Math.max(i,0),this.indexPort.setValue(i),this.set(e[i]),this.parent.patch.isEditorMode()&&window.gui&&gui.patchView.isCurrentOp(this.parent)&&gui.opParams.show(this.parent)}}setUiAttribs(t){const e=t.hidePort;t.hidePort=!0,super.setUiAttribs(t),void 0!==e&&this.indexPort.setUiAttribs({hidePort:e})}}class st extends it{setUiAttribs(t){if(this.indexPort.isLinked())for(const e in t)"greyout"!=e||t[e]||(t[e]="true");super.setUiAttribs(t)}}var rt=class{constructor(t,e,i,s,r,n,o,h,l,c){if(this._log=new a("cg_uniform"),this._type=e,this._name=i,this._shader=t,this._value=1e-5,this._oldValue=null,this._port=null,this._structName=l,this._structUniformName=h,this._propertyName=c,this._shader._addUniform(this),this.needsUpdate=!0,this.shaderType=null,this.comment=null,"f"==e)this.set=this.setValue=this.setValueF.bind(this),this.updateValue=this.updateValueF.bind(this);else if("f[]"==e)this.set=this.setValue=this.setValueArrayF.bind(this),this.updateValue=this.updateValueArrayF.bind(this);else if("2f[]"==e)this.set=this.setValue=this.setValueArray2F.bind(this),this.updateValue=this.updateValueArray2F.bind(this);else if("3f[]"==e)this.set=this.setValue=this.setValueArray3F.bind(this),this.updateValue=this.updateValueArray3F.bind(this);else if("4f[]"==e)this.set=this.setValue=this.setValueArray4F.bind(this),this.updateValue=this.updateValueArray4F.bind(this);else if("i"==e)this.set=this.setValue=this.setValueI.bind(this),this.updateValue=this.updateValueI.bind(this);else if("2i"==e)this.set=this.setValue=this.setValue2I.bind(this),this.updateValue=this.updateValue2I.bind(this);else if("3i"==e)this.set=this.setValue=this.setValue3I.bind(this),this.updateValue=this.updateValue3I.bind(this);else if("4i"==e)this.set=this.setValue=this.setValue4I.bind(this),this.updateValue=this.updateValue4I.bind(this);else if("b"==e)this.set=this.setValue=this.setValueBool.bind(this),this.updateValue=this.updateValueBool.bind(this);else if("4f"==e)this.set=this.setValue=this.setValue4F.bind(this),this.updateValue=this.updateValue4F.bind(this);else if("3f"==e)this.set=this.setValue=this.setValue3F.bind(this),this.updateValue=this.updateValue3F.bind(this);else if("2f"==e)this.set=this.setValue=this.setValue2F.bind(this),this.updateValue=this.updateValue2F.bind(this);else if("t"==e)this.set=this.setValue=this.setValueT.bind(this),this.updateValue=this.updateValueT.bind(this);else if("tc"==e)this.set=this.setValue=this.setValueT.bind(this),this.updateValue=this.updateValueT.bind(this);else if("t[]"==e)this.set=this.setValue=this.setValueArrayT.bind(this),this.updateValue=this.updateValueArrayT.bind(this);else{if("m4"!=e&&"m4[]"!=e)throw new Error("Unknown uniform type");this.set=this.setValue=this.setValueM4.bind(this),this.updateValue=this.updateValueM4.bind(this)}"object"==typeof s&&s instanceof et?(this._port=s,this._value=this._port.get(),r&&n&&o?(r instanceof et&&n instanceof et&&o instanceof et||this._log.error("[cgl_uniform] mixed port/value parameter for vec4 ",this._name),this._value=[0,0,0,0],this._port2=r,this._port3=n,this._port4=o,this._port.on("change",this.updateFromPort4f.bind(this)),this._port2.on("change",this.updateFromPort4f.bind(this)),this._port3.on("change",this.updateFromPort4f.bind(this)),this._port4.on("change",this.updateFromPort4f.bind(this)),this.updateFromPort4f()):r&&n?(r instanceof et&&n instanceof et||this._log.error("[cgl_uniform] mixed port/value parameter for vec4 ",this._name),this._value=[0,0,0],this._port2=r,this._port3=n,this._port.on("change",this.updateFromPort3f.bind(this)),this._port2.on("change",this.updateFromPort3f.bind(this)),this._port3.on("change",this.updateFromPort3f.bind(this)),this.updateFromPort3f()):r?(r instanceof et||this._log.error("[cgl_uniform] mixed port/value parameter for vec4 ",this._name),this._value=[0,0],this._port2=r,this._port.on("change",this.updateFromPort2f.bind(this)),this._port2.on("change",this.updateFromPort2f.bind(this)),this.updateFromPort2f()):this._port.on("change",this.updateFromPort.bind(this))):this._value=s,this.setValue(this._value),this.needsUpdate=!0}getType(){return this._type}getName(){return this._name}getValue(){return this._value}getShaderType(){return this.shaderType}isStructMember(){return!!this._structName}updateFromPort4f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this._value[2]=this._port3.get(),this._value[3]=this._port4.get(),this.setValue(this._value)}updateFromPort3f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this._value[2]=this._port3.get(),this.setValue(this._value)}updateFromPort2f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this.setValue(this._value)}updateFromPort(){this.setValue(this._port.get())}};class nt extends rt{constructor(t,e,i,s,r,n,o,a,h,l){super(t,e,i,s,r,n,o,a,h,l),this._loc=-1,this._cgl=t._cgl}copy(t){const e=new nt(t,this._type,this._name,this._value,this._port2,this._port3,this._port4,this._structUniformName,this._structName,this._propertyName);return e.shaderType=this.shaderType,e}getGlslTypeString(){return nt.glslTypeString(this._type)}_isValidLoc(){return-1!=this._loc}resetLoc(){this._loc=-1,this.needsUpdate=!0}bindTextures(){}getLoc(){return this._loc}updateFromPort4f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this._value[2]=this._port3.get(),this._value[3]=this._port4.get(),this.setValue(this._value)}updateFromPort3f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this._value[2]=this._port3.get(),this.setValue(this._value)}updateFromPort2f(){this._value[0]=this._port.get(),this._value[1]=this._port2.get(),this.setValue(this._value)}updateFromPort(){this.setValue(this._port.get())}updateValueF(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._shader.getCgl().gl.uniform1f(this._loc,this._value),this._cgl.profileData.profileUniformCount++}setValueF(t){t!=this._value&&(this.needsUpdate=!0,this._value=t)}updateValueI(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._shader.getCgl().gl.uniform1i(this._loc,this._value),this._cgl.profileData.profileUniformCount++}updateValue2I(){this._value&&(this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._shader.getCgl().gl.uniform2i(this._loc,this._value[0],this._value[1]),this.needsUpdate=!1,this._cgl.profileData.profileUniformCount++)}updateValue3I(){this._value&&(this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._shader.getCgl().gl.uniform3i(this._loc,this._value[0],this._value[1],this._value[2]),this.needsUpdate=!1,this._cgl.profileData.profileUniformCount++)}updateValue4I(){this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._shader.getCgl().gl.uniform4i(this._loc,this._value[0],this._value[1],this._value[2],this._value[3]),this._cgl.profileData.profileUniformCount++}setValueI(t){t!=this._value&&(this.needsUpdate=!0,this._value=t)}setValue2I(t){t&&(this._oldValue?t[0]==this._oldValue[0]&&t[1]==this._oldValue[1]||(this._oldValue[0]=t[0],this._oldValue[1]=t[1],this.needsUpdate=!0):(this._oldValue=[t[0]-1,1],this.needsUpdate=!0),this._value=t)}setValue3I(t){t&&(this._oldValue?t[0]==this._oldValue[0]&&t[1]==this._oldValue[1]&&t[2]==this._oldValue[2]||(this._oldValue[0]=t[0],this._oldValue[1]=t[1],this._oldValue[2]=t[2],this.needsUpdate=!0):(this._oldValue=[t[0]-1,1,2],this.needsUpdate=!0),this._value=t)}setValue4I(t){this.needsUpdate=!0,this._value=t||vec4.create()}updateValueBool(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._shader.getCgl().gl.uniform1i(this._loc,this._value?1:0),this._cgl.profileData.profileUniformCount++}setValueBool(t){t!=this._value&&(this.needsUpdate=!0,this._value=t)}setValueArray4F(t){this.needsUpdate=!0,this._value=t}updateValueArray4F(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._value&&(this._shader.getCgl().gl.uniform4fv(this._loc,this._value),this._cgl.profileData.profileUniformCount++)}setValueArray3F(t){this.needsUpdate=!0,this._value=t}updateValueArray3F(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._value&&(this._shader.getCgl().gl.uniform3fv(this._loc,this._value),this._cgl.profileData.profileUniformCount++)}setValueArray2F(t){this.needsUpdate=!0,this._value=t}updateValueArray2F(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._value&&(this._shader.getCgl().gl.uniform2fv(this._loc,this._value),this._cgl.profileData.profileUniformCount++)}setValueArrayF(t){this.needsUpdate=!0,this._value=t}updateValueArrayF(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._value&&(this._shader.getCgl().gl.uniform1fv(this._loc,this._value),this._cgl.profileData.profileUniformCount++)}setValueArrayT(t){this.needsUpdate=!0,this._value=t}updateValue3F(){this._value&&(this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._shader.getCgl().gl.uniform3f(this._loc,this._value[0],this._value[1],this._value[2]),this.needsUpdate=!1,this._cgl.profileData.profileUniformCount++)}setValue3F(t){t&&(this._oldValue?t[0]==this._oldValue[0]&&t[1]==this._oldValue[1]&&t[2]==this._oldValue[2]||(this._oldValue[0]=t[0],this._oldValue[1]=t[1],this._oldValue[2]=t[2],this.needsUpdate=!0):(this._oldValue=[t[0]-1,1,2],this.needsUpdate=!0),this._value=t)}updateValue2F(){this._value&&(this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._shader.getCgl().gl.uniform2f(this._loc,this._value[0],this._value[1]),this.needsUpdate=!1,this._cgl.profileData.profileUniformCount++)}setValue2F(t){t&&(this._oldValue?t[0]==this._oldValue[0]&&t[1]==this._oldValue[1]||(this._oldValue[0]=t[0],this._oldValue[1]=t[1],this.needsUpdate=!0):(this._oldValue=[t[0]-1,1],this.needsUpdate=!0),this._value=t)}updateValue4F(){this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._value||(this._log.warn("no value for uniform",this._name,this),this._value=[0,0,0,0]),this.needsUpdate=!1,this._shader.getCgl().gl.uniform4f(this._loc,this._value[0],this._value[1],this._value[2],this._value[3]),this._cgl.profileData.profileUniformCount++}setValue4F(t){"number"==typeof this.value&&(this.value=vec4.create()),t&&(this._oldValue?t[0]==this._oldValue[0]&&t[1]==this._oldValue[1]&&t[2]==this._oldValue[2]&&t[3]==this._oldValue[3]||(this._oldValue[0]=t[0],this._oldValue[1]=t[1],this._oldValue[2]=t[2],this.needsUpdate=!0):(this._oldValue=[t[0]-1,1,2,3],this.needsUpdate=!0),this._value=t)}updateValueM4(){if(this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),!this._value||this._value.length%16!=0)return console.log("this.name",this._name,this._value);this._shader.getCgl().gl.uniformMatrix4fv(this._loc,!1,this._value),this._cgl.profileData.profileUniformCount++}setValueM4(t){this.needsUpdate=!0,this._value=t||mat4.create()}updateValueArrayT(){this._isValidLoc()?this.needsUpdate=!1:this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._value&&(this._shader.getCgl().gl.uniform1iv(this._loc,this._value),this._cgl.profileData.profileUniformCount++)}updateValueT(){this._isValidLoc()||(this._loc=this._shader.getCgl().gl.getUniformLocation(this._shader.getProgram(),this._name),this._cgl.profileData.profileShaderGetUniform++,this._cgl.profileData.profileShaderGetUniformName=this._name),this._cgl.profileData.profileUniformCount++,this._shader.getCgl().gl.uniform1i(this._loc,this._value),this.needsUpdate=!1}setValueT(t){this.needsUpdate=!0,this._value=t}}nt.glslTypeString=t=>"f"==t?"float":"b"==t?"bool":"i"==t?"int":"2i"==t?"ivec2":"2f"==t?"vec2":"3f"==t?"vec3":"4f"==t?"vec4":"m4"==t?"mat4":"t"==t?"sampler2D":"tc"==t?"samplerCube":"3f[]"==t||"m4[]"==t||"f[]"==t?null:void(void 0)._log.warn("[CGL UNIFORM] unknown glsl type string ",t);const ot=180/Math.PI,at={MATH:{DEG2RAD:Math.PI/180,RAD2DEG:ot},SHADER:{SHADERVAR_VERTEX_POSITION:"vPosition",SHADERVAR_VERTEX_NUMBER:"attrVertIndex",SHADERVAR_VERTEX_NORMAL:"attrVertNormal",SHADERVAR_VERTEX_TEXCOORD:"attrTexCoord",SHADERVAR_INSTANCE_MMATRIX:"instMat",SHADERVAR_VERTEX_COLOR:"attrVertColor",SHADERVAR_UNI_PROJMAT:"projMatrix",SHADERVAR_UNI_VIEWMAT:"viewMatrix",SHADERVAR_UNI_MODELMAT:"modelMatrix",SHADERVAR_UNI_NORMALMAT:"normalMatrix",SHADERVAR_UNI_INVVIEWMAT:"inverseViewMatrix",SHADERVAR_UNI_INVPROJMAT:"invProjMatrix",SHADERVAR_UNI_VIEWPOS:"camPos"},BLEND_MODES:{BLEND_NONE:0,BLEND_NORMAL:1,BLEND_ADD:2,BLEND_SUB:3,BLEND_MUL:4}};const ht={lastMesh:null},lt=function(t,e,i){this._cgl=t,this._log=new a("cgl_mesh"),this._bufVertexAttrib=null,this._bufVerticesIndizes=this._cgl.gl.createBuffer(),this._indexType=this._cgl.gl.UNSIGNED_SHORT,this._attributes=[],this._attribLocs={},this._geom=null,this._lastShader=null,this._numInstances=0,this._glPrimitive=i,this._preWireframeGeom=null,this.addVertexNumbers=!1,this.feedBackAttributes=[],this.setGeom(e),this._feedBacks=[],this._feedBacksChanged=!1,this._transformFeedBackLoc=-1,this._lastAttrUpdate=0,this._name="unknown",this._cgl.profileData.addHeavyEvent("mesh constructed",this._name),this._queryExt=null,Object.defineProperty(this,"numInstances",{get(){return this._numInstances},set(t){this.setNumInstances(t)}})};var ct;lt.prototype.updateVertices=function(t){this.setAttribute(at.SHADER.SHADERVAR_VERTEX_POSITION,t.vertices,3),this._numVerts=t.vertices.length/3},lt.prototype.setAttributePointer=function(t,e,i,s){for(let r=0;r=e.length-1&&this._log.log(this._cgl.canvas.id+" "+t.name+" buffersubdata out of bounds ?",e.length,s,i,t),1==this._cgl.glVersion?this._cgl.gl.bufferSubData(this._cgl.gl.ARRAY_BUFFER,0,e):this._cgl.gl.bufferSubData(this._cgl.gl.ARRAY_BUFFER,4*i,e,i,s-i))},lt.prototype._resizeAttr=function(t,e){e.buffer&&this._cgl.gl.deleteBuffer(e.buffer),e.buffer=this._cgl.gl.createBuffer(),this._cgl.gl.bindBuffer(this._cgl.gl.ARRAY_BUFFER,e.buffer),this._bufferArray(t,e),e.numItems=t.length/e.itemSize},lt.prototype._bufferArray=function(t,e){let i=null;t&&(this._cgl.debugOneFrame&&console.log("_bufferArray",t.length,e.name),t instanceof Float32Array?i=t:e&&i&&i.length==t.length?i.set(t):(i=new Float32Array(t),this._cgl.debugOneFrame&&console.log("_bufferArray create new float32array",t.length,e.name),this._cgl.profileData.profileNonTypedAttrib++,this._cgl.profileData.profileNonTypedAttribNames="("+this._name+":"+e.name+")"),e.arrayLength=i.length,this._cgl.gl.bufferData(this._cgl.gl.ARRAY_BUFFER,i,this._cgl.gl.DYNAMIC_DRAW))},lt.prototype.addAttribute=lt.prototype.updateAttribute=lt.prototype.setAttribute=function(t,e,i,s){if(!e)throw this._log.error("mesh addAttribute - no array given! "+t),new Error;let r=null,n=!1,o=0;const a=e.length/i;for(this._cgl.profileData.profileMeshAttributes+=a||0,"function"==typeof s&&(r=s),"object"==typeof s&&(s.cb&&(r=s.cb),s.instanced&&(n=s.instanced)),t==at.SHADER.SHADERVAR_INSTANCE_MMATRIX&&(n=!0),o=0;o0)this.setAttribute(at.SHADER.SHADERVAR_VERTEX_TEXCOORD,t.texCoords,2);else{const e=new Float32Array(Math.round(t.vertices.length/3*2));this.setAttribute(at.SHADER.SHADERVAR_VERTEX_TEXCOORD,e,2)}},lt.prototype.updateNormals=function(t){if(t.vertexNormals&&t.vertexNormals.length>0)this.setAttribute(at.SHADER.SHADERVAR_VERTEX_NORMAL,t.vertexNormals,3);else{const e=new Float32Array(Math.round(t.vertices.length));this.setAttribute(at.SHADER.SHADERVAR_VERTEX_NORMAL,e,3)}},lt.prototype._setVertexNumbers=function(t){if(!this._verticesNumbers||this._verticesNumbers.length!=this._numVerts||t){if(t)this._verticesNumbers=t;else{this._verticesNumbers=new Float32Array(this._numVerts);for(let t=0;t{i.uniformNumVertices||(i.uniformNumVertices=new nt(i,"f","numVertices",this._numVerts)),i.uniformNumVertices.setValue(this._numVerts)})}},lt.prototype.setVertexIndices=function(t){if(this._bufVerticesIndizes)if(t.length>0){for(let e=0;e=this._numVerts)return void this._log.warn("invalid index in "+this._name);this._cgl.gl.bindBuffer(this._cgl.gl.ELEMENT_ARRAY_BUFFER,this._bufVerticesIndizes),t instanceof Float32Array&&this._log.warn("vertIndices float32Array: "+this._name),t instanceof Uint32Array?(this.vertIndicesTyped=t,this._indexType=this._cgl.gl.UNSIGNED_INT):t instanceof Uint16Array?this.vertIndicesTyped=t:this.vertIndicesTyped=new Uint16Array(t),this._cgl.gl.bufferData(this._cgl.gl.ELEMENT_ARRAY_BUFFER,this.vertIndicesTyped,this._cgl.gl.DYNAMIC_DRAW),this._bufVerticesIndizes.itemSize=1,this._bufVerticesIndizes.numItems=t.length}else this._bufVerticesIndizes.numItems=0;else this._log.warn("no bufVerticesIndizes: "+this._name)},lt.prototype.setGeom=function(t,e){this._geom=t,null!=t.glPrimitive&&(this._glPrimitive=t.glPrimitive),this._geom&&this._geom.name&&(this._name="mesh "+this._geom.name),ht.lastMesh=null,this._cgl.profileData.profileMeshSetGeom++,this._disposeAttributes(),this.updateVertices(this._geom),this.setVertexIndices(this._geom.verticesIndices),this.addVertexNumbers&&this._setVertexNumbers();const i=this._geom.getAttributes(),s={texCoords:at.SHADER.SHADERVAR_VERTEX_TEXCOORD,vertexNormals:at.SHADER.SHADERVAR_VERTEX_NORMAL,vertexColors:at.SHADER.SHADERVAR_VERTEX_COLOR,tangents:"attrTangent",biTangents:"attrBiTangent"};for(const t in i)i[t].data&&i[t].data.length&&this.setAttribute(s[t]||t,i[t].data,i[t].itemSize);e&&(this._geom=null)},lt.prototype._preBind=function(t){for(let e=0;ethis._lastAttrUpdate||e.length!=this._attributes.length){this._lastAttrUpdate=t.lastCompile;for(let t=0;t=0&&this._cgl.gl.disableVertexAttribArray(e[t])):(this._cgl.gl.vertexAttribDivisor(e[t],0),this._cgl.gl.vertexAttribDivisor(e[t]+1,0),this._cgl.gl.vertexAttribDivisor(e[t]+2,0),this._cgl.gl.vertexAttribDivisor(e[t]+3,0),this._cgl.gl.disableVertexAttribArray(e[t]+1),this._cgl.gl.disableVertexAttribArray(e[t]+2),this._cgl.gl.disableVertexAttribArray(e[t]+3))),-1!=e[t]&&this._cgl.gl.disableVertexAttribArray(e[t])},lt.prototype.meshChanged=function(){return this._cgl.lastMesh&&this._cgl.lastMesh!=this},lt.prototype.printDebug=function(t){console.log("--attributes");for(let t=0;t0},ct.prototype.removeFeedbacks=function(t){this._feedbacks&&(this._feedbacks.length=0,this._feedBacksChanged=!0)},ct.prototype.setAttributeFeedback=function(){},ct.prototype.setFeedback=function(t,e,i){let s={nameOut:e},r=!1;this.unBindFeedbacks();for(let t=0;t=e)return t.setUiError("texeffect",null),!0;e&&t.patch.cgl.currentTextureEffect.imgCompVer="+e+' Downgrade to previous version",1)}return!!t.patch.cgl.currentTextureEffect||(t.patch.cgl.currentTextureEffect||t.uiAttribs.uierrors&&0!=t.uiAttribs.uierrors.length?!!t.patch.cgl.currentTextureEffect:(t.setUiError("texeffect",'This op must be a child of an ImageCompose op! More infos here. ',1),!1))},gt.getBlendCode=function(t){let e="".endl()+"vec3 _blend(vec3 base,vec3 blend)".endl()+"{".endl()+" vec3 colNew=blend;".endl()+" #ifdef BM_MULTIPLY".endl()+" colNew=base*blend;".endl()+" #endif".endl()+" #ifdef BM_MULTIPLY_INV".endl()+" colNew=base* vec3(1.0)-blend;".endl()+" #endif".endl()+" #ifdef BM_AVERAGE".endl()+" colNew=((base + blend) / 2.0);".endl()+" #endif".endl()+" #ifdef BM_ADD".endl()+" colNew=min(base + blend, vec3(1.0));".endl()+" #endif".endl()+" #ifdef BM_SUBTRACT_ONE".endl()+" colNew=max(base + blend - vec3(1.0), vec3(0.0));".endl()+" #endif".endl()+" #ifdef BM_SUBTRACT".endl()+" colNew=base - blend;".endl()+" #endif".endl()+" #ifdef BM_DIFFERENCE".endl()+" colNew=abs(base - blend);".endl()+" #endif".endl()+" #ifdef BM_NEGATION".endl()+" colNew=(vec3(1.0) - abs(vec3(1.0) - base - blend));".endl()+" #endif".endl()+" #ifdef BM_EXCLUSION".endl()+" colNew=(base + blend - 2.0 * base * blend);".endl()+" #endif".endl()+" #ifdef BM_LIGHTEN".endl()+" colNew=max(blend, base);".endl()+" #endif".endl()+" #ifdef BM_DARKEN".endl()+" colNew=min(blend, base);".endl()+" #endif".endl()+" #ifdef BM_OVERLAY".endl()+" #define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))".endl()+" colNew=vec3(BlendOverlayf(base.r, blend.r),BlendOverlayf(base.g, blend.g),BlendOverlayf(base.b, blend.b));".endl()+" #endif".endl()+" #ifdef BM_SCREEN".endl()+" #define BlendScreenf(base, blend) (1.0 - ((1.0 - base) * (1.0 - blend)))".endl()+" colNew=vec3(BlendScreenf(base.r, blend.r),BlendScreenf(base.g, blend.g),BlendScreenf(base.b, blend.b));".endl()+" #endif".endl()+" #ifdef BM_SOFTLIGHT".endl()+" #define BlendSoftLightf(base, blend) ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend)))".endl()+" colNew=vec3(BlendSoftLightf(base.r, blend.r),BlendSoftLightf(base.g, blend.g),BlendSoftLightf(base.b, blend.b));".endl()+" #endif".endl()+" #ifdef BM_HARDLIGHT".endl()+" #define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))".endl()+" colNew=vec3(BlendOverlayf(base.r, blend.r),BlendOverlayf(base.g, blend.g),BlendOverlayf(base.b, blend.b));".endl()+" #endif".endl()+" #ifdef BM_COLORDODGE".endl()+" #define BlendColorDodgef(base, blend) ((blend == 1.0) ? blend : min(base / (1.0 - blend), 1.0))".endl()+" colNew=vec3(BlendColorDodgef(base.r, blend.r),BlendColorDodgef(base.g, blend.g),BlendColorDodgef(base.b, blend.b));".endl()+" #endif".endl()+" #ifdef BM_COLORBURN".endl()+" #define BlendColorBurnf(base, blend) ((blend == 0.0) ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0))".endl()+" colNew=vec3(BlendColorBurnf(base.r, blend.r),BlendColorBurnf(base.g, blend.g),BlendColorBurnf(base.b, blend.b));".endl()+" #endif".endl()+" return colNew;".endl()+"}".endl();return t||(e+="vec4 cgl_blend(vec4 oldColor,vec4 newColor,float amount)".endl()+"{".endl()+"vec4 col=vec4( _blend(oldColor.rgb,newColor.rgb) ,1.0);".endl()+"col=vec4( mix( col.rgb, oldColor.rgb ,1.0-oldColor.a*amount),1.0);".endl()+"return col;".endl()+"}".endl()),t>=3&&(e+="vec4 cgl_blendPixel(vec4 base,vec4 col,float amount)".endl()+"{".endl()+"vec3 colNew=_blend(base.rgb,col.rgb);".endl()+"float newA=clamp(base.a+(col.a*amount),0.,1.);".endl()+"#ifdef BM_ALPHAMASKED".endl()+"newA=base.a;".endl()+"#endif".endl()+"return vec4(".endl()+"mix(colNew,base.rgb,1.0-(amount*col.a)),".endl()+"newA);".endl()+"}".endl()),e},gt.onChangeBlendSelect=function(t,e,i){t.toggleDefine("BM_NORMAL","normal"==e),t.toggleDefine("BM_MULTIPLY","multiply"==e),t.toggleDefine("BM_MULTIPLY_INV","multiply invert"==e),t.toggleDefine("BM_AVERAGE","average"==e),t.toggleDefine("BM_ADD","add"==e),t.toggleDefine("BM_SUBTRACT_ONE","subtract one"==e),t.toggleDefine("BM_SUBTRACT","subtract"==e),t.toggleDefine("BM_DIFFERENCE","difference"==e),t.toggleDefine("BM_NEGATION","negation"==e),t.toggleDefine("BM_EXCLUSION","exclusion"==e),t.toggleDefine("BM_LIGHTEN","lighten"==e),t.toggleDefine("BM_DARKEN","darken"==e),t.toggleDefine("BM_OVERLAY","overlay"==e),t.toggleDefine("BM_SCREEN","screen"==e),t.toggleDefine("BM_SOFTLIGHT","softlight"==e),t.toggleDefine("BM_HARDLIGHT","hardlight"==e),t.toggleDefine("BM_COLORDODGE","color dodge"==e),t.toggleDefine("BM_COLORBURN","color burn"==e),t.toggleDefine("BM_ALPHAMASKED",i)},gt.AddBlendSelect=function(t,e,i){return t.inValueSelect(e||"Blend Mode",["normal","lighten","darken","multiply","multiply invert","average","add","subtract","difference","negation","exclusion","overlay","screen","color dodge","color burn","softlight","hardlight","subtract one"],i||"normal")},gt.AddBlendAlphaMask=function(t,e,i){return t.inSwitch(e||"Alpha Mask",["Off","On"],i||"Off")},gt.setupBlending=function(t,e,i,s,r){const n=()=>{let s=!1;r&&(s="On"==r.get()),gt.onChangeBlendSelect(e,i.get(),s);let n=i.get();"normal"==n?n=null:"multiply"==n?n="mul":"multiply invert"==n?n="mulinv":"lighten"==n?n="light":"darken"==n?n="darken":"average"==n?n="avg":"subtract one"==n?n="sub one":"subtract"==n?n="sub":"difference"==n?n="diff":"negation"==n||"negation"==n||"negation"==n?n="neg":"exclusion"==n?n="exc":"overlay"==n?n="ovl":"color dodge"==n?n="dodge":"color burn"==n?n="burn":"softlight"==n?n="soft":"hardlight"==n&&(n="hard"),t.setUiAttrib({extendTitle:n})};t.setPortGroup("Blending",[i,s,r]);let o=!1;i.onChange=n,r&&(r.onChange=n,o="On"==r.get()),gt.onChangeBlendSelect(e,i.get(),o)};const pt={"CGL.BLENDMODES":function(){this.name="blendmodes",this.srcHeadFrag=gt.getBlendCode()},"CGL.BLENDMODES3":function(){this.name="blendmodes3",this.srcHeadFrag=gt.getBlendCode(3)},"CGL.LUMINANCE":function(){this.name="luminance",this.srcHeadFrag="".endl()+"float cgl_luminance(vec3 c)".endl()+"{".endl()+" return dot(vec3(0.2126,0.7152,0.0722),c);".endl()+"}".endl()},"CGL.RANDOM_OLD":function(){this.name="randomNumber",this.srcHeadFrag="".endl()+"float cgl_random(vec2 co)".endl()+"{".endl()+" return fract(sin(dot(co.xy ,vec2(12.9898,4.1414))) * 432758.5453);".endl()+"}".endl()+"vec3 cgl_random3(vec2 co)".endl()+"{".endl()+" return vec3( cgl_random(co),cgl_random(co+0.5711),cgl_random(co+1.5711));".endl()+"}"},"CGL.RANDOM_LOW":function(){this.name="randomNumber",this.srcHeadFrag="".endl()+"float cgl_random(vec2 co)".endl()+"{".endl()+" return fract(sin(dot(co.xy ,vec2(12.9898,4.1414))) * 358.5453);".endl()+"}".endl()+"vec3 cgl_random3(vec2 co)".endl()+"{".endl()+" return vec3( cgl_random(co),cgl_random(co+0.5711),cgl_random(co+1.5711));".endl()+"}"},"CGL.RANDOM_TEX":function(){this.name="randomNumbertex",this.srcHeadFrag="".endl()+"UNI sampler2D CGLRNDTEX;".endl()+"float cgl_random(vec2 co)".endl()+"{".endl()+" return texture(CGLRNDTEX,co*5711.0).r;".endl()+"}".endl()+"vec3 cgl_random3(vec2 co)".endl()+"{".endl()+" return texture(CGLRNDTEX,co*5711.0).rgb;".endl()+"}",this.initUniforms=function(t){return[new nt(t,"t","CGLRNDTEX",7)]},this.onBind=function(t,e){B.getRandomTexture(t),t.setTexture(7,B.getRandomTexture(t).tex)}}},_t=function(){return window.performance.now()},dt=function(){return _t()},ft=function(){CABLES.EventTarget.apply(this),this._timeStart=_t(),this._timeOffset=0,this._currentTime=0,this._lastTime=0,this._paused=!0,this._delay=0,this.overwriteTime=-1};ft.prototype._getTime=function(){return this._lastTime=(_t()-this._timeStart)/1e3,this._lastTime+this._timeOffset},ft.prototype.setDelay=function(t){this._delay=t,this.emitEvent("timeChange")},ft.prototype.isPlaying=function(){return!this._paused},ft.prototype.update=function(){if(!this._paused)return this._currentTime=this._getTime(),this._currentTime},ft.prototype.getMillis=function(){return 1e3*this.get()},ft.prototype.get=ft.prototype.getTime=function(){return this.overwriteTime>=0?this.overwriteTime-this._delay:this._currentTime-this._delay},ft.prototype.togglePlay=function(){this._paused?this.play():this.pause()},ft.prototype.setTime=function(t){(isNaN(t)||t<0)&&(t=0),this._timeStart=_t(),this._timeOffset=t,this._currentTime=t,this.emitEvent("timeChange")},ft.prototype.setOffset=function(t){this._currentTime+t<0?(this._timeStart=_t(),this._timeOffset=0,this._currentTime=0):(this._timeOffset+=t,this._currentTime=this._lastTime+this._timeOffset),this.emitEvent("timeChange")},ft.prototype.play=function(){this._timeStart=_t(),this._paused=!1,this.emitEvent("playPause")},ft.prototype.pause=function(){this._timeOffset=this._currentTime,this._paused=!0,this.emitEvent("playPause")};const mt=Math.PI/180,Et=(Math.PI,-1!=window.navigator.userAgent.indexOf("Windows")),Tt=function(t){let e;if(t.wheelDelta)e=t.wheelDelta%120-0==-0?t.wheelDelta/120:t.wheelDelta/30,e*=-1.5,Et&&(e*=2);else{let i=t.deltaY;t.shiftKey&&(i=t.deltaX);const s=i||t.detail;e=-(s%3?10*s:s/3),e*=-3}return e>20&&(e=20),e<-20&&(e=-20),e},At=Tt,bt=Tt,xt={"&":"&","<":"<",">":">",'"':""","'":"'"},vt=/[&<>"']/g,yt=RegExp(vt.source);const It=function(t,e){if(!t)throw new Error("shader constructed without cgl "+e);this._log=new a("cgl_shader"),this._cgl=t,e||this._log.stack("no shader name given"),this._name=e||"unknown",this.glslVersion=0,t.glVersion>1&&(this.glslVersion=300),this.id=x(),this._isValid=!0,this._program=null,this._uniforms=[],this._drawBuffers=[!0],this._defines=[],this._needsRecompile=!0,this._compileReason="initial",this._projMatrixUniform=null,this._mvMatrixUniform=null,this._mMatrixUniform=null,this._vMatrixUniform=null,this._camPosUniform=null,this._normalMatrixUniform=null,this._inverseViewMatrixUniform=null,this._attrVertexPos=-1,this.precision=t.patch.config.glslPrecision||"highp",this._pMatrixState=-1,this._vMatrixState=-1,this._modGroupCount=0,this._feedBackNames=[],this._attributes=[],this.glPrimitive=null,this.offScreenPass=!1,this._extensions=[],this.srcVert=this.getDefaultVertexShader(),this.srcFrag=this.getDefaultFragmentShader(),this.lastCompile=0,this._moduleNames=[],this._modules=[],this._moduleNumId=0,this._libs=[],this._structNames=[],this._structUniformNames=[],this._textureStackUni=[],this._textureStackTex=[],this._textureStackType=[],this._textureStackTexCgl=[],this._tempNormalMatrix=mat4.create(),this._tempCamPosMatrix=mat4.create(),this._tempInverseViewMatrix=mat4.create(),this._tempInverseProjMatrix=mat4.create(),this.setModules(["MODULE_VERTEX_POSITION","MODULE_COLOR","MODULE_BEGIN_FRAG"])};It.prototype.isValid=function(){return this._isValid},It.prototype.getCgl=function(){return this._cgl},It.prototype.getName=function(){return this._name},It.prototype.enableExtension=function(t){this.setWhyCompile("enable extension "+t),this._needsRecompile=!0,this._extensions.push(t)},It.prototype.getAttrVertexPos=function(){return this._attrVertexPos},It.prototype.hasTextureUniforms=function(){for(let t=0;t-1){const i=new pt[e];t=t.replace("{{"+e+"}}",i.srcHeadFrag),this._libs.push(i),i.initUniforms&&i.initUniforms(this)}return t},It.prototype.createStructUniforms=function(){let t="",e="";this._structNames=[],this._injectedStringsFrag={},this._injectedStringsVert={},this._structUniformNamesIndicesFrag=[],this._structUniformNamesIndicesVert=[];for(let i=0;it._name),e=[];for(let i=0;i-1&&e.push(i)}for(let t=this._uniforms.length-1;t>=0;t-=1)e.indexOf(t)>-1?this._uniforms.splice(t,1):this._uniforms[t].resetLoc()}this._cgl.printError("uniform resets"),this.hasTextureUniforms()&&(i+="#define HAS_TEXTURES".endl());let r="",n="";this.srcFrag||(this._log.error("[cgl shader] has no fragment source!",this),this.srcVert=this.getDefaultVertexShader(),this.srcFrag=this.getDefaultFragmentShader()),300==this.glslVersion?(r="#version 300 es".endl()+"// ".endl()+"// vertex shader "+this._name.endl()+"// ".endl()+"precision "+this.precision+" float;".endl()+"precision "+this.precision+" sampler2D;".endl()+"".endl()+"#define WEBGL2".endl()+"#define texture2D texture".endl()+"#define UNI uniform".endl()+"#define IN in".endl()+"#define OUT out".endl(),n="#version 300 es".endl()+"// ".endl()+"// fragment shader "+this._name.endl()+"// ".endl()+"precision "+this.precision+" float;".endl()+"precision "+this.precision+" sampler2D;".endl()+"".endl()+"#define WEBGL2".endl()+"#define texture2D texture".endl()+"#define IN in".endl()+"#define UNI uniform".endl()+"{{DRAWBUFFER}}".endl()):(n="".endl()+"// ".endl()+"// fragment shader "+this._name.endl()+"// ".endl()+"#define WEBGL1".endl()+"#define texture texture2D".endl()+"#define outColor gl_FragColor".endl()+"#define IN varying".endl()+"#define UNI uniform".endl(),r="".endl()+"// ".endl()+"// vertex shader "+this._name.endl()+"// ".endl()+"#define WEBGL1".endl()+"#define texture texture2D".endl()+"#define OUT varying".endl()+"#define IN attribute".endl()+"#define UNI uniform".endl());let o="\n// cgl generated".endl(),a="\n// cgl generated".endl();n+="\n// active mods: --------------- ",r+="\n// active mods: --------------- ";let h=!1,l=!1;for(let t=0;t=this._cgl.maxUniformsFrag&&this._log.warn("[cgl_shader] num uniforms frag: "+c+" / "+this._cgl.maxUniformsFrag),u>=this._cgl.maxUniformsVert&&this._log.warn("[cgl_shader] num uniforms vert: "+u+" / "+this._cgl.maxUniformsVert),-1==n.indexOf("precision")&&(n="precision "+this.precision+" float;".endl()+n),-1==r.indexOf("precision")&&(r="precision "+this.precision+" float;".endl()+r),/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)&&(n+="#define MOBILE".endl(),r+="#define MOBILE".endl()),r=e+r+i+s[0]+o+"\n// -- \n"+this.srcVert,n=e+n+i+s[1]+a+"\n// -- \n"+this.srcFrag;let g="",p="";this._modules.sort((function(t,e){return t.group-e.group})),this._modules.sort((function(t,e){return t.priority||0-e.priority||0}));let _=!1;for(let t=0;t-1&&(this._drawBuffers[0]=!0),n.indexOf("outColor1")>-1&&(this._drawBuffers[1]=!0),n.indexOf("outColor2")>-1&&(this._drawBuffers[2]=!0),n.indexOf("outColor3")>-1&&(this._drawBuffers[3]=!0),1==this._drawBuffers.length)d="out vec4 outColor;".endl(),d+="#define gl_FragColor outColor".endl();else{d+="#define MULTI_COLORTARGETS".endl(),d+="vec4 outColor;".endl();let t=0;for(let e=0;e{this.toggleDefine(t,e)},e.changeListener=e.on("change",e.onToggleDefine),e=e.get()),e?this.define(t):this.removeDefine(t)},It.prototype.define=function(t,e){null==e&&(e=""),"object"==typeof e&&(e.removeEventListener("change",e.onDefineChange),e.onDefineChange=e=>{this.define(t,e)},e.on("change",e.onDefineChange),e=e.get());for(let i=0;i0&&this._cgl.gl.transformFeedbackVaryings(t,this._feedBackNames,this._cgl.gl.SEPARATE_ATTRIBS),this._cgl.gl.linkProgram(t),this._cgl.printError("gl.linkprogram"),this._isValid=!0,this._hasErrors=!1,!1!==this._cgl.patch.config.glValidateShader&&(this._cgl.gl.validateProgram(t),this._cgl.gl.getProgramParameter(t,this._cgl.gl.VALIDATE_STATUS)||(console.log("shaderprogram validation failed..."),console.log(this._name+" programinfo: ",this._cgl.gl.getProgramInfoLog(t))),this._cgl.gl.getProgramParameter(t,this._cgl.gl.LINK_STATUS)||(this._hasErrors=!0,this._log.warn(this._cgl.gl.getShaderInfoLog(this.fshader)||"empty shader infolog"),this._log.warn(this._cgl.gl.getShaderInfoLog(this.vshader)||"empty shader infolog"),this._log.error(this._name+" shader linking fail..."),console.log(this._name+" programinfo: ",this._cgl.gl.getProgramInfoLog(t)),console.log("--------------------------------------"),console.log(this),console.log("--------------------------------------"),this._isValid=!1,this._name="errorshader",this.setSource(It.getDefaultVertexShader(),It.getErrorFragmentShader()),this._cgl.printError("shader link err")))},It.prototype.getProgram=function(){return this._program},It.prototype.setFeedbackNames=function(t){this.setWhyCompile("setFeedbackNames"),this._needsRecompile=!0,this._feedBackNames=t},It.prototype.getDefaultVertexShader=It.getDefaultVertexShader=function(){return"{{MODULES_HEAD}}\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\nIN vec3 attrTangent,attrBiTangent;\n\nIN float attrVertIndex;\n\nOUT vec2 texCoord;\nOUT vec3 norm;\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\nvoid main()\n{\n texCoord=attrTexCoord;\n norm=attrVertNormal;\n vec4 pos=vec4(vPosition, 1.0);\n vec3 tangent=attrTangent;\n vec3 bitangent=attrBiTangent;\n mat4 mMatrix=modelMatrix;\n {{MODULE_VERTEX_POSITION}}\n gl_Position = projMatrix * (viewMatrix*mMatrix) * pos;\n}\n"},It.prototype.getDefaultFragmentShader=It.getDefaultFragmentShader=function(t,e,i){return null==t&&(t=.5,e=.5,i=.5),"".endl()+"IN vec2 texCoord;".endl()+"{{MODULES_HEAD}}".endl()+"void main()".endl()+"{".endl()+" vec4 col=vec4("+t+","+e+","+i+",1.0);".endl()+" {{MODULE_COLOR}}".endl()+" outColor = col;".endl()+"}"},It.prototype.addAttribute=function(t){for(let e=0;ethis._cgl.maxTextureUnits&&this._log.warn("[shader._bindTextures] too many textures bound",this._textureStackTex.length+"/"+this._cgl.maxTextureUnits);for(let t=0;t",h+='
    '),h+=(n=i)&&yt.test(n)?n.replace(vt,(function(t){return xt[t]})):n||"",s&&(h+="
    ",h+='
    ')}console.warn(o),o=o.replace(/\n/g,"
    "),h=o+"
    "+h+"

    ",h+="
    ",t.patch.emitEvent("criticalError",{title:"Shader error "+this._name,text:h}),t.patch.isEditorMode()&&console.log("Shader error "+this._name,h),this._name="errorshader",s.setSource(It.getDefaultVertexShader(),It.getErrorFragmentShader())}var n;return r};class St{constructor(t){this._cgl=t,this._lastTime=0,this.pause=!1,this.profileUniformCount=0,this.profileShaderBinds=0,this.profileUniformCount=0,this.profileShaderCompiles=0,this.profileVideosPlaying=0,this.profileMVPMatrixCount=0,this.profileEffectBuffercreate=0,this.profileShaderGetUniform=0,this.profileFrameBuffercreate=0,this.profileMeshSetGeom=0,this.profileTextureNew=0,this.profileGenMipMap=0,this.profileOnAnimFrameOps=0,this.profileMainloopMs=0,this.profileMeshDraw=0,this.profileTextureEffect=0,this.profileTexPreviews=0,this.shaderCompileTime=0,this.profileMeshNumElements=0,this.profileMeshAttributes=0,this.profileSingleMeshAttribute=[],this.heavyEvents=[],this.doProfileGlQuery=!1,this.glQueryData={}}clear(){this.profileSingleMeshAttribute={},this.profileMeshAttributes=0,this.profileUniformCount=0,this.profileShaderGetUniform=0,this.profileShaderCompiles=0,this.profileShaderBinds=0,this.profileTextureResize=0,this.profileFrameBuffercreate=0,this.profileEffectBuffercreate=0,this.profileTextureDelete=0,this.profileMeshSetGeom=0,this.profileVideosPlaying=0,this.profileMVPMatrixCount=0,this.profileNonTypedAttrib=0,this.profileNonTypedAttribNames="",this.profileTextureNew=0,this.profileGenMipMap=0,this.profileFramebuffer=0,this.profileMeshDraw=0,this.profileTextureEffect=0,this.profileTexPreviews=0,this.profileMeshNumElements=0}clearGlQuery(){for(let t in this.glQueryData)(!this.glQueryData[t].lastClear||performance.now()-this.glQueryData[t].lastClear>1e3)&&(this.glQueryData[t].time=this.glQueryData[t]._times/this.glQueryData[t]._numcount,this.glQueryData[t].num=this.glQueryData[t]._numcount,this.glQueryData[t]._times=0,this.glQueryData[t]._numcount=0,this.glQueryData[t].lastClear=performance.now())}addHeavyEvent(t,e,i){const s={event:t,name:e,info:i,date:performance.now()};this.heavyEvents.push(s),this._cgl.emitEvent("heavyEvent",s)}}const Rt={GAPI_WEBGL:0,GAPI_WEBGPU:1,Geometry:W,BoundingBox:z,FpsCounter:class extends Y{constructor(){super(),this._timeStartFrame=0,this._timeStartSecond=0,this._fpsCounter=0,this._msCounter=0,this.stats={ms:0,fps:0}}startFrame(){this._timeStartFrame=CABLES.now()}endFrame(){this._fpsCounter++;const t=CABLES.now()-this._timeStartFrame;this._msCounter+=t,CABLES.now()-this._timeStartSecond>1e3&&this.endSecond()}endSecond(){this.stats.fps=this._fpsCounter,this.stats.ms=Math.round(this._msCounter/this._fpsCounter*100)/100,this.emitEvent("performance",this.stats),this._fpsCounter=0,this._msCounter=0,this._timeStartSecond=CABLES.now()}}},Pt=function(t){Ut.apply(this),this.patch=t,this.gApi=Rt.GAPI_WEBGPU,this._viewport=[0,0,256,256],this._shaderStack=[],this.getViewPort=function(){return[0,0,this.canvasWidth,this.canvasHeight]},this.renderStart=function(t,e,i){this.fpsCounter.startFrame(),this._startMatrixStacks(e,i),this.setViewPort(0,0,this.canvasWidth,this.canvasHeight),this.emitEvent("beginFrame")},this.renderEnd=function(t){this._endMatrixStacks(),this.emitEvent("endFrame"),this.fpsCounter.endFrame()}};Pt.prototype.setViewPort=function(t,e,i,s){this._viewport=[t,e,i,s]},Pt.prototype.getViewPort=function(){return this._viewPort},Pt.prototype.createMesh=function(t,e){return new CGP.Mesh(this,t,e)},Pt.prototype.getShader=function(){return{}},Pt.prototype.pushShader=function(t){this._shaderStack.push(t)},Pt.prototype.popShader=function(){if(0===this._shaderStack.length)throw new Error("Invalid shader stack pop!");this._shaderStack.pop()},Pt.prototype.getShader=function(){return this._shaderStack[this._shaderStack.length-1]},Pt.prototype.pushErrorScope=function(){this.device.pushErrorScope("validation")},Pt.prototype.popErrorScope=function(t,e){this.device.popErrorScope().then(i=>{i&&(this.patch.emitEvent("criticalError",{title:'WebGPU error "'+t+'"',codeText:i.message}),console.warn("[cgp]",t,i.message,i,e),e&&e(i))})};class Ot extends rt{constructor(t,e,i,s,r,n,o,a,h,l){super(t,e,i,s,r,n,o,a,h,l),this._loc=-1,this._cgl=t._cgl}updateValueF(){}setValueF(t){this.needsUpdate=!0,this._value=t}updateValue2F(){}setValue2F(t){this.needsUpdate=!0,this._value=t}updateValue3F(){}setValue3F(t){this.needsUpdate=!0,this._value=t}updateValue4F(){}setValue4F(t){this.needsUpdate=!0,this._value=t}getSizeBytes(){return"f"==this._type||"i"==this._type?4:"2i"==this._type||"2f"==this._type?8:"3f"==this._type?12:"4f"==this._type?16:"m4"==this._type?64:void this._log.warn("unknown type getSizeBytes")}}class Nt{constructor(t,e){this._shaderType=e,this._shader=t,this._cgp=t._cgp,this._gpuBuffer=null,this._values=null,this._sizeBytes=0,this.update()}update(){this._sizeBytes=0;for(let t=0;t1)for(let e=0;e{this._isValid=!1})}}class Ct{constructor(t,e){if(!t)throw new Error("no cgp");this._log=new a("cgp_texture"),this._cgp=t,this.id=CABLES.uuid(),e=e||{},this.name=e.name||"unknown"}initTexture(t,e){this.width=t.width,this.height=t.height,this.textureType="rgba8unorm";const i={size:{width:t.width,height:t.height},format:this.textureType,usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST},s=this._cgp.device.createTexture(i);return this._cgp.device.queue.copyExternalImageToTexture({source:t},{texture:s},i.size),s}getInfo(){const t={};return t.name=this.name,t.size=this.width+" x "+this.height,t.textureType=this.textureType,t}}Ct.load=function(t,e,i,s){fetch(e).then(s=>{s.blob().then(s=>{createImageBitmap(s).then(s=>{const r=new Ct(t,{name:e});r.initTexture(s),i?i(r):console.log("Texture.load no onFinished callback")})})})};const wt=Object.assign({Context:Pt,Shader:class{constructor(t,e){if(!t)throw new Error("shader constructed without cgp "+e);this._log=new a("cgp_shader"),this._cgp=t,this._name=e,this._uniforms=[],e||this._log.stack("no shader name given"),this._name=e||"unknown",this.id=x(),this._isValid=!0,this._compileReason="",this.shaderModule=null,this._needsRecompile=!0,this._src=""}get isValid(){return this._isValid}get uniforms(){return this._uniforms}getName(){return this._name}setWhyCompile(t){this._compileReason=t}setSource(t){this._src=t,this.setWhyCompile("Source changed"),this._needsRecompile=!0}compile(){this._isValid=!0,console.log("compiling shader...",this._compileReason),this._cgp.pushErrorScope(),this.shaderModule=this._cgp.device.createShaderModule({code:this._src}),this._cgp.popErrorScope("cgp_shader "+this._name,this.error.bind(this)),this._needsRecompile=!1}error(t){this._isValid=!1}bind(){for(let t=0;t["WebGL","WebGPU"][this.gApi],this.setCanvas=function(t){this.canvas="string"==typeof t?document.getElementById(t):t,this._setCanvas&&this._setCanvas(t),this.updateSize()},this.updateSize=function(){this.canvas.width=this.canvasWidth=this.canvas.clientWidth*this.pixelDensity,this.canvas.height=this.canvasHeight=this.canvas.clientHeight*this.pixelDensity},this.setSize=function(t,e,i){i||(this.canvas.style.width=t+"px",this.canvas.style.height=e+"px"),this.canvas.width=t*this.pixelDensity,this.canvas.height=e*this.pixelDensity,this.updateSize()},this._resizeToWindowSize=function(){this.setSize(window.innerWidth,window.innerHeight),this.updateSize()},this._resizeToParentSize=function(){const t=this.canvas.parentElement;t?(this.setSize(t.clientWidth,t.clientHeight),this.updateSize()):this._log.error("cables: can not resize to container element")},this.setAutoResize=function(t){window.removeEventListener("resize",this._resizeToWindowSize.bind(this)),window.removeEventListener("resize",this._resizeToParentSize.bind(this)),"window"==t&&(window.addEventListener("resize",this._resizeToWindowSize.bind(this)),window.addEventListener("orientationchange",this._resizeToWindowSize.bind(this)),this._resizeToWindowSize()),"parent"==t&&(window.addEventListener("resize",this._resizeToParentSize.bind(this)),this._resizeToParentSize())},this.pushPMatrix=function(){this.pMatrix=this._pMatrixStack.push(this.pMatrix)},this.popPMatrix=function(){return this.pMatrix=this._pMatrixStack.pop(),this.pMatrix},this.getProjectionMatrixStateCount=function(){return this._pMatrixStack.stateCounter},this.pushModelMatrix=function(){this.mMatrix=this._mMatrixStack.push(this.mMatrix)},this.popModelMatrix=function(){return this.mMatrix=this._mMatrixStack.pop(),this.mMatrix},this.modelMatrix=function(){return this.mMatrix},this.pushViewMatrix=function(){this.vMatrix=this._vMatrixStack.push(this.vMatrix)},this.popViewMatrix=function(){this.vMatrix=this._vMatrixStack.pop()},this.getViewMatrixStateCount=function(){return this._vMatrixStack.stateCounter},this._startMatrixStacks=(t,e)=>{t=t||this._ident,e=e||this._identView,mat4.perspective(this.pMatrix,45,this.canvasWidth/this.canvasHeight,.1,1e3),mat4.identity(this.mMatrix),mat4.identity(this.vMatrix),mat4.translate(this.mMatrix,this.mMatrix,t),mat4.translate(this.vMatrix,this.vMatrix,e),this.pushPMatrix(),this.pushModelMatrix(),this.pushViewMatrix()},this._endMatrixStacks=()=>{this.popViewMatrix(),this.popModelMatrix(),this.popPMatrix()}},Bt=function(t){Ut.apply(this),this.gApi=Rt.GAPI_WEBGL,this.pushMvMatrix=this.pushModelMatrix,this.popMvMatrix=this.popmMatrix=this.popModelMatrix,this.profileData=new St(this),this._log=new a("cgl_context");const e=[0,0,0,0];this.glVersion=0,this.glUseHalfFloatTex=!1,this.clearCanvasTransparent=!0,this.clearCanvasDepth=!0,this.patch=t,this.debugOneFrame=!1,this.checkGlErrors=!0,this.maxTextureUnits=0,this.maxVaryingVectors=0,this.currentProgram=null,this._hadStackError=!1,this.glSlowRenderer=!1,this._isSafariCrap=!1,this.temporaryTexture=null,this.frameStore={},this._onetimeCallbacks=[],this.gl=null,this._cursor="auto",this._currentCursor="",this._glFrameBufferStack=[],this._frameBufferStack=[],this._shaderStack=[],this._stackDepthTest=[],Object.defineProperty(this,"mvMatrix",{get(){return this.mMatrix},set(t){this.mMatrix=t}});const i=new It(this,"simpleshader");i.setModules(["MODULE_VERTEX_POSITION","MODULE_COLOR","MODULE_BEGIN_FRAG"]),i.setSource(It.getDefaultVertexShader(),It.getDefaultFragmentShader());let s=i;this.aborted=!1;const r=[];this.exitError=function(t,e){this.patch.exitError(t,e),this.aborted=!0},this._setCanvas=function(t){if(this.patch.config.canvas||(this.patch.config.canvas={}),this.patch.config.canvas.hasOwnProperty("preserveDrawingBuffer")||(this.patch.config.canvas.preserveDrawingBuffer=!1),this.patch.config.canvas.hasOwnProperty("premultipliedAlpha")||(this.patch.config.canvas.premultipliedAlpha=!1),this.patch.config.canvas.hasOwnProperty("alpha")||(this.patch.config.canvas.alpha=!1),this.patch.config.canvas.stencil=!0,this.patch.config.hasOwnProperty("clearCanvasColor")&&(this.clearCanvasTransparent=this.patch.config.clearCanvasColor),this.patch.config.hasOwnProperty("clearCanvasDepth")&&(this.clearCanvasDepth=this.patch.config.clearCanvasDepth),/^((?!chrome|android).)*safari/i.test(navigator.userAgent)&&(this._isSafariCrap=!0),this.patch.config.canvas.forceWebGl1||(this.gl=this.canvas.getContext("webgl2",this.patch.config.canvas)),this.gl&&"WebGL 1.0"!=this.gl.getParameter(this.gl.VERSION)?this.glVersion=2:(this.gl=this.canvas.getContext("webgl",this.patch.config.canvas)||this.canvas.getContext("experimental-webgl",this.patch.config.canvas),this.glVersion=1,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)&&navigator.userAgent.match(/iPhone/i)&&(this.glUseHalfFloatTex=!0),/iPad|iPhone|iPod/.test(navigator.userAgent)&&!window.MSStream&&(this.patch.config.canvas.hasOwnProperty("powerPreference")||(this.patch.config.canvas.powerPreference="high-performance"))),!this.gl)return void this.exitError("NO_WEBGL","sorry, could not initialize WebGL. Please check if your Browser supports WebGL or try to restart your browser.");const e=this.gl.getExtension("WEBGL_debug_renderer_info");e&&(this.glRenderer=this.gl.getParameter(e.UNMASKED_RENDERER_WEBGL),"Google SwiftShader"===this.glRenderer&&(this.glSlowRenderer=!0)),this.gl.getExtension("OES_standard_derivatives");const i=this.gl.getExtension("ANGLE_instanced_arrays")||this.gl;this.canvas.addEventListener("webglcontextlost",t=>{this._log.error("canvas lost...",t),this.emitEvent("webglcontextlost"),this.aborted=!0}),this.maxVaryingVectors=this.gl.getParameter(this.gl.MAX_VARYING_VECTORS),this.maxTextureUnits=this.gl.getParameter(this.gl.MAX_TEXTURE_IMAGE_UNITS),this.maxTexSize=this.gl.getParameter(this.gl.MAX_TEXTURE_SIZE),this.maxUniformsFrag=this.gl.getParameter(this.gl.MAX_FRAGMENT_UNIFORM_VECTORS),this.maxUniformsVert=this.gl.getParameter(this.gl.MAX_VERTEX_UNIFORM_VECTORS),this.maxSamples=0,this.gl.MAX_SAMPLES&&(this.maxSamples=this.gl.getParameter(this.gl.MAX_SAMPLES)),i.vertexAttribDivisorANGLE&&(this.gl.vertexAttribDivisor=i.vertexAttribDivisorANGLE.bind(i),this.gl.drawElementsInstanced=i.drawElementsInstancedANGLE.bind(i))},this.getInfo=function(){return{glVersion:this.glVersion,glRenderer:this.glRenderer,glUseHalfFloatTex:this.glUseHalfFloatTex,maxVaryingVectors:this.maxVaryingVectors,maxTextureUnits:this.maxTextureUnits,maxTexSize:this.maxTexSize,maxUniformsFrag:this.maxUniformsFrag,maxUniformsVert:this.maxUniformsVert,maxSamples:this.maxSamples}};let n=-1,o=-1;this.getViewPort=function(){return e},this.resetViewPort=function(){this.gl.viewport(e[0],e[1],e[2],e[3])},this.setViewPort=function(t,i,s,r){e[0]=Math.round(t),e[1]=Math.round(i),e[2]=Math.round(s),e[3]=Math.round(r),this.gl.viewport(e[0],e[1],e[2],e[3])},this.screenShot=function(t,e,i,s){e&&(this.gl.clearColor(1,1,1,1),this.gl.colorMask(!1,!1,!1,!0),this.gl.clear(this.gl.COLOR_BUFFER_BIT),this.gl.colorMask(!0,!0,!0,!0)),this.canvas&&this.canvas.toBlob&&this.canvas.toBlob(e=>{t?t(e):this._log.log("no screenshot callback...")},i,s)},this.endFrame=function(){if(this.patch.isEditorMode()&&CABLES.GL_MARKER.drawMarkerLayer(this),this.setPreviousShader(),this._vMatrixStack.length()>0&&this.logStackError("view matrix stack length !=0 at end of rendering..."),this._mMatrixStack.length()>0&&this.logStackError("mvmatrix stack length !=0 at end of rendering..."),this._pMatrixStack.length()>0&&this.logStackError("pmatrix stack length !=0 at end of rendering..."),this._glFrameBufferStack.length>0&&this.logStackError("glFrameBuffer stack length !=0 at end of rendering..."),this._stackDepthTest.length>0&&this.logStackError("depthtest stack length !=0 at end of rendering..."),this._stackDepthWrite.length>0&&this.logStackError("depthwrite stack length !=0 at end of rendering..."),this._stackDepthFunc.length>0&&this.logStackError("depthfunc stack length !=0 at end of rendering..."),this._stackBlend.length>0&&this.logStackError("blend stack length !=0 at end of rendering..."),this._stackBlendMode.length>0&&this.logStackError("blendMode stack length !=0 at end of rendering..."),this._shaderStack.length>0&&this.logStackError("this._shaderStack length !=0 at end of rendering..."),this._stackCullFace.length>0&&this.logStackError("this._stackCullFace length !=0 at end of rendering..."),this._stackCullFaceFacing.length>0&&this.logStackError("this._stackCullFaceFacing length !=0 at end of rendering..."),this._frameStarted=!1,n!=this.canvasWidth||o!=this.canvasHeight){n=this.canvasWidth,o=this.canvasHeight,this.setSize(this.canvasWidth/this.pixelDensity,this.canvasHeight/this.pixelDensity),this.updateSize();for(let t=0;t=0;t--)if(this._shaderStack[t]&&this.frameStore.renderOffscreen==this._shaderStack[t].offScreenPass)return this._shaderStack[t]},this.getDefaultShader=function(){return i},this.pushShader=this.setShader=function(t){this._shaderStack.push(t),s=t},this.popShader=this.setPreviousShader=function(){if(0===this._shaderStack.length)throw new Error("Invalid shader stack pop!");this._shaderStack.pop(),s=this._shaderStack[this._shaderStack.length-1]},this.pushGlFrameBuffer=function(t){this._glFrameBufferStack.push(t)},this.popGlFrameBuffer=function(){return 0==this._glFrameBufferStack.length?null:(this._glFrameBufferStack.pop(),this._glFrameBufferStack[this._glFrameBufferStack.length-1])},this.getCurrentGlFrameBuffer=function(){return 0===this._glFrameBufferStack.length?null:this._glFrameBufferStack[this._glFrameBufferStack.length-1]},this.pushFrameBuffer=function(t){this._frameBufferStack.push(t)},this.popFrameBuffer=function(){return 0==this._frameBufferStack.length?null:(this._frameBufferStack.pop(),this._frameBufferStack[this._frameBufferStack.length-1])},this.getCurrentFrameBuffer=function(){return 0===this._frameBufferStack.length?null:this._frameBufferStack[this._frameBufferStack.length-1]},this.renderStart=function(t,e,s){this.fpsCounter.startFrame(),this.pushDepthTest(!0),this.pushDepthWrite(!0),this.pushDepthFunc(t.gl.LEQUAL),this.pushCullFaceFacing(t.gl.BACK),this.pushCullFace(!1),this.clearCanvasTransparent&&(t.gl.clearColor(0,0,0,0),t.gl.clear(t.gl.COLOR_BUFFER_BIT)),this.clearCanvasDepth&&t.gl.clear(t.gl.DEPTH_BUFFER_BIT),t.setViewPort(0,0,t.canvasWidth,t.canvasHeight),this._startMatrixStacks(e,s),t.pushBlendMode(at.BLEND_MODES.BLEND_NORMAL,!1);for(let t=0;t0){for(let t=0;t0&&this.gl.cullFace(this._stackCullFaceFacing[this._stackCullFaceFacing.length-1])},Bt.prototype._stackDepthFunc=[],Bt.prototype.pushDepthFunc=function(t){this._stackDepthFunc.push(t),this.gl.depthFunc(t)},Bt.prototype.stateDepthFunc=function(){return this._stackDepthFunc.length>0&&this._stackDepthFunc[this._stackDepthFunc.length-1]},Bt.prototype.popDepthFunc=function(){this._stackDepthFunc.pop(),this._stackDepthFunc.length>0&&this.gl.depthFunc(this._stackDepthFunc[this._stackDepthFunc.length-1])},Bt.prototype._stackBlend=[],Bt.prototype.pushBlend=function(t){this._stackBlend.push(t),t?this.gl.enable(this.gl.BLEND):this.gl.disable(this.gl.BLEND)},Bt.prototype.popBlend=function(){this._stackBlend.pop(),this._stackBlend[this._stackBlend.length-1]?this.gl.enable(this.gl.BLEND):this.gl.disable(this.gl.BLEND)},Bt.prototype.stateBlend=function(){return this._stackBlend[this._stackBlend.length-1]};Bt.prototype._stackBlendMode=[],Bt.prototype._stackBlendModePremul=[],Bt.prototype.pushBlendMode=function(t,e){this._stackBlendMode.push(t),this._stackBlendModePremul.push(e);const i=this._stackBlendMode.length-1;this.pushBlend(this._stackBlendMode[i]!==at.BLEND_MODES.BLEND_NONE),this._setBlendMode(this._stackBlendMode[i],this._stackBlendModePremul[i])},Bt.prototype.popBlendMode=function(){this._stackBlendMode.pop(),this._stackBlendModePremul.pop();const t=this._stackBlendMode.length-1;this.popBlend(this._stackBlendMode[t]!==at.BLEND_MODES.BLEND_NONE),t>=0&&this._setBlendMode(this._stackBlendMode[t],this._stackBlendModePremul[t])},Bt.prototype._stackStencil=[],Bt.prototype.pushStencil=function(t){this._stackStencil.push(t),t?this.gl.enable(this.gl.STENCIL_TEST):this.gl.disable(this.gl.STENCIL_TEST)},Bt.prototype.popStencil=function(){this._stackStencil.pop(),this._stackStencil[this._stackStencil.length-1]?this.gl.enable(this.gl.STENCIL_TEST):this.gl.disable(this.gl.STENCIL_TEST)},Bt.prototype.glGetAttribLocation=function(t,e){return this.gl.getAttribLocation(t,e)},Bt.prototype.shouldDrawHelpers=function(t){if(this.frameStore.shadowPass)return!1;if(!t.patch.isEditorMode())return!1;const e=this.getCurrentFrameBuffer();if(e&&e.getWidth){if(!(this.canvasWidth/this.canvasHeight==e.getWidth()/e.getHeight()))return!1}return CABLES.UI.renderHelper||CABLES.UI.renderHelperCurrent&&t.isCurrentUiOp()},Bt.prototype._setBlendMode=function(t,e){const i=this.gl;t==at.BLEND_MODES.BLEND_NONE||(t==at.BLEND_MODES.BLEND_ADD?e?(i.blendEquationSeparate(i.FUNC_ADD,i.FUNC_ADD),i.blendFuncSeparate(i.ONE,i.ONE,i.ONE,i.ONE)):(i.blendEquation(i.FUNC_ADD),i.blendFunc(i.SRC_ALPHA,i.ONE)):t==at.BLEND_MODES.BLEND_SUB?e?(i.blendEquationSeparate(i.FUNC_ADD,i.FUNC_ADD),i.blendFuncSeparate(i.ZERO,i.ZERO,i.ONE_MINUS_SRC_COLOR,i.ONE_MINUS_SRC_ALPHA)):(i.blendEquation(i.FUNC_ADD),i.blendFunc(i.ZERO,i.ONE_MINUS_SRC_COLOR)):t==at.BLEND_MODES.BLEND_MUL?e?(i.blendEquationSeparate(i.FUNC_ADD,i.FUNC_ADD),i.blendFuncSeparate(i.ZERO,i.SRC_COLOR,i.ZERO,i.SRC_ALPHA)):(i.blendEquation(i.FUNC_ADD),i.blendFunc(i.ZERO,i.SRC_COLOR)):t==at.BLEND_MODES.BLEND_NORMAL?e?(i.blendEquationSeparate(i.FUNC_ADD,i.FUNC_ADD),i.blendFuncSeparate(i.ONE,i.ONE_MINUS_SRC_ALPHA,i.ONE,i.ONE_MINUS_SRC_ALPHA)):(i.blendEquationSeparate(i.FUNC_ADD,i.FUNC_ADD),i.blendFuncSeparate(i.SRC_ALPHA,i.ONE_MINUS_SRC_ALPHA,i.ONE,i.ONE_MINUS_SRC_ALPHA)):this._log.log("setblendmode: unknown blendmode"))},Bt.prototype.createMesh=function(t,e){return new CGL.Mesh(this,t,e)},Bt.prototype.setCursor=function(t){this._cursor=t};const Lt=Object.assign({Framebuffer:function(t,e,i,s){const r=t;this._log=new a("Framebuffer");const n=r.gl.getExtension("WEBGL_depth_texture")||r.gl.getExtension("WEBKIT_WEBGL_depth_texture")||r.gl.getExtension("MOZ_WEBGL_depth_texture")||r.gl.DEPTH_TEXTURE;n||r.exitError("NO_DEPTH_TEXTURE","no depth texture support");let o=e||512,h=i||512;(s=s||{isFloatingPointTexture:!1}).hasOwnProperty("clear")||(s.clear=!0),s.hasOwnProperty("filter")||(s.filter=B.FILTER_LINEAR);const l=new B(r,{isFloatingPointTexture:s.isFloatingPointTexture,filter:s.filter,wrap:s.wrap||B.CLAMP_TO_EDGE});let c=null;n&&(c=new B(r,{isDepthTexture:!0})),this._options=s;const u=r.gl.createFramebuffer(),g=r.gl.createRenderbuffer();this.getWidth=function(){return o},this.getHeight=function(){return h},this.getGlFrameBuffer=function(){return u},this.getDepthRenderBuffer=function(){return g},this.getTextureColor=function(){return l},this.getTextureDepth=function(){return c},this.setFilter=function(t){l.filter=t,l.setSize(o,h)},this.setSize=function(t,e){if(t<2&&(t=2),e<2&&(e=2),o=Math.ceil(t),h=Math.ceil(e),r.profileData.profileFrameBuffercreate++,r.gl.bindFramebuffer(r.gl.FRAMEBUFFER,u),r.gl.bindRenderbuffer(r.gl.RENDERBUFFER,g),l.setSize(o,h),c&&c.setSize(o,h),n&&r.gl.renderbufferStorage(r.gl.RENDERBUFFER,r.gl.DEPTH_COMPONENT16,o,h),r.gl.framebufferTexture2D(r.gl.FRAMEBUFFER,r.gl.COLOR_ATTACHMENT0,r.gl.TEXTURE_2D,l.tex,0),n&&(r.gl.framebufferRenderbuffer(r.gl.FRAMEBUFFER,r.gl.DEPTH_ATTACHMENT,r.gl.RENDERBUFFER,g),r.gl.framebufferTexture2D(r.gl.FRAMEBUFFER,r.gl.DEPTH_ATTACHMENT,r.gl.TEXTURE_2D,c.tex,0)),!r.gl.isFramebuffer(u))throw new Error("Invalid framebuffer");const i=r.gl.checkFramebufferStatus(r.gl.FRAMEBUFFER);switch(i){case r.gl.FRAMEBUFFER_COMPLETE:break;case r.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:throw this._log.warn("FRAMEBUFFER_INCOMPLETE_ATTACHMENT...",o,h,l.tex,g),new Error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT");case r.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:throw this._log.warn("FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"),new Error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");case r.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:throw this._log.warn("FRAMEBUFFER_INCOMPLETE_DIMENSIONS"),new Error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS");case r.gl.FRAMEBUFFER_UNSUPPORTED:throw this._log.warn("FRAMEBUFFER_UNSUPPORTED"),new Error("Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED");case 36059:this._log.warn("Incomplete: FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER from ext. Or Safari/iOS undefined behaviour.");break;default:throw this._log.warn("incomplete framebuffer",i),new Error("Incomplete framebuffer: "+i)}r.gl.bindTexture(r.gl.TEXTURE_2D,null),r.gl.bindRenderbuffer(r.gl.RENDERBUFFER,null),r.gl.bindFramebuffer(r.gl.FRAMEBUFFER,null)},this.renderStart=function(){r.pushModelMatrix(),r.gl.bindFramebuffer(r.gl.FRAMEBUFFER,u),r.pushGlFrameBuffer(u),r.pushFrameBuffer(this),r.pushPMatrix(),r.gl.viewport(0,0,o,h),this._options.clear&&(r.gl.clearColor(0,0,0,0),r.gl.clear(r.gl.COLOR_BUFFER_BIT|r.gl.DEPTH_BUFFER_BIT))},this.renderEnd=function(){r.popPMatrix(),r.gl.bindFramebuffer(r.gl.FRAMEBUFFER,r.popGlFrameBuffer()),r.popFrameBuffer(),r.popModelMatrix(),r.resetViewPort()},this.delete=function(){l.delete(),c&&c.delete(),r.gl.deleteRenderbuffer(g),r.gl.deleteFramebuffer(u)},this.setSize(o,h)},Framebuffer2:L,Geometry:W,BoundingBox:z,Marker:function(t){const e=new W("marker");e.setPointVertices([1e-5,0,0,1,0,0,0,1e-5,0,0,1,0,0,0,1e-5,0,0,1]);const i=new lt(t,e,t.gl.LINES);i.setGeom(e);const s=new It(t,"markermaterial"),r="".endl()+"precision highp float;".endl()+"IN vec3 axisColor;".endl()+"void main()".endl()+"{".endl()+" vec4 col=vec4(axisColor,1.0);".endl()+" outColor = col;".endl()+"}",n="".endl()+"IN vec3 vPosition;".endl()+"UNI mat4 projMatrix;".endl()+"UNI mat4 mvMatrix;".endl()+"OUT vec3 axisColor;".endl()+"void main()".endl()+"{".endl()+" vec4 pos=vec4(vPosition, 1.0);".endl()+" if(pos.x!=0.0)axisColor=vec3(1.0,0.3,0.0);".endl()+" if(pos.y!=0.0)axisColor=vec3(0.0,1.0,0.2);".endl()+" if(pos.z!=0.0)axisColor=vec3(0.0,0.5,1.0);".endl()+" gl_Position = projMatrix * mvMatrix * pos;".endl()+"}";s.setSource(n,r),this._vScale=vec3.create(),this.draw=function(t,e,r){const n=e||2;t.pushModelMatrix(),t.pushShader(s),vec3.set(this._vScale,n,n,n),mat4.scale(t.mvMatrix,t.mvMatrix,this._vScale),t.pushDepthTest(1==r),i.render(t.getShader()),t.popDepthTest(),t.popShader(),t.popModelMatrix()}},WirePoint:function(t){const e=t.gl.createBuffer(),i=vec3.create();this.render=function(t,s){t.pushModelMatrix(),vec3.set(i,s,s,s),mat4.scale(t.mvMatrix,t.mvMatrix,i);const r=t.getShader();r&&(r.bind(),t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.vertexAttribPointer(r.getAttrVertexPos(),e.itemSize,t.gl.FLOAT,!1,0,0),t.gl.enableVertexAttribArray(r.getAttrVertexPos()),t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.drawArrays(t.gl.LINE_STRIP,0,e.numItems)),t.popModelMatrix()},function(){const i=[];let s=0,r=0;for(s=0;s<=Math.round(24);s++)r=360/Math.round(24)*s*mt,i.push(.5*Math.cos(r)),i.push(0),i.push(.5*Math.sin(r));for(s=0;s<=Math.round(24);s++)r=360/Math.round(24)*s*mt,i.push(.5*Math.cos(r)),i.push(.5*Math.sin(r)),i.push(0);for(s=0;s<=Math.round(24);s++)r=360/Math.round(24)*s*mt,i.push(0),i.push(.5*Math.cos(r)),i.push(.5*Math.sin(r));t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.bufferData(t.gl.ARRAY_BUFFER,new Float32Array(i),t.gl.STATIC_DRAW),e.itemSize=3,e.numItems=i.length/e.itemSize}()},WireCube:function(t){const e=t.gl.createBuffer(),i=vec3.create();this.render=function(t,s,r,n){t.pushModelMatrix(),vec3.set(i,s||1,r||1,n||1),mat4.scale(t.mvMatrix,t.mvMatrix,i);const o=t.getShader();o&&(o.bind(),t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.vertexAttribPointer(o.getAttrVertexPos(),e.itemSize,t.gl.FLOAT,!1,0,0),t.gl.enableVertexAttribArray(o.getAttrVertexPos()),t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.drawArrays(t.gl.LINE_STRIP,0,e.numItems)),t.popModelMatrix()},function(){const i=[];i.push(-1,-1,1),i.push(1,-1,1),i.push(1,1,1),i.push(-1,1,1),i.push(-1,-1,1),i.push(-1,-1,-1),i.push(1,-1,-1),i.push(1,1,-1),i.push(-1,1,-1),i.push(-1,-1,-1),i.push(-1,-1,-1),i.push(-1,1,-1),i.push(-1,1,1),i.push(-1,-1,1),i.push(-1,-1,-1),i.push(1,-1,-1),i.push(1,1,-1),i.push(1,1,1),i.push(1,-1,1),i.push(1,-1,-1),t.gl.bindBuffer(t.gl.ARRAY_BUFFER,e),t.gl.bufferData(t.gl.ARRAY_BUFFER,new Float32Array(i),t.gl.STATIC_DRAW),e.itemSize=3,e.numItems=i.length/e.itemSize}()},MatrixStack:Mt,Mesh:lt,MESH:ht,ShaderLibMods:pt,Shader:It,Uniform:nt,MESHES:ut,Context:Bt,Texture:B,TextureEffect:gt,isWindows:Et,getWheelSpeed:At,getWheelDelta:bt,onLoadingAssetsFinished:null,ProfileData:St,UniColorShader:class{constructor(t){this.shader=new CGL.Shader(t,"markermaterial");const e="".endl()+"void main()".endl()+"{".endl()+" outColor = vec4(color.rgb,1.0);".endl()+"}",i="".endl()+"IN vec3 vPosition;".endl()+"UNI mat4 projMatrix;".endl()+"UNI mat4 mvMatrix;".endl()+"void main()".endl()+"{".endl()+" gl_Position = projMatrix * mvMatrix * vec4(vPosition,1.0);".endl()+"}";this.shader.setSource(i,e),this.coloruni=this.shader.addUniformFrag("4f","color",[1,.777,1,1])}setColor(t,e,i,s){this.coloruni.set(t,e,i,s)}}},at.BLEND_MODES,at.SHADER,at.MATH,at.BLEND_MODES);window.CGL=Lt;const kt=function(t){Y.apply(this),this.id=CABLES.uuid(),this.portIn=null,this.portOut=null,this.scene=t,this.activityCounter=0,this.ignoreInSerialize=!1};kt.prototype.setValue=function(t){void 0===t?this._setValue():this.portIn.set(t)},kt.prototype.activity=function(){this.activityCounter++},kt.prototype._setValue=function(){if(!this.portOut)return void this.remove();const t=this.portOut.get();t==t&&(this.portIn.type!=l.OP_PORT_TYPE_FUNCTION&&this.activity(),(this.portIn.get()!==t||this.portIn.changeAlways)&&this.portIn.set(t))},kt.prototype.getOtherPort=function(t){return t==this.portIn?this.portOut:this.portIn},kt.prototype.remove=function(){this.portIn&&this.portIn.removeLink(this),this.portOut&&this.portOut.removeLink(this),this.scene&&this.scene.emitEvent("onUnLink",this.portIn,this.portOut,this),!this.portIn||this.portIn.type!=l.OP_PORT_TYPE_OBJECT&&this.portIn.type!=l.OP_PORT_TYPE_ARRAY||(this.portIn.set(null),this.portIn.links.length>0&&this.portIn.set(this.portIn.links[0].getOtherPort(this.portIn).get())),this.portIn&&this.portIn.parent._checkLinksNeededToWork(),this.portOut&&this.portOut.parent._checkLinksNeededToWork(),this.portIn=null,this.portOut=null,this.scene=null},kt.prototype.link=function(t,e){if(!kt.canLink(t,e))return console.warn("[core_link] cannot link ports!",t,e),!1;t.direction==c.PORT_DIR_IN?(this.portIn=t,this.portOut=e):(this.portIn=e,this.portOut=t),t.addLink(this),e.addLink(this),this.setValue(),t.onLink&&t.onLink(this),e.onLink&&e.onLink(this),t.parent._checkLinksNeededToWork(),e.parent._checkLinksNeededToWork()},kt.prototype.getSerialized=function(){const t={};return t.portIn=this.portIn.getName(),t.portOut=this.portOut.getName(),t.objIn=this.portIn.parent.id,t.objOut=this.portOut.parent.id,t},kt.canLinkText=function(t,e){if(t.direction==e.direction){let t="(out)";return e.direction==c.PORT_DIR_IN&&(t="(in)"),"can not link: same direction "+t}return t.parent==e.parent?"can not link: same op":t.type!=l.OP_PORT_TYPE_DYNAMIC&&e.type!=l.OP_PORT_TYPE_DYNAMIC&&t.type!=e.type?"can not link: different type":t?e?t.direction==c.PORT_DIR_IN&&t.isAnimated()||e.direction==c.PORT_DIR_IN&&e.isAnimated()?"can not link: is animated":t.isLinkedTo(e)?"ports already linked":t.canLink&&!t.canLink(e)||e.canLink&&!e.canLink(t)?"Incompatible":"can link":"can not link: port 2 invalid":"can not link: port 1 invalid"},kt.canLink=function(t,e){return!!t&&(!!e&&((t.direction!=c.PORT_DIR_IN||!t.isAnimated())&&((e.direction!=c.PORT_DIR_IN||!e.isAnimated())&&(!t.isHidden()&&!e.isHidden()&&(!t.isLinkedTo(e)&&(t.direction!=e.direction&&((t.type==e.type||t.type==l.OP_PORT_TYPE_DYNAMIC||e.type==l.OP_PORT_TYPE_DYNAMIC)&&(t.type==l.OP_PORT_TYPE_DYNAMIC||e.type==l.OP_PORT_TYPE_DYNAMIC||t.parent!=e.parent&&(!(t.canLink&&!t.canLink(e))&&!(e.canLink&&!e.canLink(t)))))))))))};const Vt=function(){Y.apply(this),this._log=new a("core_op"),this.data={},this.storage={},this.objName="",this.portsOut=[],this.portsIn=[],this.portsInData=[],this.opId="",this.uiAttribs={},this.enabled=!0,this.patch=arguments[0],this.name=arguments[1],this._needsLinkedToWork=[],this._needsParentOp=null,this._shortOpName="",this.hasUiErrors=!1,this._uiErrors={},arguments[1]&&(this._shortOpName=CABLES.getShortOpName(arguments[1]),this.getTitle()),this.id=arguments[2]||T(),this.onAddPort=null,this.onCreate=null,this.onResize=null,this.onLoaded=null,this.onDelete=null,this.onUiAttrChange=null,this.onError=null,this._instances=null,this.preRender=null,this.init=null};{Vt.prototype.clearUiAttrib=function(t){const e={name:null};this.uiAttrib(e)},Vt.prototype.getTitle=function(){return this.uiAttribs?(void 0!==this.uiAttribs.title&&""!==this.uiAttribs.title||-1!=this.objName.indexOf("Ops.Ui.")||(this.uiAttribs.title=this._shortOpName),void 0===this.uiAttribs.title&&(this.uiAttribs.title=this._shortOpName),this.uiAttribs.title):"nouiattribs"+this.name},Vt.prototype.setTitle=function(t){const e=this.name!=t;this.name=t,this.uiAttr({title:t}),e&&this.emitEvent("onTitleChange",t)};const t=function(t){if(!t)return;(t.error||t.warning||t.hint)&&this._log.warn("old ui error/warning attribute in "+this.name+", use op.setUiError !",t),"object"!=typeof t&&this._log.error("op.uiAttrib attribs are not of type object"),this.uiAttribs||(this.uiAttribs={});let e=!1;for(const i in t)this.uiAttribs[i]!=t[i]&&(e=!0),this.uiAttribs[i]=t[i];this.uiAttribs.hasOwnProperty("selected")&&0==this.uiAttribs.selected&&delete this.uiAttribs.selected,t.title&&t.title!=this.name&&this.setTitle(t.title),e&&(this.emitEvent("onUiAttribsChange",t),this.patch.emitEvent("onUiAttribsChange",this,t))};Vt.prototype.setUiAttribs=Vt.prototype.setUiAttrib=Vt.prototype.uiAttr=t,Vt.prototype.getName=function(){return this.uiAttribs.name?this.uiAttribs.name:this.objName.split(".")},Vt.prototype.addOutPort=function(t){return t.direction=c.PORT_DIR_OUT,t.parent=this,this.portsOut.push(t),this.emitEvent("onPortAdd",t),t},Vt.prototype.hasDynamicPort=function(){let t=0;for(t=0;tt==i);n.setValue(t),r.defaultValue=i,n.defaultValue=t}}return r},Vt.prototype.inSwitch=function(t,e,i,s){let r=null;if(s){const i=new et(this,t,l.OP_PORT_TYPE_STRING,{display:"switch",hidePort:!0,type:"string",values:e});r=this.addInPort(i)}else{const s=new et(this,t+" index",l.OP_PORT_TYPE_VALUE,{increment:"integer",hideParam:!0}),n=this.addInPort(s),o=new it(this,t,l.OP_PORT_TYPE_STRING,{display:"switch",hidePort:!0,type:"string",values:e},n);if(s.onLinkChanged=function(){o.setUiAttribs({greyout:s.isLinked()})},r=this.addInPort(o),void 0!==i){r.set(i);const t=e.findIndex(t=>t==i);n.setValue(t),r.defaultValue=i,n.defaultValue=t}}return r},Vt.prototype.inValueInt=Vt.prototype.inInt=function(t,e){const i=this.addInPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{increment:"integer"}));return void 0!==e&&(i.set(e),i.defaultValue=e),i},Vt.prototype.inFile=function(t,e,i){const s=this.addInPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{display:"file",type:"string",filter:e}));return void 0!==i&&(s.set(i),s.defaultValue=i),s},Vt.prototype.inUrl=function(t,e,i){const s=this.addInPort(new et(this,t,l.OP_PORT_TYPE_STRING,{display:"file",type:"string",filter:e}));return void 0!==i&&(s.set(i),s.defaultValue=i),s},Vt.prototype.inTexture=function(t,e){const i=this.addInPort(new et(this,t,l.OP_PORT_TYPE_OBJECT,{display:"texture",objType:"texture",preview:!0}));return void 0!==e&&i.set(e),i},Vt.prototype.inObject=function(t,e,i){const s=this.addInPort(new et(this,t,l.OP_PORT_TYPE_OBJECT,{objType:i}));return void 0!==e&&s.set(e),s},Vt.prototype.inGradient=function(t,e){const i=this.addInPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{display:"gradient",hidePort:!0}));return void 0!==e&&i.set(e),i},Vt.prototype.inArray=function(t,e,i){!i&&CABLES.UTILS.isNumeric(e)&&(i=e);const s=this.addInPort(new et(this,t,l.OP_PORT_TYPE_ARRAY,{stride:i}));return void 0===e||!Array.isArray(e)&&null!=e||s.set(e),s},Vt.prototype.inValueSlider=Vt.prototype.inFloatSlider=function(t,e,i,s){const r={display:"range"};null!=i&&null!=s&&(r.min=i,r.max=s);const n=this.addInPort(new et(this,t,l.OP_PORT_TYPE_VALUE,r));return void 0!==e&&(n.set(e),n.defaultValue=e),n},Vt.prototype.outFunction=Vt.prototype.outTrigger=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_FUNCTION));return void 0!==e&&i.set(e),i},Vt.prototype.outValue=Vt.prototype.outNumber=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_VALUE));return void 0!==e&&i.set(e),i},Vt.prototype.outValueBool=Vt.prototype.outBool=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{display:"bool"}));return void 0!==e?i.set(e):i.set(0),i},Vt.prototype.outBoolNum=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{display:"boolnum"}));return i.set=function(t){this.setValue(t?1:0)}.bind(i),void 0!==e?i.set(e):i.set(0),i},Vt.prototype.outValueString=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_VALUE,{type:"string"}));return void 0!==e&&i.set(e),i},Vt.prototype.outString=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_STRING,{type:"string"}));return void 0!==e?i.set(e):i.set(""),i},Vt.prototype.outObject=function(t,e,i){const s=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_OBJECT,{objType:i||null}));return s.set(e||null),s.ignoreValueSerialize=!0,s},Vt.prototype.outArray=function(t,e,i){!i&&CABLES.UTILS.isNumeric(e)&&(i=e);const s=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_ARRAY,{stride:i}));return void 0===e||!Array.isArray(e)&&null!=e||s.set(e),s.ignoreValueSerialize=!0,s},Vt.prototype.outTexture=function(t,e){const i=this.addOutPort(new et(this,t,l.OP_PORT_TYPE_OBJECT,{preview:!0,objType:"texture"}));return void 0!==e&&i.set(e),i.ignoreValueSerialize=!0,i},Vt.prototype.inDynamic=function(t,e,i,s){const r=new et(this,t,l.OP_PORT_TYPE_DYNAMIC,i);return r.shouldLink=function(t,i){if(e&&g.isArray(e)){for(let s=0;s0&&(t.storage=this.storage),this.uiAttribs.hasOwnProperty("working")&&1==this.uiAttribs.working&&delete this.uiAttribs.working,t.portsIn=[],t.portsOut=[];for(let e=0;e-1&&this._log.warn("setuierror id cant have spaces! ",t),!e&&this._uiErrors.hasOwnProperty(t)?delete this._uiErrors[t]:!e||this._uiErrors.hasOwnProperty(t)&&this._uiErrors[t].txt==e||(null==i&&(i=2),this._uiErrors[t]={txt:e,level:i,id:t});const s=[];for(const t in this._uiErrors)s.push(this._uiErrors[t]);this.uiAttr({uierrors:s}),this.hasUiErrors=Object.keys(this._uiErrors).length,this.emitEvent("uiErrorChange")},Vt.prototype.setError=function(t,e){this._log.warn("old error message op.error() - use op.setUiError()")},Vt.prototype.setEnabled=function(t){this.enabled=t,this.emitEvent("onEnabledChange",t)},Vt.prototype.setPortGroup=function(t,e){for(let i=0;i{e(this._patch),this.emitEvent("finishedAll")},100)}this._wasFinishedPrinted||(this._wasFinishedPrinted=!0,this.print()),this.emitEvent("finishedAll")}},Dt.prototype.getList=function(){let t=[];for(const e in this._loadingAssets)t.push(this._loadingAssets[e]);return t},Dt.prototype.getListJobs=function(){let t=[];for(const e in this._loadingAssets)this._loadingAssets[e].finished||t.push(this._loadingAssets[e].name);return t},Dt.prototype.print=function(){if(this._patch.config.silent)return;const t=[];for(const e in this._loadingAssets)t.push([this._loadingAssets[e].order,this._loadingAssets[e].type,this._loadingAssets[e].name,(this._loadingAssets[e].timeEnd-this._loadingAssets[e].timeStart)/1e3+"s"]);this._log.groupCollapsed("finished loading "+this._order+" assets in "+(Date.now()-this._startTime)/1e3+"s"),this._log.table(t),this._log.groupEnd()},Dt.prototype.finished=function(t){this._loadingAssets[t]&&(this._loadingAssets[t].finished=!0,this._loadingAssets[t].timeEnd=Date.now()),this.checkStatus(),this.emitEvent("finishedTask")},Dt.prototype._startAssetTasks=function(){for(let t=0;t0},Gt.prototype.increment=function(){this._index++},Gt.prototype.index=function(){return this._index};const Ht=function(t){this.startFrame=t.getFrameNum();let e={},i=null,s=0;this.getItems=function(){return e},this.clear=function(){e={}},this.add=function(r,n){null!==i&&(n&&n.id==i||e[i]&&(e[i].timeUsed+=performance.now()-s,(!e[i].peakTime||dt()-e[i].peakTime>5e3)&&(e[i].peak=0,e[i].peakTime=dt()),e[i].peak=Math.max(e[i].peak,performance.now()-s))),null!==n?(e[n.id]||(e[n.id]={numTriggers:0,timeUsed:0}),e[n.id].lastFrame!=t.getFrameNum()&&(e[n.id].numTriggers=0),e[n.id].lastFrame=t.getFrameNum(),e[n.id].numTriggers++,e[n.id].opid=n.parent.id,e[n.id].title=n.parent.name+"."+n.name,e[n.id].subPatch=n.parent.uiAttribs.subPatch,i=n.id,s=performance.now()):i=null},this.print=function(){console.log("--------");for(const t in e)console.log(e[t].title+": "+e[t].numTriggers+" / "+e[t].timeUsed)}};var zt=class extends Y{constructor(t,e,i){super(),this._name=t,this.type=i,this.setValue(e)}addListener(t){this.on("change",t,"var")}getValue(){return this._v}getName(){return this._name}setValue(t){this._v=t,this.emitEvent("change",t,this)}};const Wt=function(t){Y.apply(this),this._log=new a("core_patch"),this.ops=[],this.settings={},this.config=t||{glCanvasResizeToWindow:!1,prefixAssetPath:"",prefixJsPath:"",silent:!0,onError:null,onFinishedLoading:null,onFirstFrameRendered:null,onPatchLoaded:null,fpsLimit:0},this.timer=new ft,this.freeTimer=new ft,this.animFrameOps=[],this.animFrameCallbacks=[],this.gui=!1,CABLES.logSilent=this.silent=!0,this.profiler=null,this.aborted=!1,this._crashedOps=[],this._renderOneFrame=!1,this._animReq=null,this._opIdCache={},this._triggerStack=[],this.loading=new Dt(this),this._volumeListeners=[],this._paused=!1,this._frameNum=0,this.instancing=new Gt,this.onOneFrameRendered=null,this.namedTriggers={},this._origData=null,this._frameNext=0,this._frameInterval=0,this._lastFrameTime=0,this._frameWasdelayed=!0,function(){return!this}()||this._log.warn("not in strict mode: core patch"),this._isLocal=0===document.location.href.indexOf("file:"),this.config.hasOwnProperty("silent")&&(this.silent=CABLES.logSilent=this.config.silent),this.config.hasOwnProperty("doRequestAnimation")||(this.config.doRequestAnimation=!0),this.config.prefixAssetPath||(this.config.prefixAssetPath=""),this.config.prefixJsPath||(this.config.prefixJsPath=""),this.config.masterVolume||(this.config.masterVolume=1),this._variables={},this._variableListeners=[],this.vars={},t&&t.vars&&(this.vars=t.vars),this.cgl=new Bt(this),this.cgp=null,this.cgl.setCanvas(this.config.glCanvasId||this.config.glCanvas||"glcanvas"),!0===this.config.glCanvasResizeToWindow&&this.cgl.setAutoResize("window"),!0===this.config.glCanvasResizeToParent&&this.cgl.setAutoResize("parent"),this.loading.setOnFinishedLoading(this.config.onFinishedLoading),this.cgl.aborted&&(this.aborted=!0),this.cgl.silent&&(this.silent=!0),this.freeTimer.play(),this.exec(),this.aborted||(this.config.patch?this.deSerialize(this.config.patch):this.config.patchFile&&w(this.config.patchFile,(t,e)=>{const i=JSON.parse(e);if(t){return this._log.error("err",t),this._log.error("data",i),void this._log.error("data",i.msg)}this.deSerialize(i)}),this.timer.play()),console.log("made with https://cables.gl")};Wt.prototype.isPlaying=function(){return!this._paused},Wt.prototype.isRenderingOneFrame=function(){return this._renderOneFrame},Wt.prototype.renderOneFrame=function(){this._paused=!0,this._renderOneFrame=!0,this.exec(),this._renderOneFrame=!1},Wt.prototype.getFPS=function(){return console.log("deprecated getfps"),0},Wt.prototype.isEditorMode=function(){return!0===this.config.editorMode},Wt.prototype.pause=function(){cancelAnimationFrame(this._animReq),this.emitEvent("pause"),this._animReq=null,this._paused=!0,this.freeTimer.pause()},Wt.prototype.resume=function(){this._paused&&(cancelAnimationFrame(this._animReq),this._paused=!1,this.freeTimer.play(),this.emitEvent("resume"),this.exec())},Wt.prototype.setVolume=function(t){this.config.masterVolume=t;for(let e=0;e0||document.location.href.indexOf("cables.local")>0){const e=document.location.pathname.split("/");return"/assets/"+(t||e[e.length-1])+"/"}return this.config.hasOwnProperty("assetPath")?this.config.assetPath:"assets/"},Wt.prototype.getJsPath=function(){return this.config.hasOwnProperty("jsPath")?this.config.jsPath:"js/"},Wt.prototype.getFilePath=function(t){return this._isLocal&&!this.config.allowLocalFileAccess&&this.exitError("localAccess","Browser security forbids loading files directly without a webserver. Upload files to a server to work. use allowLocalFileAccess:true to ignore this."),t?0===(t=String(t)).indexOf("https:")||0===t.indexOf("http:")||0===t.indexOf("data:")?t:(t=t.replace("//","/"),this.config.prefixAssetPath+t+(this.config.suffixAssetPath||"")):t},Wt.prototype.clear=function(){for(this.emitEvent("patchClearStart"),this.cgl.TextureEffectMesh=null,this.animFrameOps.length=0,this.timer=new ft;this.ops.length>0;)this.deleteOp(this.ops[0].id);this.emitEvent("patchClearEnd")},Wt.getOpClass=function(t){const e=t.split(".");let i=null;try{return 2==e.length?i=window[e[0]][e[1]]:3==e.length?i=window[e[0]][e[1]][e[2]]:4==e.length?i=window[e[0]][e[1]][e[2]][e[3]]:5==e.length?i=window[e[0]][e[1]][e[2]][e[3]][e[4]]:6==e.length?i=window[e[0]][e[1]][e[2]][e[3]][e[4]][e[5]]:7==e.length?i=window[e[0]][e[1]][e[2]][e[3]][e[4]][e[5]][e[6]]:8==e.length?i=window[e[0]][e[1]][e[2]][e[3]][e[4]][e[5]][e[6]][e[7]]:9==e.length?i=window[e[0]][e[1]][e[2]][e[3]][e[4]][e[5]][e[6]][e[7]][e[8]]:10==e.length&&(i=window[e[0]][e[1]][e[2]][e[3]][e[4]][e[5]][e[6]][e[7]][e[8]][e[9]]),i}catch(t){return null}},Wt.prototype.createOp=function(t,e,i=null){let s=null,r="";try{if(-1===t.indexOf("Ops.")){const n=t;if(CABLES.OPS[n])r=CABLES.OPS[n].objName,s=new CABLES.OPS[n].f(this,r,e,n),s.opId=n;else{if(!i)throw new Error("could not find op by id: "+n);t=i,this._log.warn("could not find op by id: "+n)}}if(!s){r=t;const i=t.split(".");if(!Wt.getOpClass(r))throw this.emitEvent("criticalError",{title:"unknown op",text:"unknown op: "+r}),this._log.error("unknown op: "+r),new Error("unknown op: "+r);if(2==i.length?s=new window[i[0]][i[1]](this,r,e):3==i.length?s=new window[i[0]][i[1]][i[2]](this,r,e):4==i.length?s=new window[i[0]][i[1]][i[2]][i[3]](this,r,e):5==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]](this,r,e):6==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]](this,r,e):7==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]][i[6]](this,r,e):8==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]][i[6]][i[7]](this,r,e):9==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]][i[6]][i[7]][i[8]](this,r,e):10==i.length?s=new window[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]][i[6]][i[7]][i[8]][i[9]](this,r,e):this._log.warn("parts.length",i.length),s){s.opId=null;for(const t in CABLES.OPS)CABLES.OPS[t].objName==r&&(s.opId=t)}}}catch(t){if(this._crashedOps.push(r),this.emitEvent("exceptionOp",t,r,s),!this.isEditorMode())throw this._log.error(t),this._log.error("[instancing error] "+r,t),CABLES.api&&CABLES.api.sendErrorReport(t),this.exitError("INSTANCE_ERR","Instancing Error "+r,t),new Error("instancing error "+r)}return s?(s.objName=r,s.patch=this):this._log.log("no op was created!?",t,e),s},Wt.prototype.addOp=function(t,e,i,s,r){const n=this.createOp(t,i,r);if(n){if((e=e||{}).hasOwnProperty("errors")&&delete e.errors,e.hasOwnProperty("error")&&delete e.error,e.subPatch=e.subPatch||0,n.uiAttr(e),n.onCreate&&n.onCreate(),n.hasOwnProperty("onAnimFrame")&&this.addOnAnimFrame(n),n.hasOwnProperty("onMasterVolumeChanged")&&this._volumeListeners.push(n),this._opIdCache[n.id])return void this._log.warn("opid with id "+n.id+" already exists in patch!");this.ops.push(n),this._opIdCache[n.id]=n,this.emitEvent("onOpAdd",n,s),n.init&&n.init()}else this._log.error("addop: no op.....");return n},Wt.prototype.addOnAnimFrame=function(t){for(let e=0;e0&&n.portsIn[0].isLinked()&&n.portsOut.length>0&&n.portsOut[0].isLinked()&&n.portsIn[0].getType()==n.portsOut[0].getType()&&n.portsIn[0].links[0]&&(o=n.portsIn[0].links[0].getOtherPort(n.portsIn[0]),a=n.portsOut[0].links[0].getOtherPort(n.portsOut[0]));const h=this.ops[r];h.removeLinks(),h.emitEvent("onDelete",this.ops[r]),this.onDelete&&(this._log.warn("deprecated this.onDelete",this.onDelete),this.onDelete(h)),this.ops.splice(r,1),this.emitEvent("onOpDelete",h,i),h.onDelete&&h.onDelete(i),h.cleanUp(),null!==o&&null!==a&&this.link(o.parent,o.getName(),a.parent,a.getName()),delete this._opIdCache[t];break}}s||this._log.warn("core patch deleteop: not found...")},Wt.prototype.getFrameNum=function(){return this._frameNum},Wt.prototype.emitOnAnimFrameEvent=function(t){t=t||this.timer.getTime();for(let e=0;e=500&&0!==this._lastFrameTime&&!this._frameWasdelayed)return this._lastFrameTime=0,setTimeout(this.exec.bind(this),500),this.emitEvent("renderDelayStart"),void(this._frameWasdelayed=!0);if(this._renderOneFrame||0===this.config.fpsLimit||i>this._frameInterval||this._frameWasdelayed){CABLES.now();this.renderFrame(),this._frameInterval&&(this._frameNext=e-i%this._frameInterval)}this._frameWasdelayed&&(this.emitEvent("renderDelayEnd"),this._frameWasdelayed=!1),this._renderOneFrame&&(this.onOneFrameRendered&&this.onOneFrameRendered(),this.emitEvent("renderedOneFrame"),this._renderOneFrame=!1),this.config.doRequestAnimation&&(this._animReq=requestAnimationFrame(this.exec.bind(this)))},Wt.prototype.link=function(t,e,i,s,r,n){if(!t)return void this._log.warn("link: op1 is null ");if(!i)return void this._log.warn("link: op2 is null");const o=t.getPort(e,r),a=i.getPort(s,r);if(o)if(a){if(!o.shouldLink(o,a)||!a.shouldLink(o,a))return!1;if(kt.canLink(o,a)){const t=new kt(this);return t.link(o,a),this.emitEvent("onLink",o,a,t,n),t}}else this._log.warn("port not found! "+s+" of "+i.name+"("+i.objName+")");else this._log.warn("port not found! "+e+"("+t.objName+")")},Wt.prototype.serialize=function(t){const e={};t=t||{},e.ops=[],e.settings=this.settings;for(const t in this.ops){const i=this.ops[t];e.ops.push(i.getSerialized())}return t.asObject?e:JSON.stringify(e)},Wt.prototype.getOpById=function(t){return this._opIdCache[t]},Wt.prototype.getOpsByName=function(t){const e=[];for(const i in this.ops)this.ops[i].name==t&&e.push(this.ops[i]);return e},Wt.prototype.getOpsByObjName=function(t){const e=[];for(const i in this.ops)this.ops[i].objName==t&&e.push(this.ops[i]);return e},Wt.prototype.loadLib=function(t){C("/ui/libs/"+t+".js",(t,e)=>{const i=document.createElement("script");i.type="text/javascript",i.text=e,document.getElementsByTagName("head")[0].appendChild(i)},"GET")},Wt.prototype.reloadOp=function(t,e){let i=0;const s=[],r=[];for(const e in this.ops)this.ops[e].objName==t&&r.push(this.ops[e]);for(let e=0;e200&&this._log.warn("long op init ",t.ops[s].objName,a)}for(const t in this.ops)this.ops[t].onLoadedValueSet&&(this.ops[t].onLoadedValueSet(this.ops[t]._origData),this.ops[t].onLoadedValueSet=null,this.ops[t]._origData=null),this.ops[t].emitEvent("loadedValueSet");if(t.ops)for(let e=0;e{this.loading.finished(s)},100),this.config.onPatchLoaded&&this.config.onPatchLoaded(this),this.emitEvent("patchLoadEnd",i,t,e)},Wt.prototype.profile=function(t){this.profiler=new Ht(this);for(const e in this.ops)this.ops[e].profile(t)},Wt.prototype.setVariable=function(t,e){void 0!==this._variables[t]?this._variables[t].setValue(e):this._log.warn("variable "+t+" not found!")},Wt.prototype._sortVars=function(){if(!this.isEditorMode())return;const t={};Object.keys(this._variables).sort((t,e)=>t.localeCompare(e,"en",{sensitivity:"base"})).forEach(e=>{t[e]=this._variables[e]}),this._variables=t},Wt.prototype.hasVar=function(t){return void 0!==this._variables[t]},Wt.prototype.setVarValue=function(t,e,i){return this.hasVar(t)?this._variables[t].setValue(e):(this._variables[t]=new zt(t,e,i),this._sortVars(),this.emitEvent("variablesChanged")),this._variables[t]},Wt.prototype.getVarValue=function(t,e){if(this._variables.hasOwnProperty(t))return this._variables[t].getValue()},Wt.prototype.getVar=function(t){if(this._variables.hasOwnProperty(t))return this._variables[t]},Wt.prototype.deleteVar=function(t){for(let e=0;e=i._param.minValue&&e<=i._param.maxValue)try{i.setValueAtTime?i.setValueAtTime(e,s.currentTime):i.value=e}catch(e){throw t.log("Possible AudioParam problem with tone.js op: ",e),e.printStackTrace&&e.printStackTrace(),e}else t.log("Warning: The value for an audio parameter is out of range!");else if(i.minValue&&i.maxValue)if(e>=i.minValue&&e<=i.maxValue)try{i.setValueAtTime?i.setValueAtTime(e,s.currentTime):i.value=e}catch(e){throw t.log("AudioParam has minValue / maxValue defined, and value is in range, but setting the value failed! ",e),e.printStackTrace&&e.printStackTrace(),e}else t.log("Warning: The value for an audio parameter is out of range!");else try{i.setValueAtTime?i.setValueAtTime(e,s.currentTime):i.value=e}catch(e){throw t.log("Possible AudioParam problem (without minValue / maxValue): ",e),e.printStackTrace&&e.printStackTrace(),e}if(n.webAudio.previousAudioInNode&&n.webAudio.previousAudioInNode.disconnect){try{n.webAudio.previousAudioInNode.disconnect(i)}catch(e){throw t.log("Could not disconnect previous audio node: ",e),e}n.webAudio.previousAudioInNode=void 0}}else n.webAudio.previousAudioInNode},n},loadAudioFile:function(t,e,i,s,r){const n=jt.createAudioContext();let o=null;(r||void 0===r)&&(o=t.loading.start("audio",e),t.isEditorMode()&&gui.jobs().start({id:"loadaudio"+o,title:" loading audio ("+e+")"}));const a=new XMLHttpRequest;e&&(a.open("GET",e,!0),a.responseType="arraybuffer",a.onload=function(){t.loading.finished(o),t.isEditorMode()&&gui.jobs().finish("loadaudio"+o),n.decodeAudioData(a.response,i,s)},a.send())},isValidToneTime:function(t){try{new Tone.Time(t)}catch(t){return!1}return!0},isValidToneNote:function(t){try{Tone.Frequency(t)}catch(t){return!1}return!0}},Kt=function(t,e,i){this._patch=t,this.connector=i,this._log=new a("PatchConnectionReceiver")};Kt.prototype._addOp=function(t){let e=null;t.vars.uiAttribs&&(e=t.vars.uiAttribs);const i=this._patch.addOp(t.vars.objName,e,t.vars.opId,!0);i&&(i.id=t.vars.opId,t.vars.portsIn&&t.vars.portsIn.forEach(t=>{const e=i.getPortByName(t.name);e&&e.set(t.value)}))},Kt.prototype._receive=function(t){let e={};if(e=t.hasOwnProperty("event")?t:JSON.parse(t.data),e.event==u.PACO_OP_CREATE){if(this._patch.getOpById(e.vars.opId))return;this._log.verbose("op create:",e.vars.objName),window.gui?gui.serverOps.loadOpLibs(e.vars.objName,()=>{this._addOp(e)}):this._addOp(e)}else if(e.event==u.PACO_DESERIALIZE)e.vars.json&&(window.gui?gui.serverOps.loadProjectDependencies(e.vars.json,()=>{this._patch.deSerialize(e.vars.json,e.vars.genIds)}):this._patch.deSerialize(e.vars.json,e.vars.genIds));else if(e.event==u.PACO_LOAD)this._log.verbose("PACO load patch....."),this._patch.clear(),window.gui?gui.serverOps.loadProjectDependencies(JSON.parse(e.vars.patch),()=>{this._patch.deSerialize(e.vars.patch)}):this._patch.deSerialize(e.vars.patch);else if(e.event==u.PACO_CLEAR)this._patch.clear(),this._log.log("clear");else if(e.event==u.PACO_OP_DELETE){this._log.verbose("op delete",e.vars.objName);this._patch.getOpById(e.vars.op);this._patch.deleteOp(e.vars.op,!0)}else if(e.event==u.PACO_OP_ENABLE){const t=this._patch.getOpById(e.vars.op);t&&(t.enabled=!0)}else if(e.event==u.PACO_OP_DISABLE){const t=this._patch.getOpById(e.vars.op);t&&(t.enabled=!1)}else if(e.event==u.PACO_UIATTRIBS){const t=this._patch.getOpById(e.vars.op);t&&t.setUiAttrib(e.vars.uiAttribs)}else if(e.event==u.PACO_UNLINK){const t=this._patch.getOpById(e.vars.op1),i=this._patch.getOpById(e.vars.op2);if(!t||!i)return;const s=t.getPort(e.vars.port1),r=i.getPort(e.vars.port2);s&&r?s.removeLinkTo(r):this._log.warn("paco unlink could not find port...")}else if(e.event==u.PACO_LINK){const t=this._patch.getOpById(e.vars.op1),i=this._patch.getOpById(e.vars.op2);t&&i&&this._patch.link(t,e.vars.port1,i,e.vars.port2)}else if(e.event==u.PACO_VALUECHANGE){if("+ create new one"===e.vars.v)return;const t=this._patch.getOpById(e.vars.op);if(t){const i=t.getPort(e.vars.port);i&&i.set(e.vars.v)}}else if(e.event==u.PACO_VARIABLES){const t=this._patch.getOpById(e.vars.opId);t&&t.varName&&t.varName.set(e.vars.varName)}else if(e.event==u.PACO_TRIGGERS){const t=this._patch.getOpById(e.vars.opId);t&&t.varName&&t.varName.set(e.vars.varName)}else if(e.event==u.PACO_PORT_SETVARIABLE){const t=this._patch.getOpById(e.vars.opId);if(t){const i=t.getPortByName(e.vars.portName);i&&i.setVariable(e.vars.variableName)}}else if(e.event==u.PACO_PORT_SETANIMATED){const t=this._patch.getOpById(e.vars.opId);if(t){t.portsIn[e.vars.portIndex]&&e.vars.hasOwnProperty("targetState")&&this._patch.emitEvent("pacoPortValueSetAnimated",t,e.vars.portIndex,e.vars.targetState,e.vars.defaultValue)}}else if(e.event==u.PACO_PORT_ANIM_UPDATED){const t=this._patch.getOpById(e.vars.opId);if(t){const i=t.getPortByName(e.vars.portName);if(i){const t=i.getSerialized();t.anim=e.vars.anim,i.anim=null,i.deSerializeSettings(t),this._patch.emitEvent("pacoPortAnimUpdated",i)}}}else this._log.warn("unknown patchConnectionEvent!",t)};const Qt=function(t){this.connectors=[],this.paused=!1,t.addEventListener("onOpDelete",t=>{this.send(CABLES.PACO_OP_DELETE,{op:t.id,objName:t.objName})}),t.addEventListener("patchClearStart",()=>{this.paused=!0}),t.addEventListener("patchClearEnd",()=>{this.paused=!1}),t.addEventListener("patchLoadStart",()=>{this.paused=!0}),t.addEventListener("patchLoadEnd",(t,e,i)=>{this.paused=!1,this.send(CABLES.PACO_DESERIALIZE,{json:e,genIds:i})}),t.addEventListener("onOpAdd",t=>{const e=[];t.portsIn.forEach(t=>{const i={id:t.id,name:t.name,value:t.get()};e.push(i)});let i={};t.uiAttribs&&(i={...t.uiAttribs}),this.send(CABLES.PACO_OP_CREATE,{opId:t.id,objName:t.objName,uiAttribs:i,portsIn:e})}),t.addEventListener("onUnLink",(t,e)=>{this.send(CABLES.PACO_UNLINK,{op1:t.parent.id,op2:e.parent.id,port1:t.getName(),port2:e.getName()})}),t.addEventListener("onUiAttribsChange",(t,e)=>{e&&(delete e.extendTitle,delete e.history,delete e.translate,Object.keys(e).length>0&&this.send(CABLES.PACO_UIATTRIBS,{op:t.id,uiAttribs:e}))}),t.addEventListener("opVariableNameChanged",(t,e)=>{const i={opId:t.id,varName:e};this.send(CABLES.PACO_VARIABLES,i)}),t.addEventListener("opTriggerNameChanged",(t,e)=>{const i={opId:t.id,varName:e};this.send(CABLES.PACO_TRIGGERS,i)}),t.addEventListener("onLink",(t,e)=>{this.send(CABLES.PACO_LINK,{op1:t.parent.id,op2:e.parent.id,port1:t.name,port2:e.name})}),t.addEventListener("portSetVariable",(t,e,i)=>{const s={opId:t.id,portName:e.name,variableName:i};this.send(CABLES.PACO_PORT_SETVARIABLE,s)}),t.addEventListener("portAnimUpdated",(t,e,i)=>{if(t&&e){const s={opId:t.id,portName:e.name,anim:i.getSerialized()};this.send(CABLES.PACO_PORT_ANIM_UPDATED,s)}})};Qt.prototype.send=function(t,e){if(!this.paused&&(t!==CABLES.PACO_VALUECHANGE||"+ create new one"!==e.v))for(let i=0;i 300) + { + firsttime = true; + init(); + } + + lastTime = performance.now(); + + let v = anim.getValue(t); + + result.set(v); + next.trigger(); +}; + + +}; + +Ops.Anim.AnimNumber.prototype = new CABLES.Op(); +CABLES.OPS["e5b0b016-9663-4c9d-9365-f54ae3c5fbb6"]={f:Ops.Anim.AnimNumber,objName:"Ops.Anim.AnimNumber"}; + + + + +// ************************************************************** +// +// Ops.Anim.Bang +// +// ************************************************************** + +Ops.Anim.Bang = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUpdate = op.inTrigger("update"), + inBang = op.inTriggerButton("Bang"), + inDuration = op.inValue("Duration", 0.1), + invert = op.inBool("Invert", false), + outTrigger = op.outTrigger("Trigger Out"), + outValue = op.outNumber("Value"); + +const anim = new CABLES.Anim(); +let startTime = CABLES.now(); +op.toWorkPortsNeedToBeLinked(inUpdate); + +inBang.onTriggered = function () +{ + startTime = CABLES.now(); + + anim.clear(); + anim.setValue(0, 1); + anim.setValue(inDuration.get(), 0); +}; + +inUpdate.onTriggered = function () +{ + let v = anim.getValue((CABLES.now() - startTime) / 1000); + if (invert.get()) outValue.set(1.0 - v); + else outValue.set(v); + + outTrigger.trigger(); +}; + + +}; + +Ops.Anim.Bang.prototype = new CABLES.Op(); +CABLES.OPS["92ca45a7-5b4b-4238-956e-23d79bdc659f"]={f:Ops.Anim.Bang,objName:"Ops.Anim.Bang"}; + + + + +// ************************************************************** +// +// Ops.Anim.BoolAnim +// +// ************************************************************** + +Ops.Anim.BoolAnim = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const anim = new CABLES.Anim(); + +const + exe = op.inTrigger("exe"), + bool = op.inValueBool("bool"), + pease = anim.createPort(op, "easing"), + duration = op.inValue("duration", 0.5), + dir = op.inValueSelect("Direction", ["Animate Both", "Only True", "Only False"], "Both"), + valueFalse = op.inValue("value false", 0), + valueTrue = op.inValue("value true", 1), + next = op.outTrigger("trigger"), + value = op.outNumber("value"), + finished = op.outBoolNum("finished"), + finishedTrigger = op.outTrigger("Finished Trigger"); + +const startTime = CABLES.now(); +op.toWorkPortsNeedToBeLinked(exe); +op.setPortGroup("Animation", [duration, pease]); +op.setPortGroup("Values", [valueFalse, valueTrue]); + +dir.onChange = bool.onChange = valueFalse.onChange = valueTrue.onChange = duration.onChange = setAnim; +setAnim(); + +function setAnim() +{ + finished.set(false); + const now = (CABLES.now() - startTime) / 1000; + const oldValue = anim.getValue(now); + anim.clear(); + + anim.setValue(now, oldValue); + + if (!bool.get()) + { + if (dir.get() != "Only True") anim.setValue(now + duration.get(), valueFalse.get()); + else anim.setValue(now, valueFalse.get()); + } + else + { + if (dir.get() != "Only False") anim.setValue(now + duration.get(), valueTrue.get()); + else anim.setValue(now, valueTrue.get()); + } +} + +exe.onTriggered = function () +{ + const t = (CABLES.now() - startTime) / 1000; + value.set(anim.getValue(t)); + + if (anim.hasEnded(t)) + { + if (!finished.get()) finishedTrigger.trigger(); + finished.set(true); + } + + next.trigger(); +}; + + +}; + +Ops.Anim.BoolAnim.prototype = new CABLES.Op(); +CABLES.OPS["06ad9d35-ccf5-4d31-889c-e23fa062588a"]={f:Ops.Anim.BoolAnim,objName:"Ops.Anim.BoolAnim"}; + + + + +// ************************************************************** +// +// Ops.Anim.FrameRangeAnimSwitcher +// +// ************************************************************** + +Ops.Anim.FrameRangeAnimSwitcher = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + idx = op.inValueInt("Index"), + inDur = op.inValue("Duration", 0.5), + resultTime1 = op.outNumber("Time 1"), + resultFade = op.outNumber("Time Fade"), + resultTime2 = op.outNumber("Time 2"); + +let anim = new CABLES.Anim(); +anim.createPort(op, "easing"); + +let startTime = 0; +let valuePorts = []; +let oldIdx = -1; + +idx.onChange = setIndex; + +for (let i = 0; i < 10; i++) +{ + let p = op.inValue("Value " + i); + valuePorts.push(p); + p.onChange = update; +} + +setIndex(); + +function update() +{ + if (!valuePorts[oldIdx]) return; + if (!valuePorts[idx.get()]) return; + + resultTime1.set(valuePorts[oldIdx].get()); + resultTime2.set(valuePorts[idx.get()].get()); + let fade = anim.getValue(CABLES.now() / 1000); + resultFade.set(fade); +} + +function setIndex() +{ + let now = (CABLES.now()) / 1000; + let startTime = now; + anim.clear(); + + if (oldIdx == -1)oldIdx = idx.get(); + + anim.setValue(now, 0); + anim.setValue(now + inDur.get(), 1, + function () + { + oldIdx = idx.get(); + let now = (CABLES.now()) / 1000; + anim.setValue(now, 0); + }); + update(); +} + + +}; + +Ops.Anim.FrameRangeAnimSwitcher.prototype = new CABLES.Op(); +CABLES.OPS["d801703b-af5f-4988-af90-39d57706c4b6"]={f:Ops.Anim.FrameRangeAnimSwitcher,objName:"Ops.Anim.FrameRangeAnimSwitcher"}; + + + + +// ************************************************************** +// +// Ops.Anim.FrameRangeAnim_v2 +// +// ************************************************************** + +Ops.Anim.FrameRangeAnim_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let inTime = op.inValue("Time"); +let inStr = op.inString("Frames"); +let inLoop = op.inValueBool("Loop"); +let inRewind = op.inTriggerButton("Rewind"); +let outValue = op.outNumber("result time"); +let outArr = op.outArray("Expanded Frames"); +let finished = op.outBoolNum("Finished", false); +let finishedTrigger = op.outTrigger("Finished Trigger"); +let outAnimLength = op.outNumber("Anim Length"); +let outProgress = op.outNumber("Progress"); + +let anim = new CABLES.Anim(); +let FPS = 30; + +let timeOffset = 0; +inStr.onChange = +inLoop.onChange = parse; + +inRewind.onTriggered = function () +{ + timeOffset = inTime.get(); +}; + +function setupAnim(frames) +{ + anim.clear(); + anim.loop = inLoop.get(); + + for (let i = 0; i < frames.length; i++) + { + anim.defaultEasing = CABLES.ANIM.EASING_ABSOLUTE; + if (i < frames.length - 1) + { + if (frames[i + 1] == frames[i] + 1 || frames[i + 1] == frames[i] - 1) + anim.defaultEasing = CABLES.ANIM.EASING_LINEAR; + } + + anim.setValue(i / FPS, frames[i] / FPS); + } +} + +function parse() +{ + let str = inStr.get(); + let frames = []; + let parts = str.split(","); + + for (let i = 0; i < parts.length; i++) + { + if (CABLES.UTILS.isNumeric(parts[i])) + { + frames.push(parseInt(parts[i], 10)); + } + else if (parts[i].indexOf("-") > -1) + { + let r = parts[i].split("-"); + r[0] = parseInt(r[0], 10); + r[1] = parseInt(r[1], 10); + + if (r[1] > r[0]) + for (var j = r[0]; j <= r[1]; j++) frames.push(j); + else + for (var j = r[0]; j >= r[1]; j--) frames.push(j); + } + } + + outArr.set(null); + outArr.set(frames); + outAnimLength.set(frames.length / FPS); + setupAnim(frames); +} + +inTime.onChange = function () +{ + let t = inTime.get() - timeOffset; + outValue.set(anim.getValue(t)); + + if (anim.keys.length > 1) + { + let l = anim.keys[anim.keys.length - 1].time - anim.keys[0].time; + let p = (t % l) / (l); + if (!inLoop.get()) p = Math.min(t / l, 1); + outProgress.set(p); + } + + if (anim.hasEnded(t)) + { + if (!finished.get()) finishedTrigger.trigger(); + finished.set(true); + } + else + { + finished.set(false); + } +}; + + +}; + +Ops.Anim.FrameRangeAnim_v2.prototype = new CABLES.Op(); +CABLES.OPS["c0613f9e-5ddb-479e-bc90-4ffc23f97860"]={f:Ops.Anim.FrameRangeAnim_v2,objName:"Ops.Anim.FrameRangeAnim_v2"}; + + + + +// ************************************************************** +// +// Ops.Anim.InOutInAnim +// +// ************************************************************** + +Ops.Anim.InOutInAnim = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const anim = new CABLES.Anim(); + +const + update = op.inTrigger("Update"), + duration1 = op.inValue("Duration Out", 0.25), + easing1 = anim.createPort(op, "Easing Out"), + value1 = op.inValue("Value Out", 0), + holdDuration = op.inValue("Hold duration", 0.0), + duration2 = op.inValue("Duration In", 0.25), + easing2 = anim.createPort(op, "Easing In"), + value2 = op.inValue("Value In", 1), + trigger = op.inTriggerButton("Start"), + next = op.outTrigger("Next"), + outVal = op.outNumber("Result", 0), + started = op.outTrigger("Started"), + middle = op.outTrigger("Middle"), + finished = op.outTrigger("finished"); + +let time = 0; +trigger.onTriggered = setupAnim; + +update.onTriggered = function () +{ + time = CABLES.now() / 1000.0; + if (anim.isStarted(time)) outVal.set(anim.getValue(time)); + else outVal.set(value2.get()); + + next.trigger(); +}; + +value2.onChange = function () +{ + outVal.set(value2.get()); +}; + +function setupAnim() +{ + anim.clear(); + // start + anim.setValue(time, value2.get(), function () + { + started.trigger(); + }); + // attack + anim.setValue(time + + duration1.get(), value1.get(), function () + { + + }); + // Hold + anim.setValue(time + + duration1.get() + holdDuration.get(), value1.get(), function () + { + middle.trigger(); + }); + // release + anim.setValue(time + + duration1.get() + + duration2.get() + holdDuration.get(), value2.get(), function () + { + finished.trigger(); + }); + + anim.keys[0].setEasing( + anim.easingFromString(easing1.get())); + + anim.keys[2].setEasing( + anim.easingFromString(easing2.get())); +} + + +}; + +Ops.Anim.InOutInAnim.prototype = new CABLES.Op(); +CABLES.OPS["ae46d30d-9ea6-417b-968b-e7b5726afdde"]={f:Ops.Anim.InOutInAnim,objName:"Ops.Anim.InOutInAnim"}; + + + + +// ************************************************************** +// +// Ops.Anim.LFO +// +// ************************************************************** + +Ops.Anim.LFO = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + time = op.inValue("Time"), + type = op.inValueSelect("Type", ["sine", "triangle", "ramp up", "ramp down", "block"], "sine"), + phase = op.inValue("Phase", 0), + amplitude = op.inValue("Amplitude", 1), + result = op.outNumber("Result"); + +let v = 0; +type.onChange = updateType; + +updateType(); + +const PI2 = Math.PI / 2; + +function updateType() +{ + if (type.get() == "sine") time.onChange = sine; + if (type.get() == "ramp up") time.onChange = rampUp; + if (type.get() == "ramp down") time.onChange = rampDown; + if (type.get() == "block") time.onChange = block; + if (type.get() == "triangle") time.onChange = triangle; +} + +function updateTime() +{ + return time.get() + phase.get(); +} + +function block() +{ + let t = updateTime() + 0.5; + v = t % 2.0; + if (v <= 1.0)v = -1; + else v = 1; + v *= amplitude.get(); + result.set(v); +} + +function rampUp() +{ + let t = (updateTime() + 1); + t *= 0.5; + v = t % 1.0; + v -= 0.5; + v *= 2.0; + v *= amplitude.get(); + result.set(v); +} + +function rampDown() +{ + let t = updateTime(); + v = t % 1.0; + v -= 0.5; + v *= -2.0; + v *= amplitude.get(); + result.set(v); +} + +function triangle() +{ + let t = updateTime(); + v = t % 2.0; + if (v > 1) v = 2.0 - v; + v -= 0.5; + v *= 2.0; + v *= amplitude.get(); + result.set(v); +} + +function sine() +{ + let t = updateTime() * Math.PI - (PI2); + v = Math.sin((t)); + v *= amplitude.get(); + result.set(v); +} + + +}; + +Ops.Anim.LFO.prototype = new CABLES.Op(); +CABLES.OPS["559bb980-78fb-47a7-a199-16f10808b150"]={f:Ops.Anim.LFO,objName:"Ops.Anim.LFO"}; + + + + +// ************************************************************** +// +// Ops.Anim.RandomAnim +// +// ************************************************************** + +Ops.Anim.RandomAnim = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + + min = op.inValue("min", 0), + max = op.inValue("max", 1), + seed = op.inValue("random seed", 0), + + duration = op.inValue("duration", 0.5), + pause = op.inValue("pause between", 0), + next = op.outTrigger("Next"), + result = op.outNumber("result"), + looped = op.outTrigger("Looped"); + +const anim = new CABLES.Anim(); +anim.createPort(op, "easing", reinit); + +op.setPortGroup("Timing", [duration, pause]); +op.setPortGroup("Value", [min, max, seed]); + +op.toWorkPortsNeedToBeLinked(exe); + +let counter = 0; + +min.onChange = + max.onChange = + pause.onChange = + seed.onChange = + duration.onChange = reinitLater; + +let needsReinit = true; + +function reinitLater() +{ + needsReinit = true; +} + +function getRandom() +{ + const minVal = (min.get()); + return Math.seededRandom() * (max.get() - minVal) + minVal; +} + +function reinit() +{ + Math.randomSeed = seed.get() + counter * 100; + init(getRandom()); + needsReinit = false; +} + +function init(v) +{ + anim.clear(); + + anim.setValue(CABLES.now() / 1000.0, v); + if (pause.get() !== 0.0) anim.setValue(CABLES.now() / 1000.0 + pause.get(), v); + + anim.setValue(duration.get() + CABLES.now() / 1000.0 + pause.get(), getRandom()); +} + +exe.onTriggered = updateExe; + +function updateExe() +{ + if (needsReinit)reinit(); + + const t = CABLES.now() / 1000.0; + const v = anim.getValue(t); + + if (anim.hasEnded(t)) + { + counter++; + anim.clear(); + init(v); + looped.trigger(); + } + result.set(v); + next.trigger(); +} + + +}; + +Ops.Anim.RandomAnim.prototype = new CABLES.Op(); +CABLES.OPS["2d2e5f0e-b69f-4789-9a48-1ee6ade5049a"]={f:Ops.Anim.RandomAnim,objName:"Ops.Anim.RandomAnim"}; + + + + +// ************************************************************** +// +// Ops.Anim.SimpleAnim +// +// ************************************************************** + +Ops.Anim.SimpleAnim = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + reset = op.inTriggerButton("reset"), + rewind = op.inTriggerButton("rewind"), + inStart = op.inValueFloat("start", 0), + inEnd = op.inValueFloat("end", 1), + duration = op.inValueFloat("duration", 0.5), + loop = op.inValueBool("loop"), + waitForReset = op.inValueBool("Wait for Reset", true), + next = op.outTrigger("Next"), + result = op.outNumber("result"), + finished = op.outNumber("finished"), + finishedTrigger = op.outTrigger("Finished Trigger"); + +const anim = new CABLES.Anim(); +let resetted = false; +anim.createPort(op, "easing", init); +let currentEasing = -1; +loop.onChange = init; +init(); + +duration.onChange = init; + + +function init() +{ + if (anim.keys.length != 3) + { + anim.setValue(0, 0); + anim.setValue(1, 0); + anim.setValue(2, 0); + } + + anim.keys[0].time = CABLES.now() / 1000.0; + anim.keys[0].value = inStart.get(); + if (anim.defaultEasing != currentEasing) anim.keys[0].setEasing(anim.defaultEasing); + + anim.keys[1].time = duration.get() + CABLES.now() / 1000.0; + anim.keys[1].value = inEnd.get(); + + if (anim.defaultEasing != currentEasing) anim.keys[1].setEasing(anim.defaultEasing); + + anim.loop = loop.get(); + if (anim.loop) + { + anim.keys[2].time = (2.0 * duration.get()) + CABLES.now() / 1000.0; + anim.keys[2].value = inStart.get(); + if (anim.defaultEasing != currentEasing) anim.keys[2].setEasing(anim.defaultEasing); + } + else + { + anim.keys[2].time = anim.keys[1].time; + anim.keys[2].value = anim.keys[1].value; + if (anim.defaultEasing != currentEasing) anim.keys[2].setEasing(anim.defaultEasing); + } + finished.set(false); + + currentEasing = anim.defaultEasing; +} + +reset.onTriggered = function () +{ + resetted = true; + init(); +}; + +rewind.onTriggered = function () +{ + anim.keys[0].time = CABLES.now() / 1000.0; + anim.keys[0].value = inStart.get(); + + anim.keys[1].time = CABLES.now() / 1000.0; + anim.keys[1].value = inStart.get(); + + anim.keys[2].time = CABLES.now() / 1000.0; + anim.keys[2].value = inStart.get(); + + result.set(inStart.get()); +}; + +exe.onTriggered = function () +{ + if (waitForReset.get() && !resetted) + { + result.set(inStart.get()); + return; + } + let t = CABLES.now() / 1000; + let v = anim.getValue(t); + result.set(v); + if (anim.hasEnded(t)) + { + if (!finished.get()) finishedTrigger.trigger(); + finished.set(true); + } + + next.trigger(); +}; + + +}; + +Ops.Anim.SimpleAnim.prototype = new CABLES.Op(); +CABLES.OPS["5b244b6e-c505-4743-b2cc-8119ef720028"]={f:Ops.Anim.SimpleAnim,objName:"Ops.Anim.SimpleAnim"}; + + + + +// ************************************************************** +// +// Ops.Anim.SineAnim +// +// ************************************************************** + +Ops.Anim.SineAnim = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + mode = op.inSwitch("Mode", ["Sine", "Cosine"], "Sine"), + phase = op.inValueFloat("phase", 0), + mul = op.inValueFloat("frequency", 1), + amplitude = op.inValueFloat("amplitude", 1), + trigOut = op.outTrigger("Trigger out"), + result = op.outNumber("result"); + +let selectIndex = 0; +const SINE = 0; +const COSINE = 1; + +op.toWorkPortsNeedToBeLinked(exe); + +exe.onTriggered = exec; +mode.onChange = onModeChange; + +exec(); +onModeChange(); + +function onModeChange() +{ + let modeSelectValue = mode.get(); + + if (modeSelectValue === "Sine") selectIndex = SINE; + else if (modeSelectValue === "Cosine") selectIndex = COSINE; + + exec(); +} + +function exec() +{ + if (selectIndex == SINE) result.set(amplitude.get() * Math.sin((op.patch.freeTimer.get() * mul.get()) + phase.get())); + else result.set(amplitude.get() * Math.cos((op.patch.freeTimer.get() * mul.get()) + phase.get())); + trigOut.trigger(); +} + + +}; + +Ops.Anim.SineAnim.prototype = new CABLES.Op(); +CABLES.OPS["736d3d0e-c920-449e-ade0-f5ca6018fb5c"]={f:Ops.Anim.SineAnim,objName:"Ops.Anim.SineAnim"}; + + + + +// ************************************************************** +// +// Ops.Anim.Smooth +// +// ************************************************************** + +Ops.Anim.Smooth = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Update"), + inMode = op.inBool("Separate inc/dec", false), + inVal = op.inValue("Value"), + next = op.outTrigger("Next"), + inDivisorUp = op.inValue("Inc factor", 4), + inDivisorDown = op.inValue("Dec factor", 4), + result = op.outNumber("Result", 0); + +let val = 0; +let goal = 0; +let oldVal = 0; +let lastTrigger = 0; + +op.toWorkPortsNeedToBeLinked(exec); + +let divisorUp; +let divisorDown; +let divisor = 4; +let finished = true; + +let selectIndex = 0; +const MODE_SINGLE = 0; +const MODE_UP_DOWN = 1; + +onFilterChange(); +getDivisors(); + +inMode.setUiAttribs({ "hidePort": true }); + +inDivisorUp.onChange = inDivisorDown.onChange = getDivisors; +inMode.onChange = onFilterChange; +update(); + +function onFilterChange() +{ + const selectedMode = inMode.get(); + if (!selectedMode) selectIndex = MODE_SINGLE; + else selectIndex = MODE_UP_DOWN; + + if (selectIndex == MODE_SINGLE) + { + inDivisorDown.setUiAttribs({ "greyout": true }); + inDivisorUp.setUiAttribs({ "title": "Inc/Dec factor" }); + } + else if (selectIndex == MODE_UP_DOWN) + { + inDivisorDown.setUiAttribs({ "greyout": false }); + inDivisorUp.setUiAttribs({ "title": "Inc factor" }); + } + + getDivisors(); + update(); +} + +function getDivisors() +{ + if (selectIndex == MODE_SINGLE) + { + divisorUp = inDivisorUp.get(); + divisorDown = inDivisorUp.get(); + } + else if (selectIndex == MODE_UP_DOWN) + { + divisorUp = inDivisorUp.get(); + divisorDown = inDivisorDown.get(); + } + + if (divisorUp <= 0.2 || divisorUp != divisorUp)divisorUp = 0.2; + if (divisorDown <= 0.2 || divisorDown != divisorDown)divisorDown = 0.2; +} + +inVal.onChange = function () +{ + finished = false; + let oldGoal = goal; + goal = inVal.get(); +}; + +inDivisorUp.onChange = function () +{ + getDivisors(); +}; + +function update() +{ + let tm = 1; + if (performance.now() - lastTrigger > 500 || lastTrigger === 0) val = inVal.get() || 0; + else tm = (performance.now() - lastTrigger) / (performance.now() - lastTrigger); + lastTrigger = performance.now(); + + if (val != val)val = 0; + + if (divisor <= 0)divisor = 0.0001; + + const diff = goal - val; + + if (diff >= 0) val += (diff) / (divisorDown * tm); + else val += (diff) / (divisorUp * tm); + + if (Math.abs(diff) < 0.00001)val = goal; + + if (divisor != divisor)val = 0; + if (val != val || val == -Infinity || val == Infinity)val = inVal.get(); + + if (oldVal != val) + { + result.set(val); + oldVal = val; + } + + if (val == goal && !finished) + { + finished = true; + result.set(val); + } + + next.trigger(); +} + +exec.onTriggered = function () +{ + update(); +}; + + +}; + +Ops.Anim.Smooth.prototype = new CABLES.Op(); +CABLES.OPS["5677b5b5-753a-4fbf-9e91-64c81ec68a2f"]={f:Ops.Anim.Smooth,objName:"Ops.Anim.Smooth"}; + + + + +// ************************************************************** +// +// Ops.Anim.Snap +// +// ************************************************************** + +Ops.Anim.Snap = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inVal = op.inValue("Delta"), + + snapVals = op.inArray("Snap at Values"), + snapDist = op.inValue("Snap Distance"), + snapDistRelease = op.inValue("Snap Distance Release"), + inSlow = op.inValue("Slowdown", 0.4), + inBlock = op.inValue("Block Input after snap"), + inReset = op.inTriggerButton("Reset"), + inMin = op.inValue("Min", 0), + inMax = op.inValue("Max", 0), + + inMul = op.inValue("Value Mul", 1), + inEnabled = op.inValueBool("Enabled", true), + + outVal = op.outNumber("Result"), + outDist = op.outNumber("Distance"), + outSnapped = op.outBoolNum("Snapped"), + outWasSnapped = op.outBoolNum("was snapped"); + +inVal.onChange = update; +inVal.changeAlways = true; + +let snapped = false; +let val = 0; +let hasError = false; +let timeout = 0; +let blocking = false; +let lastValue = -1; +let snappedArr = []; + +snapVals.onChange = checkError; + +inReset.onTriggered = function () +{ + val = 0; + outVal.set(val); + // update(); +}; + +function checkError() +{ + let snaps = snapVals.get(); + if (!snaps || snaps.length == 0) + { + op.setUiError("snapsnull", "needs array containing snap points"); + hasError = true; + return; + } + + if (hasError) + { + op.setUiError("snapsnull", null); + hasError = false; + setTimeout(update, 500); + } + + snappedArr = []; + for (let i = 0; i < snapVals.length; i++) + { + snappedArr[i] = false; + } +} + +function update() +{ + if (blocking) return; + let snaps = snapVals.get(); + + let d = 999999999; + let snapvalue = 0; + let currentIndex = -1; + + for (let i = 0; i < snaps.length; i++) + { + let dd = Math.abs(val - snaps[i]) + 0.01; + if (dd < d) + { + d = dd; + snapvalue = snaps[i]; + currentIndex = i; + } + + if (val > snaps[i] && !snappedArr[i]) + { + val = snaps[i]; + d = 0; + currentIndex = i; + } + } + + if (d === 0) return; + if (inVal.get() === 0) return; + + if (d < snapDistRelease.get()) + { + let vv = inVal.get() * Math.abs(((d / snapDistRelease.get()) * inSlow.get())) * inMul.get(); + val += vv; + + clearTimeout(timeout); + + timeout = setTimeout(function () + { + val = snapvalue; + outVal.set(val); + }, 250); + } + else + { + clearTimeout(timeout); + val += inVal.get(); + } + + if (!inEnabled.get()) + { + outVal.set(val); + lastValue = val; + } + + inVal.set(0); + + d = Math.abs(val - snapvalue); + outDist.set(d); + let wassnapped = false; + + if (d > snapDist.get()) + { + snapped = false; + wassnapped = false; + } + + if (!snapped) + { + if (d < snapDist.get()) + { + val = snapvalue; + if (inBlock.get() > 0) + { + blocking = true; + setTimeout(function () + { + blocking = false; + }, inBlock.get() * 1000); + } + snappedArr[currentIndex] = true; + snapped = true; + wassnapped = true; + } + else + { + snapped = false; + } + } + + outSnapped.set(snapped); + outWasSnapped.set(wassnapped); + + if (inMax.get() != inMin.get() != 0) + { + if (val > inMax.get())val = inMax.get(); + else if (val < inMin.get())val = inMin.get(); + } + + outVal.set(val); + lastValue = val; +} + + +}; + +Ops.Anim.Snap.prototype = new CABLES.Op(); +CABLES.OPS["7319d30d-bce2-4e66-8143-e4c0ff5a37a2"]={f:Ops.Anim.Snap,objName:"Ops.Anim.Snap"}; + + + + +// ************************************************************** +// +// Ops.Anim.StringTypeAnimation_v2 +// +// ************************************************************** + +Ops.Anim.StringTypeAnimation_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let text = op.inStringEditor("text", "hello world"); +let inRestart = op.inTriggerButton("Restart"); +let speed = op.inValue("Speed", 500); +let speedVariation = op.inValueSlider("Speed Variation"); +const showCursor = op.inBool("Show Cursor", true); + +let outText = op.outString("Result"); +let outChanged = op.outTrigger("Changed"); +let outFinished = op.outTrigger("Finished"); + +outText.set(" \n "); +let pos = 0; +let updateInterval = 0; +let cursorblink = true; +let finished = false; + +function setNewTimeout() +{ + clearTimeout(updateInterval); + let ms = speed.get() * (Math.random() * (speedVariation.get() * 2 - 1)); + if (text.get() && pos > text.get().length)ms = speed.get(); + updateInterval = setTimeout(update, speed.get() + ms); +} + +inRestart.onTriggered = function () +{ + finished = false; + pos = 0; + setNewTimeout(); +}; + +function update() +{ + if (!text.get() || text.get() === "" || text.get() === "0" || text.get() == "0") + { + outText.set(" "); + return; + } + + let t = text.get().substring(0, pos); + cursorblink = !cursorblink; + + if (pos > text.get().length && cursorblink) + { + if (showCursor.get()) + { + // t+=' '; + // pos++; + } + + if (!finished) + { + outFinished.trigger(); + finished = true; + } + } + else + { + finished = false; + if (showCursor.get()) + { + t += "_"; + } + pos++; + } + + outText.set(t); + outChanged.trigger(); + setNewTimeout(); +} + +text.onChange = function () +{ + finished = false; + pos = 0; + setNewTimeout(); + outText.set(""); +}; + + +}; + +Ops.Anim.StringTypeAnimation_v2.prototype = new CABLES.Op(); +CABLES.OPS["66723fea-7d5f-4509-98fc-8d892a8f8d89"]={f:Ops.Anim.StringTypeAnimation_v2,objName:"Ops.Anim.StringTypeAnimation_v2"}; + + + + +// ************************************************************** +// +// Ops.Anim.TimeDelta +// +// ************************************************************** + +Ops.Anim.TimeDelta = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTriggerButton("exe"), + smooth = op.inValueBool("Smooth", false), + seconds = op.inValueBool("Seconds", false), + trigger = op.outTrigger("trigger"), + result = op.outNumber("result"); + +let lastTime = CABLES.now(); +let diff = 0; +let smoothed = -1; + +exe.onTriggered = function () +{ + diff = (CABLES.now() - lastTime); + if (seconds.get()) diff /= 1000; + + if (smooth.get()) + { + if (smoothed == -1)smoothed = diff; + else + { + smoothed = smoothed * 0.8 + diff * 0.2; + diff = smoothed; + } + } + + result.set(diff); + lastTime = CABLES.now(); + trigger.trigger(); +}; + +exe.onTriggered(); + + +}; + +Ops.Anim.TimeDelta.prototype = new CABLES.Op(); +CABLES.OPS["fba0e516-b50e-4372-9a0c-dc605669ffed"]={f:Ops.Anim.TimeDelta,objName:"Ops.Anim.TimeDelta"}; + + + + +// ************************************************************** +// +// Ops.Anim.Timer_v2 +// +// ************************************************************** + +Ops.Anim.Timer_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inSpeed = op.inValue("Speed", 1), + playPause = op.inValueBool("Play", true), + reset = op.inTriggerButton("Reset"), + inSyncTimeline = op.inValueBool("Sync to timeline", false), + outTime = op.outNumber("Time"); + +op.setPortGroup("Controls", [playPause, reset, inSpeed]); + +const timer = new CABLES.Timer(); +let lastTime = null; +let time = 0; +let syncTimeline = false; + +playPause.onChange = setState; +setState(); + +function setState() +{ + if (playPause.get()) + { + timer.play(); + op.patch.addOnAnimFrame(op); + } + else + { + timer.pause(); + op.patch.removeOnAnimFrame(op); + } +} + +reset.onTriggered = doReset; + +function doReset() +{ + time = 0; + lastTime = null; + timer.setTime(0); + outTime.set(0); +} + +inSyncTimeline.onChange = function () +{ + syncTimeline = inSyncTimeline.get(); + playPause.setUiAttribs({ "greyout": syncTimeline }); + reset.setUiAttribs({ "greyout": syncTimeline }); +}; + +op.onAnimFrame = function (tt) +{ + if (timer.isPlaying()) + { + if (CABLES.overwriteTime !== undefined) + { + outTime.set(CABLES.overwriteTime * inSpeed.get()); + } + else + + if (syncTimeline) + { + outTime.set(tt * inSpeed.get()); + } + else + { + timer.update(); + const timerVal = timer.get(); + + if (lastTime === null) + { + lastTime = timerVal; + return; + } + + const t = Math.abs(timerVal - lastTime); + lastTime = timerVal; + + time += t * inSpeed.get(); + if (time != time)time = 0; + outTime.set(time); + } + } +}; + + +}; + +Ops.Anim.Timer_v2.prototype = new CABLES.Op(); +CABLES.OPS["aac7f721-208f-411a-adb3-79adae2e471a"]={f:Ops.Anim.Timer_v2,objName:"Ops.Anim.Timer_v2"}; + + + + +// ************************************************************** +// +// Ops.Api.FxHash.FxHash +// +// ************************************************************** + +Ops.Api.FxHash.FxHash = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +if (!CABLES.fakefxhash && !window.fxhash || CABLES.fakefxhash) +{ + CABLES.fakefxhash = true; +} + +const + isReal = !CABLES.fakefxhash, + alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; + +const + inHash = op.inString("Hash", ""), + inRandomizeHash = op.inTriggerButton("Randomize Hash"), + outHash = op.outString("fxhash"), + outRandom1 = op.outNumber("fxrand 1"), + outRandom2 = op.outNumber("fxrand 2"), + outRandom3 = op.outNumber("fxrand 3"), + outRandom4 = op.outNumber("fxrand 4"), + outArr = op.outArray("Random Numbers"), + outEmbedded = op.outBoolNum("fxhash environment", isReal); + +inHash.onChange = init; + +let inited = false; + +init(); + +inRandomizeHash.onTriggered = () => +{ + inHash.set(randomHash()); + op.refreshParams(); +}; + +function randomHash() +{ + let str = ""; + const all = alphabet.length - 1; + + for (let i = 0; i < 51; i++) + { + str += alphabet[Math.round(Math.random() * all)]; + } + return str; +} + +function init() +{ + if (isReal && inited) return; + if (!isReal) + { + window.fxhash = inHash.get() || randomHash(); + let b58dec = (str) => [...str].reduce((p, c) => p * alphabet.length + alphabet.indexOf(c) | 0, 0); + let fxhashTrunc = fxhash.slice(2); + let regex = new RegExp(".{" + ((fxhash.length / 4) | 0) + "}", "g"); + let hashes = fxhashTrunc.match(regex).map((h) => b58dec(h)); + + let sfc32 = (a, b, c, d) => + () => + { + a |= 0; b |= 0; c |= 0; d |= 0; + let t = (a + b | 0) + d | 0; + d = d + 1 | 0; + a = b ^ b >>> 9; + b = c + (c << 3) | 0; + c = c << 21 | c >>> 11; + c = c + t | 0; + return (t >>> 0) / 4294967296; + }; + + window.fxrand = sfc32(...hashes); + } + + inited = true; + + outHash.set(window.fxhash); + + outRandom1.set(0); + outRandom2.set(0); + outRandom3.set(0); + outRandom4.set(0); + + outRandom1.set(fxrand()); + outRandom2.set(fxrand()); + outRandom3.set(fxrand()); + outRandom4.set(fxrand()); + + const arr = []; + for (let i = 0; i < 1000; i++)arr.push(fxrand()); + outArr.set(arr); +} + + +}; + +Ops.Api.FxHash.FxHash.prototype = new CABLES.Op(); +CABLES.OPS["090355fe-6ad9-457c-8192-9e306a9fe1eb"]={f:Ops.Api.FxHash.FxHash,objName:"Ops.Api.FxHash.FxHash"}; + + + + +// ************************************************************** +// +// Ops.Api.SoundCloud.SoundCloud_v2 +// +// ************************************************************** + +Ops.Api.SoundCloud.SoundCloud_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + soundCloudUrl = op.inString("SoundCloud URL"), + clientId = op.inString("SoundCloud Client Id", "6f693b837b47b59a17403e79bcff3626"), + clientSecret = op.inString("SoundCloud Client Secret", "efdeff51dee9e7357e2a4394b49d340a"), + streamFormats = op.inDropDown("Formats", []), + streamUrl = op.outString("Stream URL"), + artworkUrl = op.outString("Artwork URL"), + title = op.outString("Title"), + result = op.outObject("Result"); + +streamUrl.ignoreValueSerialize = true; +artworkUrl.ignoreValueSerialize = true; +streamUrl.ignoreValueSerialize = true; +title.ignoreValueSerialize = true; +soundCloudUrl.onChange = streamFormats.onChange = resolve; + +let accessToken = null; + +const fetchData = (token) => +{ + op.setUiError("error", null); + + const baseUrl = "https://api.soundcloud.com"; + const resolveUrl = baseUrl + "/resolve.json?url=" + soundCloudUrl.get(); + fetch(resolveUrl, { + "headers": { + "Authorization": "OAuth " + token + } + }).then((response) => { return response.json(); }).then((data) => + { + const trackUrl = baseUrl + "/tracks/" + data.id + "/streams"; + fetch(trackUrl, { + "headers": { + "Authorization": "OAuth " + token + } + }).then((response) => { return response.json(); }).then((trackData) => + { + const availableFormats = Object.keys(trackData); + streamFormats.setUiAttribs({ "values": availableFormats }); + let formatUrl = trackData[availableFormats[0]]; + if (streamFormats.get() && trackData.hasOwnProperty(streamFormats.get())) + { + formatUrl = trackData[streamFormats.get()]; + } + try + { + streamUrl.set(formatUrl); + artworkUrl.set(data.artwork_url); + title.set(data.title); + result.set(data); + } + catch (e) + { + op.setUiError("error", "failed to load track from soundcloud"); + op.logError(e); + } + }); + }); +}; + +function resolve() +{ + op.setUiError("credentials", null); + if (!clientId.get() || !clientSecret.get()) + { + op.setUiError("credentials", "SoundClound credentials missing - provide client id and secret"); + } + if (soundCloudUrl.get()) + { + fetch("https://api.soundcloud.com/oauth2/token", { + "method": "POST", + "headers": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "body": new URLSearchParams({ + "client_id": clientId.get(), + "client_secret": clientSecret.get(), + "grant_type": "client_credentials" + }) + }).then((response) => { return response.json(); }).then((data) => + { + const newToken = data.access_token; + accessToken = newToken; + fetchData(newToken); + }); + } +} + + +}; + +Ops.Api.SoundCloud.SoundCloud_v2.prototype = new CABLES.Op(); +CABLES.OPS["1b6b0679-4f26-4886-af31-ee696936ea8f"]={f:Ops.Api.SoundCloud.SoundCloud_v2,objName:"Ops.Api.SoundCloud.SoundCloud_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.AnglesBetweenPoints +// +// ************************************************************** + +Ops.Array.AnglesBetweenPoints = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Points"), + inTheta = op.inFloat("Theta", 0), + inPhi = op.inFloat("Phi", 0), + outArr = op.outArray("Rotations"); + +let q = quat.create(); + +inTheta.onChange = +inPhi.onChange = +inArr.onChange = () => +{ + const arr = inArr.get(); + const result = []; + + if (!arr || arr.length == 0) + { + outArr.set(null); + return; + } + + let vUp = vec3.create(); + + let qprev = quat.create(); + let prevqs = []; + + for (let i = 0; i < arr.length - 3; i += 3) + { + let va = [arr[i + 0], arr[i + 1], arr[i + 2]]; + let vb = [arr[i + 3 + 0], arr[i + 3 + 1], arr[i + 3 + 2]]; + + const + x = vb[0] - va[0], + y = vb[1] - va[1], + z = vb[2] - va[2], + r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2)), + theta = Math.acos(y / r) * (180 / Math.PI) + inTheta.get(), + phi = Math.atan2(x, z) * (180 / Math.PI) + inPhi.get(); + + result.push(phi, -theta, 0); + // beamContainer.transform.rotation = Quaternion.Euler(-90 + phi, -theta, 0f); + + /// /////////////////////////// + + // vec3.set(vb, + // vb[0] - va[0], + // vb[1] - va[1], + // vb[2] - va[2]); + + // vec3.normalize(vb, vb); + + // vec3.copy(va,vUp); + + // quat.rotationTo(q, va, vb); + + // result.push(q[0],q[1],q[2],q[3]); + } + + outArr.set(null); + outArr.set(result); +}; + + +}; + +Ops.Array.AnglesBetweenPoints.prototype = new CABLES.Op(); +CABLES.OPS["0d9eb214-c1a2-4ef5-8bcb-79b89fcfba57"]={f:Ops.Array.AnglesBetweenPoints,objName:"Ops.Array.AnglesBetweenPoints"}; + + + + +// ************************************************************** +// +// Ops.Array.AnimArray_v2 +// +// ************************************************************** + +Ops.Array.AnimArray_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTrigger = op.inTrigger("Update"), + inArr = op.inArray("Next Array"), + inDur = op.inFloat("Duration", 1), + next = op.outTrigger("Next"), + outArr = op.outArray("Matrix"); + +let lastTime = 0; +let startTime = 0; +let firsttime = true; +let cycle = 1; + +const anim = new CABLES.Anim(); +anim.createPort(op, "easing", init); +anim.loop = false; + +let lastArr = null; + +inDur.onChange = inArr.onChange = init; + +let arr1, arr2; +let result = []; + +inTrigger.onTriggered = () => +{ + let t = CABLES.now() / 1000; + const perc = anim.getValue(t); + if (arr1 && arr2) ipMat(perc); +}; + +inArr.onLinkChanged = () => +{ + if (inArr) inArr.copyLinkedUiAttrib("stride", outArr); +}; + +function copyArray(a) +{ + let b = []; + b.length = a.length; + for (let i = 0; i < a.length; i++) b[i] = a[i]; + return b; +} + +function init() +{ + if (!inArr.get()) return; + + // if (inArr.get() == lastArr) return; // cant dothat, then changes in array ref are not working... + // if (lastArr) + // if (inArr.get() == lastArr) + // return; + + lastArr = inArr.get(); + startTime = performance.now(); + anim.clear(CABLES.now() / 1000.0); + + anim.setValue(CABLES.now() / 1000.0, cycle); + + if (cycle == 1) cycle = 0; + else cycle = 1; + + if (result.length != lastArr.length)result = copyArray(lastArr); + + if (cycle == 0) + { + arr1 = inArr.get(); + arr2 = copyArray(result); + } + else + { + arr1 = copyArray(result); + arr2 = inArr.get(); + } + + anim.setValue(inDur.get() + CABLES.now() / 1000.0, cycle); + + firsttime = false; +} + +function ip(val1, val2, perc) +{ + return ((val2 - val1) * perc + val1); +} + +function ipMat(perc) +{ + if (!arr1 || !arr2 || arr1.length != arr2.length) + { + outArr.set(null); + op.logError("arrays wrong", arr1.length, arr2.length); + } + else + { + for (let i = 0; i < arr1.length; i++) + { + result[i] = ip(arr1[i], arr2[i], perc); + } + + outArr.set(null); + outArr.set(result); + } + next.trigger(); +} + + +}; + +Ops.Array.AnimArray_v2.prototype = new CABLES.Op(); +CABLES.OPS["048e852b-62ea-4314-b6f2-9ac5e7cbb066"]={f:Ops.Array.AnimArray_v2,objName:"Ops.Array.AnimArray_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.Array1To3 +// +// ************************************************************** + +Ops.Array.Array1To3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArr = op.inArray("Array1x"), + outArr = op.outArray("Array3x", null, 3), + outTotalPoints = op.outNumber("Total points"), + outArrayLength = op.outNumber("Array length"); + +let arr = []; + +inArr.onChange = function () +{ + let theArray = inArr.get(); + if (!theArray) + { + outArr.set(null); + outTotalPoints.set(0); + outArrayLength.set(0); + return; + } + + if ((theArray.length) * 3 != arr.length) + { + arr.length = (theArray.length) * 3; + } + + for (let i = 0; i < theArray.length; i++) + { + arr[i * 3 + 0] = i; + arr[i * 3 + 1] = theArray[i]; + arr[i * 3 + 2] = 0; + } + + outArr.set(null); + outArr.set(arr); + outTotalPoints.set(arr.length / 3); + outArrayLength.set(arr.length); +}; + + +}; + +Ops.Array.Array1To3.prototype = new CABLES.Op(); +CABLES.OPS["0d67fdf1-8e60-465e-9897-6aae6676e4c9"]={f:Ops.Array.Array1To3,objName:"Ops.Array.Array1To3"}; + + + + +// ************************************************************** +// +// Ops.Array.Array1toX +// +// ************************************************************** + +Ops.Array.Array1toX = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array1x"), + format = op.inSwitch("Format", ["AB", "ABC", "ABCD"], "ABC"), + axisA = op.inSwitch("A", ["Input", "index", "0-1", "-1-1", "0", "1"], "Input"), + axisB = op.inSwitch("B", ["Input", "index", "0-1", "-1-1", "0", "1"], "0-1"), + axisC = op.inSwitch("C", ["Input", "index", "0-1", "-1-1", "0", "1"], "0"), + axisD = op.inSwitch("D", ["Input", "index", "0-1", "-1-1", "0", "1"], "0"), + outArr = op.outArray("Array3x"), + outTotalPoints = op.outNumber("Total points"), + outArrayLength = op.outNumber("Array length"); + +const arr = []; + +axisA.onChange = +axisB.onChange = +axisC.onChange = +axisD.onChange = +inArr.onChange = update; + +format.onChange = function () +{ + axisC.setUiAttribs({ "greyout": format.get().length < 3 }); + axisD.setUiAttribs({ "greyout": format.get().length < 4 }); + + update(); +}; + +function fillArr_0(off, num, stride) +{ + for (let i = 0; i < num; i += stride) + { + arr[i + off] = 0; + } +} + +function fillArr_1(off, num, stride) +{ + for (let i = 0; i < num; i += stride) + { + arr[i + off] = 1; + } +} + +function fillArr_input(off, num, stride) +{ + const theArray = inArr.get(); + for (let i = 0; i < num; i += stride) + { + arr[i + off] = theArray[i / stride]; + } +} + +function fillArr_01(off, num, stride) +{ + for (let i = 0; i < num; i += stride) + { + arr[i + off] = (i / (num)) || 0; + } +} + +function fillArr_index(off, num, stride) +{ + for (let i = 0; i < num; i += stride) + { + arr[i + off] = i / stride; + } +} + +function fillArr(off, meth, stride) +{ + if (meth == "0")fillArr_0(off, arr.length, stride); + if (meth == "0-1")fillArr_01(off, arr.length, stride); + if (meth == "1")fillArr_1(off, arr.length, stride); + if (meth == "Input")fillArr_input(off, arr.length, stride); + if (meth == "index")fillArr_index(off, arr.length, stride); +} + +function update() +{ + const theArray = inArr.get(); + if (!theArray) + { + outArr.set(null); + outTotalPoints.set(0); + outArrayLength.set(0); + return; + } + + const stride = format.get().length; + const l = theArray.length * stride; + arr.length = l; + + if (stride >= 2) fillArr(0, axisA.get(), stride); + if (stride >= 2) fillArr(1, axisB.get(), stride); + if (stride >= 3) fillArr(2, axisC.get(), stride); + if (stride >= 4) fillArr(3, axisD.get(), stride); + + // for(var i=0;i +{ + parametersChanged = true; +}; + +const DISCARD_FUNCS = { + "Sphere": (inArr) => + { + const newArr = []; + const radiusSquared = inSize.get() * inSize.get(); + let CONDITION = (a, b) => { return a > b; }; + if (inInvert.get()) CONDITION = (a, b) => { return a < b; }; + + for (let i = 0, len = inArr.length / 3; i < len; i += 1) + { + vec3.set(tempVec, inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + const distSquared = vec3.squaredDistance(tempVec, rootVec); + + if (CONDITION(distSquared, radiusSquared)) + { + newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + } + + return newArr; + }, + "Box": (inArr) => + { + const size = inSize.get(); + const newArr = []; + for (let i = 0, len = inArr.length / 3; i < len; i += 1) + { + vec3.set(tempVec, inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + + if ( + Math.abs(tempVec[1] - rootVec[1]) > size + || Math.abs(tempVec[0] - rootVec[0]) > size + || Math.abs(tempVec[2] - rootVec[2]) > size + ) + { + if (!inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + else + { + if (inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + } + + return newArr; + }, + "Axis X": (inArr) => + { + const size = inSize.get(); + const newArr = []; + for (let i = 0, len = inArr.length / 3; i < len; i += 1) + { + if (Math.abs(inArr[i * 3] - rootVec[0]) > size) + { + if (!inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + else + { + if (inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + } + + return newArr; + }, + "Axis Y": (inArr) => + { + const size = inSize.get(); + const newArr = []; + for (let i = 0, len = inArr.length / 3; i < len; i += 1) + { + if (Math.abs(inArr[i * 3 + 1] - rootVec[1]) > size) + { + if (!inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + else + { + if (inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + } + + return newArr; + }, + "Axis Z": (inArr) => + { + const size = inSize.get(); + const newArr = []; + for (let i = 0, len = inArr.length / 3; i < len; i += 1) + { + if (Math.abs(inArr[i * 3 + 2] - rootVec[2]) > size) + { + if (!inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + else + { + if (inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + } + + return newArr; + }, + "Axis X Infinite": (inArr) => + { + const size = inSize.get(); + const newArr = []; + for (let i = 0, len = inArr.length / 3; i < len; i += 1) + { + if (inArr[i * 3] - rootVec[0] > size) + { + if (!inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + else + { + if (inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + } + + return newArr; + }, + "Axis Y Infinite": (inArr) => + { + const size = inSize.get(); + const newArr = []; + for (let i = 0, len = inArr.length / 3; i < len; i += 1) + { + if (inArr[i * 3 + 1] - rootVec[1] > size) + { + if (!inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + else + { + if (inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + } + + return newArr; + }, + "Axis Z Infinite": (inArr) => + { + const size = inSize.get(); + const newArr = []; + for (let i = 0, len = inArr.length / 3; i < len; i += 1) + { + if (inArr[i * 3 + 2] - rootVec[2] > size) + { + if (!inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + else + { + if (inInvert.get()) newArr.push(inArr[i * 3 + 0], inArr[i * 3 + 1], inArr[i * 3 + 2]); + } + } + + return newArr; + }, +}; + +inTrigger.onTriggered = () => +{ + const inArr = inArray.get(); + drawHelpers(); + + + vec3.set(transVec, inPosX.get(), inPosY.get(), inPosZ.get()); + vec3.transformMat4(rootVec, transVec, cgl.mMatrix); + + if (!inArr) + { + outTrigger.trigger(); + outArray.set(null); + return; + } + + if (parametersChanged) + { + const newArr = DISCARD_FUNCS[inMode.get()](inArr); + + outArray.set(null); + outArray.set(newArr); + outLength.set(newArr.length); + + parametersChanged = false; + } + + + outX.set(rootVec[0]); + outY.set(rootVec[1]); + outZ.set(rootVec[2]); + outTrigger.trigger(); +}; + +const HELPER_FUNCS = { + "Sphere": () => { return CABLES.GL_MARKER.drawSphere(op, inSize.get()); }, + "Box": () => { return CABLES.GL_MARKER.drawCube(op, inSize.get(), inSize.get(), inSize.get()); }, + "Axis X": () => { return CABLES.GL_MARKER.drawCube(op, inSize.get(), 2, 2); }, + "Axis Y": () => { return CABLES.GL_MARKER.drawCube(op, 2, inSize.get(), 2); }, + "Axis Z": () => { return CABLES.GL_MARKER.drawCube(op, 2, 2, inSize.get()); }, + "Axis X Infinite": () => { return CABLES.GL_MARKER.drawCube(op, inSize.get(), 2, 2); }, + "Axis Y Infinite": () => { return CABLES.GL_MARKER.drawCube(op, 2, inSize.get(), 2); }, + "Axis Z Infinite": () => { return CABLES.GL_MARKER.drawCube(op, 2, 2, inSize.get()); }, +}; + +function drawHelpers() +{ + if (cgl.shouldDrawHelpers(op)) + { + gui.setTransformGizmo({ "posX": inPosX, "posY": inPosY, "posZ": inPosZ }); + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, rootVec); + HELPER_FUNCS[inMode.get()](); + cgl.popModelMatrix(); + } +} + + +}; + +Ops.Array.Array3AreaRemove.prototype = new CABLES.Op(); +CABLES.OPS["db93f8c7-6bad-467e-b38f-333e84838d1d"]={f:Ops.Array.Array3AreaRemove,objName:"Ops.Array.Array3AreaRemove"}; + + + + +// ************************************************************** +// +// Ops.Array.Array3FlipAxis +// +// ************************************************************** + +Ops.Array.Array3FlipAxis = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Points", 3), + inAxis1 = op.inSwitch("Axis 1", ["X", "Y", "Z"], "X"), + inAxis2 = op.inSwitch("Axis 2", ["X", "Y", "Z"], "Y"), + inAxis3 = op.inSwitch("Axis 3", ["X", "Y", "Z"], "Z"), + + inFlipX = op.inBool("Flip Order X", false), + inFlipY = op.inBool("Flip Order Y", false), + inFlipZ = op.inBool("Flip Order Z", false), + + result = op.outArray("Result"); + +const arr = []; + +inArr.onChange = +inFlipX.onChange = +inFlipY.onChange = +inFlipZ.onChange = +inAxis1.onChange = +inAxis2.onChange = +inAxis3.onChange = () => +{ + const oldArr = inArr.get(); + if (!oldArr || oldArr.length == 0) + { + result.set(null); + return; + } + + arr.length = oldArr.length; + + let len = oldArr.length; + + if (inAxis1.get() == "X") for (let i = 0; i < len; i += 3) arr[i + 0] = oldArr[i + 0]; + if (inAxis1.get() == "Y") for (let i = 0; i < len; i += 3) arr[i + 0] = oldArr[i + 1]; + if (inAxis1.get() == "Z") for (let i = 0; i < len; i += 3) arr[i + 0] = oldArr[i + 2]; + + if (inAxis2.get() == "X") for (let i = 0; i < len; i += 3) arr[i + 1] = oldArr[i + 0]; + if (inAxis2.get() == "Y") for (let i = 0; i < len; i += 3) arr[i + 1] = oldArr[i + 1]; + if (inAxis2.get() == "Z") for (let i = 0; i < len; i += 3) arr[i + 1] = oldArr[i + 2]; + + if (inAxis3.get() == "X") for (let i = 0; i < len; i += 3) arr[i + 2] = oldArr[i + 0]; + if (inAxis3.get() == "Y") for (let i = 0; i < len; i += 3) arr[i + 2] = oldArr[i + 1]; + if (inAxis3.get() == "Z") for (let i = 0; i < len; i += 3) arr[i + 2] = oldArr[i + 2]; + + + const maxi = len; + + if (inFlipX.get()) + for (let i = 0; i < len / 2; i += 3) + { + const temp = arr[i + 0]; + arr[i + 0] = arr[len - i]; + arr[len - i] = temp; + } + + if (inFlipY.get()) + for (let i = 0; i < len / 2; i += 3) + { + const temp = arr[i + 1]; + arr[i + 1] = arr[(len - i) + 1]; + arr[(len - i) + 1] = temp; + } + + + if (inFlipZ.get()) + for (let i = 0; i < len / 2; i += 3) + { + const temp = arr[i + 2]; + arr[i + 2] = arr[(len - i) + 2]; + arr[(len - i) + 2] = temp; + } + + + result.set(null); + result.set(arr); +}; + + +}; + +Ops.Array.Array3FlipAxis.prototype = new CABLES.Op(); +CABLES.OPS["1a9a0178-5906-423b-b8f3-0532499ba169"]={f:Ops.Array.Array3FlipAxis,objName:"Ops.Array.Array3FlipAxis"}; + + + + +// ************************************************************** +// +// Ops.Array.Array3GetAverage +// +// ************************************************************** + +Ops.Array.Array3GetAverage = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array", 3), + avgX = op.outNumber("Average X"), + avgY = op.outNumber("Average Y"), + avgZ = op.outNumber("Average Z"); + +inArr.onChange = function () +{ + let arr = inArr.get(); + if (!arr) + { + avgX.set(0); + avgY.set(0); + avgZ.set(0); + return; + } + let x = 0; + let y = 0; + let z = 0; + + for (let i = 0; i < arr.length; i += 3) + { + x += arr[i + 0]; + y += arr[i + 1]; + z += arr[i + 2]; + } + + x /= arr.length / 3; + y /= arr.length / 3; + z /= arr.length / 3; + + avgX.set(x); + avgY.set(y); + avgZ.set(z); +}; + + +}; + +Ops.Array.Array3GetAverage.prototype = new CABLES.Op(); +CABLES.OPS["9643edc7-e479-4b26-9f7a-343cdcdc81f4"]={f:Ops.Array.Array3GetAverage,objName:"Ops.Array.Array3GetAverage"}; + + + + +// ************************************************************** +// +// Ops.Array.Array3GetNumbers +// +// ************************************************************** + +Ops.Array.Array3GetNumbers = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + pArr = op.inArray("Array", 3), + pIndex = op.inValueInt("Index"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outZ = op.outNumber("Z"); + +pArr.onChange = + pIndex.onChange = update; + +function update() +{ + let arr = pArr.get(); + if (!arr) + { + outX.set(0); + outY.set(0); + outZ.set(0); + return; + } + let ind = Math.min(arr.length - 3, pIndex.get() * 3); + if (arr) + { + outX.set(arr[ind + 0]); + outY.set(arr[ind + 1]); + outZ.set(arr[ind + 2]); + } +} + + +}; + +Ops.Array.Array3GetNumbers.prototype = new CABLES.Op(); +CABLES.OPS["56882cc4-c40d-4dc0-bf7c-db1b5a7acad0"]={f:Ops.Array.Array3GetNumbers,objName:"Ops.Array.Array3GetNumbers"}; + + + + +// ************************************************************** +// +// Ops.Array.Array3InterpolateDistributed +// +// ************************************************************** + +Ops.Array.Array3InterpolateDistributed = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Update"), + inArr1 = op.inArray("Array 1", 3), + inArr2 = op.inArray("Array 2", 3), + inProgress = op.inValueSlider("Progress"), + arrOut = op.outArray("Result"), + outArrayLength = op.outNumber("Array length"); + +let resultArr = []; +let indexArray = []; + +inArr2.onChange = updateArr2; +inArr1.onChange = updateArr1; + + +function updateArr1() +{ + if (!inArr1.get()) + { + arrOut.set(null); + outArrayLength.set(0); + return; + } + resultArr.length = inArr1.get().length; +} + +function updateArr2() +{ + if (!inArr2.get()) + { + arrOut.set(null); + outArrayLength.set(0); + return; + } + + indexArray.length = Math.ceil(inArr2.get().length / 3); + + let arr2 = inArr2.get(); + + for (let i = 0; i < indexArray.length; i += 3) + { + let index2 = Math.floor(arr2.length / 3 * Math.random()); + // indexArray[i+0]=arr2[index2*3+0]; + // indexArray[i+1]=arr2[index2*3+1]; + // indexArray[i+2]=arr2[index2*3+2]; + indexArray[i / 3] = index2 * 3; + // indexArray[i+1]=arr2[index2*3+1]; + // indexArray[i+2]=arr2[index2*3+2]; + } + + // arrOut.set(null); + // arrOut.set(resultArr); + // updateArr1(); +} + +exec.onTriggered = function () +{ + let perc = inProgress.get(); + let val1 = 0, val2 = 0; + let arr1 = inArr1.get(); + let arr2 = inArr2.get(); + + let l1 = 0; + let l2 = 0; + if (arr1)l1 = arr1.length; + if (arr2)l2 = arr2.length; + + let l = Math.min(l1, l2); + + + for (let i = 0; i < l; i += 3) + { + val1 = arr1[i]; + val2 = arr2[indexArray[(i / 3)]]; + resultArr[i] = ((val2 - val1) * perc + val1); + + val1 = arr1[i + 1]; + val2 = arr2[indexArray[(i / 3)] + 1]; + resultArr[i + 1] = ((val2 - val1) * perc + val1); + + val1 = arr1[i + 2]; + val2 = arr2[indexArray[(i / 3)] + 2]; + resultArr[i + 2] = ((val2 - val1) * perc + val1); + } + + arrOut.set(null); + arrOut.set(resultArr); + outArrayLength.set(resultArr.length); +}; + + +}; + +Ops.Array.Array3InterpolateDistributed.prototype = new CABLES.Op(); +CABLES.OPS["a004a6e9-a05f-42dc-91d3-69aa88e67a87"]={f:Ops.Array.Array3InterpolateDistributed,objName:"Ops.Array.Array3InterpolateDistributed"}; + + + + +// ************************************************************** +// +// Ops.Array.Array3Multiply +// +// ************************************************************** + +Ops.Array.Array3Multiply = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array3x", 3), + mulX = op.inValue("Mul X", 1), + mulY = op.inValue("Mul Y", 1), + mulZ = op.inValue("Mul Z", 1), + outArr = op.outArray("Result"); + +let arr = []; + +mulY.onChange = mulX.onChange = mulZ.onChange = +inArr.onChange = function () +{ + let newArr = inArr.get(); + if (newArr) + { + if (arr.length != newArr.length)arr.length = newArr.length; + + for (let i = 0; i < newArr.length; i += 3) + { + arr[i + 0] = newArr[i + 0] * mulX.get(); + arr[i + 1] = newArr[i + 1] * mulY.get(); + arr[i + 2] = newArr[i + 2] * mulZ.get(); + } + + outArr.set(null); + outArr.set(arr); + } + else + { + outArr.set(null); + } +}; + + +}; + +Ops.Array.Array3Multiply.prototype = new CABLES.Op(); +CABLES.OPS["a1e4d85f-0955-4ada-819c-c597cec40365"]={f:Ops.Array.Array3Multiply,objName:"Ops.Array.Array3Multiply"}; + + + + +// ************************************************************** +// +// Ops.Array.Array3PushNumbers_v2 +// +// ************************************************************** + +Ops.Array.Array3PushNumbers_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exePort = op.inTriggerButton('Execute'), + inArrayPort = op.inArray('Array'), + value1Port = op.inValue('Value 1'), + value2Port = op.inValue('Value 2'), + value3Port = op.inValue('Value 3'), + + inReset=op.inTriggerButton("Reset"), + nextPort = op.outTrigger('Next'), + outArray=op.outArray("Result Array"); + +const newArr=[]; +exePort.onTriggered = update; + + +inReset.onTriggered= +inArrayPort.onChange=function() +{ + let arr=inArrayPort.get(); + if(!arr) + { + outArray.set([]); + return; + } + newArr.length=arr.length; + for(let i=0;i= inArr.length) + { + begin %= inArr.length; + } + + if (!inArr || size < 1) + { + outArrayPort.set(null); + return; + } + let end = size + begin; + + let newLen = Math.min(inArr.length, begin + end) - begin; + if (newLen < 0) + { + op.setUiError("idx", "invalid index/array length"); + newLen = 0; + } + else op.setUiError("idx", null); + newLen = Math.min(newLen, size); + + if (newLen > size) newLen = inArr.length; + newArr.length = newLen; + for (let i = begin; i < newLen + begin; i++) + { + newArr[i - begin] = inArr[i]; + } + + outArrayPort.set(null); + outArrayLength.set(newLen); + outArrayPort.set(newArr); +} + +// change listeners +inArrayPort.onChange = setOutarray; +beginPort.onChange = setOutarray; +sizePort.onChange = setOutarray; +circularPort.onChange = setOutarray; + + +}; + +Ops.Array.ArrayChunk.prototype = new CABLES.Op(); +CABLES.OPS["c7ee6c6e-ca88-4c24-b289-78bb922bf5f7"]={f:Ops.Array.ArrayChunk,objName:"Ops.Array.ArrayChunk"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayChunkDuplicate +// +// ************************************************************** + +Ops.Array.ArrayChunkDuplicate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr=op.inArray("Array"), + inChunks=op.inInt("Chunk Size",3), + inRepeats=op.inInt("Repeats",10), + outArr=op.outArray("Result"); + + +inArr.onChange= +inChunks.onChange= +inRepeats.onChange=()=> +{ + let chu=inChunks.get(); + let reps=inRepeats.get(); + let arr=inArr.get(); + + if(!arr)return; + if(chu<=0)return; + if(chu<=0)return; + + let rArr=[]; + let count=0; + + for(let i=0;i-1); + } +} + +}; + +Ops.Array.ArrayContains_v2.prototype = new CABLES.Op(); +CABLES.OPS["b982d7e3-c9cf-41dd-aead-66daad785fa8"]={f:Ops.Array.ArrayContains_v2,objName:"Ops.Array.ArrayContains_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayDivide +// +// ************************************************************** + +Ops.Array.ArrayDivide = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let inArray = op.inArray("Array In"); +let inValue = op.inValue("Value", 1.0); +let outArray = op.outArray("Array Out"); + +let newArr = []; +outArray.set(newArr); +inArray.onChange = inValue.onChange = inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + let divide = inValue.get(); + + if (newArr.length != arr.length) newArr.length = arr.length; + + let i = 0; + for (i = 0; i < arr.length; i++) + { + newArr[i] = arr[i] / divide; + } + outArray.set(null); + outArray.set(newArr); +}; + + +}; + +Ops.Array.ArrayDivide.prototype = new CABLES.Op(); +CABLES.OPS["e9406477-1f1c-4cf3-8a64-f6009df05c7c"]={f:Ops.Array.ArrayDivide,objName:"Ops.Array.ArrayDivide"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayFloor +// +// ************************************************************** + +Ops.Array.ArrayFloor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray=op.inArray("In"), + outArray=op.outArray("Result"), + newArr=[]; + +outArray.set(newArr); + +inArray.onChange=function() +{ + var arr=inArray.get(); + + if(!arr) + { + outArray.set(null); + return; + } + + if(newArr.length!=arr.length)newArr.length=arr.length; + + for(var i=0;i +{ + const l = inLimit.get(); + + for (let i = 0; i < inPorts.length; i++) + if (inSlider.get()) inPorts[i].setUiAttribs({ "display": "range" }); + else inPorts[i].setUiAttribs({ "display": null }); + + op.refreshParams(); +}; + +inLimit.onChange = () => +{ + clearTimeout(to); + + to = setTimeout(() => + { + const l = inLimit.get(); + for (let i = 0; i < inPorts.length; i++) + { + inPorts[i].setUiAttribs({ "greyout": i >= l }); + } + }, 300); +}; + +function update() +{ + const l = Math.max(0, Math.ceil(Math.min(inLimit.get(), inPorts.length))); + arr.length = l; + for (let i = 0; i < l; i++) + { + arr[i] = inPorts[i].get(); + } + + outArr.set(null); + outArr.set(arr); + next.trigger(); +} + + +}; + +Ops.Array.ArrayFromNumbers.prototype = new CABLES.Op(); +CABLES.OPS["fb698158-3cf8-49d6-805e-6ea38fdab8c1"]={f:Ops.Array.ArrayFromNumbers,objName:"Ops.Array.ArrayFromNumbers"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayGetArray +// +// ************************************************************** + +Ops.Array.ArrayGetArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArrays = op.inArray("Array of Arrays"), + index = op.inValueInt("Index"), + result = op.outArray("Result Array"); + +inArrays.onChange = +index.onChange = update; + +function update() +{ + let theArray = inArrays.get(); + if (!theArray) + { + result.set(null); + return; + } + + let ind = Math.floor(index.get()); + if (ind < 0 || ind > theArray.length - 1) + { + result.set(null); + op.log("index wrong"); + return; + } + + result.set(null); + result.set(theArray[ind]); +} + + +}; + +Ops.Array.ArrayGetArray.prototype = new CABLES.Op(); +CABLES.OPS["b9d3f42b-3fbf-4522-9df2-a5c769a92d66"]={f:Ops.Array.ArrayGetArray,objName:"Ops.Array.ArrayGetArray"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayGetNumber +// +// ************************************************************** + +Ops.Array.ArrayGetNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + array = op.inArray("array"), + index = op.inValueInt("index"), + value = op.outNumber("value"); + +array.ignoreValueSerialize = true; + +index.onChange = array.onChange = update; + +function update() +{ + if (array.get()) + { + let input = array.get()[index.get()]; + if (isNaN(input)) + { + value.set(0); + return; + } + value.set(parseFloat(input)); + } +} + + +}; + +Ops.Array.ArrayGetNumber.prototype = new CABLES.Op(); +CABLES.OPS["d1189078-70cf-437d-9a37-b2ebe89acdaf"]={f:Ops.Array.ArrayGetNumber,objName:"Ops.Array.ArrayGetNumber"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayGetObject +// +// ************************************************************** + +Ops.Array.ArrayGetObject = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + array = op.inArray("array"), + index = op.inValueInt("index"), + value = op.outObject("value"); + +let last = null; + +array.ignoreValueSerialize = true; +value.ignoreValueSerialize = true; + +index.onChange = update; +array.onChange = update; + +op.toWorkPortsNeedToBeLinked(array); + +function update() +{ + if (index.get() < 0) + { + value.set(null); + return; + } + + const arr = array.get(); + if (!arr) + { + value.set(null); + return; + } + + const ind = index.get(); + if (ind >= arr.length) + { + value.set(null); + return; + } + if (arr[ind]) + { + value.set(null); + value.set(arr[ind]); + last = arr[ind]; + } +} + + +}; + +Ops.Array.ArrayGetObject.prototype = new CABLES.Op(); +CABLES.OPS["44d34542-174c-47c6-b9c6-adde6fc371ac"]={f:Ops.Array.ArrayGetObject,objName:"Ops.Array.ArrayGetObject"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayGetString +// +// ************************************************************** + +Ops.Array.ArrayGetString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + array = op.inArray("array"), + index = op.inValueInt("index"), + result = op.outString("result"); + +array.ignoreValueSerialize = true; + +index.onChange = update; + +array.onChange = function () +{ + update(); +}; + +function update() +{ + const arr = array.get(); + if (arr) result.set(arr[index.get()]); +} + + +}; + +Ops.Array.ArrayGetString.prototype = new CABLES.Op(); +CABLES.OPS["be8f16c0-0c8a-48a2-a92b-45dbf88c76c1"]={f:Ops.Array.ArrayGetString,objName:"Ops.Array.ArrayGetString"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayGetTexture +// +// ************************************************************** + +Ops.Array.ArrayGetTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + array = op.inArray("array"), + index = op.inValueInt("index"), + value = op.outTexture("value"); + +let last = null; + +array.ignoreValueSerialize = true; +value.ignoreValueSerialize = true; + +index.onChange = update; +array.onChange = update; + +op.toWorkPortsNeedToBeLinked(array, value); + +const emptyTex = CGL.Texture.getEmptyTexture(op.patch.cgl); + +function update() +{ + if (index.get() < 0) + { + value.set(emptyTex); + return; + } + + let arr = array.get(); + if (!arr) + { + value.set(emptyTex); + return; + } + + let ind = index.get(); + if (ind >= arr.length) + { + value.set(emptyTex); + return; + } + if (arr[ind]) + { + value.set(emptyTex); + value.set(arr[ind]); + last = arr[ind]; + } +} + + +}; + +Ops.Array.ArrayGetTexture.prototype = new CABLES.Op(); +CABLES.OPS["afea522b-ab72-4574-b721-5d37f5abaf77"]={f:Ops.Array.ArrayGetTexture,objName:"Ops.Array.ArrayGetTexture"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayIndexBetween +// +// ************************************************************** + +Ops.Array.ArrayIndexBetween = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr=op.inArray("Array"), + inNum=op.inFloat("Value",0), + outIndex=op.outNumber("Index"); + +inArr.onChange= + inNum.onChange=update; + +function update() +{ + const arr=inArr.get(); + + if(!arr) + { + outIndex.set(-1); + return; + } + + const n=inNum.get(); + + if(narr[i] && n +{ + + const arr=inArr.get(); + + if(!arr) + { + outMax.set(0); + outMin.set(0); + return; + } + + let min=Number.MAX_VALUE; + let max=-Number.MAX_VALUE; + let minIndex=-1; + let maxIndex=-1; + + for(let i=0;imax) + { + maxIndex=i; + max=arr[i]; + } + + } + + outMax.set(max); + outIndexMax.set(maxIndex); + outMin.set(min); + outIndexMin.set(minIndex); + + +}; + +}; + +Ops.Array.ArrayIndexMinMax.prototype = new CABLES.Op(); +CABLES.OPS["240172a6-7dde-4dcc-b862-bbe764aec3f3"]={f:Ops.Array.ArrayIndexMinMax,objName:"Ops.Array.ArrayIndexMinMax"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayIteratorArray +// +// ************************************************************** + +Ops.Array.ArrayIteratorArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + arr = op.inArray("array"), + trigger = op.outTrigger("trigger"), + idx = op.outNumber("index"), + val = op.outArray("Result"); + +exe.onTriggered = function () +{ + const theArr = arr.get(); + + if (!theArr) + { + val.set(null); + return; + } + + for (let i = 0; i < theArr.length; i++) + { + idx.set(i); + val.set(theArr[i]); + trigger.trigger(); + } +}; + + +}; + +Ops.Array.ArrayIteratorArray.prototype = new CABLES.Op(); +CABLES.OPS["0efcd325-0ccf-452b-a87e-3c44d42f9165"]={f:Ops.Array.ArrayIteratorArray,objName:"Ops.Array.ArrayIteratorArray"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayIteratorNumbers +// +// ************************************************************** + +Ops.Array.ArrayIteratorNumbers = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe=op.inTrigger("exe"), + arr=op.inArray("array"), + trigger=op.outTrigger('trigger'), + idx=op.addOutPort(new CABLES.Port(op,"index")), + val=op.addOutPort(new CABLES.Port(op,"value")); + +exe.onTriggered=function() +{ + if(!arr.get())return; + + for(var i=0;i", "<", ">=", "<=", "==", "!=", + ">pass", "=pass", "<=pass", "==pass", "!=pass"], ">"), + numberIn = op.inValueFloat("Number for comparison", 0.5), + inValueIfTrue = op.inFloat("value if true", 1.0), + inValueIfFalse = op.inFloat("value if false", 0.0), + outArray = op.outArray("Array result"), + outArrayLength = op.outNumber("Array length"); + +op.toWorkPortsNeedToBeLinked(inArray_0); + +let logicFunc; +let showingError = false; + +let mathArray = []; +let selectIndex = 0; + +mathSelect.onChange = onFilterChange; + +inArray_0.onChange = numberIn.onChange = inValueIfFalse.onChange = +inValueIfTrue.onChange = update; + +onFilterChange(); + +function onFilterChange() +{ + let mathSelectValue = mathSelect.get(); + if (mathSelectValue === ">") logicFunc = function (val, comp, t, f) { if (val > comp) return t; return f; }; + else if (mathSelectValue === "<") logicFunc = function (val, comp, t, f) { if (val < comp) return t; return f; }; + else if (mathSelectValue === ">=") logicFunc = function (val, comp, t, f) { if (val >= comp) return t; return f; }; + else if (mathSelectValue === "<=") logicFunc = function (val, comp, t, f) { if (val <= comp) return t; return f; }; + else if (mathSelectValue === "==") logicFunc = function (val, comp, t, f) { if (val === comp) return t; return f; }; + else if (mathSelectValue === "!=") logicFunc = function (val, comp, t, f) { if (val !== comp) return t; return f; }; + else if (mathSelectValue === ">pass") logicFunc = function (val, comp, t, f) { if (val > comp) return val; return f; }; + else if (mathSelectValue === "=pass") logicFunc = function (val, comp, t, f) { if (val >= comp) return val; return f; }; + else if (mathSelectValue === "<=pass") logicFunc = function (val, comp, t, f) { if (val <= comp) return val; return f; }; + else if (mathSelectValue === "==pass") logicFunc = function (val, comp, t, f) { if (val === comp) return val; return f; }; + else if (mathSelectValue === "!=pass") logicFunc = function (val, comp, t, f) { if (val !== comp) return val; return f; }; + update(); + op.setUiAttrib({ "extendTitle": mathSelectValue }); +} + +function update() +{ + let array0 = inArray_0.get(); + + let mathNumberIn = numberIn.get(); + let valueFalse = inValueIfFalse.get(); + let valueTrue = inValueIfTrue.get(); + + mathArray.length = 0; + + if (!array0) + { + outArray.set(null); + outArrayLength.set(0); + return; + } + + mathArray.length = array0.length; + + let i = 0; + + for (i = 0; i < array0.length; i++) + { + mathArray[i] = logicFunc(array0[i], mathNumberIn, valueTrue, valueFalse); + } + + outArray.set(null); + outArray.set(mathArray); + outArrayLength.set(mathArray.length); +} + + +}; + +Ops.Array.ArrayLogic.prototype = new CABLES.Op(); +CABLES.OPS["502083f2-8ade-4cb5-b1c5-e5b60eb3255f"]={f:Ops.Array.ArrayLogic,objName:"Ops.Array.ArrayLogic"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayLogicArray +// +// ************************************************************** + +Ops.Array.ArrayLogicArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// this op expects 2 arrays. The user can then pick a +// logical comparison which will be applied to the +// two arrays. If arrays have a different length then a warning +// is given in the panel +// pass allows the value from array 0 through if the +// comparison evaulates to true else zero + +const inArray_0 = op.inArray("array 0"), + inArray_1 = op.inArray("array 1"), + inValueIfTrue = op.inFloat("value if true", 1.0), + inValueIfFalse = op.inFloat("value if false", 0.0), + mathSelect = op.inValueSelect("Comparison mode", [">", "<", ">=", "<=", "==", "!=", + ">pass", "=pass", "<=pass", "==pass", "!=pass"], ">"), + outArray = op.outArray("Array result"), + outArrayLength = op.outNumber("Array length"); + +op.toWorkPortsNeedToBeLinked(inArray_1, inArray_0); + +let logicFunc; +// cache for errors +let showingError = false; + +// create array to store multiplied result from both arrays +let mathArray = []; +let selectIndex = 0; + +mathSelect.onChange = onFilterChange; + +inArray_0.onChange = inArray_1.onChange = +inValueIfTrue.onChange = inValueIfFalse.onChange = update; + +onFilterChange(); +function onFilterChange() +{ + let mathSelectValue = mathSelect.get(); + if (mathSelectValue === ">") logicFunc = function (val, comp, t, f) { if (val > comp) return t; return f; }; + else if (mathSelectValue === "<") logicFunc = function (val, comp, t, f) { if (val < comp) return t; return f; }; + else if (mathSelectValue === ">=") logicFunc = function (val, comp, t, f) { if (val >= comp) return t; return f; }; + else if (mathSelectValue === "<=") logicFunc = function (val, comp, t, f) { if (val <= comp) return t; return f; }; + else if (mathSelectValue === "==") logicFunc = function (val, comp, t, f) { if (val === comp) return t; return f; }; + else if (mathSelectValue === "!=") logicFunc = function (val, comp, t, f) { if (val !== comp) return t; return f; }; + else if (mathSelectValue === ">pass") logicFunc = function (val, comp, t, f) { if (val > comp) return val; return f; }; + else if (mathSelectValue === "=pass") logicFunc = function (val, comp, t, f) { if (val >= comp) return val; return f; }; + else if (mathSelectValue === "<=pass") logicFunc = function (val, comp, t, f) { if (val <= comp) return val; return f; }; + else if (mathSelectValue === "==pass") logicFunc = function (val, comp, t, f) { if (val === comp) return val; return f; }; + else if (mathSelectValue === "!=pass") logicFunc = function (val, comp, t, f) { if (val !== comp) return val; return f; }; + + update(); + op.setUiAttrib({ "extendTitle": mathSelectValue }); +} + +function update() +{ + let array0 = inArray_0.get(); + let array1 = inArray_1.get(); + + let valueFalse = inValueIfFalse.get(); + let valueTrue = inValueIfTrue.get(); + + mathArray.length = 0; + + if (!array0 || !array1) + { + outArray.set(null); + outArrayLength.set(0); + return; + } + + if (array0.length !== array1.length) + { + outArray.set(null); + outArrayLength.set(0); + if (!showingError) + { + op.uiAttr({ "error": "Arrays do not have the same length !" }); + showingError = true; + } + return; + } + if (showingError) + { + showingError = false; + op.uiAttr({ "error": null }); + } + + mathArray.length = array0.length; + + let i = 0; + + for (i = 0; i < array0.length; i++) + { + mathArray[i] = logicFunc(array0[i], array1[i], valueTrue, valueFalse); + } + + outArray.set(null); + outArray.set(mathArray); + outArrayLength.set(mathArray.length); +} + + +}; + +Ops.Array.ArrayLogicArray.prototype = new CABLES.Op(); +CABLES.OPS["850a40f5-f623-438f-9ff8-461b1a9aeefc"]={f:Ops.Array.ArrayLogicArray,objName:"Ops.Array.ArrayLogicArray"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayLogicBetween_v2 +// +// ************************************************************** + +Ops.Array.ArrayLogicBetween_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArray = op.inArray("Array"), + inMin = op.inFloat("Min", 0), + inMax = op.inFloat("Max", 0.5), + inPass = op.inBool("pass value when true", false), + outArray = op.outArray("Result"); + +inArray.onChange = +inMin.onChange = +inMax.onChange = +inPass.onChange = +function update() +{ + let arr = inArray.get(); + if (!arr) return; + + let pass = inPass.get(); + const min = inMin.get(); + const max = inMax.get(); + + let newArr = []; + newArr.length = arr.length; + + if (!pass) + { + for (var i = 0; i < arr.length; i++) + { + if (arr[i] > min && arr[i] < max) + { + newArr[i] = 1.0; + } + else newArr[i] = 0.0; + } + } + else + { + for (var i = 0; i < arr.length; i++) + { + if (arr[i] > min && arr[i] < max) + { + newArr[i] = arr[i]; + } + else newArr[i] = 0.0; + } + } + outArray.set(null); + outArray.set(newArr); +}; + + +}; + +Ops.Array.ArrayLogicBetween_v2.prototype = new CABLES.Op(); +CABLES.OPS["c109a5d0-3338-4d7b-9dac-7c948d4663b8"]={f:Ops.Array.ArrayLogicBetween_v2,objName:"Ops.Array.ArrayLogicBetween_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayMath +// +// ************************************************************** + +Ops.Array.ArrayMath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray_0 = op.inArray("array 0"), + NumberIn = op.inValueFloat("Number for math", 0.0), + mathSelect = op.inSwitch("Math function", ["+", "-", "*", "/", "%", "min", "max"], "+"), + outArray = op.outArray("Array result"), + outArrayLength = op.outNumber("Array length"); + +let mathFunc; + +let showingError = false; + +let mathArray = []; + +inArray_0.onChange = NumberIn.onChange = update; +mathSelect.onChange = onFilterChange; + +onFilterChange(); + +inArray_0.onLinkChanged = () => +{ + if (inArray_0) inArray_0.copyLinkedUiAttrib("stride", outArray); +}; + +function onFilterChange() +{ + let mathSelectValue = mathSelect.get(); + + if (mathSelectValue === "+") mathFunc = function (a, b) { return a + b; }; + else if (mathSelectValue === "-") mathFunc = function (a, b) { return a - b; }; + else if (mathSelectValue === "*") mathFunc = function (a, b) { return a * b; }; + else if (mathSelectValue === "/") mathFunc = function (a, b) { return a / b; }; + else if (mathSelectValue === "%") mathFunc = function (a, b) { return a % b; }; + else if (mathSelectValue === "min") mathFunc = function (a, b) { return Math.min(a, b); }; + else if (mathSelectValue === "max") mathFunc = function (a, b) { return Math.max(a, b); }; + update(); + op.setUiAttrib({ "extendTitle": mathSelectValue }); +} + +function update() +{ + let array0 = inArray_0.get(); + + mathArray.length = 0; + + if (!array0) + { + outArrayLength.set(0); + outArray.set(null); + return; + } + + let num = NumberIn.get(); + mathArray.length = array0.length; + + let i = 0; + + for (i = 0; i < array0.length; i++) + { + mathArray[i] = mathFunc(array0[i], num); + } + + outArray.set(null); + outArray.set(mathArray); + outArrayLength.set(mathArray.length); +} + + +}; + +Ops.Array.ArrayMath.prototype = new CABLES.Op(); +CABLES.OPS["c7617717-3114-452f-9625-e4fefd841e88"]={f:Ops.Array.ArrayMath,objName:"Ops.Array.ArrayMath"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayMathArray +// +// ************************************************************** + +Ops.Array.ArrayMathArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray_0 = op.inArray("array 0"), + inArray_1 = op.inArray("array 1"), + mathSelect = op.inSwitch("Math function", ["+", "-", "*", "/", "%", "min", "max"], "+"), + outArray = op.outArray("Array result"), + outArrayLength = op.outNumber("Array length"); + +let mathFunc; + +let showingError = false; + +const mathArray = []; + +op.toWorkPortsNeedToBeLinked(inArray_1, inArray_0); + +mathSelect.onChange = onFilterChange; + +inArray_0.onChange = inArray_1.onChange = update; +onFilterChange(); + +function onFilterChange() +{ + const mathSelectValue = mathSelect.get(); + + if (mathSelectValue === "+") mathFunc = function (a, b) { return a + b; }; + else if (mathSelectValue === "-") mathFunc = function (a, b) { return a - b; }; + else if (mathSelectValue === "*") mathFunc = function (a, b) { return a * b; }; + else if (mathSelectValue === "/") mathFunc = function (a, b) { return a / b; }; + else if (mathSelectValue === "%") mathFunc = function (a, b) { return a % b; }; + else if (mathSelectValue === "min") mathFunc = function (a, b) { return Math.min(a, b); }; + else if (mathSelectValue === "max") mathFunc = function (a, b) { return Math.max(a, b); }; + update(); + op.setUiAttrib({ "extendTitle": mathSelectValue }); +} + +function update() +{ + const array0 = inArray_0.get(); + const array1 = inArray_1.get(); + + if (!array0 || !array1) + { + outArray.set(null); + outArrayLength.set(0); + return; + } + + const l = mathArray.length = array0.length; + + for (let i = 0; i < l; i++) + { + mathArray[i] = mathFunc(array0[i], array1[i]); + } + + outArray.set(null); + outArrayLength.set(mathArray.length); + outArray.set(mathArray); +} + + +}; + +Ops.Array.ArrayMathArray.prototype = new CABLES.Op(); +CABLES.OPS["f31a1764-ce14-41de-9b3f-dc2fe249bb52"]={f:Ops.Array.ArrayMathArray,objName:"Ops.Array.ArrayMathArray"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayMathExpression +// +// ************************************************************** + +Ops.Array.ArrayMathExpression = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inA = op.inArray("A"), + inB = op.inArray("B"), + inC = op.inArray("C"), + + inX = op.inFloat("X", 1), + inY = op.inFloat("Y", 1), + inZ = op.inFloat("Z", 1), + + inExpression = op.inString("Expression", "a*(b+c+d)"), + + outResultArray = op.outArray("Result Array"), + outLength = op.outNumber("Array Length"), + outExpressionIsValid = op.outBool("Expression Valid"); + +op.setPortGroup("Expression", [inExpression]); +op.setPortGroup("Parameters", [inA, inB, inC, inX, inY, inZ]); + +const functionChanged = false; +const inputsChanged = false; +const resultArray = []; +let currentFunction = inExpression.get(); +let functionValid = false; +let showingError = false; + +inA.onChange = inB.onChange = inC.onChange + = inX.onChange = inY.onChange = inZ.onChange = evaluateFunction; +inExpression.onChange = createFunction; + +function createFunction() +{ + try + { + currentFunction = new Function("m", "a", "b", "c", "x", "y", "z", "i", "len", `with(m) { return ${inExpression.get()} }`); + functionValid = true; + evaluateFunction(); + + outExpressionIsValid.set(functionValid); + } + catch (e) + { + functionValid = false; + + outExpressionIsValid.set(functionValid); + outResultArray.set(null); + + if (e instanceof ReferenceError || e instanceof SyntaxError) return; + } +} + +function evaluateFunction() +{ + const arrayA = inA.get(); + const arrayB = inB.get(); + const arrayC = inC.get(); + const arrays = [arrayA, arrayB, arrayC]; + + const x = inX.get(); + const y = inY.get(); + const z = inZ.get(); + + // * check if we have at least 2 arrays that are valid + if (arrays.filter(Boolean).length === 0) + { + outResultArray.set(null); + outLength.set(0); + outResultArray.set(null); + return; + } + else + { + const arrayLengths = arrays.map((arr) => (arr ? arr.length : undefined)); + const firstValidArrayLength = arrayLengths.find(Boolean); + const sameLength = arrayLengths.filter(Boolean).every((length) => length === firstValidArrayLength); + + let validArrays = []; + + if (sameLength) + { + op.setUiError("notsamelength", null); + const firstValidArray = arrays.find(Boolean); + validArrays = arrays.map((arr, index) => + { + // * map all undefined arrays to 0 values + if (!arr) arr = arrays.find(Boolean).map((x) => 0); + + return arr; + }); + + resultArray.length = firstValidArray.length; + + if (functionValid) + { + for (let i = 0; i < firstValidArray.length; i += 1) + { + resultArray[i] = currentFunction( + Math, + validArrays[0][i], validArrays[1][i], validArrays[2][i], + x, y, z, + i, + resultArray.length + ); + } + + outResultArray.set(null); + outResultArray.set(resultArray); + outLength.set(resultArray.length); + } + } + else + { + outResultArray.set(null); + outLength.set(0); + op.setUiError("notsamelength", "Arrays do not have the same length!", 2); + showingError = true; + } + } + + outExpressionIsValid.set(functionValid); +} + + +}; + +Ops.Array.ArrayMathExpression.prototype = new CABLES.Op(); +CABLES.OPS["663e32a2-45a1-4141-a387-d8a44d4977d5"]={f:Ops.Array.ArrayMathExpression,objName:"Ops.Array.ArrayMathExpression"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayMathExpressionTrigger +// +// ************************************************************** + +Ops.Array.ArrayMathExpressionTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTrigger = op.inTrigger("Update"), + inA = op.inArray("A"), + inB = op.inArray("B"), + inC = op.inArray("C"), + + inX = op.inFloat("X", 1), + inY = op.inFloat("Y", 1), + inZ = op.inFloat("Z", 1), + + inExpression = op.inString("Expression", "a*(b+c+d)"), + + outNext = op.outTrigger("Next"), + + outResultArray = op.outArray("Result Array"), + outLength = op.outNumber("Array Length"), + outExpressionIsValid = op.outBool("Expression Valid"); + +op.setPortGroup("Expression", [inExpression]); +op.setPortGroup("Parameters", [inA, inB, inC, inX, inY, inZ]); + +const functionChanged = false; +const inputsChanged = false; +const resultArray = []; +let currentFunction = inExpression.get(); +let functionValid = false; +let showingError = false; +let needsupdate = true; + +inA.onChange = inB.onChange = inC.onChange + = inX.onChange = inY.onChange = inZ.onChange = () => { needsupdate = true; }; + +inTrigger.onTriggered = evaluateFunction; +inExpression.onChange = createFunction; + +function createFunction() +{ + try + { + currentFunction = new Function("m", "a", "b", "c", "x", "y", "z", "i", "len", `with(m) { return ${inExpression.get()} }`); + functionValid = true; + evaluateFunction(); + + outExpressionIsValid.set(functionValid); + } + catch (e) + { + functionValid = false; + + outExpressionIsValid.set(functionValid); + outResultArray.set(null); + + if (e instanceof ReferenceError || e instanceof SyntaxError) return; + } +} + +function evaluateFunction() +{ + if (needsupdate) + { + const arrayA = inA.get(); + const arrayB = inB.get(); + const arrayC = inC.get(); + const arrays = [arrayA, arrayB, arrayC]; + + const x = inX.get(); + const y = inY.get(); + const z = inZ.get(); + + // * check if we have at least 2 arrays that are valid + if (arrays.filter(Boolean).length === 0) + { + outResultArray.set(null); + outLength.set(0); + outResultArray.set(null); + return; + } + else + { + const arrayLengths = arrays.map((arr) => (arr ? arr.length : undefined)); + const firstValidArrayLength = arrayLengths.find(Boolean); + const sameLength = arrayLengths.filter(Boolean).every((length) => length === firstValidArrayLength); + + let validArrays = []; + + if (sameLength) + { + op.setUiError("notsamelength", null); + const firstValidArray = arrays.find(Boolean); + validArrays = arrays.map((arr, index) => + { + // * map all undefined arrays to 0 values + if (!arr) arr = arrays.find(Boolean).map((x) => 0); + + return arr; + }); + + resultArray.length = firstValidArray.length; + + if (functionValid) + { + for (let i = 0; i < firstValidArray.length; i += 1) + { + resultArray[i] = currentFunction( + Math, + validArrays[0][i], validArrays[1][i], validArrays[2][i], + x, y, z, + i, + resultArray.length + ); + } + + outResultArray.set(null); + outResultArray.set(resultArray); + outLength.set(resultArray.length); + } + } + else + { + outResultArray.set(null); + outLength.set(0); + op.setUiError("notsamelength", "Arrays do not have the same length!", 2); + showingError = true; + } + } + + outExpressionIsValid.set(functionValid); + needsupdate = false; + } + outNext.trigger(); +} + + +}; + +Ops.Array.ArrayMathExpressionTrigger.prototype = new CABLES.Op(); +CABLES.OPS["e0d5da32-8463-4dfb-89f5-0d755167cd49"]={f:Ops.Array.ArrayMathExpressionTrigger,objName:"Ops.Array.ArrayMathExpressionTrigger"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayMax +// +// ************************************************************** + +Ops.Array.ArrayMax = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray = op.inArray("Array In"); +const inValue = op.inValue("Value", 1.0); +const outArray = op.outArray("Array Out"); + +let newArr = []; +outArray.set(newArr); + +inValue.onChange = inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + let inMax = inValue.get(); + + if (newArr.length != arr.length)newArr.length = arr.length; + + let i = 0; + for (i = 0; i < arr.length; i++) + { + newArr[i] = Math.max(arr[i], inMax); + } + outArray.set(null); + outArray.set(newArr); +}; + + +}; + +Ops.Array.ArrayMax.prototype = new CABLES.Op(); +CABLES.OPS["3bf1ead6-e8d2-43dd-a631-93f7a15fc270"]={f:Ops.Array.ArrayMax,objName:"Ops.Array.ArrayMax"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayMergeTrigger +// +// ************************************************************** + +Ops.Array.ArrayMergeTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Merge"), + next = op.outTrigger("Next"); + +const numArrays = 12; +const inArrs = []; +let needsUpdate = true; + +for (let i = 0; i < numArrays; i++) +{ + inArrs[i] = op.inArray("Array " + i); + inArrs[i].onChange = () => + { + needsUpdate = true; + }; +} + +const + outArr = op.outArray("Result"), + outArrayLength = op.outNumber("Array length"); + +let arr = []; + +exec.onTriggered = () => +{ + if (needsUpdate) + { + arr.length = 0; + + for (let i = 0; i < numArrays; i++) + { + const ar = inArrs[i].get(); + if (ar)arr = arr.concat(ar); + } + + outArr.set(null); + outArr.set(arr); + outArrayLength.set(arr.length); + } + + next.trigger(); +}; + + +}; + +Ops.Array.ArrayMergeTrigger.prototype = new CABLES.Op(); +CABLES.OPS["f45fdd5e-b20a-4553-a83b-57eed8a98c80"]={f:Ops.Array.ArrayMergeTrigger,objName:"Ops.Array.ArrayMergeTrigger"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayMerge_v3 +// +// ************************************************************** + +Ops.Array.ArrayMerge_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const numArrays = 8; +const inArrs = []; + +for (let i = 0; i < numArrays; i++) +{ + inArrs[i] = op.inArray("Array " + i); + inArrs[i].onChange = function () + { + update(); + }; +} + +const + outArr = op.outArray("Result"), + outArrayLength = op.outNumber("Array length"); + +let arr = []; + +function update() +{ + arr.length = 0; + + for (let i = 0; i < numArrays; i++) + { + const ar = inArrs[i].get(); + if (ar)arr = arr.concat(ar); + } + + outArr.set(null); + outArr.set(arr); + outArrayLength.set(arr.length); +} + + +}; + +Ops.Array.ArrayMerge_v3.prototype = new CABLES.Op(); +CABLES.OPS["753d053a-04a3-44c7-abf0-ae2676ced13e"]={f:Ops.Array.ArrayMerge_v3,objName:"Ops.Array.ArrayMerge_v3"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayMin +// +// ************************************************************** + +Ops.Array.ArrayMin = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray = op.inArray("Array In"); +const inValue = op.inValue("Value", 1.0); +const outArray = op.outArray("Array Out"); + +let newArr = []; +outArray.set(newArr); + +inValue.onChange = inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + let inMin = inValue.get(); + + if (newArr.length != arr.length)newArr.length = arr.length; + + let i = 0; + for (i = 0; i < arr.length; i++) + { + newArr[i] = Math.min(arr[i], inMin); + } + outArray.set(null); + outArray.set(newArr); +}; + + +}; + +Ops.Array.ArrayMin.prototype = new CABLES.Op(); +CABLES.OPS["fbb78acc-c004-4669-a277-98c47eb59598"]={f:Ops.Array.ArrayMin,objName:"Ops.Array.ArrayMin"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayModulo +// +// ************************************************************** + +Ops.Array.ArrayModulo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArray = op.inArray("Array In"), + inValue = op.inValue("Value", 2.0), + outArray = op.outArray("Array Out"); + +let newArr = []; +outArray.set(newArr); +inArray.onChange = +inValue.onChange = inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + let modulo = inValue.get(); + + if (newArr.length != arr.length) newArr.length = arr.length; + + let i = 0; + for (i = 0; i < arr.length; i++) + { + newArr[i] = arr[i] % modulo; + } + outArray.set(null); + outArray.set(newArr); +}; + + +}; + +Ops.Array.ArrayModulo.prototype = new CABLES.Op(); +CABLES.OPS["30a5568b-31da-4504-9525-578ee404993c"]={f:Ops.Array.ArrayModulo,objName:"Ops.Array.ArrayModulo"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayMultiply +// +// ************************************************************** + +Ops.Array.ArrayMultiply = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArray = op.inArray("In"), + inValue = op.inValue("Value", 1.0), + outArray = op.outArray("Result"); + +let newArr = []; +outArray.set(newArr); +inArray.onChange = +inValue.onChange = inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + let mul = inValue.get(); + + if (newArr.length != arr.length)newArr.length = arr.length; + + for (let i = 0; i < arr.length; i++) + { + newArr[i] = arr[i] * mul; + } + + outArray.set(null); + outArray.set(newArr); +}; + +inArray.onLinkChanged = () => +{ + if (inArray) inArray.copyLinkedUiAttrib("stride", outArray); +}; + + +}; + +Ops.Array.ArrayMultiply.prototype = new CABLES.Op(); +CABLES.OPS["a01c344b-4129-4b01-9c8f-36cefe86d7cc"]={f:Ops.Array.ArrayMultiply,objName:"Ops.Array.ArrayMultiply"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayOfArrays +// +// ************************************************************** + +Ops.Array.ArrayOfArrays = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +const + inExec = op.inTriggerButton("Update"), + outArr = op.outArray("Result"); + +inExec.onTriggered = update; +const arrayPorts = []; +const finalArray = []; + +for (let i = 0; i < 10; i++) +{ + arrayPorts.push(op.inArray("Array " + i)); +} + + +function update() +{ + let count = 0; + for (let i = 0; i < arrayPorts.length; i++) if (arrayPorts[i].get()) + { + finalArray[count] = arrayPorts[i].get(); + count++; + } + + finalArray.length = count; + + outArr.set(null); + outArr.set(finalArray); +} + + +}; + +Ops.Array.ArrayOfArrays.prototype = new CABLES.Op(); +CABLES.OPS["7a7b84c7-39e9-4e00-84cc-a8d7049d8247"]={f:Ops.Array.ArrayOfArrays,objName:"Ops.Array.ArrayOfArrays"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayOfObjectsFilterByKeyValue +// +// ************************************************************** + +Ops.Array.ArrayOfObjectsFilterByKeyValue = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray = op.inArray("Array"); +const inKeyToFilterBy = op.inString("Filter Key", ""); +const inKeyShouldEqual = op.inString("Filter Value", ""); +const inInvertEquality = op.inBool("Invert Filter", false); +const outArray = op.outArray("arrayOut"); + +const COMPARATOR_FUNC = (obj, comparator, key) => { return obj[key] == comparator; }; +const INV_COMPARATOR_FUNC = (obj, comparator, key) => { return (obj[key] != comparator); }; + +inArray.onChange = inInvertEquality.onChange += inKeyToFilterBy.onChange = inKeyShouldEqual.onChange = function () { + const inValue = inArray.get(); + if (!inValue) { + return; + } + + if (Array.isArray(inValue)) { + const key = inKeyToFilterBy.get(); + + if (!inKeyShouldEqual.get()) { + outArray.set(null); + outArray.set(inValue); + return; // no filter value given + } + + const comparatorFunction = !inInvertEquality.get() ? + COMPARATOR_FUNC : INV_COMPARATOR_FUNC; + + const filteredArray = []; + const keyToEqual = inKeyShouldEqual.get(); + for (let i = 0, len = inValue.length; i < len; i += 1) { + const obj = inValue[i]; + if (comparatorFunction(obj, keyToEqual, key)) { + filteredArray.push(obj); + } + } + + outArray.set(null); + outArray.set(filteredArray); + } + else + { + outArray.set(null); + } +}; + + +}; + +Ops.Array.ArrayOfObjectsFilterByKeyValue.prototype = new CABLES.Op(); +CABLES.OPS["0d4b9bba-2c00-49f7-b486-86e4cde2428e"]={f:Ops.Array.ArrayOfObjectsFilterByKeyValue,objName:"Ops.Array.ArrayOfObjectsFilterByKeyValue"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayOfObjectsFilterKeys +// +// ************************************************************** + +Ops.Array.ArrayOfObjectsFilterKeys = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray = op.inArray("Array"); +const inKeysToKeep = op.inStringEditor("Keys",'1,2,3'); +const inSeperator = op.inString("Seperator", ","); +const inInvert = op.inBool("Invert Filter", false); +const outArray = op.outArray("Array Out"); + +const COMPARATOR_FUNC = (arr, key) => arr.includes(key); +const INV_COMPARATOR_FUNC = (arr, key) => !(arr.includes(key)); + +inArray.onChange = inKeysToKeep.onChange = inInvert.onChange = inSeperator.onChange = +function () { + if (!inKeysToKeep.get()) return; + const keys = inKeysToKeep.get().split(inSeperator.get()); + const inValue = inArray.get(); + + if (Array.isArray(inValue)) { + const newArray = []; + const comparatorFunc = !inInvert.get() ? COMPARATOR_FUNC : INV_COMPARATOR_FUNC + + for (let i = 0, len = inValue.length; i < len; i += 1) { + const obj = inValue[i]; + const objKeys = Object.keys(obj); + const newObj = {}; + + for (let j = 0, len2 = objKeys.length; j < len2; j += 1) { + const key = objKeys[j]; + if (comparatorFunc(keys, key)) + Object.assign(newObj, { [key]: obj[key] }); + } + + newArray.push(newObj); + } + + outArray.set(null); + outArray.set(newArray); + } + else { + outArray.set(null); + } +}; + + +}; + +Ops.Array.ArrayOfObjectsFilterKeys.prototype = new CABLES.Op(); +CABLES.OPS["8c9a7621-51c7-46b7-934b-1924cb0ff0b6"]={f:Ops.Array.ArrayOfObjectsFilterKeys,objName:"Ops.Array.ArrayOfObjectsFilterKeys"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayOfObjectsToString +// +// ************************************************************** + +Ops.Array.ArrayOfObjectsToString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray = op.inArray("Array In"); +const outString = op.outString("String"); + +inArray.onChange = function() { + if (!inArray.get()) { + outString.set(""); + return; + } + + const arr = inArray.get(); + let result = ""; + + for (let i = 0; i < arr.length; i += 1) { + const objToString = JSON.stringify(arr[i]); + result += "\n" + objToString; + } + + outString.set(result); +} + +}; + +Ops.Array.ArrayOfObjectsToString.prototype = new CABLES.Op(); +CABLES.OPS["1593cd67-2a90-43ab-b95e-ad6bbe9af37e"]={f:Ops.Array.ArrayOfObjectsToString,objName:"Ops.Array.ArrayOfObjectsToString"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayPack +// +// ************************************************************** + +Ops.Array.ArrayPack = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const outArr = op.outArray("Result"); + +const NUM_PORTS = 8; +const inArrPorts = []; + +let showingError = false; + +for (let i = 0; i < NUM_PORTS; i++) +{ + let p = op.inArray("Array " + i); + p.onChange = update; + inArrPorts.push(p); +} + +function update() +{ + const arr = []; + const inArrays = []; + let i = 0; + + for (i = 0; i < NUM_PORTS; i++) + { + let a = inArrPorts[i].get(); + if (a) + { + inArrays.push(a); + if (a.length != inArrays[0].length) + { + if (!showingError)op.setUiError("arraylen", "Arrays do not have the same length !"); + outArr.set(null); + showingError = true; + return; + } + } + } + + if (inArrays.length === 0) + { + if (!showingError)op.setUiError("invalid", "No Valid Arrays"); + // op.uiAttr({ "error": "No Valid Arrays" }); + outArr.set(null); + showingError = true; + return; + } + + if (showingError) + { + op.setUiError("arraylen", null); + op.setUiError("invalid", null); + } + showingError = false; + + for (let j = 0; j < inArrays[0].length; j++) + for (i = 0; i < inArrays.length; i++) + arr.push(inArrays[i][j]); + + outArr.set(null); + outArr.set(arr); +} + + +}; + +Ops.Array.ArrayPack.prototype = new CABLES.Op(); +CABLES.OPS["51df396d-87be-4890-8fbd-c8bb3d364d3b"]={f:Ops.Array.ArrayPack,objName:"Ops.Array.ArrayPack"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayPack2 +// +// ************************************************************** + +Ops.Array.ArrayPack2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTriggerButton("Trigger in"), + inArr1 = op.inArray("Array 1"), + inArr2 = op.inArray("Array 2"), + exeOut = op.outTrigger("Trigger out"), + outArr = op.outArray("Array out", null, 2), + outArrayLength = op.outNumber("Array length"); + +let showingError = false; + +let arr = []; +let emptyArray = []; +let needsCalc = true; + +exe.onTriggered = update; + +inArr1.onChange = inArr2.onChange = calcLater; +function calcLater() +{ + needsCalc = true; +} + +function update() +{ + let array1 = inArr1.get(); + let array2 = inArr2.get(); + + if (!array1 && !array2) + { + outArr.set(null); + return; + } + if (needsCalc) + { + let arrlen = 0; + + if (!array1 || !array2) + { + if (array1) arrlen = array1.length; + else if (array2) arrlen = array2.length; + + if (emptyArray.length != arrlen) + for (var i = 0; i < arrlen; i++) emptyArray[i] = 0; + + if (!array1)array1 = emptyArray; + if (!array2)array2 = emptyArray; + } + + if (array1.length !== array2.length) + { + if (!showingError) + { + op.uiAttr({ "error": "Arrays do not have the same length !" }); + showingError = true; + } + return; + } + + if (showingError) + { + showingError = false; + op.uiAttr({ "error": null }); + } + + arr.length = array1.length; + for (var i = 0; i < array1.length; i++) + { + arr[i * 2 + 0] = array1[i]; + arr[i * 2 + 1] = array2[i]; + } + + needsCalc = false; + outArr.set(null); + outArr.set(arr); + outArrayLength.set(arr.length); + } + + exeOut.trigger(); +} + + +}; + +Ops.Array.ArrayPack2.prototype = new CABLES.Op(); +CABLES.OPS["0db296db-e4a7-4356-9593-858f7e1bc7f3"]={f:Ops.Array.ArrayPack2,objName:"Ops.Array.ArrayPack2"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayPack3 +// +// ************************************************************** + +Ops.Array.ArrayPack3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTrigger("Trigger in"), + inArr1 = op.inArray("Array 1"), + inArr2 = op.inArray("Array 2"), + inArr3 = op.inArray("Array 3"), + exeOut = op.outTrigger("Trigger out"), + outArr = op.outArray("Array out", 3), + outNum = op.outNumber("Num Points"), + outArrayLength = op.outNumber("Array length"); + +let showingError = false; + +let arr = []; +let emptyArray = []; +let needsCalc = true; + +exe.onTriggered = update; + +inArr1.onChange = inArr2.onChange = inArr3.onChange = calcLater; + +function calcLater() +{ + needsCalc = true; +} + +function update() +{ + let array1 = inArr1.get(); + let array2 = inArr2.get(); + let array3 = inArr3.get(); + + if (!array1 && !array2 && !array3) + { + outArr.set(null); + outNum.set(0); + return; + } + // only update if array has changed + if (needsCalc) + { + let arrlen = 0; + + if (!array1 || !array2 || !array3) + { + if (array1) arrlen = array1.length; + else if (array2) arrlen = array2.length; + else if (array3) arrlen = array3.length; + + if (emptyArray.length != arrlen) + for (var i = 0; i < arrlen; i++) emptyArray[i] = 0; + + if (!array1)array1 = emptyArray; + if (!array2)array2 = emptyArray; + if (!array3)array3 = emptyArray; + } + + if ((array1.length !== array2.length) || (array2.length !== array3.length)) + { + op.setUiError("arraylen", "Arrays do not have the same length !"); + return; + } + op.setUiError("arraylen", null); + + arr.length = array1.length; + for (var i = 0; i < array1.length; i++) + { + arr[i * 3 + 0] = array1[i]; + arr[i * 3 + 1] = array2[i]; + arr[i * 3 + 2] = array3[i]; + } + + needsCalc = false; + outArr.set(null); + outArr.set(arr); + outNum.set(arr.length / 3); + outArrayLength.set(arr.length); + } + + exeOut.trigger(); +} + + +}; + +Ops.Array.ArrayPack3.prototype = new CABLES.Op(); +CABLES.OPS["2bcf32fe-3cbd-48fd-825a-61255bebda9b"]={f:Ops.Array.ArrayPack3,objName:"Ops.Array.ArrayPack3"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayPack3Simple +// +// ************************************************************** + +Ops.Array.ArrayPack3Simple = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr1 = op.inArray("Array 1"), + inArr2 = op.inArray("Array 2"), + inArr3 = op.inArray("Array 3"), + + outArr = op.outArray("Array out", 3), + outNum = op.outNumber("Num Points"), + outArrayLength = op.outNumber("Array length"); + +let showingError = false; + +let arr = []; +let emptyArray = []; +let needsCalc = true; + +inArr1.onChange = inArr2.onChange = inArr3.onChange = update; + +function update() +{ + let array1 = inArr1.get(); + let array2 = inArr2.get(); + let array3 = inArr3.get(); + + if (!array1 && !array2 && !array3) + { + outArr.set(null); + outNum.set(0); + return; + } + let arrlen = 0; + + if (!array1 || !array2 || !array3) + { + if (array1) arrlen = array1.length; + else if (array2) arrlen = array2.length; + else if (array3) arrlen = array3.length; + + if (emptyArray.length != arrlen) + for (var i = 0; i < arrlen; i++) emptyArray[i] = 0; + + if (!array1)array1 = emptyArray; + if (!array2)array2 = emptyArray; + if (!array3)array3 = emptyArray; + } + + if ((array1.length !== array2.length) || (array2.length !== array3.length)) + { + // + op.setUiError("arraylen", "Arrays do not have the same length !"); + return; + } + op.setUiError("arraylen", null); + + arr.length = array1.length; + for (var i = 0; i < array1.length; i++) + { + arr[i * 3 + 0] = array1[i]; + arr[i * 3 + 1] = array2[i]; + arr[i * 3 + 2] = array3[i]; + } + + needsCalc = false; + outArr.set(null); + outArr.set(arr); + outNum.set(arr.length / 3); + outArrayLength.set(arr.length); +} + + +}; + +Ops.Array.ArrayPack3Simple.prototype = new CABLES.Op(); +CABLES.OPS["9c48785b-4cac-472c-a70f-dbd3c240b782"]={f:Ops.Array.ArrayPack3Simple,objName:"Ops.Array.ArrayPack3Simple"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayPack4 +// +// ************************************************************** + +Ops.Array.ArrayPack4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTrigger("Trigger in"), + inArr1 = op.inArray("Array 1"), + inArr2 = op.inArray("Array 2"), + inArr3 = op.inArray("Array 3"), + inArr4 = op.inArray("Array 4"), + outTrigger = op.outTrigger("Trigger out"), + outArr = op.outArray("Array out", 4), + outArrayLength = op.outNumber("Array length"); + +let showingError = false; +let arr = []; +let emptyArray = []; +let needsCalc = true; + +exe.onTriggered = update; + +inArr1.onChange = inArr2.onChange = inArr3.onChange = inArr4.onChange = calcLater; +function calcLater() +{ + needsCalc = true; +} + +function update() +{ + outTrigger.trigger(); + let array1 = inArr1.get(); + let array2 = inArr2.get(); + let array3 = inArr3.get(); + let array4 = inArr4.get(); + + if (!array1 && !array2 && !array3 && !array4) + { + outArr.set(null); + return; + } + + if (needsCalc) + { + let arrlen = 0; + + if (!array1 || !array2 || !array3 || !array4) + { + if (array1) arrlen = array1.length; + else if (array2) arrlen = array2.length; + else if (array3) arrlen = array3.length; + else if (array4) arrlen = array4.length; + + if (emptyArray.length != arrlen) + for (var i = 0; i < arrlen; i++) emptyArray[i] = 0; + + if (!array1)array1 = emptyArray; + if (!array2)array2 = emptyArray; + if (!array3)array3 = emptyArray; + if (!array4)array4 = emptyArray; + } + + if ((array1.length !== array2.length) || (array2.length !== array3.length) + || (array3.length !== array4.length)) + { + if (!showingError) + { + op.setUiError("arrlength", "Arrays do not have the same length !"); + outArrayLength.set(0); + showingError = true; + } + return; + } + + if (showingError) + { + showingError = false; + op.setUiError("arrlength", null); + } + + arr.length = array1.length; + + for (var i = 0; i < array1.length; i++) + { + arr[i * 4 + 0] = array1[i]; + arr[i * 4 + 1] = array2[i]; + arr[i * 4 + 2] = array3[i]; + arr[i * 4 + 3] = array4[i]; + } + needsCalc = false; + outArr.set(null); + outArr.set(arr); + outArrayLength.set(arr.length); + } +} + + +}; + +Ops.Array.ArrayPack4.prototype = new CABLES.Op(); +CABLES.OPS["68ea2c83-05fc-47a7-aba8-3c400f1dd737"]={f:Ops.Array.ArrayPack4,objName:"Ops.Array.ArrayPack4"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayPack4Simple +// +// ************************************************************** + +Ops.Array.ArrayPack4Simple = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr1 = op.inArray("Array 1"), + inArr2 = op.inArray("Array 2"), + inArr3 = op.inArray("Array 3"), + inArr4 = op.inArray("Array 4"), + + outArr = op.outArray("Array out", 4), + outNum = op.outNumber("Num Points"), + outArrayLength = op.outNumber("Array length"); + +let showingError = false; + +let arr = []; +let emptyArray = []; +let needsCalc = true; + +inArr1.onChange = inArr2.onChange = inArr3.onChange = inArr4.onChange = update; + +function update() +{ + let array1 = inArr1.get(); + let array2 = inArr2.get(); + let array3 = inArr3.get(); + let array4 = inArr4.get(); + + if (!array1 && !array2 && !array3 && !array4) + { + outArr.set(null); + outNum.set(0); + return; + } + let arrlen = 0; + + if (!array1 || !array2 || !array3 || !array4) + { + if (array1) arrlen = array1.length; + else if (array2) arrlen = array2.length; + else if (array3) arrlen = array3.length; + else if (array4) arrlen = array4.length; + + if (emptyArray.length != arrlen) + for (let i = 0; i < arrlen; i++) emptyArray[i] = 0; + + if (!array1)array1 = emptyArray; + if (!array2)array2 = emptyArray; + if (!array3)array3 = emptyArray; + if (!array4)array4 = emptyArray; + } + + if ((array1.length !== array2.length) || (array2.length !== array3.length)) + { + // + op.setUiError("arraylen", "Arrays do not have the same length !"); + return; + } + op.setUiError("arraylen", null); + + arr.length = array1.length * 4; + for (let i = 0; i < array1.length; i++) + { + arr[i * 4 + 0] = array1[i]; + arr[i * 4 + 1] = array2[i]; + arr[i * 4 + 2] = array3[i]; + arr[i * 4 + 3] = array4[i]; + } + + needsCalc = false; + outArr.set(null); + outArr.set(arr); + outNum.set(arr.length / 4); + outArrayLength.set(arr.length); +} + + +}; + +Ops.Array.ArrayPack4Simple.prototype = new CABLES.Op(); +CABLES.OPS["6fe2bb0e-4bfc-42c1-8bc1-19fda052e091"]={f:Ops.Array.ArrayPack4Simple,objName:"Ops.Array.ArrayPack4Simple"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayPow +// +// ************************************************************** + +Ops.Array.ArrayPow = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// The pow function will not work correctly with neagtive numbers +// Use the ArrayAbs op to make numbers have only positive values +// to work correctly with this op + +let inArray = op.inArray("Array in"); +let inValue = op.inValue("Pow factor", 1.0); +let outArray = op.outArray("Array out"); + +let newArr = []; +outArray.set(newArr); + +inArray.onChange = +inValue.onChange = inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + let pow = inValue.get(); + if (pow < 0.0) + { + pow = 0.0; + } + + if (newArr.length !== arr.length)newArr.length = arr.length; + + for (let i = 0; i < arr.length; i++) + { + newArr[i] = Math.pow(arr[i], pow); + } + outArray.set(null); + outArray.set(newArr); +}; + + +}; + +Ops.Array.ArrayPow.prototype = new CABLES.Op(); +CABLES.OPS["d60b6c52-d142-41dd-a38d-d185148be33f"]={f:Ops.Array.ArrayPow,objName:"Ops.Array.ArrayPow"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayPushString +// +// ************************************************************** + +Ops.Array.ArrayPushString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr=op.inArray("Array"), + inString=op.inString("String",""), + result=op.outArray("Result"); + +const arr=[]; + + inString.onChange= + inArr.onChange= + function() + { + const oldArr=inArr.get(); + result.set(null); + if(!oldArr)return; + + arr.length=oldArr.length+1; + for(let i=0;i +{ + if (!inScale.get()) + { + outQuantized.set(inValue.get()); + outError.set(0); + return; + } + + const arr = inScale.get(); + const quantized = closest(inValue.get(), arr); + outQuantized.set(quantized); + outError.set(quantized - inValue.get()); +}; + + +}; + +Ops.Array.ArrayQuantizer.prototype = new CABLES.Op(); +CABLES.OPS["fc05b6ed-6584-4e37-aec2-fc659012ff60"]={f:Ops.Array.ArrayQuantizer,objName:"Ops.Array.ArrayQuantizer"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayRandomSelection +// +// ************************************************************** + +Ops.Array.ArrayRandomSelection = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray = op.inArray("Array"), + inNum = op.inValueInt("Elements", 10), + inSeed = op.inValue("Seed", 1), + result = op.outArray("Result"), + outArrayLength = op.outNumber("Array length"); + +let arr = []; +inSeed.onChange = inArray.onChange = inNum.onChange = update; + +function update() +{ + if (Math.floor(inNum.get()) < 0 || !inArray.get()) + { + result.set(null); + outArrayLength.set(0); + return; + } + + let oldArr = inArray.get(); + + arr.length = Math.floor(inNum.get()); + + let nums = []; + + for (var i = 0; i < Math.max(arr.length, oldArr.length); i++) + nums[i] = i % (oldArr.length); + + nums = CABLES.shuffleArray(nums); + + Math.randomSeed = inSeed.get(); + + for (var i = 0; i < inNum.get(); i++) + { + let index = nums[i]; + arr[i] = oldArr[index]; + } + result.set(null); + result.set(arr); + outArrayLength.set(arr.length); +} + + +}; + +Ops.Array.ArrayRandomSelection.prototype = new CABLES.Op(); +CABLES.OPS["3dc059c8-bcb3-4d63-b806-ce81215da3b5"]={f:Ops.Array.ArrayRandomSelection,objName:"Ops.Array.ArrayRandomSelection"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayReverse +// +// ************************************************************** + +Ops.Array.ArrayReverse = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Input"), + outArr = op.outArray("Result"); + +inArr.onChange = function () +{ + let arr = inArr.get(); + if (arr) + { + let arrCopy = arr.slice(); + outArr.set(arrCopy.reverse()); + } +}; + + +}; + +Ops.Array.ArrayReverse.prototype = new CABLES.Op(); +CABLES.OPS["88d8662f-2c01-42e6-943d-4d3cf90657b0"]={f:Ops.Array.ArrayReverse,objName:"Ops.Array.ArrayReverse"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySetArray +// +// ************************************************************** + +Ops.Array.ArraySetArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe=op.inTriggerButton("exe"), + array=op.inArray("array"), + index=op.inValueInt("index"), + value=op.inArray("new Array"), + values=op.outArray("values"); + +values.ignoreValueSerialize=true; + +function updateIndex() +{ + if(exe.isLinked())return; + update(); +} + +function copyArray(source) +{ + var dest=[]; + dest.length=source.length; + for (var i = 0, n = source.length; i < n; i++) dest[i] = source[i]; + + return dest; +} + + +function update() +{ + if(!array.get() || !value.get())return; + array.get()[index.get()]=copyArray(value.get()); + + values.set(null); + values.set(array.get()); +} + +exe.onTriggered=update; + + +}; + +Ops.Array.ArraySetArray.prototype = new CABLES.Op(); +CABLES.OPS["58ddcecb-3917-4118-81d3-344ee2b75469"]={f:Ops.Array.ArraySetArray,objName:"Ops.Array.ArraySetArray"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySetNumber3 +// +// ************************************************************** + +Ops.Array.ArraySetNumber3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTrigger = op.inTriggerButton("Execute"), + inArray = op.inArray("Array"), + inIndex = op.inInt("Index", 0), + inValueX = op.inFloat("X", 1), + inValueY = op.inFloat("Y", 1), + inValueZ = op.inFloat("Z", 1), + inReset = op.inTriggerButton("Reset"), + outArray = op.outArray("Result"); + +const newArr = []; + +inReset.onTriggered = function () +{ + copyArray(true); +}; +inArray.onChange = copyArray; + +inTrigger.onTriggered = +function () +{ + const arr = inArray.get(); + + if (!arr) return; + if (newArr.length != arr.length)newArr.length = arr.length; + + const idx = Math.floor(inIndex.get()); + + if (idx >= 0) + { + newArr[idx * 3 + 0] = inValueX.get(); + newArr[idx * 3 + 1] = inValueY.get(); + newArr[idx * 3 + 2] = inValueZ.get(); + } + + inArray.onChange = null; + outArray.set(null); + outArray.set(newArr); +}; + +function copyArray(force) +{ + const arr = inArray.get(); + + if (!arr) return; + + if (force === true || (arr && !outArray.get())) + { + if (newArr.length != arr.length) newArr.length = arr.length; + for (let i = 0; i < arr.length; i++) newArr[i] = arr[i]; + + outArray.set(null); + outArray.set(newArr); + } +} + + +}; + +Ops.Array.ArraySetNumber3.prototype = new CABLES.Op(); +CABLES.OPS["4edd6ccc-17c4-48c0-8eda-91bbc08e97db"]={f:Ops.Array.ArraySetNumber3,objName:"Ops.Array.ArraySetNumber3"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySetNumber_v2 +// +// ************************************************************** + +Ops.Array.ArraySetNumber_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTrigger = op.inTriggerButton("Execute"), + inArray = op.inArray("Array"), + inIndex = op.inInt("Index", 0), + inValue = op.inFloat("Number", 1), + outNext = op.outTrigger("Next"), + outArray = op.outArray("Result"); + +let arr = []; +inArray.onChange = () => +{ + arr = inArray.get(); +}; + +inTrigger.onTriggered = () => +{ + if (!arr) + { + outArray.set(null); + return; + } + + const newArr = []; + // if (newArr.length != arr.length) newArr.length = arr.length; + for (let i = 0; i < arr.length; i++) newArr[i] = arr[i]; + const idx = Math.floor(inIndex.get()); + + if (idx >= 0) + // if (idx >= 0 && idx < arr.length) + { + newArr[idx] = inValue.get(); + } + + arr = newArr; + outArray.set(null); + outArray.set(newArr); + outNext.trigger(); +}; + + +}; + +Ops.Array.ArraySetNumber_v2.prototype = new CABLES.Op(); +CABLES.OPS["a6366c3e-dab0-4b4c-b790-e66b82b93a25"]={f:Ops.Array.ArraySetNumber_v2,objName:"Ops.Array.ArraySetNumber_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySetObject +// +// ************************************************************** + +Ops.Array.ArraySetObject = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe=op.inTriggerButton("exe"), + array=op.inArray("array"), + index=op.inValueInt("index"), + value=op.inObject("object"), + values=op.outArray("values"); + +values.ignoreValueSerialize=true; +exe.onTriggered=update; + +function updateIndex() +{ + if(exe.isLinked())return; + update(); +} + +function update() +{ + var arr=array.get(); + if(!arr)return; + arr[index.get()]=value.get(); + + values.set(null); + values.set(arr); +} + + + +}; + +Ops.Array.ArraySetObject.prototype = new CABLES.Op(); +CABLES.OPS["5cccb9a4-3653-4c58-b54e-932c4c585b5f"]={f:Ops.Array.ArraySetObject,objName:"Ops.Array.ArraySetObject"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySetString +// +// ************************************************************** + +Ops.Array.ArraySetString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTriggerButton("Execute"), + array = op.inArray("Array"), + index = op.inValueInt("Index"), + value = op.inString("String"), + values = op.outArray("Result"); + +values.ignoreValueSerialize = true; +exe.onTriggered = update; + +function updateIndex() +{ + if (exe.isLinked()) return; + update(); +} + +function update() +{ + const arr = array.get(); + if (!arr) return; + arr[index.get()] = value.get(); + + values.set(""); + values.set(arr); +} + + +}; + +Ops.Array.ArraySetString.prototype = new CABLES.Op(); +CABLES.OPS["2752b35e-592d-41db-b8dd-cdc43a7ccbe2"]={f:Ops.Array.ArraySetString,objName:"Ops.Array.ArraySetString"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySin +// +// ************************************************************** + +Ops.Array.ArraySin = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// this op allows the user to perform sin or cos +// math functions on an array +const inArray = op.inArray("array in"); +const mathSelect = op.inValueSelect("Math function", ["Sin", "Cos"], "Sin"); +const outArray = op.outArray("Array result"); + +const phase = op.inValue("Phase", 0.0); +const multiply = op.inValue("Frequency", 1.0); +const amplitude = op.inValue("Amplitude", 1.0); + +const mathArray = []; +let selectIndex = 0; + +const MATH_FUNC_SIN = 0; +const MATH_FUNC_COS = 1; + + +inArray.onChange = update; +multiply.onChange = update; +amplitude.onChange = update; +phase.onChange = update; +mathSelect.onChange = onFilterChange; + +function onFilterChange() +{ + const mathSelectValue = mathSelect.get(); + if (mathSelectValue === "Sin") selectIndex = MATH_FUNC_SIN; + else if (mathSelectValue === "Cos") selectIndex = MATH_FUNC_COS; + update(); +} + +function update() +{ + const arrayIn = inArray.get(); + + + if (!arrayIn) + { + mathArray.length = 0; + return; + } + + mathArray.length = arrayIn.length; + + const amp = amplitude.get(); + const mul = multiply.get(); + const pha = phase.get(); + + let i = 0; + if (selectIndex === MATH_FUNC_SIN) + { + for (i = 0; i < arrayIn.length; i++) + mathArray[i] = amp * Math.sin((arrayIn[i]) * mul + pha); + } + else if (selectIndex === MATH_FUNC_COS) + { + for (i = 0; i < arrayIn.length; i++) + mathArray[i] = amp * (Math.cos(arrayIn[i] * mul + pha)); + } + outArray.set(null); + outArray.set(mathArray); +} + + +}; + +Ops.Array.ArraySin.prototype = new CABLES.Op(); +CABLES.OPS["ded44bae-a24e-48c5-9585-4cb31f331ab6"]={f:Ops.Array.ArraySin,objName:"Ops.Array.ArraySin"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySmoothStep +// +// ************************************************************** + +Ops.Array.ArraySmoothStep = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArray = op.inArray("Array In"), + inMinimum = op.inValue("Min", 0.0), + inMaximum = op.inValue("Max", 1.0), + outArray = op.outArray("Array out"); + +let newArr = []; +outArray.set(newArr); + +inArray.onChange = +inMinimum.onChange = +inMaximum.onChange = inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + let min = inMinimum.get(); + let max = inMaximum.get(); + + if (newArr.length != arr.length)newArr.length = arr.length; + + for (let i = 0; i < arr.length; i++) + { + newArr[i] = smoothstep(min, max, arr[i]); + } + outArray.set(null); + outArray.set(newArr); +}; + +function smoothstep(min, max, value) +{ + let x = Math.max(0.0, Math.min(1.0, (value - min) / (max - min))); + return x * x * (3 - 2 * x); +} + + +}; + +Ops.Array.ArraySmoothStep.prototype = new CABLES.Op(); +CABLES.OPS["eb95b563-937b-4189-a8c9-ba6776971719"]={f:Ops.Array.ArraySmoothStep,objName:"Ops.Array.ArraySmoothStep"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySqrt +// +// ************************************************************** + +Ops.Array.ArraySqrt = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArray = op.inArray("In"), + outArray = op.outArray("Result"); + +let newArr = []; +outArray.set(newArr); + +inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + if (newArr.length != arr.length)newArr.length = arr.length; + + for (let i = 0; i < arr.length; i++) + { + newArr[i] = Math.sqrt(arr[i]); + } + outArray.set(null); + outArray.set(newArr); +}; + + +}; + +Ops.Array.ArraySqrt.prototype = new CABLES.Op(); +CABLES.OPS["93f3dc25-a1e7-46f5-9069-527c1bd2c3a3"]={f:Ops.Array.ArraySqrt,objName:"Ops.Array.ArraySqrt"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySubtract +// +// ************************************************************** + +Ops.Array.ArraySubtract = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let inArray = op.inArray("Array In"); +let inValue = op.inValue("Value", 1.0); +let outArray = op.outArray("Array Out"); + +let newArr = []; +outArray.set(newArr); +inArray.onChange = +inValue.onChange = inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + let subtract = inValue.get(); + + if (newArr.length != arr.length)newArr.length = arr.length; + + let i = 0; + for (i = 0; i < arr.length; i++) + { + newArr[i] = arr[i] - subtract; + } + outArray.set(null); + outArray.set(newArr); +}; + + +}; + +Ops.Array.ArraySubtract.prototype = new CABLES.Op(); +CABLES.OPS["af78ab59-75d5-4ead-9a8d-27a63e1cbf3f"]={f:Ops.Array.ArraySubtract,objName:"Ops.Array.ArraySubtract"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySum +// +// ************************************************************** + +Ops.Array.ArraySum = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArray = op.inArray("In"), + inValue = op.inValue("Value", 1.0), + outArray = op.outArray("Result"); + +let newArr = []; +outArray.set(newArr); + +inValue.onChange = +inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + let add = inValue.get(); + + if (newArr.length != arr.length)newArr.length = arr.length; + + for (let i = 0; i < arr.length; i++) + { + newArr[i] = arr[i] + add; + } + + outArray.set(null); + outArray.set(newArr); +}; + + +}; + +Ops.Array.ArraySum.prototype = new CABLES.Op(); +CABLES.OPS["c6b5bf63-0be8-4eea-acc0-9d32973e665a"]={f:Ops.Array.ArraySum,objName:"Ops.Array.ArraySum"}; + + + + +// ************************************************************** +// +// Ops.Array.ArraySumPrevious +// +// ************************************************************** + +Ops.Array.ArraySumPrevious = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr=op.inArray("Array"), + inPad=op.inFloat("Padding",0), + outArr=op.outArray("Result"); + +const newArr=[]; + +inPad.onChange= +inArr.onChange=()=> +{ + outArr.set(null); + let arr=inArr.get(); + if(!arr || arr.length<1)return; + + newArr.length=arr.length; + + newArr[0]=arr[0]; + + for(let i=1;i +{ + if (!inArray.get()) + { + outArray.set(null); + outLength.set(0); + return; + } + + const mode = inDedupeMode.get(); + const func = FUNCTIONS[mode]; + const newArray = func(inArray.get()); + + outArray.set(null); + outArray.set(newArray); + outLength.set(newArray.length); +}; + +function compareSingle(arr) +{ + const len = arr.length; + const seen = {}; + const deduped = []; + + for (let i = 0; i < len; i++) + { + if (!seen[arr[i]]) + { + deduped.push(arr[i]); + seen[arr[i]] = true; + } + } + + return deduped; +} + +const FUNCTIONS = { + "X": compareSingle, + "XY": compareVec2, + "XYZ": compareVec3, +}; + +function compareVec2(arr) +{ + const len = Math.floor(arr.length / 2); + const seen = {}; + const deduped = []; + + for (let i = 0; i < len; i += 1) + { + const key = [arr[i * 2], arr[i * 2 + 1]].join(); + + if (!seen[key]) + { + deduped.push(arr[i * 2], arr[i * 2 + 1]); + seen[key] = true; + } + } + + return deduped; +} + +function compareVec3(arr) +{ + const len = Math.floor(arr.length / 3); + const seen = {}; + const deduped = []; + + for (let i = 0; i < len; i += 1) + { + const key = [arr[i * 3], arr[i * 3 + 1], arr[i * 3 + 2]].join(); + + if (!seen[key]) + { + deduped.push(arr[i * 3], arr[i * 3 + 1], arr[i * 3 + 2]); + seen[key] = true; + } + } + + return deduped; +} + + +/* +// OLD CODE IN CASE OF SOMETHING GOING WRONG + +const inArray = op.inArray("array"); +const outArray = op.outArray("arrayOut"); + +inArray.onChange = function () +{ + const inValue = inArray.get(); + if (Array.isArray(inValue)) + { + const unique = inValue.filter((v, i, a) => a.indexOf(v) === i); + outArray.set(unique); + } + else + { + outArray.set(inValue); + } +}; + +*/ + + +}; + +Ops.Array.ArrayUnique.prototype = new CABLES.Op(); +CABLES.OPS["81204fcc-c416-4948-9cba-2218763db4fc"]={f:Ops.Array.ArrayUnique,objName:"Ops.Array.ArrayUnique"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayUniqueItemInfo +// +// ************************************************************** + +Ops.Array.ArrayUniqueItemInfo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray = op.inArray("array"); +const outObject = op.outObject("objectOut"); + +inArray.onChange = function () +{ + const inValue = inArray.get(); + const counts = {}; + if (Array.isArray(inValue)) + { + for (let i = 0; i < inValue.length; i++) + { + const value = inValue[i]; + if (counts.hasOwnProperty(value)) + { + counts[value]++; + } + else + { + counts[value] = 1; + } + } + outObject.set(counts); + } + else + { + outObject.set(inValue); + } +}; + + +}; + +Ops.Array.ArrayUniqueItemInfo.prototype = new CABLES.Op(); +CABLES.OPS["6c6927e6-abd3-4427-92b2-4095f6f0b1c0"]={f:Ops.Array.ArrayUniqueItemInfo,objName:"Ops.Array.ArrayUniqueItemInfo"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayUnpack2 +// +// ************************************************************** + +Ops.Array.ArrayUnpack2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray1 = op.inArray("Array in xyz"), + outArray1 = op.outArray("Array 1 out"), + outArray2 = op.outArray("Array 2 out"), + outArrayLength = op.outNumber("Array lengths"); + +let showingError = false; + +const arr1 = []; +const arr2 = []; + +inArray1.onChange = update; + +function update() +{ + let array1 = inArray1.get(); + + if (!array1) + { + outArray1.set(null); + return; + } + + if (array1.length % 2 !== 0) + { + if (!showingError) + { + op.uiAttr({ "error": "Arrays length not divisible by 2 !" }); + outArrayLength.set(0); + showingError = true; + } + return; + } + if (array1.length === 0) + { + outArrayLength.set(0); + outArray1.set(null); + outArray2.set(null); + } + + if (showingError) + { + showingError = false; + op.uiAttr({ "error": null }); + } + + arr1.length = Math.floor(array1.length / 2); + arr2.length = Math.floor(array1.length / 2); + + for (let i = 0; i < array1.length / 2; i++) + { + arr1[i] = array1[i * 2]; + arr2[i] = array1[i * 2 + 1]; + } + + outArray1.set(null); + outArray2.set(null); + outArray1.set(arr1); + outArray2.set(arr2); + outArrayLength.set(arr1.length); +} + + +}; + +Ops.Array.ArrayUnpack2.prototype = new CABLES.Op(); +CABLES.OPS["3f789664-9937-4478-ba28-63ccb67e5114"]={f:Ops.Array.ArrayUnpack2,objName:"Ops.Array.ArrayUnpack2"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayUnpack3 +// +// ************************************************************** + +Ops.Array.ArrayUnpack3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray1 = op.inArray("Array in xyz"), + outArray1 = op.outArray("Array 1 out"), + outArray2 = op.outArray("Array 2 out"), + outArray3 = op.outArray("Array 3 out"), + outArrayLength = op.outNumber("Array lengths"); + +let showingError = false; + +const arr1 = []; +const arr2 = []; +const arr3 = []; + +inArray1.onChange = update; + +function update() +{ + let array1 = inArray1.get(); + + if (!array1) + { + outArray1.set(null); + return; + } + + if (array1.length % 3 !== 0) + { + if (!showingError) + { + op.uiAttr({ "error": "Arrays length not divisible by 3 !" }); + outArrayLength.set(0); + showingError = true; + } + return; + } + + if (showingError) + { + showingError = false; + op.uiAttr({ "error": null }); + } + + arr1.length = Math.floor(array1.length / 3); + arr2.length = Math.floor(array1.length / 3); + arr3.length = Math.floor(array1.length / 3); + + for (let i = 0; i < array1.length / 3; i++) + { + arr1[i] = array1[i * 3]; + arr2[i] = array1[i * 3 + 1]; + arr3[i] = array1[i * 3 + 2]; + } + + outArray1.set(null); + outArray2.set(null); + outArray3.set(null); + outArray1.set(arr1); + outArray2.set(arr2); + outArray3.set(arr3); + outArrayLength.set(arr1.length); +} + + +}; + +Ops.Array.ArrayUnpack3.prototype = new CABLES.Op(); +CABLES.OPS["fa671f66-6957-41e6-ac35-d615b7c29285"]={f:Ops.Array.ArrayUnpack3,objName:"Ops.Array.ArrayUnpack3"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayUnpack4 +// +// ************************************************************** + +Ops.Array.ArrayUnpack4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArray1 = op.inArray("Array in xyzw"), + outArray1 = op.outArray("Array 1 out"), + outArray2 = op.outArray("Array 2 out"), + outArray3 = op.outArray("Array 3 out"), + outArray4 = op.outArray("Array 4 out"), + outArrayLength = op.outNumber("Array lengths"); + +let showingError = false; + +const arr1 = []; +const arr2 = []; +const arr3 = []; +const arr4 = []; + +inArray1.onChange = update; + +function update() +{ + let array1 = inArray1.get(); + + if (!array1) + { + outArray1.set(null); + return; + } + + if (array1.length % 4 !== 0) + { + if (!showingError) + { + op.uiAttr({ "error": "Arrays length not divisible by 4 !" }); + outArrayLength.set(0); + showingError = true; + } + return; + } + + if (showingError) + { + showingError = false; + op.uiAttr({ "error": null }); + } + + arr1.length = Math.floor(array1.length / 4); + arr2.length = Math.floor(array1.length / 4); + arr3.length = Math.floor(array1.length / 4); + arr4.length = Math.floor(array1.length / 4); + + for (let i = 0; i < array1.length / 4; i++) + { + arr1[i] = array1[i * 4]; + arr2[i] = array1[i * 4 + 1]; + arr3[i] = array1[i * 4 + 2]; + arr4[i] = array1[i * 4 + 3]; + } + + outArray1.set(null); + outArray2.set(null); + outArray3.set(null); + outArray4.set(null); + outArray1.set(arr1); + outArray2.set(arr2); + outArray3.set(arr3); + outArray4.set(arr4); + outArrayLength.set(arr1.length); +} + + +}; + +Ops.Array.ArrayUnpack4.prototype = new CABLES.Op(); +CABLES.OPS["8d2127d8-f3e4-4036-8cd8-75c6a404b582"]={f:Ops.Array.ArrayUnpack4,objName:"Ops.Array.ArrayUnpack4"}; + + + + +// ************************************************************** +// +// Ops.Array.ArrayUnshiftString +// +// ************************************************************** + +Ops.Array.ArrayUnshiftString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + inString = op.inString("String", ""), + result = op.outArray("Result"); + +const arr = []; + +inString.onChange = + inArr.onChange = + function () + { + const oldArr = inArr.get(); + result.set(null); + if (!oldArr) return; + + arr.length = oldArr.length + 1; + arr[0] = inString.get(); + + for (let i = 0; i < oldArr.length; i++) + { + arr[i + 1] = oldArr[i]; + } + + result.set(arr); + }; + + +}; + +Ops.Array.ArrayUnshiftString.prototype = new CABLES.Op(); +CABLES.OPS["b6b78f4f-487e-4372-bd35-aaaa097083ff"]={f:Ops.Array.ArrayUnshiftString,objName:"Ops.Array.ArrayUnshiftString"}; + + + + +// ************************************************************** +// +// Ops.Array.Array_v3 +// +// ************************************************************** + +Ops.Array.Array_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inLength = op.inValueInt("Array length", 10), + modeSelect = op.inSwitch("Mode select", ["Number", "1,2,3,4", "0-1"], "Number"), + inDefaultValue = op.inValueFloat("Default Value"), + inReverse=op.inBool("Reverse",false), + outArr = op.outArray("Array"), + outArrayLength = op.outNumber("Array length out"); + +let arr = []; +let selectIndex = 0; +const MODE_NUMBER = 0; +const MODE_1_TO_4 = 1; +const MODE_0_TO_1 = 2; + +modeSelect.onChange = onFilterChange; + +inReverse.onChange = + inDefaultValue.onChange = + inLength.onChange = reset; + +onFilterChange(); +reset(); + +function onFilterChange() +{ + let selectedMode = modeSelect.get(); + if (selectedMode === "Number") selectIndex = MODE_NUMBER; + else if (selectedMode === "1,2,3,4") selectIndex = MODE_1_TO_4; + else if (selectedMode === "0-1") selectIndex = MODE_0_TO_1; + + inDefaultValue.setUiAttribs({ "greyout": selectIndex !== MODE_NUMBER }); + + op.setUiAttrib({ "extendTitle": modeSelect.get() }); + + reset(); +} + +function reset() +{ + arr.length = 0; + + let arrLength = inLength.get(); + let valueForArray = inDefaultValue.get(); + let i; + + // mode 0 - fill all array values with one number + if (selectIndex === MODE_NUMBER) + { + for (i = 0; i < arrLength; i++) + { + arr[i] = valueForArray; + } + } + // mode 1 Continuous number array - increments up to array length + else if (selectIndex === MODE_1_TO_4) + { + for (i = 0; i < arrLength; i++) + { + arr[i] = i; + } + } + // mode 2 Normalized array + else if (selectIndex === MODE_0_TO_1) + { + for (i = 0; i < arrLength; i++) + { + arr[i] = i / (arrLength-1); + } + } + + if(inReverse.get())arr=arr.reverse(); + + outArr.set(null); + outArr.set(arr); + outArrayLength.set(arr.length); +} + + +}; + +Ops.Array.Array_v3.prototype = new CABLES.Op(); +CABLES.OPS["e4d31a46-bf64-42a8-be34-4cbb2bbc2600"]={f:Ops.Array.Array_v3,objName:"Ops.Array.Array_v3"}; + + + + +// ************************************************************** +// +// Ops.Array.AverageArray +// +// ************************************************************** + +Ops.Array.AverageArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray = op.inArray("Array"), + inIterations = op.inValueInt("Iterations", 1), + inMode = op.inValueSelect("Mode", ["repeat", "clamp"], "repeat"), + outArray = op.outArray("Smoothed array"), + outArrayLength = op.outNumber("Array length"); + +let smoothed = []; + +inArray.onChange = update; +inIterations.onChange = update; + +function smooth(values) +{ + smoothed.length = values.length; + let curr, prev, next, improved, i, pi, ni, mode; + mode = inMode.get(); + + for (i = 0; i < smoothed.length; i++) + { + pi = i - 1; + ni = i + 1; + curr = values[i]; + + if (pi < 0) + { + if (mode == "repeat") + prev = values[values.length - 1]; + else if (mode == "clamp") + prev = curr; + } + else + prev = smoothed[pi]; + + if (ni == values.length) + { + if (mode == "repeat") + next = values[0]; + else if (mode == "clamp") + next = curr; + } + else + next = values[ni]; + + improved = (prev + curr + next) / 3; + smoothed[i] = improved; + } + return smoothed; +} + +function update() +{ + let array = inArray.get(), num = inIterations.get(), i; + + if (!array) + return; + + for (i = 0; i < num; i++) + array = smooth(array); + + outArray.set(null); + outArray.set(array); + outArrayLength.set(array.length); +} + + +}; + +Ops.Array.AverageArray.prototype = new CABLES.Op(); +CABLES.OPS["40011294-10c4-4174-aa18-1b891826f436"]={f:Ops.Array.AverageArray,objName:"Ops.Array.AverageArray"}; + + + + +// ************************************************************** +// +// Ops.Array.BoolStateArray +// +// ************************************************************** + +Ops.Array.BoolStateArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// constants +let ARRAY_LENGTH_DEFAULT = 10; +let INACTIVE_VALUE = 0; +let ACTIVE_VALUE = 1; + +// variables +let stateArray = []; + +// inputs +let arrayLengthPort = op.inValue("Array Length", ARRAY_LENGTH_DEFAULT); +let activeIndexPort = op.inValue("Active Index", 0); +let inactiveValuePort = op.inValue("Inactive Value", 0); +let activeValuePort = op.inValue("Active Value", 1); + +// outputs +let stateArrayPort = op.outArray("State Array"); + +// change listeners +arrayLengthPort.onChange = update; +activeIndexPort.onChange = update; +inactiveValuePort.onChange = update; +activeValuePort.onChange = update; + +// init +update(); + +// functions + +function update() +{ + let arrLength = Math.max(0, arrayLengthPort.get()); + let activeIndex = Math.round(activeIndexPort.get()); + let inactiveValue = inactiveValuePort.get(); + let activeValue = activeValuePort.get(); + for (let i = 0; i < arrLength; i++) + { + if (i === activeIndex) + { + stateArray[i] = activeValue; + } + else + { + stateArray[i] = inactiveValue; + } + } + stateArray.length = arrLength; + stateArrayPort.set(null); + stateArrayPort.set(stateArray); +} + + +}; + +Ops.Array.BoolStateArray.prototype = new CABLES.Op(); +CABLES.OPS["f8c30313-4251-4d39-bff3-fb1cc9234d54"]={f:Ops.Array.BoolStateArray,objName:"Ops.Array.BoolStateArray"}; + + + + +// ************************************************************** +// +// Ops.Array.CopyArray +// +// ************************************************************** + +Ops.Array.CopyArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTriggerButton("Exec"), + inArr = op.inArray("Array"), + inReset = op.inTriggerButton("Reset"), + + deflt = op.inArray("Default"), + outArr = op.outArray("Result"); + + +function copyArray(source) +{ + let dest = []; + dest.length = source.length; + for (let i = 0; i < source.length; i++) + { + dest[i] = source[i]; + } + return dest; +} + +inReset.onTriggered = +deflt.onChange = function () +{ + let arr = deflt.get(); + outArr.set(null); + if (arr) outArr.set(copyArray(arr)); +}; + +inExec.onTriggered = function () +{ + let arr = inArr.get(); + if (!arr || !arr.length) return; + const cop = copyArray(arr); + outArr.set(null); + outArr.set(cop); +}; + + +}; + +Ops.Array.CopyArray.prototype = new CABLES.Op(); +CABLES.OPS["2f9b834e-c357-4dc7-ac2e-966b11735ef8"]={f:Ops.Array.CopyArray,objName:"Ops.Array.CopyArray"}; + + + + +// ************************************************************** +// +// Ops.Array.CropArray +// +// ************************************************************** + +Ops.Array.CropArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + srcArrayPort = op.inArray("Source Array"), + inStartIndex = op.inInt("Start Index"), + newLengthPort = op.inInt("New Length"), + croppedArrayOutPort = op.outArray("Cropped Array"), + outArrayLength = op.outNumber("Array length"); + +inStartIndex.onChange = srcArrayPort.onChange = newLengthPort.onChange = setOutPort; + +function setOutPort() +{ + const srcArray = srcArrayPort.get(); + + if(!srcArray) + { + croppedArrayOutPort.set(null); + outArrayLength.set(0); + return; + } + var newLength = parseInt(newLengthPort.get()); + const start=Math.floor(Math.abs(inStartIndex.get())); + + if(start+newLength >= srcArray.length) newLength = srcArray.length-start; + if(start+newLength <= srcArray.length) + { + var croppedArr = srcArray.slice(start, start+newLength); + croppedArrayOutPort.set(null); + croppedArrayOutPort.set(croppedArr); + outArrayLength.set(croppedArr.length); + } + +} + +}; + +Ops.Array.CropArray.prototype = new CABLES.Op(); +CABLES.OPS["d8deecd3-418f-43dd-8edb-dfe238b5327c"]={f:Ops.Array.CropArray,objName:"Ops.Array.CropArray"}; + + + + +// ************************************************************** +// +// Ops.Array.CutArray +// +// ************************************************************** + +Ops.Array.CutArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const srcArrayPort = op.inArray("Source Array"); +const inStartIndex = op.inInt("Remove from Start"); +const inEndIndex = op.inInt("Remove From End"); +const croppedArrayOutPort = op.outArray("Cut Array"); +const outArrayLength = op.outNumber("Array Length"); + +inStartIndex.onChange = inEndIndex.onChange = srcArrayPort.onChange = setOutPort; + +function setOutPort() +{ + const srcArray = srcArrayPort.get(); + + if (!srcArray) + { + croppedArrayOutPort.set(null); + outArrayLength.set(0); + return; + } + + const oldLength = srcArray.length; + const start = Math.max(0, Number(inStartIndex.get())); + const end = Math.max(0, Number(inEndIndex.get())); + + const newArr = []; + + for (let i = start; i < oldLength - end; i += 1) + { + newArr.push(srcArray[i]); + } + + croppedArrayOutPort.set(null); + croppedArrayOutPort.set(newArr); + outArrayLength.set(newArr.length); +} + + +}; + +Ops.Array.CutArray.prototype = new CABLES.Op(); +CABLES.OPS["9e6ea32a-3f5d-417a-b437-05c59d47c56e"]={f:Ops.Array.CutArray,objName:"Ops.Array.CutArray"}; + + + + +// ************************************************************** +// +// Ops.Array.EaseArray +// +// ************************************************************** + +Ops.Array.EaseArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + inMin = op.inValue("Min", 0), + inMax = op.inValue("Max", 1), + outArr = op.outArray("Result Array"), + anim = new CABLES.Anim(); + +anim.createPort(op, "Easing", updateAnimEasing); +anim.setValue(0, 0); +anim.setValue(1, 1); +let resultArr = []; +op.onLoaded = inMin.onChange = inMax.onChange = updateMinMax; + +inArr.onChange = updateArray; + +function updateMinMax() +{ + anim.keys[0].time = anim.keys[0].value = Math.min(inMin.get(), inMax.get()); + anim.keys[1].time = anim.keys[1].value = Math.max(inMin.get(), inMax.get()); +} + +function updateAnimEasing() +{ + anim.keys[0].setEasing(anim.defaultEasing); + updateArray(); +} + +function updateArray() +{ + const arr = inArr.get(); + if (!arr) + { + outArr.set(null); + return; + } + resultArr.length = arr.length; + + for (let i = 0; i < arr.length; i++) + { + resultArr[i] = anim.getValue(arr[i]); + } + outArr.set(null); + outArr.set(resultArr); +} + + +}; + +Ops.Array.EaseArray.prototype = new CABLES.Op(); +CABLES.OPS["3bda237e-819a-43d8-9fb8-0f32bd3f7cc8"]={f:Ops.Array.EaseArray,objName:"Ops.Array.EaseArray"}; + + + + +// ************************************************************** +// +// Ops.Array.FillArrayRandomDuplicates_v2 +// +// ************************************************************** + +Ops.Array.FillArrayRandomDuplicates_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + inNum = op.inValueInt("Num Elements", 10), + inSeed = op.inValueFloat("Random seed", 1), + outArr = op.outArray("Result"); + +op.toWorkPortsNeedToBeLinked(inArr); + +let arr = []; + +inArr.onChange = + inNum.onChange = + inSeed.onChange = () => + { + let oldArr = inArr.get(); + + if (!oldArr) return outArr.set(null); + + let num = inNum.get(); + let numOld = oldArr.length; + + Math.randomSeed = inSeed.get(); + arr.length = num; + + for (let i = 0; i < num; i++) + { + let ind = Math.floor(Math.seededRandom() * numOld); + arr[i] = oldArr[ind]; + } + + outArr.set(null); + outArr.set(arr); + }; + + +}; + +Ops.Array.FillArrayRandomDuplicates_v2.prototype = new CABLES.Op(); +CABLES.OPS["cbf5f595-9fbe-45a9-a324-5089665e8e51"]={f:Ops.Array.FillArrayRandomDuplicates_v2,objName:"Ops.Array.FillArrayRandomDuplicates_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.FilterArray +// +// ************************************************************** + +Ops.Array.FilterArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + inArrayStide = op.inSwitch("Stride", ["X", "XY", "XYZ", "XYZW"], "XYZ"), + inEle = op.inSwitch("Compare Element", ["X", "Y", "Z", "W"], "Z"), + inMeth = op.inSwitch("Filter Method", [">", "<"], "<"), + inFilterNum = op.inFloat("Compare to", 0.5), + outArr = op.outArray("Result"); + +let compEl = 2; +let stride = 3; + +inEle.onChange = () => +{ + if (inEle.get() == "X")compEl = 0; + else if (inEle.get() == "Y")compEl = 1; + else if (inEle.get() == "Z")compEl = 2; + else if (inEle.get() == "W")compEl = 3; + + update(); +}; + +inArrayStide.onChange = () => +{ + if (inArrayStide.get() == "X")stride = 1; + else if (inArrayStide.get() == "XY")stride = 2; + else if (inArrayStide.get() == "XYZ")stride = 3; + else if (inArrayStide.get() == "XYZW")stride = 4; + + update(); +}; + +inMeth.onChange = +inFilterNum.onChange = +inArr.onChange = update; + +function update() +{ + const arr = inArr.get(); + const newArr = []; + if (!arr) return; + + if (inMeth.get() == "<") + { + for (let i = 0; i < arr.length; i += stride) + if (arr[i + compEl] < inFilterNum.get()) // comparison + for (let j = 0; j < stride; j++) + newArr.push(arr[i + j]); + } + else if (inMeth.get() == ">") + { + for (let i = 0; i < arr.length; i += stride) + if (arr[i + compEl] > inFilterNum.get()) // comparison + for (let j = 0; j < stride; j++) + newArr.push(arr[i + j]); + } + + outArr.set(null); + outArr.set(newArr); +} + + +}; + +Ops.Array.FilterArray.prototype = new CABLES.Op(); +CABLES.OPS["ea396c36-c070-44d8-ae0b-fd0bffa56b35"]={f:Ops.Array.FilterArray,objName:"Ops.Array.FilterArray"}; + + + + +// ************************************************************** +// +// Ops.Array.FilterValidArray +// +// ************************************************************** + +Ops.Array.FilterValidArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + inLength = op.inBool("Invalid when length is 0", true), + + outArray = op.outArray("Last Valid Array"), + outValid = op.outBool("Is Valid"); + +inLength.onChange = +inArr.onChange = + update; + +function update() +{ + const arr = inArr.get(); + + let r = true; + + if (!arr || !arr.length) r = false; + else if (inLength.get() && arr.length == 0) r = false; + + if (r) + { + outArray.set([]); + outArray.set(arr); + } + + outValid.set(r); +} + + +}; + +Ops.Array.FilterValidArray.prototype = new CABLES.Op(); +CABLES.OPS["f2669593-eb06-48a6-b94c-4bc243747ee1"]={f:Ops.Array.FilterValidArray,objName:"Ops.Array.FilterValidArray"}; + + + + +// ************************************************************** +// +// Ops.Array.FlattenArray +// +// ************************************************************** + +Ops.Array.FlattenArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArr=op.inArray("Array"), + outArr=op.outArray("Result"); + + inArr.onChange=function() + { + var arr=inArr.get(); + outArr.set(null); + + if(!arr) + { + return; + } + + + var newArr=arr.slice(0); + newArr=newArr.flat(Infinity); + + outArr.set(newArr); + + + }; + + +}; + +Ops.Array.FlattenArray.prototype = new CABLES.Op(); +CABLES.OPS["bd5f43cf-7a69-4bff-85b3-08df62b899b4"]={f:Ops.Array.FlattenArray,objName:"Ops.Array.FlattenArray"}; + + + + +// ************************************************************** +// +// Ops.Array.GateArray_v2 +// +// ************************************************************** + +Ops.Array.GateArray_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const passThrough = op.inValueBool("Pass Through", true), + arrayIn = op.inArray("Array in"), + arrayOut = op.outArray("Array Out"); + +let oldArr = null; + +function copyArray(source) +{ + if (!source) return null; + const dest = []; + dest.length = source.length; + for (let i = 0; i < source.length; i++) + { + dest[i] = source[i]; + } + return dest; +} + +arrayIn.onChange = passThrough.onChange = function () +{ + if (passThrough.get()) + { + oldArr = copyArray(arrayIn.get()); + arrayOut.set(oldArr); + } + // else + // { + // arrayOut.set(oldArr); + // } +}; + + +}; + +Ops.Array.GateArray_v2.prototype = new CABLES.Op(); +CABLES.OPS["e28a489c-46b6-4279-928c-6b0cbaaaae2a"]={f:Ops.Array.GateArray_v2,objName:"Ops.Array.GateArray_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.GetValuesFromArrayOfObjects +// +// ************************************************************** + +Ops.Array.GetValuesFromArrayOfObjects = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + inKey = op.inString("Key"), + inignoreNonNums = op.inBool("Numbers Only", false), + inRemoveInvalid = op.inBool("Remove empty/invalid", false), + outArray = op.outArray("Result"); + +inRemoveInvalid.onChange = +inKey.onChange = +inArr.onChange = +inignoreNonNums.onChange = exec; + +function exec() +{ + const arr = inArr.get(); + + if (!arr) + { + outArray.set(null); + return; + } + + const newArr = []; + const key = inKey.get(); + const numsonly = inignoreNonNums.get(); + + op.setUiAttrib({ "extendTitle": inKey.get() }); + + const removeInvalid = inRemoveInvalid.get(); + + for (let i = 0; i < arr.length; i++) + { + const obj = arr[i]; + + if (obj) + { + if (!(key in obj)) continue; + + const v = obj[key]; + + if (removeInvalid) + { + if (v === "" || + v === null || + v === undefined + + ) continue; + } + + if (numsonly) + { + if (CABLES.UTILS.isNumeric(v)) newArr.push(parseFloat(v)); + } + else + { + newArr.push(v); + } + } + } + + outArray.set(null); + outArray.set(newArr); +} + + +}; + +Ops.Array.GetValuesFromArrayOfObjects.prototype = new CABLES.Op(); +CABLES.OPS["e068629a-7963-48b0-853f-a89348d03654"]={f:Ops.Array.GetValuesFromArrayOfObjects,objName:"Ops.Array.GetValuesFromArrayOfObjects"}; + + + + +// ************************************************************** +// +// Ops.Array.HSBtoRGBArray +// +// ************************************************************** + +Ops.Array.HSBtoRGBArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +function HSBtoRGB(hue, saturation, lightness) +{ + // based on algorithm from http://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB + const chroma = (1 - Math.abs((2 * lightness) - 1)) * saturation; + let huePrime = hue * 6; // / 60; + let secondComponent = chroma * (1 - Math.abs((huePrime % 2) - 1)); + + huePrime = Math.floor(huePrime) || 0; + let red = 0; + let green = 0; + let blue = 0; + + if (huePrime === 0) + { + red = chroma; + green = secondComponent; + blue = 0; + } + else if (huePrime === 1) + { + red = secondComponent; + green = chroma; + blue = 0; + } + else if (huePrime === 2) + { + red = 0; + green = chroma; + blue = secondComponent; + } + else if (huePrime === 3) + { + red = 0; + green = secondComponent; + blue = chroma; + } + else if (huePrime === 4) + { + red = secondComponent; + green = 0; + blue = chroma; + } + else if (huePrime >= 5) + { + red = chroma; + green = 0; + blue = secondComponent; + } + + const lightnessAdjustment = (lightness - (chroma / 2)); + + red += lightnessAdjustment; + green += lightnessAdjustment; + blue += lightnessAdjustment; + + return [red, green, blue]; +} + +const inTrigger = op.inTrigger("Trigger Input"); +const inHue = op.inArray("In Hue Array"); +const inSat = op.inArray("In Saturation Array"); +const inBright = op.inArray("In Brightness Array"); +const inAlpha = op.inArray("In Alpha Array"); + +const outTrigger = op.outTrigger("Trigger Output"); +const outArray = op.outArray("Result Array"); +const outLength = op.outNumber("Array Length", 0); +const outTupleLength = op.outNumber("RGBA Tuple Length", 0); + +let newArr = []; +const emptyArr = []; +outArray.set(newArr); +outLength.set(newArr.length); +outTupleLength.set(newArr.length / 4); + +outArray.ignoreValueSerialize = true; +inAlpha.ignoreValueSerialize = true; +inBright.ignoreValueSerialize = true; +inSat.ignoreValueSerialize = true; +inHue.ignoreValueSerialize = true; + +let shouldRecalculate = true; + +inTrigger.onTriggered = handleTrigger; +inHue.onChange = inSat.onChange = inBright.onChange = inAlpha.onChange = function () { shouldRecalculate = true; }; + +function handleTrigger() +{ + let arrH = inHue.get(); + let arrS = inSat.get(); + let arrB = inBright.get(); + let arrA = inAlpha.get(); + + if (!arrH && !arrS && !arrB && !arrA) + { + outArray.set(null); + outLength.set(0); + outTupleLength.set(0); + return; + } + + let length = 0; + + if (shouldRecalculate) + { + if ([arrH, arrS, arrB, arrA].some(function (array) { return !array; })) + { + if (arrH) length = arrH.length; + else if (arrS) length = arrS.length; + else if (arrB) length = arrB.length; + else if (arrA) length = arrA.length; + + emptyArr.length = length; + for (let i = 0; i < length; i += 1) emptyArr[i] = 0; + + if (!arrH) arrH = emptyArr.map(function (val) { return 0; }); + if (!arrS) arrS = emptyArr.map(function (val) { return 1; }); + if (!arrB) arrB = emptyArr.map(function (val) { return 0.5; }); + if (!arrA) arrA = emptyArr.map(function (val) { return 1; }); + } + else + { + length = arrH.length; + } + + const areSameLength = [arrH, arrS, arrB, arrA].every(function (val, i, arr) { return val.length === length; }); + + if (!areSameLength) + { + // op.log([arrH, arrS, arrB, arrA].map(a => a.length), length); + op.setUiError("arrlen", "Arrays don't have the same length!"); + } + else + { + op.setUiError("arrlen", null); + } + + newArr.length = length * 4; + + for (let i = 0; i < newArr.length / 4; i += 1) + { + const hsbArray = HSBtoRGB(arrH[i], arrS[i], arrB[i]); + newArr[i * 4 + 0] = hsbArray[0]; + newArr[i * 4 + 1] = hsbArray[1]; + newArr[i * 4 + 2] = hsbArray[2]; + newArr[i * 4 + 3] = arrA[i]; + } + + shouldRecalculate = false; + outArray.set(null); + outArray.set(newArr); + outLength.set(newArr.length); + outTupleLength.set(newArr.length / 4); + } + outTrigger.trigger(); +} + + +}; + +Ops.Array.HSBtoRGBArray.prototype = new CABLES.Op(); +CABLES.OPS["0f552671-7431-4031-97e3-211f2cf9f111"]={f:Ops.Array.HSBtoRGBArray,objName:"Ops.Array.HSBtoRGBArray"}; + + + + +// ************************************************************** +// +// Ops.Array.InfoArray +// +// ************************************************************** + +Ops.Array.InfoArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + outMin = op.outNumber("Min"), + outMax = op.outNumber("Max"), + outAvg = op.outNumber("Average"); + +inArr.onChange = function () +{ + let arr = inArr.get(); + + let min = 999999999; + let max = -999999999; + let avg = 0; + + if (arr) + { + for (let i = 0; i < arr.length; i++) + { + avg += arr[i]; + min = Math.min(min, arr[i]); + max = Math.max(max, arr[i]); + } + avg /= arr.length; + } + outMin.set(min); + outMax.set(max); + outAvg.set(avg); +}; + + +}; + +Ops.Array.InfoArray.prototype = new CABLES.Op(); +CABLES.OPS["1db230c8-212f-4679-87d6-3531659363da"]={f:Ops.Array.InfoArray,objName:"Ops.Array.InfoArray"}; + + + + +// ************************************************************** +// +// Ops.Array.InfoArray2 +// +// ************************************************************** + +Ops.Array.InfoArray2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let inArr = op.inArray("Array", 2), + + outNum = op.outNumber("Num Items"), + + outMinX = op.outNumber("Min X"), + outMaxX = op.outNumber("Max X"), + outAvgX = op.outNumber("Average X"), + + outMinY = op.outNumber("Min Y"), + outMaxY = op.outNumber("Max Y"), + outAvgY = op.outNumber("Average Y"); + +inArr.onChange = function () +{ + let arr = inArr.get(); + + let minX = 999999999; + let maxX = -999999999; + let avgX = 0; + + let minY = 999999999; + let maxY = -999999999; + let avgY = 0; + outNum.set(0); + + if (arr) + { + outNum.set(arr.length / 2); + + for (let i = 0; i < arr.length; i += 2) + { + avgX += arr[i]; + minX = Math.min(minX, arr[i]); + maxX = Math.max(maxX, arr[i]); + + avgY += arr[i + 1]; + minY = Math.min(minY, arr[i + 1]); + maxY = Math.max(maxY, arr[i + 1]); + } + + avgX /= arr.length / 2; + avgY /= arr.length / 2; + } + + outMinX.set(minX); + outMaxX.set(maxX); + outAvgX.set(avgX); + + outMinY.set(minY); + outMaxY.set(maxY); + outAvgY.set(avgY); +}; + + +}; + +Ops.Array.InfoArray2.prototype = new CABLES.Op(); +CABLES.OPS["0ea5fb62-94e7-4a1b-928f-bdcab373c022"]={f:Ops.Array.InfoArray2,objName:"Ops.Array.InfoArray2"}; + + + + +// ************************************************************** +// +// Ops.Array.InfoArray3 +// +// ************************************************************** + +Ops.Array.InfoArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let inArr = op.inArray("Array"), + + outNum = op.outNumber("Num Items"), + + outMinX = op.outNumber("Min X"), + outMaxX = op.outNumber("Max X"), + outAvgX = op.outNumber("Average X"), + + outMinY = op.outNumber("Min Y"), + outMaxY = op.outNumber("Max Y"), + outAvgY = op.outNumber("Average Y"), + + outMinZ = op.outNumber("Min Z"), + outMaxZ = op.outNumber("Max Z"), + outAvgZ = op.outNumber("Average Z"); + +inArr.onChange = function () +{ + let arr = inArr.get(); + + let minX = 999999999; + let maxX = -999999999; + let avgX = 0; + + let minZ = 999999999; + let maxZ = -999999999; + let avgZ = 0; + + let minY = 999999999; + let maxY = -999999999; + let avgY = 0; + outNum.set(0); + + if (arr) + { + outNum.set(arr.length / 3); + + for (let i = 0; i < arr.length; i += 3) + { + avgX += arr[i]; + minX = Math.min(minX, arr[i]); + maxX = Math.max(maxX, arr[i]); + + avgY += arr[i + 1]; + minY = Math.min(minY, arr[i + 1]); + maxY = Math.max(maxY, arr[i + 1]); + + avgZ += arr[i + 2]; + minZ = Math.min(minZ, arr[i + 2]); + maxZ = Math.max(maxZ, arr[i + 2]); + } + + avgX /= arr.length / 3; + avgY /= arr.length / 3; + avgZ /= arr.length / 3; + } + + outMinX.set(minX); + outMaxX.set(maxX); + outAvgX.set(avgX); + + outMinY.set(minY); + outMaxY.set(maxY); + outAvgY.set(avgY); + + outMinZ.set(minZ); + outMaxZ.set(maxZ); + outAvgZ.set(avgZ); +}; + + +}; + +Ops.Array.InfoArray3.prototype = new CABLES.Op(); +CABLES.OPS["e2ace702-eed6-4b40-86f6-cea716ba8579"]={f:Ops.Array.InfoArray3,objName:"Ops.Array.InfoArray3"}; + + + + +// ************************************************************** +// +// Ops.Array.InterpolateArrays +// +// ************************************************************** + +Ops.Array.InterpolateArrays = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("Exe"), + inArr1 = op.inArray("Array 1"), + inArr2 = op.inArray("Array 2"), + inPerc = op.inValueSlider("perc"), + next = op.outTrigger("Next"), + outArr = op.outArray("Result"); + +let needsCalc = true; +let resultArr = []; + +function calcLater() +{ + needsCalc = true; +} + +inArr1.onChange = inArr2.onChange = inPerc.onChange = calcLater; + +exe.onTriggered = execute; + +function execute() +{ + let arr1 = inArr1.get(); + let arr2 = inArr2.get(); + + let val1; + let val2; + let m; + + if (!arr1 || !arr2 || arr1.length < arr2.length) + { + outArr.set(null); + return; + } + + if (needsCalc) + { + if (resultArr.length != arr1.length) resultArr.length = arr1.length; + + let perc = inPerc.get(); + + for (let i = 0; i < arr1.length; i++) + { + val1 = arr1[i]; + val2 = arr2[i]; + m = (val2 - val1) * perc + val1; + resultArr[i] = m; + } + needsCalc = false; + outArr.set(null); + outArr.set(resultArr); + } + + next.trigger(); +} + +// check that array input is string or not +inArr1.onLinkChanged = inArr2.onLinkChanged = function () +{ + let arr1 = inArr1.get(); + let arr2 = inArr2.get(); + + if (!arr1 || !arr2) + { + outArr.set(null); + return; + } + + let stringTest1 = arr1[0]; + let stringTest2 = arr2[0]; + + if (typeof stringTest1 === "string" || typeof stringTest2 === "string") + { + outArr.set(null); + } +}; + + +}; + +Ops.Array.InterpolateArrays.prototype = new CABLES.Op(); +CABLES.OPS["09296117-7312-4f80-982b-7b4a81d22cf8"]={f:Ops.Array.InterpolateArrays,objName:"Ops.Array.InterpolateArrays"}; + + + + +// ************************************************************** +// +// Ops.Array.InterpolateArraysRange +// +// ************************************************************** + +Ops.Array.InterpolateArraysRange = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTrigger("Exe"); +const inArr1 = op.inArray("Array 1"); +const inArr2 = op.inArray("Array 2"); + +const inPos = op.inValueSlider("Pos"); +const inWidth = op.inValueSlider("Width"); + +const easing = op.inValueSelect("Easing", [ + "Linear", + "Expo in", "Expo out", "Expo in out", + "Cubic in", "Cubic out", "Cubic in out"], +"Linear"); + +const reverse = op.inValueBool("Reverse"); + +const next = op.outTrigger("Next"); +const outArr = op.outArray("Result"); + +const resultArr = []; +let easingFunction = null; + +easing.onChange = function () +{ + if (easing.get() == "Expo in") easingFunction = CABLES.easeExpoIn; + else if (easing.get() == "Expo out") easingFunction = CABLES.easeExpoOut; + else if (easing.get() == "Expo in out") easingFunction = CABLES.easeExpoInOut; + else if (easing.get() == "Cubic in") easingFunction = CABLES.easeCubicIn; + else if (easing.get() == "Cubic out") easingFunction = CABLES.easeCubicOut; + else if (easing.get() == "Cubic in out") easingFunction = CABLES.easeCubicInOut; + else easingFunction = null; +}; + +let needsUpdate = true; + +inArr1.onChange = +inArr2.onChange = +inPos.onChange = +inWidth.onChange = () => +{ + needsUpdate = true; +}; + +exe.onTriggered = function () +{ + const arr1 = inArr1.get(); + const arr2 = inArr2.get(); + + if (needsUpdate) + if (!arr1 || !arr2 || arr2.length < arr1.length) + { + outArr.set(null); + } + else + { + if (resultArr.length != arr1.length) resultArr.length = arr1.length; + const distNum = inWidth.get() * (resultArr.length * 4); + const pos = inPos.get() * (arr1.length + distNum); + + for (let i = 0; i < arr1.length; i++) + { + const val1 = arr1[i]; + const val2 = arr2[i]; + + let ppos = pos - i; + if (reverse.get())ppos = pos - (arr1.length - i); + let dist = ppos / distNum; + + if (dist > 1) resultArr[i] = val2; + else if (dist <= 0) resultArr[i] = val1; + else + { + if (easingFunction) dist = easingFunction(dist); + const m = ((val2 - val1) * dist + val1); + resultArr[i] = m; + } + } + + outArr.set(null); + outArr.set(resultArr); + } + + next.trigger(); + needsUpdate = false; +}; + + +}; + +Ops.Array.InterpolateArraysRange.prototype = new CABLES.Op(); +CABLES.OPS["f89f5664-7cfa-4598-b09c-e4320b588b45"]={f:Ops.Array.InterpolateArraysRange,objName:"Ops.Array.InterpolateArraysRange"}; + + + + +// ************************************************************** +// +// Ops.Array.InterpolateNumbersArray +// +// ************************************************************** + +Ops.Array.InterpolateNumbersArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inIndex = op.inValue("Index Position"), + inArr = op.inArray("Array"), + outX = op.outNumber("result"); + +inIndex.onChange = inArr.onChange = function () +{ + let i = Math.floor(inIndex.get()); + let fr = inIndex.get() - Math.floor(inIndex.get()); + let arr = inArr.get(); + + if (i < 0 || !arr) + { + return; + } + + if (i >= arr.length - 1) + { + outX.set(arr[arr.length - 1]); + return; + } + + let x = arr[i + 0]; + + let x2 = arr[i + 1]; + + x += (x2 - x) * fr; + + outX.set(x); +}; + + +}; + +Ops.Array.InterpolateNumbersArray.prototype = new CABLES.Op(); +CABLES.OPS["d0894a6b-f921-4542-aafc-3f1b21615a44"]={f:Ops.Array.InterpolateNumbersArray,objName:"Ops.Array.InterpolateNumbersArray"}; + + + + +// ************************************************************** +// +// Ops.Array.InterpolateNumbersArray3 +// +// ************************************************************** + +Ops.Array.InterpolateNumbersArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inIndex = op.inValue("Index Position"), + inArr = op.inArray("Array"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outZ = op.outNumber("Z"); + +inIndex.onChange = inArr.onChange = function () +{ + let i = inIndex.get(); + const arr = inArr.get(); + + if (i < 0 || !arr) + { + return; + } + + const maxIdx = Math.floor((arr.length / 3) - 1); + const intIdx = Math.floor(inIndex.get()); + if (intIdx == maxIdx) + { + outX.set(arr[arr.length - 3]); + outY.set(arr[arr.length - 2]); + outZ.set(arr[arr.length - 1]); + return; + } + + const fr = inIndex.get() - Math.floor(inIndex.get()); + i = Math.floor((inIndex.get())) * 3; + i %= (arr.length); + + let x = arr[i + 0]; + let y = arr[i + 1]; + let z = arr[i + 2]; + + const x2 = arr[i + 3]; + const y2 = arr[i + 4]; + const z2 = arr[i + 5]; + + x += (x2 - x) * fr; + y += (y2 - y) * fr; + z += (z2 - z) * fr; + + outX.set(x); + outY.set(y); + outZ.set(z); +}; + + +}; + +Ops.Array.InterpolateNumbersArray3.prototype = new CABLES.Op(); +CABLES.OPS["1feb4565-32c5-4fb9-b869-88502b461256"]={f:Ops.Array.InterpolateNumbersArray3,objName:"Ops.Array.InterpolateNumbersArray3"}; + + + + +// ************************************************************** +// +// Ops.Array.IteratorArray3 +// +// ************************************************************** + +Ops.Array.IteratorArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("Execute"), + arr = op.inArray("Array"), + pStep = op.inValue("Step"), + trigger = op.outTrigger("Trigger"), + idx = op.outNumber("Index"), + valX = op.outNumber("Value 1"), + valY = op.outNumber("Value 2"), + valZ = op.outNumber("Value 3"); + +let ar = arr.get() || []; + +let vstep = 1; +pStep.onChange = changeStep; +changeStep(); + +let i = 0; +let count = 0; + +arr.onChange = function () +{ + ar = arr.get() || []; +}; + +function changeStep() +{ + vstep = pStep.get() || 1; + if (vstep < 1.0)vstep = 1.0; + vstep = 3 * vstep; +} + +exe.onTriggered = function () +{ + count = 0; + + for (let i = 0, len = ar.length; i < len; i += vstep) + // for (var i = ar.length-1; i >=0; i-=vstep) + { + idx.set(count); + valX.set(ar[i + 0]); + valY.set(ar[i + 1]); + valZ.set(ar[i + 2]); + trigger.trigger(); + count++; + } +}; + + +}; + +Ops.Array.IteratorArray3.prototype = new CABLES.Op(); +CABLES.OPS["3f7db864-7409-418f-8c03-b2c966c050b3"]={f:Ops.Array.IteratorArray3,objName:"Ops.Array.IteratorArray3"}; + + + + +// ************************************************************** +// +// Ops.Array.LissajouseSpline +// +// ************************************************************** + +Ops.Array.LissajouseSpline = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inForm=op.inValueSelect("Formula",[1,2,3],1), + inA=op.inValueInt("A",5), + inB=op.inValueInt("B",4), + inC=op.inValueInt("C",1), + inD=op.inValueInt("D",2), + result=op.outArray("Result"), + outTotalPoints = op.outNumber("Total points"), + outArrayLength = op.outNumber("Array length"); + +inForm.onChange=inA.onChange=inB.onChange=inC.onChange=inD.onChange=calc; +calc(); + +function calc() +{ + var numPoints=13200; + var step=40; + + var arr=[]; + var x=0,y=0,z=0; + var form=parseInt(inForm.get()); + var th=0.03; + + for(var i = 0; i < numPoints; i+=step) + { + var index=i/step; + + if(form==1) + { + x=Math.sin( (i * inA.get()) * 0.001 ); + y=Math.cos( (i * inB.get()) * 0.001 ); + z=Math.sin( (i * inC.get()) * 0.001 ); + } + else if(form==2) + { + x=(Math.cos( (i * inA.get()) * 0.001 )+Math.cos( (i * inB.get()) * 0.001 ) )/2; + y=(Math.sin( (i * inA.get()) * 0.001 )+Math.sin( (i * inC.get()) * 0.001 ) )/2; + z=(Math.sin( (i * inD.get()) * 0.001 )); + } + else if(form==3) + { + x=(Math.sin( (i * inA.get()) * 0.001 )*(1+Math.cos( (i * inB.get()) * 0.001 ) ))/2; + y=(Math.sin( (i * inA.get()) * 0.001 )*(1+Math.sin( (i * inC.get()) * 0.001 ) ))/2; + z=(Math.sin( (i * inD.get()) * 0.001 )); + } + + arr[index*3+0] = x; + arr[index*3+1] = y; + arr[index*3+2] = z; + + if(index>10 && Math.abs(x-arr[0])= Math.max(old_max.get(), old_min.get())) + { + outArray[i] = new_max.get(); + } + else if (x <= Math.min(old_max.get(), old_min.get())) + { + outArray[i] = new_min.get(); + } + else + { + const nMin = new_min.get(); + const nMax = new_max.get(); + const oMin = old_min.get(); + const oMax = old_max.get(); + + let reverseInput = false; + const oldMin = Math.min(oMin, oMax); + const oldMax = Math.max(oMin, oMax); + if (oldMin !== oMin) reverseInput = true; + + let reverseOutput = false; + const newMin = Math.min(nMin, nMax); + const newMax = Math.max(nMin, nMax); + if (newMin !== nMin) reverseOutput = true; + + let portion = 0; + + if (reverseInput) + { + portion = (oldMax - x) * (newMax - newMin) / (oldMax - oldMin); + } + else + { + portion = (x - oldMin) * (newMax - newMin) / (oldMax - oldMin); + } + + if (reverseOutput) + { + r = newMax - portion; + } + else + { + r = portion + newMin; + } + + if (ease === 0) + { + outArray[i] = r; + } + else if (ease === 1) + { + x = Math.max(0, Math.min(1, (r - nMin) / (nMax - nMin))); + outArray[i] = nMin + x * x * (3 - 2 * x) * (nMax - nMin); // smoothstep + } + else if (ease === 2) + { + x = Math.max(0, Math.min(1, (r - nMin) / (nMax - nMin))); + outArray[i] = nMin + x * x * x * (x * (x * 6 - 15) + 10) * (nMax - nMin); // smootherstep + } + } + } + result.set(outArray); +} + +v.set(null); +old_min.set(0); +old_max.set(1); +new_min.set(-1); +new_max.set(1); + +v.onChange = exec; +old_min.onChange = exec; +old_max.onChange = exec; +new_min.onChange = exec; +new_max.onChange = exec; + +result.set(null); + +exec(); + + +}; + +Ops.Array.MapRangeArray.prototype = new CABLES.Op(); +CABLES.OPS["20f921bf-adc2-45fb-b387-834af4f5e19b"]={f:Ops.Array.MapRangeArray,objName:"Ops.Array.MapRangeArray"}; + + + + +// ************************************************************** +// +// Ops.Array.PaletteLibrary +// +// ************************************************************** + +Ops.Array.PaletteLibrary = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const outArrayPalette = op.outArray("Palette Array out"); + +const colors=[ +'#E6E2AF','#A7A37E','#EFECCA','#046380','002F2F', +'#468966','#FFF0A5','#FFB03B','#B64926','8E2800', +'#FCFFF5','#D1DBBD','#91AA9D','#3E606F','193441', +'#FF6138','#FFFF9D','#BEEB9F','#79BD8F','00A388', +'#105B63','#FFFAD5','#FFD34E','#DB9E36','BD4932', +'#225378','#1695A3','#ACF0F2','#F3FFE2','EB7F00', +'#2C3E50','#E74C3C','#ECF0F1','#3498DB','2980B9', +'#000000','#263248','#7E8AA2','#FFFFFF','FF9800', +'#004358','#1F8A70','#BEDB39','#FFE11A','FD7400', +'#DC3522','#D9CB9E','#374140','#2A2C2B','1E1E20', +'#7D8A2E','#C9D787','#FFFFFF','#FFC0A9','FF8598', +'#B9121B','#4C1B1B','#F6E497','#FCFAE1','BD8D46', +'#2E0927','#D90000','#FF2D00','#FF8C00','04756F', +'#595241','#B8AE9C','#FFFFFF','#ACCFCC','8A0917', +'#10222B','#95AB63','#BDD684','#E2F0D6','F6FFE0', +'#F6F792','#333745','#77C4D3','#DAEDE2','EA2E49', +'#703030','#2F343B','#7E827A','#E3CDA4','C77966', +'#2F2933','#01A2A6','#29D9C2','#BDF271','FFFFA6', +'#D8CAA8','#5C832F','#284907','#382513','363942', +'#FFF8E3','#CCCC9F','#33332D','#9FB4CC','DB4105', +'#85DB18','#CDE855','#F5F6D4','#A7C520','493F0B', +'#04BFBF','#CAFCD8','#F7E967','#A9CF54','588F27', +'#292929','#5B7876','#8F9E8B','#F2E6B6','412A22', +'#332532','#644D52','#F77A52','#FF974F','A49A87', +'#405952','#9C9B7A','#FFD393','#FF974F','F54F29', +'#2B3A42','#3F5765','#BDD4DE','#EFEFEF','FF530D', +'#962D3E','#343642','#979C9C','#F2EBC7','348899', +'#96CA2D','#B5E655','#EDF7F2','#4BB5C1','7FC6BC', +'#1C1D21','#31353D','#445878','#92CDCF','EEEFF7', +'#3E454C','#2185C5','#7ECEFD','#FFF6E5','FF7F66', +'#00585F','#009393','#FFFCC4','#F0EDBB','FF3800', +'#B4AF91','#787746','#40411E','#32331D','C03000', +'#63A69F','#F2E1AC','#F2836B','#F2594B','CD2C24', +'#88A825','#35203B','#911146','#CF4A30','ED8C2B', +'#F2385A','#F5A503','#E9F1DF','#4AD9D9','36B1BF', +'#CFC291','#FFF6C5','#A1E8D9','#FF712C','695D46', +'#FF5335','#B39C85','#306E73','#3B424D','1D181F', +'#000000','#333333','#FF358B','#01B0F0','AEEE00', +'#E8E595','#D0A825','#40627C','#26393D','FFFAE4', +'#E7E8D1','#D3CEAA','#FBF7E4','#424242','8E001C', +'#354242','#ACEBAE','#FFFF9D','#C9DE55','7D9100', +'#2F2933','#01A2A6','#29D9C2','#BDF271','FFFFA6', +'#DDDCC5','#958976','#611427','#1D2326','6A6A61', +'#6C6E58','#3E423A','#417378','#A4CFBE','F4F7D9', +'#E1E6FA','#C4D7ED','#ABC8E2','#375D81','183152', +'#6B0C22','#D9042B','#F4CB89','#588C8C','011C26', +'#304269','#91BED4','#D9E8F5','#FFFFFF','F26101', +'#96CEB4','#FFEEAD','#FF6F69','#FFCC5C','AAD8B0', +'#B0CC99','#677E52','#B7CA79','#F6E8B1','89725B', +'#334D5C','#45B29D','#EFC94C','#E27A3F','DF5A49', +'#16193B','#35478C','#4E7AC7','#7FB2F0','ADD5F7', +'#00261C','#044D29','#168039','#45BF55','96ED89', +'#36362C','#5D917D','#A8AD80','#E6D4A7','825534', +'#F9E4AD','#E6B098','#CC4452','#723147','31152B', +'#2C3E50','#FC4349','#D7DADB','#6DBCDB','FFFFFF', +'#002635','#013440','#AB1A25','#D97925','EFE7BE', +'#FF8000','#FFD933','#CCCC52','#8FB359','192B33', +'#272F32','#9DBDC6','#FFFFFF','#FF3D2E','DAEAEF', +'#B8ECD7','#083643','#B1E001','#CEF09D','476C5E', +'#002F32','#42826C','#A5C77F','#FFC861','C84663', +'#5C4B51','#8CBEB2','#F2EBBF','#F3B562','F06060', +'#5A1F00','#D1570D','#FDE792','#477725','A9CC66', +'#5E0042','#2C2233','#005869','#00856A','8DB500', +'#52656B','#FF3B77','#CDFF00','#FFFFFF','B8B89F', +'#801637','#047878','#FFB733','#F57336','C22121', +'#730046','#BFBB11','#FFC200','#E88801','C93C00', +'#24221F','#363F45','#4B5F6D','#5E7C88','FEB41C', +'#E64661','#FFA644','#998A2F','#2C594F','002D40', +'#C24704','#D9CC3C','#FFEB79','#A0E0A9','00ADA7', +'#484A47','#C1CE96','#ECEBF0','#687D77','353129', +'#588C7E','#F2E394','#F2AE72','#D96459','8C4646', +'#BAB293','#A39770','#EFE4BD','#A32500','2B2922', +'#6A7059','#FDEEA7','#9BCC93','#1A9481','003D5C', +'#174C4F','#207178','#FF9666','#FFE184','F5E9BE', +'#D5FBFF','#9FBCBF','#647678','#2F3738','59D8E6', +'#DB5800','#FF9000','#F0C600','#8EA106','59631E', +'#450003','#5C0002','#94090D','#D40D12','FF1D23', +'#211426','#413659','#656F8C','#9BBFAB','F2EFDF', +'#EA6045','#F8CA4D','#F5E5C0','#3F5666','2F3440', +'#F2F2F2','#C6E070','#91C46C','#287D7D','1C344D', +'#334D5C','#45B29D','#EFC94C','#E27A3F','DF5A49', +'#705B35','#C7B07B','#E8D9AC','#FFF6D9','570026', +'#F7F2B2','#ADCF4F','#84815B','#4A1A2C','8E3557', +'#1A1F2B','#30395C','#4A6491','#85A5CC','D0E4F2', +'#25064D','#36175E','#553285','#7B52AB','9768D1', +'#004056','#2C858D','#74CEB7','#C9FFD5','FFFFCB', +'#CFCA4C','#FCF5BF','#9FE5C2','#5EB299','745A33', +'#776045','#A8C545','#DFD3B6','#FFFFFF','0092B2', +'#CC3910','#F1F2C0','#CCC59E','#8FA68E','332F29', +'#FF6600','#C13B00','#5E6D70','#424E4F','1B1D1E', +'#690011','#BF0426','#CC2738','#F2D99C','E5B96F', +'#1B1D26','#425955','#778C7A','#F1F2D8','BFBD9F', +'#F6B1C3','#F0788C','#DE264C','#BC0D35','A20D1E', +'#597533','#332F28','#61B594','#E6DEA5','C44E18', +'#3FB8AF','#7FC7AF','#DAD8A7','#FF9E9D','FF3D7F', +'#0F2D40','#194759','#296B73','#3E8C84','D8F2F0', +'#42282F','#74A588','#D6CCAD','#DC9C76','D6655A', +'#002A4A','#17607D','#FFF1CE','#FF9311','D64700', +'#003056','#04518C','#00A1D9','#47D9BF','F2D03B', +'#13140F','#D4FF00','#E4FFE6','#68776C','00D6DD', +'#FCFAD0','#A1A194','#5B605F','#464646','A90641', +'#289976','#67CC8E','#B1FF91','#FFE877','FF5600', +'#302B1D','#3F522B','#737D26','#A99E46','D9CB84', +'#56626B','#6C9380','#C0CA55','#F07C6C','AD5472', +'#32450C','#717400','#DC8505','#EC5519','BE2805', +'#C7B773','#E3DB9A','#F5FCD0','#B1C2B3','778691', +'#E83A25','#FFE9A3','#98CC96','#004563','191B28', +'#3399CC','#67B8DE','#91C9E8','#B4DCED','E8F8FF', +'#1A212C','#1D7872','#71B095','#DEDBA7','D13F32', +'#7D2A35','#CC9258','#917A56','#B4BA6C','FEFFC2', +'#E7E9D1','#D3D4AA','#FCFAE6','#444444','901808', +'#FFFFFF','#AEAEAE','#E64C66','#2D3E50','1BBC9B', +'#E0FFB3','#61C791','#31797D','#2A2F36','F23C55', +'#EB5937','#1C1919','#403D3C','#456F74','D3CBBD', +'#E6DD00','#8CB302','#008C74','#004C66','332B40', +'#14A697','#F2C12E','#F29D35','#F27649','F25252', +'#261822','#40152A','#731630','#CC1E2C','FF5434', +'#261F27','#FEE169','#CDD452','#F9722E','C9313D', +'#5C4B51','#8CBEB2','#F2EBBF','#F3B562','F06060', +'#2F3837','#C5C7B6','#FFF8D3','#4C493E','222028', +'#E3CBAC','#9C9985','#C46D3B','#788880','324654', +'#3F0B1B','#7A1631','#CF423C','#FC7D49','FFD462', +'#14212B','#293845','#4F6373','#8F8164','D9D7AC', +'#98A89E','#BAC0AC','#FAFAC6','#FF4411','D40015', +'#FEFFFF','#3C3F36','#9FB03E','#EBE9DC','72918B', +'#CC6B32','#FFAB48','#FFE7AD','#A7C9AE','888A63', +'#262526','#404040','#8C8979','#F2F2F2','F60A20', +'#00305A','#004B8D','#0074D9','#4192D9','7ABAF2', +'#0C273D','#54D0ED','#FFFEF1','#70B85D','2C5E2E', +'#4C1B33','#EFE672','#98A942','#2D6960','141D14', +'#2F3540','#666A73','#F2EDE4','#D9D1C7','8C8681', +'#0D1F30','#3B6670','#8BADA3','#F0E3C0','DB6C0F', +'#FFBC67','#DA727E','#AC6C82','#685C79','455C7B', +'#092140','#024959','#F2C777','#F24738','BF2A2A', +'#133463','#365FB7','#799AE0','#F4EFDC','BA9B65', +'#C4D4CB','#55665E','#30282A','#542733','E84167', +'#CDDEC6','#4DAAAB','#1E4F6A','#2A423C','93A189', +'#EF5411','#FA5B0F','#FF6517','#FF6D1F','FF822E', +'#41434A','#6E9489','#DEDCC3','#F2F1E9','877963', +'#292929','#2BBFBD','#F2B33D','#F29B30','F22E2E', +'#F2385A','#F5A503','#E9F1DF','#56D9CD','3AA1BF', +'#D5F8B4','#A6E3A8','#8A9A85','#7E566B','422335', +'#3CBAC8','#93EDD4','#F3F5C4','#F9CB8F','F19181', +'#979926','#38CCB5','#EEFF8E','#FFD767','CC2A09', +'#404040','#024959','#037E8C','#F2EFDC','F24C27', +'#94B34D','#D3FF82','#363D52','#121D2B','111B1C', +'#282E33','#25373A','#164852','#495E67','FF3838', +'#313732','#8AA8B0','#DEDEDE','#FFFFFF','F26101', +'#FFFFFF','#E5E1D1','#52616D','#2C343B','C44741', +'#FFF6B8','#ABCCA7','#403529','#7A5E2F','A68236', +'#4F1025','#C5003E','#D9FF5B','#78AA00','15362D', +'#49404F','#596166','#D1FFCD','#A9BD8B','948A54', +'#FF2151','#FF7729','#FFAD29','#FFEBCA','1AB58A', +'#73603D','#BF8A49','#F2CA80','#5E5A59','0D0D0D', +'#3D4C53','#70B7BA','#F1433F','#E7E1D4','FFFFFF', +'#006D8D','#008A6E','#549E39','#8AB833','C0CF3A', +'#BDDFB3','#2BAA9C','#2F2E2E','#0F2625','465F3F', +'#F2F2F2','#BF0404','#8C0303','#590202','400101', +'#76A19A','#272123','#A68D60','#B0C5BB','D9593D', +'#0E3D59','#88A61B','#F29F05','#F25C05','D92525', +'#C1E1ED','#76C7C6','#273D3B','#131A19','E35C14', +'#2D112C','#530031','#820233','#CA293E','EF4339', +'#AF7575','#EFD8A1','#BCD693','#AFD7DB','3D9CA8', +'#D74B4B','#DCDDD8','#475F77','#354B5E','FFFFFF', +'#FFF6C9','#C8E8C7','#A4DEAB','#85CC9F','499E8D', +'#229396','#8BA88F','#C7C5A7','#F0DFD0','F23C3C', +'#57385C','#A75265','#EC7263','#FEBE7E','FFEDBC', +'#96526B','#D17869','#EBAD60','#F5CF66','8BAB8D', +'#0D1C33','#17373C','#2B6832','#4F9300','A1D700', +'#1B2B32','#37646F','#A3ABAF','#E1E7E8','B22E2F', +'#C5D9B2','#53A194','#572C2C','#3D2324','695A3B', +'#425957','#81AC8B','#F2E5A2','#F89883','D96666', +'#002E40','#2A5769','#FFFFFF','#FABD4A','FA9600', +'#FFFEFC','#E2E3DF','#515B5E','#2E3233','CAF200', +'#FFF0A3','#B8CC6E','#4B6000','#E4F8FF','004460', +'#3B596A','#427676','#3F9A82','#A1CD73','ECDB60', +'#F2E6CE','#8AB39F','#606362','#593325','1D1D1F', +'#212B40','#C2E078','#FFFFFF','#BADCDD','547B97', +'#0B3C4D','#0E5066','#136480','#127899','1A8BB3', +'#222130','#464D57','#D4E8D3','#FFFCFB','ED8917', +'#B33600','#FF8A00','#FFC887','#CC5400','B31E00', +'#012530','#28544B','#ACBD86','#FFD6A0','FF302C', +'#2E95A3','#50B8B4','#C6FFFA','#E2FFA8','D6E055', +'#112F41','#068587','#4FB99F','#F2B134','ED553B', +'#202B30','#4E7178','#4FA9B8','#74C0CF','F1F7E2', +'#302B2F','#696153','#FFA600','#9BB58F','FFD596', +'#458C6B','#F2D8A7','#D9A86C','#D94436','A62424', +'#22475E','#75B08A','#F0E797','#FF9D84','FF5460', +'#FFAA5C','#DA727E','#AC6C82','#685C79','455C7B', +'#686E75','#9BAAC1','#82787B','#E4F1DB','AAC19B', +'#F0C755','#E2AD3B','#BF5C00','#901811','5C110F', +'#FFFBDC','#BFBCA5','#7F7D6E','#3F3E37','E5E2C6', +'#BEBEBE','#F1E4D8','#594735','#94C7BA','D8F1E4', +'#1B1E26','#F2EFBD','#B6D051','#70A99A','2F6D7A', +'#F7E4A2','#A7BD5B','#DC574E','#8DC7B8','ED9355', +'#70E8CB','#FFE9C7','#FF5B5B','#545454','2D2D2F', +'#17111A','#321433','#660C47','#B33467','CCBB51', +'#2B2E2E','#595855','#A2ABA5','#CAE6E8','313F54', +'#023B47','#295E52','#F2E085','#FCAB55','EE7F38', +'#302C29','#D1D1BC','#A7C4BB','#6C8C84','466964', +'#212629','#067778','#49B8A8','#85EDB6','D9E5CD', +'#334D5C','#45B29D','#EFC94C','#E27A3F','DF4949', +'#2C3E50','#FC4349','#6DBCDB','#D7DADB','FFFFFF', +'#35262D','#FFFBFF','#E8ECED','#A4B7BB','76A0B0', +'#61E8D2','#FCEEB9','#302F25','#704623','BBE687', +'#E1E6B9','#C4D7A4','#ABC8A4','#375D3B','183128', +'#C98B2F','#803C27','#C56520','#E1B41B','807916', +'#A3D9B0','#93BF9E','#F2F0D5','#8C8474','40362E', +'#524656','#CF4747','#EA7A58','#E4DCCB','A6C4BC', +'#5C2849','#A73E5C','#EC4863','#FFDA66','1FCECB', +'#0EEAFF','#15A9FA','#1B76FF','#1C3FFD','2C1DFF', +'#010000','#393845','#9B96A3','#5C0009','940315', +'#468071','#FFE87A','#FFCA53','#FF893B','E62738', +'#404040','#024959','#037E8C','#F2EFDC','F24C27', +'#FF765E','#C2AE8B','#FCCF65','#FFE5C6','B7BDC4', +'#003647','#00717D','#F2D8A7','#A4A66A','515932', +'#FAFAC0','#C4BE90','#8C644C','#594D37','293033', +'#2B3A42','#3F5765','#BDD4DE','#EFEFEF','E74C3C', +'#3B3B3B','#A8877E','#FFA49D','#FF7474','FF476C', +'#0A3A4A','#196674','#33A6B2','#9AC836','D0E64B', +'#FFA340','#38001C','#571133','#017A74','00C2BA', +'#DCEBDD','#A0D5D6','#789AA1','#304345','AD9A27', +'#588C7E','#F2E394','#F2AE72','#D96459','8C4646', +'#F0E6B1','#B5D6AA','#99A37A','#70584B','3D3536', +'#2F400D','#8CBF26','#A8CA65','#E8E5B0','419184', +'#010712','#13171F','#1C1F26','#24262D','961227', +'#403F33','#6E755F','#AFC2AA','#FFDEA1','E64C10', +'#C74029','#FAE8CD','#128085','#385052','F0AD44', +'#CFF09E','#A8DBA8','#79BD9A','#3B8686','0B486B', +'#E0401C','#E6B051','#272F30','#F7EDB7','9E2B20', +'#FFE2C5','#FFEEDD','#FFDDAA','#FFC484','FFDD99', +'#FFFFE4','#F2E5BD','#B9BF8E','#A69F7C','8C6865', +'#5C8A2D','#AFD687','#FFFFFF','#00C3A9','008798', +'#4F3130','#FF1F3D','#5BE3E3','#FDFFF1','8B9698', +'#D23600','#D95100','#DE6D00','#EE8900','FCA600', +'#FFFFFA','#A1A194','#5B605F','#464646','FF6600', +'#F34A53','#FAE3B4','#AAC789','#437356','1E4147', +'#2A7A8C','#176273','#063540','#E6D9CF','403D3A', +'#21455B','#567D8C','#A59E8C','#8C8372','F2F2F2', +'#012340','#026873','#83A603','#BBBF45','F2F0CE', +'#FDFF98','#A7DB9E','#211426','#6B073B','DA8C25', +'#002F36','#142426','#D1B748','#EDDB43','FFFD84', +'#420000','#600000','#790000','#931111','BF1616', +'#3C989E','#5DB5A4','#F4CDA5','#F57A82','ED5276', +'#23A38F','#B7C11E','#EFF1C2','#F0563D','2E313D', +'#F5ECD9','#2BACB5','#B4CCB9','#E84D5B','3B3B3B', +'#A5EB3C','#60C21E','#159E31','#53DB50','C5FFCB', +'#263138','#406155','#7C9C71','#DBC297','FF5755', +'#0A111F','#263248','#7E8AA2','#E3E3E3','C73226', +'#003B59','#00996D','#A5D900','#F2E926','FF930E', +'#00A19A','#04BF9D','#F2E85C','#F53D54','404040', +'#324152','#47535E','#796466','#C1836A','DEA677', +'#036F73','#84CDC2','#FEF2D8','#F18C79','EF504F', +'#174040','#888C65','#D9CA9C','#D98162','A65858', +'#56797F','#87A0A4','#FCFBDC','#F2DDB6','A6937C', +'#A8BAA9','#FFF5CF','#DBCDAD','#B39C7D','806854', +'#60655F','#AB9675','#FFE0C9','#D4CCBA','CF8442', +'#BDDFB3','#009D57','#2C372E','#0F2925','465F3F', +'#3E3947','#735360','#D68684','#F1B0B0','EBD0C4', +'#0A7B83','#2AA876','#FFD265','#F19C65','CE4D45', +'#FFFFFF','#F4921E','#858585','#C5D2DB','3E6B85', +'#11151E','#212426','#727564','#B9AA81','690C07', +'#000000','#910000','#CBB370','#FFFBF1','21786C', +'#F78F00','#C43911','#75003C','#37154A','0F2459', +'#003354','#91BED4','#D9E8F5','#FFFFFF','F26101', +'#3DA8A4','#7ACCBE','#FFFFF7','#FF99A1','FF5879', +'#64C733','#F0F0F0','#3E879E','#57524D','36302B', +'#343844','#2AB69D','#E65848','#FDC536','FCF2D7', +'#E34517','#F5FF53','#B4E85E','#00BD72','0B4239', +'#A84B3A','#FF9F67','#233138','#FFF7F5','4C646B', +'#59535E','#FAEEFF','#F1BAF3','#5D4970','372049', +'#FF6F22','#D9984F','#FFE8A9','#3E4237','32948A', +'#5D7370','#7FA6A1','#B8D9B8','#D6EDBD','FFF5BC', +'#FFBE00','#FFDC00','#FFD10F','#FFDE20','E8CA00', +'#003840','#005A5B','#007369','#008C72','02A676', +'#E1E6FA','#C4D7ED','#ABC8E2','#375D81','183152', +'#BA2F1D','#FFF8A4','#F5E67F','#264A59','1E2C30', +'#222526','#FFBB6E','#F28D00','#D94F00','80203B', +'#EBD096','#D1B882','#5D8A66','#1A6566','21445B', +'#F00807','#5F6273','#A4ABBF','#CCC9D1','E2E1E9', +'#DFE0AF','#A4BAA2','#569492','#41505E','383245', +'#152737','#2B4E69','#799AA5','#FFFFF0','682321', +'#C44C51','#FFB6B8','#FFEFB6','#A2B5BF','5F8CA3', +'#5ADED4','#4DAAAB','#26596A','#163342','6C98A1', +'#FF5B2B','#B1221C','#34393E','#8CC6D7','FFDA8C', +'#3D4D4D','#99992E','#E6E666','#F2FFBF','800033', +'#242424','#437346','#97D95C','#D9FF77','E9EB9B', +'#FFEBB0','#FFB05A','#F84322','#C33A1A','9F3818', +'#4D2B2F','#E57152','#E8DE67','#FFEFC3','C0CCAB', +'#A82221','#DB5E31','#EDA23E','#F2CB67','BFB840', +'#3B3140','#BFB8A3','#F2E0C9','#F2B9AC','D97E7E', +'#43464D','#9197A6','#D3DCF2','#7690CF','48577D', +'#EFDFBB','#9EBEA6','#335D6A','#D64F2A','7A8A7F', +'#000001','#313634','#C7CECF','#5C0402','941515', +'#334D5C','#45B29D','#EFC94C','#E27A3F','DF5A49', +'#F5F4E1','#D6C9B5','#B4AA97','#D44917','82877A', +'#19162B','#1C425C','#6ABDC4','#F0E4C5','D6C28F', +'#00132B','#7F9DB0','#C5E2ED','#FFFFFF','F95900', +'#1F3642','#6D968D','#B6CCB8','#FFE2B3','56493F', +'#08A689','#82BF56','#C7D93D','#E9F2A0','F2F2F2', +'#DE3961','#A4E670','#FFFFDC','#B3EECC','00ADA7', +'#849972','#D9D094','#A6A23E','#4F2F1D','8F5145', +'#F41C54','#FF9F00','#FBD506','#A8BF12','00AAB5', +'#00585F','#009393','#F5F3DC','#454445','FF5828', +'#FF6138','#FFFF9D','#BEEB9F','#79BD8F','00A388', +'#140B04','#332312','#B59D75','#E3D2B4','FFF7EA', +'#ED3B3B','#171F26','#77B59C','#F2E7B1','635656', +'#46594B','#A6977C','#D9B384','#734F30','260B01', +'#CCB8A3','#FF8FB1','#FFF5EA','#4E382F','B29882', +'#B70000','#FFFFFF','#FFCA3D','#94C4F4','0092B3', +'#053B44','#06736C','#A53539','#B9543C','EAD075', +'#E8C1B9','#FFB3AB','#FFCAB8','#E8B69C','FFCEAB', +'#E7F2DF','#69043B','#59023B','#231E2D','161726', +'#E82B1E','#E5DEAF','#A0B688','#557A66','453625', +'#F1E6D4','#BA3D49','#791F33','#9F9694','E3E1DC', +'#CED59F','#F1EDC0','#B1BEA4','#647168','282828', +'#2C3E50','#E74C3C','#ECF0F1','#3498DB','646464', +'#DE7047','#FFDE8D','#FFFFFF','#CDDE47','528540', +'#8EAB99','#40232B','#D95829','#D97338','DEC085', +'#E9662C','#EBAF3C','#00AC65','#068894','2B2B2B', +'#46483C','#A0AA8F','#EBE3CB','#FFFFFF','F26101', +'#170F0E','#290418','#505217','#FFD372','FFF1AF', +'#263545','#C4273C','#D7DADB','#6DBCDB','FFFFFF', +'#DCFAC0','#B1E1AE','#85C79C','#56AE8B','00968B', +'#075807','#097609','#70AF1A','#B9D40B','E5EB0B', +'#521000','#712800','#744E1D','#879666','F1D98C', +'#261F26','#3F3B40','#6C7367','#BFBF8A','F2E086', +'#2C3E50','#FC4349','#D7DADB','#6DBCDB','FFFFFF', +'#506D7D','#94CCB9','#FFECA7','#FFB170','F07D65', +'#3F4036','#8DA681','#F2E1C2','#BF2806','8C1D04', +'#990700','#CC542E','#FF964F','#FFCB7C','787730', +'#195073','#7F8C1F','#EE913F','#F2E5BD','9FD7C7', +'#1B3E59','#F2F0F0','#FFAC00','#BF0404','730202', +'#EA6045','#F8CA4D','#F5E5C0','#3F5666','2F3440', +'#F95759','#FDA099','#FFFFFF','#D9F3CB','8AC2B0', +'#265573','#386D73','#81A68A','#9FBF8F','D4D9B0', +'#E1DA36','#FFEA1B','#6FE4DA','#1DB0BC','007BBC', +'#013859','#185E65','#F9CC7F','#F15C25','9E1617', +'#36CC7C','#D6FFBE','#94D794','#228765','77A668', +'#94201F','#D4421F','#478A80','#D9E061','F08835', +'#F16233','#00B5B5','#F0F0F0','#3E4651','5C6D7E', +'#2E806C','#76CC99','#E0FFED','#FF5F3A','D2413C', +'#00393B','#00766C','#44A18E','#E5EDB6','F6695B', +'#734854','#F2F2E9','#D9D7C5','#A69580','736766', +'#03497E','#0596D5','#9DEBFC','#8D7754','FEB228', +'#F0E14C','#FFBB20','#FA7B12','#E85305','59CC0D', +'#FE4365','#FC9D9A','#F9CDAD','#C8C8A9','83AF9B', +'#00557C','#186D94','#3488AD','#81C1DC','BBE5F3', +'#DEE8D7','#918773','#420A1A','#240001','4D493A', +'#FFFFFF','#CAC535','#97AF25','#158471','41342C', +'#041F3D','#0B2E41','#165751','#448C61','9AC16D', +'#FA8C01','#FF6405','#577700','#082400','A0A600', +'#78C0F9','#FFDDCE','#FFFFFF','#FFDBE6','FE86A4', +'#351330','#CC2A41','#E7CAA4','#759A8A','524549', +'#02151A','#043A47','#087891','#C8C8C8','B31D14', +'#F34A53','#FAE3B4','#AAC789','#437356','1E4147', +'#58838C','#DAD7C7','#BF996B','#BF5841','A61C1C', +'#556354','#E68F0D','#8C948A','#495450','42423F', +'#323640','#5B6470','#8C94A1','#BDC7D6','DFE2FF', +'#FF0000','#FF950B','#2FA88C','#DEEB00','4B2C04', +'#0F3D48','#174C5B','#366774','#ECECE7','E96151', +'#3DBB7E','#A3CD39','#FBAC1D','#F96C1E','EE4036', +'#23363B','#A44F3F','#F8983D','#8D9151','BBC946', +'#4B5657','#969481','#D2C9B0','#F4E3C1','B6B835', +'#E8980C','#B1F543','#F2FF00','#FF5E00','59BBAB', +'#849696','#FEFFFB','#232D33','#17384D','FF972C', +'#555555','#7BB38E','#F4F1D7','#F8AB65','F15C4C', +'#1D3C42','#67BFAD','#F2EC99','#F2C48D','F25050', +'#334D5C','#45B29D','#EFC94C','#E27A3F','DF4949', +'#B8E1F2','#249AA7','#ABD25E','#F8C830','F1594A', +'#FDEDD0','#BCF1ED','#FF634D','#FD795B','FFF0AA', +'#FFFFFF','#E5E1D1','#52616D','#2C343B','C44741', +'#FFFFF1','#D5FF9B','#8FB87F','#5A7B6C','374E5A', +'#010340','#0E1E8C','#0003C7','#1510F0','1441F7', +'#002A4A','#17607D','#FFF1CE','#FF9311','E33200', +'#871E31','#CCC097','#9E9D7B','#687061','262626', +'#F16663','#F48D6C','#F2E07B','#8ABE9B','4A6D8B', +'#001F11','#204709','#0C8558','#FFD96A','FF4533', +'#1D1626','#F2E0BD','#BFAA8F','#8C786C','594C4C', +'#685D47','#913420','#1E2729','#C1D9C5','FEEFB1', +'#1D7561','#FC8448','#FF4138','#A8282B','38141B', +'#BF0633','#FF484E','#FF9273','#D1D0B4','E5ECED', +'#8E9E63','#E6DBB0','#F5EED7','#C4BCA0','176573', +'#665446','#809994','#AECCB6','#DEF2C4','E6683F', +'#3D0D26','#660A3E','#891C56','#B0276F','C93482', +'#082136','#00294D','#004B8D','#0068C4','2998FF', +'#3C4631','#9A746F','#F8A2AB','#F1C6B3','EAE9C0', +'#FF534E','#FFD7AC','#BED194','#499989','176785', +'#006D80','#BDA44D','#3C2000','#84CECC','78A419', +'#352C2B','#3C555C','#9E9657','#FFEBCD','CD5510', +'#2C3E50','#FC4349','#6DBCDB','#D7DADB','FFFFFF', +'#523631','#D1BE91','#605E3A','#4D462F','592F39', +'#18293B','#5B5A56','#F2DEA0','#D0B580','FFFBFF', +'#C8DBB6','#ECEBB7','#CCC68A','#B8B165','827A5D ', +'#7DA88C','#EBE9A0','#BED24B','#859132','35323C', +'#E8574C','#F27B29','#E6A51B','#D9CC3C','399977', +'#324032','#B7C22C','#FFFFE1','#22A8B5','2A3F42', +'#B3A589','#FFB896','#FFF9B1','#9AB385','11929E', +'#272433','#343F4F','#3D6066','#77994D','B2D249', +'#250701','#6D4320','#B0925F','#E7DEC0','82ABB8', +'#023550','#028A9E','#04BFBF','#EFEFEF','FF530D', +'#594732','#40342A','#7A422E','#D4CA9A','EDE5AE', +'#013C4D','#BA5B22','#DB913C','#F0B650','FAD46B', +'#143840','#5C6B63','#A69E89','#E0C297','D96523', +'#3FB8AF','#7FC7AF','#DAD8A7','#FFB38B','FF3F34', +'#CA3995','#F58220','#FFDF05','#BED73D','61BC46', +'#FFE1D0','#FFBFB4','#FF837E','#FF4242','BF1616', +'#C4EEFF','#7BA32D','#094201','#A41717','C48726', +'#001325','#187072','#90BD90','#D7D8A2','F2E4C2', +'#1A4F63','#068587','#6FB07F','#FCB03C','FC5B3F', +'#97B350','#333230','#736D61','#BAAB90','FFE5BA', +'#403D33','#807966','#CCC2A3','#8C0000','590000', +'#5F8A42','#86AD59','#F6FAA1','#F28410','D66011', +'#BF355D','#ED8168','#FAB66A','#F2DC86','83BFA1', +'#E1F03E','#FFBA14','#DB3A0F','#A1003D','630024', +'#212226','#45433F','#687067','#BDBB99','F0EAC3', +'#FE4365','#FC9D9A','#F9CDAD','#C8C8A9','83AF9B', +'#293B47','#5F7A87','#FFFFFF','#CBFF48','00ADA9', +'#282A33','#697371','#FFE7A6','#F5BA52','FA8000', +'#0C304A','#2B79A1','#F3F4F1','#85A71E','BFD841', +'#008B83','#4DAE83','#A0AE79','#FFE499','FF665E', +'#5D7359','#E0D697','#D6AA5C','#8C5430','661C0E', +'#324452','#97BDBF','#F2DFBB','#F28705','BF3604', +'#EEEFB9','#6ACFAE','#369C93','#232928','B03831', +'#332F45','#015770','#2A8782','#9FD6AE','FFFED2', +'#2B2830','#5C504F','#ABAB8E','#D9D7A3','C7BE88', +'#DC941B','#EDC266','#B6952C','#E1D3A6','E9A119', +'#00305A','#00448D','#0074D9','#4192D9','7ABAF2', +'#344459','#485F73','#5DA6A6','#A9D9CB','F2EAD0', +'#060719','#4D1B2F','#9E332E','#EB6528','FC9D1C', +'#96CEB4','#FFEEAD','#FF6F69','#FFCC5C','AAD8B0', +'#05F2F2','#04BFBF','#EEF1D9','#A60201','7E100E', +'#E6F1F5','#636769','#AAB3B6','#6E7476','4B4E50', +'#DA0734','#F1A20D','#4AABB1','#FCF3E7','3F1833', +'#202D44','#FC4349','#6DBCDB','#D7DADB','FFFFFF', +'#CC3B37','#398899','#FFFCE8','#FF857F','CCC1A3', +'#5DBEA9','#EFEDDF','#EF7247','#4E3F35','D1CBBA', +'#FFC62D','#E49400','#DD5200','#EFE38A','91B166', +'#B67D14','#F2921F','#F0B23E','#A62409','441208', +'#C71B1B','#D6BA8A','#017467','#E08F23','0B0D0C', +'#474143','#A69E9D','#E7E2DA','#FFFFFF','E7E8E7', +'#435772','#2DA4A8','#FEAA3A','#FD6041','CF2257', +'#6DD19D','#99E89D','#D0E8A1','#FFF9C0','D40049', +'#FAF1D5','#DEC9AC','#CCA18B','#11282D','A5C4BB', +'#000000','#141414','#1C1919','#1A1716','24201F', +'#D5D8DD','#5CA2BE','#135487','#2A4353','989DA4', +'#73161E','#BF0F30','#BFB093','#037F8C','0A2140', +'#195962','#F56F6C','#FFFFFF','#252932','191C21', +'#F8EFB6','#FEBAC5','#6CD1EA','#FACFD7','C2EAE9', +'#91D6BC','#768C6A','#755F31','#B37215','FFBA4B', +'#F2E6BB','#DD4225','#202724','#63BD99','F8FDD8', +'#762B1B','#807227','#CCBF7A','#FFEF98','60B0A1', +'#707864','#C1D74E','#F5FF7C','#DFE6B4','A6B89C', +'#FFF3D2','#97B48F','#E87657','#FF9B6F','E8D495', +'#33262E','#733230','#CC5539','#E6D27F','86A677', +'#122430','#273E45','#FFFCE2','#EBD2B5','E63531', +'#30394F','#FF434C','#6ACEEB','#EDE8DF','FFFBED', +'#0A3A4A','#196A73','#32A6A6','#A1BF36','C8D94A', +'#FFF7CC','#CCC28F','#70995C','#33664D','142933', +'#43464D','#9197A6','#D3DCF2','#7690CF','48577D', +'#DFE0AF','#A4BAA2','#569492','#41505E','383245', +'#B52841','#FFC051','#FF8939','#E85F4D','590051', +'#473C35','#A36D5C','#9C968B','#D9CEAD','8A866A', +'#DB4C39','#2D3638','#109489','#44D487','D0DB86', +'#6F8787','#AEC2AE','#E6DFAE','#B0B57B','888F51', +'#C8385A','#FFCF48','#ECEABE','#1FCECB','1CA9C9', +'#42282E','#75A48B','#D9CFB0','#DC9B74','D6665A', +'#362F2D','#4C4C4C','#94B73E','#B5C0AF','FAFDF2', +'#98293A','#B14A58','#C86C6B','#DE9D76','EFC77F', +'#C1D301','#76AB01','#0E6A00','#083500','042200', +'#453F22','#7A6B26','#CCAD5C','#A1191F','4E1716', +'#541E32','#8E3557','#88A33E','#C2BD86','F7F2B2', +'#2B1B2E','#54344D','#FFFFD6','#B89E95','6E444F', +'#6EC1A5','#9FBEA6','#F5D3A3','#FF9F88','FB7878', +'#2F252C','#D3CCB2','#99AD93','#6E6751','5C3122', +'#BE333F','#F2E9CE','#C8C5B1','#939F88','307360', +'#F0F1F2','#232625','#647362','#B3D929','D2D9B8', +'#FA2B31','#FFBF1F','#FFF146','#ABE319','00C481', +'#09455C','#527E7C','#F5FFCC','#E0EB6E','C4D224', +'#F2DA91','#F2B950','#F29D35','#D96704','BF4904', +'#A2CFA5','#E0E7AB','#F5974E','#E96B56','D24344', +'#150033','#310D42','#5C2445','#AB6946','FFCE4C', +'#23A38F','#B7C11E','#EFF1C2','#F0563D','2E313D', +'#FF2468','#E0D4B1','#FFFFE3','#00A5A6','005B63', +'#65A683','#218777','#3F585F','#47384D','F53357', +'#000623','#28475C','#4A6C74','#8BA693','F0E3C0', +'#E65322','#D19552','#B8BF73','#B6DB83','FFF991', +'#112F41','#068587','#6FB07F','#FCB03C','FC5B3F', +'#C89B41','#A16B2B','#77312B','#1C2331','152C52', +'#C24366','#D9C099','#FFF8D8','#A8E0BA','00ADA7', +'#CC0000','#006600','#FFFFEC','#9C9178','6C644F', +'#3D0319','#720435','#C1140E','#FC5008','32241B', +'#CFC7A4','#5A9E94','#005275','#002344','A38650', +'#FFEBC3','#CC3A00','#FF3600','#FF851B','800C00', +'#EFC164','#F3835D','#F35955','#286275','00434C', +'#E9F29D','#B7C29D','#878E8F','#67617A','51456B', +'#445859','#03A696','#49C4BE','#F1F2E4','FF7746', +'#FA726C','#FFD794','#BAD174','#3BA686','5F6F8C', +'#4D2B1F','#635D61','#7992A2','#97BFD5','BFDCF5', +'#CC4D00','#E6CF73','#668059','#264D4D','00CCB3', +'#4385F5','#DC4437','#FCBE1F','#109D59','FFFFFF', +'#271F2E','#A4A680','#F2EBC9','#D9B166','A66B38', +'#0B2C3C','#FF6666','#DADFE1','#FFFFFF','444444', +'#CFF09E','#A8DBA8','#79BD9A','#3B8686','0B486B', +'#302B26','#A6B827','#EDE9DD','#98D3D4','594E7A', +'#4B0505','#720707','#BFB694','#004659','00292B', +'#B52C38','#EBD1B0','#536682','#D9964B','DE6846', +'#F2F1DF','#F2B705','#F2C84B','#BF820F','734002', +'#26140C','#3D2216','#784E3D','#AB8574','D6BCB1', +'#26221D','#8C2C0F','#E6E5B8','#BFB38D','402D1F', +'#1F8181','#F2BC79','#F28972','#BF1B39','730240', +'#002635','#013440','#AB1A25','#D97925','EFE7BE', +'#8EC447','#FFFFFF','#96D3D4','#636466','2D2D2E', +'#2D1E1E','#4B3C37','#96A576','#CDE196','FFFFBE', +'#F06060','#FA987D','#F7F2CB','#72CCA7','10A296', +'#1D8281','#44BF87','#FBD258','#F29A3F','DB634F', +'#DEDE91','#EF9950','#F34E52','#C91452','492449', +'#6D8EAD','#1F3447','#1A0B07','#362416','CFCDB4', +'#00CD73','#008148','#2D9668','#3ECD8E','004E2C', +'#3D8080','#628282','#858383','#A38282','C28080', +'#475159','#839795','#B2BDB7','#CCC9C0','F2F2F2', +'#0E6870','#C6B599','#C65453','#FFDDB4','EDAA7D', +'#CEF0B7','#A8DBA8','#79BD9A','#3B8686','0B486B', +'#292C44','#FF5349','#F0F0F1','#18CDCA','4F80E1', +'#272A2B','#383737','#473B39','#692B28','940500', +'#D6C274','#DB9E46','#25706B','#3D2423','AB362E', +'#FFA68F','#FF4867','#FFF9C8','#B5EBB9','18B29D', +'#A1A16A','#727D59','#366353','#133C40','03212E', +'#D45354','#A9DC3A','#2FCAD8','#818B85','CDCDC1', +'#F14B6A','#3D3C3E','#22BDAF','#BAD7D4','F4F4F4', +'#FFE2C5','#FFEEDD','#FFDDAA','#FFC484','FFDD99', +'#9FFF4A','#1ABF93','#087363','#004040','2F1933', +'#FFDB97','#B28F4E','#FFFDFB','#466CB2','97BBFF', +'#991C00','#E09A25','#FFFCDB','#008B83','262B30', +'#44281A','#00ACAE','#F5EFD5','#F37606','EE4717', +'#FF5952','#FCEEC9','#96D6D9','#4FAAC9','176075', +'#5C4B51','#8CBEB2','#F2EBBF','#A5C88F','EF847B', +'#105F73','#F7F3B2','#C6CC33','#F28322','CC5404', +'#137072','#56B292','#B7F5AB','#FBFFC0','BF223D', +'#E3F23E','#6C821C','#A6A53F','#E0E0AC','33302E', +'#00215E','#003CAA','#1967F7','#5E4000','AA7400', +'#273A3D','#54695C','#AD9970','#FFBF87','FF8F60', +'#FFAA00','#C2B93E','#808F5D','#576157','302F30', +'#BE1405','#F2DCAC','#AABEAA','#736E41','413C2D', +'#6B1229','#C76A61','#FAB99A','#F7D9B5','CCB1A7', +'#2D9993','#58B3A3','#83BFA3','#B0D9A8','FFFCB6', +'#334D5C','#45B29D','#EFC94C','#E27A3F','DF5A49', +'#F30B55','#010326','#012840','#54717F','F2E6CE', +'#2A3411','#73662C','#BC9847','#FFDFB2','6B0031', +'#637D74','#403D3A','#8C3B3B','#AB6937','D4A960', +'#010A26','#011640','#B6D6F2','#FFFFFF','E83338', +'#924847','#EB986C','#E4C678','#9C7885','372C2C', +'#022440','#3F95AA','#4EC6DE','#EAE2DF','F7572F', +'#2B1D2E','#323657','#076473','#54B087','D6F567', +'#052229','#004043','#BCC373','#E3FF55','D0D90C', +'#4C514A','#907A62','#879796','#755854','B09880', +'#1D2939','#1CAF9A','#FFFFFF','#EE4F4B','D1DC48', +'#004B67','#41CCB4','#FFEA95','#FF7C5D','C70151', +'#C0272D','#FCFBE7','#9FD3DA','#008C9A','05484F', +'#213130','#FF5E3D','#C9C83E','#FDFFF1','559398', +'#B1E4FC','#366D78','#39D5F1','#FFFFFF','D9FF03', +'#DECE6C','#FCF9B6','#BFE3B5','#5D826E','262E2B', +'#520A17','#668F91','#F5E6AC','#AB8E5B','52301C', +'#2D3032','#DD5F18','#FBA922','#F7F7F7','404333', +'#0C2538','#2B434F','#638270','#BCC98E','EDE059', +'#E85066','#F28E76','#E6CEB0','#5A8C81','382837', +'#BF2633','#A6242F','#D9CEAD','#C0B18F','011C26', +'#002A4A','#17607D','#FFF1CE','#FF9311','E33200', +'#0A8B91','#485956','#C4B98F','#FFF9BC','EEDF2E', +'#B89A7B','#9BBAAC','#F2D649','#D95D50','DBDBDB', +'#BD7938','#8D4421','#643001','#532700','3A1C00', +'#E1E6FA','#C4D7ED','#ABC8E2','#375D81','183152', +'#2E4259','#F7483B','#ECF0F1','#03C8FA','737373', +'#364656','#5D6B74','#94A4A5','#F2F5E9','FF8C31', +'#3E5916','#93A605','#F28705','#F25C05','E5EFFA', +'#248077','#74AD8D','#C82754','#F7BB21','F9E2B7', +'#20736A','#8BD9CA','#B1D95B','#93A651','403E34', +'#D74B4B','#DCDDD8','#475F77','#354B5E','FFFFFF', +'#252F33','#415C4F','#869C80','#93C2CC','CEEAEE', +'#012840','#79C7D9','#9BF2EA','#497358','9DBF8E', +'#EE7E94','#F8B4C4','#C7CAC9','#D8505C','41424', +'#282828','#505050','#FFFFFF','#2DCEDB','F20000', +'#004358','#1F8A70','#BEDB39','#FF5347','FD7400', +'#470C3B','#802F56','#C0576F','#E38679','FFBD83', +'#573328','#B05A3A','#FF8548','#29332E','0F1B1C', +'#461F2D','#E1FFBB','#BAD47F','#849C23','52533F', +'#333A40','#4C5E5E','#ADD0E5','#CDE4FF','729EBF', +'#DE5605','#F7A035','#B1DEB5','#EFECCA','65ABA6', +'#76D6D2','#F9E270','#EF6F56','#F4EED8','596B56', +'#403E3F','#F2F2F2','#D9D9D9','#9DAABB','8C8C8C', +'#059E9A','#F4F2ED','#F5A243','#DB3E3B','585857', +'#FFBF41','#EE8943','#C02221','#FFF4D3','249CA9', +'#024E76','#39A6B2','#FCE138','#F5B824','F08106', +'#FF0067','#FF3D6A','#E7FF04','#9CFF00','56FF00', +'#003540','#0D3F40','#487360','#8FA671','F2D795', +'#FF493C','#FFFFFF','#B3ECEF','#31C4F5','ADEB41', +'#244358','#4A8B87','#7CBCAE','#F0D4AF','C5252B', +'#EA5930','#F8AF1E','#F5E5C0','#097380','372560', +'#A1DBB2','#FEE5AD','#FACA66','#F7A541','F45D4C', +'#2C4A47','#6C9A7F','#BB523D','#C89D11','81810B', +'#F0F1F2','#232625','#647362','#FF5629','D2D9B8', +'#7C9B5F','#B8D197','#E3FFF3','#9BDEC7','568F84', +'#E54E45','#DBC390','#F2F2EF','#13A3A5','403833', +'#77A7FB','#E57368','#FBCB43','#34B67A','FFFFFF', +'#001A2E','#8F0000','#FFFFFF','#8A874B','41594F', +'#312F40','#49A69C','#EFEAC5','#E89063','BF5656', +'#047C8C','#018B8D','#F3BF81','#F49B78','F1706D', +'#00303E','#7096AD','#C1D1DE','#FFF9EF','EC4911', +'#2D6891','#70A0BF','#F5EEDC','#DC4C1A','F0986C', +'#040002','#3D1309','#E8B96A','#BC5D15','5C0F00', +'#8B929C','#5E6070','#514454','#3B313D','FF2479', +'#142D58','#447F6E','#E1B65B','#C8782A','9E3E17', +'#22104D','#2D1E5E','#483A85','#7067AB','A49CFA', +'#919C86','#9E373E','#2B2E36','#D1B993','C45A3B', +'#332F45','#015770','#2A8782','#9FD6AE','FFFED2', +'#37C78F','#FEE293','#FF4D38','#CC2249','380C2A', +'#47282C','#8C8468','#C9B37F','#DBDAB7','C4C49C', +'#14191A','#2D2B21','#A69055','#CCB287','FFB88C', +'#F5E3CD','#696158','#B6A898','#877D71','504A43', +'#005151','#009393','#F56200','#454445','969692', +'#D95F47','#FFF2C1','#80A894','#106153','072C36', +'#9E352C','#E6E8A9','#93C28C','#2E5A5C','2B2623', +'#03013A','#334A94','#6B9EDF','#83C3F2','99E6FF', +'#372A26','#4D4D4D','#6DA0A7','#9ED5A8','C7F5FF', +'#03658C','#022E40','#F2B705','#F28705','F25C05', +'#FF3B16','#E87826','#E8BA4A','#80A272','003045', +'#00748E','#E3DFBB','#F4BA4D','#E3753C','DA3B3A', +'#25401E','#56732C','#84A63C','#B8D943','EAF2AC', +'#449BB5','#043D5D','#EB5055','#68C39F','FFFCF5', +'#108F97','#FF8B6B','#FFE39F','#16866D','103636', +'#1A4F63','#068F86','#6FD57F','#FCB03C','FC5B3F', +'#381C19','#472E29','#948658','#F0E99A','362E29', +'#D7E8F7','#BBD0E3','#9CB7CF','#6A8BAB','375D81', +'#0F1C28','#136972','#67BFA7','#F3CF5B','F07444', +'#FFFFFF','#4EA9A0','#969514','#FE9C03','FCDE8E', +'#2F2D30','#656566','#65537A','#51386E','2A2333', +'#4C2916','#F05A28','#FBAF3F','#38B449','FFFFFF', +'#132537','#006C80','#EBCAB8','#FE8315','FA3113', +'#ECEEE1','#A8DACF','#F05B4F','#D8403A','221E1F', +'#00305A','#004B8C','#0074D9','#4192D9','7ABAF2', +'#72CF3F','#85FF00','#23E000','#2FB81B','00FF1C', +'#45CEEF','#FFF5A5','#FFD4DA','#99D2E4','D8CAB4', +'#FF5B00','#A1716C','#728296','#439AAB','00CABD', +'#EB6C2D','#D9C8A2','#939C80','#496158','232F38', +'#D94214','#FFF2C1','#80A894','#52736B','093844', +'#4D1B2F','#9E332E','#EB6528','#FC9D1C','FFCA50', +'#FFEEB0','#9AE8A4','#C7C12D','#F76245','ED1C43', +'#FFFAED','#D4DBFF','#879AC9','#242942','FF8800', +'#022840','#013440','#517360','#9DA67C','F2DC99', +'#331A0F','#519994','#BA4B3C','#EEDDAA','789F63', +'#577867','#EDCE82','#D68644','#AB3229','662845', +'#435A66','#88A6AF','#F5F2EB','#D9CDB8','424342', +'#FF8840','#958D4F','#737B55','#595540','513E38', +'#9D805A','#EBC99D','#FFE6C5','#9DCEEA','4B809E', +'#272D40','#364659','#55736D','#9DBF8E','D0D991', +'#23A38F','#B7C11E','#EFF1C2','#F0563D','2E313D', +'#98C000','#3D4C53','#EA2E49','#FFE11A','0CDBE8', +'#A20E30','#E93C4F','#DCDCD4','#ADBCC3','2D4255', +'#1C2640','#263357','#384C80','#4E6AB3','5979CD', +'#D94214','#FFF2C1','#80A894','#52736B','093844', +'#3B596A','#427676','#3F9A82','#A1CD73','ECDB60', +'#1E1E1F','#424143','#67666A','#807F83','CBC9CF', +'#E04946','#3BA686','#B6D15D','#FFD495','FA847E', +'#FFEBB0','#FFB05A','#F84322','#C33A1A','9F3818', +'#FFA136','#FF814A','#E6635A','#785D6B','534557', +'#CDCF91','#EBEACC','#D6D5B8','#6D7D80','41545E', +'#011526','#011C40','#4E8DA6','#F2EA79','F2B33D', +'#353230','#3F4E51','#7B8F70','#99B2BE','F6F4EA', +'#063559','#0D8C7F','#8FBF4D','#F2D13E','D95929', +'#158000','#199900','#20BF00','#24D900','29FF00', +'#0B0D0E','#137074','#7EB7A3','#F1DDBB','EC6766', +'#02151A','#043A47','#087891','#C8C8C8','B31D14', +'#59361F','#5C992E','#A3CC52','#E6E673','FF5933', +'#FE4365','#FC9D9A','#F9CDAD','#C8C8A9','83AF9B', +'#4B1E18','#F9E5C2','#BBB082','#829993','4F5D4E', +'#032843','#1F595B','#508C6D','#71A670','A6DB89', +'#191724','#4C4547','#8C594E','#D18952','FDB157', +'#191919','#182828','#60702D','#AAB232','E6FA87', +'#212A3F','#434F5B','#F2F2F2','#8AB839','2E2E2E', +'#004158','#026675','#038B8B','#F1EEC9','F09979', +'#023059','#3F7EA6','#F2F2F2','#D99E32','BF5E0A', +'#F21E52','#FFFFFF','#3D3B42','#0C6F73','63CFD4', +'#452743','#E7635E','#F8E9A8','#89E0AD','00928C', +'#FAAD63','#D1714D','#785E48','#39403B','3D1C24', +'#4C0016','#FFF7EB','#DCCEA7','#A17345','104F53', +'#BF2431','#F24150','#2A4557','#3B848C','EFF2E4', +'#3B3013','#8F6031','#E88833','#9C0C0A','FDF3C1', +'#1E2422','#88BEB1','#FF006D','#DAFFFF','718A94', +'#F1F4F7','#AF9F7B','#775E43','#40413C','251C17', +'#00182E','#0C6BA1','#D4D6D4','#FFFDEB','FF7500', +'#FFAB4A','#CCBAAB','#1E2129','#3D5E6E','47A3A3', +'#66B3A7','#C0D4B6','#EEF0BD','#F0563D','2C2F3B', +'#332525','#907465','#EDC5B5','#878C6D','63674A', +'#F04C16','#DBDBD0','#EDBD1F','#4CB09C','313B4A', +'#2B211D','#611C26','#C5003E','#8EB7A8','F1E4B7', +'#1A1F2B','#30395C','#4A6491','#85A5CC','D0E4F2', +'#03497E','#0596D5','#9DEBFC','#999999','FE4B28', +'#2F4159','#465E73','#88A649','#F2ECE4','D98841', +'#323A46','#22282F','#EB4A33','#FFFFFF','E9F0F5', +'#2C3E50','#FC4349','#6DBCDB','#D7DADB','FFFFFF', +'#F29727','#E05723','#B0382F','#982E4B','713045', +'#4D584A','#465943','#428552','#3E754E','4C694B', +'#47191C','#59574B','#829690','#B5B09A','E1E3CB', +'#1D5123','#B1C661','#FFDA68','#FE9257','F64448', +'#59323C','#260126','#F2EEB3','#BFAF80','8C6954', +'#4E0805','#9E0522','#FFF4D4','#B8C591','447622', +'#424862','#FB9A63','#BFC4D5','#F6FBF4','FEBC98', +'#FF2468','#E0D4B1','#FFFFE3','#00A5A6','005B63', +'#1C2F40','#4C6173','#8094A6','#D9D1BA','F2E9D8', +'#DFD7B7','#EB7707','#5C5445','#3B2323','9CBFC7', +'#262E3B','#9C8878','#CFCAAA','#FBF8FF','992435', +'#FFBC67','#DA727E','#AC6C82','#685C79','455C7B', +'#404A69','#516C8A','#8AC0DE','#FFFFFF','FFAC00', +'#485B61','#4B8C74','#74C476','#A4E66D','CFFC83', +'#A31180','#C42795','#DE52B4','#EA88CE','FFBFE5', +'#E64D2E','#FFF5F1','#7893AD','#576B9C','2D2A52', +'#BF0436','#8C0327','#590219','#F2CBA1','8C674C', +'#CF5B6F','#FFF8C8','#CAD9B1','#8FB3A0','648991', +'#341D44','#744D90','#BB8CDD','#3E4417','88904D', +'#00293E','#003D4E','#006269','#00918F','00BAB5', +'#43212E','#D9666F','#F2D57E','#A9A688','516057', +'#2A3B30','#ABFFD1','#EBFFF5','#9DFEFF','273B40', +'#A63343','#E65159','#F5E9DB','#F4F7CF','BAD984', +'#1BA68C','#54BFAC','#F2EDA7','#F2E530','D94625', +'#1A2A40','#3F7369','#F2DEA0','#CE5251','EA895E', +'#1E9382','#70A758','#EFF1C2','#F0563D','2E313D', +'#A991E8','#FFB4BB','#ACF7FF','#A2E891','FFEDAE', +'#225B66','#17A3A5','#8DBF67','#FCCB5F','FC6E59', +'#282624','#BFB7AA','#403D39','#807A71','ABA398', +'#334D5C','#45B29D','#EFC94C','#E27A3F','DF4949', +'#440008','#605521','#988432','#D9A54E','9E3711', +'#649670','#36291E','#69AD6C','#92E67C','C5FF84', +'#42342C','#738076','#B2B39B','#DFE5E1','294359', +'#1A3838','#3F7A51','#82A352','#D1C062','FFBE59', +'#7D8C22','#B3BF67','#F2E49B','#D9DFF4','6791BF', +'#8A7D6D','#2D2D38','#E86E48','#FFFFE8','9CC9C9', +'#CFC949','#FFF5BF','#A9E6C4','#6AB39F','665841', +'#A1172D','#FDFFBA','#A7DB9E','#275C57','1F1B19', +'#FF6C0D','#F29E00','#E6C10F','#44996F','216273', +'#2C3E50','#FA4248','#D7DADB','#6DBCDB','FFFFFF', +'#627369','#99B397','#E2F2C6','#91CCAD','376266', +'#04496E','#66CAFF','#A3FC7E','#70D44A','2C6B0F', +'#1BA68C','#97BF3F','#F2ECD8','#F2B035','F2522E', +'#A2D9B1','#7CBF9E','#F2F1B9','#8C8575','193741', +'#024959','#037E8C','#F2EFDC','#E74C30','363636', +'#212625','#9CA6A2','#D0D9D6','#BF0404','C2C6AF', +'#00FFFF','#00FF00','#FFFF00','#FF5100','FF007C', +'#212629','#CDCF19','#FFF77D','#96C4AB','CF2A56', +'#CFF9FF','#BFC7BB','#787051','#332730','57324F', +'#98CACB','#FDEFBE','#F0542B','#736E5B','ABA68E', +'#F2F1EB','#BFB9A4','#262222','#802A30','8C0303', +'#65356B','#AB434F','#C76347','#FFA24C','519183', +'#78BF82','#A4D17C','#CFD96C','#EBD464','FFD970', +'#806265','#FFA256','#F7DD77','#E0D054','ABA73C', +'#8F323C','#123943','#80BDDB','#4189AB','C98127', +'#683820','#8C9A89','#E7D6A2','#BEAA65','9A8234', +'#021B21','#032C36','#065F73','#E8DFD6','FF2A1D', +'#2D6C73','#3FA693','#B4D9CB','#9ABF49','C6D93B', +'#141F26','#2B4040','#405950','#A69E86','F2D9BB', +'#4A8279','#003330','#610400','#003B06','02730F', +'#69B5E1','#D4E4F5','#EAF2F8','#BEDBED','000000', +'#893660','#EF7261','#68D693','#A0D7E2','299CA8', +'#073A59','#2D9AA6','#F2E2DC','#F23322','A61B1B', +'#2A3A48','#3E6372','#B2D4DC','#FAFAFF','FF6900', +'#F3BD8D','#F1A280','#BE6D6B','#704A5B','3E263C', +'#1C2742','#3C91C7','#5A9ABE','#95C5DE','E0EEFB', +'#426261','#465A59','#577573','#739A97','9AC1C0', +'#002A4A','#17607D','#FFF1CE','#FF9311','D64700', +'#589373','#BFBD99','#F2D6B3','#C2512F','241E1E', +'#1F518B','#1488C8','#F7E041','#E2413E','B5292A', +'#549494','#E85649','#232C2E','#E6E8D2','706558', +'#392133','#FFECBE','#D9D098','#C4AB6D','AB7D3A', +'#F0F0F0','#1C1C1C','#A2FDF5','#1CCDC7','27EDDF', +'#011526','#025959','#027353','#03A678','03A696', +'#004358','#1F8A70','#BEDB39','#FFE11A','FD7400', +'#37465D','#F2F2F2','#9DC02E','#779324','051A37', +'#580022','#AA2C30','#FFBE8D','#487B80','011D24', +'#F9F9F9','#03A678','#E9EDEB','#F44647','00707F', +'#800000','#BF0000','#E2D6C2','#F6EDD8','FFFFFF', +'#F7F6AF','#1B2124','#D62822','#97D6A6','468263', +'#432852','#992255','#FF3D4C','#28656E','00968F', +'#444344','#52BBB2','#2B344D','#EE5555','F8F7EE', +'#45334A','#796B7D','#CCC4B0','#FFF1B5','FFA3A3', +'#5A4B53','#9C3C58','#DE2B5B','#D86A41','D2A825', +'#14151C','#0C242B','#297059','#84D66E','D1FB7A', +'#272D40','#364659','#55736D','#9DBF8E','D0D991', +'#23A38F','#B7C11E','#EFF1C2','#F0563D','2E313D', +'#2E064D','#80176B','#B356A1','#59580B','FFFF00', +'#CC3333','#FF9D33','#F7F7F0','#3EBBA7','00747A', +'#5C4B51','#8CBEB2','#F2EBBF','#F3B562','BD6060', +'#0D3E58','#1C848C','#19C0C2','#F3EDD6','DA6260', +'#022629','#2A5945','#FAFFED','#E6DCC0','B3371C', +'#F4FAC7','#7BAD8D','#FFB159','#F77F45','C2454E', +'#A2C1C6','#86B1B7','#AECBAD','#CFDCB0','D6E1D1', +'#B0DAFF','#325B80','#64B7FF','#586D80','5092CC', +'#0F808C','#6C8C26','#F2A71B','#F26A1B','D91818', +'#FFBC6C','#FE9F6C','#BD716E','#74495F','3B2C4D', +'#FF4D41','#F2931F','#E6CA21','#91B321','1E8C65', +'#302821','#453629','#5C4837','#8A735F','BDA895', +'#415457','#5F7B7F','#9ACCAF','#E6EBC4','F9F7C8', +'#474143','#A69E9D','#E7E2DA','#FFFFFF','E7E8E7', +'#805939','#BD9962','#E6CD7D','#578072','2D4B4D', +'#03588C','#1763A6','#419CA6','#54BF83','8DBF41', +'#00CCFF','#A1FCFF','#040438','#004878','C9FAFF', +'#534C64','#B7DECF','#F0F3D7','#7E858C','D96557', +'#7F7364','#CBB08E','#CBC1B7','#789DCB','646F7F', +'#5C2849','#A73E5C','#EC7263','#FE9551','FFD285', +'#FF0012','#FF7D00','#FFD900','#5BE300','0084B0', +'#F24C32','#F29471','#FCDFA6','#36B898','3D7585', +'#083157','#0A6C87','#459C97','#92CCA5','C9F0B1', +'#DC941B','#EDC266','#B6952C','#E1D3A6','E9A119', +'#323836','#BAD1B5','#DBE8CF','#F0F7E8','FFFEF5', +'#081724','#589494','#8EBBB4','#D0DCD0','F5EED2', +'#50781C','#9CAD1C','#EAF7E6','#40A5DE','0B5191', +'#537F79','#78A68F','#CBD49C','#FED457','CB252A', +'#F23C13','#CBAB78','#FFFFFF','#898373','1F1C17', +'#450003','#5C0002','#94090D','#D40D12','FFED75', +'#0770A2','#82D9F7','#FEFEFE','#AEC844','F36622', +'#30394F','#FF434C','#6ACEEB','#EDE8DF','0E6569', +'#FF6B6B','#556270','#C7F464','#4ECDC4','EDC8BB', +'#D9B500','#FFED9C','#BFCC85','#748F74','454545', +'#452E32','#A34B1B','#B5A187','#EDDF9A','A7CC31', +'#2C2B33','#596664','#909980','#CCC08D','FF8A00', +'#C21F1F','#FFFFFC','#E34446','#FFFFDB','E36D6F', +'#282828','#00AAB5','#C1C923','#F41C54','F5F0F0', +'#3A3F40','#202627','#151B1E','#EFF4FF','41444D', +'#DEBB73','#4D0017','#010000','#4D0F30','9A002F', +'#EB9328','#FFA754','#FFD699','#FFF5DC','4FA6B3', +'#025E73','#037F8C','#D9D59A','#D9BD6A','590202', +'#636266','#E0CEA4','#E8A579','#7D6855','42403E', +'#FF0000','#FF4000','#FF7F00','#FFBF00','FFFF00', +'#FFFFFF','#74ADA6','#1E5E6F','#241B1F','68A81E', +'#5A0532','#FF6745','#FFC861','#9DAE64','27404A', +'#ACCBBC','#467847','#E8E4C1','#A60303','730202', +'#5C4B51','#8CBEB2','#F2EBBF','#F3B562','F06060', +'#0D2557','#93A8C9','#FFFFFF','#F5DED5','558D96', +'#F53C4A','#565157','#10CFC8','#F2EEE7','F5D662', +'#FFD97B','#E65029','#A60027','#660033','191C26', +'#595408','#A6800D','#A66D03','#A63F03','730C02', +'#2E031F','#590424','#8C072B','#BF0A2B','DEEFC5', +'#E0C882','#A6874E','#BFA169','#D9B779','F2D399', +'#D88681','#A67673','#746566','#535A5D','324F54', +'#FC297D','#FB607A','#FDA286','#FDC188','FEE78A', +'#FFECA1','#B3B27F','#4C5E52','#2F3436','FFBE2C', +'#D93312','#B3AB82','#45735F','#394D47','2C3233', +'#324143','#6595A3','#C8E3E8','#FCFFED','B6C28B', +'#477984','#FEF5EB','#C03C44','#EEAA4D','313E4A', +'#334D5C','#45B29D','#EFC94C','#E27A3F','DF4949', +'#630B11','#33322F','#2A2927','#1E1D1C','000000', +'#D94214','#FFF2C1','#80A894','#52736B','093844', +'#051E21','#00302D','#856434','#F28C28','FFAD4E', +'#D7DADD','#DDDEE3','#E1E1E9','#EDEFF4','F2F3F8', +'#BF495E','#41A693','#F2EC9B','#D9CF48','D9583B', +'#067072','#14A589','#DECFA6','#BAAE8C','F94B06', +'#423A38','#47B8C8','#E7EEE2','#BDB9B1','D7503E', +'#730324','#DFE3E6','#B4C4D4','#8BA2BD','456382', +'#537374','#F9BD7F','#EBD7A5','#ADC9A5','5C9E99', +'#88B59E','#B6DEC8','#39464D','#C04229','ABD1AB', +'#11A7FC','#95D127','#F2E415','#FF8638','EE3551', +'#212640','#5D718C','#4B95A6','#60BFBF','EFF2D8', +'#D8A64D','#9B5422','#351411','#5B0D0D','991C11', +'#53324F','#668D6E','#F2E0A0','#F19B7A','F0756E', +'#DFE0AF','#A4BAA2','#569492','#41505E','383245', +'#7BBADE','#93DE7F','#FFED5D','#F29E4A','FF7050', +'#133800','#1B4F1B','#398133','#5C9548','93E036', +'#D9D7AD','#91A685','#FF6A00','#37485C','1C232E', +'#008767','#FFB27A','#FF6145','#AB2141','5E1638', +'#727B7F','#CCEAEA','#7A7556','#2E2125','44CACC', +'#FFFFED','#FF2C38','#FF9A3A','#FFF040','67D9FF', +'#007148','#60A859','#9BDA6A','#C7F774','F9FFEF', +'#092740','#45698B','#90B0CC','#F1FAFF','8FD36F', +'#E2FFA0','#7D8076','#FAFFED','#C2CCBE','8F7D70', +'#00736A','#00BC9F','#F1EEC7','#FEA301','F2561A', +'#26282E','#AD5138','#F7F7F7','#DDDAE0','8594AE', +'#1A191F','#35352F','#484042','#4E5252','E64D38', +'#49454A','#E69B02','#FAF4C6','#B1D631','324052', +'#5F1A2B','#1D2834','#6F8B78','#E4D49E','C96466', +'#012D3D','#38AD9E','#FFEB9E','#FF6867','D0DBED', +'#0D1F36','#104954','#1E9C89','#38CC85','60EB7B', +'#8C4E03','#9FA66A','#F2EC94','#F23005','8B0F03', +'#000001','#20201F','#E2E2E4','#590402','B80000', +'#344059','#465973','#F2D272','#A69460','595139', +'#33454C','#608F85','#B6E5CB','#8BAF95','54584E', +'#FBFEF6','#B7BFA4','#687F70','#1A3841','BF3847', +'#D7E836','#86FFC7','#FFB048','#E8366C','593BFF', +'#34A9FF','#5982DB','#665EB8','#684682','632E62', +'#004056','#2C858D','#74CEB7','#C9FFD5','FFFFCB', +'#BFB978','#84945C','#516967','#4D3130','281B24', +'#103B73','#20638C','#3786A6','#4EABBF','EBEFF2', +'#9FB1BF','#1D2D63','#1C5357','#1F6E56','196331', +'#FFEBBA','#C3BD91','#88947B','#4C3F3F','2A2727', +'#347373','#4EA6A6','#91D9D9','#FFFFFD','F2E205', +'#828948','#EFDFC2','#006971','#DC533E','840000', +'#000137','#29003F','#79003D','#D04D14','F89801', +'#370005','#4B0005','#5F0005','#730005','870005', +'#C3AE8D','#F25260','#2D5F73','#6BADC9','8FCED6', +'#9E1B36','#F7EDBA','#E69B3D','#EB3355','3D241D', +'#1D8281','#44BF87','#FBD258','#F29A3F','DB634F', +'#035C75','#1B808C','#31A6A8','#F3F1BC','F3AD14', +'#FF7500','#665130','#EBB643','#CEDAA8','668E84', +'#384D3A','#3E6653','#728053','#A68357','D97C71', +'#012326','#17455C','#E1CAAB','#FE8333','FA4913', +'#1A2944','#2DA7C7','#56ACBA','#98C4C9','CBD5D2', +'#BF3542','#CDC5BA','#EBE3D6','#3C3C3C','2E2E2E', +'#231921','#695F74','#BEB4CB','#EBEBF0','D2DCEB', +'#34373F','#686C75','#F3E9D0','#BEB7A7','8E867C', +'#661510','#D9351A','#F2C76F','#BF9727','204D3F', +'#3CFFEE','#24AABC','#356781','#2C3D51','1C1F24', +'#DA3537','#FFFCC4','#00585F','#6A6A61','2A2C2B', +'#AE3135','#D1AF87','#8C826B','#3D3C33','F2F0CE', +'#FF0894','#FF5E9F','#FF91A7','#FFB5CA','F5F0BA', +'#99878D','#323232','#646464','#7E4A5C','372129', +'#3FB8AF','#7FC7AF','#DAD8A7','#FFB38B','FF3F34', +'#402B3C','#6AA6A6','#D9CCA7','#F2B263','F26835', +'#6AA690','#F2BC1B','#F2DC99','#F29057','BF1F1F', +'#F4FAC7','#7BAD8D','#FFB158','#F77F45','C2454E', +'#E5533C','#F5E346','#93D06D','#50AC6A','227864', +'#39588A','#A9BDD7','#FFFFFF','#FFEADD','FFD0BB', +'#B0B595','#615F4F','#828567','#91A380','EAFFCD', +'#00427F','#0066BD','#66B5CC','#F0E4C5','D6C28F', +'#FF6313','#F9E4B3','#C29689','#74474B','45232E', +'#00585F','#009393','#FFFCC4','#C7C49B','EB0A00', +'#091840','#44A2FF','#F7F7EA','#B3CC63','4C6620', +'#5CBBE3','#FCF1BC','#5C8182','#383A47','B4F257', +'#9E9E9E','#E5E1D1','#E0393D','#253746','425563', +'#4D9453','#FFFFB1','#ADDE4E','#FF9D27','A62A16', +'#B70046','#FF850B','#FFEBC5','#109679','675A4C', +'#363636','#0599B0','#A4BD0A','#FFA615','FF2E00', +'#7D8077','#BBBFB2','#FAFFED','#E82A33','E3DEBC', +'#FD9F44','#FC5C65','#007269','#03A679','FAF0B9', +'#134B57','#81A489','#F1D8B5','#F2A054','C04D31', +'#946E49','#394042','#EDDBAC','#872A0C','BA8E3A', +'#404040','#024959','#037E8C','#FFFFFF','F24C27', +'#2A3342','#163C6E','#4E5F61','#E6A015','EDE7BE', +'#445060','#829AB5','#849E91','#C14543','D6D5D1', +'#8A9126','#B7BF5E','#FFE9C4','#F5B776','F58E45', +'#9B2D1E','#3C3A28','#78A080','#9BCD9E','FFFFAE', +'#FF6138','#FFFF9D','#BEEB9F','#79BD8F','00A388', +'#990000','#FF6600','#FF9900','#996633','CC9966', +'#DCE6DA','#B8CCBB','#98B3A5','#7A9994','62858C', +'#0B1C29','#3B7C8F','#73A5A3','#98C1B7','F0EBD2', +'#F6CB51','#E25942','#13A89E','#3F4953','F2E7DA', +'#282F36','#FFFEFC','#BDA21D','#BFBC5B','D2E098', +'#8C182D','#DE7140','#FCB95A','#FAE285','6A7349', +'#6B9100','#FFE433','#FF841F','#E03D19','A6001C', +'#FFEAA7','#D9D697','#9FC49F','#718C6A','543122', +'#CFF09E','#A8DBA8','#79BD9A','#3B8686','0B486B', +'#0C2233','#065471','#0A91AB','#FFC045','F2F2F2', +'#BEE8E0','#373C40','#2E2621','#73320B','FF5E00', +'#1B2C35','#A3BFC6','#FF005D','#222A30','293A42', +'#FF8400','#3B4044','#494948','#E6E1D8','F7F2E9', +'#6A482D','#518C86','#F6BF3D','#EF7C27','BF2424', +'#261C2B','#292B39','#226468','#608D80','829D8F', +'#B2AD9A','#110E00','#363226','#A9A695','ECE9D8', +'#1B1B26','#26394D','#286480','#13B3BF','A3FF57', +'#F2C2A7','#F5E5C5','#593D28','#422C21','93DEDB', +'#001028','#033140','#1E5A5B','#7BA78C','EBEDC6', +'#544E6E','#808CB0','#ABD1D9','#D9FFF7','DDF556', +'#323A45','#596677','#758194','#FFFFFF','E74C3C', +'#45291A','#AB926D','#DBD1BC','#4999C3','5FCBEC', +'#6B151D','#2E1615','#A8553A','#DB8F5A','F2C18E', +'#000623','#28475C','#4A6C74','#8BA693','F0E3B1', +'#60807B','#81B37A','#BCCC5F','#FFEE65','E64964', +'#FFFFFA','#A1A194','#5B605F','#464646','FF6600', +'#1E1B17','#577270','#9C9A79','#C7BDA1','580E0C', +'#452F27','#5E504A','#6B6865','#9BBAB2','B0FFED', +'#1B5257','#F7F6C3','#F28159','#CC5850','4F1C2E', +'#FAA51B','#BF511F','#2C445E','#2F6D82','5EE4EB', +'#BF3952','#59364A','#556D73','#D9D1A9','D95F5F', +'#024959','#037E8C','#F2EFDC','#E74C30','363636', +'#221A26','#544759','#A197A6','#F27405','D93D04', +'#C4A44A','#E6D399','#9AB8A9','#7C8A7F','4E4B44', +'#FFFEC8','#B1BF99','#5B604D','#39382B','26181E', +'#4E3C51','#21A68D','#3BBF9A','#F2E8B6','F25749', +'#102144','#1B325E','#254580','#3C63B0','5D8AEA', +'#2A3A48','#3E6372','#B2D4DC','#FAFAFF','FF4B00', +'#FFF1BF','#F20058','#FFAEAC','#000001','7D7A96', +'#FDFFC6','#F2F096','#FF0080','#DE0049','521218', +'#5B0E00','#FBB500','#FBD864','#807D1A','59233C', +'#1E1E1F','#424143','#67666A','#807F83','CBC9CF', +'#3C3658','#3EC8B7','#7CD0B4','#B9D8B1','F7E0AE', +'#FFFFFF','#99B75F','#D5DD98','#EBF4DB','D8D8D8', +'#248A8A','#C9FA58','#F9E555','#FAAC38','F2572A', +'#086B63','#77A490','#E2D8C1','#BFAE95','7C7159', +'#5C4B51','#8CBEB2','#F2EBBF','#A5C88F','EF847B', +'#17162F','#89346D','#C76058','#FFB248','E8C475', +'#6E8F4A','#65D9C5','#F2E7B6','#EDA430','AB3E2C', +'#30394F','#FF434C','#6ACEEB','#EDE8DF','0E6569', +'#8E1B13','#F9E4B3','#849689','#46464A','29232E', +'#686B30','#AB9A52','#E8BA67','#D68F4F','BA512E', +'#E54E45','#DBC390','#F2F2EF','#13A3A5','403833', +'#65BA99','#59A386','#F1DDBB','#D6C4A6','E74C3C', +'#A6FFBC','#4ACFAF','#00A995','#006161','003D4C', +'#33271E','#8B7653','#C8D9A0','#FDEE9D','233331', +'#048789','#503D2E','#D44D27','#E2A72E','EFEBC8', +'#E5FF1E','#A9D943','#75A660','#698070','494D4B', +'#2DEBA2','#91F57F','#EBAA69','#E70049','2B0027', +'#990000','#336699','#DDDDDD','#999999','333333', +'#F13A4B','#3D3C3E','#22BDAF','#F4F4F4','D7D7D7', +'#F53A59','#001D2D','#15A88C','#B7D9C8','F3F5F4',]; + +outArrayPalette.set(colors); + +}; + +Ops.Array.PaletteLibrary.prototype = new CABLES.Op(); +CABLES.OPS["a96020ac-0832-478e-bbb7-d6246d0eeedf"]={f:Ops.Array.PaletteLibrary,objName:"Ops.Array.PaletteLibrary"}; + + + + +// ************************************************************** +// +// Ops.Array.ParseArray_v2 +// +// ************************************************************** + +Ops.Array.ParseArray_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const text = op.inStringEditor("text", "1,2,3"), + separator = op.inString("separator", ","), + toNumber = op.inValueBool("Numbers", true), + trim = op.inValueBool("Trim", true), + splitNewLines = op.inBool("Split Lines", false), + parsed = op.outTrigger("Parsed"), + arr = op.outArray("array"), + len = op.outNumber("length"); + +text.setUiAttribs({ "ignoreBigPort": true }); + +text.onChange = separator.onChange = toNumber.onChange = trim.onChange = parse; + +splitNewLines.onChange = () => +{ + separator.setUiAttribs({ "greyout": splitNewLines.get() }); + parse(); +}; + +parse(); + +function parse() +{ + if (!text.get()) + { + arr.set(null); + arr.set([]); + len.set(0); + return; + } + + let textInput = text.get(); + if (trim.get() && textInput) + { + textInput = textInput.replace(/^\s+|\s+$/g, ""); + textInput = textInput.trim(); + } + + let r; + let sep = separator.get(); + if (separator.get() === "\\n") sep = "\n"; + if (splitNewLines.get()) r = textInput.split("\n"); + else r = textInput.split(sep); + + if (r[r.length - 1] === "") r.length -= 1; + + len.set(r.length); + + if (trim.get()) + { + for (let i = 0; i < r.length; i++) + { + r[i] = r[i].replace(/^\s+|\s+$/g, ""); + r[i] = r[i].trim(); + } + } + + op.setUiError("notnum", null); + if (toNumber.get()) + { + let hasStrings = false; + for (let i = 0; i < r.length; i++) + { + r[i] = Number(r[i]); + if (!CABLES.UTILS.isNumeric(r[i])) + { + hasStrings = true; + } + } + if (hasStrings) + { + op.setUiError("notnum", "Parse Error / Not all values numerical!"); + } + } + + arr.set(null); + arr.set(r); + parsed.trigger(); +} + + +}; + +Ops.Array.ParseArray_v2.prototype = new CABLES.Op(); +CABLES.OPS["c974de41-4ce4-4432-b94d-724741109c71"]={f:Ops.Array.ParseArray_v2,objName:"Ops.Array.ParseArray_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.PerlinArray +// +// ************************************************************** + +Ops.Array.PerlinArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArrayX = op.inArray("Array in X"), + inTimeArray = op.inArray("Array time"), + inTime = op.inFloat("Time in Y", 0), + seed = op.inFloatSlider("Seed 0-1", 0.0), + inFrequency = op.inFloat("Frequency", 10), + outArray = op.outArray("Array out"), + outArrayLength = op.outNumber("Array length out"); + +let showingError = false; + +let newArr = []; +outArray.set(newArr); + +seed.set(Math.random()); + +inArrayX.onChange = inTimeArray.onChange = inTime.onChange = inFrequency.onChange = update; + +seed.onChange = function () +{ + Math.randomSeed = seed.get(); + noise.seed(Math.seededRandom()); + update(); +}; + +function update() +{ + let arr = inArrayX.get(); + let arrTime = inTimeArray.get(); + + let time = inTime.get(); + let mult = inFrequency.get(); + + if (arrTime) + { + if (!arr || !arrTime) + { + outArray.set(null); + return; + } + if (arr.length != arrTime.length) + { + if (!showingError) + { + op.uiAttr({ "error": "Arrays do not have the same length !" }); + outArrayLength.set(0); + showingError = true; + } + outArray.set(null); + return; + } + if (showingError) + { + showingError = false; + op.uiAttr({ "error": null }); + } + + if (newArr.length != arr.length)newArr.length = arr.length; + + for (var i = 0; i < arr.length; i++) + { + newArr[i] = noise.perlin2(arr[i] * mult, arrTime[i] + time); + } + } + else if (!arrTime) + { + if (!arr) + { + outArray.set(null); + return; + } + + if (newArr.length != arr.length)newArr.length = arr.length; + + for (var i = 0; i < arr.length; i++) + { + newArr[i] = noise.perlin2(arr[i] * mult, time); + } + } + + outArray.set(null); + outArray.set(newArr); + outArrayLength.set(newArr.length); +} + + +}; + +Ops.Array.PerlinArray.prototype = new CABLES.Op(); +CABLES.OPS["ed55556e-f319-47cc-bce6-ea015b45650b"]={f:Ops.Array.PerlinArray,objName:"Ops.Array.PerlinArray"}; + + + + +// ************************************************************** +// +// Ops.Array.Phyllotaxis +// +// ************************************************************** + +Ops.Array.Phyllotaxis = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exec=op.inTrigger("Render"); +const inNum=op.inValueInt("Num",400); +const inC=op.inValue("Scale",0.1); +const inI=op.inValue("Param",100); + +const outArr=op.outArray("Coordinates"); + +const arr=[]; + +inNum.onChange=update; +inC.onChange=update; +inI.onChange=update; + +function update() +{ + arr.length=Math.floor(inNum.get()*3); + + var n=inNum.get(); + var c=inC.get(); + + var ii=inI.get(); + + for (var i = 0; i < n; i++) + { + var a = i * ii; + var r = c * Math.sqrt(i); + var x = r * Math.cos(a); + var y = r * Math.sin(a); + var hu = i/3.0 % 360; + + arr[i*3+0]=x; + arr[i*3+1]=y; + arr[i*3+2]=0; + } + outArr.set(null); + outArr.set(arr); +} + +update(); + +}; + +Ops.Array.Phyllotaxis.prototype = new CABLES.Op(); +CABLES.OPS["312b5220-c0f9-4003-9072-e400f216251a"]={f:Ops.Array.Phyllotaxis,objName:"Ops.Array.Phyllotaxis"}; + + + + +// ************************************************************** +// +// Ops.Array.PointArray.CircularPoints +// +// ************************************************************** + +Ops.Array.PointArray.CircularPoints = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + radius = op.inValue("Radius", 1), + segments = op.inValue("Round Segments", 40), + percent = op.inFloat("Rounds", 1), + inRoundOffset = op.inFloat("Radius Add Round", 0.1), + inPointRadOffset = op.inFloat("Radius Add Point", 0.1), + inOffset = op.inFloat("Offset", 0), + inPointOffset = op.inFloat("Point Offset XY", 0.1), + inPointOffsetZ = op.inFloat("Point Offset Z", 0.1), + inOffsetRot = op.inFloat("Offset Rotation"), + outArr = op.outArray("Points", 3), + outRotArr = op.outArray("Rotation", 3), + outTotalPoints = op.outNumber("Total points"), + outArrayLength = op.outNumber("Array lengths"); + +inOffset.onChange = + inOffsetRot.onChange = + inPointRadOffset.onChange = + inPointOffset.onChange = + radius.onChange = + inPointOffsetZ.onChange = + inRoundOffset.onChange = + percent.onChange = + segments.onChange = calcArray; + +function calcArray() +{ + const segs = Math.max(3, Math.floor(segments.get())); + const points = []; + const rots = []; + + const roundOffset = inRoundOffset.get(); + + let count = 0; + const offsetRot = inOffsetRot.get(); + const r = radius.get(); + const pointOff = inPointRadOffset.get() / 100; + const roundOff = inRoundOffset.get(); + const offZ = inPointOffsetZ.get(); + let progressOffset = 0.0; + + for (let i = 0; i < segs * percent.get(); i++) + { + progressOffset = i * inPointOffset.get() + inOffset.get(); + const radiusOff = Math.floor(i / segs) * roundOff; + + let degInRad = (360 / segs) * (i + progressOffset) * CGL.DEG2RAD; + let posx = Math.cos(degInRad) * (r + radiusOff + pointOff * i); + let posy = Math.sin(degInRad) * (r + radiusOff + pointOff * i); + + rots.push(0, 0, degInRad * CGL.RAD2DEG - offsetRot); + + points.push(posx, posy, i * offZ); + } + + outArr.set(null); + outArr.set(points); + + outRotArr.set(null); + outRotArr.set(rots); + + outTotalPoints.set(points.length / 3); + outArrayLength.set(points.length); +} + +calcArray(); + + +}; + +Ops.Array.PointArray.CircularPoints.prototype = new CABLES.Op(); +CABLES.OPS["5d2de367-c2b8-4702-abf0-b16bbeb22f37"]={f:Ops.Array.PointArray.CircularPoints,objName:"Ops.Array.PointArray.CircularPoints"}; + + + + +// ************************************************************** +// +// Ops.Array.PointArray.FillPointArrayDuplicates +// +// ************************************************************** + +Ops.Array.PointArray.FillPointArrayDuplicates = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + inNumPoints = op.inValueInt("Num Elements", 1000), + inCalc = op.inTriggerButton("Calculate"), + outArr = op.outArray("Result"); +op.toWorkPortsNeedToBeLinked(inArr); + +let arr = []; + +inCalc.onTriggered = function () +{ + let num = inNumPoints.get(); + + arr.length = num * 3; + + let oldArr = inArr.get(); + if (!oldArr) + { + outArr.set(null); + return; + } + let numOld = oldArr.length; + + let i = 0; + for (i = 0; i < numOld; i++) + { + arr[i] = oldArr[i]; + } + + Math.randomSeed = 5711; + + while (i < (num - 1) * 3) + { + let ind = Math.floor(Math.seededRandom() * (numOld / 3)) * 3; + + arr[i + 0] = oldArr[ind + 0]; + arr[i + 1] = oldArr[ind + 1]; + arr[i + 2] = oldArr[ind + 2]; + i += 3; + } + + outArr.set(null); + outArr.set(arr); +}; + + +}; + +Ops.Array.PointArray.FillPointArrayDuplicates.prototype = new CABLES.Op(); +CABLES.OPS["dae7416d-b130-487e-bdd3-ca5a18278d47"]={f:Ops.Array.PointArray.FillPointArrayDuplicates,objName:"Ops.Array.PointArray.FillPointArrayDuplicates"}; + + + + +// ************************************************************** +// +// Ops.Array.PointArray.RedistributeSplinePoints +// +// ************************************************************** + +Ops.Array.PointArray.RedistributeSplinePoints = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array3x"), + num = op.inValueInt("Num Points", 100), + inExec = op.inTriggerButton("Calculate"), + inNormalized = op.inValueBool("Normalized"), + result = op.outArray("Result"), + outSplineLength = op.outNumber("Spline Length"); + +const + animX = new CABLES.Anim(), + animY = new CABLES.Anim(), + animZ = new CABLES.Anim(); + +let needsMapping = true; +let newArray = []; +let totalSplineLength = 0; + +function dist(x1, y1, z1, x2, y2, z2) +{ + let xd = x1 - x2; + let yd = y1 - y2; + let zd = z1 - z2; + return Math.sqrt(xd * xd + yd * yd + zd * zd); +} + +function splineLength(arr) +{ + let l = 0; + for (let i = 3; i < arr.length; i += 3) + { + l += dist(arr[i - 3], arr[i - 2], arr[i - 1], arr[i + 0], arr[i + 1], arr[i + 2]); + } + + return l; +} + +function mapArrays() +{ + animX.clear(); + animY.clear(); + animZ.clear(); + let arr = inArr.get(); + if (!arr) + { + result.set([]); + return; + } + totalSplineLength = splineLength(arr); + outSplineLength.set(totalSplineLength); + + let distPos = 0; + + for (let i = 0; i < arr.length; i += 3) + { + let p = i / (arr.length - 3); + if (i > 0) + { + distPos += dist(arr[i - 3], arr[i - 2], arr[i - 1], arr[i + 0], arr[i + 1], arr[i + 2]); + } + + animX.setValue(distPos, arr[i + 0]); + animY.setValue(distPos, arr[i + 1]); + animZ.setValue(distPos, arr[i + 2]); + } + + needsMapping = false; +} + +function buildResultArray() +{ + let n = Math.max(0, num.get()); + if (n === 0) + { + result.set([]); + return; + } + + newArray.length = n * 3; + + for (let i = 0; i < n; i++) + { + newArray[i * 3 + 0] = animX.getValue(i / n * totalSplineLength); + newArray[i * 3 + 1] = animY.getValue(i / n * totalSplineLength); + newArray[i * 3 + 2] = animZ.getValue(i / n * totalSplineLength); + } + + result.set(null); + result.set(newArray); +} + +inArr.onChange = function () +{ + needsMapping = true; +}; + +inExec.onTriggered = function () +{ + if (needsMapping)mapArrays(); + buildResultArray(); +}; + + +}; + +Ops.Array.PointArray.RedistributeSplinePoints.prototype = new CABLES.Op(); +CABLES.OPS["f17bae71-6047-49ea-af17-f7690e6e4d36"]={f:Ops.Array.PointArray.RedistributeSplinePoints,objName:"Ops.Array.PointArray.RedistributeSplinePoints"}; + + + + +// ************************************************************** +// +// Ops.Array.RandomArrays +// +// ************************************************************** + +Ops.Array.RandomArrays = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const numValues = op.inValueInt("Num Values", 100); +const inModeSwitch = op.inSwitch("Mode", ["A", "AB", "ABC", "ABCD"], "A"); +const inSeed = op.inValueFloat("Random Seed ", 0); +const inInteger = op.inBool("Integer", false); +const outValues = op.outArray("Array Out"); + +const letters = ["A", "B", "C", "D"]; + +const inArray = letters.map(function (value) +{ + return { + "min": op.inValueFloat("Min " + value, -1), + "max": op.inValueFloat("Max " + value, 1), + }; +}); + +for (let i = 0; i < inArray.length; i += 1) +{ + const portObj = inArray[i]; + const keys = Object.keys(portObj); + + op.setPortGroup("Value Range " + letters[i], keys.map(function (key) { return portObj[key]; })); + + if (i > 0) keys.forEach(function (key) { portObj[key].setUiAttribs({ "greyout": true }); }); +} + + +inModeSwitch.onChange = function () +{ + const mode = inModeSwitch.get(); + const modes = inModeSwitch.uiAttribs.values; + + outValues.setUiAttribs({ "stride": inModeSwitch.get().length }); + + const index = modes.indexOf(mode); + + inArray.forEach(function (portObj, i) + { + const keys = Object.keys(portObj); + keys.forEach(function (key, j) + { + if (i <= index) portObj[key].setUiAttribs({ "greyout": false }); + else portObj[key].setUiAttribs({ "greyout": true }); + }); + }); + init(); +}; + +const outTotalPoints = op.outNumber("Chunks Amount"); +const outArrayLength = op.outNumber("Array length"); + +outValues.ignoreValueSerialize = true; + +numValues.onChange = inSeed.onChange = inInteger.onChange = init; + +const minMaxArray = []; + +init(); + +function init() +{ + const arr = []; + const mode = inModeSwitch.get(); + const modes = inModeSwitch.uiAttribs.values; + const index = modes.indexOf(mode); + + Math.randomSeed = inSeed.get(); + + const dimension = index + 1; + const length = Math.floor(Math.abs(numValues.get() * dimension)); + + arr.length = length; + const tupleLength = length / dimension; + const isInteger = inInteger.get(); + + // optimization: we only need to fetch the max min for each component once + for (let i = 0; i < dimension; i += 1) + { + const portObj = inArray[i]; + const max = portObj.max.get(); + const min = portObj.min.get(); + minMaxArray[i] = [min, max]; + } + + for (let j = 0; j < tupleLength; j += 1) + { + for (let k = 0; k < dimension; k += 1) + { + const min = minMaxArray[k][0]; + const max = minMaxArray[k][1]; + const index = j * dimension + k; + + if (isInteger) arr[index] = Math.floor(Math.seededRandom() * ((max + 1) - min) + min); + else arr[index] = Math.seededRandom() * (max - min) + min; + } + } + + outValues.set(null); + + outValues.set(arr); + outTotalPoints.set(arr.length / dimension); + outArrayLength.set(arr.length); +} + +// assign change handler +inArray.forEach(function (obj) +{ + Object.keys(obj).forEach(function (key) + { + const x = obj[key]; + x.onChange = init; + }); +}); + + +}; + +Ops.Array.RandomArrays.prototype = new CABLES.Op(); +CABLES.OPS["8a9fa2c6-c229-49a9-9dc8-247001539217"]={f:Ops.Array.RandomArrays,objName:"Ops.Array.RandomArrays"}; + + + + +// ************************************************************** +// +// Ops.Array.RandomNumbersArray3_v2 +// +// ************************************************************** + +Ops.Array.RandomNumbersArray3_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + numValues = op.inValueInt("numValues", 100), + min = op.inValueFloat("Min", -1), + max = op.inValueFloat("Max", 1), + seed = op.inValueFloat("random seed"), + closed = op.inValueBool("Last == First"), + inInteger = op.inValueBool("Integer", false), + values = op.outArray("values", null, 3), + outTotalPoints = op.outNumber("Total points"), + outArrayLength = op.outNumber("Array length"); + +op.setPortGroup("Value Range", [min, max]); +op.setPortGroup("", [seed, closed]); + +values.ignoreValueSerialize = true; + +closed.onChange = + max.onChange = + min.onChange = + numValues.onChange = + seed.onChange = + inInteger.onChange = init; + +const arr = []; +init(); + +function init() +{ + Math.randomSeed = seed.get(); + + const isInteger = inInteger.get(); + + const arrLength = arr.length = Math.max(0, Math.floor(Math.abs((numValues.get() || 0) * 3))); + + if (arrLength === 0) + { + values.set(null); + outTotalPoints.set(0); + outArrayLength.set(0); + return; + } + + const minIn = min.get(); + const maxIn = max.get(); + + for (let i = 0; i < arrLength; i += 3) + { + if (!isInteger) + { + arr[i + 0] = Math.seededRandom() * (maxIn - minIn) + minIn; + arr[i + 1] = Math.seededRandom() * (maxIn - minIn) + minIn; + arr[i + 2] = Math.seededRandom() * (maxIn - minIn) + minIn; + } + else + { + arr[i + 0] = Math.floor(Math.seededRandom() * ((maxIn - minIn) + 1) + minIn); + arr[i + 1] = Math.floor(Math.seededRandom() * ((maxIn - minIn) + 1) + minIn); + arr[i + 2] = Math.floor(Math.seededRandom() * ((maxIn - minIn) + 1) + minIn); + } + } + + if (closed.get() && arrLength > 3) + { + arr[arrLength - 3 + 0] = arr[0]; + arr[arrLength - 3 + 1] = arr[1]; + arr[arrLength - 3 + 2] = arr[2]; + } + + values.set(null); + values.set(arr); + outTotalPoints.set(arrLength / 3); + outArrayLength.set(arrLength); +} + + +}; + +Ops.Array.RandomNumbersArray3_v2.prototype = new CABLES.Op(); +CABLES.OPS["45b2113a-1ca0-41d8-8d90-db3e394b669c"]={f:Ops.Array.RandomNumbersArray3_v2,objName:"Ops.Array.RandomNumbersArray3_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.RandomNumbersArray4_v2 +// +// ************************************************************** + +Ops.Array.RandomNumbersArray4_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + numValues = op.inValueInt("numValues", 100), + min = op.inValueFloat("Min", 0), + max = op.inValueFloat("Max", 1), + seed = op.inValueFloat("random seed"), + closed = op.inValueBool("Last == First"), + inInteger = op.inValueBool("Integer", false), + values = op.outArray("values", null, 4), + outTotalPoints = op.outNumber("Tuple Amount"), + outArrayLength = op.outNumber("Array length"); + +op.setPortGroup("Value Range", [min, max]); +op.setPortGroup("", [seed, closed]); + +values.ignoreValueSerialize = true; + +closed.onChange = max.onChange = + min.onChange = + numValues.onChange = + seed.onChange = + values.onLinkChanged = + inInteger.onChange = init; + +let arr = []; +init(); + +function init() +{ + Math.randomSeed = seed.get(); + + let isInteger = inInteger.get(); + + let arrLength = arr.length = Math.floor(Math.abs(numValues.get() * 4)); + if (arrLength === 0) + { + values.set(null); + outTotalPoints.set(0); + outArrayLength.set(0); + return; + } + + let minIn = min.get(); + let maxIn = max.get(); + + for (let i = 0; i < arrLength; i += 4) + { + if (!isInteger) + { + arr[i + 0] = Math.seededRandom() * (maxIn - minIn) + minIn; + arr[i + 1] = Math.seededRandom() * (maxIn - minIn) + minIn; + arr[i + 2] = Math.seededRandom() * (maxIn - minIn) + minIn; + arr[i + 3] = Math.seededRandom() * (maxIn - minIn) + minIn; + } + else + { + arr[i + 0] = Math.floor(Math.seededRandom() * ((maxIn - minIn) + 1) + minIn); + arr[i + 1] = Math.floor(Math.seededRandom() * ((maxIn - minIn) + 1) + minIn); + arr[i + 2] = Math.floor(Math.seededRandom() * ((maxIn - minIn) + 1) + minIn); + arr[i + 3] = Math.floor(Math.seededRandom() * ((maxIn - minIn) + 1) + minIn); + } + } + + if (closed.get() && arrLength > 4) + { + arr[arrLength - 4 + 0] = arr[0]; + arr[arrLength - 4 + 1] = arr[1]; + arr[arrLength - 4 + 2] = arr[2]; + arr[arrLength - 4 + 3] = arr[2]; + } + + values.set(null); + values.set(arr); + outTotalPoints.set(arrLength / 4); + outArrayLength.set(arrLength); +} + + +}; + +Ops.Array.RandomNumbersArray4_v2.prototype = new CABLES.Op(); +CABLES.OPS["23c28491-06d3-4ea8-9e0d-c615b0783817"]={f:Ops.Array.RandomNumbersArray4_v2,objName:"Ops.Array.RandomNumbersArray4_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.RandomNumbersArray_v3 +// +// ************************************************************** + +Ops.Array.RandomNumbersArray_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + numValues = op.inValueInt("numValues", 10), + min = op.inValueFloat("Min", 0), + max = op.inValueFloat("Max", 1), + seed = op.inValueFloat("random seed"), + values = op.outArray("values", 100), + outArrayLength = op.outNumber("Array length"), + inInteger = op.inValueBool("Integer", false); + +values.ignoreValueSerialize = true; +op.setPortGroup("Value Range", [min, max]); +op.setPortGroup("", [seed]); + +max.onChange = + min.onChange = + numValues.onChange = + seed.onChange = + values.onLinkChanged = + inInteger.onChange = init; + +let arr = []; +init(); + +function init() +{ + Math.randomSeed = seed.get(); + let isInteger = inInteger.get(); + + let arrLength = arr.length = Math.max(0, Math.abs(parseInt(numValues.get() || 0))); + + let minIn = min.get(); + let maxIn = max.get(); + + if (arrLength === 0) + { + values.set(null); + outArrayLength.set(0); + return; + } + if (!isInteger) + { + for (var i = 0; i < arrLength; i++) + { + arr[i] = Math.seededRandom() * (maxIn - minIn) + minIn; + } + } + else + { + for (var i = 0; i < arrLength; i++) + { + arr[i] = Math.floor(Math.seededRandom() * ((maxIn - minIn) + 1) + minIn); + } + } + + values.set(null); + values.set(arr); + outArrayLength.set(arrLength); +} + + +}; + +Ops.Array.RandomNumbersArray_v3.prototype = new CABLES.Op(); +CABLES.OPS["e8609213-0803-4338-ab20-f95d0d65a110"]={f:Ops.Array.RandomNumbersArray_v3,objName:"Ops.Array.RandomNumbersArray_v3"}; + + + + +// ************************************************************** +// +// Ops.Array.RandomWordsArray +// +// ************************************************************** + +Ops.Array.RandomWordsArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let arr = ["apple", "baby", "back", "ball", "bear", "bed", "bell", "bird", + "birthday", "boat", "box", "boy", "bread", "brother", "cake", "car", + "cables", "cat", "chair", "chicken", "children", "Christmas", "coat", "corn", + "cow", "day", "dog", "doll", "door", "duck", "egg", "eye", "farm", "farmer", + "father", "feet", "fire", "fish", "floor", "flower", "game", "garden", "girl", + "good-bye", "grass", "ground", "hand", "head", "hill", "home", "horse", "house", + "kitty", "leg", "letter", "man", "men", "milk", "money", "morning", "mother", + "name", "nest", "night", "paper", "party", "picture", "pig", "rabbit", "rain", + "ring", "robin", "Santa Claus", "school", "seed", "sheep", "shoe", "sister", + "snow", "song", "squirrel", "stick", "street", "sun", "table", "thing", "time", + "top", "toy", "tree", "watch", "water", "way", "wind", "window", "wood"]; + +arr = CABLES.shuffleArray(arr); + +let values = op.outArray("Words", arr); + + +}; + +Ops.Array.RandomWordsArray.prototype = new CABLES.Op(); +CABLES.OPS["98057c3a-f6ec-48a6-8e8a-4b8316a0dcf1"]={f:Ops.Array.RandomWordsArray,objName:"Ops.Array.RandomWordsArray"}; + + + + +// ************************************************************** +// +// Ops.Array.ReduceArray3_v3 +// +// ************************************************************** + +Ops.Array.ReduceArray3_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + arr = op.inArray("Array"), + inMeth = op.inSwitch("Remove", ["Xth Item", "Random", "Duplicates"], "Xth Item"), + num = op.inValueInt("Every xth Item", 2), + inThresh = op.inFloatSlider("Threshold", 0.5), + inSeed = op.inFloat("Seed", 1), + outArr = op.outArray("Result Array"); + +num.onChange = + inThresh.onChange = + inSeed.onChange = + arr.onChange = update; + +const newArray = []; + +let updateMethod = updateXth; + +inMeth.onChange = () => +{ + if (inMeth.get() == "Xth Item")updateMethod = updateXth; + if (inMeth.get() == "Random")updateMethod = updateRandom; + if (inMeth.get() == "Duplicates")updateMethod = updateDupes; + + update(); +}; + +function update() +{ + const theArray = arr.get(); + if (!theArray) return; + + let newArray = updateMethod(theArray); + + outArr.set(null); + outArr.set(newArray); +} + +function updateDupes(theArray) +{ + const noDupesArr = []; + + for (let i = 0; i < theArray.length; i += 3) + { + let found = false; + for (let j = 0; j < noDupesArr.length; j += 3) + { + if ( + theArray[i] == noDupesArr[j] && + theArray[i + 1] == noDupesArr[j + 1] && + theArray[i + 2] == noDupesArr[j + 2] + ) + { + found = true; + break; + } + } + + if (!found) + { + noDupesArr.push(theArray[i], theArray[i + 1], theArray[i + 2]); + } + } + return noDupesArr; +} + +function updateRandom(theArray) +{ + Math.randomSeed = inSeed.get(); + + const newArray = []; + + const thresh = inThresh.get(); + + for (let i = 0; i < theArray.length; i += 3) + { + if (Math.seededRandom() > thresh) + newArray.push(theArray[i + 0], theArray[i + 1], theArray[i + 2]); + } + + return newArray; +} + +function updateXth(theArray) +{ + const step = Math.max(0, Math.floor(num.get())); + const newArray = []; + + if (step === 0) + { + outArr.set(null); + outArr.set([]); + return; + } + + for (let i = 0; i < theArray.length; i += step * 3) + newArray.push(theArray[i + 0], theArray[i + 1], theArray[i + 2]); + + return newArray; +} + + +}; + +Ops.Array.ReduceArray3_v3.prototype = new CABLES.Op(); +CABLES.OPS["5ba30060-2e42-4441-8b15-49279dc3bb67"]={f:Ops.Array.ReduceArray3_v3,objName:"Ops.Array.ReduceArray3_v3"}; + + + + +// ************************************************************** +// +// Ops.Array.ReverseArray3 +// +// ************************************************************** + +Ops.Array.ReverseArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +let inArrPort = op.inArray("Array"); + +// outputs +let outArrayPort = op.outArray("Reversed Array", []); + +// change listeners +inArrPort.onChange = function () +{ + let inArr = inArrPort.get(); + let reversedArr = []; + if (inArr && inArr.length >= 3) + { + // in case the array is not dividable by 3, get rid of the rest + // e.g. length = 31 -> ignore the last value + // length = 30 -> perfect fit for [x, y, z, ...] + let iStart = (Math.floor(inArr.length / 3) * 3) - 3; + for (let i = iStart; i >= 0; i -= 3) + { + reversedArr.push(inArr[i], inArr[i + 1], inArr[i + 2]); + } + } + + outArrayPort.set(null); + outArrayPort.set(reversedArr); +}; + + +}; + +Ops.Array.ReverseArray3.prototype = new CABLES.Op(); +CABLES.OPS["5b0feea0-06ee-4e9d-befb-41793ab4fa2c"]={f:Ops.Array.ReverseArray3,objName:"Ops.Array.ReverseArray3"}; + + + + +// ************************************************************** +// +// Ops.Array.RingBuffer +// +// ************************************************************** + +Ops.Array.RingBuffer = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let DEFAULT_LENGTH = 10; + +const + inVal = op.inValue("Value"), + inWrite = op.inTriggerButton("Write"), + inNum = op.inValueInt("Length", DEFAULT_LENGTH), + inReset = op.inTriggerButton("Reset Index"), + outArr = op.outArray("Result"), + outIndex = op.outNumber("Index"); + +let index = 0; +let arr = []; +inNum.onChange = updateLength; +updateLength(); + +function updateLength() +{ + arr.length = Math.floor(inNum.get()); + for (let i = 0; i < arr.length; i++) arr[i] = 0; + outArr.set(null); + outArr.set(arr); +} + +inWrite.onTriggered = function () +{ + index = Math.floor(index % inNum.get()); + arr[index] = inVal.get(); + outIndex.set(index); + outArr.set(null); + outArr.set(arr); + index++; +}; + +inReset.onTriggered = function () +{ + index = 0; +}; + + +}; + +Ops.Array.RingBuffer.prototype = new CABLES.Op(); +CABLES.OPS["38b77ea5-b44b-48b3-9433-09a83753a209"]={f:Ops.Array.RingBuffer,objName:"Ops.Array.RingBuffer"}; + + + + +// ************************************************************** +// +// Ops.Array.RotateArray +// +// ************************************************************** + +Ops.Array.RotateArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArray = op.inArray("Array in"); +const count = op.inValueInt("Rotate amount", 0); +const outArray = op.outArray("ArrayOut"); + +let newArr = []; +outArray.set(newArr); + +count.onChange = +inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + let rotateIndex = -count.get(); + + newArr = rotate(inArray.get(), rotateIndex, 0); + outArray.set(null); + outArray.set(newArr); +}; + +// https://gist.github.com/aubergene/7ecfe624199e68f60258 +function rotate(array, n, guard) +{ + let head, tail; + n = (n === null) || guard ? 1 : n; + n %= array.length; + tail = array.slice(n); + + head = array.slice(0, n); + return tail.concat(head); +} + + +}; + +Ops.Array.RotateArray.prototype = new CABLES.Op(); +CABLES.OPS["e435d07b-8545-4469-befb-868510adcb76"]={f:Ops.Array.RotateArray,objName:"Ops.Array.RotateArray"}; + + + + +// ************************************************************** +// +// Ops.Array.RouteArray +// +// ************************************************************** + +Ops.Array.RouteArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +const + NUM_PORTS = 10, + DEFAULT_ARRAY_DEFAULT = [], + indexPort = op.inInt('index'), + arrayPort = op.inArray('array in'), + defaultArrayPort = op.inArray('default array', DEFAULT_ARRAY_DEFAULT), + arrayPorts = createOutPorts(DEFAULT_ARRAY_DEFAULT); + +indexPort.onChange = arrayPort.onChange = defaultArrayPort.onChange = update; + +setDefaultValues(); +update(); + +function createOutPorts() +{ + var arr = []; + for(var i=0; i port.set(null)); + if(defaultArrayPort.get()) + { + arrayPorts.forEach(port => port.set(defaultValue)); + } +}; + +function update() +{ + setDefaultValues(); + var index = indexPort.get(); + var value = arrayPort.get(); + + index = Math.floor(index); + index = clamp(index, 0, NUM_PORTS-1); + arrayPorts[index].set(value); +}; + +function clamp(value, min, max) +{ + return Math.min(Math.max(value, min), max); +}; + + +}; + +Ops.Array.RouteArray.prototype = new CABLES.Op(); +CABLES.OPS["6597c011-7b2a-4d7c-93f5-40fcc2047cfe"]={f:Ops.Array.RouteArray,objName:"Ops.Array.RouteArray"}; + + + + +// ************************************************************** +// +// Ops.Array.SetNumberArray +// +// ************************************************************** + +Ops.Array.SetNumberArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTriggerButton("exe"), + array = op.inArray("array"), + index = op.inValueInt("index"), + value = op.inValueFloat("value"), + values = op.outArray("values"); + +exe.onTriggered = update; + +let newArr = []; + +function update() +{ + let arr = array.get(); + + if (!Array.isArray(arr)) + { + values.set(null); + return; + } + + newArr.length = arr.length; + for (let i = 0; i < arr.length; i++)newArr[i] = arr[i]; + + newArr[Math.floor(index.get())] = value.get(); + + values.set(null); + values.set(newArr); +} + + +}; + +Ops.Array.SetNumberArray.prototype = new CABLES.Op(); +CABLES.OPS["ed39fd33-1b8d-4d04-a44d-4a22e4477a0a"]={f:Ops.Array.SetNumberArray,objName:"Ops.Array.SetNumberArray"}; + + + + +// ************************************************************** +// +// Ops.Array.SetNumbersArray3 +// +// ************************************************************** + +Ops.Array.SetNumbersArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTriggerButton("exe"), + array = op.inArray("array"), + index = op.inValueInt("index"), + value1 = op.inValueFloat("Value 1"), + value2 = op.inValueFloat("Value 2"), + value3 = op.inValueFloat("Value 3"), + values = op.outArray("values"); + +let newArr = []; + +function update() +{ + let arr = array.get(); + if (!arr) return; + + newArr.length = arr.length; + for (let i = 0; i < arr.length; i++) newArr[i] = arr[i]; + + const idx = 3 * Math.abs(Math.floor(index.get())); + newArr[idx + 0] = value1.get(); + newArr[idx + 1] = value2.get(); + newArr[idx + 2] = value3.get(); + + values.set(null); + values.set(newArr); +} + +exe.onTriggered = update; + + +}; + +Ops.Array.SetNumbersArray3.prototype = new CABLES.Op(); +CABLES.OPS["c3b2d0c9-0aa5-4605-948a-a53784fa8f1a"]={f:Ops.Array.SetNumbersArray3,objName:"Ops.Array.SetNumbersArray3"}; + + + + +// ************************************************************** +// +// Ops.Array.ShuffleArray3_v2 +// +// ************************************************************** + +Ops.Array.ShuffleArray3_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array3"), + inSeed = op.inFloat("Seed", 1), + outArr = op.outArray("Result"); + +const newArr = []; +const rndArr = []; +inArr.onChange = update; +inSeed.onChange = update; + +function fisherYatesShuffle(array) +{ + let i = 0; + let j = 0; + let temp = null; + + for (i = array.length - 1; i >= 0; i -= 1) + { + j = Math.floor(Math.seededRandom() * (i + 1)); + temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } +} + +function update() +{ + const arr = inArr.get(); + + if (!arr || arr.length % 3 !== 0) return; + if (arr.length != newArr.length) newArr.length = arr.length; + + let i = 0; + let j = 0; + const temp = null; + Math.randomSeed = inSeed.get() + 1; + + for (i = 0; i < arr.length; i += 3) rndArr[i / 3] = i; + + for (let i = 0; i < 4; i++) fisherYatesShuffle(rndArr); + + for (i = 0; i < arr.length; i += 3) + { + j = rndArr[i / 3]; + + newArr[i + 0] = arr[j + 0]; + newArr[i + 1] = arr[j + 1]; + newArr[i + 2] = arr[j + 2]; + } + + outArr.set(null); + outArr.set(newArr); +} + + +}; + +Ops.Array.ShuffleArray3_v2.prototype = new CABLES.Op(); +CABLES.OPS["147f0224-3241-423d-927c-14306134befa"]={f:Ops.Array.ShuffleArray3_v2,objName:"Ops.Array.ShuffleArray3_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.ShuffleArray_v2 +// +// ************************************************************** + +Ops.Array.ShuffleArray_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array3"), + inSeed = op.inFloat("Seed", 1), + outArr = op.outArray("Result"); + +let newArr = []; +let rndArr = []; +inArr.onChange = update; +inSeed.onChange = update; + +function fisherYatesShuffle(array) +{ + let i = 0; + let j = 0; + let temp = null; + + for (i = array.length - 1; i > 0; i -= 1) + { + j = Math.floor(Math.seededRandom() * (i + 1)); + temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } +} + +function update() +{ + const arr = inArr.get(); + + if (!arr) + { + outArr.set(null); + return; + } + if (arr.length != newArr.length) + { + rndArr.length = + newArr.length = arr.length; + } + + let j = 0; + let temp = null; + Math.randomSeed = inSeed.get(); + + for (let i = 0; i < arr.length; i++) + { + rndArr[i] = i; + } + + fisherYatesShuffle(rndArr); + + for (let i = 0; i < arr.length; i++) + { + j = rndArr[i]; + newArr[i] = arr[j]; + } + + outArr.set(null); + outArr.set(newArr); +} + + +}; + +Ops.Array.ShuffleArray_v2.prototype = new CABLES.Op(); +CABLES.OPS["ea752da3-79f4-4674-95bf-722634365f53"]={f:Ops.Array.ShuffleArray_v2,objName:"Ops.Array.ShuffleArray_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.SimplexArray +// +// ************************************************************** + +Ops.Array.SimplexArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArrayX = op.inArray("Array in X "), + inTimeArray = op.inArray("Array time"), + inTime = op.inFloat("Time in Y", 0), + seed = op.inFloatSlider("Seed 0-1", 0.5), + inFrequency = op.inFloat("Frequency", 10), + outArray = op.outArray("Array out"), + outArrayLength = op.outNumber("Array length out"); + +let showingError = false; + +let newArr = []; +outArray.set(newArr); + +seed.set(Math.random()); + +inArrayX.onChange = inTime.onChange = inFrequency.onChange = update; + +seed.onChange = function () +{ + Math.randomSeed = seed.get(); + noise.seed(Math.seededRandom()); + update(); +}; +function update() +{ + let arr = inArrayX.get(); + let arrTime = inTimeArray.get(); + + let time = inTime.get(); + let mult = inFrequency.get(); + + if (arrTime) + { + if (!arr || !arrTime) + { + outArray.set(null); + return; + } + if (arr.length != arrTime.length) + { + if (!showingError) + { + op.uiAttr({ "error": "Arrays do not have the same length !" }); + outArrayLength.set(0); + showingError = true; + } + outArray.set(null); + return; + } + if (showingError) + { + showingError = false; + op.uiAttr({ "error": null }); + } + + if (newArr.length != arr.length)newArr.length = arr.length; + + for (var i = 0; i < arr.length; i++) + { + newArr[i] = noise.simplex2(arr[i] * mult, arrTime[i] + time); + } + } + else if (!arrTime) + { + if (!arr) + { + outArray.set(null); + return; + } + + if (newArr.length != arr.length)newArr.length = arr.length; + + for (var i = 0; i < arr.length; i++) + { + newArr[i] = noise.simplex2(arr[i] * mult, time); + } + } + + outArray.set(null); + outArray.set(newArr); + outArrayLength.set(newArr.length); +} + + +}; + +Ops.Array.SimplexArray.prototype = new CABLES.Op(); +CABLES.OPS["094af2fe-c511-4c89-b384-4cc7c4c7b626"]={f:Ops.Array.SimplexArray,objName:"Ops.Array.SimplexArray"}; + + + + +// ************************************************************** +// +// Ops.Array.SmoothArray +// +// ************************************************************** + +Ops.Array.SmoothArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// look at http://sol.gfxile.net/interpolation/ +const exec = op.inTrigger("Execute"), + inArray = op.inArray("Array In"), + inModeBool = op.inBool("Separate inc/dec", false), + incFactor = op.inValue("Inc factor", 4), + decFactor = op.inValue("Dec factor", 4), + next = op.outTrigger("Next"), + outArray = op.outArray("Array Out"); + +let goal = []; +let reset = false; +let lastTrigger = 0; + +let newArr = []; +outArray.set(newArr); + +let divisorUp; +let divisorDown; + +let selectedMode = false; + +onFilterChange(); +getDivisors(); +function onFilterChange() +{ + selectedMode = inModeBool.get(); + + if (!selectedMode) + { + decFactor.setUiAttribs({ "greyout": true }); + incFactor.setUiAttribs({ "title": "Inc/Dec factor" }); + } + else + { + decFactor.setUiAttribs({ "greyout": false }); + incFactor.setUiAttribs({ "title": "Inc factor" }); + } + + getDivisors(); + update(); +} + +function getDivisors() +{ + divisorUp = incFactor.get(); + + if (selectedMode == false) divisorDown = incFactor.get(); + else divisorDown = decFactor.get(); + + if (divisorUp <= 0 || divisorUp != divisorUp)divisorUp = 0.0001; + if (divisorDown <= 0 || divisorDown != divisorDown)divisorDown = 0.0001; + if (divisorUp <= 1.0) divisorUp = 1.0; + if (divisorDown <= 1.0) divisorDown = 1.0; +} + +inArray.onLinkChanged = () => +{ + if (inArray) inArray.copyLinkedUiAttrib("stride", outArray); +}; + +inArray.onChange = function () +{ + let arr = inArray.get(); + if (!arr) return; + + for (let i = 0; i < arr.length; i++) + { + goal[i] = arr[i] || 0; + } +}; + +let oldVal = 0; + +function update() +{ + let arr = inArray.get(); + if (!arr) return; + + if (newArr.length != arr.length) + { + newArr.length = arr.length || 0; + reset = true; + } + + let tm = 1; + if (CABLES.now() - lastTrigger > 500 || lastTrigger === 0)reset = true; + else tm = (CABLES.now() - lastTrigger) / 17; + lastTrigger = CABLES.now(); + + if (reset) + { + for (var i = 0; i < arr.length; i++) + { + newArr[i] = arr[i]; + } + reset = false; + } + + for (var i = 0; i < arr.length; i++) + { + let val = newArr[i]; + + let diff = goal[i] - val; + + if (diff >= 0) + val += (diff) / (divisorDown * tm); + else + val += (diff) / (divisorUp * tm); + + if (val > 0 && val < 0.000000001)val = 0; + if (!val) val = 0; + + if (newArr[i] != val) + { + newArr[i] = val; + oldVal = val; + } + } + outArray.set(null); + outArray.set(newArr); + + next.trigger(); +} + +exec.onTriggered = function () +{ + update(); +}; + +incFactor.onChange = decFactor.onChange = getDivisors; +inModeBool.onChange = onFilterChange; +update(); + + +}; + +Ops.Array.SmoothArray.prototype = new CABLES.Op(); +CABLES.OPS["8fd2ed9b-02e5-4349-b7bc-6665ca240ffa"]={f:Ops.Array.SmoothArray,objName:"Ops.Array.SmoothArray"}; + + + + +// ************************************************************** +// +// Ops.Array.SortArray +// +// ************************************************************** + +Ops.Array.SortArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const arrayIn = op.inArray("Array to sort"), + sortMode = op.inSwitch("Sorting mode", ["Sort ascending", "Sort descending"], "Sort ascending"), + arrayOut = op.outArray("Sorted array"); +let arrOut = []; + +arrayIn.onChange = sortMode.onChange = update; +update(); + +function update() +{ + let arrIn = arrayIn.get(); + + arrOut.length = 0; + + if (!arrIn) + { + arrayOut.set(null); + return; + } + + arrOut.length = arrIn.length; + + let i; + for (i = 0; i < arrIn.length; i++) + { + arrOut[i] = arrIn[i]; + } + + if (sortMode.get() === "Sort ascending") + { + arrOut.sort(function (a, b) { return a - b; }); + } + else + { + arrOut.sort(function (a, b) { return b - a; }); + } + + arrayOut.set(null); + arrayOut.set(arrOut); +} + + +}; + +Ops.Array.SortArray.prototype = new CABLES.Op(); +CABLES.OPS["9ab49d2c-b4e4-4fc2-9e6e-8e664e094369"]={f:Ops.Array.SortArray,objName:"Ops.Array.SortArray"}; + + + + +// ************************************************************** +// +// Ops.Array.SortArray3 +// +// ************************************************************** + +Ops.Array.SortArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let inArr = op.inArray("Array"); +let what = op.inValueSelect("What", ["None", "X", "Y", "Z", "XYZ"]); +what.set("X"); +let outArr = op.outArray("Result"); + +let comparator = compareX; +let arrArr = []; + +op.toWorkPortsNeedToBeLinked(inArr); + +function compareX(a, b) { return a[0] - b[0]; } + +function compareY(a, b) { return a[1] - b[1]; } + +// function compareZ(a, b){ return b[2]-a[2]; } +function compareZ(a, b) { return a[2] - b[2]; } + +function compareXYZ(a, b) { return (a[0] + a[1] + a[2]) - (b[0] + b[1] + b[2]); } + +function sliceArray() +{ + let size = 3; + arrArr.length = 0; + let bigarray = inArr.get(); + for (let i = 0; i < bigarray.length; i += size) + { + arrArr.push(bigarray.slice(i, i + size)); + } +} + +inArr.onChange = recalc; + +function recalc() +{ + if (!Array.isArray(inArr.get())) + { + outArr.set(null); + return; + } + if (!comparator) + { + outArr.set(null); + outArr.set(inArr.get()); + return; + } + + let start = performance.now(); + + sliceArray(); + + arrArr.sort(comparator); + outArr.set(null); + + if (arrArr.flat)arrArr = arrArr.flat(); + else arrArr = [].concat.apply([], arrArr); + + outArr.set(null); + outArr.set(arrArr); +} + +what.onChange = function () +{ + // if(what.get()=='None')comparator=null; + if (what.get() == "X")comparator = compareX; + if (what.get() == "Y")comparator = compareY; + if (what.get() == "Z")comparator = compareZ; + if (what.get() == "XYZ")comparator = compareXYZ; + recalc(); +}; + + +}; + +Ops.Array.SortArray3.prototype = new CABLES.Op(); +CABLES.OPS["b0fbc366-09a8-4695-87b3-5946210bf722"]={f:Ops.Array.SortArray3,objName:"Ops.Array.SortArray3"}; + + + + +// ************************************************************** +// +// Ops.Array.SortArray3ByDistance +// +// ************************************************************** + +Ops.Array.SortArray3ByDistance = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + outArr = op.outArray("Result"); + +function dist(x1, y1, z1, x2, y2, z2) +{ + let xd = x2 - x1; + let yd = y2 - y1; + let zd = z2 - z1; + return Math.abs(Math.sqrt(xd * xd + yd * yd + zd * zd)); +} + +inArr.onChange = function () +{ + if (!inArr.get()) return; + + let arr = inArr.get(); + + if (arr.length == 0) return; + + let data = []; + let i = 0; + for (i = 0; i < arr.length; i += 3) + { + data[i / 3] = { + "x": arr[i + 0], + "y": arr[i + 1], + "z": arr[i + 2], + "found": false, + "pos": 0 + }; + } + + let lastPoint = data[0]; + data[0].found = true; + + for (i = 0; i < data.length; i++) + { + let smallest = null; + let smallDist = 999999999; + + for (let j = 0; j < data.length; j++) + { + let d = dist( + lastPoint.x, lastPoint.y, lastPoint.z, + data[j].x, data[j].y, data[j].z + ); + + if (d < smallDist && !data[j].found) + { + smallDist = d; + smallest = data[j]; + } + } + + if (smallest) + { + smallest.pos = i; + smallest.found = true; + lastPoint = smallest; + } + } + + data.sort(function (a, b) + { + return a.pos - b.pos; + }); + + let outArray = []; + for (i = 0; i < data.length; i++) + { + outArray[i * 3 + 0] = data[i].x; + outArray[i * 3 + 1] = data[i].y; + outArray[i * 3 + 2] = data[i].z; + } + outArr.set(outArray); +}; + + +}; + +Ops.Array.SortArray3ByDistance.prototype = new CABLES.Op(); +CABLES.OPS["d67e094b-0206-4ab6-b97f-98a1242b334c"]={f:Ops.Array.SortArray3ByDistance,objName:"Ops.Array.SortArray3ByDistance"}; + + + + +// ************************************************************** +// +// Ops.Array.SortArrayObjectsByKey +// +// ************************************************************** + +Ops.Array.SortArrayObjectsByKey = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const arrayIn = op.inArray('Array to sort'); +const propIn = op.inString('Sort property'); +const sortMode = op.inSwitch('Sorting mode', ['Sort ascending', 'Sort descending'], 'Sort ascending'); +const arrayOut = op.outArray('Sorted array'); + +let arrOut = []; + +arrayIn.onChange = propIn.onChange = sortMode.onChange = update; + +update(); + +function update() { + const path = propIn.get(); + + if (!arrayIn.get() || !path) { + arrayOut.set(null); + return; + } + + arrOut = arrayIn.get(); + + if (sortMode.get() === 'Sort ascending') { + arrOut.sort(function(a, b) { + const properties = Array.isArray(path) ? path : path.split('.'); + const propA = properties.reduce((prev, curr) => prev && prev[curr], a); + const propB = properties.reduce((prev, curr) => prev && prev[curr], b); + if (propA < propB) { + return -1; + } else if ( propA > propB) { + return 1; + } else { + return 0; + } + }); + } else { + arrOut.sort(function(a, b) { + const properties = Array.isArray(path) ? path : path.split('.'); + const propA = properties.reduce((prev, curr) => prev && prev[curr], a); + const propB = properties.reduce((prev, curr) => prev && prev[curr], b); + if (propA < propB) { + return 1; + } else if ( propA > propB) { + return -1; + } else { + return 0; + } + }); + } + + arrayOut.set(null); + arrayOut.set(arrOut); +} + + +}; + +Ops.Array.SortArrayObjectsByKey.prototype = new CABLES.Op(); +CABLES.OPS["6a5a6674-9247-47ea-b9e1-0438ca07529a"]={f:Ops.Array.SortArrayObjectsByKey,objName:"Ops.Array.SortArrayObjectsByKey"}; + + + + +// ************************************************************** +// +// Ops.Array.SortArrayWithIndices +// +// ************************************************************** + +Ops.Array.SortArrayWithIndices = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + arrayIn = op.inArray("Array to sort"), + sortMode = op.inSwitch("Sorting mode", ["Ascending", "Descending"], "Ascending"), + arrayOut = op.outArray("Sorted array"), + arrayOutIdx = op.outArray("Sorted Indices"); + +let arrOut = []; +let indices = []; + +arrayIn.onChange = sortMode.onChange = update; +update(); + +function update() +{ + let arrIn = arrayIn.get(); + + arrOut.length = 0; + + if (!arrIn) + { + arrayOut.set(null); + return; + } + + arrOut.length = arrIn.length; + indices.length = arrIn.length; + + for (let i = 0; i < arrIn.length; ++i) indices[i] = i; + + for (let i = 0; i < arrIn.length; i++) + { + arrOut[i] = arrIn[i]; + } + + if (sortMode.get() === "Sort ascending") + { + indices.sort(function (a, b) { return arrOut[a] < arrOut[b] ? -1 : arrOut[a] > arrOut[b] ? 1 : 0; }); + + // arrOut.sort(function (a, b) { return a - b; }); + } + else + { + indices.sort(function (a, b) { return arrOut[a] > arrOut[b] ? -1 : arrOut[a] < arrOut[b] ? 1 : 0; }); + } + + arrayOut.set(null); + arrayOut.set(arrOut); + arrayOutIdx.set(null); + arrayOutIdx.set(indices); +} + + +}; + +Ops.Array.SortArrayWithIndices.prototype = new CABLES.Op(); +CABLES.OPS["83f78f91-027b-4251-9cd9-70ac676059ff"]={f:Ops.Array.SortArrayWithIndices,objName:"Ops.Array.SortArrayWithIndices"}; + + + + +// ************************************************************** +// +// Ops.Array.SplineLengthArray3 +// +// ************************************************************** + +Ops.Array.SplineLengthArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array3x"), + inCalc = op.inTriggerButton("Calculate"), + outValue = op.outNumber("Length"); + +let needsCalc = true; + +inArr.onChange = function () +{ + needsCalc = true; +}; + +inCalc.onTriggered = function () +{ + if (needsCalc) + { + needsCalc = false; + let arr = inArr.get(); + if (!arr || arr.length < 3) + { + outValue.set(0); + return; + } + + let l = 0; + for (let i = 3; i < arr.length; i += 3) + { + let xd = arr[i - 3] - arr[i + 0]; + let yd = arr[i - 2] - arr[i + 1]; + let zd = arr[i - 1] - arr[i + 2]; + l += Math.sqrt(xd * xd + yd * yd + zd * zd); + } + + if (l != l)l = 0; + outValue.set(l); + } +}; + + +}; + +Ops.Array.SplineLengthArray3.prototype = new CABLES.Op(); +CABLES.OPS["894db700-f220-427d-99fe-553db0a60034"]={f:Ops.Array.SplineLengthArray3,objName:"Ops.Array.SplineLengthArray3"}; + + + + +// ************************************************************** +// +// Ops.Array.SplinePositionAtDistanceArray3 +// +// ************************************************************** + +Ops.Array.SplinePositionAtDistanceArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTriggerButton("Calculate"), + inArr = op.inArray("Array3x"), + inDist = op.inValue("Distance"), + inNormalized = op.inValueBool("Normalized"), + + outNext = op.outTrigger("Next"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outZ = op.outNumber("Z"), + + outSplineLength = op.outNumber("Spline Length"); + +let animX = new CABLES.Anim(); +let animY = new CABLES.Anim(); +let animZ = new CABLES.Anim(); + +let needsMapping = true; + +function dist(x1, y1, z1, x2, y2, z2) +{ + let xd = x1 - x2; + let yd = y1 - y2; + let zd = z1 - z2; + return Math.sqrt(xd * xd + yd * yd + zd * zd); +} + +function splineLength(arr) +{ + let l = 0; + for (let i = 3; i < arr.length; i += 3) + { + l += dist(arr[i - 3], arr[i - 2], arr[i - 1], arr[i + 0], arr[i + 1], arr[i + 2]); + } + + outSplineLength.set(l); + return l; +} + +function mapArrays() +{ + animX.clear(); + animY.clear(); + animZ.clear(); + let arr = inArr.get(); + let sl = splineLength(arr); + + let distPos = 0; + + for (let i = 0; i < arr.length; i += 3) + { + let p = i / (arr.length - 3); + if (i > 0) + { + distPos += dist(arr[i - 3], arr[i - 2], arr[i - 1], arr[i + 0], arr[i + 1], arr[i + 2]); + } + + animX.setValue(distPos, arr[i + 0]); + animY.setValue(distPos, arr[i + 1]); + animZ.setValue(distPos, arr[i + 2]); + } + + needsMapping = false; +} + +inArr.onChange = function () +{ + needsMapping = true; +}; + +inExec.onTriggered = function () +{ + if (needsMapping)mapArrays(); + + let d = inDist.get(); + if (inNormalized.get())d *= outSplineLength.get(); + + outX.set(animX.getValue(d)); + outY.set(animY.getValue(d)); + outZ.set(animZ.getValue(d)); + + outNext.trigger(); +}; + + +}; + +Ops.Array.SplinePositionAtDistanceArray3.prototype = new CABLES.Op(); +CABLES.OPS["02100bb5-fe07-4c1b-8792-67f9add7afff"]={f:Ops.Array.SplinePositionAtDistanceArray3,objName:"Ops.Array.SplinePositionAtDistanceArray3"}; + + + + +// ************************************************************** +// +// Ops.Array.SplinesToLineStripArray +// +// ************************************************************** + +Ops.Array.SplinesToLineStripArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArrays = op.inArray("Array"), + outArr = op.outArray("Result"); + +inArrays.onChange = function () +{ + let inArr = inArrays.get(); + + let arr = []; + + if (!inArr) return; + + for (let i = 0; i < inArr.length; i++) + { + let pointArray = inArr[i]; + + for (let j = 3; j < pointArray.length - 3; j += 3) + { + arr.push(pointArray[j - 3]); + arr.push(pointArray[j - 2]); + arr.push(pointArray[j - 1]); + arr.push(pointArray[j + 0]); + arr.push(pointArray[j + 1]); + arr.push(pointArray[j + 2]); + } + } + + outArr.set(null); + outArr.set(arr); +}; + + +}; + +Ops.Array.SplinesToLineStripArray.prototype = new CABLES.Op(); +CABLES.OPS["0877ac11-4ebc-484c-aa10-276040756d0a"]={f:Ops.Array.SplinesToLineStripArray,objName:"Ops.Array.SplinesToLineStripArray"}; + + + + +// ************************************************************** +// +// Ops.Array.StringToCharArray +// +// ************************************************************** + +Ops.Array.StringToCharArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + strIn = op.inString("String", "test"), + convertToNumbersBool = op.inBool("Convert to numbers", false), + arrOut = op.outArray("Array out"); + +const arr = []; + +function update() +{ + const str = strIn.get(); + if (str !== undefined && str !== null) + { + const strLength = str.length; + + arr.length = 0; + arr.length = strLength; + if (convertToNumbersBool.get()) + { + for (let i = 0; i < strLength; i++) + { + arr[i] = str.charCodeAt(i); + } + } + else + { + for (let i = 0; i < strLength; i++) + { + arr[i] = str.charAt(i); + } + } + + op.setUiError("null", null); + arrOut.set(null); + arrOut.set(arr); + } + else + { + op.setUiError("null", "input is not of type string"); + arrOut.set(null); + } +} + +update(); +strIn.onChange = convertToNumbersBool.onChange = update; + + +}; + +Ops.Array.StringToCharArray.prototype = new CABLES.Op(); +CABLES.OPS["02ca1ecb-b087-4000-90f9-de96dca2f459"]={f:Ops.Array.StringToCharArray,objName:"Ops.Array.StringToCharArray"}; + + + + +// ************************************************************** +// +// Ops.Array.SubdivideArray1 +// +// ************************************************************** + +Ops.Array.SubdivideArray1 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let inArr = op.inArray("Points"); +let subDivs = op.inInt("Num Subdivs", 5); +let bezier = op.inValueBool("Smooth", true); +let bezierEndPoints = op.inValueBool("Bezier Start/End Points", true); + +let result = op.outArray("Result"); + +op.toWorkPortsNeedToBeLinked(inArr); + +subDivs.onChange = calc; +bezier.onChange = calc; +inArr.onChange = calc; +bezierEndPoints.onChange = calc; + +function ip(x0, x1, x2, t)// Bezier +{ + let r = (x0 * (1 - t) * (1 - t) + 2 * x1 * (1 - t) * t + x2 * t * t); + return r; +} + +let arr = []; + +function calc() +{ + if (!inArr.get()) + { + result.set(null); + return; + } + const subd = Math.floor(subDivs.get()); + let inPoints = inArr.get(); + + if (inPoints.length < 3) return; + + let i = 0; + let j = 0; + let k = 0; + let count = 0; + + if (subd > 0 && !bezier.get()) + { + const newLen = (inPoints.length - 1) * subd; + if (newLen != arr.length) + { + arr.length = newLen; + } + + count = 0; + for (i = 0; i < inPoints.length - 1; i++) + { + for (j = 0; j < subd; j++) + { + arr[count] = + inPoints[i] + (inPoints[i + 1] - inPoints[i]) * j / subd; + count++; + } + } + } + else + if (subd > 0 && bezier.get()) + { + let newLen = (inPoints.length - 6) * (subd - 1); + if (bezierEndPoints.get())newLen += 6; + + if (newLen != arr.length) arr.length = Math.floor(Math.abs(newLen)); + count = 0; + + if (bezierEndPoints.get()) + { + arr[0] = inPoints[0]; + count = 1; + } + + for (i = 1; i < inPoints.length - 1; i++) + { + for (j = 0; j < subd; j++) + { + let p = ip( + (inPoints[i - 1] + inPoints[i]) / 2, + inPoints[i + 0], + (inPoints[i + 1] + inPoints[i]) / 2, + j / subd + ); + arr[count] = p; + count++; + } + } + + if (bezierEndPoints.get()) + { + arr[count] = inPoints[inPoints.length - 1]; + } + } + result.set(null); + result.set(arr); +} + + +}; + +Ops.Array.SubdivideArray1.prototype = new CABLES.Op(); +CABLES.OPS["fa10c94d-d14e-41d3-b690-26d7cac5f146"]={f:Ops.Array.SubdivideArray1,objName:"Ops.Array.SubdivideArray1"}; + + + + +// ************************************************************** +// +// Ops.Array.SubdivideArray3_v2 +// +// ************************************************************** + +Ops.Array.SubdivideArray3_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Points"), + subDivs = op.inInt("Num Subdivs", 5), + bezier = op.inValueBool("Smooth", true), + inLoop = op.inValueBool("Loop", false), + bezierEndPoints = op.inValueBool("Bezier Start/End Points", true), + result = op.outArray("Result"); + +op.toWorkPortsNeedToBeLinked(inArr); + +let arr = []; + +subDivs.onChange = + inLoop.onChange = + bezier.onChange = + inArr.onChange = + bezierEndPoints.onChange = calc; + +function ip(x0, x1, x2, t)// Bezier +{ + const r = (x0 * (1 - t) * (1 - t) + 2 * x1 * (1 - t) * t + x2 * t * t); + return r; +} + +function calc() +{ + inLoop.setUiAttribs({ "greyout": !bezier.get() }); + bezierEndPoints.setUiAttribs({ "greyout": !bezier.get() }); + + if (!inArr.get()) + { + result.set(null); + return; + } + const subd = Math.floor(subDivs.get()); + const inPoints = inArr.get(); + + if (inPoints.length < 3) return; + + let i = 0; + let j = 0; + let k = 0; + let count = 0; + + if (subd > 0 && !bezier.get()) + { + const newLen = (inPoints.length - 3) * subd + 3; + if (newLen != arr.length) + { + arr.length = newLen; + } + + count = 0; + for (i = 0; i < inPoints.length - 3; i += 3) + { + for (j = 0; j < subd; j++) + { + for (k = 0; k < 3; k++) + { + arr[count] = + inPoints[i + k] + (inPoints[i + k + 3] - inPoints[i + k]) * j / subd; + count++; + } + } + } + arr[newLen - 3] = inPoints[inPoints.length - 3]; + arr[newLen - 2] = inPoints[inPoints.length - 2]; + arr[newLen - 1] = inPoints[inPoints.length - 1]; + } + else + if (subd > 0 && bezier.get()) + { + let newLen = (inPoints.length - 6) * (subd - 1); + if (bezierEndPoints.get())newLen += 6; + + if (newLen != arr.length) arr.length = Math.floor(Math.abs(newLen)); + count = 0; + + if (bezierEndPoints.get()) + { + arr[0] = inPoints[0]; + arr[1] = inPoints[1]; + arr[2] = inPoints[2]; + count = 3; + } + + const doLoop = inLoop.get(); + + function idx(i) + { + if (doLoop) return i % (inPoints.length - 3); + else return i; + } + + let endi = inPoints.length - 3; + if (doLoop)endi = inPoints.length; + + for (i = 3; i < endi; i += 3) + { + for (j = 0; j < subd; j++) + { + for (k = 0; k < 3; k++) + { + const p = ip( + (inPoints[idx(i + k - 3)] + inPoints[idx(i + k)]) / 2, + inPoints[idx(i + k + 0)], + (inPoints[idx(i + k + 3)] + inPoints[idx(i + k + 0)]) / 2, + j / subd + ); + arr[count] = p; + count++; + } + } + } + + if (doLoop) + { + arr[count + 0] = arr[0]; + arr[count + 1] = arr[1]; + arr[count + 2] = arr[2]; + count++; count++; count++; + } + + if (bezierEndPoints.get()) + { + arr[count - 0] = inPoints[inPoints.length - 3]; + arr[count + 1] = inPoints[inPoints.length - 2]; + arr[count + 2] = inPoints[inPoints.length - 1]; + } + } + if (subd == 0) + { + arr = Array.from(inPoints); + } + + result.set(null); + result.set(arr); +} + + +}; + +Ops.Array.SubdivideArray3_v2.prototype = new CABLES.Op(); +CABLES.OPS["d8bb5727-35e4-4e2a-999b-112ebc659720"]={f:Ops.Array.SubdivideArray3_v2,objName:"Ops.Array.SubdivideArray3_v2"}; + + + + +// ************************************************************** +// +// Ops.Array.SwitchArray +// +// ************************************************************** + +Ops.Array.SwitchArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let idx = op.inValueInt("Index"); +let valuePorts = []; +let result = op.outArray("Result"); + +idx.onChange = update; + +for (let i = 0; i < 10; i++) +{ + let p = op.inArray("Array " + i); + valuePorts.push(p); + p.onChange = update; +} + +function update() +{ + if (idx.get() >= 0 && valuePorts[idx.get()]) + { + result.set(valuePorts[idx.get()].get()); + } +} + + +}; + +Ops.Array.SwitchArray.prototype = new CABLES.Op(); +CABLES.OPS["3fab881c-c2cf-42a0-9c42-2d8edfd93f57"]={f:Ops.Array.SwitchArray,objName:"Ops.Array.SwitchArray"}; + + + + +// ************************************************************** +// +// Ops.Array.SwitchArrayOnTrigger +// +// ************************************************************** + +Ops.Array.SwitchArrayOnTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let N_PORTS = 8; + +// input ports +const + inTrigger1 = op.inTriggerButton("Trigger 1"), + inArray1 = op.inArray("Array 1"), + inTrigger2 = op.inTriggerButton("Trigger 2"), + inArray2 = op.inArray("Array 2"), + inTrigger3 = op.inTriggerButton("Trigger 3"), + inArray3 = op.inArray("Array 3"), + inTrigger4 = op.inTriggerButton("Trigger 4"), + inArray4 = op.inArray("Array 4"), + inTrigger5 = op.inTriggerButton("Trigger 5"), + inArray5 = op.inArray("Array 5"), + inTrigger6 = op.inTriggerButton("Trigger 6"), + inArray6 = op.inArray("Array 6"), + inTrigger7 = op.inTriggerButton("Trigger 7"), + inArray7 = op.inArray("Array 7"), + inTrigger8 = op.inTriggerButton("Trigger 8"), + inArray8 = op.inArray("Array 8"); + +// output ports +let outArray = op.outArray("Out Array"); + +// change listeners +inTrigger1.onTriggered = function () +{ + outArray.set(inArray1.get()); +}; +inTrigger2.onTriggered = function () +{ + outArray.set(inArray2.get()); +}; +inTrigger3.onTriggered = function () +{ + outArray.set(inArray3.get()); +}; +inTrigger4.onTriggered = function () +{ + outArray.set(inArray4.get()); +}; +inTrigger5.onTriggered = function () +{ + outArray.set(inArray5.get()); +}; +inTrigger6.onTriggered = function () +{ + outArray.set(inArray6.get()); +}; +inTrigger7.onTriggered = function () +{ + outArray.set(inArray7.get()); +}; +inTrigger8.onTriggered = function () +{ + outArray.set(inArray8.get()); +}; + + +}; + +Ops.Array.SwitchArrayOnTrigger.prototype = new CABLES.Op(); +CABLES.OPS["08e0cc91-8fda-48c1-a98e-2bf7265d6683"]={f:Ops.Array.SwitchArrayOnTrigger,objName:"Ops.Array.SwitchArrayOnTrigger"}; + + + + +// ************************************************************** +// +// Ops.Array.TextureBufferArray +// +// ************************************************************** + +Ops.Array.TextureBufferArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inExec = op.inTrigger("Write"); + +const inTexture = op.inTexture("Texture"); +const inNum = op.inValueInt("Num", 8); + +const outArr = op.outArray("Result"); + +const inSort = op.inValueBool("Order"); +const inClear = op.inValueBool("Clear", true); + +const cgl = op.patch.cgl; +const frameBuf = cgl.gl.createFramebuffer(); +const renderbuffer = cgl.gl.createRenderbuffer(); +let index = 0; +const textures = []; +let quadMesh = null; +let inited = false; +const sorted = []; + +inNum.onChange = init; + +const bgFrag = "" + .endl() + "UNI float a;" + .endl() + "UNI sampler2D tex;" + .endl() + "IN vec2 texCoord;" + .endl() + "void main()" + .endl() + "{" + .endl() + " vec4 col=texture2D(tex,texCoord);" + .endl() + " outColor= col;" + .endl() + "}"; +const bgShader = new CGL.Shader(cgl, "imgcompose bg"); +bgShader.setSource(bgShader.getDefaultVertexShader(), bgFrag); +const textureUniform = new CGL.Uniform(bgShader, "t", "tex", 0); + +inExec.onTriggered = render; + +function init() +{ + if (inNum.get() == 0) return; + for (let i = 0; i < textures.length; i++) + { + textures[i].delete(); + } + + if (!inTexture.get()) return; + textures.length = inNum.get(); + sorted.length = inNum.get(); + + // op.log(inTexture.get()); + + for (let i = 0; i < inNum.get(); i++) + { + textures[i] = inTexture.get().clone(); + // textures[i].updateMipMap(); + } + + // cgl.gl.bindFramebuffer(cgl.gl.FRAMEBUFFER, null); + + // cgl.gl.bindFramebuffer(cgl.gl.FRAMEBUFFER, frameBuf); + cgl.gl.bindFramebuffer(cgl.gl.FRAMEBUFFER, frameBuf); + cgl.gl.framebufferTexture2D(cgl.gl.FRAMEBUFFER, cgl.gl.COLOR_ATTACHMENT0, cgl.gl.TEXTURE_2D, textures[0].tex, 0); + + // cgl.gl.bindRenderbuffer(cgl.gl.RENDERBUFFER, renderbuffer); + + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, null); + // cgl.gl.bindRenderbuffer(cgl.gl.RENDERBUFFER, null); + cgl.gl.bindFramebuffer(cgl.gl.FRAMEBUFFER, null); + + // textures[0].updateMipMap(); + + inited = true; +} + +function createMesh() +{ + const geom = new CGL.Geometry("textureEffect rect"); + + geom.vertices = [ + 1.0, 1.0, 0.0, + -1.0, 1.0, 0.0, + 1.0, -1.0, 0.0, + -1.0, -1.0, 0.0 + ]; + + geom.texCoords = [ + 1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 0.0, 0.0 + ]; + + geom.verticesIndices = [ + 0, 1, 2, + 2, 1, 3 + ]; + + quadMesh = new CGL.Mesh(cgl, geom); +} + +function render() +{ + if (!inTexture.get()) + { + op.log("no tex 1"); + return; + } + + if (!inTexture.get().tex) + { + op.log("no tex 2"); + return; + } + + if (!quadMesh)createMesh(); + if (!inited || !frameBuf)init(); + if (!textures[0] || textures.length == 0) + { + op.log("no tex"); + return; + } + + if (!textures[0].compareSettings(inTexture.get()))init(); + // if(inTexture.get().width!=textures[0].width)init(); + // if(inTexture.get().height!=textures[0].height)init(); + + index %= inNum.get(); + + cgl.gl.bindFramebuffer(cgl.gl.FRAMEBUFFER, frameBuf); + cgl.gl.framebufferTexture2D(cgl.gl.FRAMEBUFFER, cgl.gl.COLOR_ATTACHMENT0, cgl.gl.TEXTURE_2D, textures[index].tex, 0); + cgl.pushGlFrameBuffer(frameBuf); + + cgl.pushDepthTest(false); + + if (inClear.get()) + { + cgl.gl.clearColor(0, 0, 0, 1); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + } + + cgl.pushModelMatrix(); + + cgl.pushPMatrix(); + cgl.gl.viewport(0, 0, inTexture.get().width, inTexture.get().height); + mat4.perspective(cgl.pMatrix, 45, inTexture.get().width / inTexture.get().height, 0.1, 1100.0); + + cgl.pushPMatrix(); + mat4.identity(cgl.pMatrix); + + cgl.pushViewMatrix(); + mat4.identity(cgl.vMatrix); + + cgl.pushModelMatrix(); + mat4.identity(cgl.mMatrix); + + // here be rendering + + cgl.pushShader(bgShader); + // cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, inTexture.get().tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, ); + + quadMesh.render(cgl.getShader()); + + cgl.gl.bindFramebuffer(cgl.gl.FRAMEBUFFER, cgl.popGlFrameBuffer()); + + cgl.popShader(); + + cgl.popDepthTest(); + cgl.popModelMatrix(); + + cgl.popPMatrix(); + cgl.popModelMatrix(); + cgl.popViewMatrix(); + + cgl.popPMatrix(); + cgl.resetViewPort(); + + op.patch.cgl.gl.bindTexture(op.patch.cgl.gl.TEXTURE_2D, textures[index].tex); + // this._colorTextures[i].updateMipMap(); + textures[index].updateMipMap(); + op.patch.cgl.gl.bindTexture(op.patch.cgl.gl.TEXTURE_2D, null); + + if (inSort.get()) + { + for (let i = 0; i < textures.length; i++) + { + sorted[textures.length - i - 1] = textures[(index + i + 1) % inNum.get()]; + } + + outArr.set(null); + outArr.set(sorted); + } + else + { + outArr.set(null); + outArr.set(textures); + } + index++; +} + + +}; + +Ops.Array.TextureBufferArray.prototype = new CABLES.Op(); +CABLES.OPS["04dc13d2-e339-4e1d-82c5-9c9ff1b175b8"]={f:Ops.Array.TextureBufferArray,objName:"Ops.Array.TextureBufferArray"}; + + + + +// ************************************************************** +// +// Ops.Array.TransformArray3 +// +// ************************************************************** + +Ops.Array.TransformArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTriggerButton("Transform"), + inArr = op.inArray("Array", 3), + transX = op.inFloat("Translate X"), + transY = op.inFloat("Translate Y"), + transZ = op.inFloat("Translate Z"), + scaleX = op.inFloat("Scale X", 1), + scaleY = op.inFloat("Scale Y", 1), + scaleZ = op.inFloat("Scale Z", 1), + rotX = op.inFloat("Rotation X"), + rotY = op.inFloat("Rotation Y"), + rotZ = op.inFloat("Rotation Z"), + next = op.outTrigger("Next"), + outArr = op.outArray("Result", 3); + +op.setPortGroup("Translation", [transX, transY, transZ]); +op.setPortGroup("Scale", [scaleX, scaleY, scaleZ]); +op.setPortGroup("Rotation", [rotX, rotY, rotZ]); + +let resultArr = []; +let needsCalc = true; + +let rotVec = vec3.create(); +let emptyVec = vec3.create(); +let transVec = vec3.create(); +let centerVec = vec3.create(); + +inExec.onTriggered = doTransform; + +inArr.onChange = +transX.onChange = transY.onChange = transZ.onChange = +scaleX.onChange = scaleY.onChange = scaleZ.onChange = +rotX.onChange = rotY.onChange = rotZ.onChange = calcLater; + +function calcLater() +{ + needsCalc = true; +} + +function doTransform() +{ + let arr = inArr.get(); + if (!arr) + { + outArr.set(null); + return; + } + if (needsCalc) + { + resultArr.length = arr.length; + + const nrotx = rotX.get(); + const nroty = rotY.get(); + const nrotz = rotZ.get(); + const scx = scaleX.get(); + const scy = scaleY.get(); + const scz = scaleZ.get(); + const transx = transX.get(); + const transy = transY.get(); + const transz = transZ.get(); + const doRot = nrotx || nroty || nrotz; + + for (let i = 0; i < arr.length; i += 3) + { + resultArr[i + 0] = arr[i + 0] * scx; + resultArr[i + 1] = arr[i + 1] * scy; + resultArr[i + 2] = arr[i + 2] * scz; + + resultArr[i + 0] = resultArr[i + 0] + transx; + resultArr[i + 1] = resultArr[i + 1] + transy; + resultArr[i + 2] = resultArr[i + 2] + transz; + + if (doRot) + { + vec3.set(rotVec, + resultArr[i + 0], + resultArr[i + 1], + resultArr[i + 2]); + + if (nrotx != 0) vec3.rotateX(rotVec, rotVec, transVec, nrotx * CGL.DEG2RAD); + if (nroty != 0) vec3.rotateY(rotVec, rotVec, transVec, nroty * CGL.DEG2RAD); + if (nrotz != 0) vec3.rotateZ(rotVec, rotVec, transVec, nrotz * CGL.DEG2RAD); + + resultArr[i + 0] = rotVec[0]; + resultArr[i + 1] = rotVec[1]; + resultArr[i + 2] = rotVec[2]; + } + } + + needsCalc = false; + outArr.set(null); + outArr.set(resultArr); + } + next.trigger(); +} + + +}; + +Ops.Array.TransformArray3.prototype = new CABLES.Op(); +CABLES.OPS["b18040d6-13d7-4f55-950f-3f95cafa4e90"]={f:Ops.Array.TransformArray3,objName:"Ops.Array.TransformArray3"}; + + + + +// ************************************************************** +// +// Ops.Array.WeaveArrays +// +// ************************************************************** + +Ops.Array.WeaveArrays = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +let inPort1 = op.inArray("Array 1"); +let inPort2 = op.inArray("Array 2"); +let chunkSizePort = op.inValue("Chunk Size", 1); + +// outputs +let outPort = op.outArray("Combined Array"); + +// change listeners +inPort1.onChange = update; +inPort2.onChange = update; +chunkSizePort.onChange = update; + +// functions + +function update() +{ + let newArr = []; + let arr1 = inPort1.get(); + let arr2 = inPort2.get(); + + let chunkSize = chunkSizePort.get(); + if (chunkSize < 1) + { + chunkSize = 1; + // TODO: Show warning in gui!? + } + // array 2 is empty -> just use array 1 + if (arr1 && !arr2) + { + newArr = arr1.slice(0); + } + // array 1 is empty -> just use array 2 + if (!arr1 && arr2) // normally else if + { + newArr = arr2.slice(0); + } + + // array 1 and 2 are not empty -> combine them + else if (arr1 && arr2) + { + for (let i = 0; i < Math.max(arr1.length, arr2.length); i += chunkSize) + { + for (let j = 0; j < chunkSize && j + i < arr1.length; j++) + { + newArr.push(arr1[i + j]); + } + for (let k = 0; k < chunkSize && k + i < arr2.length; k++) + { + newArr.push(arr2[i + k]); + } + } + } + outPort.set(null); + outPort.set(newArr); +} + + +}; + +Ops.Array.WeaveArrays.prototype = new CABLES.Op(); +CABLES.OPS["ad6d77b7-d81d-4115-a2f2-5160adaaef3a"]={f:Ops.Array.WeaveArrays,objName:"Ops.Array.WeaveArrays"}; + + + + +// ************************************************************** +// +// Ops.Arrays.ArrayToArrays +// +// ************************************************************** + +Ops.Arrays.ArrayToArrays = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + inStride = op.inInt("Stride", 6), + outArr = op.outArray("Result"); + +inArr.onChange = () => +{ + const stride = inStride.get(); + const result = []; + const arr = inArr.get(); + if (!arr) return outArr.set(null); + + let count = 0; + for (let i = 0; i < arr.length; i += stride) + { + const newArr = []; + for (let j = 0; j < stride; j++) + { + newArr[j] = arr[i + j] || 0; + } + result[count] = newArr; + count++; + } + + outArr.set(null); + outArr.set(result); +}; + + +}; + +Ops.Arrays.ArrayToArrays.prototype = new CABLES.Op(); +CABLES.OPS["037a9886-c185-4ab2-8c7e-b9f85b1e678c"]={f:Ops.Arrays.ArrayToArrays,objName:"Ops.Arrays.ArrayToArrays"}; + + + + +// ************************************************************** +// +// Ops.Audio.BpmTap +// +// ************************************************************** + +Ops.Audio.BpmTap = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const DEFAULT_BPM = 127; + +const exe = op.inTrigger("exe"); +const tap = op.inTriggerButton("tap"); +const sync = op.inTriggerButton("sync"); +const nudgeLeft = op.inTriggerButton("nudgeLeft"); +const nudgeRight = op.inTriggerButton("nudgeRight"); + +const beat = op.outTrigger("beat"); +// const bpm = this.addOutPort(new CABLES.Port(this, "Bpm", CABLES.OP_PORT_TYPE_VALUE, { "display": "editor" }),0); +const bpm = op.outNumber("Bpm", DEFAULT_BPM); +const outStates = op.outArray("States"); +const beatNum = op.outNumber("Beat Index"); + +const DEFAULT_MILLIS = bpmToMillis(DEFAULT_BPM); +const NUDGE_VALUE = 0.5; // to add / substract from avg bpm + +let lastFlash = -1; +let lastTap = -1; + +const taps = []; + +let avgBpm = DEFAULT_BPM; +let avgMillis = getAvgMillis(); + +let beatCounter = 1; // [1, 2, 3, 4] +const states = [1, 0, 0, 0]; + +exe.onTriggered = function () +{ + if (op.patch.freeTimer.get() * 1000 < lastFlash) + { + lastFlash = op.patch.freeTimer.get() * 1000; + } + + if (op.patch.freeTimer.get() * 1000 > lastFlash + avgMillis) + { + beat.trigger(); + incrementState(); + outStates.set(null); + outStates.set(states); + + if (taps.length > 0) bpm.set(millisToBpm(avgMillis)); + lastFlash = op.patch.freeTimer.get() * 1000; + } +}; + +function incrementState() +{ + beatCounter++; + if (beatCounter > 4) + { + beatCounter = 1; + } + for (let i = 0; i < 4; i++) + { + states[i] = 0; + } + states[beatCounter - 1] = 1; + + beatNum.set(beatCounter - 1); +} + +function tapPressed() +{ + // start new tap session + if (op.patch.freeTimer.get() * 1000 - lastTap > 1000) + { + taps.length = 0; + beatCounter = 0; + } + else + { + taps.push(op.patch.freeTimer.get() * 1000 - lastTap); + } + lastTap = op.patch.freeTimer.get() * 1000; + avgMillis = getAvgMillis(); +} + +function millisToBpm(millis) +{ + return Number(60 * 1000 / millis).toFixed(2); +} + +function bpmToMillis(bpms) +{ + return 60 * 1000 / bpms; +} + +function getAvgMillis() +{ + if (taps.length >= 1) + { + let sum = 0.0; + for (let i = 0; i < taps.length; i++) + { + sum += taps[i]; + } + return sum / taps.length; + } + else + { + return DEFAULT_MILLIS; + } +} + +function syncPressed() +{ + // on next execute everything will be reset to first beat + lastFlash = -1; + beatCounter = 0; +} + +function nudgeLeftPressed() +{ + avgBpm += NUDGE_VALUE; + avgMillis = bpmToMillis(avgBpm); +} + +function nudgeRightPressed() +{ + avgBpm -= NUDGE_VALUE; + avgMillis = bpmToMillis(avgBpm); +} + +tap.onTriggered = tapPressed; +sync.onTriggered = syncPressed; +nudgeLeft.onTriggered = nudgeLeftPressed; +nudgeRight.onTriggered = nudgeRightPressed; + + +}; + +Ops.Audio.BpmTap.prototype = new CABLES.Op(); +CABLES.OPS["86096ee6-08f7-4463-b7c3-41cf7bc80de7"]={f:Ops.Audio.BpmTap,objName:"Ops.Audio.BpmTap"}; + + + + +// ************************************************************** +// +// Ops.Audio.MidiJson +// +// ************************************************************** + +Ops.Audio.MidiJson = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObj = op.inObject("MidiJson"), + inTime = op.inValue("Time"), + outBeat = op.outNumber("Beat"), + outTrackNames = op.outArray("Track Names"), + outNames = op.outArray("Names"), + outProgress = op.outArray("Progress"), + outVelocity = op.outArray("Velocity"), + outNumTracks = op.outNumber("Num Tracks"), + outBPM = op.outNumber("BPM"), + outData = op.outObject("Data"); + +let midi = null; +let arrNames = []; +let arrProgress = []; +let arrVelocity = []; +let bpm = 0; + +inObj.onChange = function () +{ + midi = null; + outNumTracks.set(0); + + midi = inObj.get(); + if (!midi) return; + if (!midi.tracks) return; + + outNumTracks.set(midi.tracks.length); + + let arrTrackNames = []; + for (let i = 0; i < midi.tracks.length; i++) + { + arrTrackNames[i] = midi.tracks[i].name || "?"; + } + + outTrackNames.set(null); + outTrackNames.set(arrTrackNames); + + arrNames.length = midi.tracks.length; + + bpm = midi.header.bpm; + outBPM.set(midi.header.bpm); + + for (let t = 0; t < midi.tracks.length; t++) + { + for (let n = 0; n < midi.tracks[t].notes.length; n++) + { + let note = midi.tracks[t].notes[n]; + note.timeEnd = note.time + note.duration; + } + } +}; + +inTime.onChange = function () +{ + if (!midi) return; + if (!midi.tracks) return; + + let time = inTime.get(); + outNames.set(null); + outProgress.set(null); + outVelocity.set(null); + outData.set(null); + + let beat = Math.round(inTime.get() / 60 * (bpm)); + + for (let t = 0; t < midi.tracks.length; t++) + { + arrNames[t] = ""; + arrProgress[t] = 0; + arrVelocity[t] = 0; + + for (let n = 0; n < midi.tracks[t].notes.length; n++) + { + let note = midi.tracks[t].notes[n]; + + if ( + time > note.time && + time < note.timeEnd) + { + arrProgress[t] = (time - note.time) / (note.duration); + arrNames[t] = note.name; + arrVelocity[t] = note.velocity; + + const data = + { + "beat": 0, + "names": arrNames, + "progress": arrProgress, + "velocity": arrVelocity, + "midi": note.midi, + "beat": beat + + }; + + outData.set(data); + } + } + } + + outNames.set(arrNames); + outNames.setUiAttribs({ "stride": arrNames.length }); + outProgress.set(arrProgress); + outVelocity.set(arrVelocity); + + outBeat.set(beat); +}; + + +}; + +Ops.Audio.MidiJson.prototype = new CABLES.Op(); +CABLES.OPS["695aef03-8ab0-48a1-b3ee-2c6c01a21640"]={f:Ops.Audio.MidiJson,objName:"Ops.Audio.MidiJson"}; + + + + +// ************************************************************** +// +// Ops.Audio.MidiJsonNote_v2 +// +// ************************************************************** + +Ops.Audio.MidiJsonNote_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inData = op.inObject("Data"), + // inFilter = op.inSwitch("Filter", ["Note Name", "Midi"], "Note Name"), + inNote = op.inString("Note"), + // inNoteMidi = op.inInt("Midi"), + inChannel = op.inValueString("Channel", -1), + inStartBeat = op.inValueInt("Beat Start", 0), + inEndBeat = op.inValueInt("Beat End", 0), + + outCount = op.outNumber("Count"), + outProgress = op.outNumber("Progress"), + outTimeSince = op.outNumber("Time since last"), + outTrigger = op.outTrigger("Trigger"), + outReset = op.outTrigger("Reseted"); + +let lastBeat = 0; +let counter = 0; +let oldNames = null; +let oldProgress = null; +let lastHit = 0; +let notfound = true; +// let NoteOrMidi = true; +// inFilter.onChange = updateUi; +// updateUi(); + +// function updateUi() +// { +// inNote.setUiAttribs({ "greyout": inFilter.get() != "Note Name" }); +// inNoteMidi.setUiAttribs({ "greyout": inFilter.get() != "Midi" }); + +// NoteOrMidi = inFilter.get() == "Note Name"; +// } + +inNote.onChange = () => +{ + op.setUiAttrib({ "extendTitle": "" + inNote.get() }); +}; + +inData.onChange = function () +{ + const data = inData.get(); + if (!data) return; + const beat = data.beat; + + if (inStartBeat.get() != inEndBeat.get()) + { + if (beat < inStartBeat.get()) return; + if (beat > inEndBeat.get()) return; + } + + const names = data.names; + const progress = data.progress; + if (!names) return; + if (!progress) return; + + if (beat < lastBeat) + { + counter = 0; + lastHit = 0; + notfound = true; + outTimeSince.set(0); + outReset.trigger(); + } + lastBeat = beat; + + const note = inNote.get(); + + if (!oldNames) + { + oldNames = []; + oldProgress = []; + oldNames.length = names.length; + oldProgress.length = names.length; + } + + let startChn = 0; + let endChn = names.length; + + if (inChannel.get() >= 0) + { + startChn = inChannel.get(); + endChn = startChn + 1; + } + + let prog = 0; + let progCount = 0; + + for (let i = startChn; i < endChn; i++) + { + if ((NoteOrMidi && names[i] == note)) + { + if ((oldNames[i] != note) || notfound) // ( oldNames[i]==note && progress[i] port.set(defaultValue)); +} + +function update() +{ + setDefaultValues(); + let index = indexPort.get(); + let value = valuePort.get(); + index = Math.round(index); + index = CABLES.clamp(index, 0, NUM_PORTS - 1); + valuePorts[index].set(value); +} + + +}; + +Ops.Boolean.RouteBoolean.prototype = new CABLES.Op(); +CABLES.OPS["88b3f5ec-0e45-4651-89ca-33173d001ab6"]={f:Ops.Boolean.RouteBoolean,objName:"Ops.Boolean.RouteBoolean"}; + + + + +// ************************************************************** +// +// Ops.Boolean.ToggleBoolValue +// +// ************************************************************** + +Ops.Boolean.ToggleBoolValue = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + bool = op.inValueBool("in bool"), + outbool = op.outBoolNum("out bool"); + +bool.changeAlways = true; + +bool.onChange = function () +{ + outbool.set(!(bool.get() == true)); +}; + + +}; + +Ops.Boolean.ToggleBoolValue.prototype = new CABLES.Op(); +CABLES.OPS["7b1abd02-3aad-4106-9848-7f4c3cfab6a9"]={f:Ops.Boolean.ToggleBoolValue,objName:"Ops.Boolean.ToggleBoolValue"}; + + + + +// ************************************************************** +// +// Ops.Boolean.ToggleBool_v2 +// +// ************************************************************** + +Ops.Boolean.ToggleBool_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + trigger = op.inTriggerButton("trigger"), + reset = op.inTriggerButton("reset"), + outBool = op.outBool("result"), + inDefault = op.inBool("Default", false); + +let theBool = false; + +op.onLoadedValueSet = () => +{ + outBool.set(inDefault.get()); +}; + +trigger.onTriggered = function () +{ + theBool = !theBool; + outBool.set(theBool); +}; + +reset.onTriggered = function () +{ + theBool = inDefault.get(); + outBool.set(theBool); +}; + + +}; + +Ops.Boolean.ToggleBool_v2.prototype = new CABLES.Op(); +CABLES.OPS["4313d9bb-96b6-43bc-9190-6068cfb2593c"]={f:Ops.Boolean.ToggleBool_v2,objName:"Ops.Boolean.ToggleBool_v2"}; + + + + +// ************************************************************** +// +// Ops.Boolean.TriggerBoolean +// +// ************************************************************** + +Ops.Boolean.TriggerBoolean = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTriggerTrue = op.inTriggerButton("True"), + inTriggerFalse = op.inTriggerButton("false"), + outResult = op.outBoolNum("Result"); + +inTriggerTrue.onTriggered = function () +{ + outResult.set(true); +}; + +inTriggerFalse.onTriggered = function () +{ + outResult.set(false); +}; + + +}; + +Ops.Boolean.TriggerBoolean.prototype = new CABLES.Op(); +CABLES.OPS["31f65abe-9d6c-4ba6-a291-ef2de41d2087"]={f:Ops.Boolean.TriggerBoolean,objName:"Ops.Boolean.TriggerBoolean"}; + + + + +// ************************************************************** +// +// Ops.Boolean.TriggerChangedFalse +// +// ************************************************************** + +Ops.Boolean.TriggerChangedFalse = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let val = op.inValueBool("Value", false); +let next = op.outTrigger("Next"); + +let oldVal = 0; + +val.onChange = function () +{ + let newVal = val.get(); + if (oldVal && !newVal) + { + oldVal = false; + next.trigger(); + } + else + { + oldVal = true; + } +}; + + +}; + +Ops.Boolean.TriggerChangedFalse.prototype = new CABLES.Op(); +CABLES.OPS["6387bcb0-6091-4199-8ab7-f96ad4aa3c7d"]={f:Ops.Boolean.TriggerChangedFalse,objName:"Ops.Boolean.TriggerChangedFalse"}; + + + + +// ************************************************************** +// +// Ops.Boolean.TriggerChangedTrue +// +// ************************************************************** + +Ops.Boolean.TriggerChangedTrue = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let val = op.inValueBool("Value", false); +let next = op.outTrigger("Next"); +let oldVal = 0; + +val.onChange = function () +{ + let newVal = val.get(); + if (!oldVal && newVal) + { + oldVal = true; + next.trigger(); + } + else + { + oldVal = false; + } +}; + + +}; + +Ops.Boolean.TriggerChangedTrue.prototype = new CABLES.Op(); +CABLES.OPS["385197e1-8b34-4d1c-897f-d1386d99e3b3"]={f:Ops.Boolean.TriggerChangedTrue,objName:"Ops.Boolean.TriggerChangedTrue"}; + + + + +// ************************************************************** +// +// Ops.Boolean.TriggerOnChangeBoolean +// +// ************************************************************** + +Ops.Boolean.TriggerOnChangeBoolean = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inBool = op.inValueBool("Value"), + outTrue = op.outTrigger("True"), + outFalse = op.outTrigger("False"); + +inBool.onChange = function () +{ + if (inBool.get()) outTrue.trigger(); + else outFalse.trigger(); +}; + + +}; + +Ops.Boolean.TriggerOnChangeBoolean.prototype = new CABLES.Op(); +CABLES.OPS["dba19c07-e3c4-4971-a991-c9e6212ca1c8"]={f:Ops.Boolean.TriggerOnChangeBoolean,objName:"Ops.Boolean.TriggerOnChangeBoolean"}; + + + + +// ************************************************************** +// +// Ops.Browser.BrowserInfo_v3 +// +// ************************************************************** + +Ops.Browser.BrowserInfo_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + isMobile = op.outBool("Is Mobile", false), + isTouch = op.outBool("Is Touchscreen", false), + isIe = op.outBool("Is IE", false), + isEdge = op.outBool("Is Edge", false), + isChrome = op.outBool("Is Chrome", false), + isFirefox = op.outBool("Is Firefox", false), + isOpera = op.outBool("Is Opera", false), + isSafari = op.outBool("Is Safari", false), + isWindows = op.outBool("Is Windows", false), + isLinux = op.outBool("Is Linux", false), + isMac = op.outBool("Is Mac", false), + isIos = op.outBool("Is iOS", false), + isAndroid = op.outBool("Is Android", false), + isElectron = op.outBool("Is Electron", false), + outOS = op.outString("Operating System", ""), + outBrowserName = op.outString("Browser Name", ""), + outVersion = op.outString("OS Version", ""), + outNav = op.outString("Language"), + outUA = op.outString("User Agent"); + +op.setPortGroup("Browsers", [isIe, isEdge, isChrome, isFirefox, isOpera, isSafari]); +op.setPortGroup("Operating Systems", [isWindows, isLinux, isMac, isIos, isAndroid, isElectron]); +op.setPortGroup("Textual Information", [outOS, outBrowserName, outNav, outVersion, outUA]); +const pf = platform; + +const osFamily = pf.os.family; + +const isOperaBool = pf.name === "Opera"; +const isSafariBool = pf.name === "Safari"; +const isIEBool = pf.name === "IE"; +const isEdgeBool = pf.name === "Microsoft Edge"; +const isChromeBool = pf.name === "Chrome" || pf.name === "Chrome Mobile"; +const isBlinkBool = pf.layout === "Blink"; +const isFirefoxBool = pf.name === "Firefox" || pf.name === "Firefox Mobile" || pf.name === "Firefox for iOS"; + +const isLinuxBool = osFamily === "Linux" + || osFamily === "Ubuntu" + || osFamily === "SuSE" + || osFamily === "Fedora" + || osFamily === "OpenBSD" + || osFamily === "Debian" + || osFamily === "Red Hat"; + +const isMacBool = osFamily === "Mac" || osFamily === "Macintosh" || osFamily === "Mac OS X" || osFamily === "OS X"; +const isWindowsBool = osFamily === "Windows" || osFamily === "Windows 98;"; +const isAndroidBool = osFamily === "Android"; +const isIosBool = osFamily === "iOS"; +const isMobileBool = (osFamily === "webOS" // LG mobile phones + || osFamily === "Windows Phone" + || osFamily === "Android" + || osFamily === "iOS") + || + (pf.name === "Chrome Mobile" + || pf.name === "Firefox for iOS" + || pf.name === "Firefox Mobile" + || pf.name === "IE Mobile" + || pf.name === "Opera Mobile"); +const isElectronBool = pf.name === "Electron"; + +const isTouchDevice = ( + ("ontouchstart" in window) || + (navigator.maxTouchPoints > 0) || + (navigator.msMaxTouchPoints > 0)); + +isMobile.set(isMobileBool); +isTouch.set(isTouchDevice); + +isIe.set(isIEBool); +isEdge.set(isEdgeBool); +isChrome.set(isChromeBool); +isFirefox.set(isFirefoxBool); +isOpera.set(isOperaBool); +isSafari.set(isSafariBool); + +isMac.set(isMacBool); +isLinux.set(isLinuxBool); +isWindows.set(isWindowsBool); +isIos.set(isIosBool); +isAndroid.set(isAndroidBool); +isElectron.set(isElectronBool); + +outNav.set(navigator.language || navigator.userLanguage); +outUA.set(pf.ua); +outVersion.set(pf.os.version); +outOS.set(pf.os.toString()); +outBrowserName.set(pf.name); + + +}; + +Ops.Browser.BrowserInfo_v3.prototype = new CABLES.Op(); +CABLES.OPS["ec3e0121-b2c2-4c31-bbda-a6982080f73f"]={f:Ops.Browser.BrowserInfo_v3,objName:"Ops.Browser.BrowserInfo_v3"}; + + + + +// ************************************************************** +// +// Ops.Browser.JsMemory +// +// ************************************************************** + +Ops.Browser.JsMemory = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Update"), + outUsed = op.outNumber("Used Heap Size", 0), + outTotal = op.outNumber("Total Heap Size", 0); + +if (performance.memory && performance.memory.usedJSHeapSize) + inExec.onTriggered = update; + +function update() +{ + outUsed.set(performance.memory.usedJSHeapSize / 1024 / 1024); + outTotal.set(performance.memory.totalJSHeapSize / 1024 / 1024); +} + + +}; + +Ops.Browser.JsMemory.prototype = new CABLES.Op(); +CABLES.OPS["7176f9c7-1ee8-419e-b58b-178cca9265e7"]={f:Ops.Browser.JsMemory,objName:"Ops.Browser.JsMemory"}; + + + + +// ************************************************************** +// +// Ops.Browser.WebShare +// +// ************************************************************** + +Ops.Browser.WebShare = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inText = op.inString("Text", "check out undev"), + inUrl = op.inString("URL", "https://undev.studio"), + inBase64 = op.inString("Base64 File"), + inDataUrl = op.inString("Data URL"), + inFileType = op.inString("Filetype", "image/png"), + inFileName = op.inString("Filename", "screenshot.png"), + exec = op.inTriggerButton("Share"), + outStatus = op.outString("Status", "none"), + outSupport = op.outBoolNum("Supported", !!navigator.share); + +op.setPortGroup("File", [inBase64, inDataUrl, inFileType, inFileName]); + +if (!navigator.share) +{ + op.setUiError("noShare", "webshare api not supported on this device", 1); +} + +exec.onTriggered = () => +{ + op.setUiError("noShare", null); + if (!navigator.share) + { + op.setUiError("noShare", "webshare api not supported on this device", 1); + outSupport.set(false); + return; + } + outSupport.set(true); + + const shareData = {}; + shareData.text = inText.get(); + shareData.url = inUrl.get(); + + let url = inDataUrl.get(); + if (inBase64.get()) + { + url = "data:" + inFileType.get() + ";base64," + inBase64.get(); + } + + if (url) + { + fetch(url) + .then((res) => res.blob()) + .then((blob) => + { + shareData.files = [ + new File( + [blob], + inFileName.get(), + { + "type": inFileType.get(), + "lastModified": new Date().getTime() + } + ) + ]; + if (!navigator.canShare(shareData)) + { + op.setUiError("noShare", "browser can't share files", 1); + op.log("browser cant share files", shareData); + delete shareData.files; + } + if (!navigator.canShare(shareData)) + { + op.setUiError("noShare", "browser can't share data", 1); + op.log("browser cant share data", shareData); + outStatus.set("error"); + } + else + { + doShare(shareData); + } + }); + } + else + { + doShare(shareData); + } +}; + +function doShare(shareData) +{ + navigator.share(shareData) + .then(() => + { + outStatus.set("success"); + }) + .catch((error) => + { + outStatus.set("error"); + op.setUiError("noShare", "Error sharing", error); + op.log("Error sharing", error); + }); +} + + +}; + +Ops.Browser.WebShare.prototype = new CABLES.Op(); +CABLES.OPS["2e24a18f-161a-480c-a86b-4232c59f68c4"]={f:Ops.Browser.WebShare,objName:"Ops.Browser.WebShare"}; + + + + +// ************************************************************** +// +// Ops.Cables.AssetPathURL +// +// ************************************************************** + +Ops.Cables.AssetPathURL = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + fn = op.inString("Filename", ""), + path = op.outString("Path"); + +fn.onChange = update; + +update(); + +function update() +{ + let filename = fn.get(); + + if (!fn.get()) + { + path.set(""); + return; + } + + let patchId = null; + if (op.storage && op.storage.blueprint && op.storage.blueprint.patchId) + { + patchId = op.storage.blueprint.patchId; + } + filename = op.patch.getAssetPath(patchId) + filename; + let url = op.patch.getFilePath(filename); + path.set(url); +} + + +}; + +Ops.Cables.AssetPathURL.prototype = new CABLES.Op(); +CABLES.OPS["e502ae39-c87e-4516-9e78-cb71333bcfff"]={f:Ops.Cables.AssetPathURL,objName:"Ops.Cables.AssetPathURL"}; + + + + +// ************************************************************** +// +// Ops.Cables.CustomOp_v2 +// +// ************************************************************** + +Ops.Cables.CustomOp_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const defaultCode = "\ +// you can use custom javascript code here, it will be bound to the\n\ +// scope of the current op, which is available as `op`.\n\ +// \n\ +// have a look at the documentation at:\n\ +// https://docs.cables.gl/dev_hello_op/dev_hello_op.html\n\ +\n\ +"; + +const inJS = op.inStringEditor("JavaScript"); +inJS.setUiAttribs({ "editorSyntax": "js" }); +inJS.set(defaultCode); +const inLib = op.inUrl("Library", [".js"]); + +const portsData = op.inString("portsData", "{}"); +portsData.setUiAttribs({ "hidePort": true }); +portsData.setUiAttribs({ "hideParam": true }); +const protectedPorts = [inJS.id, inLib.id, portsData.id]; + +let wasPasted = false; + +op.setUiError("error", null); + +const init = function () +{ + if (op.uiAttribs) + { + wasPasted = op.uiAttribs.pasted; + } + restorePorts(); + loadLibAndExecute(); + inLib.onChange = inJS.onChange = loadLibAndExecute; + if (wasPasted) wasPasted = false; +}; + +op.onLoadedValueSet = init; +op.patch.on("onOpAdd", (newOp, fromDeserizalize) => +{ + if (op == newOp && !fromDeserizalize) + { + init(); + } +}); + +op.onError = function (ex) +{ + if (op.patch.isEditorMode()) + { + op.setUiError("error", ex); + const str = inJS.get(); + const badLines = []; + let htmlWarning = ""; + const lines = str.match(/^.*((\r\n|\n|\r)|$)/gm); + + let anonLine = ""; + const exLines = ex.stack.split("\n"); + for (let i = 0; i < exLines.length; i++) + { + // firefox + if (exLines[i].includes("Function:")) + { + anonLine = exLines[i]; + break; + } + // chrome + if (exLines[i].includes("anonymous")) + { + anonLine = exLines[i]; + break; + } + } + + let lineFields = anonLine.split(":"); + let errorLine = lineFields[lineFields.length - 2]; + + badLines.push(errorLine - 2); + + for (const i in lines) + { + const j = parseInt(i, 10) + 1; + const line = j + ": " + lines[i]; + + let isBadLine = false; + for (const bj in badLines) + if (badLines[bj] == j) isBadLine = true; + + if (isBadLine) htmlWarning += ""; + htmlWarning += line.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """) + .replaceAll("'", "'"); + if (isBadLine) htmlWarning += ""; + } + + ex.customMessage = htmlWarning; + ex.stack = ""; + op.patch.emitEvent("exceptionOp", ex, op.name); + } +}; + +const getEvalFunction = () => +{ + op.setUiError("error", null); + let errorEl = document.getElementById("customop-error-" + op.id); + if (errorEl) + { + errorEl.remove(); + } + try + { + return new Function("op", inJS.get()); + } + catch (err) + { + op.onError(err); + if (op.patch.isEditorMode()) + { + errorEl = document.createElement("script"); + errorEl.id = "customop-error-" + op.id; + errorEl.type = "text/javascript"; + errorEl.innerHTML = inJS.get(); + document.body.appendChild(errorEl); + } + else + { + op.logError("error creating javascript function", err); + } + return null; + } +}; + +function loadLibAndExecute() +{ + if (inLib.get()) + { + let scriptTag = document.getElementById("customop_lib_" + op.id); + if (scriptTag) + { + scriptTag.remove(); + } + scriptTag = document.createElement("script"); + scriptTag.id = "customlib_" + op.id; + scriptTag.type = "text/javascript"; + scriptTag.src = op.patch.getFilePath(String(inLib.get())); + scriptTag.onload = function () + { + op.logVerbose("done loading library", inLib.get()); + execute(); + }; + document.body.appendChild(scriptTag); + } + else if (inJS.get() && inJS.get() !== defaultCode) + { + execute(); + } +} + +const removeInPort = (port) => +{ + port.removeLinks(); + for (let ipi = 0; ipi < op.portsIn.length; ipi++) + { + if (op.portsIn[ipi] == port) + { + op.portsIn.splice(ipi, 1); + return; + } + } +}; + +const removeOutPort = (port) => +{ + port.removeLinks(); + for (let ipi = 0; ipi < op.portsOut.length; ipi++) + { + if (op.portsOut[ipi] == port) + { + op.portsOut.splice(ipi, 1); + return; + } + } +}; + +const execute = () => +{ + const evalFunction = getEvalFunction(); + if (evalFunction) + { + try + { + const oldLinksIn = {}; + const oldValuesIn = {}; + const oldLinksOut = {}; + const removeInPorts = []; + const removeOutPorts = []; + op.portsIn.forEach((port) => + { + if (!protectedPorts.includes(port.id)) + { + oldLinksIn[port.name] = []; + oldValuesIn[port.name] = port.get(); + port.links.forEach((link) => + { + const linkInfo = { + "op": link.portOut.parent, + "portName": link.portOut.name + }; + oldLinksIn[port.name].push(linkInfo); + }); + removeInPorts.push(port); + } + }); + op.portsOut.forEach((port) => + { + oldLinksOut[port.name] = []; + port.links.forEach((link) => + { + const linkInfo = { + "op": link.portIn.parent, + "portName": link.portIn.name + }; + oldLinksOut[port.name].push(linkInfo); + }); + removeOutPorts.push(port); + }); + removeInPorts.forEach((port) => + { + removeInPort(port); + }); + removeOutPorts.forEach((port) => + { + removeOutPort(port); + }); + if (removeOutPorts.length > 0 || removeInPorts.length > 0) + { + this.emitEvent("onUiAttribsChange", {}); + this.emitEvent("onPortRemoved", {}); + } + evalFunction(this); + + op.portsIn.forEach((port) => + { + if (!protectedPorts.includes(port.id)) + { + port.onLinkChanged = savePortData; + + if (oldLinksIn[port.name]) + { + oldLinksIn[port.name].forEach((link) => + { + op.patch.link(op, port.name, link.op, link.portName); + }); + } + + if (typeof port.onChange == "function") + { + const oldHandler = port.onChange; + port.onChange = (p, v) => + { + if (!port.isLinked()) savePortData(); + oldHandler(p, v); + }; + } + else + { + port.onChange = () => + { + if (!port.isLinked()) savePortData(); + }; + } + + // for backwards compatibility, do not add default handler (handled above) + if (typeof port.onValueChanged == "function") + { + const oldValueHandler = port.onValueChanged; + port.onValueChanged = (p, v) => + { + if (!port.isLinked()) savePortData(); + oldValueHandler(p, v); + }; + } + + if (oldValuesIn[port.name]) + { + port.set(oldValuesIn[port.name]); + } + } + }); + op.portsOut.forEach((port) => + { + port.onLinkChanged = savePortData; + + if (oldLinksOut[port.name]) + { + oldLinksOut[port.name].forEach((link) => + { + op.patch.link(op, port.name, link.op, link.portName); + }); + } + }); + if (wasPasted) + { + wasPasted = false; + } + savePortData(); + } + catch (e) + { + if (op.patch.isEditorMode()) + { + op.onError(e); + const name = "Ops.Custom.CUSTOM" + op.id.replace(/-/g, ""); + const code = inJS.get(); + let codeHead = "Ops.Custom = Ops.Custom || {};\n"; + codeHead += name + " = " + name + " || {};\n"; + codeHead += name + " = function()\n{\nCABLES.Op.apply(this,arguments);\nconst op=this;\n"; + let codeFoot = "\n\n};\n\n" + name + ".prototype = new CABLES.Op();\n"; + codeFoot += "new " + name + "();\n"; + const opCode = codeHead + code + codeFoot; + const errorEl = document.createElement("script"); + errorEl.id = "customop-error-" + op.id; + errorEl.type = "text/javascript"; + errorEl.innerHTML = opCode; + document.body.appendChild(errorEl); + } + else + { + op.logError("error executing javascript code", e); + } + } + } +}; + +function savePortData() +{ + const newPortsData = { "portsIn": {}, "portsOut": {} }; + op.portsIn.forEach((port) => + { + if (!protectedPorts.includes(port.id)) + { + let v = port.get(); + if (port.ignoreValueSerialize)v = null; + const portData = { + "name": port.name, + "title": port.title, + "value": v, + "type": port.type, + "links": [] + }; + port.links.forEach((link) => + { + const linkData = { + "objOut": link.portOut.parent.id, + "portOut": link.portOut.name + }; + portData.links.push(linkData); + }); + newPortsData.portsIn[port.name] = portData; + } + }); + + op.portsOut.forEach((port) => + { + if (!protectedPorts.includes(port.id)) + { + let v = port.get(); + if (port.ignoreValueSerialize)v = null; + + const portData = { + "name": port.name, + "title": port.title, + "value": v, + "type": port.type, + "links": [] + }; + port.links.forEach((link) => + { + const linkData = { + "objIn": link.portIn.parent.id, + "portIn": link.portIn.name + }; + portData.links.push(linkData); + }); + newPortsData.portsOut[port.name] = portData; + } + }); + + let serializedPortsData = "{}"; + try + { + serializedPortsData = JSON.stringify(newPortsData); + } + catch (e) + { + op.log("failed to stringify new port data", newPortsData); + } + portsData.set(serializedPortsData); +} + +const getOldPorts = () => +{ + const jsonData = portsData.get(); + let oldPorts = {}; + try + { + oldPorts = JSON.parse(jsonData); + } + catch (e) + { + op.log("failed to parse old port data", jsonData); + } + + let oldPortsIn = {}; + let oldPortsOut = {}; + + if (oldPorts.portsOut) + { + oldPortsOut = oldPorts.portsOut; + } + if (oldPorts.portsIn) + { + oldPortsIn = oldPorts.portsIn; + } + return { "portsIn": oldPortsIn, "portsOut": oldPortsOut }; +}; + +const restorePorts = () => +{ + const oldPorts = getOldPorts(); + const portInKeys = Object.keys(oldPorts.portsIn); + if (op.patch.isEditorMode()) CABLES.UI.undo.pause(); + for (let i = 0; i < portInKeys.length; i++) + { + const oldPortIn = oldPorts.portsIn[portInKeys[i]]; + const newPort = op.addInPort(new CABLES.Port(op, oldPortIn.name, oldPortIn.type)); + + if (!wasPasted && Array.isArray(oldPortIn.links)) + { + oldPortIn.links.forEach((link) => + { + let parent = op.patch.getOpById(link.objOut); + if (parent) + { + op.patch.link(parent, link.portOut, op, newPort.name); + } + }); + } + if (!newPort.isLinked()) + { + newPort.set(oldPortIn.value); + } + newPort.onLinkChanged = savePortData; + + if (oldPortIn.title) + { + newPort.setUiAttribs({ "title": oldPortIn.title }); + } + } + + const portOutKeys = Object.keys(oldPorts.portsOut); + for (let i = 0; i < portOutKeys.length; i++) + { + const oldPortOut = oldPorts.portsOut[portOutKeys[i]]; + const newPort = op.addOutPort(new CABLES.Port(op, oldPortOut.name, oldPortOut.type)); + if (!wasPasted && Array.isArray(oldPortOut.links)) + { + oldPortOut.links.forEach((link) => + { + let parent = op.patch.getOpById(link.objIn); + if (parent) + { + op.patch.link(op, newPort.name, parent, link.portIn); + } + }); + if (!newPort.isLinked()) + { + newPort.set(oldPortOut.value); + } + newPort.onLinkChanged = savePortData; + + if (oldPortOut.title) + { + newPort.setUiAttribs({ "title": oldPortOut.title }); + } + } + } + if (op.patch.isEditorMode()) CABLES.UI.undo.resume(); +}; + + +}; + +Ops.Cables.CustomOp_v2.prototype = new CABLES.Op(); +CABLES.OPS["19166505-2619-4012-ad85-d2de60f27274"]={f:Ops.Cables.CustomOp_v2,objName:"Ops.Cables.CustomOp_v2"}; + + + + +// ************************************************************** +// +// Ops.Cables.ExitError +// +// ************************************************************** + +Ops.Cables.ExitError = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inId = op.inString("ID", "fail"), + inMessage = op.inString("Message", "a critical error happened!"), + exit = op.inTriggerButton("Exit"); + +exit.onTriggered = () => +{ + op.patch.exitError(inId.get(), inMessage.get()); +}; + + +}; + +Ops.Cables.ExitError.prototype = new CABLES.Op(); +CABLES.OPS["e1ad65d5-1f84-45d7-8291-925474bd2bd9"]={f:Ops.Cables.ExitError,objName:"Ops.Cables.ExitError"}; + + + + +// ************************************************************** +// +// Ops.Cables.FPS_v2 +// +// ************************************************************** + +Ops.Cables.FPS_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + active = op.inBool("Active", true), + outFPS = op.outNumber("FPS"), + outMS = op.outNumber("MS"); + +const listener = op.patch.addEventListener("performance", update); + +op.onDelete = function () +{ + op.patch.removeEventListener(listener); +}; + +function update(p) +{ + if (active.get()) + { + outFPS.set(p.fps); + outMS.set(p.ms); + } +} + + +}; + +Ops.Cables.FPS_v2.prototype = new CABLES.Op(); +CABLES.OPS["6dbb866c-b57a-4875-9f1d-22172162eaa8"]={f:Ops.Cables.FPS_v2,objName:"Ops.Cables.FPS_v2"}; + + + + +// ************************************************************** +// +// Ops.Cables.PatchInfo_v2 +// +// ************************************************************** + +Ops.Cables.PatchInfo_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const outConfig = op.outObject("Config"); +const outName = op.outString("Name"); +const outPatchId = op.outString("Patch Id"); +const outNamespace = op.outString("Namespace"); +const outSaveDate = op.outNumber("Last Saved"); +const outExportDate = op.outNumber("Last Exported"); + +const patch = getPatch(); + +outConfig.set(patch.config); +outName.set(patch.name); +outPatchId.set(getPatchId()); +outNamespace.set(patch.namespace); +outSaveDate.set(getLastSaveDate()); +outExportDate.set(getLastExportDate()); + +function getPatchId() +{ + let id = null; + if (patch && patch._id) id = patch._id; + if (patch && patch.config && patch.config.patch && patch.config.patch._id) id = patch.config.patch._id; + if (window.gui && window.gui.patchId) id = window.gui.patchId; + if (window.CABLES && window.CABLES.patchId) id = window.CABLES.patchId; + if (CABLES.patchId) id = CABLES.patchId; + return id; +} + +function getLastSaveDate() +{ + let date = null; + if (patch && patch.config && patch.config.patch) date = patch.config.patch.updated; + if (window.gui && window.gui.project) date = window.gui.project().updated; + return new Date(date).getTime(); +} + +function getLastExportDate() +{ + let date = null; + if (patch && patch.config && patch.config.patch) + { + if (patch.config.patch.deployments && patch.config.patch.deployments.lastDeployment) + { + date = patch.config.patch.deployments.lastDeployment.date; + } + } + if (window.gui && window.gui.project) + { + const p = window.gui.project(); + if (p.deployments && p.deployments.lastDeployment) + { + date = p.deployments.lastDeployment.date; + } + } + return new Date(date).getTime(); +} + +function getPatch() +{ + let thePatch = null; + if (CABLES && CABLES.exportedPatch) thePatch = CABLES.exportedPatch; + if (CABLES && CABLES.patch) thePatch = CABLES.patch; + if (op.patch) thePatch = op.patch; + return thePatch; +} + + +}; + +Ops.Cables.PatchInfo_v2.prototype = new CABLES.Op(); +CABLES.OPS["7187c2f2-67a9-479e-92c4-0e415443f504"]={f:Ops.Cables.PatchInfo_v2,objName:"Ops.Cables.PatchInfo_v2"}; + + + + +// ************************************************************** +// +// Ops.Cables.UIMode +// +// ************************************************************** + +Ops.Cables.UIMode = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const outUI = op.outBool("UI", op.patch.isEditorMode()); +const outRemoteViewer = op.outBool("Remote Viewer", window.gui ? window.gui.isRemoteClient : false); + + +}; + +Ops.Cables.UIMode.prototype = new CABLES.Op(); +CABLES.OPS["7c110d60-829f-4b06-b3e4-0af911550570"]={f:Ops.Cables.UIMode,objName:"Ops.Cables.UIMode"}; + + + + +// ************************************************************** +// +// Ops.Color.ColorPalettes +// +// ************************************************************** + +Ops.Color.ColorPalettes = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const index = op.inValueInt("Index", 0); +const textureOut = op.outTexture("Texture"); +const inLinear = op.inValueBool("Smooth"); +const arrOut = op.outArray("Color Array"); + +let canvas = document.createElement("canvas"); +canvas.id = "canvas_" + CABLES.generateUUID(); +canvas.width = 5; +canvas.height = 8; +canvas.style.display = "none"; + +let body = document.getElementsByTagName("body")[0]; +body.appendChild(canvas); +let ctx = canvas.getContext("2d"); + +index.onChange = +inLinear.onChange = buildTextureLater; + +let arr = []; +arr.length = 5 * 3; +let lastFilter = null; + +buildTextureLater(); + +function hexToR(h) +{ + return parseInt((cutHex(h)).substring(0, 2), 16); +} + +function hexToG(h) +{ + return parseInt((cutHex(h)).substring(2, 4), 16); +} + +function hexToB(h) +{ + return parseInt((cutHex(h)).substring(4, 6), 16); +} + +function cutHex(h) +{ + return (h.charAt(0) == "#") ? h.substring(1, 7) : h; +} + +function buildTextureLater() +{ + op.patch.cgl.addNextFrameOnceCallback(buildTexture); +} + +function buildTexture() +{ + let ind = Math.round(index.get()) * 5; + if (ind >= colors.length - 5)ind = 0; + if (ind < 0)ind = 0; + if (ind != ind)ind = 0; + + for (let i = 0; i < 5; i++) + { + let r = hexToR(colors[ind + i]); + let g = hexToG(colors[ind + i]); + let b = hexToB(colors[ind + i]); + + arr[i * 3 + 0] = r / 255; + arr[i * 3 + 1] = g / 255; + arr[i * 3 + 2] = b / 255; + + ctx.fillStyle = "rgb(" + r + "," + g + "," + b + ")"; + ctx.fillRect( + canvas.width / 5 * i, + 0, + canvas.width / 5, + canvas.height + ); + } + + let filter = CGL.Texture.FILTER_NEAREST; + if (inLinear.get())filter = CGL.Texture.FILTER_LINEAR; + + if (lastFilter == filter && textureOut.get()) textureOut.get().initTexture(canvas, filter); + else textureOut.set(new CGL.Texture.createFromImage(op.patch.cgl, canvas, { "filter": filter })); + + arrOut.set(null); + arrOut.set(arr); + textureOut.get().unpackAlpha = false; + lastFilter = filter; +} + +op.onDelete = function () +{ + canvas.remove(); +}; + +const colors = [ + "#E6E2AF", "#A7A37E", "#EFECCA", "#046380", "002F2F", + "#468966", "#FFF0A5", "#FFB03B", "#B64926", "8E2800", + "#FCFFF5", "#D1DBBD", "#91AA9D", "#3E606F", "193441", + "#FF6138", "#FFFF9D", "#BEEB9F", "#79BD8F", "00A388", + "#105B63", "#FFFAD5", "#FFD34E", "#DB9E36", "BD4932", + "#225378", "#1695A3", "#ACF0F2", "#F3FFE2", "EB7F00", + "#2C3E50", "#E74C3C", "#ECF0F1", "#3498DB", "2980B9", + "#000000", "#263248", "#7E8AA2", "#FFFFFF", "FF9800", + "#004358", "#1F8A70", "#BEDB39", "#FFE11A", "FD7400", + "#DC3522", "#D9CB9E", "#374140", "#2A2C2B", "1E1E20", + "#7D8A2E", "#C9D787", "#FFFFFF", "#FFC0A9", "FF8598", + "#B9121B", "#4C1B1B", "#F6E497", "#FCFAE1", "BD8D46", + "#2E0927", "#D90000", "#FF2D00", "#FF8C00", "04756F", + "#595241", "#B8AE9C", "#FFFFFF", "#ACCFCC", "8A0917", + "#10222B", "#95AB63", "#BDD684", "#E2F0D6", "F6FFE0", + "#F6F792", "#333745", "#77C4D3", "#DAEDE2", "EA2E49", + "#703030", "#2F343B", "#7E827A", "#E3CDA4", "C77966", + "#2F2933", "#01A2A6", "#29D9C2", "#BDF271", "FFFFA6", + "#D8CAA8", "#5C832F", "#284907", "#382513", "363942", + "#FFF8E3", "#CCCC9F", "#33332D", "#9FB4CC", "DB4105", + "#85DB18", "#CDE855", "#F5F6D4", "#A7C520", "493F0B", + "#04BFBF", "#CAFCD8", "#F7E967", "#A9CF54", "588F27", + "#292929", "#5B7876", "#8F9E8B", "#F2E6B6", "412A22", + "#332532", "#644D52", "#F77A52", "#FF974F", "A49A87", + "#405952", "#9C9B7A", "#FFD393", "#FF974F", "F54F29", + "#2B3A42", "#3F5765", "#BDD4DE", "#EFEFEF", "FF530D", + "#962D3E", "#343642", "#979C9C", "#F2EBC7", "348899", + "#96CA2D", "#B5E655", "#EDF7F2", "#4BB5C1", "7FC6BC", + "#1C1D21", "#31353D", "#445878", "#92CDCF", "EEEFF7", + "#3E454C", "#2185C5", "#7ECEFD", "#FFF6E5", "FF7F66", + "#00585F", "#009393", "#FFFCC4", "#F0EDBB", "FF3800", + "#B4AF91", "#787746", "#40411E", "#32331D", "C03000", + "#63A69F", "#F2E1AC", "#F2836B", "#F2594B", "CD2C24", + "#88A825", "#35203B", "#911146", "#CF4A30", "ED8C2B", + "#F2385A", "#F5A503", "#E9F1DF", "#4AD9D9", "36B1BF", + "#CFC291", "#FFF6C5", "#A1E8D9", "#FF712C", "695D46", + "#FF5335", "#B39C85", "#306E73", "#3B424D", "1D181F", + "#000000", "#333333", "#FF358B", "#01B0F0", "AEEE00", + "#E8E595", "#D0A825", "#40627C", "#26393D", "FFFAE4", + "#E7E8D1", "#D3CEAA", "#FBF7E4", "#424242", "8E001C", + "#354242", "#ACEBAE", "#FFFF9D", "#C9DE55", "7D9100", + "#2F2933", "#01A2A6", "#29D9C2", "#BDF271", "FFFFA6", + "#DDDCC5", "#958976", "#611427", "#1D2326", "6A6A61", + "#6C6E58", "#3E423A", "#417378", "#A4CFBE", "F4F7D9", + "#E1E6FA", "#C4D7ED", "#ABC8E2", "#375D81", "183152", + "#6B0C22", "#D9042B", "#F4CB89", "#588C8C", "011C26", + "#304269", "#91BED4", "#D9E8F5", "#FFFFFF", "F26101", + "#96CEB4", "#FFEEAD", "#FF6F69", "#FFCC5C", "AAD8B0", + "#B0CC99", "#677E52", "#B7CA79", "#F6E8B1", "89725B", + "#334D5C", "#45B29D", "#EFC94C", "#E27A3F", "DF5A49", + "#16193B", "#35478C", "#4E7AC7", "#7FB2F0", "ADD5F7", + "#00261C", "#044D29", "#168039", "#45BF55", "96ED89", + "#36362C", "#5D917D", "#A8AD80", "#E6D4A7", "825534", + "#F9E4AD", "#E6B098", "#CC4452", "#723147", "31152B", + "#2C3E50", "#FC4349", "#D7DADB", "#6DBCDB", "FFFFFF", + "#002635", "#013440", "#AB1A25", "#D97925", "EFE7BE", + "#FF8000", "#FFD933", "#CCCC52", "#8FB359", "192B33", + "#272F32", "#9DBDC6", "#FFFFFF", "#FF3D2E", "DAEAEF", + "#B8ECD7", "#083643", "#B1E001", "#CEF09D", "476C5E", + "#002F32", "#42826C", "#A5C77F", "#FFC861", "C84663", + "#5C4B51", "#8CBEB2", "#F2EBBF", "#F3B562", "F06060", + "#5A1F00", "#D1570D", "#FDE792", "#477725", "A9CC66", + "#5E0042", "#2C2233", "#005869", "#00856A", "8DB500", + "#52656B", "#FF3B77", "#CDFF00", "#FFFFFF", "B8B89F", + "#801637", "#047878", "#FFB733", "#F57336", "C22121", + "#730046", "#BFBB11", "#FFC200", "#E88801", "C93C00", + "#24221F", "#363F45", "#4B5F6D", "#5E7C88", "FEB41C", + "#E64661", "#FFA644", "#998A2F", "#2C594F", "002D40", + "#C24704", "#D9CC3C", "#FFEB79", "#A0E0A9", "00ADA7", + "#484A47", "#C1CE96", "#ECEBF0", "#687D77", "353129", + "#588C7E", "#F2E394", "#F2AE72", "#D96459", "8C4646", + "#BAB293", "#A39770", "#EFE4BD", "#A32500", "2B2922", + "#6A7059", "#FDEEA7", "#9BCC93", "#1A9481", "003D5C", + "#174C4F", "#207178", "#FF9666", "#FFE184", "F5E9BE", + "#D5FBFF", "#9FBCBF", "#647678", "#2F3738", "59D8E6", + "#DB5800", "#FF9000", "#F0C600", "#8EA106", "59631E", + "#450003", "#5C0002", "#94090D", "#D40D12", "FF1D23", + "#211426", "#413659", "#656F8C", "#9BBFAB", "F2EFDF", + "#EA6045", "#F8CA4D", "#F5E5C0", "#3F5666", "2F3440", + "#F2F2F2", "#C6E070", "#91C46C", "#287D7D", "1C344D", + "#334D5C", "#45B29D", "#EFC94C", "#E27A3F", "DF5A49", + "#705B35", "#C7B07B", "#E8D9AC", "#FFF6D9", "570026", + "#F7F2B2", "#ADCF4F", "#84815B", "#4A1A2C", "8E3557", + "#1A1F2B", "#30395C", "#4A6491", "#85A5CC", "D0E4F2", + "#25064D", "#36175E", "#553285", "#7B52AB", "9768D1", + "#004056", "#2C858D", "#74CEB7", "#C9FFD5", "FFFFCB", + "#CFCA4C", "#FCF5BF", "#9FE5C2", "#5EB299", "745A33", + "#776045", "#A8C545", "#DFD3B6", "#FFFFFF", "0092B2", + "#CC3910", "#F1F2C0", "#CCC59E", "#8FA68E", "332F29", + "#FF6600", "#C13B00", "#5E6D70", "#424E4F", "1B1D1E", + "#690011", "#BF0426", "#CC2738", "#F2D99C", "E5B96F", + "#1B1D26", "#425955", "#778C7A", "#F1F2D8", "BFBD9F", + "#F6B1C3", "#F0788C", "#DE264C", "#BC0D35", "A20D1E", + "#597533", "#332F28", "#61B594", "#E6DEA5", "C44E18", + "#3FB8AF", "#7FC7AF", "#DAD8A7", "#FF9E9D", "FF3D7F", + "#0F2D40", "#194759", "#296B73", "#3E8C84", "D8F2F0", + "#42282F", "#74A588", "#D6CCAD", "#DC9C76", "D6655A", + "#002A4A", "#17607D", "#FFF1CE", "#FF9311", "D64700", + "#003056", "#04518C", "#00A1D9", "#47D9BF", "F2D03B", + "#13140F", "#D4FF00", "#E4FFE6", "#68776C", "00D6DD", + "#FCFAD0", "#A1A194", "#5B605F", "#464646", "A90641", + "#289976", "#67CC8E", "#B1FF91", "#FFE877", "FF5600", + "#302B1D", "#3F522B", "#737D26", "#A99E46", "D9CB84", + "#56626B", "#6C9380", "#C0CA55", "#F07C6C", "AD5472", + "#32450C", "#717400", "#DC8505", "#EC5519", "BE2805", + "#C7B773", "#E3DB9A", "#F5FCD0", "#B1C2B3", "778691", + "#E83A25", "#FFE9A3", "#98CC96", "#004563", "191B28", + "#3399CC", "#67B8DE", "#91C9E8", "#B4DCED", "E8F8FF", + "#1A212C", "#1D7872", "#71B095", "#DEDBA7", "D13F32", + "#7D2A35", "#CC9258", "#917A56", "#B4BA6C", "FEFFC2", + "#E7E9D1", "#D3D4AA", "#FCFAE6", "#444444", "901808", + "#FFFFFF", "#AEAEAE", "#E64C66", "#2D3E50", "1BBC9B", + "#E0FFB3", "#61C791", "#31797D", "#2A2F36", "F23C55", + "#EB5937", "#1C1919", "#403D3C", "#456F74", "D3CBBD", + "#E6DD00", "#8CB302", "#008C74", "#004C66", "332B40", + "#14A697", "#F2C12E", "#F29D35", "#F27649", "F25252", + "#261822", "#40152A", "#731630", "#CC1E2C", "FF5434", + "#261F27", "#FEE169", "#CDD452", "#F9722E", "C9313D", + "#5C4B51", "#8CBEB2", "#F2EBBF", "#F3B562", "F06060", + "#2F3837", "#C5C7B6", "#FFF8D3", "#4C493E", "222028", + "#E3CBAC", "#9C9985", "#C46D3B", "#788880", "324654", + "#3F0B1B", "#7A1631", "#CF423C", "#FC7D49", "FFD462", + "#14212B", "#293845", "#4F6373", "#8F8164", "D9D7AC", + "#98A89E", "#BAC0AC", "#FAFAC6", "#FF4411", "D40015", + "#FEFFFF", "#3C3F36", "#9FB03E", "#EBE9DC", "72918B", + "#CC6B32", "#FFAB48", "#FFE7AD", "#A7C9AE", "888A63", + "#262526", "#404040", "#8C8979", "#F2F2F2", "F60A20", + "#00305A", "#004B8D", "#0074D9", "#4192D9", "7ABAF2", + "#0C273D", "#54D0ED", "#FFFEF1", "#70B85D", "2C5E2E", + "#4C1B33", "#EFE672", "#98A942", "#2D6960", "141D14", + "#2F3540", "#666A73", "#F2EDE4", "#D9D1C7", "8C8681", + "#0D1F30", "#3B6670", "#8BADA3", "#F0E3C0", "DB6C0F", + "#FFBC67", "#DA727E", "#AC6C82", "#685C79", "455C7B", + "#092140", "#024959", "#F2C777", "#F24738", "BF2A2A", + "#133463", "#365FB7", "#799AE0", "#F4EFDC", "BA9B65", + "#C4D4CB", "#55665E", "#30282A", "#542733", "E84167", + "#CDDEC6", "#4DAAAB", "#1E4F6A", "#2A423C", "93A189", + "#EF5411", "#FA5B0F", "#FF6517", "#FF6D1F", "FF822E", + "#41434A", "#6E9489", "#DEDCC3", "#F2F1E9", "877963", + "#292929", "#2BBFBD", "#F2B33D", "#F29B30", "F22E2E", + "#F2385A", "#F5A503", "#E9F1DF", "#56D9CD", "3AA1BF", + "#D5F8B4", "#A6E3A8", "#8A9A85", "#7E566B", "422335", + "#3CBAC8", "#93EDD4", "#F3F5C4", "#F9CB8F", "F19181", + "#979926", "#38CCB5", "#EEFF8E", "#FFD767", "CC2A09", + "#404040", "#024959", "#037E8C", "#F2EFDC", "F24C27", + "#94B34D", "#D3FF82", "#363D52", "#121D2B", "111B1C", + "#282E33", "#25373A", "#164852", "#495E67", "FF3838", + "#313732", "#8AA8B0", "#DEDEDE", "#FFFFFF", "F26101", + "#FFFFFF", "#E5E1D1", "#52616D", "#2C343B", "C44741", + "#FFF6B8", "#ABCCA7", "#403529", "#7A5E2F", "A68236", + "#4F1025", "#C5003E", "#D9FF5B", "#78AA00", "15362D", + "#49404F", "#596166", "#D1FFCD", "#A9BD8B", "948A54", + "#FF2151", "#FF7729", "#FFAD29", "#FFEBCA", "1AB58A", + "#73603D", "#BF8A49", "#F2CA80", "#5E5A59", "0D0D0D", + "#3D4C53", "#70B7BA", "#F1433F", "#E7E1D4", "FFFFFF", + "#006D8D", "#008A6E", "#549E39", "#8AB833", "C0CF3A", + "#BDDFB3", "#2BAA9C", "#2F2E2E", "#0F2625", "465F3F", + "#F2F2F2", "#BF0404", "#8C0303", "#590202", "400101", + "#76A19A", "#272123", "#A68D60", "#B0C5BB", "D9593D", + "#0E3D59", "#88A61B", "#F29F05", "#F25C05", "D92525", + "#C1E1ED", "#76C7C6", "#273D3B", "#131A19", "E35C14", + "#2D112C", "#530031", "#820233", "#CA293E", "EF4339", + "#AF7575", "#EFD8A1", "#BCD693", "#AFD7DB", "3D9CA8", + "#D74B4B", "#DCDDD8", "#475F77", "#354B5E", "FFFFFF", + "#FFF6C9", "#C8E8C7", "#A4DEAB", "#85CC9F", "499E8D", + "#229396", "#8BA88F", "#C7C5A7", "#F0DFD0", "F23C3C", + "#57385C", "#A75265", "#EC7263", "#FEBE7E", "FFEDBC", + "#96526B", "#D17869", "#EBAD60", "#F5CF66", "8BAB8D", + "#0D1C33", "#17373C", "#2B6832", "#4F9300", "A1D700", + "#1B2B32", "#37646F", "#A3ABAF", "#E1E7E8", "B22E2F", + "#C5D9B2", "#53A194", "#572C2C", "#3D2324", "695A3B", + "#425957", "#81AC8B", "#F2E5A2", "#F89883", "D96666", + "#002E40", "#2A5769", "#FFFFFF", "#FABD4A", "FA9600", + "#FFFEFC", "#E2E3DF", "#515B5E", "#2E3233", "CAF200", + "#FFF0A3", "#B8CC6E", "#4B6000", "#E4F8FF", "004460", + "#3B596A", "#427676", "#3F9A82", "#A1CD73", "ECDB60", + "#F2E6CE", "#8AB39F", "#606362", "#593325", "1D1D1F", + "#212B40", "#C2E078", "#FFFFFF", "#BADCDD", "547B97", + "#0B3C4D", "#0E5066", "#136480", "#127899", "1A8BB3", + "#222130", "#464D57", "#D4E8D3", "#FFFCFB", "ED8917", + "#B33600", "#FF8A00", "#FFC887", "#CC5400", "B31E00", + "#012530", "#28544B", "#ACBD86", "#FFD6A0", "FF302C", + "#2E95A3", "#50B8B4", "#C6FFFA", "#E2FFA8", "D6E055", + "#112F41", "#068587", "#4FB99F", "#F2B134", "ED553B", + "#202B30", "#4E7178", "#4FA9B8", "#74C0CF", "F1F7E2", + "#302B2F", "#696153", "#FFA600", "#9BB58F", "FFD596", + "#458C6B", "#F2D8A7", "#D9A86C", "#D94436", "A62424", + "#22475E", "#75B08A", "#F0E797", "#FF9D84", "FF5460", + "#FFAA5C", "#DA727E", "#AC6C82", "#685C79", "455C7B", + "#686E75", "#9BAAC1", "#82787B", "#E4F1DB", "AAC19B", + "#F0C755", "#E2AD3B", "#BF5C00", "#901811", "5C110F", + "#FFFBDC", "#BFBCA5", "#7F7D6E", "#3F3E37", "E5E2C6", + "#BEBEBE", "#F1E4D8", "#594735", "#94C7BA", "D8F1E4", + "#1B1E26", "#F2EFBD", "#B6D051", "#70A99A", "2F6D7A", + "#F7E4A2", "#A7BD5B", "#DC574E", "#8DC7B8", "ED9355", + "#70E8CB", "#FFE9C7", "#FF5B5B", "#545454", "2D2D2F", + "#17111A", "#321433", "#660C47", "#B33467", "CCBB51", + "#2B2E2E", "#595855", "#A2ABA5", "#CAE6E8", "313F54", + "#023B47", "#295E52", "#F2E085", "#FCAB55", "EE7F38", + "#302C29", "#D1D1BC", "#A7C4BB", "#6C8C84", "466964", + "#212629", "#067778", "#49B8A8", "#85EDB6", "D9E5CD", + "#334D5C", "#45B29D", "#EFC94C", "#E27A3F", "DF4949", + "#2C3E50", "#FC4349", "#6DBCDB", "#D7DADB", "FFFFFF", + "#35262D", "#FFFBFF", "#E8ECED", "#A4B7BB", "76A0B0", + "#61E8D2", "#FCEEB9", "#302F25", "#704623", "BBE687", + "#E1E6B9", "#C4D7A4", "#ABC8A4", "#375D3B", "183128", + "#C98B2F", "#803C27", "#C56520", "#E1B41B", "807916", + "#A3D9B0", "#93BF9E", "#F2F0D5", "#8C8474", "40362E", + "#524656", "#CF4747", "#EA7A58", "#E4DCCB", "A6C4BC", + "#5C2849", "#A73E5C", "#EC4863", "#FFDA66", "1FCECB", + "#0EEAFF", "#15A9FA", "#1B76FF", "#1C3FFD", "2C1DFF", + "#010000", "#393845", "#9B96A3", "#5C0009", "940315", + "#468071", "#FFE87A", "#FFCA53", "#FF893B", "E62738", + "#404040", "#024959", "#037E8C", "#F2EFDC", "F24C27", + "#FF765E", "#C2AE8B", "#FCCF65", "#FFE5C6", "B7BDC4", + "#003647", "#00717D", "#F2D8A7", "#A4A66A", "515932", + "#FAFAC0", "#C4BE90", "#8C644C", "#594D37", "293033", + "#2B3A42", "#3F5765", "#BDD4DE", "#EFEFEF", "E74C3C", + "#3B3B3B", "#A8877E", "#FFA49D", "#FF7474", "FF476C", + "#0A3A4A", "#196674", "#33A6B2", "#9AC836", "D0E64B", + "#FFA340", "#38001C", "#571133", "#017A74", "00C2BA", + "#DCEBDD", "#A0D5D6", "#789AA1", "#304345", "AD9A27", + "#588C7E", "#F2E394", "#F2AE72", "#D96459", "8C4646", + "#F0E6B1", "#B5D6AA", "#99A37A", "#70584B", "3D3536", + "#2F400D", "#8CBF26", "#A8CA65", "#E8E5B0", "419184", + "#010712", "#13171F", "#1C1F26", "#24262D", "961227", + "#403F33", "#6E755F", "#AFC2AA", "#FFDEA1", "E64C10", + "#C74029", "#FAE8CD", "#128085", "#385052", "F0AD44", + "#CFF09E", "#A8DBA8", "#79BD9A", "#3B8686", "0B486B", + "#E0401C", "#E6B051", "#272F30", "#F7EDB7", "9E2B20", + "#FFE2C5", "#FFEEDD", "#FFDDAA", "#FFC484", "FFDD99", + "#FFFFE4", "#F2E5BD", "#B9BF8E", "#A69F7C", "8C6865", + "#5C8A2D", "#AFD687", "#FFFFFF", "#00C3A9", "008798", + "#4F3130", "#FF1F3D", "#5BE3E3", "#FDFFF1", "8B9698", + "#D23600", "#D95100", "#DE6D00", "#EE8900", "FCA600", + "#FFFFFA", "#A1A194", "#5B605F", "#464646", "FF6600", + "#F34A53", "#FAE3B4", "#AAC789", "#437356", "1E4147", + "#2A7A8C", "#176273", "#063540", "#E6D9CF", "403D3A", + "#21455B", "#567D8C", "#A59E8C", "#8C8372", "F2F2F2", + "#012340", "#026873", "#83A603", "#BBBF45", "F2F0CE", + "#FDFF98", "#A7DB9E", "#211426", "#6B073B", "DA8C25", + "#002F36", "#142426", "#D1B748", "#EDDB43", "FFFD84", + "#420000", "#600000", "#790000", "#931111", "BF1616", + "#3C989E", "#5DB5A4", "#F4CDA5", "#F57A82", "ED5276", + "#23A38F", "#B7C11E", "#EFF1C2", "#F0563D", "2E313D", + "#F5ECD9", "#2BACB5", "#B4CCB9", "#E84D5B", "3B3B3B", + "#A5EB3C", "#60C21E", "#159E31", "#53DB50", "C5FFCB", + "#263138", "#406155", "#7C9C71", "#DBC297", "FF5755", + "#0A111F", "#263248", "#7E8AA2", "#E3E3E3", "C73226", + "#003B59", "#00996D", "#A5D900", "#F2E926", "FF930E", + "#00A19A", "#04BF9D", "#F2E85C", "#F53D54", "404040", + "#324152", "#47535E", "#796466", "#C1836A", "DEA677", + "#036F73", "#84CDC2", "#FEF2D8", "#F18C79", "EF504F", + "#174040", "#888C65", "#D9CA9C", "#D98162", "A65858", + "#56797F", "#87A0A4", "#FCFBDC", "#F2DDB6", "A6937C", + "#A8BAA9", "#FFF5CF", "#DBCDAD", "#B39C7D", "806854", + "#60655F", "#AB9675", "#FFE0C9", "#D4CCBA", "CF8442", + "#BDDFB3", "#009D57", "#2C372E", "#0F2925", "465F3F", + "#3E3947", "#735360", "#D68684", "#F1B0B0", "EBD0C4", + "#0A7B83", "#2AA876", "#FFD265", "#F19C65", "CE4D45", + "#FFFFFF", "#F4921E", "#858585", "#C5D2DB", "3E6B85", + "#11151E", "#212426", "#727564", "#B9AA81", "690C07", + "#000000", "#910000", "#CBB370", "#FFFBF1", "21786C", + "#F78F00", "#C43911", "#75003C", "#37154A", "0F2459", + "#003354", "#91BED4", "#D9E8F5", "#FFFFFF", "F26101", + "#3DA8A4", "#7ACCBE", "#FFFFF7", "#FF99A1", "FF5879", + "#64C733", "#F0F0F0", "#3E879E", "#57524D", "36302B", + "#343844", "#2AB69D", "#E65848", "#FDC536", "FCF2D7", + "#E34517", "#F5FF53", "#B4E85E", "#00BD72", "0B4239", + "#A84B3A", "#FF9F67", "#233138", "#FFF7F5", "4C646B", + "#59535E", "#FAEEFF", "#F1BAF3", "#5D4970", "372049", + "#FF6F22", "#D9984F", "#FFE8A9", "#3E4237", "32948A", + "#5D7370", "#7FA6A1", "#B8D9B8", "#D6EDBD", "FFF5BC", + "#FFBE00", "#FFDC00", "#FFD10F", "#FFDE20", "E8CA00", + "#003840", "#005A5B", "#007369", "#008C72", "02A676", + "#E1E6FA", "#C4D7ED", "#ABC8E2", "#375D81", "183152", + "#BA2F1D", "#FFF8A4", "#F5E67F", "#264A59", "1E2C30", + "#222526", "#FFBB6E", "#F28D00", "#D94F00", "80203B", + "#EBD096", "#D1B882", "#5D8A66", "#1A6566", "21445B", + "#F00807", "#5F6273", "#A4ABBF", "#CCC9D1", "E2E1E9", + "#DFE0AF", "#A4BAA2", "#569492", "#41505E", "383245", + "#152737", "#2B4E69", "#799AA5", "#FFFFF0", "682321", + "#C44C51", "#FFB6B8", "#FFEFB6", "#A2B5BF", "5F8CA3", + "#5ADED4", "#4DAAAB", "#26596A", "#163342", "6C98A1", + "#FF5B2B", "#B1221C", "#34393E", "#8CC6D7", "FFDA8C", + "#3D4D4D", "#99992E", "#E6E666", "#F2FFBF", "800033", + "#242424", "#437346", "#97D95C", "#D9FF77", "E9EB9B", + "#FFEBB0", "#FFB05A", "#F84322", "#C33A1A", "9F3818", + "#4D2B2F", "#E57152", "#E8DE67", "#FFEFC3", "C0CCAB", + "#A82221", "#DB5E31", "#EDA23E", "#F2CB67", "BFB840", + "#3B3140", "#BFB8A3", "#F2E0C9", "#F2B9AC", "D97E7E", + "#43464D", "#9197A6", "#D3DCF2", "#7690CF", "48577D", + "#EFDFBB", "#9EBEA6", "#335D6A", "#D64F2A", "7A8A7F", + "#000001", "#313634", "#C7CECF", "#5C0402", "941515", + "#334D5C", "#45B29D", "#EFC94C", "#E27A3F", "DF5A49", + "#F5F4E1", "#D6C9B5", "#B4AA97", "#D44917", "82877A", + "#19162B", "#1C425C", "#6ABDC4", "#F0E4C5", "D6C28F", + "#00132B", "#7F9DB0", "#C5E2ED", "#FFFFFF", "F95900", + "#1F3642", "#6D968D", "#B6CCB8", "#FFE2B3", "56493F", + "#08A689", "#82BF56", "#C7D93D", "#E9F2A0", "F2F2F2", + "#DE3961", "#A4E670", "#FFFFDC", "#B3EECC", "00ADA7", + "#849972", "#D9D094", "#A6A23E", "#4F2F1D", "8F5145", + "#F41C54", "#FF9F00", "#FBD506", "#A8BF12", "00AAB5", + "#00585F", "#009393", "#F5F3DC", "#454445", "FF5828", + "#FF6138", "#FFFF9D", "#BEEB9F", "#79BD8F", "00A388", + "#140B04", "#332312", "#B59D75", "#E3D2B4", "FFF7EA", + "#ED3B3B", "#171F26", "#77B59C", "#F2E7B1", "635656", + "#46594B", "#A6977C", "#D9B384", "#734F30", "260B01", + "#CCB8A3", "#FF8FB1", "#FFF5EA", "#4E382F", "B29882", + "#B70000", "#FFFFFF", "#FFCA3D", "#94C4F4", "0092B3", + "#053B44", "#06736C", "#A53539", "#B9543C", "EAD075", + "#E8C1B9", "#FFB3AB", "#FFCAB8", "#E8B69C", "FFCEAB", + "#E7F2DF", "#69043B", "#59023B", "#231E2D", "161726", + "#E82B1E", "#E5DEAF", "#A0B688", "#557A66", "453625", + "#F1E6D4", "#BA3D49", "#791F33", "#9F9694", "E3E1DC", + "#CED59F", "#F1EDC0", "#B1BEA4", "#647168", "282828", + "#2C3E50", "#E74C3C", "#ECF0F1", "#3498DB", "646464", + "#DE7047", "#FFDE8D", "#FFFFFF", "#CDDE47", "528540", + "#8EAB99", "#40232B", "#D95829", "#D97338", "DEC085", + "#E9662C", "#EBAF3C", "#00AC65", "#068894", "2B2B2B", + "#46483C", "#A0AA8F", "#EBE3CB", "#FFFFFF", "F26101", + "#170F0E", "#290418", "#505217", "#FFD372", "FFF1AF", + "#263545", "#C4273C", "#D7DADB", "#6DBCDB", "FFFFFF", + "#DCFAC0", "#B1E1AE", "#85C79C", "#56AE8B", "00968B", + "#075807", "#097609", "#70AF1A", "#B9D40B", "E5EB0B", + "#521000", "#712800", "#744E1D", "#879666", "F1D98C", + "#261F26", "#3F3B40", "#6C7367", "#BFBF8A", "F2E086", + "#2C3E50", "#FC4349", "#D7DADB", "#6DBCDB", "FFFFFF", + "#506D7D", "#94CCB9", "#FFECA7", "#FFB170", "F07D65", + "#3F4036", "#8DA681", "#F2E1C2", "#BF2806", "8C1D04", + "#990700", "#CC542E", "#FF964F", "#FFCB7C", "787730", + "#195073", "#7F8C1F", "#EE913F", "#F2E5BD", "9FD7C7", + "#1B3E59", "#F2F0F0", "#FFAC00", "#BF0404", "730202", + "#EA6045", "#F8CA4D", "#F5E5C0", "#3F5666", "2F3440", + "#F95759", "#FDA099", "#FFFFFF", "#D9F3CB", "8AC2B0", + "#265573", "#386D73", "#81A68A", "#9FBF8F", "D4D9B0", + "#E1DA36", "#FFEA1B", "#6FE4DA", "#1DB0BC", "007BBC", + "#013859", "#185E65", "#F9CC7F", "#F15C25", "9E1617", + "#36CC7C", "#D6FFBE", "#94D794", "#228765", "77A668", + "#94201F", "#D4421F", "#478A80", "#D9E061", "F08835", + "#F16233", "#00B5B5", "#F0F0F0", "#3E4651", "5C6D7E", + "#2E806C", "#76CC99", "#E0FFED", "#FF5F3A", "D2413C", + "#00393B", "#00766C", "#44A18E", "#E5EDB6", "F6695B", + "#734854", "#F2F2E9", "#D9D7C5", "#A69580", "736766", + "#03497E", "#0596D5", "#9DEBFC", "#8D7754", "FEB228", + "#F0E14C", "#FFBB20", "#FA7B12", "#E85305", "59CC0D", + "#FE4365", "#FC9D9A", "#F9CDAD", "#C8C8A9", "83AF9B", + "#00557C", "#186D94", "#3488AD", "#81C1DC", "BBE5F3", + "#DEE8D7", "#918773", "#420A1A", "#240001", "4D493A", + "#FFFFFF", "#CAC535", "#97AF25", "#158471", "41342C", + "#041F3D", "#0B2E41", "#165751", "#448C61", "9AC16D", + "#FA8C01", "#FF6405", "#577700", "#082400", "A0A600", + "#78C0F9", "#FFDDCE", "#FFFFFF", "#FFDBE6", "FE86A4", + "#351330", "#CC2A41", "#E7CAA4", "#759A8A", "524549", + "#02151A", "#043A47", "#087891", "#C8C8C8", "B31D14", + "#F34A53", "#FAE3B4", "#AAC789", "#437356", "1E4147", + "#58838C", "#DAD7C7", "#BF996B", "#BF5841", "A61C1C", + "#556354", "#E68F0D", "#8C948A", "#495450", "42423F", + "#323640", "#5B6470", "#8C94A1", "#BDC7D6", "DFE2FF", + "#FF0000", "#FF950B", "#2FA88C", "#DEEB00", "4B2C04", + "#0F3D48", "#174C5B", "#366774", "#ECECE7", "E96151", + "#3DBB7E", "#A3CD39", "#FBAC1D", "#F96C1E", "EE4036", + "#23363B", "#A44F3F", "#F8983D", "#8D9151", "BBC946", + "#4B5657", "#969481", "#D2C9B0", "#F4E3C1", "B6B835", + "#E8980C", "#B1F543", "#F2FF00", "#FF5E00", "59BBAB", + "#849696", "#FEFFFB", "#232D33", "#17384D", "FF972C", + "#555555", "#7BB38E", "#F4F1D7", "#F8AB65", "F15C4C", + "#1D3C42", "#67BFAD", "#F2EC99", "#F2C48D", "F25050", + "#334D5C", "#45B29D", "#EFC94C", "#E27A3F", "DF4949", + "#B8E1F2", "#249AA7", "#ABD25E", "#F8C830", "F1594A", + "#FDEDD0", "#BCF1ED", "#FF634D", "#FD795B", "FFF0AA", + "#FFFFFF", "#E5E1D1", "#52616D", "#2C343B", "C44741", + "#FFFFF1", "#D5FF9B", "#8FB87F", "#5A7B6C", "374E5A", + "#010340", "#0E1E8C", "#0003C7", "#1510F0", "1441F7", + "#002A4A", "#17607D", "#FFF1CE", "#FF9311", "E33200", + "#871E31", "#CCC097", "#9E9D7B", "#687061", "262626", + "#F16663", "#F48D6C", "#F2E07B", "#8ABE9B", "4A6D8B", + "#001F11", "#204709", "#0C8558", "#FFD96A", "FF4533", + "#1D1626", "#F2E0BD", "#BFAA8F", "#8C786C", "594C4C", + "#685D47", "#913420", "#1E2729", "#C1D9C5", "FEEFB1", + "#1D7561", "#FC8448", "#FF4138", "#A8282B", "38141B", + "#BF0633", "#FF484E", "#FF9273", "#D1D0B4", "E5ECED", + "#8E9E63", "#E6DBB0", "#F5EED7", "#C4BCA0", "176573", + "#665446", "#809994", "#AECCB6", "#DEF2C4", "E6683F", + "#3D0D26", "#660A3E", "#891C56", "#B0276F", "C93482", + "#082136", "#00294D", "#004B8D", "#0068C4", "2998FF", + "#3C4631", "#9A746F", "#F8A2AB", "#F1C6B3", "EAE9C0", + "#FF534E", "#FFD7AC", "#BED194", "#499989", "176785", + "#006D80", "#BDA44D", "#3C2000", "#84CECC", "78A419", + "#352C2B", "#3C555C", "#9E9657", "#FFEBCD", "CD5510", + "#2C3E50", "#FC4349", "#6DBCDB", "#D7DADB", "FFFFFF", + "#523631", "#D1BE91", "#605E3A", "#4D462F", "592F39", + "#18293B", "#5B5A56", "#F2DEA0", "#D0B580", "FFFBFF", + "#C8DBB6", "#ECEBB7", "#CCC68A", "#B8B165", "827A5D ", + "#7DA88C", "#EBE9A0", "#BED24B", "#859132", "35323C", + "#E8574C", "#F27B29", "#E6A51B", "#D9CC3C", "399977", + "#324032", "#B7C22C", "#FFFFE1", "#22A8B5", "2A3F42", + "#B3A589", "#FFB896", "#FFF9B1", "#9AB385", "11929E", + "#272433", "#343F4F", "#3D6066", "#77994D", "B2D249", + "#250701", "#6D4320", "#B0925F", "#E7DEC0", "82ABB8", + "#023550", "#028A9E", "#04BFBF", "#EFEFEF", "FF530D", + "#594732", "#40342A", "#7A422E", "#D4CA9A", "EDE5AE", + "#013C4D", "#BA5B22", "#DB913C", "#F0B650", "FAD46B", + "#143840", "#5C6B63", "#A69E89", "#E0C297", "D96523", + "#3FB8AF", "#7FC7AF", "#DAD8A7", "#FFB38B", "FF3F34", + "#CA3995", "#F58220", "#FFDF05", "#BED73D", "61BC46", + "#FFE1D0", "#FFBFB4", "#FF837E", "#FF4242", "BF1616", + "#C4EEFF", "#7BA32D", "#094201", "#A41717", "C48726", + "#001325", "#187072", "#90BD90", "#D7D8A2", "F2E4C2", + "#1A4F63", "#068587", "#6FB07F", "#FCB03C", "FC5B3F", + "#97B350", "#333230", "#736D61", "#BAAB90", "FFE5BA", + "#403D33", "#807966", "#CCC2A3", "#8C0000", "590000", + "#5F8A42", "#86AD59", "#F6FAA1", "#F28410", "D66011", + "#BF355D", "#ED8168", "#FAB66A", "#F2DC86", "83BFA1", + "#E1F03E", "#FFBA14", "#DB3A0F", "#A1003D", "630024", + "#212226", "#45433F", "#687067", "#BDBB99", "F0EAC3", + "#FE4365", "#FC9D9A", "#F9CDAD", "#C8C8A9", "83AF9B", + "#293B47", "#5F7A87", "#FFFFFF", "#CBFF48", "00ADA9", + "#282A33", "#697371", "#FFE7A6", "#F5BA52", "FA8000", + "#0C304A", "#2B79A1", "#F3F4F1", "#85A71E", "BFD841", + "#008B83", "#4DAE83", "#A0AE79", "#FFE499", "FF665E", + "#5D7359", "#E0D697", "#D6AA5C", "#8C5430", "661C0E", + "#324452", "#97BDBF", "#F2DFBB", "#F28705", "BF3604", + "#EEEFB9", "#6ACFAE", "#369C93", "#232928", "B03831", + "#332F45", "#015770", "#2A8782", "#9FD6AE", "FFFED2", + "#2B2830", "#5C504F", "#ABAB8E", "#D9D7A3", "C7BE88", + "#DC941B", "#EDC266", "#B6952C", "#E1D3A6", "E9A119", + "#00305A", "#00448D", "#0074D9", "#4192D9", "7ABAF2", + "#344459", "#485F73", "#5DA6A6", "#A9D9CB", "F2EAD0", + "#060719", "#4D1B2F", "#9E332E", "#EB6528", "FC9D1C", + "#96CEB4", "#FFEEAD", "#FF6F69", "#FFCC5C", "AAD8B0", + "#05F2F2", "#04BFBF", "#EEF1D9", "#A60201", "7E100E", + "#E6F1F5", "#636769", "#AAB3B6", "#6E7476", "4B4E50", + "#DA0734", "#F1A20D", "#4AABB1", "#FCF3E7", "3F1833", + "#202D44", "#FC4349", "#6DBCDB", "#D7DADB", "FFFFFF", + "#CC3B37", "#398899", "#FFFCE8", "#FF857F", "CCC1A3", + "#5DBEA9", "#EFEDDF", "#EF7247", "#4E3F35", "D1CBBA", + "#FFC62D", "#E49400", "#DD5200", "#EFE38A", "91B166", + "#B67D14", "#F2921F", "#F0B23E", "#A62409", "441208", + "#C71B1B", "#D6BA8A", "#017467", "#E08F23", "0B0D0C", + "#474143", "#A69E9D", "#E7E2DA", "#FFFFFF", "E7E8E7", + "#435772", "#2DA4A8", "#FEAA3A", "#FD6041", "CF2257", + "#6DD19D", "#99E89D", "#D0E8A1", "#FFF9C0", "D40049", + "#FAF1D5", "#DEC9AC", "#CCA18B", "#11282D", "A5C4BB", + "#000000", "#141414", "#1C1919", "#1A1716", "24201F", + "#D5D8DD", "#5CA2BE", "#135487", "#2A4353", "989DA4", + "#73161E", "#BF0F30", "#BFB093", "#037F8C", "0A2140", + "#195962", "#F56F6C", "#FFFFFF", "#252932", "191C21", + "#F8EFB6", "#FEBAC5", "#6CD1EA", "#FACFD7", "C2EAE9", + "#91D6BC", "#768C6A", "#755F31", "#B37215", "FFBA4B", + "#F2E6BB", "#DD4225", "#202724", "#63BD99", "F8FDD8", + "#762B1B", "#807227", "#CCBF7A", "#FFEF98", "60B0A1", + "#707864", "#C1D74E", "#F5FF7C", "#DFE6B4", "A6B89C", + "#FFF3D2", "#97B48F", "#E87657", "#FF9B6F", "E8D495", + "#33262E", "#733230", "#CC5539", "#E6D27F", "86A677", + "#122430", "#273E45", "#FFFCE2", "#EBD2B5", "E63531", + "#30394F", "#FF434C", "#6ACEEB", "#EDE8DF", "FFFBED", + "#0A3A4A", "#196A73", "#32A6A6", "#A1BF36", "C8D94A", + "#FFF7CC", "#CCC28F", "#70995C", "#33664D", "142933", + "#43464D", "#9197A6", "#D3DCF2", "#7690CF", "48577D", + "#DFE0AF", "#A4BAA2", "#569492", "#41505E", "383245", + "#B52841", "#FFC051", "#FF8939", "#E85F4D", "590051", + "#473C35", "#A36D5C", "#9C968B", "#D9CEAD", "8A866A", + "#DB4C39", "#2D3638", "#109489", "#44D487", "D0DB86", + "#6F8787", "#AEC2AE", "#E6DFAE", "#B0B57B", "888F51", + "#C8385A", "#FFCF48", "#ECEABE", "#1FCECB", "1CA9C9", + "#42282E", "#75A48B", "#D9CFB0", "#DC9B74", "D6665A", + "#362F2D", "#4C4C4C", "#94B73E", "#B5C0AF", "FAFDF2", + "#98293A", "#B14A58", "#C86C6B", "#DE9D76", "EFC77F", + "#C1D301", "#76AB01", "#0E6A00", "#083500", "042200", + "#453F22", "#7A6B26", "#CCAD5C", "#A1191F", "4E1716", + "#541E32", "#8E3557", "#88A33E", "#C2BD86", "F7F2B2", + "#2B1B2E", "#54344D", "#FFFFD6", "#B89E95", "6E444F", + "#6EC1A5", "#9FBEA6", "#F5D3A3", "#FF9F88", "FB7878", + "#2F252C", "#D3CCB2", "#99AD93", "#6E6751", "5C3122", + "#BE333F", "#F2E9CE", "#C8C5B1", "#939F88", "307360", + "#F0F1F2", "#232625", "#647362", "#B3D929", "D2D9B8", + "#FA2B31", "#FFBF1F", "#FFF146", "#ABE319", "00C481", + "#09455C", "#527E7C", "#F5FFCC", "#E0EB6E", "C4D224", + "#F2DA91", "#F2B950", "#F29D35", "#D96704", "BF4904", + "#A2CFA5", "#E0E7AB", "#F5974E", "#E96B56", "D24344", + "#150033", "#310D42", "#5C2445", "#AB6946", "FFCE4C", + "#23A38F", "#B7C11E", "#EFF1C2", "#F0563D", "2E313D", + "#FF2468", "#E0D4B1", "#FFFFE3", "#00A5A6", "005B63", + "#65A683", "#218777", "#3F585F", "#47384D", "F53357", + "#000623", "#28475C", "#4A6C74", "#8BA693", "F0E3C0", + "#E65322", "#D19552", "#B8BF73", "#B6DB83", "FFF991", + "#112F41", "#068587", "#6FB07F", "#FCB03C", "FC5B3F", + "#C89B41", "#A16B2B", "#77312B", "#1C2331", "152C52", + "#C24366", "#D9C099", "#FFF8D8", "#A8E0BA", "00ADA7", + "#CC0000", "#006600", "#FFFFEC", "#9C9178", "6C644F", + "#3D0319", "#720435", "#C1140E", "#FC5008", "32241B", + "#CFC7A4", "#5A9E94", "#005275", "#002344", "A38650", + "#FFEBC3", "#CC3A00", "#FF3600", "#FF851B", "800C00", + "#EFC164", "#F3835D", "#F35955", "#286275", "00434C", + "#E9F29D", "#B7C29D", "#878E8F", "#67617A", "51456B", + "#445859", "#03A696", "#49C4BE", "#F1F2E4", "FF7746", + "#FA726C", "#FFD794", "#BAD174", "#3BA686", "5F6F8C", + "#4D2B1F", "#635D61", "#7992A2", "#97BFD5", "BFDCF5", + "#CC4D00", "#E6CF73", "#668059", "#264D4D", "00CCB3", + "#4385F5", "#DC4437", "#FCBE1F", "#109D59", "FFFFFF", + "#271F2E", "#A4A680", "#F2EBC9", "#D9B166", "A66B38", + "#0B2C3C", "#FF6666", "#DADFE1", "#FFFFFF", "444444", + "#CFF09E", "#A8DBA8", "#79BD9A", "#3B8686", "0B486B", + "#302B26", "#A6B827", "#EDE9DD", "#98D3D4", "594E7A", + "#4B0505", "#720707", "#BFB694", "#004659", "00292B", + "#B52C38", "#EBD1B0", "#536682", "#D9964B", "DE6846", + "#F2F1DF", "#F2B705", "#F2C84B", "#BF820F", "734002", + "#26140C", "#3D2216", "#784E3D", "#AB8574", "D6BCB1", + "#26221D", "#8C2C0F", "#E6E5B8", "#BFB38D", "402D1F", + "#1F8181", "#F2BC79", "#F28972", "#BF1B39", "730240", + "#002635", "#013440", "#AB1A25", "#D97925", "EFE7BE", + "#8EC447", "#FFFFFF", "#96D3D4", "#636466", "2D2D2E", + "#2D1E1E", "#4B3C37", "#96A576", "#CDE196", "FFFFBE", + "#F06060", "#FA987D", "#F7F2CB", "#72CCA7", "10A296", + "#1D8281", "#44BF87", "#FBD258", "#F29A3F", "DB634F", + "#DEDE91", "#EF9950", "#F34E52", "#C91452", "492449", + "#6D8EAD", "#1F3447", "#1A0B07", "#362416", "CFCDB4", + "#00CD73", "#008148", "#2D9668", "#3ECD8E", "004E2C", + "#3D8080", "#628282", "#858383", "#A38282", "C28080", + "#475159", "#839795", "#B2BDB7", "#CCC9C0", "F2F2F2", + "#0E6870", "#C6B599", "#C65453", "#FFDDB4", "EDAA7D", + "#CEF0B7", "#A8DBA8", "#79BD9A", "#3B8686", "0B486B", + "#292C44", "#FF5349", "#F0F0F1", "#18CDCA", "4F80E1", + "#272A2B", "#383737", "#473B39", "#692B28", "940500", + "#D6C274", "#DB9E46", "#25706B", "#3D2423", "AB362E", + "#FFA68F", "#FF4867", "#FFF9C8", "#B5EBB9", "18B29D", + "#A1A16A", "#727D59", "#366353", "#133C40", "03212E", + "#D45354", "#A9DC3A", "#2FCAD8", "#818B85", "CDCDC1", + "#F14B6A", "#3D3C3E", "#22BDAF", "#BAD7D4", "F4F4F4", + "#FFE2C5", "#FFEEDD", "#FFDDAA", "#FFC484", "FFDD99", + "#9FFF4A", "#1ABF93", "#087363", "#004040", "2F1933", + "#FFDB97", "#B28F4E", "#FFFDFB", "#466CB2", "97BBFF", + "#991C00", "#E09A25", "#FFFCDB", "#008B83", "262B30", + "#44281A", "#00ACAE", "#F5EFD5", "#F37606", "EE4717", + "#FF5952", "#FCEEC9", "#96D6D9", "#4FAAC9", "176075", + "#5C4B51", "#8CBEB2", "#F2EBBF", "#A5C88F", "EF847B", + "#105F73", "#F7F3B2", "#C6CC33", "#F28322", "CC5404", + "#137072", "#56B292", "#B7F5AB", "#FBFFC0", "BF223D", + "#E3F23E", "#6C821C", "#A6A53F", "#E0E0AC", "33302E", + "#00215E", "#003CAA", "#1967F7", "#5E4000", "AA7400", + "#273A3D", "#54695C", "#AD9970", "#FFBF87", "FF8F60", + "#FFAA00", "#C2B93E", "#808F5D", "#576157", "302F30", + "#BE1405", "#F2DCAC", "#AABEAA", "#736E41", "413C2D", + "#6B1229", "#C76A61", "#FAB99A", "#F7D9B5", "CCB1A7", + "#2D9993", "#58B3A3", "#83BFA3", "#B0D9A8", "FFFCB6", + "#334D5C", "#45B29D", "#EFC94C", "#E27A3F", "DF5A49", + "#F30B55", "#010326", "#012840", "#54717F", "F2E6CE", + "#2A3411", "#73662C", "#BC9847", "#FFDFB2", "6B0031", + "#637D74", "#403D3A", "#8C3B3B", "#AB6937", "D4A960", + "#010A26", "#011640", "#B6D6F2", "#FFFFFF", "E83338", + "#924847", "#EB986C", "#E4C678", "#9C7885", "372C2C", + "#022440", "#3F95AA", "#4EC6DE", "#EAE2DF", "F7572F", + "#2B1D2E", "#323657", "#076473", "#54B087", "D6F567", + "#052229", "#004043", "#BCC373", "#E3FF55", "D0D90C", + "#4C514A", "#907A62", "#879796", "#755854", "B09880", + "#1D2939", "#1CAF9A", "#FFFFFF", "#EE4F4B", "D1DC48", + "#004B67", "#41CCB4", "#FFEA95", "#FF7C5D", "C70151", + "#C0272D", "#FCFBE7", "#9FD3DA", "#008C9A", "05484F", + "#213130", "#FF5E3D", "#C9C83E", "#FDFFF1", "559398", + "#B1E4FC", "#366D78", "#39D5F1", "#FFFFFF", "D9FF03", + "#DECE6C", "#FCF9B6", "#BFE3B5", "#5D826E", "262E2B", + "#520A17", "#668F91", "#F5E6AC", "#AB8E5B", "52301C", + "#2D3032", "#DD5F18", "#FBA922", "#F7F7F7", "404333", + "#0C2538", "#2B434F", "#638270", "#BCC98E", "EDE059", + "#E85066", "#F28E76", "#E6CEB0", "#5A8C81", "382837", + "#BF2633", "#A6242F", "#D9CEAD", "#C0B18F", "011C26", + "#002A4A", "#17607D", "#FFF1CE", "#FF9311", "E33200", + "#0A8B91", "#485956", "#C4B98F", "#FFF9BC", "EEDF2E", + "#B89A7B", "#9BBAAC", "#F2D649", "#D95D50", "DBDBDB", + "#BD7938", "#8D4421", "#643001", "#532700", "3A1C00", + "#E1E6FA", "#C4D7ED", "#ABC8E2", "#375D81", "183152", + "#2E4259", "#F7483B", "#ECF0F1", "#03C8FA", "737373", + "#364656", "#5D6B74", "#94A4A5", "#F2F5E9", "FF8C31", + "#3E5916", "#93A605", "#F28705", "#F25C05", "E5EFFA", + "#248077", "#74AD8D", "#C82754", "#F7BB21", "F9E2B7", + "#20736A", "#8BD9CA", "#B1D95B", "#93A651", "403E34", + "#D74B4B", "#DCDDD8", "#475F77", "#354B5E", "FFFFFF", + "#252F33", "#415C4F", "#869C80", "#93C2CC", "CEEAEE", + "#012840", "#79C7D9", "#9BF2EA", "#497358", "9DBF8E", + "#EE7E94", "#F8B4C4", "#C7CAC9", "#D8505C", "41424", + "#282828", "#505050", "#FFFFFF", "#2DCEDB", "F20000", + "#004358", "#1F8A70", "#BEDB39", "#FF5347", "FD7400", + "#470C3B", "#802F56", "#C0576F", "#E38679", "FFBD83", + "#573328", "#B05A3A", "#FF8548", "#29332E", "0F1B1C", + "#461F2D", "#E1FFBB", "#BAD47F", "#849C23", "52533F", + "#333A40", "#4C5E5E", "#ADD0E5", "#CDE4FF", "729EBF", + "#DE5605", "#F7A035", "#B1DEB5", "#EFECCA", "65ABA6", + "#76D6D2", "#F9E270", "#EF6F56", "#F4EED8", "596B56", + "#403E3F", "#F2F2F2", "#D9D9D9", "#9DAABB", "8C8C8C", + "#059E9A", "#F4F2ED", "#F5A243", "#DB3E3B", "585857", + "#FFBF41", "#EE8943", "#C02221", "#FFF4D3", "249CA9", + "#024E76", "#39A6B2", "#FCE138", "#F5B824", "F08106", + "#FF0067", "#FF3D6A", "#E7FF04", "#9CFF00", "56FF00", + "#003540", "#0D3F40", "#487360", "#8FA671", "F2D795", + "#FF493C", "#FFFFFF", "#B3ECEF", "#31C4F5", "ADEB41", + "#244358", "#4A8B87", "#7CBCAE", "#F0D4AF", "C5252B", + "#EA5930", "#F8AF1E", "#F5E5C0", "#097380", "372560", + "#A1DBB2", "#FEE5AD", "#FACA66", "#F7A541", "F45D4C", + "#2C4A47", "#6C9A7F", "#BB523D", "#C89D11", "81810B", + "#F0F1F2", "#232625", "#647362", "#FF5629", "D2D9B8", + "#7C9B5F", "#B8D197", "#E3FFF3", "#9BDEC7", "568F84", + "#E54E45", "#DBC390", "#F2F2EF", "#13A3A5", "403833", + "#77A7FB", "#E57368", "#FBCB43", "#34B67A", "FFFFFF", + "#001A2E", "#8F0000", "#FFFFFF", "#8A874B", "41594F", + "#312F40", "#49A69C", "#EFEAC5", "#E89063", "BF5656", + "#047C8C", "#018B8D", "#F3BF81", "#F49B78", "F1706D", + "#00303E", "#7096AD", "#C1D1DE", "#FFF9EF", "EC4911", + "#2D6891", "#70A0BF", "#F5EEDC", "#DC4C1A", "F0986C", + "#040002", "#3D1309", "#E8B96A", "#BC5D15", "5C0F00", + "#8B929C", "#5E6070", "#514454", "#3B313D", "FF2479", + "#142D58", "#447F6E", "#E1B65B", "#C8782A", "9E3E17", + "#22104D", "#2D1E5E", "#483A85", "#7067AB", "A49CFA", + "#919C86", "#9E373E", "#2B2E36", "#D1B993", "C45A3B", + "#332F45", "#015770", "#2A8782", "#9FD6AE", "FFFED2", + "#37C78F", "#FEE293", "#FF4D38", "#CC2249", "380C2A", + "#47282C", "#8C8468", "#C9B37F", "#DBDAB7", "C4C49C", + "#14191A", "#2D2B21", "#A69055", "#CCB287", "FFB88C", + "#F5E3CD", "#696158", "#B6A898", "#877D71", "504A43", + "#005151", "#009393", "#F56200", "#454445", "969692", + "#D95F47", "#FFF2C1", "#80A894", "#106153", "072C36", + "#9E352C", "#E6E8A9", "#93C28C", "#2E5A5C", "2B2623", + "#03013A", "#334A94", "#6B9EDF", "#83C3F2", "99E6FF", + "#372A26", "#4D4D4D", "#6DA0A7", "#9ED5A8", "C7F5FF", + "#03658C", "#022E40", "#F2B705", "#F28705", "F25C05", + "#FF3B16", "#E87826", "#E8BA4A", "#80A272", "003045", + "#00748E", "#E3DFBB", "#F4BA4D", "#E3753C", "DA3B3A", + "#25401E", "#56732C", "#84A63C", "#B8D943", "EAF2AC", + "#449BB5", "#043D5D", "#EB5055", "#68C39F", "FFFCF5", + "#108F97", "#FF8B6B", "#FFE39F", "#16866D", "103636", + "#1A4F63", "#068F86", "#6FD57F", "#FCB03C", "FC5B3F", + "#381C19", "#472E29", "#948658", "#F0E99A", "362E29", + "#D7E8F7", "#BBD0E3", "#9CB7CF", "#6A8BAB", "375D81", + "#0F1C28", "#136972", "#67BFA7", "#F3CF5B", "F07444", + "#FFFFFF", "#4EA9A0", "#969514", "#FE9C03", "FCDE8E", + "#2F2D30", "#656566", "#65537A", "#51386E", "2A2333", + "#4C2916", "#F05A28", "#FBAF3F", "#38B449", "FFFFFF", + "#132537", "#006C80", "#EBCAB8", "#FE8315", "FA3113", + "#ECEEE1", "#A8DACF", "#F05B4F", "#D8403A", "221E1F", + "#00305A", "#004B8C", "#0074D9", "#4192D9", "7ABAF2", + "#72CF3F", "#85FF00", "#23E000", "#2FB81B", "00FF1C", + "#45CEEF", "#FFF5A5", "#FFD4DA", "#99D2E4", "D8CAB4", + "#FF5B00", "#A1716C", "#728296", "#439AAB", "00CABD", + "#EB6C2D", "#D9C8A2", "#939C80", "#496158", "232F38", + "#D94214", "#FFF2C1", "#80A894", "#52736B", "093844", + "#4D1B2F", "#9E332E", "#EB6528", "#FC9D1C", "FFCA50", + "#FFEEB0", "#9AE8A4", "#C7C12D", "#F76245", "ED1C43", + "#FFFAED", "#D4DBFF", "#879AC9", "#242942", "FF8800", + "#022840", "#013440", "#517360", "#9DA67C", "F2DC99", + "#331A0F", "#519994", "#BA4B3C", "#EEDDAA", "789F63", + "#577867", "#EDCE82", "#D68644", "#AB3229", "662845", + "#435A66", "#88A6AF", "#F5F2EB", "#D9CDB8", "424342", + "#FF8840", "#958D4F", "#737B55", "#595540", "513E38", + "#9D805A", "#EBC99D", "#FFE6C5", "#9DCEEA", "4B809E", + "#272D40", "#364659", "#55736D", "#9DBF8E", "D0D991", + "#23A38F", "#B7C11E", "#EFF1C2", "#F0563D", "2E313D", + "#98C000", "#3D4C53", "#EA2E49", "#FFE11A", "0CDBE8", + "#A20E30", "#E93C4F", "#DCDCD4", "#ADBCC3", "2D4255", + "#1C2640", "#263357", "#384C80", "#4E6AB3", "5979CD", + "#D94214", "#FFF2C1", "#80A894", "#52736B", "093844", + "#3B596A", "#427676", "#3F9A82", "#A1CD73", "ECDB60", + "#1E1E1F", "#424143", "#67666A", "#807F83", "CBC9CF", + "#E04946", "#3BA686", "#B6D15D", "#FFD495", "FA847E", + "#FFEBB0", "#FFB05A", "#F84322", "#C33A1A", "9F3818", + "#FFA136", "#FF814A", "#E6635A", "#785D6B", "534557", + "#CDCF91", "#EBEACC", "#D6D5B8", "#6D7D80", "41545E", + "#011526", "#011C40", "#4E8DA6", "#F2EA79", "F2B33D", + "#353230", "#3F4E51", "#7B8F70", "#99B2BE", "F6F4EA", + "#063559", "#0D8C7F", "#8FBF4D", "#F2D13E", "D95929", + "#158000", "#199900", "#20BF00", "#24D900", "29FF00", + "#0B0D0E", "#137074", "#7EB7A3", "#F1DDBB", "EC6766", + "#02151A", "#043A47", "#087891", "#C8C8C8", "B31D14", + "#59361F", "#5C992E", "#A3CC52", "#E6E673", "FF5933", + "#FE4365", "#FC9D9A", "#F9CDAD", "#C8C8A9", "83AF9B", + "#4B1E18", "#F9E5C2", "#BBB082", "#829993", "4F5D4E", + "#032843", "#1F595B", "#508C6D", "#71A670", "A6DB89", + "#191724", "#4C4547", "#8C594E", "#D18952", "FDB157", + "#191919", "#182828", "#60702D", "#AAB232", "E6FA87", + "#212A3F", "#434F5B", "#F2F2F2", "#8AB839", "2E2E2E", + "#004158", "#026675", "#038B8B", "#F1EEC9", "F09979", + "#023059", "#3F7EA6", "#F2F2F2", "#D99E32", "BF5E0A", + "#F21E52", "#FFFFFF", "#3D3B42", "#0C6F73", "63CFD4", + "#452743", "#E7635E", "#F8E9A8", "#89E0AD", "00928C", + "#FAAD63", "#D1714D", "#785E48", "#39403B", "3D1C24", + "#4C0016", "#FFF7EB", "#DCCEA7", "#A17345", "104F53", + "#BF2431", "#F24150", "#2A4557", "#3B848C", "EFF2E4", + "#3B3013", "#8F6031", "#E88833", "#9C0C0A", "FDF3C1", + "#1E2422", "#88BEB1", "#FF006D", "#DAFFFF", "718A94", + "#F1F4F7", "#AF9F7B", "#775E43", "#40413C", "251C17", + "#00182E", "#0C6BA1", "#D4D6D4", "#FFFDEB", "FF7500", + "#FFAB4A", "#CCBAAB", "#1E2129", "#3D5E6E", "47A3A3", + "#66B3A7", "#C0D4B6", "#EEF0BD", "#F0563D", "2C2F3B", + "#332525", "#907465", "#EDC5B5", "#878C6D", "63674A", + "#F04C16", "#DBDBD0", "#EDBD1F", "#4CB09C", "313B4A", + "#2B211D", "#611C26", "#C5003E", "#8EB7A8", "F1E4B7", + "#1A1F2B", "#30395C", "#4A6491", "#85A5CC", "D0E4F2", + "#03497E", "#0596D5", "#9DEBFC", "#999999", "FE4B28", + "#2F4159", "#465E73", "#88A649", "#F2ECE4", "D98841", + "#323A46", "#22282F", "#EB4A33", "#FFFFFF", "E9F0F5", + "#2C3E50", "#FC4349", "#6DBCDB", "#D7DADB", "FFFFFF", + "#F29727", "#E05723", "#B0382F", "#982E4B", "713045", + "#4D584A", "#465943", "#428552", "#3E754E", "4C694B", + "#47191C", "#59574B", "#829690", "#B5B09A", "E1E3CB", + "#1D5123", "#B1C661", "#FFDA68", "#FE9257", "F64448", + "#59323C", "#260126", "#F2EEB3", "#BFAF80", "8C6954", + "#4E0805", "#9E0522", "#FFF4D4", "#B8C591", "447622", + "#424862", "#FB9A63", "#BFC4D5", "#F6FBF4", "FEBC98", + "#FF2468", "#E0D4B1", "#FFFFE3", "#00A5A6", "005B63", + "#1C2F40", "#4C6173", "#8094A6", "#D9D1BA", "F2E9D8", + "#DFD7B7", "#EB7707", "#5C5445", "#3B2323", "9CBFC7", + "#262E3B", "#9C8878", "#CFCAAA", "#FBF8FF", "992435", + "#FFBC67", "#DA727E", "#AC6C82", "#685C79", "455C7B", + "#404A69", "#516C8A", "#8AC0DE", "#FFFFFF", "FFAC00", + "#485B61", "#4B8C74", "#74C476", "#A4E66D", "CFFC83", + "#A31180", "#C42795", "#DE52B4", "#EA88CE", "FFBFE5", + "#E64D2E", "#FFF5F1", "#7893AD", "#576B9C", "2D2A52", + "#BF0436", "#8C0327", "#590219", "#F2CBA1", "8C674C", + "#CF5B6F", "#FFF8C8", "#CAD9B1", "#8FB3A0", "648991", + "#341D44", "#744D90", "#BB8CDD", "#3E4417", "88904D", + "#00293E", "#003D4E", "#006269", "#00918F", "00BAB5", + "#43212E", "#D9666F", "#F2D57E", "#A9A688", "516057", + "#2A3B30", "#ABFFD1", "#EBFFF5", "#9DFEFF", "273B40", + "#A63343", "#E65159", "#F5E9DB", "#F4F7CF", "BAD984", + "#1BA68C", "#54BFAC", "#F2EDA7", "#F2E530", "D94625", + "#1A2A40", "#3F7369", "#F2DEA0", "#CE5251", "EA895E", + "#1E9382", "#70A758", "#EFF1C2", "#F0563D", "2E313D", + "#A991E8", "#FFB4BB", "#ACF7FF", "#A2E891", "FFEDAE", + "#225B66", "#17A3A5", "#8DBF67", "#FCCB5F", "FC6E59", + "#282624", "#BFB7AA", "#403D39", "#807A71", "ABA398", + "#334D5C", "#45B29D", "#EFC94C", "#E27A3F", "DF4949", + "#440008", "#605521", "#988432", "#D9A54E", "9E3711", + "#649670", "#36291E", "#69AD6C", "#92E67C", "C5FF84", + "#42342C", "#738076", "#B2B39B", "#DFE5E1", "294359", + "#1A3838", "#3F7A51", "#82A352", "#D1C062", "FFBE59", + "#7D8C22", "#B3BF67", "#F2E49B", "#D9DFF4", "6791BF", + "#8A7D6D", "#2D2D38", "#E86E48", "#FFFFE8", "9CC9C9", + "#CFC949", "#FFF5BF", "#A9E6C4", "#6AB39F", "665841", + "#A1172D", "#FDFFBA", "#A7DB9E", "#275C57", "1F1B19", + "#FF6C0D", "#F29E00", "#E6C10F", "#44996F", "216273", + "#2C3E50", "#FA4248", "#D7DADB", "#6DBCDB", "FFFFFF", + "#627369", "#99B397", "#E2F2C6", "#91CCAD", "376266", + "#04496E", "#66CAFF", "#A3FC7E", "#70D44A", "2C6B0F", + "#1BA68C", "#97BF3F", "#F2ECD8", "#F2B035", "F2522E", + "#A2D9B1", "#7CBF9E", "#F2F1B9", "#8C8575", "193741", + "#024959", "#037E8C", "#F2EFDC", "#E74C30", "363636", + "#212625", "#9CA6A2", "#D0D9D6", "#BF0404", "C2C6AF", + "#00FFFF", "#00FF00", "#FFFF00", "#FF5100", "FF007C", + "#212629", "#CDCF19", "#FFF77D", "#96C4AB", "CF2A56", + "#CFF9FF", "#BFC7BB", "#787051", "#332730", "57324F", + "#98CACB", "#FDEFBE", "#F0542B", "#736E5B", "ABA68E", + "#F2F1EB", "#BFB9A4", "#262222", "#802A30", "8C0303", + "#65356B", "#AB434F", "#C76347", "#FFA24C", "519183", + "#78BF82", "#A4D17C", "#CFD96C", "#EBD464", "FFD970", + "#806265", "#FFA256", "#F7DD77", "#E0D054", "ABA73C", + "#8F323C", "#123943", "#80BDDB", "#4189AB", "C98127", + "#683820", "#8C9A89", "#E7D6A2", "#BEAA65", "9A8234", + "#021B21", "#032C36", "#065F73", "#E8DFD6", "FF2A1D", + "#2D6C73", "#3FA693", "#B4D9CB", "#9ABF49", "C6D93B", + "#141F26", "#2B4040", "#405950", "#A69E86", "F2D9BB", + "#4A8279", "#003330", "#610400", "#003B06", "02730F", + "#69B5E1", "#D4E4F5", "#EAF2F8", "#BEDBED", "000000", + "#893660", "#EF7261", "#68D693", "#A0D7E2", "299CA8", + "#073A59", "#2D9AA6", "#F2E2DC", "#F23322", "A61B1B", + "#2A3A48", "#3E6372", "#B2D4DC", "#FAFAFF", "FF6900", + "#F3BD8D", "#F1A280", "#BE6D6B", "#704A5B", "3E263C", + "#1C2742", "#3C91C7", "#5A9ABE", "#95C5DE", "E0EEFB", + "#426261", "#465A59", "#577573", "#739A97", "9AC1C0", + "#002A4A", "#17607D", "#FFF1CE", "#FF9311", "D64700", + "#589373", "#BFBD99", "#F2D6B3", "#C2512F", "241E1E", + "#1F518B", "#1488C8", "#F7E041", "#E2413E", "B5292A", + "#549494", "#E85649", "#232C2E", "#E6E8D2", "706558", + "#392133", "#FFECBE", "#D9D098", "#C4AB6D", "AB7D3A", + "#F0F0F0", "#1C1C1C", "#A2FDF5", "#1CCDC7", "27EDDF", + "#011526", "#025959", "#027353", "#03A678", "03A696", + "#004358", "#1F8A70", "#BEDB39", "#FFE11A", "FD7400", + "#37465D", "#F2F2F2", "#9DC02E", "#779324", "051A37", + "#580022", "#AA2C30", "#FFBE8D", "#487B80", "011D24", + "#F9F9F9", "#03A678", "#E9EDEB", "#F44647", "00707F", + "#800000", "#BF0000", "#E2D6C2", "#F6EDD8", "FFFFFF", + "#F7F6AF", "#1B2124", "#D62822", "#97D6A6", "468263", + "#432852", "#992255", "#FF3D4C", "#28656E", "00968F", + "#444344", "#52BBB2", "#2B344D", "#EE5555", "F8F7EE", + "#45334A", "#796B7D", "#CCC4B0", "#FFF1B5", "FFA3A3", + "#5A4B53", "#9C3C58", "#DE2B5B", "#D86A41", "D2A825", + "#14151C", "#0C242B", "#297059", "#84D66E", "D1FB7A", + "#272D40", "#364659", "#55736D", "#9DBF8E", "D0D991", + "#23A38F", "#B7C11E", "#EFF1C2", "#F0563D", "2E313D", + "#2E064D", "#80176B", "#B356A1", "#59580B", "FFFF00", + "#CC3333", "#FF9D33", "#F7F7F0", "#3EBBA7", "00747A", + "#5C4B51", "#8CBEB2", "#F2EBBF", "#F3B562", "BD6060", + "#0D3E58", "#1C848C", "#19C0C2", "#F3EDD6", "DA6260", + "#022629", "#2A5945", "#FAFFED", "#E6DCC0", "B3371C", + "#F4FAC7", "#7BAD8D", "#FFB159", "#F77F45", "C2454E", + "#A2C1C6", "#86B1B7", "#AECBAD", "#CFDCB0", "D6E1D1", + "#B0DAFF", "#325B80", "#64B7FF", "#586D80", "5092CC", + "#0F808C", "#6C8C26", "#F2A71B", "#F26A1B", "D91818", + "#FFBC6C", "#FE9F6C", "#BD716E", "#74495F", "3B2C4D", + "#FF4D41", "#F2931F", "#E6CA21", "#91B321", "1E8C65", + "#302821", "#453629", "#5C4837", "#8A735F", "BDA895", + "#415457", "#5F7B7F", "#9ACCAF", "#E6EBC4", "F9F7C8", + "#474143", "#A69E9D", "#E7E2DA", "#FFFFFF", "E7E8E7", + "#805939", "#BD9962", "#E6CD7D", "#578072", "2D4B4D", + "#03588C", "#1763A6", "#419CA6", "#54BF83", "8DBF41", + "#00CCFF", "#A1FCFF", "#040438", "#004878", "C9FAFF", + "#534C64", "#B7DECF", "#F0F3D7", "#7E858C", "D96557", + "#7F7364", "#CBB08E", "#CBC1B7", "#789DCB", "646F7F", + "#5C2849", "#A73E5C", "#EC7263", "#FE9551", "FFD285", + "#FF0012", "#FF7D00", "#FFD900", "#5BE300", "0084B0", + "#F24C32", "#F29471", "#FCDFA6", "#36B898", "3D7585", + "#083157", "#0A6C87", "#459C97", "#92CCA5", "C9F0B1", + "#DC941B", "#EDC266", "#B6952C", "#E1D3A6", "E9A119", + "#323836", "#BAD1B5", "#DBE8CF", "#F0F7E8", "FFFEF5", + "#081724", "#589494", "#8EBBB4", "#D0DCD0", "F5EED2", + "#50781C", "#9CAD1C", "#EAF7E6", "#40A5DE", "0B5191", + "#537F79", "#78A68F", "#CBD49C", "#FED457", "CB252A", + "#F23C13", "#CBAB78", "#FFFFFF", "#898373", "1F1C17", + "#450003", "#5C0002", "#94090D", "#D40D12", "FFED75", + "#0770A2", "#82D9F7", "#FEFEFE", "#AEC844", "F36622", + "#30394F", "#FF434C", "#6ACEEB", "#EDE8DF", "0E6569", + "#FF6B6B", "#556270", "#C7F464", "#4ECDC4", "EDC8BB", + "#D9B500", "#FFED9C", "#BFCC85", "#748F74", "454545", + "#452E32", "#A34B1B", "#B5A187", "#EDDF9A", "A7CC31", + "#2C2B33", "#596664", "#909980", "#CCC08D", "FF8A00", + "#C21F1F", "#FFFFFC", "#E34446", "#FFFFDB", "E36D6F", + "#282828", "#00AAB5", "#C1C923", "#F41C54", "F5F0F0", + "#3A3F40", "#202627", "#151B1E", "#EFF4FF", "41444D", + "#DEBB73", "#4D0017", "#010000", "#4D0F30", "9A002F", + "#EB9328", "#FFA754", "#FFD699", "#FFF5DC", "4FA6B3", + "#025E73", "#037F8C", "#D9D59A", "#D9BD6A", "590202", + "#636266", "#E0CEA4", "#E8A579", "#7D6855", "42403E", + "#FF0000", "#FF4000", "#FF7F00", "#FFBF00", "FFFF00", + "#FFFFFF", "#74ADA6", "#1E5E6F", "#241B1F", "68A81E", + "#5A0532", "#FF6745", "#FFC861", "#9DAE64", "27404A", + "#ACCBBC", "#467847", "#E8E4C1", "#A60303", "730202", + "#5C4B51", "#8CBEB2", "#F2EBBF", "#F3B562", "F06060", + "#0D2557", "#93A8C9", "#FFFFFF", "#F5DED5", "558D96", + "#F53C4A", "#565157", "#10CFC8", "#F2EEE7", "F5D662", + "#FFD97B", "#E65029", "#A60027", "#660033", "191C26", + "#595408", "#A6800D", "#A66D03", "#A63F03", "730C02", + "#2E031F", "#590424", "#8C072B", "#BF0A2B", "DEEFC5", + "#E0C882", "#A6874E", "#BFA169", "#D9B779", "F2D399", + "#D88681", "#A67673", "#746566", "#535A5D", "324F54", + "#FC297D", "#FB607A", "#FDA286", "#FDC188", "FEE78A", + "#FFECA1", "#B3B27F", "#4C5E52", "#2F3436", "FFBE2C", + "#D93312", "#B3AB82", "#45735F", "#394D47", "2C3233", + "#324143", "#6595A3", "#C8E3E8", "#FCFFED", "B6C28B", + "#477984", "#FEF5EB", "#C03C44", "#EEAA4D", "313E4A", + "#334D5C", "#45B29D", "#EFC94C", "#E27A3F", "DF4949", + "#630B11", "#33322F", "#2A2927", "#1E1D1C", "000000", + "#D94214", "#FFF2C1", "#80A894", "#52736B", "093844", + "#051E21", "#00302D", "#856434", "#F28C28", "FFAD4E", + "#D7DADD", "#DDDEE3", "#E1E1E9", "#EDEFF4", "F2F3F8", + "#BF495E", "#41A693", "#F2EC9B", "#D9CF48", "D9583B", + "#067072", "#14A589", "#DECFA6", "#BAAE8C", "F94B06", + "#423A38", "#47B8C8", "#E7EEE2", "#BDB9B1", "D7503E", + "#730324", "#DFE3E6", "#B4C4D4", "#8BA2BD", "456382", + "#537374", "#F9BD7F", "#EBD7A5", "#ADC9A5", "5C9E99", + "#88B59E", "#B6DEC8", "#39464D", "#C04229", "ABD1AB", + "#11A7FC", "#95D127", "#F2E415", "#FF8638", "EE3551", + "#212640", "#5D718C", "#4B95A6", "#60BFBF", "EFF2D8", + "#D8A64D", "#9B5422", "#351411", "#5B0D0D", "991C11", + "#53324F", "#668D6E", "#F2E0A0", "#F19B7A", "F0756E", + "#DFE0AF", "#A4BAA2", "#569492", "#41505E", "383245", + "#7BBADE", "#93DE7F", "#FFED5D", "#F29E4A", "FF7050", + "#133800", "#1B4F1B", "#398133", "#5C9548", "93E036", + "#D9D7AD", "#91A685", "#FF6A00", "#37485C", "1C232E", + "#008767", "#FFB27A", "#FF6145", "#AB2141", "5E1638", + "#727B7F", "#CCEAEA", "#7A7556", "#2E2125", "44CACC", + "#FFFFED", "#FF2C38", "#FF9A3A", "#FFF040", "67D9FF", + "#007148", "#60A859", "#9BDA6A", "#C7F774", "F9FFEF", + "#092740", "#45698B", "#90B0CC", "#F1FAFF", "8FD36F", + "#E2FFA0", "#7D8076", "#FAFFED", "#C2CCBE", "8F7D70", + "#00736A", "#00BC9F", "#F1EEC7", "#FEA301", "F2561A", + "#26282E", "#AD5138", "#F7F7F7", "#DDDAE0", "8594AE", + "#1A191F", "#35352F", "#484042", "#4E5252", "E64D38", + "#49454A", "#E69B02", "#FAF4C6", "#B1D631", "324052", + "#5F1A2B", "#1D2834", "#6F8B78", "#E4D49E", "C96466", + "#012D3D", "#38AD9E", "#FFEB9E", "#FF6867", "D0DBED", + "#0D1F36", "#104954", "#1E9C89", "#38CC85", "60EB7B", + "#8C4E03", "#9FA66A", "#F2EC94", "#F23005", "8B0F03", + "#000001", "#20201F", "#E2E2E4", "#590402", "B80000", + "#344059", "#465973", "#F2D272", "#A69460", "595139", + "#33454C", "#608F85", "#B6E5CB", "#8BAF95", "54584E", + "#FBFEF6", "#B7BFA4", "#687F70", "#1A3841", "BF3847", + "#D7E836", "#86FFC7", "#FFB048", "#E8366C", "593BFF", + "#34A9FF", "#5982DB", "#665EB8", "#684682", "632E62", + "#004056", "#2C858D", "#74CEB7", "#C9FFD5", "FFFFCB", + "#BFB978", "#84945C", "#516967", "#4D3130", "281B24", + "#103B73", "#20638C", "#3786A6", "#4EABBF", "EBEFF2", + "#9FB1BF", "#1D2D63", "#1C5357", "#1F6E56", "196331", + "#FFEBBA", "#C3BD91", "#88947B", "#4C3F3F", "2A2727", + "#347373", "#4EA6A6", "#91D9D9", "#FFFFFD", "F2E205", + "#828948", "#EFDFC2", "#006971", "#DC533E", "840000", + "#000137", "#29003F", "#79003D", "#D04D14", "F89801", + "#370005", "#4B0005", "#5F0005", "#730005", "870005", + "#C3AE8D", "#F25260", "#2D5F73", "#6BADC9", "8FCED6", + "#9E1B36", "#F7EDBA", "#E69B3D", "#EB3355", "3D241D", + "#1D8281", "#44BF87", "#FBD258", "#F29A3F", "DB634F", + "#035C75", "#1B808C", "#31A6A8", "#F3F1BC", "F3AD14", + "#FF7500", "#665130", "#EBB643", "#CEDAA8", "668E84", + "#384D3A", "#3E6653", "#728053", "#A68357", "D97C71", + "#012326", "#17455C", "#E1CAAB", "#FE8333", "FA4913", + "#1A2944", "#2DA7C7", "#56ACBA", "#98C4C9", "CBD5D2", + "#BF3542", "#CDC5BA", "#EBE3D6", "#3C3C3C", "2E2E2E", + "#231921", "#695F74", "#BEB4CB", "#EBEBF0", "D2DCEB", + "#34373F", "#686C75", "#F3E9D0", "#BEB7A7", "8E867C", + "#661510", "#D9351A", "#F2C76F", "#BF9727", "204D3F", + "#3CFFEE", "#24AABC", "#356781", "#2C3D51", "1C1F24", + "#DA3537", "#FFFCC4", "#00585F", "#6A6A61", "2A2C2B", + "#AE3135", "#D1AF87", "#8C826B", "#3D3C33", "F2F0CE", + "#FF0894", "#FF5E9F", "#FF91A7", "#FFB5CA", "F5F0BA", + "#99878D", "#323232", "#646464", "#7E4A5C", "372129", + "#3FB8AF", "#7FC7AF", "#DAD8A7", "#FFB38B", "FF3F34", + "#402B3C", "#6AA6A6", "#D9CCA7", "#F2B263", "F26835", + "#6AA690", "#F2BC1B", "#F2DC99", "#F29057", "BF1F1F", + "#F4FAC7", "#7BAD8D", "#FFB158", "#F77F45", "C2454E", + "#E5533C", "#F5E346", "#93D06D", "#50AC6A", "227864", + "#39588A", "#A9BDD7", "#FFFFFF", "#FFEADD", "FFD0BB", + "#B0B595", "#615F4F", "#828567", "#91A380", "EAFFCD", + "#00427F", "#0066BD", "#66B5CC", "#F0E4C5", "D6C28F", + "#FF6313", "#F9E4B3", "#C29689", "#74474B", "45232E", + "#00585F", "#009393", "#FFFCC4", "#C7C49B", "EB0A00", + "#091840", "#44A2FF", "#F7F7EA", "#B3CC63", "4C6620", + "#5CBBE3", "#FCF1BC", "#5C8182", "#383A47", "B4F257", + "#9E9E9E", "#E5E1D1", "#E0393D", "#253746", "425563", + "#4D9453", "#FFFFB1", "#ADDE4E", "#FF9D27", "A62A16", + "#B70046", "#FF850B", "#FFEBC5", "#109679", "675A4C", + "#363636", "#0599B0", "#A4BD0A", "#FFA615", "FF2E00", + "#7D8077", "#BBBFB2", "#FAFFED", "#E82A33", "E3DEBC", + "#FD9F44", "#FC5C65", "#007269", "#03A679", "FAF0B9", + "#134B57", "#81A489", "#F1D8B5", "#F2A054", "C04D31", + "#946E49", "#394042", "#EDDBAC", "#872A0C", "BA8E3A", + "#404040", "#024959", "#037E8C", "#FFFFFF", "F24C27", + "#2A3342", "#163C6E", "#4E5F61", "#E6A015", "EDE7BE", + "#445060", "#829AB5", "#849E91", "#C14543", "D6D5D1", + "#8A9126", "#B7BF5E", "#FFE9C4", "#F5B776", "F58E45", + "#9B2D1E", "#3C3A28", "#78A080", "#9BCD9E", "FFFFAE", + "#FF6138", "#FFFF9D", "#BEEB9F", "#79BD8F", "00A388", + "#990000", "#FF6600", "#FF9900", "#996633", "CC9966", + "#DCE6DA", "#B8CCBB", "#98B3A5", "#7A9994", "62858C", + "#0B1C29", "#3B7C8F", "#73A5A3", "#98C1B7", "F0EBD2", + "#F6CB51", "#E25942", "#13A89E", "#3F4953", "F2E7DA", + "#282F36", "#FFFEFC", "#BDA21D", "#BFBC5B", "D2E098", + "#8C182D", "#DE7140", "#FCB95A", "#FAE285", "6A7349", + "#6B9100", "#FFE433", "#FF841F", "#E03D19", "A6001C", + "#FFEAA7", "#D9D697", "#9FC49F", "#718C6A", "543122", + "#CFF09E", "#A8DBA8", "#79BD9A", "#3B8686", "0B486B", + "#0C2233", "#065471", "#0A91AB", "#FFC045", "F2F2F2", + "#BEE8E0", "#373C40", "#2E2621", "#73320B", "FF5E00", + "#1B2C35", "#A3BFC6", "#FF005D", "#222A30", "293A42", + "#FF8400", "#3B4044", "#494948", "#E6E1D8", "F7F2E9", + "#6A482D", "#518C86", "#F6BF3D", "#EF7C27", "BF2424", + "#261C2B", "#292B39", "#226468", "#608D80", "829D8F", + "#B2AD9A", "#110E00", "#363226", "#A9A695", "ECE9D8", + "#1B1B26", "#26394D", "#286480", "#13B3BF", "A3FF57", + "#F2C2A7", "#F5E5C5", "#593D28", "#422C21", "93DEDB", + "#001028", "#033140", "#1E5A5B", "#7BA78C", "EBEDC6", + "#544E6E", "#808CB0", "#ABD1D9", "#D9FFF7", "DDF556", + "#323A45", "#596677", "#758194", "#FFFFFF", "E74C3C", + "#45291A", "#AB926D", "#DBD1BC", "#4999C3", "5FCBEC", + "#6B151D", "#2E1615", "#A8553A", "#DB8F5A", "F2C18E", + "#000623", "#28475C", "#4A6C74", "#8BA693", "F0E3B1", + "#60807B", "#81B37A", "#BCCC5F", "#FFEE65", "E64964", + "#FFFFFA", "#A1A194", "#5B605F", "#464646", "FF6600", + "#1E1B17", "#577270", "#9C9A79", "#C7BDA1", "580E0C", + "#452F27", "#5E504A", "#6B6865", "#9BBAB2", "B0FFED", + "#1B5257", "#F7F6C3", "#F28159", "#CC5850", "4F1C2E", + "#FAA51B", "#BF511F", "#2C445E", "#2F6D82", "5EE4EB", + "#BF3952", "#59364A", "#556D73", "#D9D1A9", "D95F5F", + "#024959", "#037E8C", "#F2EFDC", "#E74C30", "363636", + "#221A26", "#544759", "#A197A6", "#F27405", "D93D04", + "#C4A44A", "#E6D399", "#9AB8A9", "#7C8A7F", "4E4B44", + "#FFFEC8", "#B1BF99", "#5B604D", "#39382B", "26181E", + "#4E3C51", "#21A68D", "#3BBF9A", "#F2E8B6", "F25749", + "#102144", "#1B325E", "#254580", "#3C63B0", "5D8AEA", + "#2A3A48", "#3E6372", "#B2D4DC", "#FAFAFF", "FF4B00", + "#FFF1BF", "#F20058", "#FFAEAC", "#000001", "7D7A96", + "#FDFFC6", "#F2F096", "#FF0080", "#DE0049", "521218", + "#5B0E00", "#FBB500", "#FBD864", "#807D1A", "59233C", + "#1E1E1F", "#424143", "#67666A", "#807F83", "CBC9CF", + "#3C3658", "#3EC8B7", "#7CD0B4", "#B9D8B1", "F7E0AE", + "#FFFFFF", "#99B75F", "#D5DD98", "#EBF4DB", "D8D8D8", + "#248A8A", "#C9FA58", "#F9E555", "#FAAC38", "F2572A", + "#086B63", "#77A490", "#E2D8C1", "#BFAE95", "7C7159", + "#5C4B51", "#8CBEB2", "#F2EBBF", "#A5C88F", "EF847B", + "#17162F", "#89346D", "#C76058", "#FFB248", "E8C475", + "#6E8F4A", "#65D9C5", "#F2E7B6", "#EDA430", "AB3E2C", + "#30394F", "#FF434C", "#6ACEEB", "#EDE8DF", "0E6569", + "#8E1B13", "#F9E4B3", "#849689", "#46464A", "29232E", + "#686B30", "#AB9A52", "#E8BA67", "#D68F4F", "BA512E", + "#E54E45", "#DBC390", "#F2F2EF", "#13A3A5", "403833", + "#65BA99", "#59A386", "#F1DDBB", "#D6C4A6", "E74C3C", + "#A6FFBC", "#4ACFAF", "#00A995", "#006161", "003D4C", + "#33271E", "#8B7653", "#C8D9A0", "#FDEE9D", "233331", + "#048789", "#503D2E", "#D44D27", "#E2A72E", "EFEBC8", + "#E5FF1E", "#A9D943", "#75A660", "#698070", "494D4B", + "#2DEBA2", "#91F57F", "#EBAA69", "#E70049", "2B0027", + "#990000", "#336699", "#DDDDDD", "#999999", "333333", + "#F13A4B", "#3D3C3E", "#22BDAF", "#F4F4F4", "D7D7D7", + "#F53A59", "#001D2D", "#15A88C", "#B7D9C8", "F3F5F4",]; + + +}; + +Ops.Color.ColorPalettes.prototype = new CABLES.Op(); +CABLES.OPS["31d33a1e-9a0a-49f7-8bc8-9e83ab71e23e"]={f:Ops.Color.ColorPalettes,objName:"Ops.Color.ColorPalettes"}; + + + + +// ************************************************************** +// +// Ops.Color.HSBtoRGB +// +// ************************************************************** + +Ops.Color.HSBtoRGB = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inH = op.inValueSlider("Hue"), + inS = op.inValueSlider("Saturation", 1), + inV = op.inValueSlider("Brightness", 0.5), + outR = op.outNumber("R"), + outG = op.outNumber("G"), + outB = op.outNumber("B"); + +inH.onChange = inS.onChange = inV.onChange = update; +update(); + +function update() +{ + let hue = (inH.get()); + let saturation = (inS.get()); + let lightness = (inV.get()); + + // based on algorithm from http://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB + + let chroma = (1 - Math.abs((2 * lightness) - 1)) * saturation; + let huePrime = hue * 6; // / 60; + let secondComponent = chroma * (1 - Math.abs((huePrime % 2) - 1)); + + huePrime = Math.floor(huePrime) || 0; + let red = 0; + let green = 0; + let blue = 0; + + if (huePrime === 0) + { + red = chroma; + green = secondComponent; + blue = 0; + } + else if (huePrime === 1) + { + red = secondComponent; + green = chroma; + blue = 0; + } + else if (huePrime === 2) + { + red = 0; + green = chroma; + blue = secondComponent; + } + else if (huePrime === 3) + { + red = 0; + green = secondComponent; + blue = chroma; + } + else if (huePrime === 4) + { + red = secondComponent; + green = 0; + blue = chroma; + } + else if (huePrime >= 5) + { + red = chroma; + green = 0; + blue = secondComponent; + } + let lightnessAdjustment = (lightness - (chroma / 2)); + red += lightnessAdjustment; + green += lightnessAdjustment; + blue += lightnessAdjustment; + + outR.set(red); + outG.set(green); + outB.set(blue); + + // return [Math.round(red * 255), Math.round(green * 255), Math.round(blue * 255)]; +} + + +}; + +Ops.Color.HSBtoRGB.prototype = new CABLES.Op(); +CABLES.OPS["909ee871-b0f3-477f-bee2-d0ab40bb5804"]={f:Ops.Color.HSBtoRGB,objName:"Ops.Color.HSBtoRGB"}; + + + + +// ************************************************************** +// +// Ops.Color.HexToRGB_v2 +// +// ************************************************************** + +Ops.Color.HexToRGB_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + hex = op.inString("Hex", "#ff0000"), + asBytes = op.inValueBool("Bytes"), + outR = op.outNumber("R"), + outG = op.outNumber("G"), + outB = op.outNumber("B"); + +function hexToR(h) +{ + return parseInt((cutHex(h)).substring(0, 2), 16) || 0; +} +function hexToG(h) +{ + return parseInt((cutHex(h)).substring(2, 4), 16) || 0; +} +function hexToB(h) +{ + return parseInt((cutHex(h)).substring(4, 6), 16) || 0; +} +function cutHex(h) +{ + return (h.charAt(0) == "#") ? h.substring(1, 7) : h; +} + +hex.onChange = parse; +asBytes.onChange = parse; + +function parse() +{ + let str = hex.get() || ""; + let r = hexToR(str); + let g = hexToG(str); + let b = hexToB(str); + + if (!asBytes.get()) + { + r /= 255; + g /= 255; + b /= 255; + } + + outR.set(r); + outB.set(b); + outG.set(g); +} + + +}; + +Ops.Color.HexToRGB_v2.prototype = new CABLES.Op(); +CABLES.OPS["9877f198-8dac-48e5-9310-244ef1a8dec5"]={f:Ops.Color.HexToRGB_v2,objName:"Ops.Color.HexToRGB_v2"}; + + + + +// ************************************************************** +// +// Ops.Color.RGBtoHSB +// +// ************************************************************** + +Ops.Color.RGBtoHSB = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inR = op.inValueSlider("R", 0), + inG = op.inValueSlider("G", 0), + inB = op.inValueSlider("B", 0), + outH = op.outNumber("Hue"), + outS = op.outNumber("Saturation"), + outB = op.outNumber("Brightness"); + +inR.onChange = inG.onChange = inB.onChange = update; + +/** + * Converts an RGB color value to HSV. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSV_color_space. + * Assumes r, g, and b are contained in the set [0, 255] and + * returns h, s, and v in the set [0, 1]. + * + * @param Number r The red color value + * @param Number g The green color value + * @param Number b The blue color value + * @return Array The HSV representation + */ + +function update() +{ + let r = (inR.get()); + let g = (inG.get()); + let b = (inB.get()); + + // public static float[] RGBtoHSB(var r, var g, var b, float[] hsbvals) { + let hue, saturation, brightness; + // if (hsbvals == null) { + // hsbvals = []; + // } + let cmax = (r > g) ? r : g; + if (b > cmax) cmax = b; + let cmin = (r < g) ? r : g; + if (b < cmin) cmin = b; + + brightness = (cmax); + if (cmax != 0) + saturation = ((cmax - cmin)) / (cmax); + else + saturation = 0; + if (saturation == 0) + hue = 0; + else + { + let redc = ((cmax - r)) / ((cmax - cmin)); + let greenc = ((cmax - g)) / ((cmax - cmin)); + let bluec = ((cmax - b)) / ((cmax - cmin)); + if (r == cmax) + hue = bluec - greenc; + else if (g == cmax) + hue = 2.0 + redc - bluec; + else + hue = 4.0 + greenc - redc; + hue /= 6.0; + if (hue < 0) + hue += 1.0; + } + + // hsbvals[0] = hue; + // hsbvals[1] = saturation; + // hsbvals[2] = brightness; + // return hsbvals; + // 942 } + // var max = Math.max(r, g, b), min = Math.min(r, g, b); + // var h, s, v = max; + + // var d = max - min; + // s = max == 0 ? 0 : d / max; + + // if (max == min) { + // h = 0; // achromatic + // } else { + // switch (max) { + // case r: h = (g - b) / d + (g < b ? 6 : 0); break; + // case g: h = (b - r) / d + 2; break; + // case b: h = (r - g) / d + 4; break; + // } + + // h /= 6; + // } + + outH.set(hue); + outS.set(saturation); + outB.set(brightness / 2.0); +} + +update(); + + +}; + +Ops.Color.RGBtoHSB.prototype = new CABLES.Op(); +CABLES.OPS["22dd3a12-79ff-49ce-abe6-d3b5a3b06ff3"]={f:Ops.Color.RGBtoHSB,objName:"Ops.Color.RGBtoHSB"}; + + + + +// ************************************************************** +// +// Ops.Color.RgbToHex +// +// ************************************************************** + +Ops.Color.RgbToHex = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + result=op.outString("Result"); + +r.setUiAttribs({ colorPick: true }); + +r.onChange= +g.onChange= +b.onChange=()=> +{ + + const red=parseInt(r.get()*255); + const green=parseInt(g.get()*255); + const blue=parseInt(b.get()*255); + + result.set(((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1)); +}; + +}; + +Ops.Color.RgbToHex.prototype = new CABLES.Op(); +CABLES.OPS["c0233dfa-ef4c-4451-b86e-3b2aadc8e9a5"]={f:Ops.Color.RgbToHex,objName:"Ops.Color.RgbToHex"}; + + + + +// ************************************************************** +// +// Ops.Date.DateAndTime +// +// ************************************************************** + +Ops.Date.DateAndTime = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let UPDATE_RATE_DEFAULT = 500; +let UPDATE_RATE_MIN = 50; +let updateRate = UPDATE_RATE_DEFAULT; + +const + outYear = op.outNumber("Year"), + outMonth = op.outNumber("Month"), + outDay = op.outNumber("Day"), + outHours = op.outNumber("Hours"), + outMinutes = op.outNumber("Minutes"), + outSeconds = op.outNumber("Seconds"), + outTimestemp = op.outNumber("Timestamp"), + updateRatePort = op.inInt("Update Rate", UPDATE_RATE_DEFAULT); + +let d = new Date(); + +let timeout = setTimeout(update, UPDATE_RATE_DEFAULT); +update(); + +function update() +{ + d = new Date(); + + outSeconds.set(d.getSeconds()); + outMinutes.set(d.getMinutes()); + outHours.set(d.getHours()); + outDay.set(d.getDate()); + outMonth.set(d.getMonth()); + outYear.set(d.getFullYear()); + + timeout = setTimeout(update, updateRate); + + outTimestemp.set(Date.now()); +} + +updateRatePort.onChange = function () +{ + let newUpdateRate = updateRatePort.get(); + if (newUpdateRate && newUpdateRate >= UPDATE_RATE_MIN) + { + updateRate = newUpdateRate; + } +}; + +op.onDelete = function () +{ + clearTimeout(timeout); +}; + + +}; + +Ops.Date.DateAndTime.prototype = new CABLES.Op(); +CABLES.OPS["beff95ec-7b50-4b6e-80b8-a7e4ab97d8cc"]={f:Ops.Date.DateAndTime,objName:"Ops.Date.DateAndTime"}; + + + + +// ************************************************************** +// +// Ops.Date.DateCalc +// +// ************************************************************** + +Ops.Date.DateCalc = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTimestamp = op.inValue("timestamp"), + inDiff = op.inInt("difference"), + diffType = op.inDropDown("type", ["years", "months", "days", "hours", "minutes", "seconds"]), + inTrigger = op.inTrigger("update"), + + outDate = op.outObject("Date"), + outResult = op.outNumber("Timestamp"); + +inTimestamp.onChange = + inDiff.onChange = + diffType.onChange = + inTrigger.onChange = update; + +function update() +{ + let ts = inTimestamp.get(); + if (!inTimestamp.isLinked()) + { + ts = Date.now(); + } + const diff = inDiff.get(); + const date = new Date(ts); + switch (diffType.get()) + { + case "years": + date.setYear(date.getYear() + diff); + break; + case "months": + date.setMonth(date.getMonth() + diff); + break; + case "days": + date.setDate(date.getDate() + diff); + break; + case "hours": + date.setHours(date.getHours() + diff); + break; + case "minutes": + date.setMinutes(date.getMinutes() + diff); + break; + case "seconds": + date.setSeconds(date.getSeconds() + diff); + break; + + default: + // code + } + outResult.set(date.getTime()); + outDate.set(date); +} + + +}; + +Ops.Date.DateCalc.prototype = new CABLES.Op(); +CABLES.OPS["1420b781-7b09-4102-b1fd-2cb841e57b95"]={f:Ops.Date.DateCalc,objName:"Ops.Date.DateCalc"}; + + + + +// ************************************************************** +// +// Ops.Date.DateDifference +// +// ************************************************************** + +Ops.Date.DateDifference = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + ts1 = op.inValue("Timestamp 1"), + ts2 = op.inValue("Timestamp 2"), + stopAtZero = op.inValueBool("Stop at 0"), + outYear = op.outNumber("Year"), + outMonth = op.outNumber("Month"), + outDay = op.outNumber("Day"), + outHours = op.outNumber("Hours"), + outMinutes = op.outNumber("Minutes"), + outSeconds = op.outNumber("Seconds"), + outMilliSeconds = op.outNumber("Milliseconds"), + outDiff = op.outNumber("Diff"); + +ts1.onChange = update; +ts2.onChange = update; + +function update() +{ + let d = new Date(ts1.get() - ts2.get()); + outDiff.set(d.getTime()); + if (stopAtZero.get()) + { + if (d.getTime() < 0)d = new Date(0); + } + + outMilliSeconds.set(d.getMilliseconds()); + outSeconds.set(d.getSeconds()); + outMinutes.set(d.getMinutes()); + outHours.set(d.getHours() - 1); + outDay.set(d.getDate() - 1); + outMonth.set(d.getMonth()); + outYear.set(d.getFullYear() - 1970); +} + + +}; + +Ops.Date.DateDifference.prototype = new CABLES.Op(); +CABLES.OPS["34cd865c-cc4d-4c58-afb2-626773eb412e"]={f:Ops.Date.DateDifference,objName:"Ops.Date.DateDifference"}; + + + + +// ************************************************************** +// +// Ops.Date.DateFormatter +// +// ************************************************************** + +Ops.Date.DateFormatter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inTimestamp = op.inValue("Timestamp"); +const inDate = op.inObject("Date"); +const inFormat = op.inString("Format", "YYYY-MM-DD"); +const outString = op.outString("StringDate"); + +inTimestamp.onChange = function () +{ + const ts = inTimestamp.get(); + update(new Date(ts)); +}; +inDate.onChange = function () +{ + const date = inDate.get(); + update(date); +}; +inFormat.onChange = function () +{ + update(new Date()); +}; + +function update(date) +{ + const m = moment(date); + const f = inFormat.get(); + outString.set(m.format(f)); +} + + +}; + +Ops.Date.DateFormatter.prototype = new CABLES.Op(); +CABLES.OPS["8933d01f-39ac-428c-a64b-902c534a4fc0"]={f:Ops.Date.DateFormatter,objName:"Ops.Date.DateFormatter"}; + + + + +// ************************************************************** +// +// Ops.Date.DateIsoToTimestamp +// +// ************************************************************** + +Ops.Date.DateIsoToTimestamp = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const date = op.inString("datetime"); +const timestamp = op.outNumber("timestamp"); + +date.onChange = function() { + const parsed = Date.parse(date.get()); + timestamp.set(parsed); +} + +}; + +Ops.Date.DateIsoToTimestamp.prototype = new CABLES.Op(); +CABLES.OPS["cedf9c37-f864-4416-9675-ecc15f07025d"]={f:Ops.Date.DateIsoToTimestamp,objName:"Ops.Date.DateIsoToTimestamp"}; + + + + +// ************************************************************** +// +// Ops.Date.DateTimestamp +// +// ************************************************************** + +Ops.Date.DateTimestamp = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inYear = op.inValueInt("Year"), + inMonth = op.inValueInt("Month"), + inDay = op.inValueInt("Day"), + inHour = op.inValueInt("Hour"), + inMinute = op.inValueInt("Minute"), + outTimestamp = op.outNumber("Timestamp"); + +inYear.onChange = +inMonth.onChange = +inDay.onChange = +inHour.onChange = +inMinute.onChange = setDate; + +function setDate() +{ + const d = new Date(); + + const datum = new Date(Date.UTC( + inYear.get(), + inMonth.get() - 1, + inDay.get(), + inHour.get(), + inMinute.get() + ) + d.getTimezoneOffset() * 60 * 1000); + + outTimestamp.set(datum.getTime()); +} + + +}; + +Ops.Date.DateTimestamp.prototype = new CABLES.Op(); +CABLES.OPS["e86a668b-db87-472e-9484-3fd102ccbf8a"]={f:Ops.Date.DateTimestamp,objName:"Ops.Date.DateTimestamp"}; + + + + +// ************************************************************** +// +// Ops.Debug.Console +// +// ************************************************************** + +Ops.Debug.Console = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + visible = op.inValueBool("visible", true), + inClear = op.inTriggerButton("Clear"), + outEle = op.outObject("Element", null, "element"); + +let eleLog = null; +let canvas = op.patch.cgl.canvas.parentElement; + +let oldLog = console.log; +let oldLogError = console.error; +let oldLogWarn = console.warn; + +console.log = thelog; +console.error = thelog; +console.warn = thelog; + +addElement(); + +op.onDelete = function () +{ + removeElement(); + console.log = oldLog; + console.error = oldLogError; + console.warn = oldLogWarn; +}; + +visible.onChange = function () +{ + if (visible.get()) eleLog.style.display = "block"; + else eleLog.style.display = "none"; +}; + +function addElement() +{ + if (eleLog)removeElement(); + eleLog = document.createElement("div"); + eleLog.style.padding = "0px"; + eleLog.style.position = "absolute"; + eleLog.style.overflow = "scroll"; + if (CABLES.UI) + { + eleLog.style.width = "100%"; + eleLog.style.height = "50%"; + } + else + { + eleLog.style.width = "100vw"; + eleLog.style.height = "50vh"; + } + eleLog.style["background-color"] = "rgba(0,0,0,0.74)"; + eleLog.style["box-sizing"] = "border-box"; + eleLog.style.padding = "5px"; + eleLog.style["z-index"] = "9999"; + eleLog.style.color = "#fff"; + + canvas.appendChild(eleLog); +} + +function removeElement() +{ + canvas.removeChild(eleLog); + eleLog = null; +} + +function thelog() +{ + if (!eleLog)addElement(); + oldLog.apply(console, arguments); + + try + { + let html = ""; + for (let i = 0; i < arguments.length; i++) + { + if (typeof arguments[i] == "object") html += (JSON && JSON.stringify ? JSON.stringify(arguments[i], undefined, 2) : arguments[i]) + " "; + else html += arguments[i] + " "; + } + eleLog.innerHTML += html + ""; + } + catch (e) {} + eleLog.scrollTop = eleLog.scrollHeight; +} + +inClear.onTriggered = () => +{ + eleLog.innerHTML = ""; +}; + + +}; + +Ops.Debug.Console.prototype = new CABLES.Op(); +CABLES.OPS["1e650a0b-672f-4dca-bcf0-5df281a2d31e"]={f:Ops.Debug.Console,objName:"Ops.Debug.Console"}; + + + + +// ************************************************************** +// +// Ops.Debug.ConsoleLog +// +// ************************************************************** + +Ops.Debug.ConsoleLog = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inNumber=op.inFloat("Number",0), + inString=op.inString("String",""); + + +inNumber.onChange=function() +{ + console.log(inNumber.get()); +}; + +inString.onChange=function() +{ + console.log(inString.get()); +}; + +}; + +Ops.Debug.ConsoleLog.prototype = new CABLES.Op(); +CABLES.OPS["545e7225-73b0-4d40-923b-4b39940403a8"]={f:Ops.Debug.ConsoleLog,objName:"Ops.Debug.ConsoleLog"}; + + + + +// ************************************************************** +// +// Ops.Debug.GlLogErrors +// +// ************************************************************** + +Ops.Debug.GlLogErrors = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Exec"), + inLimit = op.inInt("Limit Error Logs Num", 1), + inStop = op.inBool("Stop trigger after limit", false), + inShowHistory = op.inTriggerButton("show gl history"), + next = op.outTrigger("Next"); + +const gl = op.patch.cgl.gl; +const cgl = op.patch.cgl; + +const originals = {}; +let shouldStart = true; +let count = 0; +let errorCount = 0; +let history = []; +let countFrames = 0; + +exec.onLinkChanged = +next.onLinkChanged = () => +{ + end(); + shouldStart = true; +}; + + +let showHistory = false; + +inShowHistory.onTriggered = () => +{ + showHistory = true; +}; + +let glConsts = {}; + +for (let i in op.patch.cgl.gl) +{ + if (i == i.toUpperCase() && typeof op.patch.cgl.gl[i] == "number") + { + glConsts[op.patch.cgl.gl[i]] = i; + } +} + +function showCodeModal(title, code, type) +{ + if (!CABLES.UI || !CABLES.UI.ModalDialog) + { + console.log(title, code); + } + + let html = ""; + html += "

    Code

    "; + html += "" + title + " "; + html += "

    "; + html += "

    "; + + code = code || ""; + code = code.replace(/\/g, ">"); // for > + + html += "
    " + code + "
    "; + + new CABLES.UI.ModalDialog({ + "title": title, + "html": html + }); + + Array.from(document.querySelectorAll("pre code")).forEach(function (block) + { + hljs.highlightBlock(block); + }); +} + +exec.onTriggered = function () +{ + count = 0; + if (shouldStart) start(); + + if (errorCount >= inLimit.get() && inStop.get()) return; + + next.trigger(); + + countFrames++; + shouldStart = false; + + if (showHistory) + { + if (CABLES.UI) showCodeModal("gl history", getHistoryAsString(), "javascript"); + + showHistory = false; + } + history.length = 0; +}; + +function glGetError() +{ + return op.patch.cgl.gl.getError(); + // return originals["getError"].apply(this, arguments); +} + +function getHistoryString(i) +{ + let str = history[i].f + "( "; + for (let j in history[i].a) + { + if (j != 0)str += ", "; + + const typ = typeof history[i].a[j]; + + if (typ == "number" || typ == "boolean") + { + let argStr = history[i].a[j]; + + if (glConsts[history[i].a[j]]) argStr = "gl." + glConsts[history[i].a[j]]; + str += argStr; + } + else if (typ == "string") + { + let argStr = history[i].a[j] + ""; + if (argStr.length > 20)argStr = argStr.substr(0, 20) + "...\""; + argStr = argStr.replace(/(\r\n|\n|\r)/gm, ""); + + str += "\"" + argStr + "\""; + } + else + { + let argStr = ""; + if (history[i].a[j] instanceof Float32Array) argStr = "{Float32Array(" + history[i].a[j].length + ")}"; + else if (history[i].a[j] instanceof Uint16Array) argStr = "{Uint16Array(" + history[i].a[j].length + ")}"; + else if (history[i].a[j] instanceof Uint8Array) argStr = "{Uint8Array(" + history[i].a[j].length + ")}"; + else argStr = JSON.stringify(history[i].a[j]) + ""; + + if (argStr == "{}") argStr = history[i].a[j]; + + if (argStr.length > 20)argStr = argStr.substr(0, 20) + "..."; + str += argStr; + } + } + str += " );"; + return str; +} + +function getHistoryAsString() +{ + let str = ""; + for (let i = 0; i < history.length; i++) + { + str += "gl." + getHistoryString(i) + "\n"; + } + return str; +} + +function profile(func, funcName) +{ + return function () + { + if (errorCount >= inLimit.get()) return; + + count++; + // const start = performance.now(), + let returnVal = func.apply(this, arguments); + + history.push({ "f": funcName, "a": arguments }); + + // op.patch.cgl.gl.getError(); + const error = glGetError(); + + if (error != gl.NO_ERROR) + { + let errStr = ""; + if (error == gl.OUT_OF_MEMORY) errStr = "OUT_OF_MEMORY"; + if (error == gl.INVALID_ENUM) errStr = "INVALID_ENUM"; + if (error == gl.INVALID_OPERATION) errStr = "INVALID_OPERATION"; + if (error == gl.INVALID_FRAMEBUFFER_OPERATION) errStr = "INVALID_FRAMEBUFFER_OPERATION"; + if (error == gl.INVALID_VALUE) errStr = "INVALID_VALUE"; + if (error == gl.CONTEXT_LOST_WEBGL) + { + this.aborted = true; + errStr = "CONTEXT_LOST_WEBGL"; + } + if (error == gl.NO_ERROR) errStr = "NO_ERROR"; + + console.warn("GL ERROR in frame " + countFrames + ", " + count + "th command: ", funcName); + console.log("arguments", arguments); + + console.log("gl error [" + this.canvas.id + "]: ", error, errStr); + op.patch.printTriggerStack(); + console.log((new Error()).stack); + + op.patch.printTriggerStack(); + if (errorCount == 0) + { + for (let i = 0; i < history.length; i++) + { + let str = getHistoryString(i); + console.log("[GL] " + i + ": gl." + str); + } + } + + const error2 = glGetError(); + console.log("err after", error2); + errorCount++; + + if (errorCount == inLimit.get()) console.log("gl Errors stopping after " + inLimit.get() + " errors"); + } + + return returnVal; + }; +} + +function start() +{ + for (const i in gl) + { + if (typeof gl[i] == "function" && i != "getError") + { + originals[i] = gl[i]; + const orig = originals[i]; + + gl[i] = profile(gl[i], "" + i); + } + } +} + +function end() +{ + cgl.debugOneFrame = false; + for (const i in gl) + if (originals[i] && typeof gl[i] == "function") + gl[i] = originals[i]; +} + + +}; + +Ops.Debug.GlLogErrors.prototype = new CABLES.Op(); +CABLES.OPS["26be55ca-23c0-4f22-bdea-0fd4c63b2067"]={f:Ops.Debug.GlLogErrors,objName:"Ops.Debug.GlLogErrors"}; + + + + +// ************************************************************** +// +// Ops.Debug.GlStates +// +// ************************************************************** + +Ops.Debug.GlStates = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +const + exec = op.inTrigger("Update"), + next = op.outTrigger("Next"), + + outError = op.outBoolNum("glGetError"), + + depth = op.outBoolNum("Depthtest"), + depthStack = op.outBoolNum("Stack Depthtest"), + + depthWrite = op.outBoolNum("Depth Writing"), + depthWriteStack = op.outBoolNum("Stack Depth Writing"), + + depthFunc = op.outBoolNum("DepthFunc"), + depthFuncStack = op.outBoolNum("Stack DepthFunc"), + + blend = op.outBoolNum("Blend"), + blendStack = op.outBoolNum("Blend Stack"), + + cull = op.outBoolNum("Cull Mode"), + culling = op.outBoolNum("Face Culling"), + isShadowPass = op.outBool("Is Shadowpass"); + +exec.onTriggered = function () +{ + depth.set(cgl.gl.isEnabled(cgl.gl.DEPTH_TEST)); + depthStack.set(cgl.stateDepthTest()); + + depthWrite.set(cgl.gl.getParameter(cgl.gl.DEPTH_WRITEMASK)); + depthWriteStack.set(cgl.stateDepthWrite()); + + depthFunc.set(depthFuncToString(cgl.gl.getParameter(cgl.gl.DEPTH_FUNC))); + depthFuncStack.set(depthFuncToString(cgl.stateDepthFunc())); + + blend.set(cgl.gl.isEnabled(cgl.gl.BLEND)); + blendStack.set(cgl.stateBlend()); + + culling.set(cgl.gl.isEnabled(cgl.gl.CULL_FACE)); + + cull.set(cullModeToString(cgl.gl.getParameter(cgl.gl.CULL_FACE_MODE))); + + outError.set(errorToString(cgl.gl.getError())); + isShadowPass.set(cgl.frameStore.shadowPass); + + op.log(cgl._textureslots); + next.trigger(); +}; + +function errorToString(e) +{ + if (e == cgl.gl.NO_ERROR) return "NO_ERROR"; + if (e == cgl.gl.OUT_OF_MEMORY) return "OUT_OF_MEMORY"; + if (e == cgl.gl.INVALID_ENUM) return "INVALID_ENUM"; + if (e == cgl.gl.INVALID_OPERATION) return "INVALID_OPERATION"; + if (e == cgl.gl.INVALID_FRAMEBUFFER_OPERATION) return "INVALID_FRAMEBUFFER_OPERATION"; + if (e == cgl.gl.INVALID_VALUE) return "INVALID_VALUE"; + if (e == cgl.gl.CONTEXT_LOST_WEBGL) return "CONTEXT_LOST_WEBGL"; +} + +function cullModeToString(c) +{ + if (c == cgl.gl.FRONT) return "FRONT"; + if (c == cgl.gl.BACK) return "BACK"; + if (c == cgl.gl.FRONT_AND_BACK) return "FRONT_AND_BACK"; +} + +function depthFuncToString(f) +{ + if (f == cgl.gl.NEVER) return "NEVER"; + if (f == cgl.gl.LESS) return "LESS"; + if (f == cgl.gl.EQUAL) return "EQUAL"; + if (f == cgl.gl.LEQUAL) return "LEQUAL"; + if (f == cgl.gl.GREATER) return "GREATER"; + if (f == cgl.gl.NOTEQUAL) return "NOTEQUAL"; + if (f == cgl.gl.GEQUAL) return "GEQUAL"; + if (f == cgl.gl.ALWAYS) return "ALWAYS"; +} + + +}; + +Ops.Debug.GlStates.prototype = new CABLES.Op(); +CABLES.OPS["3e2bc72c-8af4-4fdb-a998-555aaf62cc9d"]={f:Ops.Debug.GlStates,objName:"Ops.Debug.GlStates"}; + + + + +// ************************************************************** +// +// Ops.Debug.ProfileGL +// +// ************************************************************** + +Ops.Debug.ProfileGL = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exec = op.inTrigger("Exec"); +const next = op.outTrigger("Next"); +const dump = op.inTriggerButton("Debug one Frame"); + +const gl = op.patch.cgl.gl; +const cgl = op.patch.cgl; + +const originals = {}; +const counts = {}; +const durations = {}; +let branches = {}; +let dumpFrame = false; + +const started = false; + +// const query = null; +// const ext = gl.getExtension("EXT_disjoint_timer_query_webgl2"); + +exec.onTriggered = function () +{ + if (dumpFrame) + { + start(); + resetStats(); + } + + next.trigger(); + + if (dumpFrame) + { + end(); + const rows = []; + let numGlCalls = 0; + for (const i in originals) + if (counts[i] > 0) + { + rows.push([i, counts[i], durations[i]]); + numGlCalls += counts[i]; + } + + console.table(rows); + + const rowsBranches = []; + for (const i in branches) + { + let count = 0; + for (const j in branches[i].counts) count += branches[i].counts[j]; + + op.log("branch", i, branches[i].counts); + rowsBranches.push([i, count, Math.round(count / numGlCalls * 100) + "%"]); + } + console.table(rowsBranches); + + op.log(CABLES.profilerBranchesTimes); + + resetStats(); + dumpFrame = false; + } +}; + +function profile(func, funcName) +{ + return function () + { + const start = performance.now(), + returnVal = func.apply(this, arguments), + duration = performance.now() - start; + + if (CABLES.profilerBranches && CABLES.profilerBranches.length > 0) + { + let branchName = CABLES.profilerBranches.join(" / "); + if (CABLES.profilerBranches.length == 0)branchName = "_unknown"; + branches[branchName] = branches[branchName] || {}; + branches[branchName].counts = branches[branchName].counts || {}; + branches[branchName].counts[funcName] = branches[branchName].counts[funcName] || 0; + branches[branchName].counts[funcName]++; + } + + durations[funcName] += duration; + counts[funcName]++; + return returnVal; + }; +} + +function resetStats() +{ + branches = {}; + CABLES.profilerBranchesTimes = {}; + + for (const i in originals) + { + durations[i] = 0; + counts[i] = 0; + } +} + +function start() +{ + op.log("-----------------------------"); + cgl.debugOneFrame = true; + for (const i in gl) + { + if (typeof gl[i] == "function") + { + originals[i] = gl[i]; + const orig = originals[i]; + + gl[i] = profile(gl[i], "" + i); + } + } +} + +function end() +{ + cgl.debugOneFrame = false; + for (const i in gl) + if (typeof gl[i] == "function") + gl[i] = originals[i]; +} + +dump.onTriggered = function () +{ + dumpFrame = true; +}; + + +}; + +Ops.Debug.ProfileGL.prototype = new CABLES.Op(); +CABLES.OPS["498a7c04-a1df-4318-8a60-048a7836710e"]={f:Ops.Debug.ProfileGL,objName:"Ops.Debug.ProfileGL"}; + + + + +// ************************************************************** +// +// Ops.Debug.StopWatch +// +// ************************************************************** + +Ops.Debug.StopWatch = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("exec"), + next = op.outTrigger("next"), + timeUsed = op.outNumber("Time used"), + outTImes = op.outArray("Times"); + +let times = []; +times.length = 100; +for (let i = 0; i < times.length; i++) +{ + times[i] = 0; +} + +let count = 0; +outTImes.set(times); + +exec.onTriggered = function () +{ + let start = performance.now(); + next.trigger(); + let end = performance.now(); + + let l = end - start; + times[count] = l; + count++; + if (count >= 100)count = 0; + + timeUsed.set(l); + outTImes.set(null); + outTImes.set(times); +}; + + +}; + +Ops.Debug.StopWatch.prototype = new CABLES.Op(); +CABLES.OPS["5dd205e6-b475-4a3b-9a3b-d242847f4b81"]={f:Ops.Debug.StopWatch,objName:"Ops.Debug.StopWatch"}; + + + + +// ************************************************************** +// +// Ops.Debug.Trace +// +// ************************************************************** + +Ops.Debug.Trace = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +const exePort = op.inTriggerButton("Trace"); + +exePort.onTriggered = console.trace; + + +}; + +Ops.Debug.Trace.prototype = new CABLES.Op(); +CABLES.OPS["1b52e2b7-b3a4-400e-9a97-1b27ce80deed"]={f:Ops.Debug.Trace,objName:"Ops.Debug.Trace"}; + + + + +// ************************************************************** +// +// Ops.Dev.Array.ArraySpreadSheet +// +// ************************************************************** + +Ops.Dev.Array.ArraySpreadSheet = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + spread = op.inArray("Spreadsheet"), + inNumColumns = op.inInt("Num Columns", 3), + outp = op.inSwitch("Format", ["Flat", "Objects", "Arrays"], "Flat"), + result = op.outArray("Array"), + outColNames = op.outArray("Column Names"); + +// spread.hidePort(); +spread.setUiAttribs({ "hidePort": true }); +inNumColumns.setUiAttribs({ "hidePort": true }); +outp.setUiAttribs({ "hidePort": true }); +// inNumColumns.hidePort(); + +outp.onChange = +spread.onChange = update; + +inNumColumns.onChange = updateUi; +updateUi(); + +function updateUi() +{ + spread.setUiAttribs({ + "display": "spreadsheet", + "spread_numColumns": inNumColumns.get() + }); +} + +function update() +{ + result.set(null); + + if (!spread.get()) return; + + outColNames.set(spread.get().colNames); + + if (outp.get() == "Flat") + { + result.set(asFlat()); + } + else if (outp.get() == "Objects" || outp.get() == "Arrays") + { + result.set(asObjectArray(outp.get() == "Objects")); + } +} + +function asFlat() +{ + const data = spread.get(); + data.cells = data.cells || []; + + if (!data.cols) return; + + const arr = []; + arr.length = data.cells.length * data.cols || 1; + let lastRow = 0; + for (let y = 0; y < data.cells.length; y++) + { + if (data.cells[y]) + for (let x = 0; x < data.cells[0].length; x++) + { + let v = data.cells[y][x] || null; + if (CABLES.UTILS.isNumeric(v)) v = parseFloat(v); + if (v !== "" && v !== null) lastRow = y; + + arr[x + (y * data.cols)] = v; + } + } + + let arrSize = (lastRow + 1) * data.cols; + + arr.length = arrSize; + + return arr; +} + +function asObjectArray(objects) +{ + const data = spread.get() || {}; + data.cells = data.cells || []; + + const arr = []; + arr.length = data.cells.length; + let lastRow = 0; + for (let y = 0; y < data.cells.length; y++) + { + let o = []; + if (objects) o = {}; + arr[y] = o; + + if (data.cells[y]) + for (let x = 0; x < data.cols; x++) + { + let v = data.cells[y][x] || null; + if (CABLES.UTILS.isNumeric(v)) v = parseFloat(v); + if (v !== "" && v !== null) lastRow = y; + + if (objects) o[getColName(data, x)] = v; + else o[x] = v; + } + } + arr.length = lastRow + 1; + + return arr; +} + +function getColName(data, c) +{ + if (data && data.colNames && data.colNames.length > c && data.colNames[c]) + { + return data.colNames[c]; + } + + let str = ""; + + while (c >= 0) + { + str = "abcdefghijklmnopqrstuvwxyz"[c % 26] + str; + c = Math.floor(c / 26) - 1; + } + + return str; +} + + +}; + +Ops.Dev.Array.ArraySpreadSheet.prototype = new CABLES.Op(); +CABLES.OPS["e8f8f23c-e7d3-44bf-9f1f-86230f4e533a"]={f:Ops.Dev.Array.ArraySpreadSheet,objName:"Ops.Dev.Array.ArraySpreadSheet"}; + + + + +// ************************************************************** +// +// Ops.Dev.Blueprint +// +// ************************************************************** + +Ops.Dev.Blueprint = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const patchIdIn = op.inString("externalPatchId", ""); +const subPatchIdIn = op.inString("subPatchId", ""); +const activeIn = op.inBool("active", false); +const gotoIn = op.inTriggerButton("Open patch"); +const resolveIn = op.inTriggerButton("Convert to SubPatch"); +const portsData = op.inString("portsData", "{}"); + +const loadingOut = op.outBool("loading", false); +let loadingId = null; +patchIdIn.setUiAttribs({ "hidePort": true, "greyout": true }); +subPatchIdIn.setUiAttribs({ "hidePort": true, "greyout": true }); +portsData.setUiAttribs({ "hidePort": true }); +portsData.setUiAttribs({ "hideParam": true }); + +gotoIn.setUiAttribs({ "greyout": true }); +gotoIn.setUiAttribs({ "hidePort": true }); + +resolveIn.setUiAttribs({ "greyout": true }); +resolveIn.setUiAttribs({ "hidePort": true }); + +let wasPasted = false; + +subPatchIdIn.onChange = () => +{ + if (!activeIn.get()) + { + op.setUiError("fetchOps", null); + } + if (!loadingOut.get() && activeIn.get() && patchIdIn.get() && subPatchIdIn.get()) + { + update(); + } +}; + +if (op.patch.isEditorMode()) +{ + gotoIn.onTriggered = function () + { + if (CABLES && CABLES.sandbox && CABLES.sandbox.getCablesUrl()) + { + window.open(CABLES.sandbox.getCablesUrl() + "/edit/" + patchIdIn.get(), "_blank"); + } + }; + + resolveIn.onTriggered = () => + { + if (CABLES && CABLES.CMD && CABLES.CMD.PATCH) + { + CABLES.CMD.PATCH.convertBlueprintToSubpatch(this); + } + }; + + patchIdIn.onChange = function () + { + if (patchIdIn.get()) + { + gotoIn.setUiAttribs({ "greyout": false }); + if (!activeIn.get()) + { + op.setUiError("fetchOps", null); + } + if (!loadingOut.get() && activeIn.get() && subPatchIdIn.get()) + { + update(); + } + } + else + { + gotoIn.setUiAttribs({ "greyout": true }); + } + }; +} + +const protectedPorts = [patchIdIn.id, subPatchIdIn.id, activeIn.id, portsData.id, loadingOut.id, gotoIn.id, resolveIn.id]; + +if (!activeIn.get()) +{ + op.setUiError("inactive", "blueprint is inactive", 0); +} + +const restorePorts = () => +{ + const oldPorts = getOldPorts(); + const portInKeys = Object.keys(oldPorts.portsIn); + if (op.patch.isEditorMode()) CABLES.UI.undo.pause(); + const newPorts = []; + for (let i = 0; i < portInKeys.length; i++) + { + const oldPortIn = oldPorts.portsIn[portInKeys[i]]; + const newPort = op.addInPort(new CABLES.Port(op, oldPortIn.name, oldPortIn.type)); + if (!wasPasted && Array.isArray(oldPortIn.links)) + { + oldPortIn.links.forEach((link) => + { + let parent = op.patch.getOpById(link.objOut); + if (parent) + { + const newLink = op.patch.link(parent, link.portOut, op, newPort.name); + if (newLink) newLink.ignoreInSerialize = true; + } + else + { + parent = op.patch.ops.find((subOp) => + { + return subOp.storage && + subOp.storage.blueprint && + subOp.storage.blueprint.originalOpId == link.objOut && + op.storage && + op.storage.blueprint && + subOp.storage.blueprint.blueprintOpId == op.storage.blueprint.blueprintOpId; + }); + if (parent) + { + const newLink = op.patch.link(parent, link.portOut, op, newPort.name); + if (newLink) newLink.ignoreInSerialize = true; + } + } + }); + } + if (!newPort.isLinked()) + { + newPort.set(oldPortIn.value); + } + newPort.onLinkChanged = savePortData; + + if (oldPortIn.title) + { + newPort.setUiAttribs({ "title": oldPortIn.title }); + } + newPorts.push(newPort); + } + op.setPortGroup("Blueprint Ports", newPorts); + + const portOutKeys = Object.keys(oldPorts.portsOut); + for (let i = 0; i < portOutKeys.length; i++) + { + const oldPortOut = oldPorts.portsOut[portOutKeys[i]]; + const newPort = op.addOutPort(new CABLES.Port(op, oldPortOut.name, oldPortOut.type)); + if (!wasPasted && Array.isArray(oldPortOut.links)) + { + oldPortOut.links.forEach((link) => + { + let parent = op.patch.getOpById(link.objIn); + if (parent) + { + const newLink = op.patch.link(op, newPort.name, parent, link.portIn); + if (newLink) newLink.ignoreInSerialize = true; + } + else + { + parent = op.patch.ops.find((subOp) => + { + return subOp.storage && + subOp.storage.blueprint && + subOp.storage.blueprint.originalOpId == link.objIn && + op.storage && + op.storage.blueprint && + subOp.storage.blueprint.blueprintOpId == op.storage.blueprint.blueprintOpId; + }); + if (parent) + { + const newLink = op.patch.link(op, newPort.name, parent, link.portIn); + if (newLink) newLink.ignoreInSerialize = true; + } + } + }); + newPort.onLinkChanged = savePortData; + + if (oldPortOut.title) + { + newPort.setUiAttribs({ "title": oldPortOut.title }); + } + } + } + if (op.patch.isEditorMode()) CABLES.UI.undo.resume(); +}; + +activeIn.onChange = () => +{ + if (!loadingOut.get()) + { + if (activeIn.get()) + { + op.setUiError("inactive", null); + update(); + } + else + { + op.setUiError("fetchOps", null); + op.setUiError("inactive", "blueprint is inactive", 0); + removeImportedOps(); + if (wasPasted) wasPasted = false; + } + } +}; + +op.onLoaded = () => +{ + if (op.uiAttribs) + { + wasPasted = op.uiAttribs.pasted; + } + cleanupPorts(); + restorePorts(); +}; + +op.onDelete = removeImportedOps; + +function update() +{ + loadingOut.set(true); + const patch = op.patch; + const patchId = patchIdIn.get(); + const subPatchId = subPatchIdIn.get(); + const blueprintId = patchId + "-" + subPatchId; + + loadingId = op.patch.loading.start("blueprint", blueprintId); + + if (patch.isEditorMode()) + { + const options = { + "blueprintId": blueprintId, + "patchId": patchId, + "subPatchId": subPatchId, + "opId": op.id, + "blueprintSubpatchId": op.uiAttribs.subPatch + }; + + CABLES.sandbox.getBlueprintOps(options, (err, response) => + { + op.setUiError("fetchOps", null); + if (!err) + { + removeImportedOps(); + const blueprintData = {}; + blueprintData.ops = response.data.ops; + blueprintData.settings = op.patch.settings; + deSerializeBlueprint(blueprintData, subPatchId, true); + } + else + { + if (err.code === 403) + { + op.setUiError("fetchOps", "You do not have permission to use this Blueprint"); + } + else if (err.code === 404 && (gui.patchId === patchId)) + { + op.setUiError("fetchOps", "Save the patch and reload before using this Blueprint"); + } + else + { + op.setUiError("fetchOps", "Error fetching Blueprint, code: " + err.code + " (" + err.msg + ")"); + } + } + loadingOut.set(false); + op.patch.loading.finished(loadingId); + if (wasPasted) + { + wasPasted = false; + } + }); + } + else if (CABLES.talkerAPI) + { + // use this to workaround /viewer/ and /p/ not being "isEditorMode" but also not having exported assets + const callbackTalkerApi = (options, next) => + { + if (options.blueprint && options.blueprint.data.blueprintOpId === op.id) + { + const blueprintData = options.blueprint; + blueprintData.settings = op.patch.settings; + blueprintData.ops = blueprintData.data.ops; + deSerializeBlueprint(blueprintData, subPatchId, false); + loadingOut.set(false); + op.patch.loading.finished(loadingId); + if (wasPasted) + { + wasPasted = false; + } + CABLES.talkerAPI.removeEventListener(callbackTalkerApi); + } + }; + CABLES.talkerAPI.addEventListener("blueprint", callbackTalkerApi); + CABLES.talkerAPI.send("sendBlueprint", { "url": "/" + blueprintId + "/" + patchId + "/" + subPatchId + "/" + op.id + "/" + op.uiAttribs.subPatch }); + } + else + { + let exportId = op.id; + if (CABLES.blueprints && CABLES.blueprints[exportId]) + { + const blueprintData = CABLES.blueprints[exportId]; + blueprintData.settings = op.patch.settings; + // for some reason we have to do this in a 0ms timeout to make + // sure nested blueprints are not loaded before this one created all the ops... + setTimeout(() => + { + deSerializeBlueprint(blueprintData, subPatchId, false); + loadingOut.set(false); + op.patch.loading.finished(loadingId); + if (wasPasted) + { + wasPasted = false; + } + }, 0); + } + else + { + const blueprintUrl = op.patch.config.prefixJsPath + op.patch.getJsPath() + exportId + ".json"; + CABLES.ajax( + blueprintUrl, + function (err, data) + { + if (!err) + { + const blueprintData = JSON.parse(data); + blueprintData.settings = op.patch.settings; + deSerializeBlueprint(blueprintData, subPatchId, false); + } + else + { + op.logError("failed to load blueprint from", blueprintUrl, err); + } + loadingOut.set(false); + op.patch.loading.finished(loadingId); + if (wasPasted) + { + wasPasted = false; + } + } + ); + } + } +} + +function deSerializeBlueprint(data, subPatchId, editorMode) +{ + resolveIn.setUiAttribs({ "greyout": true }); + if (Array.isArray(data.ops) && data.ops.length > 0) + { + let listenerId; + const cb = () => + { + const parentSubPatch = op.patch.ops.find((subOp) => + { + return subOp.storage && + subOp.storage.blueprint && + subOp.storage.blueprint.isParentSubPatch && + subOp.storage.blueprint.blueprintOpId == op.id; + } + ); + if (parentSubPatch) + { + op.setUiAttrib({ "extendTitle": parentSubPatch.uiAttribs.title }); + setupPorts(parentSubPatch); + } + op.patch.removeEventListener(listenerId); + if (editorMode) + { + CABLES.UI.undo.resume(); + if (originalSaveState === true) + { + gui.setStateSaved(); + } + resolveIn.setUiAttribs({ "greyout": false }); + } + }; + + let originalSaveState = null; + if (editorMode) + { + originalSaveState = gui.getSavedState(); + CABLES.UI.undo.pause(); + + gui.serverOps.loadProjectDependencies(data, () => + { + listenerId = op.patch.addEventListener("patchLoadEnd", cb); + data.settings = op.patch.settings; + data.namespace = op.patch.namespace; + data.name = op.patch.name; + op.patch.deSerialize(data, false); + const originalSubPatchId = gui.patchView.getCurrentSubPatch(); + gui.patchView.setCurrentSubPatch(originalSubPatchId); + }); + } + else + { + listenerId = op.patch.addEventListener("patchLoadEnd", cb); + op.patch.deSerialize(data, false); + } + } +} + +function removeImportedOps() +{ + const parentSubPatch = op.patch.ops.find((subOp) => + { + return subOp.storage && + subOp.storage.blueprint && + subOp.storage.blueprint.isParentSubPatch && + subOp.storage.blueprint.blueprintOpId == op.id; + } + ); + if (parentSubPatch) + { + if (op.patch.isEditorMode()) CABLES.UI.undo.pause(); + op.patch.deleteOp(parentSubPatch.id, parentSubPatch.storage.blueprint.blueprintOpId); + if (op.patch.isEditorMode()) CABLES.UI.undo.resume(); + } +} + +const getOldPorts = () => +{ + const oldPorts = JSON.parse(portsData.get()); + let oldPortsOut = {}; + if (oldPorts.portsOut) + { + oldPortsOut = oldPorts.portsOut; + } + let oldPortsIn = {}; + if (oldPorts.portsIn) + { + oldPortsIn = oldPorts.portsIn; + } + return { "portsIn": oldPortsIn, "portsOut": oldPortsOut }; +}; + +const cleanupPorts = () => +{ + const removeInPorts = []; + const removeOutPorts = []; + + op.portsIn.forEach((port) => + { + if (!protectedPorts.includes(port.id)) + { + removeInPorts.push(port); + } + }); + op.portsOut.forEach((port) => + { + if (!protectedPorts.includes(port.id)) + { + removeOutPorts.push(port); + } + }); + + removeInPorts.forEach((port) => + { + removeInPort(port); + op.emitEvent("onPortRemoved", {}); + }); + + removeOutPorts.forEach((port) => + { + removeOutPort(port); + op.emitEvent("onPortRemoved", {}); + }); + + if (removeOutPorts.length > 0 || removeInPorts.length > 0) + { + op.emitEvent("onUiAttribsChange", {}); + } +}; + +const removeInPort = (port) => +{ + port.removeLinks(); + for (let ipi = 0; ipi < op.portsIn.length; ipi++) + { + if (op.portsIn[ipi] == port) + { + op.portsIn.splice(ipi, 1); + return; + } + } +}; + +const removeOutPort = (port) => +{ + port.removeLinks(); + for (let ipi = 0; ipi < op.portsOut.length; ipi++) + { + if (op.portsOut[ipi] == port) + { + op.portsOut.splice(ipi, 1); + return; + } + } +}; + +function setupPorts(parentSubPatch) +{ + const subPatchDataPort = parentSubPatch.portsIn.find((port) => { return port.name === "dataStr"; }); + if (!subPatchDataPort) return; + if (!subPatchDataPort.get()) return; + + const oldPorts = getOldPorts(); + cleanupPorts(); + + const subPatchData = JSON.parse(subPatchDataPort.get()); + const subPatchPortsIn = subPatchData.ports || []; + const subPatchPortsOut = subPatchData.portsOut || []; + let i = 0; + + if (op.patch.isEditorMode()) CABLES.UI.undo.pause(); + const newPorts = []; + for (i = 0; i < subPatchPortsIn.length; i++) + { + if (!op.getPortByName(subPatchPortsIn[i].name)) + { + const subPatchPort = parentSubPatch.portsIn.find((port) => { return port.name == subPatchPortsIn[i].name; }); + const newPort = op.addInPort(new CABLES.Port(op, subPatchPort.name, subPatchPort.type)); + + if (subPatchPort) + { + switch (subPatchPort.type) + { + case CABLES.OP_PORT_TYPE_FUNCTION: + newPort.onTriggered = () => + { + subPatchPort.onTriggered(); + }; + break; + default: + newPort.onChange = () => + { + subPatchPort.set(newPort.get()); + if (!newPort.isLinked()) + { + savePortData(); + } + }; + } + } + + if (oldPorts.portsIn.hasOwnProperty(newPort.name)) + { + if (!wasPasted && Array.isArray(oldPorts.portsIn[newPort.name].links)) + { + oldPorts.portsIn[newPort.name].links.forEach((link) => + { + let parent = op.patch.getOpById(link.objOut); + if (parent) + { + const newLink = op.patch.link(parent, link.portOut, op, newPort.name); + if (newLink) newLink.ignoreInSerialize = true; + } + else + { + parent = op.patch.ops.find((subOp) => + { + return subOp.storage && + subOp.storage.blueprint && + subOp.storage.blueprint.originalOpId == link.objOut && + op.storage && + op.storage.blueprint && + subOp.storage.blueprint.blueprintOpId == op.storage.blueprint.blueprintOpId; + }); + if (parent) + { + const newLink = op.patch.link(parent, link.portOut, op, newPort.name); + if (newLink) newLink.ignoreInSerialize = true; + } + } + }); + } + if (!newPort.isLinked()) + { + newPort.set(oldPorts.portsIn[newPort.name].value); + } + } + newPort.onLinkChanged = savePortData; + + if (subPatchPort.title) + { + newPort.setUiAttribs({ "title": subPatchPortsIn[i].title }); + } + else if (subPatchPort.uiAttribs && subPatchPort.uiAttribs.title) + { + newPort.setUiAttribs({ "title": subPatchPort.uiAttribs.title }); + } + if (subPatchPort.uiAttribs && subPatchPort.uiAttribs.objType) + { + newPort.setUiAttribs({ "objType": subPatchPort.uiAttribs.objType }); + } + newPorts.push(newPort); + } + } + op.setPortGroup("Blueprint Ports", newPorts); + + for (i = 0; i < subPatchPortsOut.length; i++) + { + if (!op.getPortByName(subPatchPortsOut[i].name)) + { + const patchPortIn = parentSubPatch.portsIn.find((port) => { return port.name === "patchId"; }); + const patchOutputOP = op.patch.getSubPatchOp(patchPortIn.value, "Ops.Ui.PatchOutput"); + if (patchOutputOP.portsIn) + { + const subPatchPort = patchOutputOP.portsIn.find((port) => { return port.name == subPatchPortsOut[i].name; }); + const newPort = op.addOutPort(new CABLES.Port(op, subPatchPort.name, subPatchPort.type)); + newPort.ignoreValueSerialize = true; + + if (subPatchPort) + { + switch (subPatchPort.type) + { + case CABLES.OP_PORT_TYPE_FUNCTION: + subPatchPort.onTriggered = () => + { + newPort.trigger(); + }; + break; + default: + subPatchPort.onChange = () => + { + newPort.set(subPatchPort.get()); + }; + } + newPort.set(subPatchPort.get()); + } + + if (oldPorts.portsOut.hasOwnProperty(newPort.name)) + { + if (!wasPasted && Array.isArray(oldPorts.portsOut[newPort.name].links)) + { + oldPorts.portsOut[newPort.name].links.forEach((link) => + { + let parent = op.patch.getOpById(link.objIn); + if (parent) + { + const newLink = op.patch.link(op, newPort.name, parent, link.portIn); + if (newLink) newLink.ignoreInSerialize = true; + } + else + { + parent = op.patch.ops.find((subOp) => + { + return subOp.storage && + subOp.storage.blueprint && + subOp.storage.blueprint.originalOpId == link.objIn && + op.storage && + op.storage.blueprint && + subOp.storage.blueprint.blueprintOpId == op.storage.blueprint.blueprintOpId; + }); + if (parent) + { + const newLink = op.patch.link(op, newPort.name, parent, link.portIn); + if (newLink) newLink.ignoreInSerialize = true; + } + } + }); + } + } + newPort.onLinkChanged = savePortData; + + if (subPatchPort.title) + { + newPort.setUiAttribs({ "title": subPatchPortsOut[i].title }); + } + else if (subPatchPort.uiAttribs && subPatchPort.uiAttribs.title) + { + newPort.setUiAttribs({ "title": subPatchPort.uiAttribs.title }); + } + + if (subPatchPort.uiAttribs && subPatchPort.uiAttribs.objType) + { + newPort.setUiAttribs({ "objType": subPatchPort.uiAttribs.objType }); + } + } + } + } + savePortData(); + if (op.patch.isEditorMode()) CABLES.UI.undo.resume(); +} + +function savePortData() +{ + const newPortsData = { "portsIn": {}, "portsOut": {} }; + op.portsIn.forEach((port) => + { + if (!protectedPorts.includes(port.id)) + { + const portData = { + "name": port.name, + "title": port.title, + "type": port.type, + "links": [] + }; + if (!port.links || port.links.length === 0) portData.value = port.get(); + port.links.forEach((link) => + { + link.ignoreInSerialize = true; + const linkData = { + "objOut": link.portOut.parent.id, + "portOut": link.portOut.name + }; + portData.links.push(linkData); + }); + newPortsData.portsIn[port.name] = portData; + } + }); + + op.portsOut.forEach((port) => + { + if (!protectedPorts.includes(port.id)) + { + const portData = { + "name": port.name, + "title": port.title, + "type": port.type, + "links": [] + }; + if (!port.links || port.links.length === 0) + { + let portValue = port.get(); + try + { + JSON.stringify(portValue); + } + catch (e) + { + portValue = null; + } + portData.value = portValue; + } + port.links.forEach((link) => + { + link.ignoreInSerialize = true; + const linkData = { + "objIn": link.portIn.parent.id, + "portIn": link.portIn.name + }; + portData.links.push(linkData); + }); + newPortsData.portsOut[port.name] = portData; + } + }); + const serializedPortsData = JSON.stringify(newPortsData); + portsData.set(serializedPortsData); +} + + +}; + +Ops.Dev.Blueprint.prototype = new CABLES.Op(); +CABLES.OPS["3b25fb24-2a1f-4e5a-a18e-e08911a94030"]={f:Ops.Dev.Blueprint,objName:"Ops.Dev.Blueprint"}; + + + + +// ************************************************************** +// +// Ops.Dev.BranchProfiler +// +// ************************************************************** + +Ops.Dev.BranchProfiler = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Exec"), + outNext = op.outTrigger("Next"), + outProfilerStack = op.outObject("Profiler Data"); + +op.patch.cgl.frameStore.branchProfiler = op.patch.cgl.frameStore.branchProfiler || {}; + +inExec.onTriggered = () => +{ + op.patch.cgl.frameStore.branchProfiler = {}; + + op.patch.cgl.frameStore.branchStack = op.patch.cgl.frameStore.branchStack || new CABLES.BranchStack(); + + op.patch.cgl.frameStore.branchStack.start(); + + outNext.trigger(); + + op.patch.cgl.frameStore.branchStack.finish(); + + outProfilerStack.set(op.patch.cgl.frameStore.branchStack); +}; + + +}; + +Ops.Dev.BranchProfiler.prototype = new CABLES.Op(); +CABLES.OPS["5bc375f6-59f6-40f6-bc26-7b23e28d8bf2"]={f:Ops.Dev.BranchProfiler,objName:"Ops.Dev.BranchProfiler"}; + + + + +// ************************************************************** +// +// Ops.Dev.BranchProfilerBranch +// +// ************************************************************** + +Ops.Dev.BranchProfilerBranch = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Exec"), + inName = op.inString("Branch Name", "default"), + outNext = op.outTrigger("Next"), + outDur = op.outNumber("Duration"); + +op.patch.cgl.frameStore.branchProfiler = op.patch.cgl.frameStore.branchProfiler || {}; + +inExec.onTriggered = () => +{ + op.patch.cgl.frameStore.branchStack = op.patch.cgl.frameStore.branchStack || new CABLES.BranchStack(); + + const c = op.patch.cgl.frameStore.branchStack.push(inName.get()); + + outNext.trigger(); + + if (op.patch.cgl.frameStore.branchStack.current == c) op.patch.cgl.frameStore.branchStack.pop(); + + outDur.set(c.dur); +}; + + +}; + +Ops.Dev.BranchProfilerBranch.prototype = new CABLES.Op(); +CABLES.OPS["2082b704-0283-4645-b741-1e1955f28b66"]={f:Ops.Dev.BranchProfilerBranch,objName:"Ops.Dev.BranchProfilerBranch"}; + + + + +// ************************************************************** +// +// Ops.Dev.BranchProfilerBranchEnd +// +// ************************************************************** + +Ops.Dev.BranchProfilerBranchEnd = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Exec"), + outNext = op.outTrigger("Next"); + +op.patch.cgl.frameStore.branchProfiler = op.patch.cgl.frameStore.branchProfiler || {}; + +inExec.onTriggered = ()=> +{ + if(op.patch.cgl.frameStore.branchStack) op.patch.cgl.frameStore.branchStack.pop(); + outNext.trigger(); +}; + + +}; + +Ops.Dev.BranchProfilerBranchEnd.prototype = new CABLES.Op(); +CABLES.OPS["2c5f3947-9962-49ee-b09b-084bf8717f00"]={f:Ops.Dev.BranchProfilerBranchEnd,objName:"Ops.Dev.BranchProfilerBranchEnd"}; + + + + +// ************************************************************** +// +// Ops.Dev.ColorAreaSDF_v5 +// +// ************************************************************** + +Ops.Dev.ColorAreaSDF_v5 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colorarea_frag":"\nvec3 MOD_size=vec3(MOD_inSizeAmountFalloffSizeX.x);\n#ifdef MOD_DOSCALE\n MOD_size*=MOD_scale.xyz;\n#endif\n\nvec3 MOD_col=MOD_color;\n\n#ifdef MOD_USE_TEX\n MOD_col=texture(MOD_tex,gl_FragCoord.xy/float(textureSize(MOD_tex,0).xy)).rgb;\n#endif\n\n\n\n#ifdef MOD_AREA_SPHERE\n float MOD_de=MOD_sdSphere(MOD_pos.xyz-MOD_vertPos.xyz,MOD_size.x);\n#endif\n\n#ifdef MOD_AREA_BOX\n float MOD_r=MOD_scale.w;\n MOD_r*=MOD_inSizeAmountFalloffSizeX.x;\n float MOD_de=MOD_sdRoundBox(MOD_pos.xyz-MOD_vertPos.xyz,MOD_size-MOD_r,MOD_r);\n#endif\n\n#ifdef MOD_AREA_TRIPRISM\n float MOD_de=MOD_sdTriPrism(MOD_pos.xyz-MOD_vertPos.xyz,vec2(MOD_size.x,MOD_size.z));\n#endif\n\n#ifdef MOD_AREA_HEXPRISM\n float MOD_de=MOD_sdHexPrism(MOD_pos.xyz-MOD_vertPos.xyz,vec2(MOD_size.x,MOD_size.z));\n#endif\n\n\n\n// #ifndef MOD_AREA_SPHERE\n// #ifndef MOD_AREA_BOX\n// float MOD_de=1.0-smoothstep(MOD_inSizeAmountFalloffSizeX.z*MOD_inSizeAmountFalloffSizeX.x,MOD_inSizeAmountFalloffSizeX.x,MOD_de);\n// #endif\n// #endif\n\n#ifdef MOD_AREA_AXIS_X\n float MOD_de=abs(MOD_pos.x-MOD_vertPos.x);\n#endif\n#ifdef MOD_AREA_AXIS_Y\n float MOD_de=abs(MOD_pos.y-MOD_vertPos.y);\n#endif\n#ifdef MOD_AREA_AXIS_Z\n float MOD_de=abs(MOD_pos.z-MOD_vertPos.z);\n#endif\n\n#ifdef MOD_AREA_AXIS_X_INFINITE\n float MOD_de=MOD_pos.x-MOD_vertPos.x;\n#endif\n#ifdef MOD_AREA_AXIS_Y_INFINITE\n float MOD_de=MOD_pos.y-MOD_vertPos.y;\n#endif\n#ifdef MOD_AREA_AXIS_Z_INFINITE\n float MOD_de=MOD_pos.z-MOD_vertPos.z;\n#endif\n\n\nMOD_de=1.0-MOD_map(\n MOD_de,\n 0.0, MOD_inSizeAmountFalloffSizeX.z,\n 0.0,1.0\n );\n\n\n#ifdef MOD_AREA_INVERT\n MOD_de=1.0-MOD_de;\n#endif\n\n#ifdef MOD_BLEND_NORMAL\n col.rgb=mix(col.rgb,MOD_col, MOD_de*MOD_inSizeAmountFalloffSizeX.y);\n#endif\n\n\n#ifdef MOD_BLEND_MULTIPLY\n col.rgb=mix(col.rgb,col.rgb*MOD_col,MOD_de*MOD_inSizeAmountFalloffSizeX.y);\n#endif\n\n#ifdef MOD_BLEND_ADD\n col.rgb+=MOD_de*MOD_inSizeAmountFalloffSizeX.y*MOD_col;\n#endif\n\n\n#ifdef MOD_BLEND_OPACITY\n col.a*=(1.0-MOD_de*MOD_inSizeAmountFalloffSizeX.y);\n#endif\n\n#ifdef MOD_BLEND_DISCARD\n if(MOD_de*MOD_inSizeAmountFalloffSizeX.y>=0.999)discard;\n#endif\n\n// col.rgb=vec3(distance(MOD_vertPos.xyz,MOD_pos.xyz))*0.1\n// col.rgb=MOD_pos.xyz;","colorarea_head_frag":"IN vec4 MOD_vertPos;\n\nfloat MOD_map(float value,float min1,float max1,float min2,float max2)\n{\n return max(min2,min(max2,min2 + (value - min1) * (max2 - min2) / (max1 - min1)));\n\n}\n\n\nfloat MOD_sdSphere( vec3 p, float s )\n{\n return length(p)-s;\n}\n\n\nfloat MOD_sdRoundBox( vec3 p, vec3 b, float r )\n{\n vec3 q = abs(p) - b;\n return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0) - r;\n}\n\nfloat MOD_sdTriPrism( vec3 p, vec2 h )\n{\n vec3 q = abs(p);\n return max(q.z-h.y,max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5);\n}\n\nfloat MOD_sdHexPrism( vec3 p, vec2 h )\n{\n const vec3 k = vec3(-0.8660254, 0.5, 0.57735);\n p = abs(p);\n p.xy -= 2.0*min(dot(k.xy, p.xy), 0.0)*k.xy;\n vec2 d = vec2(\n length(p.xy-vec2(clamp(p.x,-k.z*h.x,k.z*h.x), h.x))*sign(p.y-h.x),\n p.z-h.y );\n return min(max(d.x,d.y),0.0) + length(max(d,0.0));\n}\n\n",}; +const + render = op.inTrigger("Render"), + inArea = op.inValueSelect("Area", ["Sphere", "Box", "Tri Prism", "Hex Prism", "Axis X", "Axis Y", "Axis Z", "Axis X Infinite", "Axis Y Infinite", "Axis Z Infinite"], "Sphere"), + inSize = op.inValue("Size", 1), + roundNess = op.inFloatSlider("Roundness", 0), + inAmount = op.inValueSlider("Amount", 0.5), + inFalloff = op.inFloat("Falloff", 0), + inInvert = op.inValueBool("Invert"), + inBlend = op.inSwitch("Blend ", ["Normal", "Multiply", "Opacity", "Add", "Discard"], "Normal"), + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + x = op.inValue("x"), + y = op.inValue("y"), + z = op.inValue("z"), + doScale = op.inBool("Change Size", false), + sizeX = op.inFloat("Size X", 1), + sizeY = op.inFloat("Size Y", 1), + sizeZ = op.inFloat("Size Z", 1), + inTex = op.inTexture("Texture"), + + inWorldSpace = op.inValueBool("WorldSpace", true), + inPrio = op.inBool("Priority", true), + next = op.outTrigger("Next"); + +op.setPortGroup("Scale", [doScale, sizeX, sizeZ, sizeY]); +op.setPortGroup("Position", [x, y, z]); +op.setPortGroup("Color", [inBlend, r, g, b]); +r.setUiAttribs({ "colorPick": true }); + +const cgl = op.patch.cgl; + +const srcHeadVert = "" + .endl() + "OUT vec4 MOD_vertPos;" + .endl(); + +const srcBodyVert = "" + .endl() + "#ifndef MOD_WORLDSPACE" + .endl() + " MOD_vertPos=vec4(vPosition,1.0);" + .endl() + "#endif" + + .endl() + "#ifdef MOD_WORLDSPACE" + .endl() + " MOD_vertPos=mMatrix*pos;" + .endl() + "#endif" + .endl(); + +inWorldSpace.onChange = + inTex.onLinkChanged = + inArea.onChange = + inInvert.onChange = + doScale.onChange = + inBlend.onChange = updateDefines; + +render.onTriggered = doRender; + +const vertModTitle = "vert_" + op.name; +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "priority": 2, + "title": vertModTitle, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert +}); + +mod.addModule({ + "title": op.name, + "name": "MODULE_COLOR", + "srcHeadFrag": attachments.colorarea_head_frag, + "srcBodyFrag": attachments.colorarea_frag +}); + +mod.addUniform("4f", "MOD_inSizeAmountFalloffSizeX", inSize, inAmount, inFalloff, 0); +mod.addUniform("3f", "MOD_color", r, g, b); +mod.addUniform("3f", "MOD_pos", x, y, z); +mod.addUniform("4f", "MOD_scale", sizeX, sizeY, sizeZ, roundNess); +mod.addUniform("t", "MOD_tex"); + +updateDefines(); + +inPrio.onChange = updatePrio; +updatePrio(); + +function updatePrio() +{ + mod.removeModule(vertModTitle); + + const vmod = { + // "priority": 0, + "title": vertModTitle, + "name": "MODULE_VERTEX_POSITION", + srcHeadVert, + srcBodyVert + }; + + if (inPrio.get()) vmod.priority = 2; + + mod.addModule(vmod); +} + +function updateDefines() +{ + mod.toggleDefine("MOD_BLEND_NORMAL", inBlend.get() == "Normal"); + mod.toggleDefine("MOD_BLEND_OPACITY", inBlend.get() == "Opacity"); + mod.toggleDefine("MOD_BLEND_MULTIPLY", inBlend.get() == "Multiply"); + mod.toggleDefine("MOD_BLEND_DISCARD", inBlend.get() == "Discard"); + mod.toggleDefine("MOD_BLEND_ADD", inBlend.get() == "Add"); + + mod.toggleDefine("MOD_AREA_SIZE", doScale.get()); + + mod.toggleDefine("MOD_AREA_INVERT", inInvert.get()); + mod.toggleDefine("MOD_WORLDSPACE", inWorldSpace.get()); + + mod.toggleDefine("MOD_AREA_AXIS_X", inArea.get() == "Axis X"); + mod.toggleDefine("MOD_AREA_AXIS_Y", inArea.get() == "Axis Y"); + mod.toggleDefine("MOD_AREA_AXIS_Z", inArea.get() == "Axis Z"); + mod.toggleDefine("MOD_AREA_AXIS_X_INFINITE", inArea.get() == "Axis X Infinite"); + mod.toggleDefine("MOD_AREA_AXIS_Y_INFINITE", inArea.get() == "Axis Y Infinite"); + mod.toggleDefine("MOD_AREA_AXIS_Z_INFINITE", inArea.get() == "Axis Z Infinite"); + mod.toggleDefine("MOD_AREA_SPHERE", inArea.get() == "Sphere"); + mod.toggleDefine("MOD_AREA_BOX", inArea.get() == "Box"); + mod.toggleDefine("MOD_AREA_TRIPRISM", inArea.get() == "Tri Prism"); + mod.toggleDefine("MOD_AREA_HEXPRISM", inArea.get() == "Hex Prism"); + + mod.toggleDefine("MOD_DOSCALE", doScale.get()); + + // mod.removeUniform("3f", "MOD_scale",sizeX,sizeY,sizeZ); + sizeX.setUiAttribs({ "greyout": !doScale.get() }); + sizeY.setUiAttribs({ "greyout": !doScale.get() }); + sizeZ.setUiAttribs({ "greyout": !doScale.get() }); + + roundNess.setUiAttribs({ "greyout": inArea.get() != "Box" }); + + mod.toggleDefine("MOD_USE_TEX", inTex.isLinked()); +} + +function drawHelpers() +{ + if (cgl.frameStore.shadowPass) return; + if (cgl.shouldDrawHelpers(op)) gui.setTransformGizmo({ "posX": x, "posY": y, "posZ": z }); +} + +function doRender() +{ + // if(doScale.get()) mod.setUniformValue("MOD_scale",[sizeX.get(),sizeY.get(),sizeZ.get()]); + mod.bind(); + + let tex = inTex.get(); + + if (!tex) tex = CGL.Texture.getEmptyTexture(cgl).tex; + else tex = tex.tex; + + mod.pushTexture("MOD_tex", tex); + + drawHelpers(); + next.trigger(); + + mod.unbind(); +} + + +}; + +Ops.Dev.ColorAreaSDF_v5.prototype = new CABLES.Op(); +CABLES.OPS["3ea4bb8a-d2a0-4600-9d98-83d5a091ea4f"]={f:Ops.Dev.ColorAreaSDF_v5,objName:"Ops.Dev.ColorAreaSDF_v5"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.Geometry.CopyGeometry +// +// ************************************************************** + +Ops.Dev.Gl.Geometry.CopyGeometry = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + geometry = op.inObject("Geometry"), + outGeom = op.outObject("Result"); + +geometry.onChange = update; + +function update() +{ + let oldGeom = geometry.get(); + + if (oldGeom) + { + let geom = oldGeom.copy(); + + outGeom.set(geom); + } + else outGeom.set(null); +} + + +}; + +Ops.Dev.Gl.Geometry.CopyGeometry.prototype = new CABLES.Op(); +CABLES.OPS["b15f57c2-4b0c-45de-962b-dd249779addf"]={f:Ops.Dev.Gl.Geometry.CopyGeometry,objName:"Ops.Dev.Gl.Geometry.CopyGeometry"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Abs +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Abs = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec = op.inObject("input", null, "sg_float"), + outvec = op.outObject("result", null, "sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc = "abs"; + +invec.onLinkChanged = () => +{ + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([invec], [invec, outvec]); +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Abs.prototype = new CABLES.Op(); +CABLES.OPS["2543cfb4-c1e2-44f1-bfd4-de6cc8143b8b"]={f:Ops.Dev.Gl.ShaderGraph.Abs,objName:"Ops.Dev.Gl.ShaderGraph.Abs"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Color +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Color = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inColor = op.inDropDown("Color", ["Red", "Green", "Blue", "Cyan", "Pink", "Yellow", "White", "Black"], "Red"); +const outvec = op.outObject("result", null, "sg_vec4"); + +const sg = new CGL.ShaderGraphOp(this); +inColor.onChange = update; +update(); + +function update() +{ + if (inColor.get() == "Red") op.shaderVar = "vec4(1.,0.,0.,1.)"; + if (inColor.get() == "Green") op.shaderVar = "vec4(0.,1.,0.,1.)"; + if (inColor.get() == "Blue") op.shaderVar = "vec4(0.,0.,1.,1.)"; + if (inColor.get() == "Cyan") op.shaderVar = "vec4(0.,1.,1.,1.)"; + if (inColor.get() == "Pink") op.shaderVar = "vec4(1.,0.,1.,1.)"; + if (inColor.get() == "Yellow") op.shaderVar = "vec4(1.,1.,0.,1.)"; + + sg.updateGraph(); +} + + +}; + +Ops.Dev.Gl.ShaderGraph.Color.prototype = new CABLES.Op(); +CABLES.OPS["afb10809-c7cc-410a-8402-97604b548914"]={f:Ops.Dev.Gl.ShaderGraph.Color,objName:"Ops.Dev.Gl.ShaderGraph.Color"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Cos +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Cos = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec = op.inObject("input", null, "sg_float"), + outvec = op.outObject("result", null, "sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc = "cos"; + +invec.onLinkChanged = () => +{ + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([invec], [invec, outvec]); +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Cos.prototype = new CABLES.Op(); +CABLES.OPS["3596827c-fc80-4109-b98e-9183d7589286"]={f:Ops.Dev.Gl.ShaderGraph.Cos,objName:"Ops.Dev.Gl.ShaderGraph.Cos"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Cross +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Cross = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec = op.inObject("value a", null, "sg_vec3"), + invecb = op.inObject("value b", null, "sg_vec3"), + outvec = op.outObject("result", null, "sg_vec3"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc = "cross"; + + +}; + +Ops.Dev.Gl.ShaderGraph.Cross.prototype = new CABLES.Op(); +CABLES.OPS["6c041b4c-2692-4068-b86e-47476f3ad261"]={f:Ops.Dev.Gl.ShaderGraph.Cross,objName:"Ops.Dev.Gl.ShaderGraph.Cross"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.CustomShaderGraphOp +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.CustomShaderGraphOp = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inCode = op.inStringEditor("Code", "float main(vec3 rgb)\n{\nreturn rgb*2.0;\n}", "glsl"); + +const sgOp = new CGL.ShaderGraphOp(this); + +op.shaderSrc = inCode.get(); +op.shaderFunc = ""; + +inCode.onChange = () => +{ + const info = sgOp.parseCode(inCode.get()); + sgOp.updatePorts(info); +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.CustomShaderGraphOp.prototype = new CABLES.Op(); +CABLES.OPS["a4145b55-6ef9-4084-b32f-7eb224a7661c"]={f:Ops.Dev.Gl.ShaderGraph.CustomShaderGraphOp,objName:"Ops.Dev.Gl.ShaderGraph.CustomShaderGraphOp"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Distance +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Distance = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec = op.inObject("value a", null, "sg_float"), + invecb = op.inObject("value b", null, "sg_float"), + outvec = op.outObject("result", null, "sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc = "distance"; + +invecb.onLinkChanged = +invec.onLinkChanged = () => +{ + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([invec, invecb], [invec, invecb]); +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Distance.prototype = new CABLES.Op(); +CABLES.OPS["bfb67ec3-a1b6-410f-a52c-0eba05b83453"]={f:Ops.Dev.Gl.ShaderGraph.Distance,objName:"Ops.Dev.Gl.ShaderGraph.Distance"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Dot +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Dot = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec = op.inObject("value a", null, "sg_genType"), + invecb = op.inObject("value b", null, "sg_genType"), + outvec = op.outObject("result", null, "sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc = "dot"; + +invecb.onLinkChanged = +invec.onLinkChanged = () => +{ + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([invec, invecb], [invec, invecb]); +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Dot.prototype = new CABLES.Op(); +CABLES.OPS["aaa64cf2-b96f-40c7-be24-12d5de08cff7"]={f:Ops.Dev.Gl.ShaderGraph.Dot,objName:"Ops.Dev.Gl.ShaderGraph.Dot"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.FragColor +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.FragColor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"shader_frag":"void setColor(vec4 col)\n{\n #ifdef SETCOLOR_ALPHA\n col.a=1.0;\n #endif\n {{MODULE_COLOR}}\n outColor=col;\n}",}; +const sgOp = new CGL.ShaderGraphOp(this, attachments.shader_frag); + +const inAlpha = op.inBool("Alpha always one", true); + +inAlpha.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + sgOp.toggleDefine("SETCOLOR_ALPHA", inAlpha.get()); +} + + +}; + +Ops.Dev.Gl.ShaderGraph.FragColor.prototype = new CABLES.Op(); +CABLES.OPS["38ad0d02-7973-437b-8372-7f3f77bfad48"]={f:Ops.Dev.Gl.ShaderGraph.FragColor,objName:"Ops.Dev.Gl.ShaderGraph.FragColor"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.InputColor +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.InputColor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inName = op.inString("Name", "myColor"), + uni1 = op.inFloat("R", 0), + uni2 = op.inFloat("G", 0), + uni3 = op.inFloat("B", 0), + uni4 = op.inFloat("A", 1), + inType = op.inSwitch("Type", ["Uniform", "Static"], "Uniform"), + result = op.outObject("vec4", null, "sg_vec4"); + +uni1.setUiAttribs({ "colorPick": true }); + +const sgOp = new CGL.ShaderGraphOp(this); + +inName.onChange = +inType.onChange = updateUniDefs; + +updateUniDefs(); + +uni1.onChange = + uni2.onChange = + uni3.onChange = + uni4.onChange = () => + { + if (inType.get() == "Static")sgOp.updateGraph(); + }; + +function updateUniDefs() +{ + const varname = (inName.get() || "myColor") + "_" + CGL.ShaderGraph.getNewId(); + op.shaderVar = varname; + op.shaderUniforms = + [{ + "type": "4f", + "name": varname, + "ports": [uni1, uni2, uni3, uni4], + "static": inType.get() == "Static" + }]; + + op.setUiAttrib({ "extendTitle": inName.get() }); + + sgOp.updateGraph(); +} + + +}; + +Ops.Dev.Gl.ShaderGraph.InputColor.prototype = new CABLES.Op(); +CABLES.OPS["ad57d840-4fc7-43cf-9095-35e52fde5c14"]={f:Ops.Dev.Gl.ShaderGraph.InputColor,objName:"Ops.Dev.Gl.ShaderGraph.InputColor"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.InputTexture +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.InputTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +const + inTex = op.inTexture("Texture"), + inName = op.inString("Name", "myTexture"), + result = op.outObject("vec4", null, "sg_sampler2D"); + +const sgOp = new CGL.ShaderGraphOp(this); +inName.onChange = +inTex.onChange = updateUniDefs; + +updateUniDefs(); + +function updateUniDefs() +{ + const varname = (inName.get() || "myVec4") + "_" + CGL.ShaderGraph.getNewId(); + op.shaderVar = varname; + op.shaderUniforms = + [{ + "type": "t", + "name": varname, + "ports": [inTex] + }]; + + op.setUiAttrib({ "extendTitle": inName.get() }); + + sgOp.updateGraph(); +} + + +}; + +Ops.Dev.Gl.ShaderGraph.InputTexture.prototype = new CABLES.Op(); +CABLES.OPS["025e1fe0-43be-4d57-b6f8-5a3e314068d1"]={f:Ops.Dev.Gl.ShaderGraph.InputTexture,objName:"Ops.Dev.Gl.ShaderGraph.InputTexture"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.InputVec4 +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.InputVec4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inName = op.inString("Name", "myVec4"), + uni1 = op.inFloat("Number X", 0), + uni2 = op.inFloat("Number Y", 0), + uni3 = op.inFloat("Number Z", 0), + uni4 = op.inFloat("Number W", 1), + inType = op.inSwitch("Type", ["Uniform", "Static"], "Uniform"), + result = op.outObject("vec4", null, "sg_vec4"); + +const sgOp = new CGL.ShaderGraphOp(this); + +inName.onChange = +inType.onChange = updateUniDefs; + +updateUniDefs(); + +uni1.onChange = + uni2.onChange = + uni3.onChange = + uni4.onChange = () => + { + if (inType.get() == "Static")sgOp.updateGraph(); + }; + +function updateUniDefs() +{ + const varname = (inName.get() || "myVec4") + "_" + CGL.ShaderGraph.getNewId(); + op.shaderVar = varname; + op.shaderUniforms = + [{ + "type": "4f", + "name": varname, + "ports": [uni1, uni2, uni3, uni4], + "static": inType.get() == "Static" + }]; + + op.setUiAttrib({ "extendTitle": inName.get() }); + + sgOp.updateGraph(); +} + + +}; + +Ops.Dev.Gl.ShaderGraph.InputVec4.prototype = new CABLES.Op(); +CABLES.OPS["4e089d60-006b-4879-9b45-b9616c7fc18d"]={f:Ops.Dev.Gl.ShaderGraph.InputVec4,objName:"Ops.Dev.Gl.ShaderGraph.InputVec4"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Max +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Max = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec = op.inObject("value a", null, "sg_float"), + invecb = op.inObject("value b", null, "sg_float"), + outvec = op.outObject("result", null, "sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc = "max"; + +invecb.onLinkChanged = +invec.onLinkChanged = () => +{ + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([invec, invecb], [invec, invecb, outvec]); +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Max.prototype = new CABLES.Op(); +CABLES.OPS["285b5693-5038-44a2-82f4-989266ac8655"]={f:Ops.Dev.Gl.ShaderGraph.Max,objName:"Ops.Dev.Gl.ShaderGraph.Max"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Min +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Min = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec = op.inObject("value a", null, "sg_float"), + invecb = op.inObject("value b", null, "sg_float"), + outvec = op.outObject("result", null, "sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc = "min"; + +invecb.onLinkChanged = +invec.onLinkChanged = () => +{ + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([invec, invecb], [invec, invecb, outvec]); +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Min.prototype = new CABLES.Op(); +CABLES.OPS["58c146e1-8512-4f1c-b23f-52099b566978"]={f:Ops.Dev.Gl.ShaderGraph.Min,objName:"Ops.Dev.Gl.ShaderGraph.Min"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Mix +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Mix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inx=op.inObject("x",null,"sg_float"), + iny=op.inObject("y",null,"sg_float"), + ina=op.inObject("a",null,"sg_float"), + result=op.outObject("result",null,"sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc="mix"; + +inx.onLinkChanged= +iny.onLinkChanged= +ina.onLinkChanged=()=> +{ + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([inx,iny],[inx,iny,result]); +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Mix.prototype = new CABLES.Op(); +CABLES.OPS["b4b9f9d3-10c1-4a8e-8282-d05df9e92801"]={f:Ops.Dev.Gl.ShaderGraph.Mix,objName:"Ops.Dev.Gl.ShaderGraph.Mix"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Multiply +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Multiply = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inputa = op.inObject("a", null, "sg_genType"), + inputb = op.inObject("b", null, "sg_genType"), + outvec = op.outObject("result", null, "sg_genType"); + +inputa.setUiAttribs({ "ignoreObjTypeErrors": true }); +inputb.setUiAttribs({ "ignoreObjTypeErrors": true }); + +new CGL.ShaderGraphOp(this); + +inputb.onLinkChanged = +inputa.onLinkChanged = () => +{ + const t = CGL.ShaderGraphOp.getMaxGenTypeFromPorts([inputb, inputa]); + + inputa.setUiAttribs({ "objType": t }); + outvec.setUiAttribs({ "objType": t }); + inputb.setUiAttribs({ "objType": t }); +}; + +op.shaderCodeOperator = "*"; + + +}; + +Ops.Dev.Gl.ShaderGraph.Multiply.prototype = new CABLES.Op(); +CABLES.OPS["84316631-39d6-45f7-8b03-2499dc22f3bb"]={f:Ops.Dev.Gl.ShaderGraph.Multiply,objName:"Ops.Dev.Gl.ShaderGraph.Multiply"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Normalize +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Normalize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec = op.inObject("input", null, "sg_float"), + outvec = op.outObject("result", null, "sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc = "normalize"; + +invec.onLinkChanged = () => +{ + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([invec], [invec, outvec]); +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Normalize.prototype = new CABLES.Op(); +CABLES.OPS["2358fdab-8261-45f0-bb63-ddef88ad4fa3"]={f:Ops.Dev.Gl.ShaderGraph.Normalize,objName:"Ops.Dev.Gl.ShaderGraph.Normalize"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.PerlinNoise3D +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.PerlinNoise3D = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"perlin_glsl":"\n//\nfloat Interpolation_C2( float x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } // 6x^5-15x^4+10x^3\t( Quintic Curve. As used by Perlin in Improved Noise. http://mrl.nyu.edu/~perlin/paper445.pdf )\nvec2 Interpolation_C2( vec2 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec3 Interpolation_C2( vec3 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2( vec4 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2_InterpAndDeriv( vec2 x ) { return x.xyxy * x.xyxy * ( x.xyxy * ( x.xyxy * ( x.xyxy * vec2( 6.0, 0.0 ).xxyy + vec2( -15.0, 30.0 ).xxyy ) + vec2( 10.0, -60.0 ).xxyy ) + vec2( 0.0, 30.0 ).xxyy ); }\nvec3 Interpolation_C2_Deriv( vec3 x ) { return x * x * (x * (x * 30.0 - 60.0) + 30.0); }\nfloat Falloff_Xsq_C1( float xsq ) { xsq = 1.0 - xsq; return xsq*xsq; }\t// ( 1.0 - x*x )^2 ( Used by Humus for lighting falloff in Just Cause 2. GPUPro 1 )\nfloat Falloff_Xsq_C2( float xsq ) { xsq = 1.0 - xsq; return xsq*xsq*xsq; }\t// ( 1.0 - x*x )^3. NOTE: 2nd derivative is 0.0 at x=1.0, but non-zero at x=0.0\nvec4 Falloff_Xsq_C2( vec4 xsq ) { xsq = 1.0 - xsq; return xsq*xsq*xsq; }\n\n\nvoid FAST32_hash_3D( vec3 gridcell, out vec4 lowz_hash, out vec4 highz_hash )\t//\tgenerates a random number for each of the 8 cell corners\n{\n// gridcell is assumed to be an integer coordinate\n//\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const float SOMELARGEFLOAT = 635.298681;\n const float ZINC = 48.500388;\n\n //\ttruncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n //\tcalculate the noise\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n P = P.xzxz * P.yyww;\n highz_hash.xy = vec2( 1.0 / ( SOMELARGEFLOAT + vec2( gridcell.z, gridcell_inc1.z ) * ZINC ) );\n lowz_hash = fract( P * highz_hash.xxxx );\n highz_hash = fract( P * highz_hash.yyyy );\n}\n\n\n\nvoid FAST32_hash_3D( \tvec3 gridcell,out vec4 lowz_hash_0,out vec4 lowz_hash_1,out vec4 lowz_hash_2,out vec4 highz_hash_0,out vec4 highz_hash_1,out vec4 highz_hash_2\t)\t\t//\tgenerates 3 random numbers for each of the 8 cell corners\n{\n // gridcell is assumed to be an integer coordinate\n\n //\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const vec3 SOMELARGEFLOATS = vec3( 635.298681, 682.357502, 668.926525 );\n const vec3 ZINC = vec3( 48.500388, 65.294118, 63.934599 );\n\n //\ttruncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n //\tcalculate the noise\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n P = P.xzxz * P.yyww;\n vec3 lowz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) );\n vec3 highz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) );\n lowz_hash_0 = fract( P * lowz_mod.xxxx );\n highz_hash_0 = fract( P * highz_mod.xxxx );\n lowz_hash_1 = fract( P * lowz_mod.yyyy );\n highz_hash_1 = fract( P * highz_mod.yyyy );\n lowz_hash_2 = fract( P * lowz_mod.zzzz );\n highz_hash_2 = fract( P * highz_mod.zzzz );\n}\n\n\n//\n//\tPerlin Noise 3D ( gradient noise )\n//\tReturn value range of -1.0->1.0\n//\thttp://briansharpe.files.wordpress.com/2011/11/perlinsample.jpg\n//\nfloat perlin3D( vec3 P )\n{\n P*=8.0;\n\n //\testablish our grid cell and unit position\n vec3 Pi = floor(P);\n vec3 Pf = P - Pi;\n vec3 Pf_min1 = Pf - 1.0;\n\n //\n //\tclassic noise.\n //\trequires 3 random values per point.\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1;\n FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n //SGPP_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n\n //\tcalculate the gradients\n vec4 grad_x0 = hashx0 - 0.49999;\n vec4 grad_y0 = hashy0 - 0.49999;\n vec4 grad_z0 = hashz0 - 0.49999;\n vec4 grad_x1 = hashx1 - 0.49999;\n vec4 grad_y1 = hashy1 - 0.49999;\n vec4 grad_z1 = hashz1 - 0.49999;\n vec4 grad_results_0 = inversesqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 );\n vec4 grad_results_1 = inversesqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 );\n\n\n //\tClassic Perlin Interpolation\n vec3 blend = Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n float final = dot( res0, blend2.zxzx * blend2.wwyy );\n final *= 1.1547005383792515290182975610039;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.75)\n return final;\n\n\n\n}\n",}; + + +const sgOp = new CGL.ShaderGraphOp(this, attachments.perlin_glsl); + + +}; + +Ops.Dev.Gl.ShaderGraph.PerlinNoise3D.prototype = new CABLES.Op(); +CABLES.OPS["bba73873-2b81-419b-899f-323e7dc159ea"]={f:Ops.Dev.Gl.ShaderGraph.PerlinNoise3D,objName:"Ops.Dev.Gl.ShaderGraph.PerlinNoise3D"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Pow +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Pow = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec = op.inObject("input", null, "sg_float"), + inpow = op.inObject("power", null, "sg_float"), + outvec = op.outObject("result", null, "sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc = "pow"; + +inpow.onLinkChanged = () => +{ + return invec.onLinkChanged = () => + { + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([invec, inpow], [invec, inpow, outvec]); + }; +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Pow.prototype = new CABLES.Op(); +CABLES.OPS["eab198bd-b8e9-467e-ad05-8175b41a9732"]={f:Ops.Dev.Gl.ShaderGraph.Pow,objName:"Ops.Dev.Gl.ShaderGraph.Pow"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.SG_Noise +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.SG_Noise = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"shader_frag":"\nfloat noise(vec2 a)\n{\n return fract(sin(dot(a, vec2(12.9898, 78.233))) * 43758.5453);\n}",}; +new CGL.ShaderGraphOp(this,attachments.shader_frag); + + +}; + +Ops.Dev.Gl.ShaderGraph.SG_Noise.prototype = new CABLES.Op(); +CABLES.OPS["7400ed85-8931-4669-b0bc-620623860864"]={f:Ops.Dev.Gl.ShaderGraph.SG_Noise,objName:"Ops.Dev.Gl.ShaderGraph.SG_Noise"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.SampleTexture +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.SampleTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"sampletex_frag":"vec4 sampleTex(sampler2D tex,vec2 texCoord)\n{\n return texture(tex,texCoord);\n}",}; +const sgOp = new CGL.ShaderGraphOp(this, attachments.sampletex_frag); + + +}; + +Ops.Dev.Gl.ShaderGraph.SampleTexture.prototype = new CABLES.Op(); +CABLES.OPS["3eb89130-b02e-432c-9633-d1ab4635a783"]={f:Ops.Dev.Gl.ShaderGraph.SampleTexture,objName:"Ops.Dev.Gl.ShaderGraph.SampleTexture"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.SetPosition +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.SetPosition = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"position_vert":"void setPosition(vec4 pos)\n{\n // vec3 tangent=attrTangent;\n pos.w=1.0;\n // vec3 bitangent=attrBiTangent;\n mat4 mMatrix=modelMatrix;\n\n {{MODULE_VERTEX_POSITION}}\n\n texCoord=attrTexCoord;\n\n gl_Position = projMatrix * (viewMatrix*mMatrix) * pos;\n}",}; +const sgOp = new CGL.ShaderGraphOp(this, attachments.position_vert); + + +}; + +Ops.Dev.Gl.ShaderGraph.SetPosition.prototype = new CABLES.Op(); +CABLES.OPS["8772c543-f2f6-4684-8792-2a615cf861b0"]={f:Ops.Dev.Gl.ShaderGraph.SetPosition,objName:"Ops.Dev.Gl.ShaderGraph.SetPosition"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Sin +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Sin = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec=op.inObject("input",null,"sg_float"), + outvec=op.outObject("result",null,"sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc="sin"; + +invec.onLinkChanged=()=> +{ + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([invec],[invec,outvec]); + +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Sin.prototype = new CABLES.Op(); +CABLES.OPS["19e60bc9-4ddf-4c24-a6e6-ca48a3856676"]={f:Ops.Dev.Gl.ShaderGraph.Sin,objName:"Ops.Dev.Gl.ShaderGraph.Sin"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Sum +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Sum = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inputa = op.inObject("a", null, "sg_float"), + inputb = op.inObject("b", null, "sg_float"), + outvec = op.outObject("result", null, "sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderCodeOperator = "+"; + +inputa.setUiAttribs({ "ignoreObjTypeErrors": true }); +inputb.setUiAttribs({ "ignoreObjTypeErrors": true }); + +inputb.onLinkChanged = +inputa.onLinkChanged = () => +{ + CGL.ShaderGraphOp.getMaxGenTypeFromPorts([inputb, inputa], [inputb, inputa, outvec]); +}; + + +}; + +Ops.Dev.Gl.ShaderGraph.Sum.prototype = new CABLES.Op(); +CABLES.OPS["5bca04f9-bb17-4bd0-bcd1-0427ee3b2933"]={f:Ops.Dev.Gl.ShaderGraph.Sum,objName:"Ops.Dev.Gl.ShaderGraph.Sum"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.Swizzle +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.Swizzle = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inp = op.inObject("vec", null, "sg_vec4", null, "sg_vec4"), + inType = op.inSwitch("Type", ["float", "vec2", "vec3", "vec4"], "vec4"), + xChannel = op.inSwitch("X", ["X", "Y", "Z", "W"], "X"), + yChannel = op.inSwitch("Y", ["X", "Y", "Z", "W"], "Y"), + zChannel = op.inSwitch("Z", ["X", "Y", "Z", "W"], "Z"), + wChannel = op.inSwitch("W", ["X", "Y", "Z", "W"], "W"), + result = op.outObject("Result", null, "sg_vec4"); + +inType.onChange = + xChannel.onChange = + yChannel.onChange = + zChannel.onChange = + wChannel.onChange = updateUi; + +const sgOp = new CGL.ShaderGraphOp(this); +updateUi(); + +function updateUi() +{ + yChannel.setUiAttribs({ "greyout": inType.get() == "float" }); + zChannel.setUiAttribs({ "greyout": inType.get() == "float" || inType.get() == "vec2" }); + wChannel.setUiAttribs({ "greyout": inType.get() == "float" || inType.get() == "vec2" || inType.get() == "vec3" }); + + result.setUiAttribs({ "objType": "sg_" + inType.get() }); + + let swizzStr = xChannel.get().toLowerCase(); + let str = ""; + + // if (inType.get() == "float") + // { + // str = swizzStr; + // str = inType.get() + " swizzle(" + inType.get() + " vec){return " + vec + "; }"; + // } + // else + { + if (inType.get() == "float") swizzStr = "vec"; + if (inType.get() == "vec2" || inType.get() == "vec3" || inType.get() == "vec4") swizzStr += "vec." + yChannel.get().toLowerCase(); + if (inType.get() == "vec3" || inType.get() == "vec4") swizzStr += "vec." + zChannel.get().toLowerCase(); + if (inType.get() == "vec4") swizzStr += "vec." + wChannel.get().toLowerCase(); + + str = inType.get() + " swizzle(" + inType.get() + " vec){return " + swizzStr + "; }"; + } + + // console.log(str); + sgOp.parseCode(str); + // console.log(sgOp.info); + sgOp.updateGraph(); +} + + +}; + +Ops.Dev.Gl.ShaderGraph.Swizzle.prototype = new CABLES.Op(); +CABLES.OPS["8a25f41b-6ded-4aaf-bd1f-219d644040d0"]={f:Ops.Dev.Gl.ShaderGraph.Swizzle,objName:"Ops.Dev.Gl.ShaderGraph.Swizzle"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.TexCoord +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.TexCoord = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +op.outObject("texCoord",null,"sg_vec2"); +op.shaderVar="texCoord"; + +new CGL.ShaderGraphOp(this); + + +}; + +Ops.Dev.Gl.ShaderGraph.TexCoord.prototype = new CABLES.Op(); +CABLES.OPS["3a7e40e5-29bd-4e17-abbd-1570ab4cfb1d"]={f:Ops.Dev.Gl.ShaderGraph.TexCoord,objName:"Ops.Dev.Gl.ShaderGraph.TexCoord"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.ShaderGraph.VertexPosition +// +// ************************************************************** + +Ops.Dev.Gl.ShaderGraph.VertexPosition = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +op.outObject("vposition", null, "sg_vec3"); +op.shaderVar = "vPosition"; + +new CGL.ShaderGraphOp(this); + + +}; + +Ops.Dev.Gl.ShaderGraph.VertexPosition.prototype = new CABLES.Op(); +CABLES.OPS["b2d21d20-a29a-441d-9ffa-0535a771042f"]={f:Ops.Dev.Gl.ShaderGraph.VertexPosition,objName:"Ops.Dev.Gl.ShaderGraph.VertexPosition"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.Shadergraph.Length +// +// ************************************************************** + +Ops.Dev.Gl.Shadergraph.Length = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + invec = op.inObject("input", null, "sg_genType"), + outvec = op.outObject("result", null, "sg_float"); + +new CGL.ShaderGraphOp(this); +op.shaderFunc = "length"; + +invec.onLinkChanged = () => +{ + invec.copyLinkedUiAttrib("objType", invec); +}; + + +}; + +Ops.Dev.Gl.Shadergraph.Length.prototype = new CABLES.Op(); +CABLES.OPS["27f420c0-aecc-447c-927c-3536abc8849a"]={f:Ops.Dev.Gl.Shadergraph.Length,objName:"Ops.Dev.Gl.Shadergraph.Length"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.Shadergraph.Shadergraph +// +// ************************************************************** + +Ops.Dev.Gl.Shadergraph.Shadergraph = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Exec"), + inFrag = op.inObject("Fragment", null, "sg_void"), + inVertex = op.inObject("Vertex", null, "sg_void"), + next = op.outTrigger("Next"), + outshader = op.outObject("Shader"), + outSrcFrag = op.outString("Source Fragment"), + outSrcVert = op.outString("Source Vertex"); + +const cgl = op.patch.cgl; +const sg = new CGL.ShaderGraph(this, inFrag, inVertex); +const shader = new CGL.Shader(cgl, op.name); +let needsUpdate = true; + +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG", "MODULE_BEGIN_VERTEX"]); +outshader.set(shader); + +let uniformTextures = []; + +sg.on("compiled", () => +{ + outSrcFrag.set(sg.getSrcFrag()); + outSrcVert.set(sg.getSrcVert()); + needsUpdate = true; +}); + +inExec.onTriggered = () => +{ + if (needsUpdate) + { + uniformTextures = []; + shader.removeAllUniforms(); + + shader.setSource(sg.getSrcVert(), sg.getSrcFrag()); + + const unis = sg.getUniforms(); + for (let i = 0; i < unis.length; i++) + { + const su = unis[i]; + shader.removeUniform(su.name); + + let uni = null; + if (su.ports) uni = shader.addUniformFrag(su.type, su.name, su.ports[0], su.ports[1], su.ports[2], su.ports[3]); + else console.log("uni has no ports", su.ports); + + if (su.type == "t")uniformTextures.push({ "uni": uni, "port": su.ports[0] }); + } + needsUpdate = false; + } + + for (let i = 0; i < uniformTextures.length; i++) + shader.pushTexture(uniformTextures[i].uni, uniformTextures[i].port.get()); + + cgl.pushShader(shader); + + next.trigger(); + + shader.popTextures(); + cgl.popShader(); +}; + + +}; + +Ops.Dev.Gl.Shadergraph.Shadergraph.prototype = new CABLES.Op(); +CABLES.OPS["aef36334-477a-4173-af03-d4e162d31bfb"]={f:Ops.Dev.Gl.Shadergraph.Shadergraph,objName:"Ops.Dev.Gl.Shadergraph.Shadergraph"}; + + + + +// ************************************************************** +// +// Ops.Dev.Gl.TextureEffects.ChromaKeyAlpha +// +// ************************************************************** + +Ops.Dev.Gl.TextureEffects.ChromaKeyAlpha = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"chromakey_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI vec3 color;\nUNI float weightMul;\n\n\n{{MODULE_BEGIN_FRAG}}\n\nvec3 rgb2hsv(vec4 rgb)\n{\n\tfloat Cmax = max(rgb.r, max(rgb.g, rgb.b));\n\tfloat Cmin = min(rgb.r, min(rgb.g, rgb.b));\n float delta = Cmax - Cmin;\n\n\tvec3 hsv = vec3(0., 0., Cmax);\n\n\tif (Cmax > Cmin)\n\t{\n\t\thsv.y = delta / Cmax;\n\n\t\tif (rgb.r == Cmax)\n\t\t\thsv.x = (rgb.g - rgb.b) / delta;\n\t\telse\n\t\t{\n\t\t\tif (rgb.g == Cmax)\n\t\t\t\thsv.x = 2. + (rgb.b - rgb.r) / delta;\n\t\t\telse\n\t\t\t\thsv.x = 4. + (rgb.r - rgb.g) / delta;\n\t\t}\n\t\thsv.x = fract(hsv.x / 6.);\n\t}\n\treturn hsv;\n}\n\nfloat chromaKey(vec4 col)\n{\n vec4 backgroundColor = vec4(color,0.0);\n vec3 weights = vec3(4.*weightMul, 1., 2.*weightMul);\n\n vec3 hsv = rgb2hsv(col);\n vec3 target = rgb2hsv(backgroundColor);\n float dist = length(weights * (target - hsv));\n\n return 1. - clamp(3. * dist - 1.5, 0., 1.);\n}\n\nfloat random(vec2 co)\n{\n return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (43758.5453));\n}\n\nvoid main()\n{\n vec4 col=vec4(1.0,1.0,0.0,1.0);\n {{MODULE_COLOR}}\n col=texture(tex,texCoord);\n\n // #ifdef MODE_R\n // float maxrb = max( col.g, col.b );\n // float perc = min(1.0,(col.r*weightMul-maxrb)*2.0);\n // #endif\n\n // #ifdef MODE_G\n // float maxrb = max( col.r, col.b );\n // float perc = min(1.0,(col.g*weightMul-maxrb)*2.0);\n // col.g=min(maxrb,col.g);\n // #endif\n\n // #ifdef MODE_COLOR\n float perc=chromaKey(col);\n // #endif\n\n float len=length(col);\n col=normalize(col)*len;\n\n col.a=1.0-perc;\n outColor= col;\n}\n\n\n\n",}; +const + render = op.inTrigger("Render"), + w = op.inValueSlider("WeightMul", 0.6), + inR = op.inFloat("R", Math.random()), + inG = op.inFloat("G", Math.random()), + inB = op.inFloat("B", Math.random()), + trigger = op.outTrigger("Next"); + +op.setPortGroup("Color", [inR, inG, inB]); +inR.setUiAttribs({ "colorPick": true }); + +const + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.chromakey_frag); + +new CGL.Uniform(shader, "t", "tex", 0), +new CGL.Uniform(shader, "3f", "color", inR, inG, inB), +new CGL.Uniform(shader, "f", "weightMul", w); + +render.onTriggered = function () +{ + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Dev.Gl.TextureEffects.ChromaKeyAlpha.prototype = new CABLES.Op(); +CABLES.OPS["5614f8a5-7267-4ed6-9cf9-b4a3c7531f7d"]={f:Ops.Dev.Gl.TextureEffects.ChromaKeyAlpha,objName:"Ops.Dev.Gl.TextureEffects.ChromaKeyAlpha"}; + + + + +// ************************************************************** +// +// Ops.Dev.GlArrayCurlNoise +// +// ************************************************************** + +Ops.Dev.GlArrayCurlNoise = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"curlnoise_frag":"UNI float z;\nUNI float x;\nUNI float y;\nUNI float scale;\nIN vec2 texCoord;\nUNI sampler2D tex;\n\nUNI float amount;\nUNI float timeDelta;\nUNI float offset;\n\n{{CGL.BLENDMODES}}\n{{CGL.RANDOM_TEX}}\n\nfloat Interpolation_C2( float x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } // 6x^5-15x^4+10x^3\t( Quintic Curve. As used by Perlin in Improved Noise. http://mrl.nyu.edu/~perlin/paper445.pdf )\nvec2 Interpolation_C2( vec2 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec3 Interpolation_C2( vec3 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2( vec4 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2_InterpAndDeriv( vec2 x ) { return x.xyxy * x.xyxy * ( x.xyxy * ( x.xyxy * ( x.xyxy * vec2( 6.0, 0.0 ).xxyy + vec2( -15.0, 30.0 ).xxyy ) + vec2( 10.0, -60.0 ).xxyy ) + vec2( 0.0, 30.0 ).xxyy ); }\nvec3 Interpolation_C2_Deriv( vec3 x ) { return x * x * (x * (x * 30.0 - 60.0) + 30.0); }\n\n\nvoid FAST32_hash_3D( \tvec3 gridcell,\n out vec4 lowz_hash_0,\n out vec4 lowz_hash_1,\n out vec4 lowz_hash_2,\n out vec4 highz_hash_0,\n out vec4 highz_hash_1,\n out vec4 highz_hash_2\t)\t\t//\tgenerates 3 random numbers for each of the 8 cell corners\n{\n // gridcell is assumed to be an integer coordinate\n\n //\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const vec3 SOMELARGEFLOATS = vec3( 635.298681, 682.357502, 668.926525 );\n const vec3 ZINC = vec3( 48.500388, 65.294118, 63.934599 );\n\n //\ttruncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n //\tcalculate the noise\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n P = P.xzxz * P.yyww;\n vec3 lowz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) );\n vec3 highz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) );\n lowz_hash_0 = fract( P * lowz_mod.xxxx );\n highz_hash_0 = fract( P * highz_mod.xxxx );\n lowz_hash_1 = fract( P * lowz_mod.yyyy );\n highz_hash_1 = fract( P * highz_mod.yyyy );\n lowz_hash_2 = fract( P * lowz_mod.zzzz );\n highz_hash_2 = fract( P * highz_mod.zzzz );\n}\n\n//\n//\tPerlin Noise 3D ( gradient noise )\n//\tReturn value range of -1.0->1.0\n//\thttp://briansharpe.files.wordpress.com/2011/11/perlinsample.jpg\n//\nfloat Perlin3D( vec3 P )\n{\n //\testablish our grid cell and unit position\n vec3 Pi = floor(P);\n vec3 Pf = P - Pi;\n vec3 Pf_min1 = Pf - 1.0;\n\n#if 1\n //\n //\tclassic noise.\n //\trequires 3 random values per point. with an efficent hash function will run faster than improved noise\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1;\n FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n //SGPP_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n\n //\tcalculate the gradients\n vec4 grad_x0 = hashx0 - 0.49999;\n vec4 grad_y0 = hashy0 - 0.49999;\n vec4 grad_z0 = hashz0 - 0.49999;\n vec4 grad_x1 = hashx1 - 0.49999;\n vec4 grad_y1 = hashy1 - 0.49999;\n vec4 grad_z1 = hashz1 - 0.49999;\n vec4 grad_results_0 = inversesqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 );\n vec4 grad_results_1 = inversesqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 );\n\n#if 1\n //\tClassic Perlin Interpolation\n vec3 blend = Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n float final = dot( res0, blend2.zxzx * blend2.wwyy );\n final *= 1.1547005383792515290182975610039;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.75)\n return final;\n#else\n //\tClassic Perlin Surflet\n //\thttp://briansharpe.wordpress.com/2012/03/09/modifications-to-classic-perlin-noise/\n Pf *= Pf;\n Pf_min1 *= Pf_min1;\n vec4 vecs_len_sq = vec4( Pf.x, Pf_min1.x, Pf.x, Pf_min1.x ) + vec4( Pf.yy, Pf_min1.yy );\n float final = dot( Falloff_Xsq_C2( min( vec4( 1.0 ), vecs_len_sq + Pf.zzzz ) ), grad_results_0 ) + dot( Falloff_Xsq_C2( min( vec4( 1.0 ), vecs_len_sq + Pf_min1.zzzz ) ), grad_results_1 );\n final *= 2.3703703703703703703703703703704;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/cube(0.75)\n return final;\n#endif\n\n#else\n //\n //\timproved noise.\n //\trequires 1 random value per point. Will run faster than classic noise if a slow hashing function is used\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hash_lowz, hash_highz;\n FAST32_hash_3D( Pi, hash_lowz, hash_highz );\n //BBS_hash_3D( Pi, hash_lowz, hash_highz );\n //SGPP_hash_3D( Pi, hash_lowz, hash_highz );\n\n //\n //\t\"improved\" noise using 8 corner gradients. Faster than the 12 mid-edge point method.\n //\tKen mentions using diagonals like this can cause \"clumping\", but we'll live with that.\n //\t[1,1,1] [-1,1,1] [1,-1,1] [-1,-1,1]\n //\t[1,1,-1] [-1,1,-1] [1,-1,-1] [-1,-1,-1]\n //\n hash_lowz -= 0.5;\n vec4 grad_results_0_0 = vec2( Pf.x, Pf_min1.x ).xyxy * sign( hash_lowz );\n hash_lowz = abs( hash_lowz ) - 0.25;\n vec4 grad_results_0_1 = vec2( Pf.y, Pf_min1.y ).xxyy * sign( hash_lowz );\n vec4 grad_results_0_2 = Pf.zzzz * sign( abs( hash_lowz ) - 0.125 );\n vec4 grad_results_0 = grad_results_0_0 + grad_results_0_1 + grad_results_0_2;\n\n hash_highz -= 0.5;\n vec4 grad_results_1_0 = vec2( Pf.x, Pf_min1.x ).xyxy * sign( hash_highz );\n hash_highz = abs( hash_highz ) - 0.25;\n vec4 grad_results_1_1 = vec2( Pf.y, Pf_min1.y ).xxyy * sign( hash_highz );\n vec4 grad_results_1_2 = Pf_min1.zzzz * sign( abs( hash_highz ) - 0.125 );\n vec4 grad_results_1 = grad_results_1_0 + grad_results_1_1 + grad_results_1_2;\n\n //\tblend the gradients and return\n vec3 blend = Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n return dot( res0, blend2.zxzx * blend2.wwyy ) * (2.0 / 3.0);\t//\t(optionally) mult by (2.0/3.0) to scale to a strict -1.0->1.0 range\n#endif\n}\n\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n\n\n vec3 rnd=\n vec3(\n Perlin3D( ( (base.xyz+20.0) + vec3(x,y,z)) *scale ),\n Perlin3D( ( (base.xyz-20.0) + vec3(x,y,z)) *scale ),\n Perlin3D( ( (base.xyz+60.0) + vec3(x,y,z)) *scale )\n );\n\n\n vec3 noise=(cgl_random3(texCoord.xy)-0.5)*offset;\n rnd+=\n vec3(\n Perlin3D( ( (base.xyz+20.0+noise.x) + vec3(x,y,z)) *scale ),\n Perlin3D( ( (base.xyz-20.0+noise.y) + vec3(x,y,z)) *scale ),\n Perlin3D( ( (base.xyz+60.0+noise.z) + vec3(x,y,z)) *scale )\n );\n\n\n // rnd=\n // (\n // vec3(\n // Perlin3D( ( (base.xyz+20.0) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz-20.0) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz+60.0) + vec3(x,y,z)) *scale )\n // )+\n // vec3(\n // Perlin3D( ( (base.xyz+20.0+(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz-20.0+(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz+60.0+(offset*scale)) + vec3(x,y,z)) *scale )\n // )*0.25+\n // vec3(\n // Perlin3D( ( (base.xyz+20.0-(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz-20.0-(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz+60.0-(offset*scale)) + vec3(x,y,z)) *scale )\n // )*0.25+\n // vec3(\n // Perlin3D( ( (base.xyz+20.0-(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz-20.0+(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz+60.0-(offset*scale)) + vec3(x,y,z)) *scale )\n // )*0.25+\n // vec3(\n // Perlin3D( ( (base.xyz+20.0+(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz-20.0-(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz+60.0+(offset*scale)) + vec3(x,y,z)) *scale )\n // )*0.25\n\n // )/2.0;\n\n\n\n\n\n\n\n #ifdef MOD_NORM_SPEED\n rnd=normalize(rnd)*0.2;\n #endif\n #ifndef MOD_NORM_SPEED\n // rnd*=timeDelta;\n #endif\n\n // rnd=clamp(rnd,vec3(-0.01),vec3(0.01));\n // rnd*=0.01;\n // float cl=0.03;\n // rnd.x=clamp(rnd.x,-cl,cl);\n // rnd.y=clamp(rnd.y,-cl,cl);\n // rnd.z=clamp(rnd.z,-cl,cl);\n\n rnd*=timeDelta;\n\n vec3 coord=base.xyz+rnd;\n // coord.x+=( Perlin3D( ( (base.xyz+20.0) + vec3(x,y,z)) *scale )*timeDelta);\n // coord.y+=( Perlin3D( ( (base.xyz-20.0) + vec3(x,y,z)) *scale )*timeDelta);\n // coord.z+=( Perlin3D( ( (base.xyz+30.0) + vec3(x,y,z)) *scale )*timeDelta);\n\n // additional noise on top\n // coord.x+=Perlin3D(vec3(texCoord.x))*0.001;\n // coord.y+=Perlin3D(vec3(texCoord.y))*0.001;\n // coord.z+=Perlin3D(vec3(texCoord.y,texCoord.x,texCoord.y))*0.001;\n\n\n outColor=vec4(coord,1.0);\n\n\n // outColor=cgl_blend(base,col,amount);\n}\n",}; +const + render = op.inTrigger("Render"), + inTex = op.inTexture("GlArray"), + scale = op.inValue("Scale", 1), + time = op.inValue("Time", 0), + + inNormSpeed = op.inBool("Normalize Speed", false), + + x = op.inValue("X", 0), + y = op.inValue("Y", 0), + z = op.inValue("Z", 0), + a = op.inValue("a", 1), + + inOffset = op.inFloatSlider("offset", 0), + trigger = op.outTrigger("trigger"), + outTex = op.outTexture("Result"); + +let lastTime = time.get(); +render.onTriggered = dorender; + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); +const texMath = new CGL.ShaderTextureMath(cgl, op.objName, { "texturePort": inTex }); + +shader.setSource(shader.getDefaultVertexShader(), attachments.curlnoise_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniformR = new CGL.Uniform(shader, "f", "x", x), + uniformG = new CGL.Uniform(shader, "f", "y", y), + uniformB = new CGL.Uniform(shader, "f", "z", z), + uniformA = new CGL.Uniform(shader, "f", "scale", scale), + uniformTimeDelta = new CGL.Uniform(shader, "f", "timeDelta", 0), + uniforoffs = new CGL.Uniform(shader, "f", "offset", inOffset); + +inNormSpeed.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("MOD_NORM_SPEED", inNormSpeed.get()); +} + +function dorender() +{ + uniformTimeDelta.set(time.get() - lastTime); + lastTime = time.get(); + + outTex.set(null); + const finTex = texMath.render(shader); + outTex.set(finTex); + trigger.trigger(); +} + + +}; + +Ops.Dev.GlArrayCurlNoise.prototype = new CABLES.Op(); +CABLES.OPS["25467f40-8f78-4dca-ba6c-951ae49cac59"]={f:Ops.Dev.GlArrayCurlNoise,objName:"Ops.Dev.GlArrayCurlNoise"}; + + + + +// ************************************************************** +// +// Ops.Dev.GlArrayMath +// +// ************************************************************** + +Ops.Dev.GlArrayMath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rgbMath_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D tex2;\nUNI float r;\nUNI float g;\nUNI float b;\nUNI float a;\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n vec4 v=vec4(r,g,b,a);\n\n #ifdef MOD_USE_TEX\n v=texture(tex2,texCoord);\n #endif\n\n\n #ifdef MOD_OP_SUB_CX\n #ifdef MOD_CHAN_R\n col.r=col.r-v.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g=col.g-v.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b=col.b-v.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a=col.a-v.a;\n #endif\n #endif\n\n #ifdef MOD_OP_SUB_XC\n #ifdef MOD_CHAN_R\n col.r=v.r-col.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g=v.g-col.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b=v.b-col.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a=v.a-col.a;\n #endif\n #endif\n\n #ifdef MOD_OP_ADD\n #ifdef MOD_CHAN_R\n col.r+=v.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g+=v.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b+=v.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a+=v.a;\n #endif\n #endif\n\n #ifdef MOD_OP_MUL\n #ifdef MOD_CHAN_R\n col.r*=v.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g*=v.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b*=v.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a*=v.a;\n #endif\n #endif\n\n #ifdef MOD_OP_DIV_XC\n #ifdef MOD_CHAN_R\n col.r=v.r/col.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g=v.g/col.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b=v.b/col.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a=v.a/col.a;\n #endif\n #endif\n\n #ifdef MOD_OP_DIV_CX\n #ifdef MOD_CHAN_R\n col.r=col.r/v.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g=col.g/v.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b=col.b/v.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a=col.a/v.a;\n #endif\n #endif\n\n #ifdef MOD_OP_MODULO\n col=mod(col,v);\n #endif\n\n\n outColor= col;\n}\n",}; +const + render = op.inTrigger("Render"), + inTex = op.inTexture("GlArray"), + inOp = op.inSwitch("Operation", ["c-x", "x-c", "c+x", "c*x", "x/c", "c/x", "c%x"], "c*x"), + chanR = op.inBool("R Active", true), + chanG = op.inBool("G Active", true), + chanB = op.inBool("B Active", true), + chanA = op.inBool("A Active", false), + inTex2 = op.inTexture("Value"), + r = op.inValue("r", 1), + g = op.inValue("g", 1), + b = op.inValue("b", 1), + a = op.inValue("a", 1), + trigger = op.outTrigger("trigger"), + outTex = op.outTexture("Result"); + +render.onTriggered = dorender; + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); +const texMath = new CGL.ShaderTextureMath(cgl, op.objName, { "texturePort": inTex }); + +shader.setSource(shader.getDefaultVertexShader(), attachments.rgbMath_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const textureUniform2 = new CGL.Uniform(shader, "t", "tex2", 1); + +const uniformR = new CGL.Uniform(shader, "f", "r", r); +const uniformG = new CGL.Uniform(shader, "f", "g", g); +const uniformB = new CGL.Uniform(shader, "f", "b", b); +const uniformA = new CGL.Uniform(shader, "f", "a", a); + +chanR.onChange = + chanG.onChange = + chanB.onChange = + chanA.onChange = + inTex2.onChange = + inOp.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("MOD_OP_SUB_CX", inOp.get() === "c-x"); + shader.toggleDefine("MOD_OP_SUB_XC", inOp.get() === "x-c"); + + shader.toggleDefine("MOD_OP_ADD", inOp.get() === "c+x"); + shader.toggleDefine("MOD_OP_MUL", inOp.get() === "c*x"); + + shader.toggleDefine("MOD_OP_DIV_XC", inOp.get() === "x/c"); + shader.toggleDefine("MOD_OP_DIV_CX", inOp.get() === "c/x"); + + shader.toggleDefine("MOD_OP_MODULO", inOp.get() === "c%x"); + + shader.toggleDefine("MOD_USE_TEX", inTex2.get()); + + shader.toggleDefine("MOD_CHAN_R", chanR.get()); + r.setUiAttribs({ "greyout": !chanR.get() || inTex2.get() }); + + shader.toggleDefine("MOD_CHAN_G", chanG.get()); + g.setUiAttribs({ "greyout": !chanG.get() || inTex2.get() }); + + shader.toggleDefine("MOD_CHAN_B", chanB.get()); + b.setUiAttribs({ "greyout": !chanB.get() || inTex2.get() }); + + shader.toggleDefine("MOD_CHAN_A", chanA.get()); + a.setUiAttribs({ "greyout": !chanA.get() || inTex2.get() }); +} + +function dorender() +{ + shader.popTextures(); + if (inTex.get()) shader.pushTexture(textureUniform, inTex.get().tex); + if (inTex2.get()) shader.pushTexture(textureUniform2, inTex2.get().tex); + + const finTex = texMath.render(shader); + + outTex.set(null); + outTex.set(finTex); + trigger.trigger(); +} + + +}; + +Ops.Dev.GlArrayMath.prototype = new CABLES.Op(); +CABLES.OPS["0204b33a-2917-4eea-9c31-3b185e4fe638"]={f:Ops.Dev.GlArrayMath,objName:"Ops.Dev.GlArrayMath"}; + + + + +// ************************************************************** +// +// Ops.Dev.GlArrayModulo +// +// ************************************************************** + +Ops.Dev.GlArrayModulo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rgbMath_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\n\nUNI float modX;\nUNI float modY;\nUNI float modZ;\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n\n #ifdef MOD_MODULO_X\n col.x=mod(col.x,modX);\n #endif\n\n #ifdef MOD_MODULO_Z\n col.y=mod(col.y,modY);\n #endif\n\n #ifdef MOD_MODULO_Z\n col.z=mod(col.z,modZ);\n #endif\n\n outColor= col;\n}\n",}; +const + render = op.inTrigger("Render"), + inTex = op.inTexture("GlArray"), + + inDoModX = op.inBool("X",true), + inModX = op.inValue("Modulo X", 1), + + inDoModY = op.inBool("Y",true), + inModY = op.inValue("Modulo Y", 1), + + inDoModZ = op.inBool("Z",true), + inModZ = op.inValue("Modulo Z", 1), + + trigger = op.outTrigger("trigger"), + outTex = op.outTexture("Result"); + +render.onTriggered = dorender; + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); +const texMath = new CGL.ShaderTextureMath(cgl, op.objName, { "texturePort": inTex }); + +shader.setSource(shader.getDefaultVertexShader(), attachments.rgbMath_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniModX = new CGL.Uniform(shader, "f", "modX", inModX), + uniModY = new CGL.Uniform(shader, "f", "modY", inModY), + uniModZ = new CGL.Uniform(shader, "f", "modZ", inModZ); + +inDoModX.onChange = +inDoModY.onChange = +inDoModZ.onChange = updateDefines; + +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("MOD_MODULO_X", inDoModX.get()); + inModX.setUiAttribs({ "greyout": !inDoModX.get() }); + + shader.toggleDefine("MOD_MODULO_Y", inDoModY.get()); + inModY.setUiAttribs({ "greyout": !inDoModY.get() }); + + shader.toggleDefine("MOD_MODULO_Z", inDoModZ.get()); + inModZ.setUiAttribs({ "greyout": !inDoModZ.get() }); +} + +function dorender() +{ + const finTex = texMath.render(shader); + outTex.set(finTex); + trigger.trigger(); +} + + +}; + +Ops.Dev.GlArrayModulo.prototype = new CABLES.Op(); +CABLES.OPS["a017cc92-2354-45df-820f-ed6aad3bbb0e"]={f:Ops.Dev.GlArrayModulo,objName:"Ops.Dev.GlArrayModulo"}; + + + + +// ************************************************************** +// +// Ops.Dev.GlArrayNormalize +// +// ************************************************************** + +Ops.Dev.GlArrayNormalize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rgbMath_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\n\nUNI float modX;\nUNI float modY;\nUNI float modZ;\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n\ncol.xyz=normalize(col.xyz);\n\n outColor= col;\n}\n",}; +const + render = op.inTrigger("Render"), + inTex = op.inTexture("GlArray"), + + trigger = op.outTrigger("trigger"), + outTex = op.outTexture("Result"); + +render.onTriggered = dorender; + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); +const texMath = new CGL.ShaderTextureMath(cgl, op.objName, { "texturePort": inTex }); + +shader.setSource(shader.getDefaultVertexShader(), attachments.rgbMath_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + + + +function dorender() +{ + const finTex = texMath.render(shader); + outTex.set(finTex); + trigger.trigger(); +} + + +}; + +Ops.Dev.GlArrayNormalize.prototype = new CABLES.Op(); +CABLES.OPS["c7ee53af-2d34-4ca9-bcea-5cbf7f5aab4e"]={f:Ops.Dev.GlArrayNormalize,objName:"Ops.Dev.GlArrayNormalize"}; + + + + +// ************************************************************** +// +// Ops.Dev.GlArrayRandom +// +// ************************************************************** + +Ops.Dev.GlArrayRandom = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"randoms_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\n\nUNI float seed;\n\nUNI float rmin;\nUNI float rmax;\nUNI float gmin;\nUNI float gmax;\nUNI float bmin;\nUNI float bmax;\n\n{{CGL.BLENDMODES}}\n{{MODULES_HEAD}}\n\n{{CGL.RANDOM_TEX}}\n\nfloat map(float value, float min1, float max1, float min2, float max2) {\n return min2 + (value - min1) * (max2 - min2) / (max1 - min1);\n}\n\n\nvoid main()\n{\n vec4 rnd=vec4((cgl_random3(texCoord.xy + vec2(seed))), 1.0);\n\n rnd.r=map(rnd.x, 0., 1., rmin, rmax);\n rnd.y=map(rnd.y, 0., 1., gmin, gmax);\n rnd.z=map(rnd.z, 0., 1., bmin, bmax);\n rnd.a=1.0;\n\n // vec4 base¿=texture(tex,texCoord);\n // vec4 col¿=vec4( _blend(base.rgb,rnd.rgb) ,1.0);\n\n // outColor=vec4( mix( col.rgb, base.rgb ,1.0-base.a*amount),1.0);\n outColor=rnd;\n}",}; +const + render = op.inTrigger("Render"), + next = op.outTrigger("Next"), + inNum = op.inInt("Length", 10000), + inLayout = op.inSwitch("Layout", ["Square", "Vertical", "Horizontal"], "Square"), + + inRMin = op.inFloat("X Min", 0), + inRMax = op.inFloat("X Max", 1), + + inGMin = op.inFloat("Y Min", 0), + inGMax = op.inFloat("Y Max", 1), + + inBMin = op.inFloat("Z Min", 0), + inBMax = op.inFloat("Z Max", 1), + + inSeed = op.inFloat("Seed", 1), + + outTex = op.outTexture("Result"); + +render.onTriggered = dorender; + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); +let texMath = null;// = new CGL.ShaderTextureMath(cgl, op.objName, { "width":100,"height":100 }); + +updateSize(); + +shader.setSource(shader.getDefaultVertexShader(), attachments.randoms_frag); +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniformRMin = new CGL.Uniform(shader, "f", "rmin", inRMin), + uniformRMax = new CGL.Uniform(shader, "f", "rmax", inRMax), + uniformGMin = new CGL.Uniform(shader, "f", "gmin", inGMin), + uniformGMax = new CGL.Uniform(shader, "f", "gmax", inGMax), + uniformBMin = new CGL.Uniform(shader, "f", "bmin", inBMin), + uniformBMax = new CGL.Uniform(shader, "f", "bmax", inBMax), + uniformSeed = new CGL.Uniform(shader, "f", "seed", inSeed); + +updateDefines(); + +inLayout.onChange = + inNum.onChange = updateSize; + +function updateSize() +{ + let w = Math.ceil(Math.sqrt(inNum.get())); // square texture size + let h = w; + const max = op.patch.cgl.gl.getParameter(op.patch.cgl.gl.MAX_TEXTURE_SIZE); + + if (inLayout.get() === "Vertical" || inLayout.get() === "Horizontal") + { + if (inNum.get() < max) + { + w = 1; + h = inNum.get(); + } + else + { + w = Math.ceil(inNum.get() / max); + h = max; + } + } + + if (inLayout.get() === "Horizontal") + { + let x = w; + w = h; + h = x; + } + + texMath = new CGL.ShaderTextureMath(cgl, op.objName, { "width": w, "height": h }); +} + +function updateDefines() +{ + +} + +function dorender() +{ + const finTex = texMath.render(shader); + outTex.set(finTex); + next.trigger(); +} + + +}; + +Ops.Dev.GlArrayRandom.prototype = new CABLES.Op(); +CABLES.OPS["041f5b18-74b4-4a03-9987-4451bd334464"]={f:Ops.Dev.GlArrayRandom,objName:"Ops.Dev.GlArrayRandom"}; + + + + +// ************************************************************** +// +// Ops.Dev.GlPatch +// +// ************************************************************** + +Ops.Dev.GlPatch = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + refresh = op.inTriggerButton("Refresh"), + debug = op.inTriggerButton("debug"), + next = op.inTrigger("Next"); + +const p = new CABLES.GLGUI.GlPatch(op.patch.cgl); +const api = new CABLES.GLGUI.GlPatchAPI(op.patch, p); + +let firstTime = true; + +refresh.onTriggered = function () +{ + api.reset(); +}; + +render.onTriggered = function () +{ + if (firstTime) + { + api.reset(); + firstTime = false; + } + + p.render( + op.patch.cgl.canvasWidth, + op.patch.cgl.canvasHeight + ); + next.trigger(); +}; + +debug.onTriggered = () => +{ + let count = 0; + for (const i in p._glOpz) + { + p._glOpz[i].updateVisible(); + p._glOpz[i].updatePosition(); + + for (const k in p._glOpz[i]._links) + { + count++; + if (p._glOpz[i]._links[k]) p._glOpz[i]._links[k].update(); + console.log(p._glOpz[i]._links[k]); + + p._glOpz[i]._links[k]._cable.updateLineStyle(); + p._glOpz[i]._links[k]._cable._updateLinePos(); + } + } +}; + + +}; + +Ops.Dev.GlPatch.prototype = new CABLES.Op(); +CABLES.OPS["ad6e3e6b-9d88-4378-b09f-6b57999d4f91"]={f:Ops.Dev.GlPatch,objName:"Ops.Dev.GlPatch"}; + + + + +// ************************************************************** +// +// Ops.Dev.GltfExplodeNodes +// +// ************************************************************** + +Ops.Dev.GltfExplodeNodes = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Update"), + inFilter = op.inString("Filter", ""), + + inSeed = op.inFloat("Seed", 1), + + axisX = op.inFloat("Axis X", 0), + axisY = op.inFloat("Axis Y", 0), + axisZ = op.inFloat("Axis Z", 0), + + rotationX = op.inFloat("Rotation X", 0), + rotationY = op.inFloat("Rotation Y", 0), + rotationZ = op.inFloat("Rotation Z", 0), + + pivotOffsetX = op.inFloat("Random Pivot Offset X", 0), + pivotOffsetY = op.inFloat("Random Pivot Offset Y", 0), + pivotOffsetZ = op.inFloat("Random Pivot Offset Z", 0), + + next = op.outTrigger("Next"), + outNum = op.outNumber("Found"); + +const cgl = op.patch.cgl; + +const mat = mat4.create(); + +inExec.onTriggered = function () +{ + if (!cgl.frameStore.currentScene) return; + + let node = null; + + const ax = axisX.get(); + const ay = axisY.get(); + const az = axisZ.get(); + + Math.randomSeed = inSeed.get(); + let found = 0; + + for (let i = 0; i < cgl.frameStore.currentScene.nodes.length; i++) + { + if (cgl.frameStore.currentScene.nodes[i].name.indexOf(inFilter.get()) >= 0) + { + node = cgl.frameStore.currentScene.nodes[i]; + + found++; + + const oldTrans = vec3.create(); + + mat4.getTranslation(oldTrans, node.mat); + + // const v = Math.sin(time + (Math.seededRandom() * offs)) * ampl; + // node.addTranslate = [v * ax, v * ay, v * az]; + node.addTranslate = [oldTrans[0] * axisX.get(), oldTrans[1] * axisY.get(), oldTrans[2] * axisZ.get()]; + + mat4.identity(mat); + mat4.rotateX(mat, mat, rotationX.get() * (Math.seededRandom() * 301) * CGL.DEG2RAD); + mat4.rotateY(mat, mat, rotationY.get() * (Math.seededRandom() * 301) * CGL.DEG2RAD); + mat4.rotateZ(mat, mat, rotationZ.get() * (Math.seededRandom() * 301) * CGL.DEG2RAD); + + mat4.translate(mat, mat, [(Math.seededRandom() - 0.5) * pivotOffsetX.get(), (Math.seededRandom() - 0.5) * pivotOffsetY.get(), (Math.seededRandom() - 0.5) * pivotOffsetZ.get()]); + + node.addMulMat = mat; + } + } + + outNum.set(found); + + next.trigger(); +}; + + +}; + +Ops.Dev.GltfExplodeNodes.prototype = new CABLES.Op(); +CABLES.OPS["5652c2ef-2a9f-48f6-80ef-70e154db04b5"]={f:Ops.Dev.GltfExplodeNodes,objName:"Ops.Dev.GltfExplodeNodes"}; + + + + +// ************************************************************** +// +// Ops.Dev.Graphics.Meshes.Circle_v2 +// +// ************************************************************** + +Ops.Dev.Graphics.Meshes.Circle_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Trigger"), + radius = op.inValue("radius", 0.5), + innerRadius = op.inValueSlider("innerRadius", 0), + segments = op.inValueInt("segments", 40), + percent = op.inValueSlider("percent", 1), + steps = op.inValue("steps", 0), + invertSteps = op.inValueBool("invertSteps", false), + mapping = op.inSwitch("mapping", ["flat", "round"], "flat"), + drawSpline = op.inValueBool("Spline", false), + doRender = op.inValueBool("Render", true), + trigger = op.outTrigger("next"), + geomOut = op.outObject("geometry", null, "geometry"); + +op.setPortGroup("Size", [radius, innerRadius]); +op.setPortGroup("Display", [percent, steps, invertSteps]); + +mapping.onChange = + segments.onChange = + radius.onChange = + innerRadius.onChange = + percent.onChange = + steps.onChange = + invertSteps.onChange = + drawSpline.onChange = calcLater; + +geomOut.ignoreValueSerialize = true; + +let geom = new CGL.Geometry("circle"); +let mesh = null; +const lastSegs = -1; + +let oldPrim = 0; +let shader = null; +let needsCalc = true; + +render.onTriggered = renderMesh; + +op.preRender = () => +{ + renderMesh(); +}; + +render.onLinkChanged = () => +{ + if (!trigger.isLinked()) + { + if (mesh) mesh.dispose(); + mesh = null; + geomOut.set(null); + } +}; + +function renderMesh() +{ + const cgl = op.patch.cg; + if (!CGL.TextureEffect.checkOpNotInTextureEffect(op)) return; + + if (needsCalc)calc(); + shader = cgl.getShader(); + if (!shader) return; + oldPrim = shader.glPrimitive; + + if (drawSpline.get()) shader.glPrimitive = cgl.gl.LINE_STRIP; + + if (mesh && doRender.get())mesh.render(shader); + trigger.trigger(); + + shader.glPrimitive = oldPrim; +} + +function calc() +{ + const segs = Math.max(3, Math.floor(segments.get())); + + geom.clear(); + + const faces = []; + const texCoords = []; + const vertexNormals = []; + const tangents = []; + const biTangents = []; + + let i = 0, degInRad = 0; + let oldPosX = 0, oldPosY = 0; + let oldPosXTexCoord = 0, oldPosYTexCoord = 0; + + let oldPosXIn = 0, oldPosYIn = 0; + let oldPosXTexCoordIn = 0, oldPosYTexCoordIn = 0; + + let posxTexCoordIn = 0, posyTexCoordIn = 0; + let posxTexCoord = 0, posyTexCoord = 0; + let posx = 0, posy = 0; + + const perc = Math.max(0.0, percent.get()); + const verts = []; + + if (drawSpline.get()) + { + let lastX = 0; + let lastY = 0; + const tc = []; + for (i = 0; i <= segs * perc; i++) + { + degInRad = (360 / segs) * i * CGL.DEG2RAD; + posx = Math.cos(degInRad) * radius.get(); + posy = Math.sin(degInRad) * radius.get(); + + posyTexCoord = 0.5; + + if (i > 0) + { + verts.push(lastX); + verts.push(lastY); + verts.push(0); + posxTexCoord = 1.0 - (i - 1) / segs; + + tc.push(posxTexCoord, posyTexCoord); + } + verts.push(posx); + verts.push(posy); + verts.push(0); + + lastX = posx; + lastY = posy; + } + geom.setPointVertices(verts); + } + else + if (innerRadius.get() <= 0) + { + for (i = 0; i <= segs * perc; i++) + { + degInRad = (360 / segs) * i * CGL.DEG2RAD; + posx = Math.cos(degInRad) * radius.get(); + posy = Math.sin(degInRad) * radius.get(); + + if (mapping.get() == "flat") + { + posxTexCoord = (Math.cos(degInRad) + 1.0) / 2; + posyTexCoord = 1.0 - (Math.sin(degInRad) + 1.0) / 2; + posxTexCoordIn = 0.5; + posyTexCoordIn = 0.5; + } + else if (mapping.get() == "round") + { + posxTexCoord = 1.0 - i / segs; + posyTexCoord = 0; + posxTexCoordIn = posxTexCoord; + posyTexCoordIn = 1; + } + + faces.push( + [posx, posy, 0], + [oldPosX, oldPosY, 0], + [0, 0, 0] + ); + + texCoords.push(posxTexCoord, posyTexCoord, oldPosXTexCoord, oldPosYTexCoord, posxTexCoordIn, posyTexCoordIn); + vertexNormals.push(0, 0, 1, 0, 0, 1, 0, 0, 1); + tangents.push(1, 0, 0, 1, 0, 0, 1, 0, 0); + biTangents.push(0, -1, 0, 0, -1, 0, 0, -1, 0); + + oldPosXTexCoord = posxTexCoord; + oldPosYTexCoord = posyTexCoord; + + oldPosX = posx; + oldPosY = posy; + } + + geom = CGL.Geometry.buildFromFaces(faces, "circle"); + geom.vertexNormals = vertexNormals; + geom.tangents = tangents; + geom.biTangents = biTangents; + geom.texCoords = texCoords; + } + else + { + let count = 0; + const numSteps = segs * perc; + const pos = 0; + + for (i = 0; i <= numSteps; i++) + { + count++; + + degInRad = (360 / segs) * i * CGL.DEG2RAD; + posx = Math.cos(degInRad) * radius.get(); + posy = Math.sin(degInRad) * radius.get(); + + const posxIn = Math.cos(degInRad) * innerRadius.get() * radius.get(); + const posyIn = Math.sin(degInRad) * innerRadius.get() * radius.get(); + + if (mapping.get() == "round") + { + posxTexCoord = 1.0 - i / segs; + posyTexCoord = 0; + posxTexCoordIn = posxTexCoord; + posyTexCoordIn = 1; + } + + if (steps.get() === 0.0 || + (count % parseInt(steps.get(), 10) === 0 && !invertSteps.get()) || + (count % parseInt(steps.get(), 10) !== 0 && invertSteps.get())) + { + faces.push( + [posx, posy, 0], + [oldPosX, oldPosY, 0], + [posxIn, posyIn, 0] + ); + + faces.push( + [posxIn, posyIn, 0], + [oldPosX, oldPosY, 0], + [oldPosXIn, oldPosYIn, 0] + ); + + texCoords.push( + posxTexCoord, 0, + oldPosXTexCoord, 0, + posxTexCoordIn, 1, + + posxTexCoord, 1, + oldPosXTexCoord, 0, + oldPosXTexCoordIn, 1); + + vertexNormals.push( + 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1 + ); + tangents.push( + 1, 0, 0, 1, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 0, 0 + ); + biTangents.push( + 0, -1, 0, 0, -1, 0, 0, -1, 0, + 0, -1, 0, 0, -1, 0, 0, -1, 0); + } + + oldPosXTexCoordIn = posxTexCoordIn; + oldPosYTexCoordIn = posyTexCoordIn; + + oldPosXTexCoord = posxTexCoord; + oldPosYTexCoord = posyTexCoord; + + oldPosX = posx; + oldPosY = posy; + + oldPosXIn = posxIn; + oldPosYIn = posyIn; + } + + geom = CGL.Geometry.buildFromFaces(faces, "circle"); + geom.vertexNormals = vertexNormals; + geom.tangents = tangents; + geom.biTangents = biTangents; + + if (mapping.get() == "flat") geom.mapTexCoords2d(); + else geom.texCoords = texCoords; + } + + geomOut.set(null); + geomOut.set(geom); + + if (geom.vertices.length == 0) return; + if (mesh) mesh.dispose(); + mesh = op.patch.cg.createMesh(geom); + needsCalc = false; +} + +function calcLater() +{ + needsCalc = true; +} + +op.onDelete = function () +{ + if (mesh)mesh.dispose(); +}; + + +}; + +Ops.Dev.Graphics.Meshes.Circle_v2.prototype = new CABLES.Op(); +CABLES.OPS["e1dc7dd7-269e-4f6f-8522-35e641d7af9a"]={f:Ops.Dev.Graphics.Meshes.Circle_v2,objName:"Ops.Dev.Graphics.Meshes.Circle_v2"}; + + + + +// ************************************************************** +// +// Ops.Dev.InstancedModulo +// +// ************************************************************** + +Ops.Dev.InstancedModulo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colorize_frag":"\n\n\n\nvec4 MOD_col=texture(MOD_texture,MOD_coord);\n\n#ifdef METH_ADD\ncol.rgb+=MOD_col.rgb*MOD_strength;\n#endif\n#ifdef METH_MUL\ncol.rgb*=MOD_col.rgb*MOD_strength;\n#endif\n\n#ifdef MOD_DEBUG\n col.rg=mod(MOD_coord,1.0).rg;\n#endif\n","colorize_head_frag":"\nIN vec2 MOD_coord;\n\nUNI sampler2D MOD_texture;\nUNI float MOD_strength;","displace_vert":"\n#ifdef INSTANCING\n\nvec4 MOD_p=vec4(instMat[3][0],instMat[3][1],instMat[3][2],0.0);\n// MOD_coord=MOD_p.xy*(1.0/MOD_scale)+MOD_offset-vec2(0.5,0.5);\n\nMOD_p*=viewMatrix;\n\n// pos.x=mod(MOD_p.x,20.0);\n\nmMatrix[3].x=-viewMatrix[3].x+mod(viewMatrix[3].x-mMatrix[3].x,MOD_size.x)-(MOD_size.x/2.0)-MOD_pos.x;\nmMatrix[3].y=-2.0*viewMatrix[3].y+mod(viewMatrix[3].y-mMatrix[3].y,MOD_size.y)-(MOD_size.y/2.0)-MOD_pos.y;\n// mMatrix[3].y=mMatrix[3].y;\nmMatrix[3].z=mMatrix[3].z;\n\n\n#endif\n\n","displace_head_vert":"OUT vec2 MOD_coord;\n\n// #ifdef MOD_COLORIZE\n// OUT vec3 MOD_dispColor;\n// #endif",}; +const + inTrigger = op.inTrigger("Trigger"), + inWidth = op.inFloat("Width", 0), + inHeight = op.inFloat("Height", 0), + inX = op.inFloat("Pos X", 0), + inY = op.inFloat("Pos Y", 0), + next = op.outTrigger("Next"); + +inTrigger.onTriggered = render; + +const cgl = op.patch.cgl; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.displace_head_vert || "", + "srcBodyVert": attachments.displace_vert || "" +}); + +// mod.addModule({ +// "title": op.name, +// "name": "MODULE_COLOR", +// "srcHeadFrag": attachments.colorize_head_frag, +// "srcBodyFrag": attachments.colorize_frag +// }); + +mod.addUniformVert("2f", "MOD_size", inWidth, inHeight); +mod.addUniformVert("2f", "MOD_pos", inX, inY); + +function updateDefines() +{ +} + +function render() +{ + mod.bind(); + next.trigger(); + mod.unbind(); +} + + +}; + +Ops.Dev.InstancedModulo.prototype = new CABLES.Op(); +CABLES.OPS["7d944e9c-c21a-460b-be18-17aa3233fde3"]={f:Ops.Dev.InstancedModulo,objName:"Ops.Dev.InstancedModulo"}; + + + + +// ************************************************************** +// +// Ops.Dev.Json.ObjectSpreadSheet +// +// ************************************************************** + +Ops.Dev.Json.ObjectSpreadSheet = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + spread = op.inArray("Spreadsheet"), + inNumColumns = op.inInt("Num Columns", 2), + result = op.outObject("Result"); + +spread.setUiAttribs({ "hidePort": true }); +inNumColumns.setUiAttribs({ "hidePort": true }); + +spread.onChange = update; +inNumColumns.onChange = updateUi; + +updateUi(); + +function updateUi() +{ + spread.setUiAttribs({ + "display": "spreadsheet", + "spread_numColumns": inNumColumns.get() + }); + update(); +} + +function update() +{ + result.set(null); + + if (!spread.get()) return; + + const data = spread.get(); + data.cells = data.cells || []; + + const obj = {}; + + for (let y = 0; y < data.cells.length; y++) + { + if (data.cells[y]) + { + if (data.cols == 2) + { + let v = data.cells[y][1] || null; + if (CABLES.UTILS.isNumeric(v)) v = parseFloat(v); + + if (data.cells[y][0]) obj[data.cells[y][0]] = v; + } + + if (data.cols > 2) + { + let row = {}; + + for (let x = 1; x < data.cols; x++) + { + let v = data.cells[y][x] || null; + if (CABLES.UTILS.isNumeric(v)) v = parseFloat(v); + + row[getColName(data, x)] = v; + } + if (data.cells[y] && data.cells[y][0] && row) obj[data.cells[y][0]] = row; + } + } + } + result.set(obj); +} + +function getColName(data, c) +{ + if (data && data.colNames && data.colNames.length > c && data.colNames[c]) + { + return data.colNames[c]; + } + + let str = ""; + + while (c >= 0) + { + str = "abcdefghijklmnopqrstuvwxyz"[c % 26] + str; + c = Math.floor(c / 26) - 1; + } + + return str; +} + + +}; + +Ops.Dev.Json.ObjectSpreadSheet.prototype = new CABLES.Op(); +CABLES.OPS["09667032-43c9-4f7c-8740-76a90f8ac356"]={f:Ops.Dev.Json.ObjectSpreadSheet,objName:"Ops.Dev.Json.ObjectSpreadSheet"}; + + + + +// ************************************************************** +// +// Ops.Dev.ParticleSystem +// +// ************************************************************** + +Ops.Dev.ParticleSystem = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"particle_frag":"IN vec2 texCoord;\n\nUNI sampler2D texOldPos;\nUNI sampler2D texSpawnPos;\nUNI sampler2D texTiming;\n\nUNI sampler2D texFeedbackVel;\nUNI sampler2D texSpawnVel;\n\nUNI float mass;\nUNI float reset;\n\nUNI vec3 pos;\nUNI vec3 scale;\nUNI vec3 gravity;\n\nUNI vec2 lifeTime;\nUNI float time;\nUNI float timeDiff;\n\n\n\nUNI vec4 velocity; // xyz: xyz / w: inherit velocity\n\n\nUNI float spread;\n\n{{MODULES_HEAD}}\n{{CGL.RANDOM_LOW}}\n\nvoid main()\n{\n vec4 oldPos=texture(texOldPos,texCoord);\n vec4 vtiming=texture(texTiming,texCoord);\n vec4 oldVelocity=texture(texFeedbackVel,texCoord);\n vec4 newPos=oldPos;//vec4(1.0);\n\n // respawn!!\n if( time>vtiming.g || reset==1.0 )\n {\n newPos.rgb =pos;\n vec3 rnd=(cgl_random3(texCoord+gl_FragCoord.x/gl_FragCoord.y+time));\n\n rnd=texture(texSpawnPos,rnd.xy).rgb;\n\n oldVelocity=texture(texSpawnVel,texCoord);\n\n newPos.rgb+=rnd;\n\n vtiming.r=time;\n\n vtiming.g=(cgl_random(time*texCoord)*(lifeTime.y-lifeTime.x))+lifeTime.x+time;\n\n if(reset==1.0)\n vtiming.g=time+cgl_random(time*22.0*texCoord)*lifeTime.y;\n }\n\n vtiming.a=1.0;\n\n float lifeProgress=( (time-vtiming.r) / (vtiming.g-vtiming.r));\n\n vec3 grav=gravity*mass*lifeProgress;\n\n\n\n // newPos.rgb+=timeDiff*velocity.rgb*3.0;\n\n newPos.rgb+=grav*timeDiff;\n newPos.rgb+=velocity.xyz*timeDiff;\n\n // newPos.g-=time*0.01;\n\n\n // newPos.rgb+=normalize(newPos.rgb-oldPos.rgb)*0.2;\n\n // newPos.rgb+=(oldVelocity.rgb)*smoothstep(0.0,1.0,lifeProgress)*0.9;\n // newPos.rgb+=(oldVelocity.rgb)*(1.0-smoothstep(0.0,1.0,lifeProgress)*0.1);\n\n newPos.rgb-=((1.0-lifeProgress)*timeDiff*40.8*velocity.w)*oldVelocity.rgb;\n\n\n // gl_Position\n // r:x\n // g:y\n // b:z\n outColor0=vec4(newPos.rgb,1.0);\n\n // timing\n // r: starttime\n // g: endtime\n outColor1=vtiming;\n\n // timing output\n // r: life progress\n outColor2=vec4(vec3(lifeProgress ),1.);\n\n // velocity\n // oldVelocity.rgb*=1.995;\n outColor3=oldVelocity;\n\n\n // outColor0=vec4(1.0,0.3,0.0,1.0);\n // outColor1=vec4(0.0,1.0,0.0,1.0);\n // outColor2=vec4(0.0,0.0,1.0,1.0);\n // outColor3=vec4(1.0,1.0,0.0,1.0);\n\n\n}",}; +const + emptyTex = CGL.Texture.getEmptyTexture(op.patch.cgl), + exec = op.inTrigger("Execute"), + inNumParticles = op.inInt("Num Particles", 10000), + inReset = op.inTriggerButton("Reset"), + + // inTime=op.inFloat("Time",0), + inLifeTimeMin = op.inFloat("Min Lifetime", 0.5), + inLifeTimeMax = op.inFloat("Max Lifetime", 2), + + inMass = op.inFloat("Mass", 0), + + posX = op.inFloatSlider("Position X", 0), + posY = op.inFloatSlider("Position Y", 0), + posZ = op.inFloatSlider("Position Z", 0), + + moveX = op.inFloatSlider("Velocity X", 0), + moveY = op.inFloatSlider("Velocity Y", 1), + moveZ = op.inFloatSlider("Velocity Z", 0), + + gravX = op.inFloatSlider("Gravity X", 0), + gravY = op.inFloatSlider("Gravity Y", 1), + gravZ = op.inFloatSlider("Gravity Z", 0), + + inherVel = op.inFloatSlider("Inherit Velocity", 0), + + inTexOldPos = op.inTexture("Feedback Positions"), + inTexSpawn = op.inTexture("Spawn Positions"), + inTexSpawnVel = op.inTexture("Spawn Velocity"), + + next = op.outTrigger("Next"), + outTexPos = op.outTexture("Positions", emptyTex), + outTexTiming = op.outTexture("Timing", emptyTex), + outTest = op.outTexture("Test", emptyTex), + outTexSize = op.outNumber("Tex Size"); + +op.setPortGroup("Constant Velocity", [moveX, moveY, moveZ]); +op.setPortGroup("Position", [posX, posY, posZ]); + +let lastX = 0; +let lastY = 0; +let lastZ = 0; + +let texTiming = null; +let texPos = null; +let lastTime = CABLES.now(); + +const tcPos = new CGL.CopyTexture(op.patch.cgl, "particlesys_pos", + { + "shader": attachments.particle_frag, + "numRenderBuffers": 4, + "isFloatingPointTexture": true, + "filter": CGL.Texture.FILTER_NEAREST + }); + +const tcTiming = new CGL.CopyTexture(op.patch.cgl, "particlesys_timing", { "isFloatingPointTexture": true, "filter": CGL.Texture.FILTER_NEAREST }); + +// only used when no input texture is given, just for simple systems... +const tcFeedback = new CGL.CopyTexture(op.patch.cgl, "particlesys_feedback", { "isFloatingPointTexture": true, "filter": CGL.Texture.FILTER_NEAREST }); +const tcFeedbackVel = new CGL.CopyTexture(op.patch.cgl, "particlesys_feedbackvel", { "isFloatingPointTexture": true, "filter": CGL.Texture.FILTER_NEAREST }); + +const + cgl = op.patch.cgl, + uniTexOldPos = new CGL.Uniform(tcPos.bgShader, "t", "texOldPos", 0), + uniTexSpawn = new CGL.Uniform(tcPos.bgShader, "t", "texSpawnPos", 1), + uniTexTiming = new CGL.Uniform(tcPos.bgShader, "t", "texTiming", 2), + uniTexSpawnVel = new CGL.Uniform(tcPos.bgShader, "t", "texSpawnVel", 3), + uniTexFeedbackVel = new CGL.Uniform(tcPos.bgShader, "t", "texFeedbackVel", 4), + + uniTime = new CGL.Uniform(tcPos.bgShader, "f", "time", 0), + uniLifeTime = new CGL.Uniform(tcPos.bgShader, "2f", "lifeTime", inLifeTimeMin, inLifeTimeMax), + uniTimeDiff = new CGL.Uniform(tcPos.bgShader, "f", "timeDiff", 0), + + uniVel = new CGL.Uniform(tcPos.bgShader, "4f", "velocity", moveX, moveY, moveZ, inherVel), + uniPos = new CGL.Uniform(tcPos.bgShader, "3f", "pos", posX, posY, posZ), + unigrav = new CGL.Uniform(tcPos.bgShader, "3f", "gravity", gravX, gravY, gravZ), + uniMass = new CGL.Uniform(tcPos.bgShader, "f", "mass", inMass), + + uniReset = new CGL.Uniform(tcPos.bgShader, "f", "reset", 0); + +inReset.onTriggered = () => +{ + uniReset.setValue(1); +}; + +inNumParticles.onChange = createTextures; + +const texBlack = new CGL.Texture(cgl, { + "isFloatingPointTexture": true, + "width": 8, + "height": 8 }); + +function createTextures() +{ + const size = Math.ceil(Math.sqrt(inNumParticles.get())); + outTexSize.set(size); + + console.log(size); + + texTiming = new CGL.Texture(cgl, { + "isFloatingPointTexture": true, + "filter": CGL.Texture.FILTER_NEAREST, + "width": size, + "height": size }); + texPos = new CGL.Texture(cgl, { + "isFloatingPointTexture": true, + "filter": CGL.Texture.FILTER_NEAREST, + "width": size, + "height": size }); +} + +exec.onTriggered = () => +{ + let firsttime = false; + // if (!inTexOldPos.get()) return; + if (!texTiming) + { + firsttime = true; + createTextures(); + uniReset.setValue(1); + } + + tcPos.bgShader.popTextures(); + + uniTime.setValue(op.patch.freeTimer.get()); + + if (!inTexOldPos.isLinked()) + { + + } + + if (firsttime)tcPos.bgShader.pushTexture(uniTexOldPos, texPos.tex); + else + { + if (inTexOldPos.isLinked()) + tcPos.bgShader.pushTexture(uniTexOldPos, inTexOldPos.get().tex); + else + tcPos.bgShader.pushTexture(uniTexOldPos, tcFeedback.copy(outTexPos.get())); + } + + if (inTexSpawn.get()) tcPos.bgShader.pushTexture(uniTexSpawn, inTexSpawn.get().tex); + else tcPos.bgShader.pushTexture(uniTexSpawn, texBlack.tex); + + if (inTexSpawnVel.get()) tcPos.bgShader.pushTexture(uniTexSpawnVel, inTexSpawnVel.get().tex); + + if (tcPos.fb) tcPos.bgShader.pushTexture(uniTexFeedbackVel, tcFeedbackVel.copy(tcPos.fb.getTextureColorNum(3))); + + if (firsttime)tcPos.bgShader.pushTexture(uniTexTiming, texTiming.tex); + else + if (tcPos.fb && tcPos.fb.getTextureColorNum(1))tcPos.bgShader.pushTexture(uniTexTiming, tcTiming.copy(tcPos.fb.getTextureColorNum(1))); + + tcPos.copy(texPos); + + uniReset.setValue(0); + + uniTimeDiff.setValue((CABLES.now() - lastTime) / 1000); + lastTime = CABLES.now(); + + lastX = moveX.get(); + lastY = moveY.get(); + lastZ = moveZ.get(); + + outTexPos.set(emptyTex); + outTexPos.set(tcPos.fb.getTextureColorNum(0)); + + outTexTiming.set(emptyTex); + outTexTiming.set(tcPos.fb.getTextureColorNum(2)); + + outTest.set(texPos); + outTest.set(tcPos.fb.getTextureColorNum(3)); + + next.trigger(); +}; + +// + + +}; + +Ops.Dev.ParticleSystem.prototype = new CABLES.Op(); +CABLES.OPS["bd94c323-a211-4fb6-a8aa-b724b610d885"]={f:Ops.Dev.ParticleSystem,objName:"Ops.Dev.ParticleSystem"}; + + + + +// ************************************************************** +// +// Ops.Dev.String.ConcatMultiArray +// +// ************************************************************** + +Ops.Dev.String.ConcatMultiArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const addSpacesCheckBox = op.inBool("add spaces", false), + newLineX = op.inBool("new lines", false), + result = op.outString("concat string"), + outArr = op.outArray("Result"), + outLines=op.outNumber("Total Lines"); + +const + stringPorts = [], + arrayPorts = []; + +stringPorts.onChange = + addSpacesCheckBox.onChange = + newLineX.onChange = update; + +for (let i = 0; i < 8; i++) +{ + let p = op.inString("String " + i); + let pA = op.inArray("Array " + i); + stringPorts.push(p); + arrayPorts.push(pA); + + p.onChange = + pA.onChange = update; +} + +function update() +{ + let str = ""; + let nl = ""; + let space = addSpacesCheckBox.get(); + let line = 0; + let numLines = 1; + let countStringLines=0; + + for (let i = 0; i < arrayPorts.length; i++) + { + if(arrayPorts[i].get()) numLines = Math.max(numLines, arrayPorts[i].get().length); + } + + for (let j = 0; j < numLines; j++) + { + for (let i = 0; i < stringPorts.length; i++) + { + const inString = stringPorts[i].get(); + const inArray = arrayPorts[i].get(); + if (!inString && !inArray) continue; + if (i > 0 && space) str += " "; + + str += inString; + if (inArray) + { + if(space)str += " "; + str += inArray[j]; + } + } + if (newLineX.get() ) + { + str += "\n"; + countStringLines++; + } + + } + + result.set(str); + outLines.set(countStringLines); +} + + +}; + +Ops.Dev.String.ConcatMultiArray.prototype = new CABLES.Op(); +CABLES.OPS["9420ebd1-e0e9-44a6-825e-402b9b1ce71f"]={f:Ops.Dev.String.ConcatMultiArray,objName:"Ops.Dev.String.ConcatMultiArray"}; + + + + +// ************************************************************** +// +// Ops.Dev.SvgPathToPoints +// +// ************************************************************** + +Ops.Dev.SvgPathToPoints = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("SVG Path"), + inStepSize = op.inFloat("Bezier Stepsize", 3), + outArr = op.outArray("Points A"), + outHoles = op.outArray("Points B"); + +inStepSize.onChange = +inStr.onChange = () => +{ + let str = inStr.get(); + + if (!str || str.length < 2) return; + + str = str.replace(/([A-Z,a-z])/g, " $1 "); + + const cmds = fromPathToArray(str); + + // create a list of closed contours + const polys = []; + cmds.forEach(({ type, x, y, x1, y1, x2, y2 }) => + { + switch (type) + { + case "M": + polys.push(new Polygon()); + polys[polys.length - 1].moveTo({ x, y }); + break; + case "L": + polys[polys.length - 1].moveTo({ x, y }); + break; + case "C": + polys[polys.length - 1].cubicTo({ x, y }, { "x": x1, "y": y1 }, { "x": x2, "y": y2 }); + break; + case "Q": + polys[polys.length - 1].conicTo({ x, y }, { "x": x1, "y": y1 }); + break; + case "Z": + polys[polys.length - 1].close(); + break; + } + }); + + // sort contours by descending area + polys.sort((a, b) => Math.abs(b.area) - Math.abs(a.area)); + // classify contours to find holes and their 'parents' + const root = []; + for (let i = 0; i < polys.length; ++i) + { + let parent = null; + for (let j = i - 1; j >= 0; --j) + { + // a contour is a hole if it is inside its parent and has different winding + if (polys[j].inside(polys[i].points[0]) && polys[i].area * polys[j].area < 0) + { + parent = polys[j]; + break; + } + } + if (parent) + { + parent.children.push(polys[i]); + } + else + { + root.push(polys[i]); + } + } + + const totalPoints = polys.reduce((sum, p) => sum + p.points.length, 0); + const vertexData = new Float32Array(totalPoints * 2); + let vertexCount = 0; + const indices = []; + + // function process(poly) { + // // construct input for earcut + // const coords = []; + // const holes = []; + // poly.points.forEach(({x, y}) => coords.push(x, y)); + // poly.children.forEach(child => { + // // children's children are new, separate shapes + // child.children.forEach(process); + + // holes.push(coords.length / 2); + // child.points.forEach(({x, y}) => coords.push(x, y)); + // }); + + // // add vertex data + // vertexData.set(coords, vertexCount * 2); + // // add index data + // earcut(coords, holes).forEach(i => indices.push(i + vertexCount)); + // vertexCount += coords.length / 2; + // } + // root.forEach(process); + + const coords = []; + const holes = []; + + for (let i = 0; i < polys.length; i++) + { + const arr = []; + for (let j = 0; j < polys[i].points.length; j++) + { + arr.push(polys[i].points[j].x, polys[i].points[j].y, 0); + } + coords.push(arr); + + if (polys[i].children) + { + for (let j = 0; j < polys[i].children.length; j++) + { + const hole = []; + + for (let k = 0; k < polys[i].children[j].points.length; k++) + { + hole.push( + polys[i].children[j].points[k].x, + polys[i].children[j].points[k].y, + 0); + } + + holes.push(hole); + if (j > 0) coords.push([]); + } + } + + // if (!polys[i].children || polys[i].children.length == 0)holes.push([]); + + // polys[i].children.forEach((child) => + // { + // const hole = []; + // child.points.forEach(({ x, y }) => hole.push(x, y, 0)); + + // holes.push(hole); + // coords.push([]); + // }); + + // for(let i=holes.length;i 0) + { + let it = items.shift(); + if (PATH_COMMANDS.hasOwnProperty(it)) + { + currentCommand = it; + } + else + { + items.unshift(it); + } + + currentElement = { "type": currentCommand }; + PATH_COMMANDS[currentCommand].forEach((prop) => + { + it = items.shift(); // TODO sanity check + currentElement[prop] = parseFloat(it); + }); + if (currentCommand === "M") + { + currentCommand = "L"; + } + else if (currentCommand === "m") + { + currentCommand = "l"; + } + segments.push(currentElement); + } + return segments; +} + +// https://stackoverflow.com/questions/50554803/triangulate-path-data-from-opentype-js-using-earcut + +const MAX_BEZIER_STEPS = 15; +// this is for inside checks - doesn't have to be particularly +// small because glyphs have finite resolution +const EPSILON = 1e-6; + +class Polygon +{ + constructor() + { + this.points = []; + this.children = []; + this.area = 0.0; + + this.BEZIER_STEP_SIZE = inStepSize.get(); + } + + moveTo(p) + { + this.points.push(p); + } + + lineTo(p) + { + this.points.push(p); + } + + close() + { + let cur = this.points[this.points.length - 1]; + this.points.forEach((next) => + { + this.area += 0.5 * cross(cur, next); + cur = next; + }); + } + + conicTo(p, p1) + { + const p0 = this.points[this.points.length - 1]; + const dist = distance(p0, p1) + distance(p1, p); + const steps = Math.max(2, Math.min(MAX_BEZIER_STEPS, dist / this.BEZIER_STEP_SIZE)); + for (let i = 1; i <= steps; ++i) + { + const t = i / steps; + this.points.push(lerp(lerp(p0, p1, t), lerp(p1, p, t), t)); + } + } + + cubicTo(p, p1, p2) + { + const p0 = this.points[this.points.length - 1]; + const dist = distance(p0, p1) + distance(p1, p2) + distance(p2, p); + const steps = Math.max(2, Math.min(MAX_BEZIER_STEPS, dist / this.BEZIER_STEP_SIZE)); + for (let i = 1; i <= steps; ++i) + { + const t = i / steps; + const a = lerp(lerp(p0, p1, t), lerp(p1, p2, t), t); + const b = lerp(lerp(p1, p2, t), lerp(p2, p, t), t); + this.points.push(lerp(a, b, t)); + } + } + + inside(p) + { + let count = 0, cur = this.points[this.points.length - 1]; + this.points.forEach((next) => + { + const p0 = (cur.y < next.y ? cur : next); + const p1 = (cur.y < next.y ? next : cur); + if (p0.y < p.y + EPSILON && p1.y > p.y + EPSILON) + { + if ((p1.x - p0.x) * (p.y - p0.y) > (p.x - p0.x) * (p1.y - p0.y)) + { + count += 1; + } + } + cur = next; + }); + return (count % 2) !== 0; + } +} + +function distance(p1, p2) +{ + const dx = p1.x - p2.x, dy = p1.y - p2.y; + return Math.sqrt(dx * dx + dy * dy); +} + +function lerp(p1, p2, t) +{ + return { "x": (1 - t) * p1.x + t * p2.x, "y": (1 - t) * p1.y + t * p2.y }; +} + +function cross(p1, p2) +{ + return p1.x * p2.y - p1.y * p2.x; +} + + +}; + +Ops.Dev.SvgPathToPoints.prototype = new CABLES.Op(); +CABLES.OPS["43bcfbe3-7460-4b22-9a42-be787adf180d"]={f:Ops.Dev.SvgPathToPoints,objName:"Ops.Dev.SvgPathToPoints"}; + + + + +// ************************************************************** +// +// Ops.Dev.TestOp +// +// ************************************************************** + +Ops.Dev.TestOp = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"sdsdsd":"",}; +// your new op +// have a look at the documentation at: +// https://docs.cables.gl/dev_hello_op/dev_hello_op.html + +// sss + +const a = op.inFloat("name", 0); +const aa = op.inFloat("aaaa", 0); +const trig = op.inTrigger("Render"); + +const cgl = op.patch.cgl; +const meshRect = new CGL.WireframeRect(cgl); + +trig.onTriggered = function () +{ + meshRect.render(); +}; + + +}; + +Ops.Dev.TestOp.prototype = new CABLES.Op(); +CABLES.OPS["274d0d70-00a0-4a19-88ca-e480b5143715"]={f:Ops.Dev.TestOp,objName:"Ops.Dev.TestOp"}; + + + + +// ************************************************************** +// +// Ops.Dev.VertexTexture.RgbCurlNoise +// +// ************************************************************** + +Ops.Dev.VertexTexture.RgbCurlNoise = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"curl_frag":"UNI float z;\nUNI float x;\nUNI float y;\nUNI float scale;\nIN vec2 texCoord;\nUNI sampler2D tex;\n\n// UNI float amount;\nUNI float timeDelta;\nUNI vec3 offset;\n\n{{CGL.BLENDMODES}}\n{{CGL.RANDOM_TEX}}\n\nfloat Interpolation_C2( float x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } // 6x^5-15x^4+10x^3\t( Quintic Curve. As used by Perlin in Improved Noise. http://mrl.nyu.edu/~perlin/paper445.pdf )\nvec2 Interpolation_C2( vec2 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec3 Interpolation_C2( vec3 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2( vec4 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2_InterpAndDeriv( vec2 x ) { return x.xyxy * x.xyxy * ( x.xyxy * ( x.xyxy * ( x.xyxy * vec2( 6.0, 0.0 ).xxyy + vec2( -15.0, 30.0 ).xxyy ) + vec2( 10.0, -60.0 ).xxyy ) + vec2( 0.0, 30.0 ).xxyy ); }\nvec3 Interpolation_C2_Deriv( vec3 x ) { return x * x * (x * (x * 30.0 - 60.0) + 30.0); }\n\n\nvoid FAST32_hash_3D( \tvec3 gridcell,\n out vec4 lowz_hash_0,\n out vec4 lowz_hash_1,\n out vec4 lowz_hash_2,\n out vec4 highz_hash_0,\n out vec4 highz_hash_1,\n out vec4 highz_hash_2\t)\t\t//\tgenerates 3 random numbers for each of the 8 cell corners\n{\n // gridcell is assumed to be an integer coordinate\n\n //\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const vec3 SOMELARGEFLOATS = vec3( 635.298681, 682.357502, 668.926525 );\n const vec3 ZINC = vec3( 48.500388, 65.294118, 63.934599 );\n\n //\ttruncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n //\tcalculate the noise\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n P = P.xzxz * P.yyww;\n vec3 lowz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) );\n vec3 highz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) );\n lowz_hash_0 = fract( P * lowz_mod.xxxx );\n highz_hash_0 = fract( P * highz_mod.xxxx );\n lowz_hash_1 = fract( P * lowz_mod.yyyy );\n highz_hash_1 = fract( P * highz_mod.yyyy );\n lowz_hash_2 = fract( P * lowz_mod.zzzz );\n highz_hash_2 = fract( P * highz_mod.zzzz );\n}\n\n//\n//\tPerlin Noise 3D ( gradient noise )\n//\tReturn value range of -1.0->1.0\n//\thttp://briansharpe.files.wordpress.com/2011/11/perlinsample.jpg\n//\nfloat Perlin3D( vec3 P )\n{\n //\testablish our grid cell and unit position\n vec3 Pi = floor(P);\n vec3 Pf = P - Pi;\n vec3 Pf_min1 = Pf - 1.0;\n\n#if 1\n //\n //\tclassic noise.\n //\trequires 3 random values per point. with an efficent hash function will run faster than improved noise\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1;\n FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n //SGPP_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n\n //\tcalculate the gradients\n vec4 grad_x0 = hashx0 - 0.49999;\n vec4 grad_y0 = hashy0 - 0.49999;\n vec4 grad_z0 = hashz0 - 0.49999;\n vec4 grad_x1 = hashx1 - 0.49999;\n vec4 grad_y1 = hashy1 - 0.49999;\n vec4 grad_z1 = hashz1 - 0.49999;\n vec4 grad_results_0 = inversesqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 );\n vec4 grad_results_1 = inversesqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 );\n\n#if 1\n //\tClassic Perlin Interpolation\n vec3 blend = Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n float final = dot( res0, blend2.zxzx * blend2.wwyy );\n final *= 1.1547005383792515290182975610039;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.75)\n return final;\n#else\n //\tClassic Perlin Surflet\n //\thttp://briansharpe.wordpress.com/2012/03/09/modifications-to-classic-perlin-noise/\n Pf *= Pf;\n Pf_min1 *= Pf_min1;\n vec4 vecs_len_sq = vec4( Pf.x, Pf_min1.x, Pf.x, Pf_min1.x ) + vec4( Pf.yy, Pf_min1.yy );\n float final = dot( Falloff_Xsq_C2( min( vec4( 1.0 ), vecs_len_sq + Pf.zzzz ) ), grad_results_0 ) + dot( Falloff_Xsq_C2( min( vec4( 1.0 ), vecs_len_sq + Pf_min1.zzzz ) ), grad_results_1 );\n final *= 2.3703703703703703703703703703704;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/cube(0.75)\n return final;\n#endif\n\n#else\n //\n //\timproved noise.\n //\trequires 1 random value per point. Will run faster than classic noise if a slow hashing function is used\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hash_lowz, hash_highz;\n FAST32_hash_3D( Pi, hash_lowz, hash_highz );\n //BBS_hash_3D( Pi, hash_lowz, hash_highz );\n //SGPP_hash_3D( Pi, hash_lowz, hash_highz );\n\n //\n //\t\"improved\" noise using 8 corner gradients. Faster than the 12 mid-edge point method.\n //\tKen mentions using diagonals like this can cause \"clumping\", but we'll live with that.\n //\t[1,1,1] [-1,1,1] [1,-1,1] [-1,-1,1]\n //\t[1,1,-1] [-1,1,-1] [1,-1,-1] [-1,-1,-1]\n //\n hash_lowz -= 0.5;\n vec4 grad_results_0_0 = vec2( Pf.x, Pf_min1.x ).xyxy * sign( hash_lowz );\n hash_lowz = abs( hash_lowz ) - 0.25;\n vec4 grad_results_0_1 = vec2( Pf.y, Pf_min1.y ).xxyy * sign( hash_lowz );\n vec4 grad_results_0_2 = Pf.zzzz * sign( abs( hash_lowz ) - 0.125 );\n vec4 grad_results_0 = grad_results_0_0 + grad_results_0_1 + grad_results_0_2;\n\n hash_highz -= 0.5;\n vec4 grad_results_1_0 = vec2( Pf.x, Pf_min1.x ).xyxy * sign( hash_highz );\n hash_highz = abs( hash_highz ) - 0.25;\n vec4 grad_results_1_1 = vec2( Pf.y, Pf_min1.y ).xxyy * sign( hash_highz );\n vec4 grad_results_1_2 = Pf_min1.zzzz * sign( abs( hash_highz ) - 0.125 );\n vec4 grad_results_1 = grad_results_1_0 + grad_results_1_1 + grad_results_1_2;\n\n //\tblend the gradients and return\n vec3 blend = Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n return dot( res0, blend2.zxzx * blend2.wwyy ) * (2.0 / 3.0);\t//\t(optionally) mult by (2.0/3.0) to scale to a strict -1.0->1.0 range\n#endif\n}\n\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n\n\n vec3 rnd=\n vec3(\n Perlin3D( ( (base.xyz+20.0) + vec3(x,y,z)) *scale ),\n Perlin3D( ( (base.xyz-20.0) + vec3(x,y,z)) *scale ),\n Perlin3D( ( (base.xyz+60.0) + vec3(x,y,z)) *scale )\n );\n\nfloat seed=1.0;\n vec3 noise=(cgl_random3(texCoord.xy)-0.5)*seed;\n rnd+=\n vec3(\n Perlin3D( ( (base.xyz+20.0+noise.x+offset.x) + vec3(x,y,z)) *scale ),\n Perlin3D( ( (base.xyz-20.0+noise.y+offset.y) + vec3(x,y,z)) *scale ),\n Perlin3D( ( (base.xyz+60.0+noise.z+offset.z) + vec3(x,y,z)) *scale )\n );\n\n\n // rnd=\n // (\n // vec3(\n // Perlin3D( ( (base.xyz+20.0) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz-20.0) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz+60.0) + vec3(x,y,z)) *scale )\n // )+\n // vec3(\n // Perlin3D( ( (base.xyz+20.0+(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz-20.0+(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz+60.0+(offset*scale)) + vec3(x,y,z)) *scale )\n // )*0.25+\n // vec3(\n // Perlin3D( ( (base.xyz+20.0-(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz-20.0-(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz+60.0-(offset*scale)) + vec3(x,y,z)) *scale )\n // )*0.25+\n // vec3(\n // Perlin3D( ( (base.xyz+20.0-(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz-20.0+(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz+60.0-(offset*scale)) + vec3(x,y,z)) *scale )\n // )*0.25+\n // vec3(\n // Perlin3D( ( (base.xyz+20.0+(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz-20.0-(offset*scale)) + vec3(x,y,z)) *scale ),\n // Perlin3D( ( (base.xyz+60.0+(offset*scale)) + vec3(x,y,z)) *scale )\n // )*0.25\n\n // )/2.0;\n\n\n\n\n\n\n\n #ifdef MOD_NORM_SPEED\n rnd=normalize(rnd)*0.2;\n #endif\n #ifndef MOD_NORM_SPEED\n // rnd*=timeDelta;\n #endif\n\n // rnd=clamp(rnd,vec3(-0.01),vec3(0.01));\n // rnd*=0.01;\n // float cl=0.03;\n // rnd.x=clamp(rnd.x,-cl,cl);\n // rnd.y=clamp(rnd.y,-cl,cl);\n // rnd.z=clamp(rnd.z,-cl,cl);\n\n rnd*=timeDelta;\n\n vec3 coord=base.xyz+rnd*0.3;\n // coord.x+=( Perlin3D( ( (base.xyz+20.0) + vec3(x,y,z)) *scale )*timeDelta);\n // coord.y+=( Perlin3D( ( (base.xyz-20.0) + vec3(x,y,z)) *scale )*timeDelta);\n // coord.z+=( Perlin3D( ( (base.xyz+30.0) + vec3(x,y,z)) *scale )*timeDelta);\n\n // additional noise on top\n // coord.x+=Perlin3D(vec3(texCoord.x))*0.001;\n // coord.y+=Perlin3D(vec3(texCoord.y))*0.001;\n // coord.z+=Perlin3D(vec3(texCoord.y,texCoord.x,texCoord.y))*0.001;\n\n\n outColor=vec4(coord,1.0);\n\n\n // outColor=cgl_blend(base,coord,1.0);\n}\n",}; +const + render = op.inTrigger("Render"), + scale = op.inFloat("Scale", 1), + time = op.inFloat("Time", 0), + posX = op.inFloat("Pos X", 0), + posY = op.inFloat("Pos Y", 0), + posZ = op.inFloat("Pos Z", 0), + + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.curl_frag); +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + // textureMaskUniform = new CGL.Uniform(shader, "t", "texMask", 1), + + uniSeed = new CGL.Uniform(shader, "3f", "offset", posX, posX, posX), + uniTime = new CGL.Uniform(shader, "f", "time", time), + uniScale = new CGL.Uniform(shader, "f", "scale", scale), + uniTimeDelta = new CGL.Uniform(shader, "f", "timeDelta", 1); + +time.onChange = + scale.onChange = updateDefines; + +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("MOD_NORM_SPEED", true); +} + +let lastTime = 0; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + uniTimeDelta.set(time.get() - lastTime); + lastTime = time.get(); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Dev.VertexTexture.RgbCurlNoise.prototype = new CABLES.Op(); +CABLES.OPS["10bc3a93-47c6-48ea-aebb-20f990153e62"]={f:Ops.Dev.VertexTexture.RgbCurlNoise,objName:"Ops.Dev.VertexTexture.RgbCurlNoise"}; + + + + +// ************************************************************** +// +// Ops.Dev.VizBranchProfiler +// +// ************************************************************** + +Ops.Dev.VizBranchProfiler = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObj = op.inObject("Profiler Data"), + + inRender = op.inSwitch("Render", ["Patch", "Canvas", "Both"], "Patch"), + inWidth = op.inInt("Width", 500), + inHeight = op.inInt("Height", 200), + outCanvas = op.outObject("Canvas", null, "element"), + outTotalDur = op.outNumber("Duration"), + inUpdate = op.inTriggerButton("Update"); + +op.setUiAttrib({ "height": 100, "width": 500, "resizable": true }); + +op.setPortGroup("Canvas", [inWidth, inHeight]); + +const colors = ["#D183BF", "#9091D6", "#FFC395", "#F0D165", "#63A8E8", "#CF5D9D", "#66C984", "#D66AA6", "#515151", "#7AC4E0"]; + +let colorCycle = 0; +let totalDur = 1; +let root = null; +let doUpdate = true; +let canvas = null; +let render2Canvas = false; +let canvasWidth = 0; +let canvasHeight = 0; + +inRender.onChange = updateUi; + +inHeight.onChange = inWidth.onChange = setupCanvas; + +function setupCanvas() +{ + if (render2Canvas && (!canvas || canvasHeight != inHeight.get() || canvasWidth != inWidth.get())) + { + if (canvas)canvas.remove(); + canvas = document.createElement("canvas"); + + canvasWidth = inWidth.get(); + canvasHeight = inHeight.get(); + canvas.setAttribute("width", canvasWidth); + canvas.setAttribute("height", canvasHeight); + } +} + +function updateUi() +{ + render2Canvas = inRender.get() != "Patch"; + + inWidth.setUiAttribs({ "greyout": !render2Canvas }); + inHeight.setUiAttribs({ "greyout": !render2Canvas }); + + setupCanvas(); +} + +function getWidth(layer, d) +{ + // if (d < 0.2) d = 0.004; + return layer.width * (d / totalDur); +} + +inUpdate.onTriggered = () => +{ + doUpdate = true; + + if (render2Canvas && inObj.get() && inObj.get().root) + { + const layer = { "width": inWidth.get(), "height": inHeight.get(), "x": 0, "y": 0 }; + const ctx = canvas.getContext("2d"); + + totalDur = inObj.get().root.dur; + // console.log(inObj.get()); + // console.log(inObj.get().root.dur) + clear(ctx, layer); + drawBranch(ctx, layer, inObj.get().root, 0, 0); + outCanvas.set(null); + outCanvas.set(canvas); + } + + outTotalDur.set(totalDur); +}; + +function clear(ctx, layer) +{ + ctx.fillStyle = "#222"; + ctx.fillRect( + layer.x, layer.y, + layer.width, layer.height); +} + +function drawBranch(ctx, layer, b, level, posx) +{ + if (!b) return; + + colorCycle++; + colorCycle %= (colors.length - 1); + + let rowHeight = (layer.height / 10); + let posy = rowHeight * level; + + ctx.fillStyle = colors[colorCycle]; + ctx.fillRect( + layer.x + posx, posy + layer.y, + getWidth(layer, b.dur), rowHeight); + + let fontSize = 18; + ctx.fillStyle = "#222"; + ctx.font = "normal " + fontSize + "px sourceCodePro"; + + let durs = "(" + Math.round(b.dur * 100) / 100 + "ms)"; + if (b.dur < 0.2)durs = ""; + ctx.fillText(b.name + durs, layer.x + posx, layer.y + posy + fontSize); + + let xadd = 0; + for (let i = 0; i < b.childs.length; i++) + { + drawBranch(ctx, layer, b.childs[i], level + 1, posx + xadd); + xadd += getWidth(layer, b.childs[i].dur); + } +} + +op.renderVizLayer = (ctx, layer) => +{ + if (!inObj.get().root) return; + clear(ctx, layer); + + colorCycle = 0; + + // console.log(totalDur); + if (doUpdate) + { + doUpdate = false; + totalDur = inObj.get().root.dur; + root = inObj.get().root; + // console.log(root); + } + + drawBranch(ctx, layer, root, 0, 0); +}; + + +}; + +Ops.Dev.VizBranchProfiler.prototype = new CABLES.Op(); +CABLES.OPS["236fb6ea-4e98-4039-b831-98dc2ff2844b"]={f:Ops.Dev.VizBranchProfiler,objName:"Ops.Dev.VizBranchProfiler"}; + + + + +// ************************************************************** +// +// Ops.Dev.WebGpu.Computetest +// +// ************************************************************** + +Ops.Dev.WebGpu.Computetest = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"compute_wgsl":"// struct Matrix {\n// size : vec2,\n// numbers: array,\n// }\n\n@group(0) @binding(0) var resultMatrix : array;\n\n@compute @workgroup_size(50,1)\nfn main(@builtin(global_invocation_id) global_id : vec3)\n{\n // Guard against out-of-bounds work group sizes\n // if (global_id.x >= u32(firstMatrix.size.x) || global_id.y >= u32(secondMatrix.size.y))\n // return;\n // }\n\n // resultMatrix.size = vec2(firstMatrix.size.x, secondMatrix.size.y);\n\n // let resultCell = vec2(global_id.x, global_id.y);\n // var result = 0.0;\n // for (var i = 0u; i < u32(firstMatrix.size.y); i = i + 1u) {\n // let a = i + resultCell.x * u32(firstMatrix.size.y);\n // let b = resultCell.y + i * u32(secondMatrix.size.y);\n // result = result + firstMatrix.numbers[a] * secondMatrix.numbers[b];\n // }\n\n // let index = resultCell.y + resultCell.x * u32(secondMatrix.size.y);\n // resultMatrix.numbers[index] = result;\n\n resultMatrix[global_id.x] = f32(global_id.x);\n}",}; +const + compute=op.inTrigger("Computer"), + next=op.outTrigger("Next"); + +let comp=null; +compute.onTriggered=()=> +{ + if(!comp) + { + comp=new CABLES.CGP.GpuCompute(op.patch.cg,"c0mput",attachments.compute_wgsl); + + comp.compute(); + } + +} + +}; + +Ops.Dev.WebGpu.Computetest.prototype = new CABLES.Op(); +CABLES.OPS["7f179cc1-a1bd-4989-9fe2-6b6f8a95a6d7"]={f:Ops.Dev.WebGpu.Computetest,objName:"Ops.Dev.WebGpu.Computetest"}; + + + + +// ************************************************************** +// +// Ops.Dev.WebGpu.WebGpuCanvas +// +// ************************************************************** + +Ops.Dev.WebGpu.WebGpuCanvas = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"tri_wgsl_frag":"@fragment\n\nfn main() -> @location(0) vec4\n{\n return vec4(1.0, 0.0, 0.0, 1.0);\n}","tri_wgsl_vert":"@vertex\n\nfn main(@builtin(vertex_index) VertexIndex : u32)\n -> @builtin(position) vec4\n {\n var pos = array, 3>(\n vec2(0.0, 0.5),\n vec2(-0.5, -0.5),\n vec2(0.5, -0.5));\n\n return vec4(pos[VertexIndex], 0.0, 1.0);\n}\n",}; +const + next = op.outTrigger("Next"), + supported = op.outBoolNum("Supported", false), + outLimits = op.outObject("Limits"); + +let device = null; +let context = null, pipeline = null; + +const canvas = document.createElement("canvas"); +canvas.id = "webgpucanvas"; +canvas.setAttribute("tabindex", 4); +canvas.style.width = 128 + "px"; +canvas.style.height = 128 + "px"; +canvas.style.right = 0 + "px"; +canvas.style["z-index"] = "22222"; +canvas.style.border = "1px solid red"; +canvas.style.position = "absolute"; + +const container = document.getElementById("cablescanvas"); +container.appendChild(canvas); + +// canvas.addEventListener("blur", () => { if (supported.get())canvas.style.border = "1px solid black"; }); +// canvas.addEventListener("focus", () => { if (supported.get())canvas.style.border = "1px solid white"; }); + +const pm = mat4.create(); + +let renderTarget = null; +let depthTexture = null; +let canvasInfo = {}; + +let sizeWidth = 0; +let sizeHeight = 0; + +op.patch.cgp = op.patch.cgp || new CABLES.CGP.Context(op.patch); +const cgp = op.patch.cgp; +const sampleCount = 1; + +cgp.canvas = canvas; + +if (CABLES.UI) +{ + gui.canvasManager.addContext(cgp); + // setTimeout(() => + // { + // gui.emitEvent("resizecanvas"); + // gui.setLayout(); + // }, 200); +} + +let stopped = false; +op.onDelete = () => +{ + stopped = true; + canvas.remove(); + device = + op.patch.cgp.device = + op.patch.cgp.adapter = + op.patch.cgp.pipeline = null; +}; + +if (!navigator.gpu) +{ + const warn = "Your browser does not support webGPU"; + op.setUiError("nowebgpu", warn); + console.log(warn); +} + +if (navigator.gpu) + navigator.gpu.requestAdapter().then((adapter) => + { + if (!adapter) + { + op.warn("webgpu: could not get adapter..."); + supported.set(false); + return; + } + adapter.requestDevice().then((_device) => + { + canvas.style.border = "1px solid black"; + + cgp.setCanvas(canvas); + cgp.setSize(container.clientWidth, container.clientHeight); + + supported.set(true); + device = _device; + + // + + const limits = {}; + for (let i in device.limits) limits[i] = device.limits[i]; + outLimits.set(limits); + + // + + cgp.device = device; + cgp.adapter = adapter; + cgp.canvas = canvas; + + console.log(adapter); + console.log(device); + + context = canvas.getContext("webgpu"); + cgp.context = context; + + createTargets(cgp); + // pipeline = device.createRenderPipeline({ + // "layout": "auto", + // "vertex": + // { + // "module": device.createShaderModule( + // { + // "code": attachments.tri_wgsl_vert, + // }), + // "entryPoint": "main", + // }, + // "fragment": + // { + // "module": device.createShaderModule( + // { + // "code": attachments.tri_wgsl_frag, + // }), + // "entryPoint": "main", + // "targets": + // [ + // { + // "format": presentationFormat, + // }, + // ], + // }, + // "primitive": { + // "topology": "triangle-list", + // }, + // }); + // cgp.pipeline = pipeline; + + // canvasInfo.depthTextureView = depthTexture.createView(); + cgp.canvasInfo = canvasInfo; + + requestAnimationFrame(frame); + }); + }); + +function createTargets(cgp) +{ + const devicePixelRatio = 1;// window.devicePixelRatio || 1; + + sizeWidth = canvas.clientWidth * devicePixelRatio; + sizeHeight = canvas.clientHeight * devicePixelRatio; + + cgp.setSize(sizeWidth, sizeHeight); + + console.log("re create targets"); + // const presentationSize = [ + // canvas.clientWidth * devicePixelRatio, + // canvas.clientHeight * devicePixelRatio, + // ]; + + console.log("presentationSize", sizeWidth, sizeHeight); + const presentationFormat = navigator.gpu.getPreferredCanvasFormat(cgp.adapter); + cgp.presentationFormat = presentationFormat; + + context.configure({ + device, + "format": presentationFormat + }); + + if (renderTarget)renderTarget.destroy(); + if (depthTexture)depthTexture.destroy(); + + renderTarget = device.createTexture( + { + "size": [sizeWidth, sizeHeight], + "format": presentationFormat, + "sampleCount": sampleCount, + "usage": GPUTextureUsage.RENDER_ATTACHMENT, + }); + // canvasInfo.renderTarget = newRenderTarget; + // canvasInfo.renderTargetView = newRenderTarget.createView(); + // }); + + depthTexture = device.createTexture({ + "size": [sizeWidth, sizeHeight], + "format": "depth24plus", + "sampleCount": sampleCount, + "usage": GPUTextureUsage.RENDER_ATTACHMENT, + }); +} + +function frame() +{ + if (stopped) return; + if (!device) + { + requestAnimationFrame(frame); + + return; + } + + const devicePixelRatio = 1;// window.devicePixelRatio || 1; + if (sizeWidth != canvas.clientWidth * devicePixelRatio || sizeHeight != canvas.clientHeight * devicePixelRatio) + { + createTargets(cgp); + } + + // mat4.perspective(cgp.pMatrix, 45, canvas.clientWidth / canvas.clientHeight, 0.1, 1110.0); + // mat4.copy(cgp.pMatrix, pm); + + const commandEncoder = device.createCommandEncoder(); + + cgp.textureView = renderTarget.createView(); + cgp.canvasInfo.depthTextureView = depthTexture.createView(); + + // cgp.textureView = textureView; + const renderPassDescriptor = { + "colorAttachments": [ + { + "view": context.getCurrentTexture().createView(), + "loadOp": "clear", + // "resolveTarget": context.getCurrentTexture().createView(), + // "clearValue": { "r": 0.8, "g": 0.2, "b": 0.8, "a": 1.0 }, + "storeOp": "store", + }, + ], + "depthStencilAttachment": { + "view": cgp.canvasInfo.depthTextureView, + // "depthTexture": cgp.canvasInfo.depthTexture, + + "depthClearValue": 1, + "depthLoadOp": "clear", + "depthStoreOp": "store", + }, + }; + cgp.renderPassDescriptor = renderPassDescriptor; + cgp.passEncoder = commandEncoder.beginRenderPass(cgp.renderPassDescriptor); + + op.patch.cg = cgp; + // cgp.passEncoders = []; + cgp.renderStart(); + + const oldCgl = op.patch.cgl; + op.patch.cgl = null; // force crash if something tries to use it + next.trigger(); + + op.patch.cgl = oldCgl; + + cgp.renderEnd(); + + // for(let i=0;i,\n viewMatrix: mat4x4,\n projMatrix: mat4x4,\n};\n\nstruct FSUniforms\n{\n color:vec3//,\n // lightDirection: vec3,\n};\n\n@group(0) @binding(0) var vsUniforms: VSUniforms;\n@group(0) @binding(1) var fsUniforms: FSUniforms;\n\n\n\nstruct MyVSInput\n{\n @location(0) position: vec3,\n @location(1) normal: vec3,\n @location(2) texcoord: vec2,\n};\n\nstruct MyVSOutput\n{\n @builtin(position) position: vec4,\n @location(0) normal: vec3,\n @location(1) texcoord: vec2,\n};\n\n@vertex\nfn myVSMain(v: MyVSInput) -> MyVSOutput\n{\n var vsOut: MyVSOutput;\n var pos =vec4(v.position, 1.0);// vsUniforms.worldViewProjection * v.position;\n // vsOut.normal = (vsUniforms.worldInverseTranspose * vec4(v.normal, 0.0)).xyz;\n\n var mvMatrix=vsUniforms.viewMatrix * vsUniforms.modelMatrix;\n vsOut.position = vsUniforms.projMatrix * mvMatrix * pos;\n\n vsOut.normal = v.normal;\n vsOut.texcoord = v.texcoord;\n return vsOut;\n}\n\n\n\n\n\n// @group(0) @binding(2) var diffuseSampler: sampler;\n// @group(0) @binding(3) var diffuseTexture: texture_2d;\n\n@fragment\nfn myFSMain(v: MyVSOutput) -> @location(0) vec4\n{\n // var diffuseColor = textureSample(diffuseTexture, diffuseSampler, v.texcoord);\n // var a_normal = normalize(v.normal);\n // var l = dot(a_normal, fsUniforms.lightDirection) * 0.5 + 0.5;\n // return vec4(diffuseColor.rgb * l, diffuseColor.a);\n // return vec4(fsUniforms.color, 1.0);\n return vec4(v.normal.xyz+fsUniforms.color*0.001 , 1.0);\n}\n\n",}; +// !! https://alain.xyz/blog/raw-webgpu +// https://github.com/gfxfundamentals/webgpufundamentals/blob/main/webgpu/webgpu-cube.html + +const + inTrigger = op.inTrigger("Render"), + inGeom = op.inObject("Geometry", null, "geometry"), + next = op.outTrigger("Next"); + +let cgp = op.patch.cgp; +let needsbuild = true; + +let bindGroup; +let positionBuffer; +let normalBuffer; +let texcoordBuffer; +let indicesBuffer; +let geom = null; +let pipeline; +let renderPassDescriptor; +let numIndices = 0; + +let depthTexture, depthTextureView; + +let vsUniformBuffer; +let fsUniformBuffer; +let vsUniformValues; +let fsUniformValues; +let matModel; +let matView; +let matProj; + +let mesh = null; +let shader = null; +let pipe = null; + +inTrigger.onTriggered = () => +{ + cgp = op.patch.cgp; + if (needsbuild)rebuild(); + + if (geom && cgp.renderPassDescriptor) + { + mat4.copy(matModel, cgp.mMatrix); + mat4.copy(matView, cgp.vMatrix); + mat4.copy(matProj, cgp.pMatrix); + + cgp.device.queue.writeBuffer( + vsUniformBuffer, + 0, + vsUniformValues.buffer, + vsUniformValues.byteOffset, + vsUniformValues.byteLength + ); + + // const colorTexture = cgp.context.getCurrentTexture(); + // renderPassDescriptor.colorAttachments[0].view = colorTexture.createView(); + + // const commandEncoder = cgp.device.createCommandEncoder(); + // const passEncoder = commandEncoder.beginRenderPass(cgp.renderPassDescriptor); + + // cgp.passEncoder.setPipeline(pipeline); + cgp.passEncoder.setPipeline(pipeline); + + cgp.passEncoder.setBindGroup(0, bindGroup); + cgp.passEncoder.setVertexBuffer(0, positionBuffer); + cgp.passEncoder.setVertexBuffer(1, normalBuffer); + cgp.passEncoder.setVertexBuffer(2, texcoordBuffer); + cgp.passEncoder.setIndexBuffer(indicesBuffer, "uint32"); + + cgp.passEncoder.drawIndexed(numIndices); + // else cgp.passEncoder.draw(geom.vertices.length ); + + // passEncoder.end(); + + // cgp.passEncoders.push(commandEncoder.finish()); + } + + cgp.pushShader(shader); + next.trigger(); + cgp.popShader(); +}; + +inGeom.onChange = () => +{ + needsbuild = true; +}; + +// @group(0) @binding(2) var diffuseSampler: sampler; +// @group(0) @binding(3) var diffuseTexture: texture_2d; + +function createShaderModule(device, code) +{ + cgp.pushErrorScope("validation"); + + const shaderModule = device.createShaderModule({ code }); + // const error = await device.popErrorScope(); + // if (error) { + // throw new Error(error.message); + // } + + cgp.popErrorScope("cgp_pipeline createshadermodule"); + + return shaderModule; +} + +function createBuffer(device, data, usage) +{ + const buffer = device.createBuffer({ + "size": data.byteLength, + "usage": usage, + "mappedAtCreation": true, + }); + const dst = new data.constructor(buffer.getMappedRange()); + dst.set(data); + buffer.unmap(); + return buffer; +} + +function rebuild() +{ + geom = inGeom.get(); + if (!geom) return; + + positionBuffer = createBuffer(cgp.device, new Float32Array(geom.vertices), GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST); + normalBuffer = createBuffer(cgp.device, new Float32Array(geom.vertexNormals), GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST); + texcoordBuffer = createBuffer(cgp.device, new Float32Array(geom.texcoords), GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST); + + let vi = geom.verticesIndices; + if (!geom.isIndexed()) + { + vi = Array.from(Array(geom.vertices.length / 3).keys()); + } + + numIndices = vi.length; + indicesBuffer = createBuffer(cgp.device, new Uint32Array(vi), GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST); + + if (!shader)shader = new CGP.Shader(cgp, "testshad0r"); + + shader.shaderModule = createShaderModule(cgp.device, attachments.mesh_wgsl); + + cgp.pushErrorScope(); + + if (!pipe)pipe = new CGP.Pipeline(cgp); + + const pipeCfg = pipe.getPiplelineObject(shader, mesh); + console.log(pipeCfg); + pipeline = cgp.device.createRenderPipeline(pipeCfg); + + cgp.popErrorScope(op.name); + + const vUniformBufferSize = 3 * 16 * 4; // 2 mat4s * 16 floats per mat * 4 bytes per float + const fUniformBufferSize = 2 * 3 * 4; // 1 vec3 * 3 floats per vec3 * 4 bytes per float + + vsUniformBuffer = cgp.device.createBuffer({ + "size": vUniformBufferSize, + "usage": GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }); + + fsUniformBuffer = cgp.device.createBuffer({ + "size": fUniformBufferSize, + "usage": GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }); + + vsUniformValues = new Float32Array(vUniformBufferSize / 4); + fsUniformValues = new Float32Array(fUniformBufferSize / 4); + + matModel = vsUniformValues.subarray(0, 16); + matView = vsUniformValues.subarray(16, 32); + matProj = vsUniformValues.subarray(32, 48); + + fsUniformValues[1] = 1.0; + fsUniformValues[0] = 1.0; + const lightDirection = fsUniformValues.subarray(0, 3); + + // console.log("pipeline bindgrouplayout ", pipeline.getBindGroupLayout(0)); + + bindGroup = cgp.device.createBindGroup( + { + "layout": pipeline.getBindGroupLayout(0), + "entries": [ + { "binding": 0, "resource": { "buffer": vsUniformBuffer } }, + { "binding": 1, "resource": { "buffer": fsUniformBuffer } } + // { binding: 2, resource: sampler }, + // { binding: 3, resource: tex.createView() }, + ], + }); + + cgp.device.queue.writeBuffer( + vsUniformBuffer, + 0, + vsUniformValues.buffer, + vsUniformValues.byteOffset, + vsUniformValues.byteLength + ); + cgp.device.queue.writeBuffer( + fsUniformBuffer, + 0, + fsUniformValues.buffer, + fsUniformValues.byteOffset, + fsUniformValues.byteLength + ); + + const textureView = cgp.context.getCurrentTexture().createView(); + + needsbuild = false; +} + + +}; + +Ops.Dev.WebGpu.WebGpuMesh.prototype = new CABLES.Op(); +CABLES.OPS["daf22c14-6ba3-4c63-83d2-27e9e60d3dbf"]={f:Ops.Dev.WebGpu.WebGpuMesh,objName:"Ops.Dev.WebGpu.WebGpuMesh"}; + + + + +// ************************************************************** +// +// Ops.Dev.WebGpu.WebGpuMinimalMaterial +// +// ************************************************************** + +Ops.Dev.WebGpu.WebGpuMinimalMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"mat_wgsl":"struct VSUniforms\n{\n modelMatrix: mat4x4,\n viewMatrix: mat4x4,\n projMatrix: mat4x4,\n};\n\nstruct FSUniforms\n{\n color:vec4\n};\n\n@group(0) @binding(0) var vsUniforms: VSUniforms;\n@group(0) @binding(1) var fsUniforms: FSUniforms;\n\nstruct MyVSInput\n{\n @location(0) position: vec3,\n @location(1) normal: vec3,\n @location(2) texcoord: vec2,\n};\n\nstruct MyVSOutput\n{\n @builtin(position) position: vec4,\n @location(0) normal: vec3,\n @location(1) texcoord: vec2,\n};\n\n@vertex\nfn myVSMain(v: MyVSInput) -> MyVSOutput\n{\n var vsOut: MyVSOutput;\n var pos =vec4(v.position, 1.0);\n\n var mvMatrix=vsUniforms.viewMatrix * vsUniforms.modelMatrix;\n vsOut.position = vsUniforms.projMatrix * mvMatrix * pos;\n\n vsOut.normal = v.normal;\n vsOut.texcoord = v.texcoord;\n return vsOut;\n}\n\n@fragment\nfn myFSMain(v: MyVSOutput) -> @location(0) vec4\n{\n return fsUniforms.color;\n}\n\n",}; +const + inTrigger = op.inTrigger("Render"), + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + a = op.inValueSlider("a", 1), + next = op.outTrigger("Next"); + +r.setUiAttribs({ "colorPick": true }); + +let shader = null; + +inTrigger.onTriggered = () => +{ + const cgp = op.patch.cg; + if (!shader) + { + shader = new CGP.Shader(cgp, op.name); + shader.setSource(attachments.mat_wgsl); + + shader.addUniformFrag("4f", "color", r, g, b, a); + } + + cgp.pushShader(shader); + + next.trigger(); + + cgp.popShader(shader); +}; + + +}; + +Ops.Dev.WebGpu.WebGpuMinimalMaterial.prototype = new CABLES.Op(); +CABLES.OPS["681ffbbf-cdf4-4d4f-ad7c-934b3a601a80"]={f:Ops.Dev.WebGpu.WebGpuMinimalMaterial,objName:"Ops.Dev.WebGpu.WebGpuMinimalMaterial"}; + + + + +// ************************************************************** +// +// Ops.Dev.WebGpu.WebGpuTexture +// +// ************************************************************** + +Ops.Dev.WebGpu.WebGpuTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + trigger = op.inTrigger("trigger"), + filename = op.inUrl("File", [".jpg", ".png", ".webp", ".jpeg", ".avif"]), + result = op.outTexture("Texture"); + +let needsReload = false; + +filename.onChange = () => +{ + needsReload = true; +}; + +trigger.onTriggered = () => +{ + if (needsReload) + { + needsReload = false; + + CGP.Texture.load(op.patch.cgp, filename.get(), (t) => + { + result.set(t); + }); + } +}; + + +}; + +Ops.Dev.WebGpu.WebGpuTexture.prototype = new CABLES.Op(); +CABLES.OPS["3a952a05-8b7b-4dab-9c08-f2c00f6cd899"]={f:Ops.Dev.WebGpu.WebGpuTexture,objName:"Ops.Dev.WebGpu.WebGpuTexture"}; + + + + +// ************************************************************** +// +// Ops.Device.Mobile.ShakeGesture +// +// ************************************************************** + +Ops.Device.Mobile.ShakeGesture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + accX = op.outNumber("Acceleration X"), + accY = op.outNumber("Acceleration Y"), + accZ = op.outNumber("Acceleration Z"); + +let lastTime = 0; +let lastTimeAcc = 0; + +let obj = {}; + +registerEvents(); + +let lastX = 0; +let lastY = 0; +let lastZ = 0; + +function moved(event) +{ + if (CABLES.now() - lastTimeAcc > 30) + { + lastTimeAcc = CABLES.now(); + + let deltaX = lastX - event.accelerationIncludingGravity.x; + let deltaY = lastY - event.accelerationIncludingGravity.y; + let deltaZ = lastZ - event.accelerationIncludingGravity.z; + + accX.set(Math.abs(deltaX)); + accY.set(Math.abs(deltaY)); + accZ.set(Math.abs(deltaZ)); + + lastX = event.accelerationIncludingGravity.x; + lastY = event.accelerationIncludingGravity.y; + lastZ = event.accelerationIncludingGravity.z; + } +} + +function registerEvents() +{ + window.addEventListener("devicemotion", moved, true); +} + +op.onDelete = function () +{ + window.removeEventListener("devicemotion", moved); +}; + + +}; + +Ops.Device.Mobile.ShakeGesture.prototype = new CABLES.Op(); +CABLES.OPS["7b32f8ad-ff4a-4634-927b-ede9955e3b53"]={f:Ops.Device.Mobile.ShakeGesture,objName:"Ops.Device.Mobile.ShakeGesture"}; + + + + +// ************************************************************** +// +// Ops.Devices.GamePad.GamePad +// +// ************************************************************** + +Ops.Devices.GamePad.GamePad = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let data = op.inObject("GamePad Data"); +let outID = op.outString("ID"); +let digitalAnalog = op.inValueBool("Analog to Digital", true); +let outAxes = op.outArray("Axes"); + +let pressedLeft = op.outBoolNum("Pad Left"); +let pressedRight = op.outBoolNum("Pad Right"); +let pressedUp = op.outBoolNum("Pad Up"); +let pressedDown = op.outBoolNum("Pad Down"); + +let pressedButton1 = op.outBoolNum("Button 1"); +let pressedButton2 = op.outBoolNum("Button 2"); +let pressedButton3 = op.outBoolNum("Button 3"); +let pressedButton4 = op.outBoolNum("Button 4"); + +let outLeftShoulder = op.outNumber("Left Shoulder"); +let outLeftShoulderBottom = op.outNumber("Left Shoulder Bottom"); + +let outRightShoulder = op.outNumber("Right Shoulder"); +let outRightShoulderBottom = op.outNumber("Right Shoulder Bottom"); + +data.onChange = function () +{ + if (data.get()) + { + outID.set(data.get().id); + if (data.get().axes)outAxes.set(data.get().axes); + + let buttons = data.get().buttons; + if (buttons) + { + pressedButton1.set(buttons[0].pressed); + pressedButton2.set(buttons[1].pressed); + pressedButton3.set(buttons[2].pressed); + pressedButton4.set(buttons[3].pressed); + } + + if (digitalAnalog.get()) + { + let axes = data.get().axes; + if (axes) + { + if (axes[0] < -0.5)pressedLeft.set(true); + else pressedLeft.set(false || buttons[14].pressed); + if (axes[0] > 0.5)pressedRight.set(true); + else pressedRight.set(false || buttons[15].pressed); + + if (axes[1] < -0.5)pressedUp.set(true); + else pressedUp.set(false || buttons[12].pressed); + if (axes[1] > 0.5)pressedDown.set(true); + else pressedDown.set(false || buttons[13].pressed); + } + } + else + { + pressedLeft.set(buttons[14].pressed); + pressedRight.set(buttons[15].pressed); + pressedDown.set(buttons[13].pressed); + pressedUp.set(buttons[12].pressed); + } + + outLeftShoulder.set(buttons[4].value); + outLeftShoulderBottom.set(buttons[6].value); + + outRightShoulder.set(buttons[5].value); + outRightShoulderBottom.set(buttons[7].value); + } +}; + +// gamepad.BUTTONS = { +// FACE_1: 0, // Face (main) buttons +// FACE_2: 1, +// FACE_3: 2, +// FACE_4: 3, +// LEFT_SHOULDER: 4, // Top shoulder buttons +// RIGHT_SHOULDER: 5, +// LEFT_SHOULDER_BOTTOM: 6, // Bottom shoulder buttons +// RIGHT_SHOULDER_BOTTOM: 7, +// SELECT: 8, +// START: 9, +// LEFT_ANALOGUE_STICK: 10, // Analogue sticks (if depressible) +// RIGHT_ANALOGUE_STICK: 11, +// PAD_TOP: 12, // Directional (discrete) pad +// PAD_BOTTOM: 13, +// PAD_LEFT: 14, +// PAD_RIGHT: 15 +// }; + +// gamepad.AXES = { +// LEFT_ANALOGUE_HOR: 0, +// LEFT_ANALOGUE_VERT: 1, +// RIGHT_ANALOGUE_HOR: 2, +// RIGHT_ANALOGUE_VERT: 3 +// }; + + +}; + +Ops.Devices.GamePad.GamePad.prototype = new CABLES.Op(); +CABLES.OPS["3acf39ed-53cc-45eb-8516-43319839d457"]={f:Ops.Devices.GamePad.GamePad,objName:"Ops.Devices.GamePad.GamePad"}; + + + + +// ************************************************************** +// +// Ops.Devices.GamePad.GamePadJoystickAxis +// +// ************************************************************** + +Ops.Devices.GamePad.GamePadJoystickAxis = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inAxis = op.inArray("Axis"), + inAxisNum = op.inValueInt("Index"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + inDeadZone = op.outNumber("DeadZone", 0.1), + outAngle = op.outNumber("Angle"); + +inAxis.onChange = function () +{ + let arr = inAxis.get(); + if (!arr) return; + + let idx = inAxisNum.get() * 2; + + let x = 0; + let y = 0; + + if (arr[idx + 0] > 0)x = CABLES.map(arr[idx + 0], inDeadZone.get(), 1, 0, 1); + else x = CABLES.map(arr[idx + 0], -1, -inDeadZone.get(), -1, 0); + + if (arr[idx + 1] > 0)y = CABLES.map(arr[idx + 1], inDeadZone.get(), 1, 0, 1); + else y = CABLES.map(arr[idx + 1], -1, -inDeadZone.get(), -1, 0); + + outX.set(x); + outY.set(y); + + if (x != 0 || y != 0) + { + let theta = Math.atan2(x, y); + + let angle = theta * 180 / Math.PI * -1; + outAngle.set(360 - (angle + 180)); + } +}; + + +}; + +Ops.Devices.GamePad.GamePadJoystickAxis.prototype = new CABLES.Op(); +CABLES.OPS["05be9e0d-c6bd-42f6-9f2c-16ae5ad4db7a"]={f:Ops.Devices.GamePad.GamePadJoystickAxis,objName:"Ops.Devices.GamePad.GamePadJoystickAxis"}; + + + + +// ************************************************************** +// +// Ops.Devices.GamePad.GamePads +// +// ************************************************************** + +Ops.Devices.GamePad.GamePads = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + outNumPads = op.outNumber("Num Gamepads"), + pad0 = op.outObject("Pad 0"), + pad1 = op.outObject("Pad 1"), + pad2 = op.outObject("Pad 2"), + pad3 = op.outObject("Pad 3"); + +exe.onTriggered = function () +{ + let gamePads = navigator.getGamepads(); + let count = 0; + + for (let gp = 0; gp < gamePads.length; gp++) + { + if (gamePads[gp]) + { + count++; + + if (gp == 0) + { + pad0.set(null); + pad0.set(gamePads[gp]); + } + if (gp == 1) + { + pad1.set(null); + pad1.set(gamePads[gp]); + } + if (gp == 2) + { + pad2.set(null); + pad2.set(gamePads[gp]); + } + if (gp == 3) + { + pad3.set(null); + pad3.set(gamePads[gp]); + } + } + } + + outNumPads.set(count); +}; + +exe.onTriggered(); + + +}; + +Ops.Devices.GamePad.GamePads.prototype = new CABLES.Op(); +CABLES.OPS["76b42a0b-21a4-4eef-b20c-5a969a2189f0"]={f:Ops.Devices.GamePad.GamePads,objName:"Ops.Devices.GamePad.GamePads"}; + + + + +// ************************************************************** +// +// Ops.Devices.Keyboard.CursorKeys +// +// ************************************************************** + +Ops.Devices.Keyboard.CursorKeys = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + canvasOnly = op.inValueBool("canvas only", true), + keysCursor = op.inValueBool("Cursor Keys", true), + keysWasd = op.inValueBool("WASD", true), + inActive = op.inBool("Active", true), + pressedUp = op.outBoolNum("Up"), + pressedDown = op.outBoolNum("Down"), + pressedLeft = op.outBoolNum("Left"), + pressedRight = op.outBoolNum("Right"); + +const cgl = op.patch.cgl; + +function onKeyDown(e) +{ + if (keysWasd.get()) + { + if (e.keyCode == 87) pressedUp.set(true); + if (e.keyCode == 83) pressedDown.set(true); + if (e.keyCode == 65) pressedLeft.set(true); + if (e.keyCode == 68) pressedRight.set(true); + } + if (keysCursor.get()) + { + if (e.keyCode == 38) pressedUp.set(true); + if (e.keyCode == 40) pressedDown.set(true); + if (e.keyCode == 37) pressedLeft.set(true); + if (e.keyCode == 39) pressedRight.set(true); + } +} + +function onKeyUp(e) +{ + if (keysWasd.get()) + { + if (e.keyCode == 87) pressedUp.set(false); + if (e.keyCode == 83) pressedDown.set(false); + if (e.keyCode == 65) pressedLeft.set(false); + if (e.keyCode == 68) pressedRight.set(false); + } + if (keysCursor.get()) + { + if (e.keyCode == 38) pressedUp.set(false); + if (e.keyCode == 40) pressedDown.set(false); + if (e.keyCode == 37) pressedLeft.set(false); + if (e.keyCode == 39) pressedRight.set(false); + } +} + +op.onDelete = function () +{ + cgl.canvas.removeEventListener("keyup", onKeyUp, false); + cgl.canvas.removeEventListener("keydown", onKeyDown, false); + document.removeEventListener("keyup", onKeyUp, false); + document.removeEventListener("keydown", onKeyDown, false); +}; + +function addListener() +{ + if (canvasOnly.get()) addCanvasListener(); + else addDocumentListener(); +} + +function onBlur() +{ + pressedUp.set(false); + pressedDown.set(false); + pressedLeft.set(false); + pressedRight.set(false); +} + +inActive.onChange = () => +{ + pressedUp.set(false); + pressedDown.set(false); + pressedLeft.set(false); + pressedRight.set(false); +}; + +function removeListeners() +{ + cgl.canvas.removeEventListener("blur", onBlur); + document.removeEventListener("keydown", onKeyDown, false); + document.removeEventListener("keyup", onKeyUp, false); + cgl.canvas.removeEventListener("keydown", onKeyDown, false); + cgl.canvas.removeEventListener("keyup", onKeyUp, false); +} + +function addCanvasListener() +{ + cgl.canvas.addEventListener("blur", onBlur); + + cgl.canvas.addEventListener("keydown", onKeyDown, false); + cgl.canvas.addEventListener("keyup", onKeyUp, false); +} + +function addDocumentListener() +{ + document.addEventListener("keydown", onKeyDown, false); + document.addEventListener("keyup", onKeyUp, false); +} + +canvasOnly.onChange = function () +{ + removeListeners(); + addListener(); +}; + +canvasOnly.set(true); +addCanvasListener(); + + +}; + +Ops.Devices.Keyboard.CursorKeys.prototype = new CABLES.Op(); +CABLES.OPS["65930ca9-c923-453f-b8c4-144eda13fa0a"]={f:Ops.Devices.Keyboard.CursorKeys,objName:"Ops.Devices.Keyboard.CursorKeys"}; + + + + +// ************************************************************** +// +// Ops.Devices.Keyboard.KeyPressLearn +// +// ************************************************************** + +Ops.Devices.Keyboard.KeyPressLearn = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const learnedKeyCode = op.inValueInt("key code"); +const canvasOnly = op.inValueBool("canvas only", true); +const modKey = op.inValueSelect("Mod Key", ["none", "alt"], "none"); +const inEnable = op.inValueBool("Enabled", true); +const preventDefault = op.inValueBool("Prevent Default"); +const learn = op.inTriggerButton("learn"); +const onPress = op.outTrigger("on press"); +const onRelease = op.outTrigger("on release"); +const outPressed = op.outBoolNum("Pressed", false); +const outKey = op.outString("Key"); + +const cgl = op.patch.cgl; +let learning = false; + +modKey.onChange = learnedKeyCode.onChange = updateKeyName; + +function onKeyDown(e) +{ + if (learning) + { + learnedKeyCode.set(e.keyCode); + if (CABLES.UI) + { + op.refreshParams(); + } + // op.log("Learned key code: " + learnedKeyCode.get()); + learning = false; + removeListeners(); + addListener(); + + if (CABLES.UI)gui.emitEvent("portValueEdited", op, learnedKeyCode, learnedKeyCode.get()); + } + else + { + if (e.keyCode == learnedKeyCode.get()) + { + if (modKey.get() == "alt") + { + if (e.altKey === true) + { + onPress.trigger(); + outPressed.set(true); + if (preventDefault.get())e.preventDefault(); + } + } + else + { + onPress.trigger(); + outPressed.set(true); + if (preventDefault.get())e.preventDefault(); + } + } + } +} + +function onKeyUp(e) +{ + if (e.keyCode == learnedKeyCode.get()) + { + // op.log("Key released, key code: " + e.keyCode); + onRelease.trigger(); + outPressed.set(false); + } +} + +op.onDelete = function () +{ + cgl.canvas.removeEventListener("keyup", onKeyUp, false); + cgl.canvas.removeEventListener("keydown", onKeyDown, false); + document.removeEventListener("keyup", onKeyUp, false); + document.removeEventListener("keydown", onKeyDown, false); +}; + +learn.onTriggered = function () +{ + // op.log("Listening for key..."); + learning = true; + addDocumentListener(); + + setTimeout(function () + { + learning = false; + removeListeners(); + addListener(); + }, 3000); +}; + +function addListener() +{ + if (canvasOnly.get()) addCanvasListener(); + else addDocumentListener(); +} + +function removeListeners() +{ + document.removeEventListener("keydown", onKeyDown, false); + document.removeEventListener("keyup", onKeyUp, false); + cgl.canvas.removeEventListener("keydown", onKeyDown, false); + cgl.canvas.removeEventListener("keyup", onKeyUp, false); + outPressed.set(false); +} + +function addCanvasListener() +{ + cgl.canvas.addEventListener("keydown", onKeyDown, false); + cgl.canvas.addEventListener("keyup", onKeyUp, false); +} + +function addDocumentListener() +{ + document.addEventListener("keydown", onKeyDown, false); + document.addEventListener("keyup", onKeyUp, false); +} + +inEnable.onChange = function () +{ + if (!inEnable.get()) + { + removeListeners(); + } + else + { + addListener(); + } +}; + +canvasOnly.onChange = function () +{ + removeListeners(); + addListener(); +}; + +function updateKeyName() +{ + let keyName = CABLES.keyCodeToName(learnedKeyCode.get()); + const modKeyName = modKey.get(); + if (modKeyName && modKeyName !== "none") + { + keyName = modKeyName.charAt(0).toUpperCase() + modKeyName.slice(1) + "-" + keyName; + } + op.setUiAttribs({ "extendTitle": keyName }); + outKey.set(keyName); +} + +addCanvasListener(); + + +}; + +Ops.Devices.Keyboard.KeyPressLearn.prototype = new CABLES.Op(); +CABLES.OPS["f069c0db-4051-4eae-989e-6ef7953787fd"]={f:Ops.Devices.Keyboard.KeyPressLearn,objName:"Ops.Devices.Keyboard.KeyPressLearn"}; + + + + +// ************************************************************** +// +// Ops.Devices.Keyboard.KeyPress_v2 +// +// ************************************************************** + +Ops.Devices.Keyboard.KeyPress_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inArea = op.inValueSelect("Area", ["Canvas", "Document", "Parent Element"], "Canvas"); +const preventDefault = op.inBool("Prevent Default", false); +const inEnable = op.inBool("Enabled", true); +const onPress = op.outTrigger("On Press"); +const keyCode = op.outNumber("Key Code"); +const outKey = op.outString("Key"); + +const cgl = op.patch.cgl; +let listenerElement = null; + +inArea.onChange = inEnable.onChange = () => +{ + if (inEnable.get()) + { + addListeners(); + } + else + { + removeListeners(); + } +}; + +op.onDelete = () => +{ + removeListeners(); +}; + +function onKeyPress(e) +{ + const keyName = CABLES.keyCodeToName(e.keyCode); + op.setUiAttribs({ "extendTitle": keyName }); + outKey.set(keyName); + keyCode.set(e.keyCode); + onPress.trigger(); + if (preventDefault.get()) e.preventDefault(); +} + +function addListeners() +{ + if (listenerElement) removeListeners(); + + listenerElement = cgl.canvas; + if (inArea.get() === "Document") listenerElement = document.body; + if (inArea.get() === "Parent Element") listenerElement = cgl.canvas.parentElement; + + listenerElement.addEventListener("keydown", onKeyPress); +} + +function removeListeners() +{ + if (!listenerElement) return; + listenerElement.removeEventListener("keydown", onKeyPress); + listenerElement = null; +} + +addListeners(); + + +}; + +Ops.Devices.Keyboard.KeyPress_v2.prototype = new CABLES.Op(); +CABLES.OPS["023d1e35-1231-4c50-a044-4a0e63609ba5"]={f:Ops.Devices.Keyboard.KeyPress_v2,objName:"Ops.Devices.Keyboard.KeyPress_v2"}; + + + + +// ************************************************************** +// +// Ops.Devices.Keyboard.QWERTYtoMidi +// +// ************************************************************** + +Ops.Devices.Keyboard.QWERTYtoMidi = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let canvasOnly = op.addInPort(new CABLES.Port(op, "canvas only", CABLES.OP_PORT_TYPE_VALUE, { "display": "bool" })); + +let noteNumberPort = op.outValue("Note Number"); +let velocityPort = op.outValue("Velocity"); +let channelPort = op.outValue("Channel"); +let commandPort = op.outValue("Command"); + +let cgl = op.patch.cgl; + +function midiMessageReceived(msgs) +{ + for (let i = 0; i < msgs.length; i++) + { + let cmd = msgs[i].data[0] >> 4; + let channel = msgs[i].data[0] & 0xf; + let noteNumber = msgs[i].data[1]; + let velocity = msgs[i].data[2]; + + noteNumberPort.set(noteNumber); + velocityPort.set(velocity); + channelPort.set(channel); + commandPort.set(cmd); + + /* + if (cmd==8) { + //myNode.noteOff(0); + } else if (cmd == 9) { + //myNode.noteOn(0); + } + */ + } +} + +/* + * Midikeys.js + * > Turn your keyboard into a midi keyboard, compatible with the Web MIDI API. + * Copyright 2012 Nick Thompson + * MIT License + * https://gist.github.com/3995530 + */ + +// Keycode to MIDI note values +let map = {}; + +map[81] = 72; // q C5 +map[87] = 74; // w D5 +map[69] = 76; // e E5 +map[82] = 77; // r F5 +map[84] = 79; // t G5 +map[89] = 81; // y A5 +map[85] = 83; // u B5 +map[73] = 84; // i C6 +map[79] = 86; // o D6 +map[80] = 88; // p E6 +map[219] = 89; // [ F6 +map[221] = 91; // ] G6 + +map[83] = 61; // s C#4 +map[68] = 63; // d D#4 +map[71] = 66; // g F#4 +map[72] = 68; // h G#4 +map[74] = 70; // j A#4 +map[76] = 73; // l C#5 +map[186] = 75; // ; D#5 + +map[90] = 60; // z C4 +map[88] = 62; // x D4 +map[67] = 64; // c E4 +map[86] = 65; // v F4 +map[66] = 67; // b G4 +map[78] = 69; // n A4 +map[77] = 71; // m B4 +map[188] = 72; // , C5 +map[190] = 74; // . D5 +map[191] = 76; // / E5 + +// Keep track of keydown and keyup events so that the keydown event doesn't +// send messages repeatedly until keyup. +let flags = {}; + +function sendMessage(e, command) +{ + // Check the event key against the midi map. + let note = map[(typeof e.which === "number") ? e.which : e.keyCode]; + + // If the key doesn't exist in the midi map, or we're trying to send a + // noteOn event without having most recently sent a noteOff, end here. + if (note === undefined || (flags[note] && command === 0x9)) + { + return false; + } + + // Build the data + let data = new Uint8Array(3); + + data[0] = (command << 4) + 0x00; // Send the command on channel 0 + data[1] = note; // Attach the midi note + data[2] = 127; // Keyboard keys default to 127 velocity. + + // Package the message + let msg = { + "data": data, + "timestamp": 0 + }; + + // Send it + api.onmessage.call(window, [msg]); + + // Update the flag table + if (command === 0x9) + { + flags[note] = true; + } + else + { + flags[note] = false; + } +} + +// MIDIKeys api object, to be exposed as window.Keys +let api = { + + // Expose the onmessage parameter like on a MIDIInput object + "onmessage": null + +}; + +let MIDIKeys = api; + +// Including Midikeys.js in your project file packages fake MIDI messages the same as a normal message, +// so you can then just attach your message handler to MIDI keys and you're done + +MIDIKeys.onmessage = midiMessageReceived; + +function onKeyDown(e) +{ + sendMessage(e, 0x09); +} + +function onKeyUp(e) +{ + sendMessage(e, 0x08); +} + +function addListener() +{ + if (canvasOnly.get()) + { + addCanvasListener(); + } + else + { + addDocumentListener(); + } +} + +function removeListeners() +{ + document.removeEventListener("keydown", onKeyDown, false); + document.removeEventListener("keyup", onKeyUp, false); + cgl.canvas.removeEventListener("keydown", onKeyDown, false); + cgl.canvas.removeEventListener("keyup", onKeyUp, false); +} + +function addCanvasListener() +{ + cgl.canvas.addEventListener("keydown", onKeyDown, false); + cgl.canvas.addEventListener("keyup", onKeyUp, false); +} + +function addDocumentListener() +{ + document.addEventListener("keydown", onKeyDown, false); + document.addEventListener("keyup", onKeyUp, false); +} + +canvasOnly.onChange = function () +{ + removeListeners(); + addListener(); +}; + +canvasOnly.set(true); +addCanvasListener(); + + +}; + +Ops.Devices.Keyboard.QWERTYtoMidi.prototype = new CABLES.Op(); +CABLES.OPS["0aecef8b-3bcb-44ac-ae62-38ce8cd4051e"]={f:Ops.Devices.Keyboard.QWERTYtoMidi,objName:"Ops.Devices.Keyboard.QWERTYtoMidi"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.DeviceList +// +// ************************************************************** + +Ops.Devices.Midi.DeviceList = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const outNumDevices = op.outValue("Num Devices"); +const outSupport = op.outValueBool("Midi Support"); +const outNames = op.outArray("Device Names"); + +let midi = null; + +function onMIDIFailure() +{ + outSupport.set(false); +} + +if (navigator.requestMIDIAccess) navigator.requestMIDIAccess({ "sysex": false }).then(onMIDISuccess, onMIDIFailure); +else onMIDIFailure(); + +function onMIDISuccess(midiAccess) +{ + let arr = []; + midi = midiAccess; + outSupport.set(true); + let inputs = midi.inputs.values(); + + let devices = []; + let numDevices = 0; + + for (let input = inputs.next(); input && !input.done; input = inputs.next()) + { + arr.push(input.value.name); + numDevices++; + } + + outNames.set(arr); + outNumDevices.set(numDevices); +} + + +}; + +Ops.Devices.Midi.DeviceList.prototype = new CABLES.Op(); +CABLES.OPS["6e52048f-557d-4332-9d5e-2201f728cb23"]={f:Ops.Devices.Midi.DeviceList,objName:"Ops.Devices.Midi.DeviceList"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiCC +// +// ************************************************************** + +Ops.Devices.Midi.MidiCC = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +/* UTIL */ +const MIDIChannels = Array.from(Array(16).keys(), (i) => { return i + 1; }); +/* IN */ +const inEvent = op.inObject("MIDI Event In"); +const midiChannelDropdown = op.inValueSelect("MIDI Channel", MIDIChannels, 1); +const ccIndexDropdown = op.inValueInt("CC Index", 0); +const normalizeDropdown = op.inSwitch("Normalize", ["none", "0 to 1", "-1 to 1"], "none"); +const learn = op.inTriggerButton("learn"); +const clear = op.inTriggerButton("clear"); + +op.setPortGroup("MIDI", [inEvent, midiChannelDropdown]); +op.setPortGroup("CC", [ccIndexDropdown, normalizeDropdown]); +op.setPortGroup("", [learn, clear]); + +const ccArray = Array.from(Array(128).keys(), (key) => { return 0; }); + +/* OUT */ +const eventOut = op.outObject("Event"); +const triggerOut = op.outTrigger("Trigger Out"); +const ccIndexOut = op.outNumber("CC Index Out"); +const ccValueOut = op.outNumber("CC Value Out"); +const arrayOut = op.outArray("Value Array"); + +op.setPortGroup("MIDI/Trigger Out", [eventOut, triggerOut]); +op.setPortGroup("CC Out", [ccIndexOut, ccValueOut]); + +arrayOut.set(ccArray); + +ccIndexDropdown.set(0); +midiChannelDropdown.set(1); +normalizeDropdown.set(normalizeDropdown.get("none")); + +let learning = false; +learn.onTriggered = () => +{ + learning = true; +}; + +clear.onTriggered = () => +{ + ccIndexDropdown.set(0); + midiChannelDropdown.set(1); + normalizeDropdown.set(normalizeDropdown.get("none")); + op.refreshParams(); +}; + +inEvent.onChange = () => +{ + const event = inEvent.get(); + if (!event) return; + if (event.messageType !== "CC") return; + + const [, ccIndex, ccValue] = event.data; + if (learning) + { + ccIndexDropdown.set(ccIndex); + midiChannelDropdown.set(event.channel + 1); + + learning = false; + + if (CABLES.UI) + { + gui.emitEvent("portValueEdited", op, ccIndexDropdown, ccIndexDropdown.get()); + gui.emitEvent("portValueEdited", op, midiChannelDropdown, midiChannelDropdown.get()); + + op.uiAttr({ "info": `bound to CC: ${ccIndexDropdown.get()}` }); + op.refreshParams(); + } + } + + if (event.channel === midiChannelDropdown.get() - 1) + { + if (normalizeDropdown.get() === "0 to 1") + { + ccArray[ccIndex] = ccValue / 127; + } + else if (normalizeDropdown.get() === "-1 to 1") + { + ccArray[ccIndex] = ccValue / (127 / 2) - 1; + } + else if (normalizeDropdown.get() === "none") + { + ccArray[ccIndex] = ccValue; + } + + if (ccIndex === ccIndexDropdown.get()) + { + ccIndexOut.set(ccIndex); + let value = ccValue; + ccArray[ccIndex] = ccValue; + if (normalizeDropdown.get() === "0 to 1") + { + value = ccValue / 127; + ccValueOut.set(value); + ccArray[ccIndex] = ccValue; + triggerOut.trigger(); + } + else if (normalizeDropdown.get() === "-1 to 1") + { + value = ccValue / (127 / 2) - 1; + triggerOut.trigger(); + ccValueOut.set(value); + ccArray[ccIndex] = ccValue; + } + else if (normalizeDropdown.get() === "none") + { + triggerOut.trigger(); + ccValueOut.set(value); + ccArray[ccIndex] = ccValue; + } + else + { + ccArray[ccIndex] = 0; + ccValue.set(0); + } + } + } + + arrayOut.set(null); + arrayOut.set(ccArray); + eventOut.set(null); + eventOut.set(event); +}; + + +}; + +Ops.Devices.Midi.MidiCC.prototype = new CABLES.Op(); +CABLES.OPS["28616ffc-a761-4792-9076-8625f9ccc965"]={f:Ops.Devices.Midi.MidiCC,objName:"Ops.Devices.Midi.MidiCC"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiCCOut +// +// ************************************************************** + +Ops.Devices.Midi.MidiCCOut = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const MIDIChannels = Array.from(Array(16).keys(), i => i + 1); +const clamp = (val, min, max) => Math.min(Math.max(val, min), max); +const CC_STATUS_BYTE = 0xb; + +const inChannel = op.inDropDown("MIDI Channel", MIDIChannels, "1"); +const inCcIndex = op.inInt("CC Index", 1); +const inCCValue = op.inInt("CC Value", 0); +const inMin = op.inFloat("Min In Value", 0); +const inMax = op.inFloat("Max In Value", 1); + +op.setPortGroup("General",[inChannel]); +op.setPortGroup("CC", [inCCValue, inCcIndex]); +op.setPortGroup("Value Range", [inMin,inMax]); + +const outEvent = op.outObject("MIDI Event Out"); + +inCCValue.onChange = function() { + const val = inCCValue.get(); + + const ccValue = Math.floor(CABLES.map(val,inMin.get(), inMax.get(), 0, 127)); + const ccIndex = clamp(inCcIndex.get(), 1, 127); + + + const event = { + deviceName: null, + output: null, + inputId: 0, + messageType: "CC", + data: [CC_STATUS_BYTE << 4 | (inChannel.get() - 1), ccIndex, ccValue], + index: ccIndex, + value: ccValue, + channel: inChannel.get() - 1, + } + + outEvent.set(null); + outEvent.set(event); +} + +}; + +Ops.Devices.Midi.MidiCCOut.prototype = new CABLES.Op(); +CABLES.OPS["f63adc97-7f4a-4cad-a53a-a20ad1e9b30e"]={f:Ops.Devices.Midi.MidiCCOut,objName:"Ops.Devices.Midi.MidiCCOut"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiChord3 +// +// ************************************************************** + +Ops.Devices.Midi.MidiChord3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +/* UTIL */ +const NOTE_OFF = 0x8; +const NOTE_ON = 0x9; +const NOTE_VALUES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; + +const MIDIChannels = Array.from(Array(16).keys()).map((i) => { return i + 1; }); + +function getMIDINote(dataByte1LSB) +{ + return dataByte1LSB <= 126 + ? `${NOTE_VALUES[dataByte1LSB % 12]}${Math.floor(dataByte1LSB / 12) - 2} - ${dataByte1LSB}` + : "NO NOTE"; +} + +const noteValues = Array.from(Array(128).keys(), (key) => { return getMIDINote(key); }); + +/* IN */ +const inEvent = op.inObject("MIDI Event In"); +const midiChannelDropdown = op.inValueSelect("MIDI Channel", MIDIChannels, 1); +const noteDropdown = op.inValueSelect("Note 1", noteValues, 0); +const noteDropdown2 = op.inValueSelect("Note 2", noteValues, 0); +const noteDropdown3 = op.inValueSelect("Note 3", noteValues, 0); +const noteDropdowns = [noteDropdown, noteDropdown2, noteDropdown3]; + +const normalizeDropdown = op.inValueSelect( + "Normalize Velocity", + ["none", "0 to 1", "-1 to 1"], + "none" +); + +const learn = op.inTriggerButton("learn"); +const reset = op.inTriggerButton("reset"); + +op.setPortGroup("MIDI", [inEvent, midiChannelDropdown]); +op.setPortGroup("Notes", [...noteDropdowns, normalizeDropdown]); +op.setPortGroup("", [learn, reset]); +/* OUT */ + +const eventOut = op.outObject("MIDI Event Out"); +const triggerOut = op.outTrigger("Trigger Out"); + +const noteIndexOut1 = op.outNumber("Note Out 1"); +const velocityOut1 = op.outNumber("Velocity 1"); +const gateOut1 = op.outValueBool("Gate 1"); + +const out1 = { + "noteIndexOut": noteIndexOut1, + "velocityOut": velocityOut1, + "gateOut": gateOut1, +}; + +const noteIndexOut2 = op.outValue("Note Out 2"); +const velocityOut2 = op.outValue("Velocity 2"); +const gateOut2 = op.outValueBool("Gate 2"); + +const out2 = { + "noteIndexOut": noteIndexOut2, + "velocityOut": velocityOut2, + "gateOut": gateOut2, +}; + +const noteIndexOut3 = op.outValue("Note Out 3"); +const velocityOut3 = op.outValue("Velocity 3"); +const gateOut3 = op.outValueBool("Gate 3"); + +const out3 = { + "noteIndexOut": noteIndexOut3, + "velocityOut": velocityOut3, + "gateOut": gateOut3, +}; + +const outs = [out1, out2, out3]; +noteDropdown.set(0); +midiChannelDropdown.set(1); + +op.setPortGroup("MIDI/Trigger Out", [eventOut, triggerOut]); +op.setPortGroup("Note 1", [noteIndexOut1, velocityOut1, gateOut1]); +op.setPortGroup("Note 2", [noteIndexOut2, velocityOut2, gateOut2]); +op.setPortGroup("Note 3", [noteIndexOut3, velocityOut3, gateOut3]); + +let learning = false; +let learnCount = 0; +let learnedNotes = []; + +learn.onTriggered = () => +{ + learning = true; +}; + +reset.onTriggered = () => +{ + learning = false; + learnCount = 0; + learnedNotes = []; + noteDropdowns.forEach((nd) => { return nd.set(0); }); + op.refreshParams(); +}; + +inEvent.onChange = () => +{ + const event = inEvent.get(); + if (!event) return; + if (event.messageType !== "Note") return; + if (!event.newNote) return; + + const [statusByte] = event.data; + + const { newNote, velocity, channel } = event; + const [noteIndex, noteName] = newNote; + const midiNote = getMIDINote(noteIndex); + const msgType = statusByte >> 4; + + if (learning && learnCount < 3) + { + if (msgType === NOTE_OFF) + { + eventOut.set(event); + return; + } + + if (!learnedNotes.includes(midiNote)) noteDropdowns[learnCount].set(midiNote); + else + { + eventOut.set(event); + return; + } + + learnedNotes.push(midiNote); + if (learnCount === 0) midiChannelDropdown.set(event.channel + 1); + + if (CABLES.UI) + { + op.uiAttr({ "info": `bound to Note: ${noteDropdowns[learnCount].get()}` }); + op.refreshParams(); + } + + learnCount += 1; + learning = learnCount < 3; + } + + if (channel === midiChannelDropdown.get() - 1) + { + const chordIndex = noteDropdowns.map((nd) => { return nd.get(); }).indexOf(midiNote); + + if (chordIndex === -1) + { + eventOut.set(event); + return; + } + + const { + gateOut, noteIndexOut, velocityOut, + } = outs[chordIndex]; + + if (msgType === NOTE_OFF || velocity === 0) + { + gateOut.set(false); + if (velocity === 0) velocityOut.set(0); + } + + if (msgType === NOTE_ON) + { + gateOut.set(true); + triggerOut.trigger(); + noteIndexOut.set(noteIndex); + + if (normalizeDropdown.get() === "0 to 1") + { + // (max'-min')/(max-min)*(value-min)+min' + velocityOut.set(1 / 126 * (velocity - 1)); + } + + else if (normalizeDropdown.get() === "-1 to 1") + { + // (max'-min')/(max-min)*(value-min)+min' + const normalizedValue = 2 / 126 * (velocity - 1) - 1; + velocityOut.set(normalizedValue); + } + else if (normalizeDropdown.get() === "none") velocityOut.set(velocity); + } + } + + eventOut.set(event); +}; + + +}; + +Ops.Devices.Midi.MidiChord3.prototype = new CABLES.Op(); +CABLES.OPS["58388fbb-3161-417f-8c12-49d4c8ee7a19"]={f:Ops.Devices.Midi.MidiChord3,objName:"Ops.Devices.Midi.MidiChord3"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiClock +// +// ************************************************************** + +Ops.Devices.Midi.MidiClock = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const eventIn = op.inObject("MIDI Event In"); +const timingIn = op.inValueSelect("Timing", ["straight", "dotted", "triplet"], "straight"); +const eventOut = op.outObject("MIDI Event Out"); +const beatOut = op.outTrigger("Tick Out"); +const clockStartOut = op.outTrigger("Clock Start"); +const clockStopOut = op.outTrigger("Clock Stop"); +const clockContinueOut = op.outTrigger("Clock Continue"); + +let outBPM = op.outValue("BPM"); +let outBeatDuration = op.outValue("Tick Duration"); +let outSubTick = op.outValue("Sub Tick"); + +const fullNoteOut = op.outTrigger("1/1"); +const halfNoteOut = op.outTrigger("1/2"); +const quarterNoteOut = op.outTrigger("1/4"); +const eigthNoteOut = op.outTrigger("1/8"); +const sixteenthNoteOut = op.outTrigger("1/16"); + +const outs = { + "full": fullNoteOut, + "half": halfNoteOut, + "quarter": quarterNoteOut, + "eigth": eigthNoteOut, + "sixteenth": sixteenthNoteOut, +}; + +const CLOCK = 0xf8; +const CLOCK_START = 0xfa; +const CLOCK_CONTINUE = 0xfb; +const CLOCK_STOP = 0xfc; + +const STRAIGHT_TICKS = { + "full": 96, + "half": 48, + "quarter": 24, + "eigth": 12, + "sixteenth": 6, +}; + +const TICKS = { + "straight": STRAIGHT_TICKS, + "dotted": { + "full": STRAIGHT_TICKS.full + STRAIGHT_TICKS.full / 2, + "half": STRAIGHT_TICKS.half + STRAIGHT_TICKS.half / 2, + "quarter": STRAIGHT_TICKS.quarter + STRAIGHT_TICKS.quarter / 2, + "eigth": STRAIGHT_TICKS.eigth + STRAIGHT_TICKS.eigth / 2, + "sixteenth": STRAIGHT_TICKS.sixteenth + STRAIGHT_TICKS.sixteenth / 2, + }, + "triplet": { + "full": (STRAIGHT_TICKS.full * 2) / 3, + "half": (STRAIGHT_TICKS.half * 2) / 3, + "quarter": (STRAIGHT_TICKS.quarter * 2) / 3, + "eigth": (STRAIGHT_TICKS.eigth * 2) / 3, + "sixteenth": (STRAIGHT_TICKS.sixteenth * 2) / 3, + } +}; + +let tickCount = 0; +let lastBeat = 0; + +eventIn.onChange = () => +{ + const event = eventIn.get(); + if (!event) return; + + const [statusByte, , ] = event.data; + if (statusByte === CLOCK_START) + { + tickCount = 0; + clockStartOut.trigger(); + } + else if (statusByte === CLOCK_STOP) + { + clockStopOut.trigger(); + } + else if (statusByte === CLOCK || statusByte === CLOCK_CONTINUE) + { + if (statusByte === CLOCK_CONTINUE) clockContinueOut.trigger(); + + const { + full, half, quarter, eigth, sixteenth, + } = TICKS[timingIn.get()]; + if (tickCount % 24 === 0) + { + if (lastBeat !== 0) + { + const diff = CABLES.now() - lastBeat; + const bpm = 60000 / diff; + outBPM.set(bpm); + outBeatDuration.set(15 / bpm); + } + + lastBeat = CABLES.now(); + } + if (tickCount % full === 0) outs.full.trigger(); + if (tickCount % half === 0) outs.half.trigger(); + if (tickCount % quarter === 0) outs.quarter.trigger(); + if (tickCount % eigth === 0) outs.eigth.trigger(); + if (tickCount % sixteenth === 0) outs.sixteenth.trigger(); + beatOut.trigger(); + tickCount += 1; + outSubTick.set(tickCount % 24); + } + + eventOut.set(event); +}; + + +}; + +Ops.Devices.Midi.MidiClock.prototype = new CABLES.Op(); +CABLES.OPS["5eea2f30-4e10-42a7-a761-63fb274cdb4f"]={f:Ops.Devices.Midi.MidiClock,objName:"Ops.Devices.Midi.MidiClock"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiInputDevice_v2 +// +// ************************************************************** + +Ops.Devices.Midi.MidiInputDevice_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// http://www.keithmcmillen.com/blog/making-music-in-the-browser-web-midi-api/ + +// https://ccrma.stanford.edu/~craig/articles/linuxmidi/misc/essenmidi.html + +/* INPUTS */ + +const deviceSelect = op.inValueSelect("Device", ["none"]); + +let learning = false; +const learn = op.inTriggerButton("Learn"); +const resetIn = op.inTriggerButton("Panic"); + +op.setPortGroup("Device Select", [deviceSelect]); +op.setPortGroup("Controls", [learn, resetIn]); +/* OPS */ +const opPrefix = "Ops.Devices.Midi.Midi"; +const OPS = { + "CC": { "NAMESPACE": `${opPrefix}CC`, "IN_PORT": "CC Index" }, + "NRPN": { "NAMESPACE": `${opPrefix}NRPN`, "IN_PORT": "NRPN Index" }, + "Note": { "NAMESPACE": `${opPrefix}Note`, "IN_PORT": "Note" }, +}; +/* OUTPUTS */ +const OUTPUT_KEYS = [ + "Event", + "Note", + "CC", + + // "Channel Pressure", + // "Poly Key Pressure", + "NRPN", + // 'SysEx', + // "Pitchbend", + "Program Change", + "Clock", +]; + +// unused midi signals +const NOT_YET_USED = ["Pitchbend", "Channel Pressure", "Poly Key Pressure", "SysEx"]; + +// create outputs from keys specified above +const OUTPUTS = OUTPUT_KEYS.reduce((acc, cur) => +{ + acc[cur] = op.outObject(cur); + return acc; +}, {}); + +op.setPortGroup("MIDI Event", [OUTPUTS.Event]); +op.setPortGroup( + "MIDI Event by Type", + Object.keys(OUTPUTS).map((key) => { return key !== "Event" && OUTPUTS[key]; }).filter(Boolean), +); + +/* CONSTANTS */ +/* http://www.indiana.edu/~emusic/etext/MIDI/chapter3_MIDI3.shtml */ +const NOTE_VALUES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; + +/* MIDI STATUS BYTES */ +const NOTE_OFF = 0x8; +const NOTE_ON = 0x9; +const POLY_KEY_PRESSURE = 0xa; +const CC = 0xb; +const PROGRAM_CHANGE = 0xc; +const CHANNEL_PRESSURE = 0xd; +const PITCH_BEND = 0xe; +const CLOCK = 0xf8; +const CLOCK_START = 0xfa; +const CLOCK_CONTINUE = 0xfb; +const CLOCK_STOP = 0xfc; +const CLOCK_SIGNALS = [CLOCK, CLOCK_START, CLOCK_CONTINUE, CLOCK_STOP]; + +const MESSAGE_TYPES = { + [NOTE_OFF]: "Note", + [NOTE_ON]: "Note", + [POLY_KEY_PRESSURE]: "Poly Key Pressure", + [CC]: "CC", + [PROGRAM_CHANGE]: "Program Change", + [CHANNEL_PRESSURE]: "Channel Pressure", + [PITCH_BEND]: "Pitchbend", + [CLOCK]: "Clock", +}; + +/* UTILITY FUNCTIONS */ +function getMIDIChannel(statusByte) +{ + return statusByte & 0x0f; +} + +function getMessageType(statusByte) +{ + return MESSAGE_TYPES[statusByte >> 4] || "UNKNOWN"; +} + +function getMIDINote(dataByte1LSB) +{ + return dataByte1LSB <= 126 + ? `${NOTE_VALUES[dataByte1LSB % 12]}${Math.floor(dataByte1LSB / 12) - 2} - ${dataByte1LSB}` + : "NO NOTE"; +} + +const NRPN_CCS = [98, 99, 6, 38]; +const NRPN_VALUE_MSB = 6; +const NRPN_VALUE_LSB = 38; +const NRPN_INDEX_MSB = 99; +const NRPN_INDEX_LSB = 98; + +let nrpnIndexMSB = null; +let nrpnIndexLSB = null; +let nrpnValueMSB = null; +let nrpnValueLSB = null; + +let nrpnIndex_ = null; +let nrpnValue_ = null; + +/* NRPN implementations differ, we need to check whether the cycle starts with LSB or MSB */ +const MSB_START = 9; +const LSB_START = 10; +let FIRST_CC = null; +let ROUTINE_TYPE = null; +/* the state of the current NRPN construction cycle */ + + +const LSBRoutine = (ccIndex, ccValue) => +{ + // NOTE: this is still the MSBRoutine + if (ccIndex === NRPN_INDEX_MSB) nrpnIndexMSB = ccValue << 7; + else if (ccIndex === NRPN_INDEX_LSB) nrpnIndexLSB = ccValue; + + nrpnIndex_ = nrpnIndexMSB | nrpnIndexLSB; + + if (typeof nrpnIndex_ === "number") + { + if (ccIndex === NRPN_VALUE_MSB) + { + nrpnValueMSB = ccValue << 7; + + + if (typeof nrpnValueLSB === "number") + { + nrpnValue_ = nrpnValueMSB | nrpnValueLSB; + return [nrpnIndex_, nrpnValue_]; + } + } + else if (ccIndex === NRPN_VALUE_LSB) + { + nrpnValueLSB = ccValue; + + nrpnValue_ = nrpnValueMSB | nrpnValueLSB; + return [nrpnIndex_, nrpnValue_]; + } + } + + return null; +}; + +const MSBRoutine = (ccIndex, ccValue) => +{ + if (ccIndex === NRPN_INDEX_MSB) nrpnIndexMSB = ccValue << 7; + else if (ccIndex === NRPN_INDEX_LSB) nrpnIndexLSB = ccValue; + + nrpnIndex_ = nrpnIndexMSB | nrpnIndexLSB; + if (typeof nrpnIndex_ === "number") + { + if (ccIndex === NRPN_VALUE_MSB) + { + nrpnValueMSB = ccValue << 7; + + if (typeof nrpnValueLSB === "number") + { + nrpnValue_ = nrpnValueMSB | nrpnValueLSB; + return [nrpnIndex_, nrpnValue_]; + } + } + else if (ccIndex === NRPN_VALUE_LSB) + { + nrpnValueLSB = ccValue; + nrpnValue_ = nrpnValueMSB | nrpnValueLSB; + return [nrpnIndex_, nrpnValue_]; + } + } + + return null; +}; + +const NRPNRoutine = (ccIndex, ccValue) => +{ + if (FIRST_CC === null) + { + FIRST_CC = ccIndex; + ROUTINE_TYPE = FIRST_CC === NRPN_INDEX_MSB ? MSB_START : LSB_START; + } + if (ROUTINE_TYPE === MSB_START) + { + return MSBRoutine(ccIndex, ccValue); + } + if (ROUTINE_TYPE === LSB_START) + { + return LSBRoutine(ccIndex, ccValue); + } + return null; +}; +let midi = null; + +/* INIT FUNCTIONS */ +let outputDevice = null; + +function onMIDIMessage(_event) +{ + if (!_event) return; + + if (op.patch.isEditorMode()) gui.emitEvent("userActivity"); + + const { data } = _event; + const [statusByte, LSB, MSB] = data; + + if (CLOCK_SIGNALS.includes(statusByte)) + { + OUTPUTS.Clock.set(_event); + return; + } + + if (statusByte > 248) + { + // we don't use statusbytes above 248 for now + return; + } + + const deviceName = deviceSelect.get(); + const channel = getMIDIChannel(statusByte); + + let messageType = getMessageType(statusByte); + const outputIndex = LSB; + const outputValue = MSB; + + const isNRPNByte = messageType === "CC" && NRPN_CCS.some((cc) => { return cc === LSB; }); + let nrpnIndex; + let nrpnValue; + + if (isNRPNByte) + { + const nrpnValueRes = NRPNRoutine(LSB, MSB); + if (nrpnValueRes) + { + const [index, value] = nrpnValueRes; + messageType = "NRPN"; + nrpnIndex = index; + nrpnValue = value; + } + } + + const newEvent = { + /* OLD EVENT v */ + deviceName, + "inputId": 0, // what is this for? + messageType, + // ..., + "index": outputIndex, + "value": outputValue, + + "cmd": data[0] >> 4, + "channel": data[0] & 0xf, + "type": data[0] & 0xf0, + "note": data[1], + "velocity": data[2], + data, + ...messageType === "Note" && { + "newNote": [LSB, getMIDINote(LSB)], + "velocity": outputValue, + }, + ...messageType === "NRPN" && { nrpnIndex, nrpnValue }, + }; + + if (learning) + { + if (["Note", "CC", "NRPN"].includes(messageType)) + { + const newOp = op.patch.addOp(OPS[messageType].NAMESPACE, { + "translate": { + "x": op.uiAttribs.translate.x, + "y": op.uiAttribs.translate.y + 100, + }, + }); + + op.patch.link(op, messageType, newOp, "MIDI Event In"); + newOp.getPortByName("MIDI Channel").set(channel + 1); + + if (messageType === "Note") + { + const { + "newNote": [, noteName], + } = newEvent; + newOp.getPortByName("Note").set(noteName); + } + + if (messageType === "CC") + { + const { index } = newEvent; + newOp.getPortByName("CC Index").set(index); + } + + if (messageType === "NRPN") + { + newOp.getPortByName("NRPN Index").set(nrpnIndex); + } + } + learning = false; + } + // if (normalize.get()) event.velocity /= 127; + + // with pressure and tilt off + // note off: 128, cmd: 8 + // note on: 144, cmd: 9 + // pressure / tilt on + // pressure: 176, cmd 11: + // bend: 224, cmd: 14 + OUTPUTS.Event.set(null); + OUTPUTS.Event.set(newEvent); + + if (messageType !== "UNKNOWN" && !NOT_YET_USED.includes(messageType)) + { + OUTPUTS[messageType].set(null); + OUTPUTS[messageType].set(newEvent); + } +} + +function setDevice() +{ + if (!midi || !midi.inputs) return; + const name = deviceSelect.get(); + + op.setTitle(`Midi ${name}`); + + const inputs = midi.inputs.values(); + // const outputs = midi.outputs.values(); + + for (let input = inputs.next(); input && !input.done; input = inputs.next()) + { + if (input.value.name === name) + { + input.value.onmidimessage = onMIDIMessage; + outputDevice = midi.inputs.get(input.value.id); + } + else if (input.value.onmidimessage === onMIDIMessage) input.value.onmidimessage = null; + } + + /* for (let output = outputs.next(); output && !output.done; output = outputs.next()) { + if (output.value.name === name) { + outputDevice = midi.outputs.get(output.value.id); + } + } */ +} + +function onMIDIFailure() +{ + op.uiAttr({ "warning": "No MIDI support in your browser." }); +} + +function onMIDISuccess(midiAccess) +{ + midi = midiAccess; + const inputs = midi.inputs.values(); + op.uiAttr({ "info": "no midi devices found" }); + + const deviceNames = []; + + for (let input = inputs.next(); input && !input.done; input = inputs.next()) + { + deviceNames.push(input.value.name); + } + + deviceSelect.uiAttribs.values = deviceNames; + + op.refreshParams(); + setDevice(); +} + +deviceSelect.onChange = setDevice; + +if (navigator.requestMIDIAccess) +{ + navigator.requestMIDIAccess({ "sysex": false }).then(onMIDISuccess, onMIDIFailure); +} +else onMIDIFailure(); + +resetIn.onTriggered = () => +{ + + // TODO: senmd note off to every note + /* + if (!outputDevice) return; + for (let i = 0; i < 12; i += 1) { + outputDevice.send([0x90, i, 0]); + outputDevice.send([0xb0, i, 0]); + } */ +}; + +learn.onTriggered = () => +{ + if (!outputDevice) return; + learning = true; +}; + + +}; + +Ops.Devices.Midi.MidiInputDevice_v2.prototype = new CABLES.Op(); +CABLES.OPS["484b3a00-41b7-4e3f-8a99-a1b32a764eff"]={f:Ops.Devices.Midi.MidiInputDevice_v2,objName:"Ops.Devices.Midi.MidiInputDevice_v2"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiMonitor +// +// ************************************************************** + +Ops.Devices.Midi.MidiMonitor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inData = op.inObject("Event"); + +op.setPortGroup("MIDI", [inData]); + +/* OUTPUTS */ +const OUTPUT_KEYS = [ + "Device", + "MIDI Channel", + "Message Type", + "Note", + "Note Velocity", + "CC Number", + "CC Value", + "Pitch Bend Value", + "NRPN Number", + "NRPN Value", + "Program Change", +]; + +const eventOut = op.outObject("MIDI Event Out"); +const triggerOut = op.outTrigger("Trigger Out"); + +// create outputs from keys specified above +const OUTPUTS = { + "Device": op.outNumber("Device", -1), + "MIDI Channel": op.outNumber("MIDI Channel", -1), + "Message Type": op.outNumber("Message Type", -1), + "Note": op.outNumber("Note", -1), + "Note Velocity": op.outNumber("Note Velocity", -1), + "CC Number": op.outNumber("CC Number", -1), + "CC Value": op.outNumber("CC Value", -1), + "Pitch Bend Value": op.outNumber("Pitch Bend Value", -1), + "NRPN Number": op.outNumber("NRPN Number", -1), + "NRPN Value": op.outNumber("NRPN Value", -1), + "Program Change Value": op.outNumber("Program Change Value", -1), + +}; + +op.setPortGroup("MIDI/Trigger Out", [eventOut, triggerOut]); +op.setPortGroup("General Info", ["Device", "MIDI Channel", "Message Type"].map((key) => OUTPUTS[key])); +op.setPortGroup("Note", ["Note", "Note Velocity"].map((key) => OUTPUTS[key])); +op.setPortGroup("CC", ["CC Number", "CC Value"].map((key) => OUTPUTS[key])); +op.setPortGroup("Pitch Bend", [OUTPUTS["Pitch Bend Value"]]); +op.setPortGroup("NRPN", ["NRPN Number", "NRPN Value"].map((key) => OUTPUTS[key])); +op.setPortGroup("Program Change", ["Program Change Value"].map((key) => OUTPUTS[key])); + +/* http://midiio.sapp.org/src/MidiOutput.cpp for NRPN */ +/* http://www.indiana.edu/~emusic/etext/MIDI/chapter3_MIDI3.shtml */ + +/* +The two things we can assume is that we will receive the NRPN index messages BEFORE the data +and that the LSB of the data will change every time we get an NRPN change. +*/ + +function getMIDIChannel(statusByte) +{ + return (statusByte & 0x0f) + 1; +} + +function getPitchBendValue(dataByte1LSB, dataByte2MSB) +{ + const pitchBendValue = (dataByte2MSB << 7) + dataByte1LSB - 8192; // scale to -1 to 1 = /8192; + return pitchBendValue; +} + +/* http://tetradev.blogspot.com/2010/03/nrpns-part-2-nrpns-in-ableton-with-max.html */ +/* https://sites.uci.edu/camp2014/2014/04/30/managing-midi-pitchbend-messages/ */ + +inData.onChange = function () +{ + const event = inData.get(); + + if (!event || !event.data) return; + const [statusByte, dataByte1LSB, dataByte2MSB] = event.data; + /* We skip MIDI signals that at the moment are not relevant for CABLES, + i.e. Ableton: if SYNC is on, every 2nd 3-Byte-Tuple sent + is a Timing clock message + we don't wanna show that for now, hence we skip everything above 248 */ + if (statusByte >= 248) + { + eventOut.set(event); + triggerOut.trigger(); + return; + } + + const { messageType, deviceName } = event; + + OUTPUTS.Device.set(deviceName); + OUTPUTS["MIDI Channel"].set(getMIDIChannel(statusByte)); + OUTPUTS["Message Type"].set(messageType); + + switch (messageType) + { + case "NRPN": + OUTPUTS["NRPN Number"].set(event.nrpnIndex); + OUTPUTS["NRPN Value"].set(event.nrpnValue); + Object.keys(OUTPUTS) + .filter( + (key) => !key.startsWith(messageType) + && key !== "Message Type" && key !== "MIDI Channel" && key !== "Device", + ) + .forEach((filteredKey) => OUTPUTS[filteredKey].set("-")); + break; + case "CC": + const [, ccIndex, ccValue] = event.data; + OUTPUTS["CC Number"].set(ccIndex); + OUTPUTS["CC Value"].set(ccValue); + Object.keys(OUTPUTS) + .filter( + (key) => !key.startsWith(messageType) + && key !== "Message Type" && key !== "MIDI Channel" && key !== "Device", + ) + .forEach((filteredKey) => OUTPUTS[filteredKey].set("-")); + break; + case "Note": + const { + "newNote": [noteIndex], + velocity, + } = event; + OUTPUTS.Note.set(noteIndex); + OUTPUTS["Note Velocity"].set(velocity); + Object.keys(OUTPUTS) + .filter( + (key) => !key.startsWith(messageType) + && key !== "Message Type" && key !== "MIDI Channel" && key !== "Device" + ) + .forEach((filteredKey) => OUTPUTS[filteredKey].set("-")); + break; + case "Pitch Bend": + OUTPUTS["Pitch Bend Value"].set(getPitchBendValue(dataByte1LSB, dataByte2MSB)); + Object.keys(OUTPUTS) + .filter( + (key) => !key.startsWith(messageType) + && key !== "Message Type" && key !== "MIDI Channel" && key !== "Device" + ) + .forEach((filteredKey) => OUTPUTS[filteredKey].set("-")); + break; + case "Program Change": + const [, pcValue] = event.data; + OUTPUTS["Program Change Value"].set(pcValue); + Object.keys(OUTPUTS) + .filter( + (key) => !key.startsWith(messageType) + && key !== "Message Type" && key !== "MIDI Channel" && key !== "Device" + ) + .forEach((filteredKey) => OUTPUTS[filteredKey].set("-")); + break; + default: + Object.keys(OUTPUTS) + .filter((key) => key !== "Message Type" && key !== "MIDI Channel" && key !== "Device") + .forEach((filteredKey) => OUTPUTS[filteredKey].set("-")); + } + + triggerOut.trigger(); + eventOut.set(event); + // } +}; + + +}; + +Ops.Devices.Midi.MidiMonitor.prototype = new CABLES.Op(); +CABLES.OPS["3f585ff5-afd6-45d5-b082-80c62f6cdf2b"]={f:Ops.Devices.Midi.MidiMonitor,objName:"Ops.Devices.Midi.MidiMonitor"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiNRPN +// +// ************************************************************** + +Ops.Devices.Midi.MidiNRPN = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +/* UTIL */ +const MIDIChannels = Array.from(Array(16).keys()).map((i) => { return i + 1; }); + +/* IN */ +const inEvent = op.inObject("MIDI Event In"); +const midiChannelDropdown = op.inValueSelect("MIDI Channel", MIDIChannels, 1); + +const nrpnIndexDropdown = op.inValueInt("NRPN Index", 0); +const normalizeDropdown = op.inValueSelect("Normalize", ["none", "0 to 1", "-1 to 1"], "none"); +const learn = op.inTriggerButton("learn"); +const clear = op.inTriggerButton("clear"); + +op.setPortGroup("MIDI", [inEvent, midiChannelDropdown]); +op.setPortGroup("NRPN", [nrpnIndexDropdown, normalizeDropdown]); +op.setPortGroup("", [learn, clear]); +/* OUT */ +const eventOut = op.outObject("MIDI Event Out"); +const triggerOut = op.outTrigger("Trigger Out"); +const nrpnIndexOut = op.outValue("NRPN Index"); +const nrpnValueOut = op.outValue("NRPN Value"); + +op.setPortGroup("MIDI/Trigger Out", [eventOut, triggerOut]); +op.setPortGroup("NRPN Out", [nrpnIndexOut, nrpnValueOut]); + +nrpnIndexDropdown.set(0); +midiChannelDropdown.set(1); +normalizeDropdown.set(normalizeDropdown.get("none")); + +let learning = false; +learn.onTriggered = () => +{ + learning = true; +}; + +clear.onTriggered = () => +{ + nrpnIndexDropdown.set(0); + midiChannelDropdown.set(1); + normalizeDropdown.set(normalizeDropdown.get("none")); + op.refreshParams(); +}; +let outValue; +inEvent.onChange = () => +{ + const event = inEvent.get(); + if (!event) return; + if (event.messageType !== "NRPN") return; + + const { channel, nrpnIndex, nrpnValue } = event; + + if (learning) + { + nrpnIndexDropdown.set(nrpnIndex); + midiChannelDropdown.set(channel + 1); + + learning = false; + + if (CABLES.UI) + { + op.uiAttr({ "info": `bound to NRPN: ${nrpnIndexDropdown.get()}` }); + op.refreshParams(); + } + } + + if (channel === midiChannelDropdown.get() - 1) + { + if (nrpnIndex === nrpnIndexDropdown.get()) + { + nrpnIndexOut.set(nrpnIndex); + + outValue = nrpnValue; + + if (normalizeDropdown.get() === "0 to 1") + { + nrpnValueOut.set(outValue / 16383); + triggerOut.trigger(); + } + else if (normalizeDropdown.get() === "-1 to 1") + { + nrpnValueOut.set(outValue / (16383 / 2) - 1); + triggerOut.trigger(); + } + else if (normalizeDropdown.get() === "none") + { + nrpnValueOut.set(outValue); + triggerOut.trigger(); + } + else nrpnValueOut.set(0); + } + } + eventOut.set(null); + eventOut.set(event); +}; + + +}; + +Ops.Devices.Midi.MidiNRPN.prototype = new CABLES.Op(); +CABLES.OPS["2b13bc7b-c1bd-4319-9c85-86a81a6e2354"]={f:Ops.Devices.Midi.MidiNRPN,objName:"Ops.Devices.Midi.MidiNRPN"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiNRPNOut +// +// ************************************************************** + +Ops.Devices.Midi.MidiNRPNOut = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// your new op +// have a look at the documentation at: +// https://docs.cables.gl/dev_hello_op/dev_hello_op.html +const MIDIChannels = Array.from(Array(16).keys(), i => i + 1); +const clamp = (val, min, max) => Math.min(Math.max(val, min), max); +const CC_STATUS_BYTE = 0xb; +const CC_STATUS_BYTE_START = 176; + +const inChannel = op.inDropDown("MIDI Channel", MIDIChannels, "1"); +const inNrpnIndex = op.inInt("NRPN Index", 0); +const inNrpnValue = op.inInt("NRPN Value", 0); +const inMin = op.inFloat("Min In Value", 0); +const inMax = op.inFloat("Max In Value", 1); +op.setPortGroup("General",[inChannel]); +op.setPortGroup("NRPN",[inNrpnValue, inNrpnIndex]); +op.setPortGroup("Value Range",[inMin, inMax]); +const outEvent = op.outObject("MIDI Event Out"); + +inNrpnValue.onChange = function() { + const val = inNrpnValue.get(); + + const nrpnValue = Math.floor(CABLES.map(val,inMin.get(), inMax.get(), 0, 16383)); + const nrpnIndex = clamp(inNrpnIndex.get(), 0, 16383); + // const ccValue = clamp(inNrpnValue.get(), 0, 127); + /* + const newEvent = Object.assign( + { + // OLD EVENT v + deviceName, + output: outputDevice, + inputId: 0, // what is this for? + messageType, + // ..., + index: outputIndex, + value: outputValue, + + cmd: data[0] >> 4, + channel: data[0] & 0xf, + type: data[0] & 0xf0, + note: data[1], + velocity: data[2], + data, + }, + messageType === 'Note' && { + newNote: [LSB, getMIDINote(LSB)], + velocity: outputValue, + }, + messageType === 'NRPN' && { nrpnIndex, nrpnValue }, + ); + */ + + const event = { + deviceName: null, + output: null, + inputId: 0, + messageType: "NRPN", + data: [], + channel: inChannel.get() - 1, + nrpnIndex, + nrpnValue, + } + + outEvent.set(null); + outEvent.set(event); +} + +}; + +Ops.Devices.Midi.MidiNRPNOut.prototype = new CABLES.Op(); +CABLES.OPS["469abc8f-598a-4a42-a5df-881bf80ee363"]={f:Ops.Devices.Midi.MidiNRPNOut,objName:"Ops.Devices.Midi.MidiNRPNOut"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiNote +// +// ************************************************************** + +Ops.Devices.Midi.MidiNote = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +/* UTIL */ +const NOTE_OFF = 0x8; +const NOTE_ON = 0x9; +const NOTE_VALUES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; + +const MIDIChannels = Array.from(Array(16).keys()).map((i) => { return i + 1; }); + +function getMIDINote(dataByte1LSB) +{ + return dataByte1LSB <= 126 + ? `${NOTE_VALUES[dataByte1LSB % 12]}${Math.floor(dataByte1LSB / 12) - 2} - ${dataByte1LSB}` + : "NO NOTE"; +} + +const noteValues = Array.from(Array(128).keys(), (key) => { return getMIDINote(key); }); +const velocityArray = Array.from(Array(128).keys(), (key) => { return 0; }); +/* IN */ +const inEvent = op.inObject("MIDI Event In"); +const midiChannelDropdown = op.inValueSelect("MIDI Channel", MIDIChannels, 1); +const noteDropdown = op.inValueSelect("Note", noteValues, "none"); +const normalizeDropdown = op.inSwitch( + "Normalize Velocity", + ["none", "0 to 1", "-1 to 1"], + "none", +); +const gateType = op.inBool("Toggle Gate", false); +const learn = op.inTriggerButton("learn"); +const clear = op.inTriggerButton("clear"); + +op.setPortGroup("MIDI", [inEvent, midiChannelDropdown]); +op.setPortGroup("", [learn, clear]); +op.setPortGroup("Note", [noteDropdown, normalizeDropdown, gateType]); + +/* OUT */ +const eventOut = op.outObject("MIDI Event Out"); +const triggerOut = op.outTrigger("Trigger Out"); +const currentNoteOut = op.outNumber("Current Note"); +const velocityOut = op.outNumber("Velocity"); +const gateOut = op.outValueBool("Gate"); +const arrayOut = op.outArray("Velocity Array"); +arrayOut.set(velocityArray); + +op.setPortGroup("MIDI/Trigger Out", [eventOut, triggerOut]); +op.setPortGroup("Note Out", [currentNoteOut, velocityOut, gateOut]); +noteDropdown.set(0); +midiChannelDropdown.set(1); + +let learning = false; +learn.onTriggered = () => +{ + learning = true; +}; + +clear.onTriggered = () => +{ + noteDropdown.set(0); + midiChannelDropdown.set(1); + normalizeDropdown.set(normalizeDropdown.get("none")); + gateType.set(false); + op.refreshParams(); +}; + +gateType.onChange = () => +{ + if (!gateType.get()) gateOut.set(false); +}; + +inEvent.onChange = () => +{ + const event = inEvent.get(); + if (!event) return; + if (event.messageType !== "Note") return; + if (!event.newNote) return; + + const [statusByte] = event.data; + + const { newNote, velocity } = event; + const [noteIndex, noteName] = newNote; + + if (learning || noteDropdown.onChange) + { + noteDropdown.set(noteName); + midiChannelDropdown.set(event.channel + 1); + + learning = false; + + if (CABLES.UI) + { + gui.emitEvent("portValueEdited", op, noteDropdown, noteDropdown.get()); + gui.emitEvent("portValueEdited", op, midiChannelDropdown, midiChannelDropdown.get()); + + op.uiAttr({ "info": `bound to Note: ${noteDropdown.get()}` }); + op.refreshParams(); + } + } + + if (event.channel === midiChannelDropdown.get() - 1) + { + if (getMIDINote(noteIndex) === noteDropdown.get()) + { + if ((statusByte >> 4 === NOTE_OFF || velocity === 0) && !gateType.get()) + { + gateOut.set(false); + velocityOut.set(0); + velocityArray[noteIndex] = 0; + arrayOut.set(null); + arrayOut.set(velocityArray); + } + else if (statusByte >> 4 === NOTE_ON) + { + if (gateType.get()) + { + gateOut.set(!gateOut.get()); + } + else + { + gateOut.set(true); + } + currentNoteOut.set(noteIndex); + velocityArray[noteIndex] = velocity; + arrayOut.set(null); + arrayOut.set(velocityArray); + if (normalizeDropdown.get() === "0 to 1") + { + // (max'-min')/(max-min)*(value-min)+min' + velocityOut.set((1 / 126) * (velocity - 1)); + velocityArray[noteIndex] = (1 / 126) * (velocity - 1); + triggerOut.trigger(); + } + else if (normalizeDropdown.get() === "-1 to 1") + { + // (max'-min')/(max-min)*(value-min)+min' + const normalizedValue = (2 / 126) * (velocity - 1) - 1; + velocityArray[noteIndex] = normalizedValue; + velocityOut.set(normalizedValue); + triggerOut.trigger(); + } + else if (normalizeDropdown.get() === "none") + { + velocityOut.set(velocity); + triggerOut.trigger(); + } + } + } + else if (noteDropdown.get() === 0) + { + // no note selected + if ((statusByte >> 4 === NOTE_OFF || velocity === 0) && !gateType.get()) + { + gateOut.set(false); + velocityOut.set(0); + velocityArray[noteIndex] = 0; + arrayOut.set(null); + arrayOut.set(velocityArray); + } + else if (statusByte >> 4 === NOTE_ON) + { + if (gateType.get()) + { + gateOut.set(!gateOut.get()); + } + else + { + gateOut.set(true); + } + currentNoteOut.set(noteIndex); + + if (normalizeDropdown.get() === "0 to 1") + { + // (max'-min')/(max-min)*(value-min)+min' + const newVelocity = (1 / 126) * (velocity - 1); + velocityOut.set(newVelocity); + velocityArray[noteIndex] = newVelocity; + arrayOut.set(null); + arrayOut.set(velocityArray); + triggerOut.trigger(); + } + else if (normalizeDropdown.get() === "-1 to 1") + { + // (max'-min')/(max-min)*(value-min)+min' + const normalizedValue = (2 / 126) * (velocity - 1) - 1; + velocityOut.set(normalizedValue); + velocityArray[noteIndex] = normalizedValue; + arrayOut.set(null); + arrayOut.set(velocityArray); + triggerOut.trigger(); + } + else if (normalizeDropdown.get() === "none") + { + velocityOut.set(velocity); + velocityArray[noteIndex] = velocity; + arrayOut.set(null); + arrayOut.set(velocityArray); + triggerOut.trigger(); + } + } + } + } + eventOut.set(null); + eventOut.set(event); +}; + + +}; + +Ops.Devices.Midi.MidiNote.prototype = new CABLES.Op(); +CABLES.OPS["517ed1fc-6110-4611-9cc7-8dd459191c65"]={f:Ops.Devices.Midi.MidiNote,objName:"Ops.Devices.Midi.MidiNote"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiNoteFilter +// +// ************************************************************** + +Ops.Devices.Midi.MidiNoteFilter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +/* UTIL */ +const NOTE_OFF = 0x8; +const NOTE_ON = 0x9; +const NOTE_VALUES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; + +const MIDIChannels = Array.from(Array(16).keys()).map((i) => { return i + 1; }); + +function getMIDINote(dataByte1LSB) +{ + return dataByte1LSB <= 126 + ? `${NOTE_VALUES[dataByte1LSB % 12]}${Math.floor(dataByte1LSB / 12) - 2} - ${dataByte1LSB}` + : "NO NOTE"; +} + +function getNoteIndexFromMIDINote(midiNote) +{ + if (!midiNote || midiNote === "NO NOTE") return null; + const string = midiNote.split("- ")[1]; + return Number(string); +} + +const noteValues = Array.from(Array(128).keys()).map((key) => { return getMIDINote(key); }); + +/* IN */ +// persistent array for learned notes +const learnedNotesIn = op.inArray("Note Values", []); +learnedNotesIn.setUiAttribs({ "hidePort": true }); + +const inEvent = op.inObject("MIDI Event"); +const midiChannelDropdown = op.inValueSelect("MIDI Channel", MIDIChannels, 1); +const noteStartDropdown = op.inValueSelect("Note Start", noteValues, 0); +const noteEndDropdown = op.inValueSelect("Note End", noteValues, 0); +const normalizeDropdown = op.inValueSelect( + "Normalize Velocity", + ["none", "0 to 1", "-1 to 1"], + "none", +); +const learn = op.inTriggerButton("learn"); +const reset = op.inTriggerButton("reset"); + +op.setPortGroup("MIDI", [inEvent, midiChannelDropdown]); +op.setPortGroup("Notes", [noteStartDropdown, noteEndDropdown, normalizeDropdown]); + +/* OUT */ +const eventOut = op.outObject("Event"); +const triggerOut = op.outTrigger("Trigger Out"); +const noteIndexOut = op.outNumber("Current Note"); +const velocityOut = op.outNumber("Velocity"); +const gateOut = op.outValueBool("Gate"); + +op.setPortGroup("MIDI/Trigger Out", [eventOut, triggerOut]); +op.setPortGroup("Notes Out", [noteIndexOut, velocityOut, gateOut]); + +noteStartDropdown.set(0); +noteEndDropdown.set(0); +midiChannelDropdown.set(1); + +let learning = false; + +learn.onTriggered = () => +{ + if (learnedNotesIn.get().length > 0) + { + learnedNotesIn.set([]); + } + learning = true; +}; +reset.onTriggered = () => +{ + learning = false; + learnedNotesIn.set([]); + noteStartDropdown.set(0); + noteEndDropdown.set(0); + midiChannelDropdown.set(1); + op.refreshParams(); +}; + +let selfRef = false; +noteStartDropdown.onChange = () => +{ + const learnedNotes = learnedNotesIn.get(); + learnedNotes[0] = getNoteIndexFromMIDINote(noteStartDropdown.get()); + op.refreshParams(); + + if (selfRef) return; + if (learnedNotes.length === 2) + { + selfRef = true; + learnedNotes.sort((a, b) => { return a - b; }); + const [start, end] = learnedNotes; + noteStartDropdown.set(getMIDINote(start)); + noteEndDropdown.set(getMIDINote(end)); + selfRef = false; + } + learnedNotesIn.set(learnedNotes); +}; + +noteEndDropdown.onChange = () => +{ + const learnedNotes = learnedNotesIn.get(); + learnedNotes[1] = getNoteIndexFromMIDINote(noteEndDropdown.get()); + op.refreshParams(); + + if (selfRef) return; + if (learnedNotes.length === 2) + { + selfRef = true; + learnedNotes.sort((a, b) => { return a - b; }); + const [start, end] = learnedNotes; + noteStartDropdown.set(getMIDINote(start)); + noteEndDropdown.set(getMIDINote(end)); + selfRef = false; + } + learnedNotesIn.set(learnedNotes); +}; + +inEvent.onChange = () => +{ + const event = inEvent.get(); + if (!event) return; + if (event.messageType !== "Note") return; + if (!event.newNote) return; + + const [statusByte] = event.data; + + const { newNote, velocity } = event; + const [noteIndex, noteName] = newNote; + const midiNote = getMIDINote(noteIndex); + const learnedNotes = learnedNotesIn.get(); + + if (learning) + { + if (statusByte >> 4 === NOTE_OFF) + { + eventOut.set(event); + return; + } + if (!learnedNotes.includes(midiNote) && learnedNotes.length < 2) + { + learnedNotes.push(noteIndex); + learnedNotesIn.set(learnedNotes); + } + if (learnedNotes.length === 2) + { + learnedNotes.sort((a, b) => { return a - b; }); + learnedNotesIn.set(learnedNotes); + const [start, end] = learnedNotes; + + noteStartDropdown.set(getMIDINote(start)); + noteEndDropdown.set(getMIDINote(end)); + learning = false; + } + + midiChannelDropdown.set(event.channel + 1); + + if (CABLES.UI) + { + gui.emitEvent("portValueEdited", op, midiChannelDropdown, midiChannelDropdown.get()); + gui.emitEvent("portValueEdited", op, learnedNotes, learnedNotes.get()); + gui.emitEvent("portValueEdited", op, learnedNotesIn, learnedNotesIn.get()); + gui.emitEvent("portValueEdited", op, noteEndDropdown, noteEndDropdown.get()); + gui.emitEvent("portValueEdited", op, noteStartDropdown, noteStartDropdown.get()); + + op.uiAttr({ "info": `Start bound to Note: ${noteStartDropdown.get()}` }); + op.uiAttr({ "info": `End bound to Note: ${noteEndDropdown.get()}` }); + op.refreshParams(); + } + eventOut.set(event); + return; + } + + if (event.channel === midiChannelDropdown.get() - 1 && learnedNotes.length === 2) + { + const [start, end] = learnedNotes; + if (start <= noteIndex && noteIndex <= end) + { + if (statusByte >> 4 === NOTE_OFF || velocity === 0) + { + gateOut.set(false); + if (velocity === 0) velocityOut.set(0); + } + if (statusByte >> 4 === NOTE_ON) + { + gateOut.set(true); + + noteIndexOut.set(noteIndex); + triggerOut.trigger(); + + if (normalizeDropdown.get() === "0 to 1") + { + // (max'-min')/(max-min)*(value-min)+min' + velocityOut.set((1 / 126) * (velocity - 1)); + } + else if (normalizeDropdown.get() === "-1 to 1") + { + // (max'-min')/(max-min)*(value-min)+min' + const normalizedValue = (2 / 126) * (velocity - 1) - 1; + velocityOut.set(normalizedValue); + } + else if (normalizeDropdown.get() === "none") velocityOut.set(velocity); + } + } + } + + eventOut.set(event); +}; + + +}; + +Ops.Devices.Midi.MidiNoteFilter.prototype = new CABLES.Op(); +CABLES.OPS["b248a588-81cc-47af-a65b-36509abd2888"]={f:Ops.Devices.Midi.MidiNoteFilter,objName:"Ops.Devices.Midi.MidiNoteFilter"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiNoteOut +// +// ************************************************************** + +Ops.Devices.Midi.MidiNoteOut = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const MIDIChannels = Array.from(Array(16).keys(), (i) => { return i + 1; }); +const NOTE_VALUES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; + +const clamp = (val, min, max) => { return Math.min(Math.max(val, min), max); }; +const NOTE_OFF = 0x8; +const NOTE_ON = 0x9; + +function getMIDINote(dataByte1LSB) +{ + return dataByte1LSB <= 126 + ? `${NOTE_VALUES[dataByte1LSB % 12]}${Math.floor(dataByte1LSB / 12) - 2} - ${dataByte1LSB}` + : "NO NOTE"; +} + +const noteValues = Array.from(Array(128).keys(), (key) => { return getMIDINote(key); }); +const velocityArray = Array.from(Array(128).keys(), (key) => { return 0; }); + +const inChannel = op.inDropDown("MIDI Channel", MIDIChannels, "1"); +const inNoteDropdown = op.inDropDown("Note", noteValues); +const inNoteNumber = op.inInt("Note Number", 0); +const inVelocity = op.inInt("Velocity", 0); +const inMin = op.inFloat("Min In Velocity", 0); +const inMax = op.inFloat("Max In Velocity", 1); +const inNoteArray = op.inArray("Velocity Array In"); +op.setPortGroup("General", [inChannel, inNoteDropdown]); +op.setPortGroup("Note", [inNoteNumber, inVelocity]); +op.setPortGroup("Velocity Range", [inMin, inMax]); + +const outEvent = op.outObject("MIDI Event Out"); + +// this is a safety mechanism for when changing a note while another one is playing, kill old one +let currentNote = null; +const killAllNotes = () => +{ + for (let i = 0; i < 128; i += 1) + { + outEvent.set(null); + outEvent.set({ "data": [(NOTE_OFF << 4 | (inChannel.get() - 1)), i, 0] }); + } +}; + +const killLastNote = () => +{ + outEvent.set(null); + outEvent.set({ "data": [(NOTE_OFF << 4 | (inChannel.get() - 1)), currentNote, 0] }); +}; + +inNoteNumber.onLinkChanged = function () +{ + if (!inNoteNumber.isLinked()) killLastNote(); +}; + +inVelocity.onLinkChanged = function () +{ + if (!inVelocity.isLinked()) killAllNotes(); +}; + +inNoteArray.onLinkChanged = function () +{ + if (!inNoteArray.isLinked()) killAllNotes(); +}; + +inNoteDropdown.onChange = inChannel.onChange = killLastNote; + +inNoteNumber.onChange = inVelocity.onChange = function () +{ + /* if (!inNoteDropdown.get()) { + // if (!op.uiAttribs.error) op.uiAttr({ error: "Please choose a MIDI Note!" }); + return; + } + */ + // if (op.uiAttribs.error) op.uiAttr({ error: null }); + + const val = inVelocity.get(); + const noteNumber = inNoteNumber.get(); + const velocity = Math.floor(CABLES.map(val, inMin.get(), inMax.get(), 0, 127)); + + let noteIndex = Math.floor(clamp(noteNumber, 0, 127)); + + if (inNoteDropdown.get() !== 0) noteIndex = clamp(Number(inNoteDropdown.get().split("-").pop()), 0, 127); + + else + { + if (currentNote) + { + if (noteIndex !== currentNote) + { + const killEvent = { + "deviceName": null, + "output": null, + "inputId": 0, + "messageType": "Note", + "data": [(NOTE_OFF << 4 | (inChannel.get() - 1)), currentNote, 0], + "index": currentNote, + "value": 0, + "newNote": [currentNote, getMIDINote(currentNote)], + "velocity": 0, + "channel": inChannel.get() - 1, + }; + outEvent.set(null); + outEvent.set(killEvent); + } + } + } + + const data = velocity > 0 ? + [(NOTE_ON << 4 | (inChannel.get() - 1)), noteIndex, velocity] + : [(NOTE_OFF << 4 | (inChannel.get() - 1)), noteIndex, velocity]; + + const event = { + "deviceName": null, + "output": null, + "inputId": 0, + "messageType": "Note", + "data": data, + "index": noteIndex, + "value": velocity, + "newNote": [noteIndex, getMIDINote(noteIndex)], + velocity, + "channel": inChannel.get() - 1, + }; + + outEvent.set(null); + outEvent.set(event); + currentNote = noteIndex; +}; +let oldArr = []; + +inNoteArray.onChange = function () +{ + if (!inNoteArray.get()) return; + const arr = inNoteArray.get(); + const length = arr.length > 127 ? 128 : arr.length; + + for (let i = 0; i < length; i += 1) + { + const velocity = Math.floor(CABLES.map(arr[i], inMin.get(), inMax.get(), 0, 127)); + const event = { + "deviceName": null, + "output": null, + "inputId": 0, + "messageType": "Note", + "data": velocity > 0 ? + [(NOTE_ON << 4 | (inChannel.get() - 1)), i, velocity] + : [(NOTE_OFF << 4 | (inChannel.get() - 1)), i, velocity], + "index": i, + "value": velocity, + "newNote": [i, getMIDINote(i)], + velocity, + "channel": inChannel.get() - 1, + }; + + oldArr = arr; + outEvent.set(null); + outEvent.set(event); + } +}; + + +}; + +Ops.Devices.Midi.MidiNoteOut.prototype = new CABLES.Op(); +CABLES.OPS["172dcd08-cd5b-4272-bc7d-2b1a61ecfd4a"]={f:Ops.Devices.Midi.MidiNoteOut,objName:"Ops.Devices.Midi.MidiNoteOut"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiOutputDevice +// +// ************************************************************** + +Ops.Devices.Midi.MidiOutputDevice = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// your new op +// have a look at the documentation at: +// https://docs.cables.gl/dev_hello_op/dev_hello_op.html +const CC_STATUS_BYTE = 0xb; +const NOTE_OFF = 0x8; + +let midi = null; +let outputDevice = null; + +const inDeviceSelect = op.inValueSelect("Device", ["none"]); +const inNote = op.inObject("Note"); +const inCC = op.inObject("CC"); +const inNRPN = op.inObject("NRPN"); + +op.setPortGroup("Device", [inDeviceSelect]); +op.setPortGroup("Midi Events", [inNote, inCC, inNRPN]); + + +inNRPN.onChange = function (_event) +{ + if (!outputDevice) return; + if (!_event) return; + + const event = _event.get(); + + if (!event) return; + if (!event.data) return; + if (event.messageType !== "NRPN") return; + + const indexLSB = (event.nrpnIndex & 0b1111111); + const indexMSB = (event.nrpnIndex >> 7); + + const valueLSB = (event.nrpnValue & 0b1111111); + const valueMSB = (event.nrpnValue >> 7); + + const dataIndexLSB = [CC_STATUS_BYTE << 4 | event.channel, 98, indexLSB]; + const dataIndexMSB = [CC_STATUS_BYTE << 4 | event.channel, 99, indexMSB]; + + const dataValueLSB = [CC_STATUS_BYTE << 4 | event.channel, 38, valueLSB]; + const dataValueMSB = [CC_STATUS_BYTE << 4 | event.channel, 6, valueMSB]; + + outputDevice.send(dataIndexLSB); + outputDevice.send(dataIndexMSB); + outputDevice.send(dataValueLSB); + outputDevice.send(dataValueMSB); +}; + +inCC.onChange = function (_event) +{ + if (!outputDevice) return; + if (!_event) return; + + const event = _event.get(); + + if (!event) return; + if (!event.data) return; + if (event.messageType !== "CC") return; + // TODO: Check for invalid status bytes + outputDevice.send(event.data); +}; + +let currentNote = null; +let currentChannel = null; + +const killLastNote = () => +{ + if (!outputDevice || !currentNote || !currentChannel) return; + outputDevice.send([(NOTE_OFF << 4 | (currentChannel)), currentNote, 0]); +}; + +const killAllNotes = () => +{ + if (!outputDevice) return; + for (let i = 0; i < 128; i += 1) + { + for (let channel = 0; channel < 16; channel += 1) outputDevice.send([(NOTE_OFF << 4 | channel), i, 0]); + } +}; + +inNote.onLinkChanged = function () +{ + if (!inNote.isLinked()) killAllNotes(); +}; + +inNote.onChange = function (_event) +{ + if (!outputDevice) return; + if (!_event) return; + + const event = _event.get(); + + if (!event) return; + if (!event.data) return; + // TODO: let CC all notes off message pass through + // if (event.messageType !== "Note") return; + // TODO: Check for invalid status bytes + + setTimeout(function () + { + outputDevice.send(event.data); + currentNote = event.index; + currentChannel = event.channel; + }, 20); +}; + +function setDevice() +{ + if (!midi || !midi.inputs) return; + const name = inDeviceSelect.get(); + + op.setTitle(`Midi ${name}`); + + const inputs = midi.inputs.values(); + const outputs = midi.outputs.values(); + + for (let output = outputs.next(); output && !output.done; output = outputs.next()) + { + if (output.value.name === name) + { + outputDevice = midi.outputs.get(output.value.id); + } + } +} + + +function onMIDIFailure() +{ + op.uiAttr({ "warning": "No MIDI support in your browser." }); +} + +function onMIDISuccess(midiAccess) +{ + midi = midiAccess; + const inputs = midi.inputs.values(); + const outputs = midi.outputs.values(); + op.uiAttr({ "info": "no midi devices found" }); + + const deviceNames = []; + + for (let output = outputs.next(); output && !output.done; output = outputs.next()) + { + deviceNames.push(output.value.name); + } + + inDeviceSelect.uiAttribs.values = deviceNames; + + op.refreshParams(); + setDevice(); +} + +inDeviceSelect.onChange = setDevice; + +if (navigator.requestMIDIAccess) +{ + navigator.requestMIDIAccess({ "sysex": false }).then(onMIDISuccess, onMIDIFailure); +} +else onMIDIFailure(); + + +}; + +Ops.Devices.Midi.MidiOutputDevice.prototype = new CABLES.Op(); +CABLES.OPS["a874a81a-4416-4ed5-bea9-66531ed83376"]={f:Ops.Devices.Midi.MidiOutputDevice,objName:"Ops.Devices.Midi.MidiOutputDevice"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiTranspose +// +// ************************************************************** + +Ops.Devices.Midi.MidiTranspose = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const NOTE_VALUES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; + +const MIDIChannels = Array.from(Array(16).keys()).map((i) => { return i + 1; }); +const NOTE_OFF = 0x8; +const NOTE_ON = 0x9; +const CC_MSG = 0xb; +function getMIDINote(dataByte1LSB) +{ + return dataByte1LSB <= 126 + ? `${NOTE_VALUES[dataByte1LSB % 12]}${Math.floor(dataByte1LSB / 12) - 2} - ${dataByte1LSB}` + : "NO NOTE"; +} + +/* IN */ +const inEvent = op.inObject("MIDI Event In"); +const midiChannelDropdown = op.inValueSelect("MIDI Channel", MIDIChannels, 1); +const inTranspose = op.inInt("Transpose Amount", 0); +const learn = op.inTriggerButton("learn"); + +op.setPortGroup("MIDI", [inEvent, midiChannelDropdown, learn]); +op.setPortGroup("Transpose", [inTranspose]); + +/* OUT */ +/* OUT */ +const eventOut = op.outObject("MIDI Event Out"); +const triggerOut = op.outTrigger("Trigger Out"); + +const killAllNotes = () => +{ + for (let i = 0; i < 128; i += 1) + { + for (let channel = 0; channel < 16; channel += 1) + { + eventOut.set(null); + eventOut.set({ "data": [(NOTE_OFF << 4 | channel), i, 0] }); + } + op.log("killAllNotes transpose"); + } +}; + +const killAllNotesAgain = () => +{ + for (let channel = 0; channel < 16; channel += 1) + { + eventOut.set(null); + eventOut.set({ "data": [(CC_MSG << 4 | channel), 123, 0] }); + } +}; +inTranspose.onChange = function () +{ + killAllNotesAgain(); + if (lastTransposedNote) + { + // eventOut.set(null); + // eventOut.set({ data: [(NOTE_OFF << 4 | lastTransposedNote.channel), lastTransposedNote.index, 0] }); + } +}; +let lastTransposedNote = null; + +let learning = false; +learn.onTriggered = () => +{ + learning = true; +}; + +inEvent.onChange = () => +{ + const event = inEvent.get(); + + if (!event) return; + if (event.messageType !== "Note") return; + if (!event.newNote) return; + + if (learning) + { + midiChannelDropdown.set(event.channel + 1); + learning = false; + if (CABLES.UI) + { + op.uiAttr({ "info": `bound to MIDI Channel: ${midiChannelDropdown.get()}` }); + op.refreshParams(); + } + } + + if (event.channel === midiChannelDropdown.get() - 1) + { + const newEvent = { ...event }; + + const note = event.index; + const transposeAmount = inTranspose.get(); + const newNoteIndex = Math.min(Math.max(note + transposeAmount, 0), 127); + + if (event.data[0] === (NOTE_ON << 4 | (event.channel))) + { + eventOut.set(null); + eventOut.set({ ...newEvent, + "data": [(NOTE_OFF << 4 | (event.channel)), note, 0], + "note": note, + "index": note, + "velocity": 0, + "newNote": [note, getMIDINote(note)], }); + } + + newEvent.note = newNoteIndex; + newEvent.index = newNoteIndex; + newEvent.data[1] = newNoteIndex; + newEvent.newNote = [newNoteIndex, getMIDINote(newNoteIndex)]; + + lastTransposedNote = newEvent; + + eventOut.set(newEvent); + } + else + { + eventOut.set(event); + } + triggerOut.trigger(); +}; + + +}; + +Ops.Devices.Midi.MidiTranspose.prototype = new CABLES.Op(); +CABLES.OPS["c3b01c76-a300-44d3-8e1a-c8ae7a776463"]={f:Ops.Devices.Midi.MidiTranspose,objName:"Ops.Devices.Midi.MidiTranspose"}; + + + + +// ************************************************************** +// +// Ops.Devices.Midi.MidiValueToNote_v2 +// +// ************************************************************** + +Ops.Devices.Midi.MidiValueToNote_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let midiValuePort = op.inValue("Midi Value"); +let notePort = op.outString("Note"); + +let FLATS = "C Db D Eb E F Gb G Ab A Bb B".split(" "); +let SHARPS = "C C# D D# E F F# G G# A A# B".split(" "); + +function note(num, sharps) +{ + if (num === true || num === false) return function (m) { return note(m, num); }; + num = Math.round(num); + let pcs = sharps === true ? SHARPS : FLATS; + let pc = pcs[num % 12]; + let o = Math.floor(num / 12) - 1; + return pc + o; +} + +midiValuePort.onChange = function () +{ + let val = midiValuePort.get(); + if (val) + { + let n = note(val, true); + notePort.set(n); + } +}; + + +}; + +Ops.Devices.Midi.MidiValueToNote_v2.prototype = new CABLES.Op(); +CABLES.OPS["6f9cc2b3-8dba-44de-a677-a0706952a64c"]={f:Ops.Devices.Midi.MidiValueToNote_v2,objName:"Ops.Devices.Midi.MidiValueToNote_v2"}; + + + + +// ************************************************************** +// +// Ops.Devices.Mobile.DeviceVibrate +// +// ************************************************************** + +Ops.Devices.Mobile.DeviceVibrate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let inVibrate = op.inTriggerButton("Vibrate"); +let outSupported = op.outBoolNum("Supported"); + +navigator.vibrate = navigator.vibrate || navigator.webkitVibrate || +navigator.mozVibrate || navigator.msVibrate; + +if (navigator.vibrate) outSupported.set(true); +else outSupported.set(false); + +inVibrate.onTriggered = function () +{ + if (navigator.vibrate) + { + navigator.vibrate(1500); + } +}; + +if (window.self !== window.top) +{ + op.setUiError("iframe", "DeviceVibrate does not work in an iframe, open the patch without an iframe to get it to work", 1); + op.warn("DeviceVibrate does not work in an iframe, open the patch without an iframe to get it to work"); +} + + +}; + +Ops.Devices.Mobile.DeviceVibrate.prototype = new CABLES.Op(); +CABLES.OPS["e947fc63-e44e-4bdd-98e0-89e79373f073"]={f:Ops.Devices.Mobile.DeviceVibrate,objName:"Ops.Devices.Mobile.DeviceVibrate"}; + + + + +// ************************************************************** +// +// Ops.Devices.Mobile.GeoLocation +// +// ************************************************************** + +Ops.Devices.Mobile.GeoLocation = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + outSupported = op.outBoolNum("Browser Support", navigator.geolocation != false), + outLat = op.outNumber("Latitude"), + outLon = op.outNumber("Longitude"), + outData = op.outObject("Data"); + +if (navigator.geolocation && navigator.geolocation.watchPosition) + navigator.geolocation.watchPosition(updatePos); + +function updatePos(position) +{ + outLat.set(position.coords.latitude); + outLon.set(position.coords.longitude); + outData.set(position); +} + + +}; + +Ops.Devices.Mobile.GeoLocation.prototype = new CABLES.Op(); +CABLES.OPS["7d9ae643-d011-417e-afe3-b3acb78a6d63"]={f:Ops.Devices.Mobile.GeoLocation,objName:"Ops.Devices.Mobile.GeoLocation"}; + + + + +// ************************************************************** +// +// Ops.Devices.Mobile.LockOrientation +// +// ************************************************************** + +Ops.Devices.Mobile.LockOrientation = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inPortrait = op.inValueBool("Portrait"); +const inLandscape = op.inValueBool("Landscape"); + +screen.lockOrientationUniversal = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation; + +const support = op.outBoolNum("Supported", screen.lockOrientationUniversal !== undefined); +const locked = op.outBoolNum("Locked"); + +inPortrait.onChange = setup; +inLandscape.onChange = setup; + +function setup() +{ + if (screen.lockOrientationUniversal) + { + let orientations = []; + if (inPortrait.get())orientations.push("portrait"); + if (inLandscape.get())orientations.push("landscape"); + + if (screen.lockOrientationUniversal(orientations)) locked.set(true); + else locked.set(false); + } + locked.set(false); +} + + +}; + +Ops.Devices.Mobile.LockOrientation.prototype = new CABLES.Op(); +CABLES.OPS["486d5713-5292-47af-bbd3-df81fb7cd3ca"]={f:Ops.Devices.Mobile.LockOrientation,objName:"Ops.Devices.Mobile.LockOrientation"}; + + + + +// ************************************************************** +// +// Ops.Devices.Mobile.MotionSensor_v2 +// +// ************************************************************** + +Ops.Devices.Mobile.MotionSensor_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + mulAxis = op.inValue("Mul Orientation", 1), + req = op.inTrigger("Request Permissions"), + axis1 = op.outNumber("Orientation Alpha"), + axis2 = op.outNumber("Orientation Beta"), + axis3 = op.outNumber("Orientation Gamma"), + accX = op.outNumber("Acceleration X"), + accY = op.outNumber("Acceleration Y"), + accZ = op.outNumber("Acceleration Z"), + accNoGravX = op.outNumber("Acceleration X no gravity"), + accNoGravY = op.outNumber("Acceleration Y no gravity"), + accNoGravZ = op.outNumber("Acceleration Z no gravity"), + + rotRate1 = op.outNumber("Rotation Rate Alpha"), + rotRate2 = op.outNumber("Rotation Rate Beta"), + rotRate3 = op.outNumber("Rotation Rate Gamma"), + + outObj = op.outObject("Object"); + +let lastTime = 0; +let lastTimeAcc = 0; +let obj = {}; + +function handleDeviceMotion(event) +{ + if (CABLES.now() - lastTimeAcc > 15) + { + lastTimeAcc = CABLES.now(); + + accX.set(event.accelerationIncludingGravity.x || 0); + accY.set(event.accelerationIncludingGravity.y || 0); + accZ.set(event.accelerationIncludingGravity.z || 0); + + accNoGravX.set(event.acceleration.x || 0); + accNoGravY.set(event.acceleration.y || 0); + accNoGravZ.set(event.acceleration.z || 0); + + obj.AccelerationX = accX.get(); + obj.AccelerationY = accY.get(); + obj.AccelerationZ = accZ.get(); + + rotRate1.set(event.rotationRate.alpha || 0); + rotRate2.set(event.rotationRate.beta || 0); + rotRate3.set(event.rotationRate.gamma || 0); + + outObj.set(null); + outObj.set(obj); + } +} + +function handleDeviceOrientation(event) +{ + if (CABLES.now() - lastTime > 15) + { + lastTime = CABLES.now(); + axis1.set((event.alpha || 0) * mulAxis.get()); + axis2.set((event.beta || 0) * mulAxis.get()); + axis3.set((event.gamma || 0) * mulAxis.get()); + + obj.OrientationAlpha = axis1.get(); + obj.OrientationBeta = axis2.get(); + obj.OrientationGamma = axis3.get(); + + outObj.set(null); + outObj.set(obj); + } +} + +req.onTriggered = function () +{ + if (window.DeviceMotionEvent && window.DeviceMotionEvent.requestPermission) + { + window.DeviceMotionEvent.requestPermission() + .then((response) => + { + if (response == "granted") + { + window.addEventListener("devicemotion", handleDeviceMotion, true); + } + else + console.log(response); + }) + .catch((e) => + { + console.log(e); + }); + + window.DeviceOrientationEvent.requestPermission() + .then((response) => + { + if (response == "granted") + { + window.addEventListener("deviceorientation", handleDeviceOrientation, true); + } + else + console.log(response); + }) + .catch((e) => + { + console.log(e); + }); + } + else + { + window.addEventListener("devicemotion", handleDeviceMotion, true); + window.addEventListener("deviceorientation", handleDeviceOrientation, true); + } +}; + +if (window.self !== window.top) +{ + op.setUiError("iframe", "MotionSensor does not work in an iframe, open the patch without an iframe to get it to work", 1); + op.warn("MotionSensor does not work in an iframe, open the patch without an iframe to get it to work"); +} + + +}; + +Ops.Devices.Mobile.MotionSensor_v2.prototype = new CABLES.Op(); +CABLES.OPS["f4102f07-e5ff-4c1e-ac5b-6a4758b81727"]={f:Ops.Devices.Mobile.MotionSensor_v2,objName:"Ops.Devices.Mobile.MotionSensor_v2"}; + + + + +// ************************************************************** +// +// Ops.Devices.Mobile.Pinch +// +// ************************************************************** + +Ops.Devices.Mobile.Pinch = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// constants +const elId = "glcanvas"; +const initialScale = 1.0; + +// inputs +const enabledPort = op.inValueBool("Enabled", true); +const minScalePort = op.inValue("Min Scale", 0.0); +const maxScalePort = op.inValue("Max Scale", 4.0); +const resetScalePort = op.inTriggerButton("Reset Scale"); +const inLimit = op.inBool("Limit", true); + +// variables +let scale = initialScale; +let tmpScale = initialScale; +let pinchInProgress = false; + +// setup +const el = document.getElementById(elId); +const hammertime = new Hammer(el); +hammertime.get("pinch").set({ "enable": true }); + +// outputs +const scalePort = op.outNumber("Scale", 1); +const eventPort = op.outObject("Event Details"); +const outDelta = op.outNumber("Delta"); + +// change listeners +window.addEventListener("gesturestart", (e) => { return e.preventDefault(); }); +window.addEventListener("gesturechange", (e) => { return e.preventDefault(); }); +window.addEventListener("gestureend", (e) => { return e.preventDefault(); }); + +hammertime.on("pinch", function (ev) +{ + op.log(ev.additionalEvent); + ev.preventDefault(); // this is ignored in some browsers + if (!enabledPort.get()) { return; } + + // if(ev.isFinal || ev.isFirst) { op.log(ev); } + + tmpScale = ev.scale; + pinchInProgress = true; + + // if(ev.isFinal || !ev.isFinal && pinchInProgress) { + const oldScale = scale; + outDelta.set(0); + + if (ev.isFinal) + { + scale *= tmpScale; + scale = checkAndCorrectBoundaries(scale); + + scalePort.set(scale); + pinchInProgress = false; + op.log("Final Pinch detected, resetting"); + tmpScale = initialScale; + } + else + { + scalePort.set(checkAndCorrectBoundaries(scale * tmpScale)); + } + + let d = oldScale - scalePort.get(); + if (d < 0) d = -1; + else if (d > 0) d = 1; + + outDelta.set(d); + + // if(ev.additionalEvent) { + /* + if(ev.additionalEvent === 'pinchin') { + scale -= Math.abs(ev.velocity); + } else if (ev.additionalEvent === 'pinchout') { + scale += Math.abs(ev.velocity); + } + */ + // } + // scale += ev.velocity; + /* + op.log('ev.scale: ', ev.scale); + tmpScale = ev.scale; + + var scaleToSet; + if(ev.isFinal) { + scale *= tmpScale; + scaleToSet = scale; + tmpScale = initialScale; + } else { + scaleToSet = scale * tmpScale; + } + + op.log('scaleToSet', scaleToSet); + + scale = checkAndCorrectBoundaries(scale); + scaleToSet = checkAndCorrectBoundaries(scaleToSet); + + scalePort.set(scaleToSet); + */ +}); + +el.addEventListener("touchend", function (ev) +{ + op.log("touchend"); + if (pinchInProgress) + { + op.log("touchend, setting manually"); + ev.preventDefault(); // this is ignored in some browsers + ev.stopPropagation(); + pinchInProgress = false; + scale *= tmpScale; + scale = checkAndCorrectBoundaries(scale); + tmpScale = initialScale; + scalePort.set(scale); + } +}); + +function checkAndCorrectBoundaries(s) +{ + let correctedS = s; + + if (inLimit.get()) + { + if (s < minScalePort.get()) + { + correctedS = minScalePort.get(); + } + else if (s > maxScalePort.get()) + { + correctedS = maxScalePort.get(); + } + } + return correctedS; +} + +resetScalePort.onTriggered = reset; + +// functions + +function reset() +{ + scale = initialScale; + scalePort.set(scale); +} + + +}; + +Ops.Devices.Mobile.Pinch.prototype = new CABLES.Op(); +CABLES.OPS["98e19e37-88ca-4c07-bed7-a050dac31e3a"]={f:Ops.Devices.Mobile.Pinch,objName:"Ops.Devices.Mobile.Pinch"}; + + + + +// ************************************************************** +// +// Ops.Devices.Mobile.ScreenOrientation +// +// ************************************************************** + +Ops.Devices.Mobile.ScreenOrientation = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + angle = op.outNumber("Angle"), + str = op.outString("Description"); + +let count = 0; +window.addEventListener("resize", onOrientationChange, false); +window.addEventListener("orientationchange", onOrientationChange, false); + +onOrientationChange(); + +if (screen && screen.orientation) +{ + screen.orientation.addEventListener("change", onOrientationChange); +} + +function onOrientationChange() +{ + count++; + if (!screen.orientation) return; + if (screen.orientation.hasOwnProperty("angle"))angle.set(screen.orientation.angle); + let s = screen.orientation.type + " #" + count + " WINORIENT:" + window.orientation; + str.set(s); +} + +op.onDelete = function () +{ + window.removeEventListener("resize", onOrientationChange); + window.removeEventListener("orientationchange", onOrientationChange); +}; + + +}; + +Ops.Devices.Mobile.ScreenOrientation.prototype = new CABLES.Op(); +CABLES.OPS["ef8f5f5a-4652-4fad-983b-aca96d9cc13b"]={f:Ops.Devices.Mobile.ScreenOrientation,objName:"Ops.Devices.Mobile.ScreenOrientation"}; + + + + +// ************************************************************** +// +// Ops.Devices.Mouse.MouseButtons +// +// ************************************************************** + +Ops.Devices.Mouse.MouseButtons = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + mouseClickLeft = op.outTrigger("Click Left"), + mouseClickRight = op.outTrigger("Click Right"), + mouseDoubleClick = op.outTrigger("Double Click"), + mouseDownLeft = op.outBoolNum("Button pressed Left", false), + mouseDownMiddle = op.outBoolNum("Button pressed Middle", false), + mouseDownRight = op.outBoolNum("Button pressed Right", false), + triggerMouseDownLeft = op.outTrigger("Mouse Down Left"), + triggerMouseDownMiddle = op.outTrigger("Mouse Down Middle"), + triggerMouseDownRight = op.outTrigger("Mouse Down Right"), + triggerMouseUpLeft = op.outTrigger("Mouse Up Left"), + triggerMouseUpMiddle = op.outTrigger("Mouse Up Middle"), + triggerMouseUpRight = op.outTrigger("Mouse Up Right"), + area = op.inValueSelect("Area", ["Canvas", "Document"], "Canvas"), + active = op.inValueBool("Active", true); + +const cgl = op.patch.cgl; +let listenerElement = null; +area.onChange = updateListeners; +op.onDelete = removeListeners; +updateListeners(); + +function onMouseDown(e) +{ + if (e.which == 1) + { + mouseDownLeft.set(true); + triggerMouseDownLeft.trigger(); + } + else if (e.which == 2) + { + mouseDownMiddle.set(true); + triggerMouseDownMiddle.trigger(); + } + else if (e.which == 3) + { + mouseDownRight.set(true); + triggerMouseDownRight.trigger(); + } +} + +function onMouseUp(e) +{ + if (e.which == 1) + { + mouseDownLeft.set(false); + triggerMouseUpLeft.trigger(); + } + else if (e.which == 2) + { + mouseDownMiddle.set(false); + triggerMouseUpMiddle.trigger(); + } + else if (e.which == 3) + { + mouseDownRight.set(false); + triggerMouseUpRight.trigger(); + } +} + +function onClickRight(e) +{ + mouseClickRight.trigger(); + e.preventDefault(); +} + +function onDoubleClick(e) +{ + mouseDoubleClick.trigger(); +} + +function onmouseclick(e) +{ + mouseClickLeft.trigger(); +} + +function ontouchstart(event) +{ + if (event.touches && event.touches.length > 0) + { + event.touches[0].which = 1; + onMouseDown(event.touches[0]); + } +} + +function ontouchend(event) +{ + onMouseUp({ "which": 1 }); +} + +function removeListeners() +{ + if (!listenerElement) return; + listenerElement.removeEventListener("touchend", ontouchend); + listenerElement.removeEventListener("touchcancel", ontouchend); + listenerElement.removeEventListener("touchstart", ontouchstart); + listenerElement.removeEventListener("dblclick", onDoubleClick); + listenerElement.removeEventListener("click", onmouseclick); + listenerElement.removeEventListener("mousedown", onMouseDown); + listenerElement.removeEventListener("mouseup", onMouseUp); + listenerElement.removeEventListener("contextmenu", onClickRight); + listenerElement.removeEventListener("mouseleave", onMouseUp); + listenerElement = null; +} + +function addListeners() +{ + if (listenerElement)removeListeners(); + + listenerElement = cgl.canvas; + if (area.get() == "Document") listenerElement = document.body; + + listenerElement.addEventListener("touchend", ontouchend); + listenerElement.addEventListener("touchcancel", ontouchend); + listenerElement.addEventListener("touchstart", ontouchstart); + listenerElement.addEventListener("dblclick", onDoubleClick); + listenerElement.addEventListener("click", onmouseclick); + listenerElement.addEventListener("mousedown", onMouseDown); + listenerElement.addEventListener("mouseup", onMouseUp); + listenerElement.addEventListener("contextmenu", onClickRight); + listenerElement.addEventListener("mouseleave", onMouseUp); +} + +op.onLoaded = updateListeners; + +active.onChange = updateListeners; + +function updateListeners() +{ + removeListeners(); + if (active.get()) addListeners(); +} + + +}; + +Ops.Devices.Mouse.MouseButtons.prototype = new CABLES.Op(); +CABLES.OPS["c7e5e545-c8a1-4fef-85c2-45422b947f0d"]={f:Ops.Devices.Mouse.MouseButtons,objName:"Ops.Devices.Mouse.MouseButtons"}; + + + + +// ************************************************************** +// +// Ops.Devices.Mouse.MouseDrag +// +// ************************************************************** + +Ops.Devices.Mouse.MouseDrag = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + active = op.inValueBool("Active", true), + speed = op.inValue("Speed", 0.01), + inputType = op.inSwitch("Input Type", ["All", "Mouse", "Touch"], "All"), + area = op.inSwitch("Area", ["Canvas", "Document"], "Canvas"), + outDeltaX = op.outNumber("Delta X"), + outDeltaY = op.outNumber("Delta Y"), + outDragging = op.outNumber("Is Dragging"); + +let listenerElement = null; +const absoluteX = 0; +const absoluteY = 0; +let pressed = false; +let lastX = 0; +let lastY = 0; +let firstMove = true; + +area.onChange = updateArea; + +updateArea(); + +function onMouseMove(e) +{ + if (e.touches) e = e.touches[0]; + + if (pressed && e) + { + if (!firstMove) + { + outDragging.set(true); + const deltaX = (e.clientX - lastX) * speed.get(); + const deltaY = (e.clientY - lastY) * speed.get(); + + outDeltaX.set(0); + outDeltaY.set(0); + outDeltaX.set(deltaX); + outDeltaY.set(deltaY); + } + + firstMove = false; + + lastX = e.clientX; + lastY = e.clientY; + } +} + +function onMouseDown(e) +{ + try { listenerElement.setPointerCapture(e.pointerId); } + catch (_e) {} + + pressed = true; +} + +function onMouseUp(e) +{ + try { listenerElement.releasePointerCapture(e.pointerId); } + catch (e) {} + + pressed = false; + outDragging.set(false); + lastX = 0; + lastY = 0; + firstMove = true; +} + +function updateArea() +{ + removeListener(); + + if (area.get() == "Document") listenerElement = document; + else listenerElement = op.patch.cgl.canvas; + + if (active.get())addListener(); +} + +function addListener() +{ + if (!listenerElement)updateArea(); + + if (inputType.get() == "All" || inputType.get() == "Mouse") + { + listenerElement.addEventListener("mousemove", onMouseMove); + listenerElement.addEventListener("mousedown", onMouseDown); + listenerElement.addEventListener("mouseup", onMouseUp); + listenerElement.addEventListener("mouseenter", onMouseUp); + listenerElement.addEventListener("mouseleave", onMouseUp); + } + + if (inputType.get() == "All" || inputType.get() == "Touch") + { + listenerElement.addEventListener("touchmove", onMouseMove); + listenerElement.addEventListener("touchend", onMouseUp); + listenerElement.addEventListener("touchstart", onMouseDown); + } +} + +function removeListener() +{ + if (!listenerElement) return; + listenerElement.removeEventListener("mousemove", onMouseMove); + listenerElement.removeEventListener("mousedown", onMouseDown); + listenerElement.removeEventListener("mouseup", onMouseUp); + listenerElement.removeEventListener("mouseenter", onMouseUp); + listenerElement.removeEventListener("mouseleave", onMouseUp); + + listenerElement.removeEventListener("touchmove", onMouseMove); + listenerElement.removeEventListener("touchend", onMouseUp); + listenerElement.removeEventListener("touchstart", onMouseDown); +} + +active.onChange = function () +{ + if (active.get())addListener(); + else removeListener(); +}; + +op.onDelete = function () +{ + removeListener(); +}; + + +}; + +Ops.Devices.Mouse.MouseDrag.prototype = new CABLES.Op(); +CABLES.OPS["5103d14e-2f21-4f43-ae91-c1b55a944226"]={f:Ops.Devices.Mouse.MouseDrag,objName:"Ops.Devices.Mouse.MouseDrag"}; + + + + +// ************************************************************** +// +// Ops.Devices.Mouse.MouseWheel_v2 +// +// ************************************************************** + +Ops.Devices.Mouse.MouseWheel_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + speed = op.inValue("Speed", 1), + preventScroll = op.inValueBool("prevent scroll", true), + flip = op.inValueBool("Flip Direction"), + inSimpleIncrement = op.inBool("Simple Delta", true), + area = op.inSwitch("Area", ["Canvas", "Document", "Parent"], "Document"), + active = op.inValueBool("active", true), + delta = op.outNumber("delta", 0), + deltaX = op.outNumber("delta X", 0), + deltaOrig = op.outNumber("browser event delta", 0), + trigger = op.outTrigger("Wheel Action"); + +const cgl = op.patch.cgl; +const value = 0; + +const startTime = CABLES.now() / 1000.0; +const v = 0; + +let dir = 1; + +let listenerElement = null; + +area.onChange = updateArea; +const vOut = 0; + +addListener(); + +const isChromium = window.chrome, + winNav = window.navigator, + vendorName = winNav.vendor, + isOpera = winNav.userAgent.indexOf("OPR") > -1, + isIEedge = winNav.userAgent.indexOf("Edge") > -1, + isIOSChrome = winNav.userAgent.match("CriOS"); + +const isWindows = window.navigator.userAgent.indexOf("Windows") != -1; +const isLinux = window.navigator.userAgent.indexOf("Linux") != -1; +const isMac = window.navigator.userAgent.indexOf("Mac") != -1; + +const isChrome = (isChromium !== null && isChromium !== undefined && vendorName === "Google Inc." && isOpera === false && isIEedge === false); +const isFirefox = navigator.userAgent.search("Firefox") > 1; + +flip.onChange = function () +{ + if (flip.get())dir = -1; + else dir = 1; +}; + +function normalizeWheel(event) +{ + let sY = 0; + + if ("detail" in event) { sY = event.detail; } + + if ("deltaY" in event) + { + sY = event.deltaY; + if (event.deltaY > 20)sY = 20; + else if (event.deltaY < -20)sY = -20; + } + return sY * dir; +} + +function normalizeWheelX(event) +{ + let sX = 0; + + if ("deltaX" in event) + { + sX = event.deltaX; + if (event.deltaX > 20)sX = 20; + else if (event.deltaX < -20)sX = -20; + } + return sX; +} + +let lastEvent = 0; + +function onMouseWheel(e) +{ + if (Date.now() - lastEvent < 10) return; + lastEvent = Date.now(); + + deltaOrig.set(e.wheelDelta || e.deltaY); + + if (e.deltaY) + { + let d = normalizeWheel(e); + if (inSimpleIncrement.get()) + { + if (d > 0)d = speed.get(); + else d = -speed.get(); + } + else d *= 0.01 * speed.get(); + + delta.set(0); + delta.set(d); + } + + if (e.deltaX) + { + let dX = normalizeWheelX(e); + dX *= 0.01 * speed.get(); + + deltaX.set(0); + deltaX.set(dX); + } + + if (preventScroll.get()) e.preventDefault(); + trigger.trigger(); +} + +function updateArea() +{ + removeListener(); + + if (area.get() == "Document") listenerElement = document; + if (area.get() == "Parent") listenerElement = cgl.canvas.parentElement; + else listenerElement = cgl.canvas; + + if (active.get())addListener(); +} + +function addListener() +{ + if (!listenerElement)updateArea(); + listenerElement.addEventListener("wheel", onMouseWheel, { "passive": false }); +} + +function removeListener() +{ + if (listenerElement) listenerElement.removeEventListener("wheel", onMouseWheel); +} + +active.onChange = function () +{ + updateArea(); +}; + + +}; + +Ops.Devices.Mouse.MouseWheel_v2.prototype = new CABLES.Op(); +CABLES.OPS["7b9626db-536b-4bb4-85c3-95401bc60d1b"]={f:Ops.Devices.Mouse.MouseWheel_v2,objName:"Ops.Devices.Mouse.MouseWheel_v2"}; + + + + +// ************************************************************** +// +// Ops.Devices.Mouse.Mouse_v3 +// +// ************************************************************** + +Ops.Devices.Mouse.Mouse_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inCoords = op.inSwitch("Coordinates", ["Pixel", "Pixel Display", "-1 to 1", "0 to 1"], "-1 to 1"), + area = op.inValueSelect("Area", ["Canvas", "Document", "Parent Element"], "Canvas"), + flipY = op.inValueBool("flip y", true), + rightClickPrevDef = op.inBool("right click prevent default", true), + touchscreen = op.inValueBool("Touch support", true), + active = op.inValueBool("Active", true), + outMouseX = op.outNumber("x", 0), + outMouseY = op.outNumber("y", 0), + mouseClick = op.outTrigger("click"), + mouseClickRight = op.outTrigger("click right"), + mouseDown = op.outBoolNum("Button is down"), + mouseOver = op.outBoolNum("Mouse is hovering"); + +const cgl = op.patch.cgl; +let normalize = 1; +let listenerElement = null; +let mouseX = cgl.canvas.width / 2; +let mouseY = cgl.canvas.height / 2; +area.onChange = addListeners; + +outMouseX.set(mouseX); +outMouseY.set(mouseY); + +inCoords.onChange = updateCoordNormalizing; +op.onDelete = removeListeners; + +addListeners(); + +function setValue(x, y) +{ + x = x || 0; + y = y || 0; + + if (normalize == 0) + { + outMouseX.set(x); + outMouseY.set(y); + } + else + if (normalize == 3) + { + outMouseX.set(x * cgl.pixelDensity); + outMouseY.set(y * cgl.pixelDensity); + } + else + { + let w = cgl.canvas.width / cgl.pixelDensity; + let h = cgl.canvas.height / cgl.pixelDensity; + if (listenerElement == document.body) + { + w = listenerElement.clientWidth / cgl.pixelDensity; + h = listenerElement.clientHeight / cgl.pixelDensity; + } + + if (normalize == 1) + { + outMouseX.set(x / w * 2.0 - 1.0); + outMouseY.set(y / h * 2.0 - 1.0); + } + if (normalize == 2) + { + outMouseX.set(x / w); + outMouseY.set(y / h); + } + } +} + +touchscreen.onChange = function () +{ + removeListeners(); + addListeners(); +}; + +active.onChange = function () +{ + if (listenerElement)removeListeners(); + if (active.get())addListeners(); +}; + +function updateCoordNormalizing() +{ + mouseX = 0; + mouseY = 0; + setValue(mouseX, mouseY); + + if (inCoords.get() == "Pixel")normalize = 0; + else if (inCoords.get() == "-1 to 1")normalize = 1; + else if (inCoords.get() == "0 to 1")normalize = 2; + else if (inCoords.get() == "Pixel CSS")normalize = 3; +} + +function onMouseEnter(e) +{ + mouseDown.set(false); + mouseOver.set(true); +} + +function onMouseDown(e) +{ + mouseDown.set(true); +} + +function onMouseUp(e) +{ + mouseDown.set(false); +} + +function onClickRight(e) +{ + mouseClickRight.trigger(); + if (rightClickPrevDef.get()) e.preventDefault(); +} + +function onmouseclick(e) +{ + mouseClick.trigger(); +} + +function onMouseLeave(e) +{ + mouseOver.set(false); + mouseDown.set(false); +} + +function setCoords(e) +{ + let x = e.clientX; + let y = e.clientY; + + if (area.get() != "Document") + { + x = e.offsetX; + y = e.offsetY; + } + + if (flipY.get()) setValue(x, listenerElement.clientHeight - y); + else setValue(x, y); +} + +function onmousemove(e) +{ + mouseOver.set(true); + setCoords(e); +} + +function ontouchmove(e) +{ + if (event.touches && event.touches.length > 0) setCoords(e.touches[0]); +} + +function ontouchstart(event) +{ + mouseDown.set(true); + + if (event.touches && event.touches.length > 0) onMouseDown(event.touches[0]); +} + +function ontouchend(event) +{ + mouseDown.set(false); + onMouseUp(); +} + +function removeListeners() +{ + if (!listenerElement) return; + listenerElement.removeEventListener("touchend", ontouchend); + listenerElement.removeEventListener("touchstart", ontouchstart); + listenerElement.removeEventListener("touchmove", ontouchmove); + + listenerElement.removeEventListener("click", onmouseclick); + listenerElement.removeEventListener("mousemove", onmousemove); + listenerElement.removeEventListener("mouseleave", onMouseLeave); + listenerElement.removeEventListener("mousedown", onMouseDown); + listenerElement.removeEventListener("mouseup", onMouseUp); + listenerElement.removeEventListener("mouseenter", onMouseEnter); + listenerElement.removeEventListener("contextmenu", onClickRight); + listenerElement = null; +} + +function addListeners() +{ + if (listenerElement || !active.get())removeListeners(); + if (!active.get()) return; + + listenerElement = cgl.canvas; + if (area.get() == "Document") listenerElement = document.body; + if (area.get() == "Parent Element") listenerElement = cgl.canvas.parentElement; + + if (touchscreen.get()) + { + listenerElement.addEventListener("touchend", ontouchend); + listenerElement.addEventListener("touchstart", ontouchstart); + listenerElement.addEventListener("touchmove", ontouchmove); + } + + listenerElement.addEventListener("mousemove", onmousemove); + listenerElement.addEventListener("mouseleave", onMouseLeave); + listenerElement.addEventListener("mousedown", onMouseDown); + listenerElement.addEventListener("mouseup", onMouseUp); + listenerElement.addEventListener("mouseenter", onMouseEnter); + listenerElement.addEventListener("contextmenu", onClickRight); + listenerElement.addEventListener("click", onmouseclick); +} + + +}; + +Ops.Devices.Mouse.Mouse_v3.prototype = new CABLES.Op(); +CABLES.OPS["6d1edbc0-088a-43d7-9156-918fb3d7f24b"]={f:Ops.Devices.Mouse.Mouse_v3,objName:"Ops.Devices.Mouse.Mouse_v3"}; + + + + +// ************************************************************** +// +// Ops.Devices.TouchGesture +// +// ************************************************************** + +Ops.Devices.TouchGesture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +const inEnabled = op.inBool("Active", true); +let enableVerticalSwipePort = op.inValueBool("Vertical Swipe", true); +let enableVerticalPanPort = op.inValueBool("Vertical Pan", true); + +// outputs +let pressPort = op.outTrigger("Press"); +let pressUpPort = op.outTrigger("Press Up"); +let panLeftPort = op.outTrigger("Pan Left"); +let panRightPort = op.outTrigger("Pan Right"); +let swipeLeftPort = op.outTrigger("Swipe Left"); +let swipeRightPort = op.outTrigger("Swipe Right"); +let swipeUpPort = op.outTrigger("Swipe Up"); +let swipeDownPort = op.outTrigger("Swipe Down"); +let eventPort = op.outObject("Event"); + +let canvas = op.patch.cgl.canvas; + +// create a simple instance +// by default, it only adds horizontal recognizers +let mc = new Hammer(canvas); + +// change listeners +enableVerticalSwipePort.onChange = onEnableVerticalSwipePortChange; +enableVerticalPanPort.onChange = onEnableVerticalPanPortChange; + +// init +onEnableVerticalSwipePortChange(); +onEnableVerticalPanPortChange(); + +function onEnableVerticalSwipePortChange() +{ + let direction = Hammer.DIRECTION_HORIZONTAL; + if (enableVerticalSwipePort.get()) + { + direction = Hammer.DIRECTION_ALL; + } + mc.get("swipe").set({ "direction": direction }); +} + +function onEnableVerticalPanPortChange() +{ + let direction = Hammer.DIRECTION_HORIZONTAL; + if (enableVerticalPanPort.get()) + { + direction = Hammer.DIRECTION_ALL; + } + mc.get("pan").set({ "direction": direction }); +} + +/* +mc.on("panleft panright tap press", function(ev) { + myElement.textContent = ev.type +" gesture detected."; +}); +*/ + +mc.on("panleft", onPanLeft); +mc.on("panright", onPanRight); +mc.on("swipeleft", onSwipeLeft); +mc.on("swiperight", onSwipeRight); +mc.on("swipeup", onSwipeUp); +mc.on("swipedown", onSwipeDown); +mc.on("press", onPress); +mc.on("pressup", onPressUp); + +function onPress(ev) +{ + if (!inEnabled.get()) return; + eventPort.set(ev); + pressPort.trigger(); +} + +function onPressUp(ev) +{ + if (!inEnabled.get()) return; + eventPort.set(ev); + pressUpPort.trigger(); +} + +function onPanLeft(ev) +{ + if (!inEnabled.get()) return; + eventPort.set(ev); + panLeftPort.trigger(); +} + +function onPanRight(ev) +{ + if (!inEnabled.get()) return; + eventPort.set(ev); + panRightPort.trigger(); +} + +function onSwipeLeft(ev) +{ + if (!inEnabled.get()) return; + eventPort.set(ev); + swipeLeftPort.trigger(); +} + +function onSwipeRight(ev) +{ + if (!inEnabled.get()) return; + eventPort.set(ev); + swipeRightPort.trigger(); +} + +function onSwipeUp(ev) +{ + if (!inEnabled.get()) return; + eventPort.set(ev); + swipeUpPort.trigger(); +} + +function onSwipeDown(ev) +{ + if (!inEnabled.get()) return; + eventPort.set(ev); + swipeDownPort.trigger(); +} + +/* +// By default it adds a set of tap, doubletap, press, +// horizontal pan and swipe, and the multi-touch pinch +// and rotate recognizers. The pinch and rotate recognizers +// are disabled by default because they would make the +// element blocking, but you can enable them by calling: +hammertime.get('pinch').set({ enable: true }); +hammertime.get('rotate').set({ enable: true }); + +// Enabling vertical or all directions for the pan and swipe recognizers: +hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL }); +hammertime.get('swipe').set({ direction: Hammer.DIRECTION_VERTICAL }); +*/ + + +}; + +Ops.Devices.TouchGesture.prototype = new CABLES.Op(); +CABLES.OPS["08c5d91e-32d2-4852-ac18-d4cae543dd37"]={f:Ops.Devices.TouchGesture,objName:"Ops.Devices.TouchGesture"}; + + + + +// ************************************************************** +// +// Ops.Devices.TouchScreen +// +// ************************************************************** + +Ops.Devices.TouchScreen = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + disableScaleWeb = op.inValueBool("Disable Scaling", true), + disableDefault = op.inValueBool("Disable Scroll", true), + hdpi = op.inValueBool("HDPI Coordinates", false), + active = op.inValueBool("Active", true), + + outTouched = op.outNumber("Touched", false), + numFingers = op.outNumber("Fingers", 0), + + f1x = op.outNumber("Finger 1 X", 0), + f1y = op.outNumber("Finger 1 Y", 0), + f1f = op.outNumber("Finger 1 Force", 0), + + f2x = op.outNumber("Finger 2 X", 0), + f2y = op.outNumber("Finger 2 Y", 0), + area = op.inSwitch("Area", ["Canvas", "Document"], "Canvas"), + + outEvents = op.outArray("Events"), + normalize = op.inValueBool("Normalize Coordinates"), + flipY = op.inValueBool("Flip Y"), + outTouchStart = op.outTrigger("Touch Start"), + outTouchEnd = op.outTrigger("Touch End"); + +area.onChange = updateArea; + +function setPos(event) +{ + if (event.touches && event.touches.length > 0) + { + var rect = event.target.getBoundingClientRect(); + var x = event.touches[0].clientX - event.touches[0].target.offsetLeft; + var y = event.touches[0].clientY - event.touches[0].target.offsetTop; + + if (flipY.get()) y = rect.height - y; + + if (hdpi.get()) + { + x *= (op.patch.cgl.pixelDensity || 1); + y *= (op.patch.cgl.pixelDensity || 1); + } + + if (normalize.get()) + { + x = (x / rect.width * 2.0 - 1.0); + y = (y / rect.height * 2.0 - 1.0); + } + + f1x.set(x); + f1y.set(y); + + if (event.touches[0].force)f1f.set(event.touches[0].force); + } + + if (event.touches && event.touches.length > 1) + { + var rect = event.target.getBoundingClientRect(); + var x = event.touches[1].clientX - event.touches[1].target.offsetLeft; + var y = event.touches[1].clientY - event.touches[1].target.offsetTop; + + if (hdpi.get()) + { + x *= (op.patch.cgl.pixelDensity || 1); + y *= (op.patch.cgl.pixelDensity || 1); + } + + f2x.set(x); + f2y.set(y); + } + outEvents.set(event.touches); +} + +const ontouchstart = function (event) +{ + outTouched.set(true); + setPos(event); + numFingers.set(event.touches.length); + outTouchStart.trigger(); +}; + +const ontouchend = function (event) +{ + outTouched.set(false); + f1f.set(0); + setPos(event); + + numFingers.set(event.touches.length); + outTouchEnd.trigger(); +}; + +const ontouchmove = function (event) +{ + setPos(event); + numFingers.set(event.touches.length); + if (disableDefault.get() || (disableScaleWeb.get() && event.scale !== 1)) + { + event.preventDefault(); + document.body.style["touch-action"] = "none"; + } + else + { + document.body.style["touch-action"] = "initial"; + } +}; + +const cgl = op.patch.cgl; +let listenerElement = null; +function addListeners() +{ + listenerElement.addEventListener("touchmove", ontouchmove, { "passive": true }); + listenerElement.addEventListener("touchstart", ontouchstart, { "passive": true }); + listenerElement.addEventListener("touchend", ontouchend, { "passive": true }); +} + +function updateArea() +{ + removeListeners(); + + if (area.get() == "Document") listenerElement = document; + else listenerElement = cgl.canvas; + + if (active.get()) addListeners(); +} + +function removeListeners() +{ + if (listenerElement) + { + listenerElement.removeEventListener("touchmove", ontouchmove); + listenerElement.removeEventListener("touchstart", ontouchstart); + listenerElement.removeEventListener("touchend", ontouchend); + } + listenerElement = null; +} + +active.onChange = function () +{ + updateArea(); +}; + +updateArea(); + + +}; + +Ops.Devices.TouchScreen.prototype = new CABLES.Op(); +CABLES.OPS["cedffacf-0f09-4342-bd21-540bd9c8037d"]={f:Ops.Devices.TouchScreen,objName:"Ops.Devices.TouchScreen"}; + + + + +// ************************************************************** +// +// Ops.Game.PersonController +// +// ************************************************************** + +Ops.Game.PersonController = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("Exe"), + speed = op.inValue("Speed", 1), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outDir = op.outNumber("Dir"), + goNorth = op.inBool("North"), + goEast = op.inBool("East"), + goSouth = op.inBool("South"), + goWest = op.inBool("West"); + +let lastTime = performance.now(); +let dir = 0; + +exe.onTriggered = function () +{ + let ago = (performance.now() - lastTime) / 1000; + let x = 0; + let y = 0; + if (goEast.get())x += ago * speed.get(); + if (goWest.get())x -= ago * speed.get(); + if (goNorth.get())y += ago * speed.get(); + if (goSouth.get())y -= ago * speed.get(); + + if (goEast.get())dir = 90; + if (goWest.get())dir = 270; + if (goNorth.get())dir = 0; + if (goSouth.get())dir = 180; + + outDir.set(dir); + outX.set(outX.get() + x); + outY.set(outY.get() + y); + lastTime = performance.now(); +}; + + +}; + +Ops.Game.PersonController.prototype = new CABLES.Op(); +CABLES.OPS["7b08dea1-a05b-422e-a1de-7832058bee51"]={f:Ops.Game.PersonController,objName:"Ops.Game.PersonController"}; + + + + +// ************************************************************** +// +// Ops.Geometry.BoundingBox +// +// ************************************************************** + +Ops.Geometry.BoundingBox = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inW = op.inFloat("Width", 1), + inH = op.inFloat("Height", 1), + inD = op.inFloat("Depth", 1), + result = op.outObject("Result"); + +inW.onChange = +inH.onChange = +inD.onChange = update; +update(); + +function update() +{ + result.set( + { + "_max": [inW.get() / 2, inH.get() / 2, inD.get() / 2], + "_min": [-inW.get() / 2, -inH.get() / 2, -inD.get() / 2], + "_center": [0, 0, 0] + }); +} + + +}; + +Ops.Geometry.BoundingBox.prototype = new CABLES.Op(); +CABLES.OPS["73dc3511-7c1d-4e42-9e00-f86bc952058a"]={f:Ops.Geometry.BoundingBox,objName:"Ops.Geometry.BoundingBox"}; + + + + +// ************************************************************** +// +// Ops.Geometry.CalculateNormals +// +// ************************************************************** + +Ops.Geometry.CalculateNormals = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + geometry = op.inObject("Geometry"), + smoothNormals = op.inValueBool("Smooth"), + forceZUp = op.inValueBool("Force Z Up"), + geomOut = op.outObject("Geometry Out"); + +geomOut.ignoreValueSerialize = true; +geometry.ignoreValueSerialize = true; + +geometry.onChange = calc; +smoothNormals.onChange = calc; +forceZUp.onChange = calc; + +let geom = null; + +function calc() +{ + if (!geometry.get()) return; + + let geom = geometry.get().copy(); + + if (!smoothNormals.get())geom.unIndex(); + + geom.calculateNormals({ + "forceZUp": forceZUp.get() + }); + + geomOut.set(geom); +} + + +}; + +Ops.Geometry.CalculateNormals.prototype = new CABLES.Op(); +CABLES.OPS["c90c70e5-59eb-4b19-9c15-962e3e2f236b"]={f:Ops.Geometry.CalculateNormals,objName:"Ops.Geometry.CalculateNormals"}; + + + + +// ************************************************************** +// +// Ops.Geometry.GeometryToWireframeArray3 +// +// ************************************************************** + +Ops.Geometry.GeometryToWireframeArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inGeom = op.inObject("Geometry"), + outArr = op.outArray("Array"); + + +inGeom.onChange = function () +{ + const g = inGeom.get(); + + outArr.set(null); + + if (!g) return; + + const arr = []; + + for (let i = 0; i < g.verticesIndices.length; i += 3) + { + arr.push( + g.vertices[g.verticesIndices[i + 0] * 3 + 0], + g.vertices[g.verticesIndices[i + 0] * 3 + 1], + g.vertices[g.verticesIndices[i + 0] * 3 + 2], + + g.vertices[g.verticesIndices[i + 1] * 3 + 0], + g.vertices[g.verticesIndices[i + 1] * 3 + 1], + g.vertices[g.verticesIndices[i + 1] * 3 + 2], + + + g.vertices[g.verticesIndices[i + 1] * 3 + 0], + g.vertices[g.verticesIndices[i + 1] * 3 + 1], + g.vertices[g.verticesIndices[i + 1] * 3 + 2], + + g.vertices[g.verticesIndices[i + 2] * 3 + 0], + g.vertices[g.verticesIndices[i + 2] * 3 + 1], + g.vertices[g.verticesIndices[i + 2] * 3 + 2], + + + g.vertices[g.verticesIndices[i + 2] * 3 + 0], + g.vertices[g.verticesIndices[i + 2] * 3 + 1], + g.vertices[g.verticesIndices[i + 2] * 3 + 2], + + g.vertices[g.verticesIndices[i + 0] * 3 + 0], + g.vertices[g.verticesIndices[i + 0] * 3 + 1], + g.vertices[g.verticesIndices[i + 0] * 3 + 2]); + } + + outArr.set(arr); +}; + + +}; + +Ops.Geometry.GeometryToWireframeArray3.prototype = new CABLES.Op(); +CABLES.OPS["d6d0f3ce-5670-49bb-893a-7c9b0262b991"]={f:Ops.Geometry.GeometryToWireframeArray3,objName:"Ops.Geometry.GeometryToWireframeArray3"}; + + + + +// ************************************************************** +// +// Ops.Geometry.Triangulate2dPath +// +// ************************************************************** + +Ops.Geometry.Triangulate2dPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTriggerButton("Update"), + inWinding = op.inDropDown("Combine", ["Positive", "Odd", "Intersect", "Negative"], "Positive"), + inPath = op.inArray("2d Point Path"), + inPath2 = op.inArray("Path 2"), + inPath3 = op.inArray("Path 3"), + next = op.outTrigger("Next"), + outGeom = op.outObject("Geometry"); + +inExec.onTriggered = tess; +let geom = null; +function tess() +{ + // var Tess2 = require('tess2'); + + // Define input + // var ca = [0,0, 10,0, 5,10]; + // var cb = [0,2, 10,2, 10,6, 0,6]; + // var contours = [ca,cb]; + + let points = inPath.get(); + + if (!points || points.length === 0) + { + outGeom.set(null); + return; + } + + let contours = [points]; + + let points2 = inPath2.get(); + if (points2 && points2.length > 0) contours.push(points2); + + let points3 = inPath3.get(); + if (points3 && points3.length > 0) contours.push(points3); + + let winding = Tess2.WINDING_ODD; + if (inWinding.get() == "Positive") + { + winding = Tess2.WINDING_POSITIVE; + } + if (inWinding.get() == "Negative") + { + winding = Tess2.WINDING_NEGATIVE; + } + if (inWinding.get() == "Intersect") + { + winding = Tess2.WINDING_ABS_GEQ_TWO; + } + + let res = null; + try + { + // Tesselate + res = Tess2.tesselate({ + "contours": contours, + "windingRule": winding, + "elementType": Tess2.POLYGONS, + "polySize": 3, + "vertexSize": 2 + }); + } + catch (e) {} + + if (res) + { + let changed = true; + + if (geom) changed = geom.vertices.length / 3 != res.vertices.length / 2; + + if (changed) + { + geom = new CGL.Geometry("tess2geom"); + } + + let verts3 = []; + for (let i = 0; i < res.vertices.length; i += 2) + { + verts3.push(res.vertices[i + 0], res.vertices[i + 1], 0); + } + + geom.vertices = verts3; + geom.verticesIndices = res.elements; + geom.calculateNormals(); + geom.mapTexCoords2d(); + geom.flipVertDir(); + + outGeom.set(null); + outGeom.set(geom); + } + + next.trigger(); +} + + +}; + +Ops.Geometry.Triangulate2dPath.prototype = new CABLES.Op(); +CABLES.OPS["6165a15f-abf3-4398-87f7-541bedc109e5"]={f:Ops.Geometry.Triangulate2dPath,objName:"Ops.Geometry.Triangulate2dPath"}; + + + + +// ************************************************************** +// +// Ops.Gl.AspectRatio +// +// ************************************************************** + +Ops.Gl.AspectRatio = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let aspects = [ + { "title": "1:2", "v": 1 / 2 }, + { "title": "1", "v": 1 }, + { "title": "5:4", "v": 5 / 4 }, + { "title": "4:3", "v": 4 / 3 }, + { "title": "3:2", "v": 3 / 2 }, + { "title": "11:8", "v": 11 / 8 }, + { "title": "16:9", "v": 16 / 9 }, + { "title": "2:1", "v": 2 }, + { "title": "21:9", "v": 21 / 9 }, + { "title": "custom", "v": 0 }, +]; +let aspectTitles = []; +for (let i = 0; i < aspects.length; i++) aspectTitles.push(aspects[i].title); + +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + outWidth = op.outNumber("Width"), + outHeight = op.outNumber("Height"), + inAspect = op.inValueSelect("Aspect Ratio", aspectTitles, aspects[0].title), + inCustom = op.inValueFloat("Custom", 1.777777); + +const + useVPSize = op.inValueBool("use viewport size", true), + width = op.inValueInt("texture width", 512), + height = op.inValueInt("texture height", 512); + +op.setPortGroup("Size", [useVPSize, width, height]); +useVPSize.onChange = updateVpSize; +function updateVpSize() +{ + width.setUiAttribs({ "greyout": useVPSize.get() }); + height.setUiAttribs({ "greyout": useVPSize.get() }); +} + +const prevViewPort = []; +const cgl = op.patch.cgl; +let w = 1000, h = 1000, x, y; +let ratio = 1; + +inAspect.onChange = changedRatio; +inCustom.onChange = changedRatio; + +changedRatio(); + +function changedRatio() +{ + let selected = inAspect.get(); + if (selected == "custom") + { + if (inCustom.uiAttribs.greyout) inCustom.setUiAttribs({ "greyout": false }); + ratio = Math.abs(inCustom.get()); + } + else + { + if (!inCustom.uiAttribs.greyout) inCustom.setUiAttribs({ "greyout": true }); + + for (let i = 0; i < aspects.length; i++) + { + if (aspects[i].title == selected) + { + ratio = aspects[i].v; + break; + } + } + } +} + +function resize() +{ + let theWidth = cgl.canvasWidth; + let theHeight = cgl.canvasHeight; + + if (!useVPSize.get()) + { + theWidth = width.get(); + theHeight = height.get(); + } + + let _w = theHeight * ratio; + let _h = theHeight; + let _x = 0; + let _y = 0; + if (_w > theWidth) + { + _w = theWidth; + _h = theWidth / ratio; + } + + if (_w < theWidth) _x = (theWidth - _w) / 2; + if (_h < theHeight) _y = (theHeight - _h) / 2; + + _w = Math.ceil(_w); + _h = Math.ceil(_h); + _x = Math.ceil(_x); + _y = Math.ceil(_y); + + if (_w != w || _h != h || _x != x || _y != y) + { + w = _w; + h = _h; + x = _x; + y = _y; + + cgl.setViewPort(x, y, w, h); + + for (let i = 0; i < op.patch.ops.length; i++) + if (op.patch.ops[i].onResize)op.patch.ops[i].onResize(); + } + + outWidth.set(w); + outHeight.set(h); +} + +op.onDelete = function () +{ + cgl.resetViewPort(); +}; + +render.onTriggered = function () +{ + let vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + resize(); + x = Math.ceil(x); + y = Math.ceil(y); + w = Math.ceil(w); + h = Math.ceil(h); + + cgl.setViewPort(x, y, w, h); + + mat4.perspective(cgl.pMatrix, 45, ratio, 0.1, 1100.0); + + trigger.trigger(); + + cgl.setViewPort(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); +}; + + +}; + +Ops.Gl.AspectRatio.prototype = new CABLES.Op(); +CABLES.OPS["b4f17994-3028-4203-9a8d-d5e5377a58fc"]={f:Ops.Gl.AspectRatio,objName:"Ops.Gl.AspectRatio"}; + + + + +// ************************************************************** +// +// Ops.Gl.BlendMode +// +// ************************************************************** + +Ops.Gl.BlendMode = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Render"), + inBlend = op.inValueSelect("Blendmode", ["None", "Normal", "Add", "Subtract", "Multiply"], "Normal"), + inPremul = op.inValueBool("Premultiplied"), + next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; +let blendMode = 0; +inBlend.onChange = update; +update(); + +function update() +{ + if (inBlend.get() == "Normal")blendMode = CGL.BLEND_NORMAL; + else if (inBlend.get() == "Add")blendMode = CGL.BLEND_ADD; + else if (inBlend.get() == "Subtract")blendMode = CGL.BLEND_SUB; + else if (inBlend.get() == "Multiply")blendMode = CGL.BLEND_MUL; + else blendMode = CGL.BLEND_NONE; + + if (CABLES.UI) + { + let blstr = ""; + if (inBlend.get() == "Normal")blstr = ""; + else if (inBlend.get() == "Add")blstr = "Add"; + else if (inBlend.get() == "Subtract")blstr = "Sub"; + else if (inBlend.get() == "Multiply")blstr = "Mul"; + else blstr = "None"; + + op.setUiAttrib({ "extendTitle": blstr }); + } +} + +exec.onTriggered = function () +{ + cgl.pushBlendMode(blendMode, inPremul.get()); + cgl.pushBlend(blendMode != CGL.BLEND_NONE); + next.trigger(); + cgl.popBlend(); + cgl.popBlendMode(); + cgl.gl.blendEquationSeparate(cgl.gl.FUNC_ADD, cgl.gl.FUNC_ADD); + cgl.gl.blendFuncSeparate(cgl.gl.SRC_ALPHA, cgl.gl.ONE_MINUS_SRC_ALPHA, cgl.gl.ONE, cgl.gl.ONE_MINUS_SRC_ALPHA); +}; + + +}; + +Ops.Gl.BlendMode.prototype = new CABLES.Op(); +CABLES.OPS["ce0fff72-1438-4373-924f-e1d0f78b053f"]={f:Ops.Gl.BlendMode,objName:"Ops.Gl.BlendMode"}; + + + + +// ************************************************************** +// +// Ops.Gl.Canvas2Texture +// +// ************************************************************** + +Ops.Gl.Canvas2Texture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + cgl = op.patch.cgl, + inCanvas = op.inObject("canvas"), + inTextureFilter = op.inValueSelect("filter", ["nearest", "linear", "mipmap"]), + inTextureWrap = op.inValueSelect("wrap", ["repeat", "mirrored repeat", "clamp to edge"], "clamp to edge"), + inTextureFlip = op.inValueBool("flip"), + inUnpackAlpha = op.inValueBool("unpackPreMultipliedAlpha"), + outTexture = op.outTexture("texture"), + outWidth = op.outNumber("width"), + outHeight = op.outNumber("height"), + canvasTexture = new CGL.Texture(cgl); + +let cgl_filter = null; +let cgl_wrap = null; + +inTextureFlip.set(false); +inUnpackAlpha.set(false); + +inTextureFlip.setUiAttribs({ "hidePort": true }); +inUnpackAlpha.setUiAttribs({ "hidePort": true }); + +inTextureFilter.onChange = onFilterChange; +inTextureWrap.onChange = onWrapChange; + +inTextureFlip.onChange = +inCanvas.onChange = +inUnpackAlpha.onChange = reload; + +function reload() +{ + let canvas = inCanvas.get(); + if (!canvas) return; + + canvasTexture.unpackAlpha = inUnpackAlpha.get(); + canvasTexture.flip = inTextureFlip.get(); + canvasTexture.wrap = cgl_wrap; + canvasTexture.image = canvas; + canvasTexture.initTexture(canvas, cgl_filter); + outWidth.set(canvasTexture.width); + outHeight.set(canvasTexture.height); + + outTexture.set(CGL.Texture.getEmptyTexture(cgl)); + outTexture.set(canvasTexture); +} + +function onFilterChange() +{ + switch (inTextureFilter.get()) + { + case "nearest": cgl_filter = CGL.Texture.FILTER_NEAREST; break; + case "mipmap": cgl_filter = CGL.Texture.FILTER_MIPMAP; break; + case "linear": + default: cgl_filter = CGL.Texture.FILTER_LINEAR; + } + reload(); +} + +function onWrapChange() +{ + switch (inTextureWrap.get()) + { + case "repeat": cgl_wrap = CGL.Texture.WRAP_REPEAT; break; + case "mirrored repeat": cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; break; + case "clamp to edge": + default: cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + } + reload(); +} + +inTextureFilter.set("linear"); +inTextureWrap.set("repeat"); + +outTexture.set(CGL.Texture.getEmptyTexture(cgl)); + + +}; + +Ops.Gl.Canvas2Texture.prototype = new CABLES.Op(); +CABLES.OPS["2fbada6b-70fa-4b43-87db-8b0d9293990b"]={f:Ops.Gl.Canvas2Texture,objName:"Ops.Gl.Canvas2Texture"}; + + + + +// ************************************************************** +// +// Ops.Gl.CanvasFocus +// +// ************************************************************** + +Ops.Gl.CanvasFocus = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inFocus = op.inTriggerButton("Focus"), + hasFocus = op.outBool("has focus"); + +op.onDelete = removeListeners; +addListeners(); + +inFocus.onTriggered = () => +{ + op.patch.cgl.canvas.focus(); +}; + +function onFocus() +{ + hasFocus.set(true); +} + +function onBlur() +{ + hasFocus.set(false); +} + +function addListeners() +{ + op.patch.cgl.canvas.addEventListener("focus", onFocus); + op.patch.cgl.canvas.addEventListener("blur", onBlur); +} + +function removeListeners() +{ + op.patch.cgl.canvas.removeEventListener("focus", onFocus); + op.patch.cgl.canvas.removeEventListener("blur", onBlur); +} + + +}; + +Ops.Gl.CanvasFocus.prototype = new CABLES.Op(); +CABLES.OPS["e235ea77-f697-475a-8799-d5e4deed7204"]={f:Ops.Gl.CanvasFocus,objName:"Ops.Gl.CanvasFocus"}; + + + + +// ************************************************************** +// +// Ops.Gl.CanvasInBrowserViewport +// +// ************************************************************** + +Ops.Gl.CanvasInBrowserViewport = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUpdate = op.inTriggerButton("Update"), + outResult = op.outBoolNum("Fully Visible"), + outResultPartly = op.outBoolNum("Partly Visible"); + +inUpdate.onTriggered = update; + +window.addEventListener("DOMContentLoaded load", update); +window.addEventListener("resize", update); +window.addEventListener("scroll", update); + +// from: https://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom + +function pointInViewport(x, y) +{ + return ( + y >= 0 && + x >= 0 && + y <= (window.innerHeight || document.documentElement.clientHeight) && + x <= (window.innerWidth || document.documentElement.clientWidth) + ); +} + +function elementInViewport(el) +{ + let rect = el.getBoundingClientRect(); + + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && + rect.right <= (window.innerWidth || document.documentElement.clientWidth) + ); +} + +function elementInViewportPartly(el) +{ + let rect = el.getBoundingClientRect(); + + return ( + pointInViewport(rect.left, rect.top) || + pointInViewport(rect.left, rect.bottom) || + pointInViewport(rect.right, rect.top) || + pointInViewport(rect.right, rect.bottom) + ); +} + +function update() +{ + let visible = elementInViewport(op.patch.cgl.canvas); + let visiblePartly = elementInViewportPartly(op.patch.cgl.canvas); + + outResultPartly.set(visiblePartly); + outResult.set(visible); +} + +update(); + + +}; + +Ops.Gl.CanvasInBrowserViewport.prototype = new CABLES.Op(); +CABLES.OPS["2721f83b-246c-46fa-9ad9-d8c829adb94d"]={f:Ops.Gl.CanvasInBrowserViewport,objName:"Ops.Gl.CanvasInBrowserViewport"}; + + + + +// ************************************************************** +// +// Ops.Gl.CanvasInfo +// +// ************************************************************** + +Ops.Gl.CanvasInfo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + width = op.outNumber("width"), + height = op.outNumber("height"), + inUnit = op.inSwitch("Pixel Unit", ["Display", "CSS"], "Display"), + pixelRatio = op.outNumber("Pixel Ratio"), + aspect = op.outNumber("Aspect Ratio"), + landscape = op.outBool("Landscape"); + +let cgl = op.patch.cgl; + +cgl.on("resize", update); + +inUnit.onChange = update; +update(); + +function update() +{ + let div = 1; + if (inUnit.get() == "CSS")div = op.patch.cgl.pixelDensity; + height.set(cgl.canvasHeight / div); + width.set(cgl.canvasWidth / div); + + pixelRatio.set(op.patch.cgl.pixelDensity); // window.devicePixelRatio + + aspect.set(cgl.canvasWidth / cgl.canvasHeight); + landscape.set(cgl.canvasWidth > cgl.canvasHeight ? 1 : 0); +} + + +}; + +Ops.Gl.CanvasInfo.prototype = new CABLES.Op(); +CABLES.OPS["94e499e5-b4ee-4861-ab48-6ab5098b2cc3"]={f:Ops.Gl.CanvasInfo,objName:"Ops.Gl.CanvasInfo"}; + + + + +// ************************************************************** +// +// Ops.Gl.ClearColor +// +// ************************************************************** + +Ops.Gl.ClearColor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + r = op.inFloatSlider("r", 0.1), + g = op.inFloatSlider("g", 0.1), + b = op.inFloatSlider("b", 0.1), + a = op.inFloatSlider("a", 1); + +r.setUiAttribs({ "colorPick": true }); + +const cgl = op.patch.cgl; + +render.onTriggered = function () +{ + cgl.gl.clearColor(r.get(), g.get(), b.get(), a.get()); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + trigger.trigger(); +}; + + +}; + +Ops.Gl.ClearColor.prototype = new CABLES.Op(); +CABLES.OPS["19b441eb-9f63-4f35-ba08-b87841517c4d"]={f:Ops.Gl.ClearColor,objName:"Ops.Gl.ClearColor"}; + + + + +// ************************************************************** +// +// Ops.Gl.ClearDepth +// +// ************************************************************** + +Ops.Gl.ClearDepth = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +const + render=op.inTrigger('render'), + trigger=op.outTrigger('trigger'), + cgl=op.patch.cgl; + +render.onTriggered=function() +{ + cgl.gl.clear(cgl.gl.DEPTH_BUFFER_BIT); + trigger.trigger(); +}; + + + + +}; + +Ops.Gl.ClearDepth.prototype = new CABLES.Op(); +CABLES.OPS["9e8a4b73-4ba7-4c4f-b266-81c5f9db9b7a"]={f:Ops.Gl.ClearDepth,objName:"Ops.Gl.ClearDepth"}; + + + + +// ************************************************************** +// +// Ops.Gl.ColorMask +// +// ************************************************************** + +Ops.Gl.ColorMask = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Execute"), + inR = op.inValueBool("Red", true), + inG = op.inValueBool("Green", true), + inB = op.inValueBool("Blue", true), + inA = op.inValueBool("Alpha", true), + next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; + +// var old=cgl.gl.getParameter(cgl.gl.COLOR_WRITEMASK); + +exec.onTriggered = function () +{ + cgl.gl.colorMask(inR.get(), inG.get(), inB.get(), inA.get()); + next.trigger(); + op.patch.cgl.gl.colorMask(true, true, true, true); +}; + + +}; + +Ops.Gl.ColorMask.prototype = new CABLES.Op(); +CABLES.OPS["44433419-dcf4-46e6-8f40-d331598029ac"]={f:Ops.Gl.ColorMask,objName:"Ops.Gl.ColorMask"}; + + + + +// ************************************************************** +// +// Ops.Gl.ColorPick +// +// ************************************************************** + +Ops.Gl.ColorPick = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + x = op.inValueFloat("x"), + y = op.inValueFloat("y"), + r = op.outNumber("r"), + g = op.outNumber("g"), + b = op.outNumber("b"), + a = op.outNumber("a"); + +const cgl = op.patch.cgl; +let pixelValues = new Uint8Array(4); +render.onTriggered = doRender; + +function doRender() +{ + cgl.gl.readPixels(x.get(), cgl.canvas.height - y.get(), 1, 1, cgl.gl.RGBA, cgl.gl.UNSIGNED_BYTE, pixelValues); + r.set(pixelValues[0] / 255); + g.set(pixelValues[1] / 255); + b.set(pixelValues[2] / 255); + a.set(pixelValues[3] / 255); +} + + +}; + +Ops.Gl.ColorPick.prototype = new CABLES.Op(); +CABLES.OPS["12de35ad-3f76-496c-9585-b77a87530de3"]={f:Ops.Gl.ColorPick,objName:"Ops.Gl.ColorPick"}; + + + + +// ************************************************************** +// +// Ops.Gl.CubeMap.CubeMapFromTextures_v2 +// +// ************************************************************** + +Ops.Gl.CubeMap.CubeMapFromTextures_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let loadingId = 0; +let skyboxCubemap = null; +const gl = op.patch.cgl.gl; +const cgl = op.patch.cgl; + + +const inFilenames = []; + +const titles = [ + "posx", "negx", + "posy", "negy", + "posz", "negz" +]; + +titles.forEach(function (title) +{ // create inlet ports + const inFilename = op.inUrl(title, "image"); + inFilename.onChange = loadImagesLater; // assign on change handlers + inFilenames.push(inFilename); +}); + +const inFlipY = op.inBool("Flip Y", false); +let outTex = op.outObject("cubemap"); +inFlipY.onChange = loadImagesLater; + +let timeoutLater = null; + +function loadImagesLater() +{ + clearTimeout(timeoutLater); + timeoutLater = setTimeout(loadImages, 100); +} + +function loadImage(src) +{ + return new Promise((resolve, reject) => + { + const image = new Image(); + image.crossOrigin = ""; + image.addEventListener("load", () => { return resolve(image); }); + image.addEventListener("error", (err) => { return reject(err); }); + image.src = src; + }); +} + +function loadImages() +{ + op.setUiError("loadingerror", null); + op.setUiError("loading", "Loading images...", 0); + + skyboxCubemap = null; + loadingId = cgl.patch.loading.start("cubemap texture", ""); + + const images = Promise.all( + inFilenames.map((inFile) => { return inFile.get(); }) // get file address + .filter(Boolean) // remove all 0's (empty file adresses) so we only resolve + .map((filename) => { return loadImage(filename); })) // map to resolver function + .then((images) => + { // wait for all images to be loaded and only then continue + if (images.length === 6) + { + cgl.gl.pixelStorei( + cgl.gl.UNPACK_FLIP_Y_WEBGL, + inFlipY.get() + ); + skyboxCubemap = cgl.gl.createTexture(cgl.gl.TEXTURE_CUBE_MAP); + cgl.gl.bindTexture(cgl.gl.TEXTURE_CUBE_MAP, skyboxCubemap); + + cgl.gl.texParameteri(cgl.gl.TEXTURE_CUBE_MAP, cgl.gl.TEXTURE_WRAP_S, cgl.gl.CLAMP_TO_EDGE); + cgl.gl.texParameteri(cgl.gl.TEXTURE_CUBE_MAP, cgl.gl.TEXTURE_WRAP_T, cgl.gl.CLAMP_TO_EDGE); + cgl.gl.texParameteri(cgl.gl.TEXTURE_CUBE_MAP, cgl.gl.TEXTURE_MIN_FILTER, cgl.gl.LINEAR_MIPMAP_LINEAR); + cgl.gl.texParameteri(cgl.gl.TEXTURE_CUBE_MAP, cgl.gl.TEXTURE_MAG_FILTER, cgl.gl.LINEAR); + + if (inFlipY.get()) + { + const temp = images[2]; + images[2] = images[3]; + images[3] = temp; + } + + images.forEach((img, index) => + { + cgl.gl.bindTexture( + cgl.gl.TEXTURE_CUBE_MAP, + skyboxCubemap + ); + + cgl.gl.texImage2D( + cgl.gl.TEXTURE_CUBE_MAP_POSITIVE_X + index, + 0, + cgl.gl.RGBA, + cgl.gl.RGBA, + cgl.gl.UNSIGNED_BYTE, + img + ); + }); + + cgl.gl.generateMipmap(cgl.gl.TEXTURE_CUBE_MAP); + + outTex.set({ "cubemap": skyboxCubemap }); + + cgl.gl.bindTexture(cgl.gl.TEXTURE_CUBE_MAP, null); + cgl.patch.loading.finished(loadingId); + + op.setUiError("loading", null); + } + }) + .catch((err) => + { + op.error("error", err); + op.setUiError("loadingerror", "Could not load textures!", 2); + }); +} + + +}; + +Ops.Gl.CubeMap.CubeMapFromTextures_v2.prototype = new CABLES.Op(); +CABLES.OPS["44e837c5-ecd8-42cf-9be6-4db2283b9cbf"]={f:Ops.Gl.CubeMap.CubeMapFromTextures_v2,objName:"Ops.Gl.CubeMap.CubeMapFromTextures_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.CubeMap.CubeMapMaterial_v2 +// +// ************************************************************** + +Ops.Gl.CubeMap.CubeMapMaterial_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"cubemap_frag":"{{MODULES_HEAD}}\n\n#define PI 3.14159265358\n#define PI_TWO 2.*PI\n#define RECIPROCAL_PI 1./PI\n#define RECIPROCAL_PI2 RECIPROCAL_PI/2.\n\nIN vec3 vCoords;\nIN vec3 v_normal;\nIN vec3 v_eyeCoords;\nIN vec3 v_pos;\nIN mat3 normalMatrix;\nIN vec3 texCoords;\nIN vec3 reflectionTexCoords;\nIN vec3 normInterpolated;\nIN vec3 fragPos;\n\nUNI vec3 camPos;\nUNI float inRotation;\nUNI vec3 inColor;\n\n\n#ifdef TEX_FORMAT_CUBEMAP\n UNI samplerCube skybox;\n #ifndef WEBGL1\n #define SAMPLETEX textureLod\n #endif\n #ifdef WEBGL1\n #define SAMPLETEX textureCubeLodEXT\n #endif\n#endif\n\n#ifndef TEX_FORMAT_CUBEMAP\n #define TEX_FORMAT_EQUIRECT\n UNI sampler2D skybox;\n #ifdef WEBGL1\n // #extension GL_EXT_shader_texture_lod : enable\n #ifdef GL_EXT_shader_texture_lod\n #define textureLod texture2DLodEXT\n #endif\n // #define textureLod texture2D\n #endif\n #define SAMPLETEX sampleEquirect\n\n#endif\n\n\n\nUNI mat4 modelMatrix;\nUNI mat4 inverseViewMatrix;\nUNI float miplevel;\n\n#ifdef TEX_FORMAT_EQUIRECT\n const vec2 invAtan = vec2(0.1591, 0.3183);\n vec4 sampleEquirect(sampler2D tex,vec3 direction,float lod)\n {\n #ifndef WEBGL1\n vec3 newDirection = normalize(direction);\n \t\tvec2 sampleUV;\n \t\tsampleUV.x = -1. * (atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.75);\n \t\tsampleUV.y = asin( clamp(direction.y, -1., 1.) ) * RECIPROCAL_PI + 0.5;\n #endif\n\n #ifdef WEBGL1\n vec3 newDirection = normalize(direction);\n \t\tvec2 sampleUV = vec2(atan(newDirection.z, newDirection.x), asin(newDirection.y+1e-6));\n sampleUV *= vec2(0.1591, 0.3183);\n sampleUV += 0.5;\n #endif\n return textureLod(tex, sampleUV, lod);\n }\n#endif\n\nvoid main()\n{\n float rot = inRotation * PI_TWO;\n float sa = sin(rot);\n float ca = cos(rot);\n mat2 matRotation = mat2(ca,sa,-sa,ca);\n\n {{MODULE_BEGIN_FRAG}}\n\n vec3 normal = normalize(normInterpolated);\n\n vec4 col = vec4(1.0,1.0,1.0,1.0);\n vec3 viewDirection = normalize((camPos - fragPos));\n\n #ifdef DO_REFLECTION\n vec3 envMapNormal = normal;\n vec3 reflectDirection = reflect(-viewDirection, normal);\n\n if (!gl_FrontFacing) {\n reflectDirection.yz *= -1.;\n } else {\n // reflectDirection.x *= -1.;\n }\n\n #ifdef FLIP_X\n reflectDirection.x *= -1.;\n #endif\n #ifdef FLIP_Y\n reflectDirection.y *= -1.;\n #endif\n #ifdef FLIP_Z\n reflectDirection.z *= -1.;\n #endif\n\n reflectDirection.xz *= matRotation;\n col = SAMPLETEX(skybox, reflectDirection,1. + miplevel*10.0);\n #endif\n\n #ifndef DO_REFLECTION\n if (!gl_FrontFacing) normal.x *= -1.;\n\n #ifdef FLIP_X\n normal.x *= -1.;\n #endif\n #ifdef FLIP_Y\n normal.y *= -1.;\n #endif\n #ifdef FLIP_Z\n normal.z *= -1.;\n #endif\n\n normal.xz *= matRotation;\n\n col = SAMPLETEX(skybox, normal, miplevel * 10.0);\n #endif\n\n #ifdef COLORIZE\n col.rgb *= inColor;\n #endif\n {{MODULE_COLOR}}\n\n outColor=col;\n}\n","cubemap_vert":"\n{{MODULES_HEAD}}\n\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\nOUT vec3 v_eyeCoords;\nOUT vec3 v_normal;\nOUT vec3 v_pos;\nOUT mat3 normalMatrix;\nOUT vec3 normInterpolated;\nOUT vec3 norm;\nOUT vec3 texCoords;\nOUT vec3 reflectionTexCoords;\nOUT vec3 fragPos;\nOUT vec4 modelPos;\nIN vec3 vPosition;\nIN vec3 attrVertNormal;\nIN vec3 attrTangent;\nIN vec3 attrBiTangent;\nIN vec2 attrTexCoord;\n\nOUT mat4 mvMatrix;\nOUT vec2 texCoord;\nmat3 transposeMat3(mat3 m)\n{\n return mat3(m[0][0], m[1][0], m[2][0],\n m[0][1], m[1][1], m[2][1],\n m[0][2], m[1][2], m[2][2]);\n}\n\nmat3 inverseMat3(mat3 m)\n{\n float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];\n float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];\n float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];\n\n float b01 = a22 * a11 - a12 * a21;\n float b11 = -a22 * a10 + a12 * a20;\n float b21 = a21 * a10 - a11 * a20;\n\n float det = a00 * b01 + a01 * b11 + a02 * b21;\n\n return mat3(b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11),\n b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10),\n b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) / det;\n}\n\nvoid main()\n{\n vec4 pos = vec4( vPosition, 1. );\n mat4 mMatrix=modelMatrix;\n\n\n norm=attrVertNormal;\n texCoord=attrTexCoord;\n vec3 tangent = attrTangent;\n vec3 bitangent = attrBiTangent;\n\n {{MODULE_VERTEX_POSITION}}\n\n mat3 mMatrixMat3 = mat3(mMatrix);\n normalMatrix = transposeMat3(inverseMat3(mMatrixMat3));\n normInterpolated = vec3(normalMatrix*norm);\n\n\n\n mvMatrix=viewMatrix*mMatrix;\n modelPos=mMatrix*pos;\n\n fragPos = vec3((mMatrix) * pos);\n gl_Position = projMatrix * mvMatrix * pos;\n\n}",}; +// https://jmonkeyengine.github.io/wiki/jme3/advanced/pbr_part3.html +// https://learnopengl.com/PBR/IBL/Diffuse-irradiance + +const render = op.inTrigger("render"); +const inCubemap = op.inObject("Cubemap"); +const inUseReflection = op.inValueBool("Use Reflection", false); +const inMiplevel = op.inValueSlider("Blur", 0.0); +op.setPortGroup("Appearance", [inMiplevel, inUseReflection]); +const inRotation = op.inFloat("Rotation", 0); +const inFlipX = op.inBool("Flip X", false); +const inFlipY = op.inBool("Flip Y", false); +const inFlipZ = op.inBool("Flip Z", false); + +op.setPortGroup("Transforms", [inRotation, inFlipX, inFlipY, inFlipZ]); +const inColorize = op.inBool("Colorize", false); +const inR = op.inFloatSlider("R", Math.random()); +const inG = op.inFloatSlider("G", Math.random()); +const inB = op.inFloatSlider("B", Math.random()); +inR.setUiAttribs({ "colorPick": true }); + +op.setPortGroup("Color", [inColorize, inR, inG, inB]); + +inUseReflection.onChange = inCubemap.onChange = +inFlipX.onChange = inFlipY.onChange = inFlipZ.onChange = updateMapping; +inColorize.onChange = function () +{ + shader.toggleDefine("COLORIZE", inColorize.get()); + inR.setUiAttribs({ "greyout": !inColorize.get() }); + inG.setUiAttribs({ "greyout": !inColorize.get() }); + inB.setUiAttribs({ "greyout": !inColorize.get() }); +}; +const trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const srcVert = attachments.cubemap_vert; +const srcFrag = attachments.cubemap_frag; + +const shader = new CGL.Shader(cgl, "cubemap material"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); + +if (cgl.glVersion == 1) +{ + if (!cgl.gl.getExtension("EXT_shader_texture_lod")) + { + op.log("no EXT_shader_texture_lod texture extension"); + throw "no EXT_shader_texture_lod texture extension"; + } + else + { + shader.enableExtension("GL_EXT_shader_texture_lod"); + cgl.gl.getExtension("OES_texture_float"); + cgl.gl.getExtension("OES_texture_float_linear"); + cgl.gl.getExtension("OES_texture_half_float"); + cgl.gl.getExtension("OES_texture_half_float_linear"); + + shader.enableExtension("GL_OES_standard_derivatives"); + shader.enableExtension("GL_OES_texture_float"); + shader.enableExtension("GL_OES_texture_float_linear"); + shader.enableExtension("GL_OES_texture_half_float"); + shader.enableExtension("GL_OES_texture_half_float_linear"); + } +} + +shader.setSource(srcVert, srcFrag); +const inMiplevelUniform = new CGL.Uniform(shader, "f", "miplevel", inMiplevel); +const inRotationUniform = new CGL.Uniform(shader, "f", "inRotation", inRotation); +const inColorUniform = new CGL.Uniform(shader, "3f", "inColor", inR, inG, inB); +const inSkyboxUniform = new CGL.Uniform(shader, "t", "skybox", 0); +render.onTriggered = doRender; +updateMapping(); + +function doRender() +{ + cgl.pushShader(shader); + shader.popTextures(); + if (inCubemap.get()) + { + if (inCubemap.get().cubemap) shader.pushTexture(inSkyboxUniform, inCubemap.get().cubemap, cgl.gl.TEXTURE_CUBE_MAP); + else shader.pushTexture(inSkyboxUniform, inCubemap.get().tex); + + if (inMiplevel.get() && inCubemap.get().filter != CGL.Texture.FILTER_MIPMAP) op.setUiError("texnomip", "blur needs to be mipmaped texture", 1); + else op.setUiError("texnomip", null); + } + else shader.pushTexture(inSkyboxUniform, CGL.Texture.getTempTexture(cgl).tex); + + trigger.trigger(); + cgl.popShader(); +} + +function updateMapping() +{ + shader.toggleDefine("FLIP_X", inFlipX.get()); + shader.toggleDefine("FLIP_Y", inFlipY.get()); + shader.toggleDefine("FLIP_Z", inFlipZ.get()); + shader.toggleDefine("DO_REFLECTION", inUseReflection.get()); + + if (inCubemap.get() && inCubemap.get().cubemap) + { + shader.define("TEX_FORMAT_CUBEMAP"); + shader.removeDefine("TEX_FORMAT_EQUIRECT"); + } + else + { + shader.removeDefine("TEX_FORMAT_CUBEMAP"); + shader.define("TEX_FORMAT_EQUIRECT"); + } +} + + +}; + +Ops.Gl.CubeMap.CubeMapMaterial_v2.prototype = new CABLES.Op(); +CABLES.OPS["d1fce807-a626-433f-ba61-3f59149fe46e"]={f:Ops.Gl.CubeMap.CubeMapMaterial_v2,objName:"Ops.Gl.CubeMap.CubeMapMaterial_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.CubeMap.CubemapToEquirectangularTexture_v2 +// +// ************************************************************** + +Ops.Gl.CubeMap.CubemapToEquirectangularTexture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"projection_frag":"IN vec2 texCoord;\nUNI samplerCube cubemap;\n\n#ifdef WEBGL2\n #define textureCube texture\n#endif\n\nvec4 encodeRGBE8( vec3 rgb )\n{\n vec4 vEncoded;\n float maxComponent = max(max(rgb.r, rgb.g), rgb.b );\n float fExp = ceil( log2(maxComponent) );\n vEncoded.rgb = rgb / exp2(fExp);\n vEncoded.a = (fExp + 128.0) / 255.0;\n return vEncoded;\n}\n\nvoid main() {\n #ifdef EQUIRECTANGULAR\n\n float phi=texCoord.s*3.1415*2.;\n float theta=(-texCoord.t - 0.5)*3.1415;\n vec3 dir = vec3(cos(phi)*cos(theta),sin(theta),sin(phi)*cos(theta));\n\n //In this example i use a depthmap with only 1 channel, but the projection should work with a colored cubemap to\n vec4 col = textureCube(cubemap, dir ).rgba;\n\n #ifdef RGBE\n col=encodeRGBE8(col.rgb);\n #endif\n\n outColor = col;\n\n #endif\n #ifndef EQUIRECTANGULAR\n vec4 col=vec4(0.);\n vec2 localST=texCoord;\n localST.y = 1. - localST.y;\n\n //Scale Tex coordinates such that each quad has local coordinates from 0,0 to 1,1\n localST.t = mod(localST.t*3.,1.);\n localST.s = mod(localST.s*4.,1.);\n\n //Due to the way my depth-cubemap is rendered, objects to the -x,y,z side is projected to the positive x,y,z side\n //Inside where tob/bottom is to be drawn?\n if (texCoord.s*4.> 1. && texCoord.s*4.<2.)\n {\n //Bottom (-y) quad\n if (texCoord.t*3. < 1.)\n {\n vec3 dir=vec3(localST.s*2.-1.,-1.,-localST.t*2.+1.);//Due to the (arbitrary) way I choose as up in my depth-viewmatrix, i her emultiply the latter coordinate with -1\n\n #ifdef WEBGL2\n col = textureCube( cubemap, dir );\n #endif\n\n #ifdef WEBGL1\n col = textureCube(cubemap, dir);\n #endif\n }\n //top (+y) quad\n else if (texCoord.t*3. > 2.)\n {\n vec3 dir=vec3(localST.s*2.-1.,1.,localST.t*2.-1.);//Get lower y texture, which is projected to the +y part of my cubemap\n #ifdef WEBGL2\n col = textureCube( cubemap, dir );\n #endif\n\n #ifdef WEBGL1\n col = textureCube(cubemap, dir);\n #endif\n }\n else//Front (-z) quad\n {\n vec3 dir=vec3(localST.s*2.-1.,-localST.t*2.+1.,1.);\n col = textureCube( cubemap, dir );\n }\n }\n //If not, only these ranges should be drawn\n else if (texCoord.t*3. > 1. && texCoord.t*3. < 2.)\n {\n if (texCoord.x*4. < 1.)//left (-x) quad\n {\n vec3 dir=vec3(-1.,-localST.t*2.+1.,localST.s*2.-1.);\n col = textureCube( cubemap, dir );\n }\n else if (texCoord.x*4. < 3.)//right (+x) quad (front was done above)\n {\n vec3 dir=vec3(1,-localST.t*2.+1.,-localST.s*2.+1.);\n col = textureCube(cubemap, dir);\n }\n else //back (+z) quad\n {\n vec3 dir=vec3(-localST.s*2.+1.,-localST.t*2.+1.,-1.);\n col = textureCube(cubemap, dir);\n }\n }\n else//Tob/bottom, but outside where we need to put something\n {\n discard;//No need to add fancy semi transparant borders for quads, this is just for debugging purpose after all\n }\n\n col.a=1.0;\n #ifdef RGBE\n col=encodeRGBE8(col.rgb);\n #endif\n outColor = col;\n\n\n #endif\n}","projection_vert":"{{MODULES_HEAD}}\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\nIN float attrVertIndex;\n\nOUT vec2 texCoord;\nOUT vec3 norm;\nOUT vec3 worldPos;\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\nconst vec2 scale = vec2(0.5, 0.5);\n\nvoid main()\n{\n texCoord=attrTexCoord;\n norm=attrVertNormal;\n vec4 pos=vec4(vPosition, 1.0);\n\n {{MODULE_VERTEX_POSITION}}\n\n gl_Position = pos;\n}",}; +const + inTrigger = op.inTrigger("In Trigger"), + inCubemap = op.inObject("Cubemap"), + inProj = op.inSwitch("Projection", ["Equirectangular", "Cube unwrap"], "Equirectangular"), + inFormat = op.inSwitch("Format", ["8bit", "32bit", "RGBE"], "8bit"), + tfilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"], "linear"), + inWidth = op.inInt("Width", 1024), + inHeight = op.inInt("Height", 512), + outTrigger = op.outTrigger("Out Trigger"), + outProjection = op.outTexture("Result"); + +op.setPortGroup("Options", [inWidth, inHeight, inProj, inFormat, tfilter]); +inProj.onChange = updateDefines; + +const cgl = op.patch.cgl; +const mesh = CGL.MESHES.getSimpleRect(cgl, "fullscreenRectangle"); +const IS_WEBGL_1 = cgl.glVersion == 1; +let sizeChanged = false; +let needsUpdate = true; +let fb = null; +let cgl_filter = CGL.Texture.FILTER_LINEAR; +const projectionShader = new CGL.Shader(cgl, "cubemapProjection"); +projectionShader.offScreenPass = true; +const uniformCubemap = new CGL.Uniform(projectionShader, "t", "cubemap", 0); + +projectionShader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +projectionShader.setSource(attachments.projection_vert, attachments.projection_frag); + +tfilter.onChange = createFb; +inFormat.onChange = createFb; +updateDefines(); +createFb(); + +function createFb() +{ + if (fb)fb.dispose(); + + cgl_filter = CGL.Texture.FILTER_LINEAR; + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + + if (IS_WEBGL_1) + { + fb = new CGL.Framebuffer(cgl, inWidth.get(), inHeight.get(), { + "isFloatingPointTexture": inFormat.get() == "32bit", + "filter": cgl_filter, + "wrap": CGL.Texture.WRAP_REPEAT + }); + } + else + { + fb = new CGL.Framebuffer2(cgl, inWidth.get(), inHeight.get(), { + "isFloatingPointTexture": inFormat.get() == "32bit", + "filter": cgl_filter, + "wrap": CGL.Texture.WRAP_REPEAT, + }); + } + updateDefines(); + needsUpdate = true; +} + +inWidth.onChange = inHeight.onChange = () => +{ + sizeChanged = true; +}; + +inCubemap.onChange = () => +{ + needsUpdate = true; +}; + +function updateDefines() +{ + projectionShader.toggleDefine("EQUIRECTANGULAR", inProj.get() == "Equirectangular"); + projectionShader.toggleDefine("RGBE", inFormat.get() == "RGBE"); + needsUpdate = true; +} + +inTrigger.onTriggered = function () +{ + if (!inCubemap.get()) + { + outTrigger.trigger(); + return; + } + + if (sizeChanged) + { + if (fb) fb.setSize(inWidth.get(), inHeight.get()); + sizeChanged = false; + needsUpdate = true; + } + + if (needsUpdate) + { + projectionShader.popTextures(); + + fb.renderStart(cgl); + projectionShader.pushTexture(uniformCubemap, inCubemap.get().cubemap, cgl.gl.TEXTURE_CUBE_MAP); + mesh.render(projectionShader); + fb.renderEnd(); + + outProjection.set(CGL.Texture.getEmptyTexture(cgl)); + outProjection.set(fb.getTextureColor()); + needsUpdate = false; + } + outTrigger.trigger(); +}; + + +}; + +Ops.Gl.CubeMap.CubemapToEquirectangularTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["5e5fa774-ecb1-44dd-8c47-ce4a592f68f1"]={f:Ops.Gl.CubeMap.CubemapToEquirectangularTexture_v2,objName:"Ops.Gl.CubeMap.CubemapToEquirectangularTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.CubeMap.EquirectangularTextureToCubemap +// +// ************************************************************** + +Ops.Gl.CubeMap.EquirectangularTextureToCubemap = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"equirect_to_cube_frag":"#define PI 3.14159265358 //97932384626433832795\n#define PI_TWO 2. * PI\n#define RECIPROCAL_PI 1./PI\n#define RECIPROCAL_PI2 RECIPROCAL_PI/2.\n\nUNI sampler2D equirectangularMap;\nIN vec3 worldPos;\n\nvec4 sampleEquirect(sampler2D tex, vec3 direction) {\nvec2 sampleUV;\nvec3 newDirection = normalize(direction);\n\nsampleUV.x = -1. * (atan( newDirection.z, newDirection.x ) * RECIPROCAL_PI2 + 0.75);\n sampleUV.y = asin( clamp(newDirection.y, -1., 1.) ) * RECIPROCAL_PI + 0.5;\n\n return texture(tex, sampleUV);\n}\n\nvoid main() {\n {{MODULE_BEGIN_FRAG}}\n vec4 col = vec4(1.);\n\n {{MODULE_COLOR}}\n\n outColor = vec4(1., 0., 0., 1.);\n vec3 newPos = worldPos;\n outColor = vec4(sampleEquirect(equirectangularMap, newPos));\n}\n\n","equirect_to_cube_vert":"{{MODULES_HEAD}}\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\nIN float attrVertIndex;\n\nOUT vec2 texCoord;\nOUT vec3 norm;\nOUT vec3 worldPos;\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\nvoid main()\n{\n texCoord=attrTexCoord;\n norm=attrVertNormal;\n vec4 pos=vec4(vPosition, 1.0);\n\n {{MODULE_VERTEX_POSITION}}\n\n mat4 mMatrix=modelMatrix;\n worldPos = vec3(mMatrix * pos);\n mat4 rotView = mat4(mat3(viewMatrix)); // remove translation from the view matrix\n vec4 clipPos = projMatrix * rotView * pos;\n\n gl_Position = clipPos.xyww;\n}",}; +const cgl = op.patch.cgl; +const geometry = new CGL.Geometry("unit cube"); + +const inTrigger = op.inTrigger("Trigger In"); +const inTexture = op.inTexture("Equirectangular Map"); + +const inSize = op.inDropDown("Cubemap Size", [32, 64, 128, 256, 512, 1024, 2048], 512); +const inAdvanced = op.inBool("Advanced", false); +const inTextureFilter = op.inSwitch("Filter", ["Nearest", "Linear"], "Linear"); +op.setPortGroup("Cubemap Options", [inSize, inAdvanced, inTextureFilter]); +const outTrigger = op.outTrigger("Trigger Out"); +const outCubemap = op.outTexture("Cubemap Projection"); + +inTextureFilter.setUiAttribs({ "greyout": !inAdvanced.get() }); + +inAdvanced.onChange = () => { return inTextureFilter.setUiAttribs({ "greyout": !inAdvanced.get() }); }; +geometry.vertices = new Float32Array([ + // * NOTE: tex coords not needed for cubemapping + -1.0, 1.0, -1.0, + -1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + 1.0, 1.0, -1.0, + -1.0, 1.0, -1.0, + + -1.0, -1.0, 1.0, + -1.0, -1.0, -1.0, + -1.0, 1.0, -1.0, + -1.0, 1.0, -1.0, + -1.0, 1.0, 1.0, + -1.0, -1.0, 1.0, + + 1.0, -1.0, -1.0, + 1.0, -1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, -1.0, + 1.0, -1.0, -1.0, + + -1.0, -1.0, 1.0, + -1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, + -1.0, -1.0, 1.0, + + -1.0, 1.0, -1.0, + 1.0, 1.0, -1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, + -1.0, 1.0, -1.0, + + -1.0, -1.0, -1.0, + -1.0, -1.0, 1.0, + 1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + -1.0, -1.0, 1.0, + 1.0, -1.0, 1.0 +]); +const mesh = new CGL.Mesh(cgl, geometry); + +// * FRAMEBUFFER * +let fb = null; +const IS_WEBGL_1 = cgl.glVersion == 1; + +let cubemap = null; + +const equirectToCubeEffect = new CGL.TextureEffect(cgl, { "isFloatingPointTexture": true }); +const equiToCubeShader = new CGL.Shader(cgl, "equirectToCube"); +const uniformEquirectangularMap = new CGL.Uniform(equiToCubeShader, "t", "equirectangularMap", 0); +equiToCubeShader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +equiToCubeShader.setSource(attachments.equirect_to_cube_vert, attachments.equirect_to_cube_frag); +equiToCubeShader.offScreenPass = true; + +inTexture.onChange = inSize.onChange = inTextureFilter.onChange = resetCubemap; + +let reinitCubemap = true; +function resetCubemap() +{ + reinitCubemap = true; +} + +function createCubemap() +{ + if (!inTexture.get()) + { + return; + } + + if (cubemap) cubemap.dispose(); + cubemap = null; + + if (fb) fb.delete(); + + const textureOptions = { + "isFloatingPointTexture": true, + "filter": CGL.Texture.FILTER_LINEAR, + "wrap": CGL.Texture.WRAP_CLAMP_TO_EDGE, + }; + + if (inAdvanced.get()) + { + textureOptions.filter = CGL.Texture["FILTER_" + inTextureFilter.get().toUpperCase()]; + } + if (IS_WEBGL_1) + { + fb = new CGL.Framebuffer(cgl, Number(inSize.get()), Number(inSize.get()), textureOptions); + } + else + { + fb = new CGL.Framebuffer2(cgl, Number(inSize.get()), Number(inSize.get()), textureOptions); + } + + cubemap = new CGL.CubemapFramebuffer(cgl, Number(inSize.get()), Number(inSize.get()), {}); + reinitCubemap = false; +} + +inTrigger.onTriggered = function () +{ + if (!inTexture.get()) + { + // outCubemap.set(null); + outTrigger.trigger(); + return; + } + + if (reinitCubemap) + { + createCubemap(); + } + + equiToCubeShader.popTextures(); + + cgl.frameStore.renderOffscreen = true; + + if (inTexture.get() && inTexture.get().tex) + { + // fb.renderStart(cgl); + + equiToCubeShader.pushTexture(uniformEquirectangularMap, inTexture.get().tex); + + cgl.pushShader(equiToCubeShader); + + cubemap.renderStart(); + + for (let i = 0; i < 6; i += 1) + { + cubemap.renderStartCubemapFace(i); + mesh.render(equiToCubeShader); + cubemap.renderEndCubemapFace(); + } + + cubemap.renderEnd(); + + cgl.popShader(); + + // fb.renderEnd(); + cgl.frameStore.renderOffscreen = false; + + outCubemap.set(null); + outCubemap.set(cubemap.getTextureColor()); + } + + outTrigger.trigger(); +}; + + +}; + +Ops.Gl.CubeMap.EquirectangularTextureToCubemap.prototype = new CABLES.Op(); +CABLES.OPS["e4cbae17-10bf-44cd-a4e8-4b16dd849d0c"]={f:Ops.Gl.CubeMap.EquirectangularTextureToCubemap,objName:"Ops.Gl.CubeMap.EquirectangularTextureToCubemap"}; + + + + +// ************************************************************** +// +// Ops.Gl.CubeMap.RenderToCubemap_v2 +// +// ************************************************************** + +Ops.Gl.CubeMap.RenderToCubemap_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTrigger = op.inTrigger("Render"), + inFp = op.inBool("32bit float tex", false), + outTrigger = op.outTrigger("Next"), + outTex = op.outTexture("cubemap"); + +const cgl = op.patch.cgl; + +const inSize = op.inDropDown("Size", [32, 64, 128, 256, 512, 1024, 2048], 512); +let sizeChanged = true; +inSize.onChange = () => { return sizeChanged = true; }; + +let fb = null; + +inFp.onChange = createFb; + +let emptyCubemap = null; + +createFb(); + +function createFb() +{ + if (fb)fb.delete(); + fb = new CGL.CubemapFramebuffer( + cgl, + Number(inSize.get()), + Number(inSize.get()), + { + "isFloatingPointTexture": inFp.get() + }); +} + +inTrigger.onTriggered = function () +{ + if (sizeChanged) + { + if (fb) fb.setSize(Number(inSize.get()), Number(inSize.get())); + sizeChanged = false; + } + + if (fb) + { + fb.renderStart(); + for (let i = 0; i < 6; i += 1) + { + fb.renderStartCubemapFace(i); + outTrigger.trigger(); + fb.renderEndCubemapFace(); + } + fb.renderEnd(); + if (!emptyCubemap)emptyCubemap = CGL.Texture.getEmptyCubemapTexture(cgl); + outTex.set(emptyCubemap); + outTex.set(fb.getTextureColor()); + } + else outTrigger.trigger(); +}; + + +}; + +Ops.Gl.CubeMap.RenderToCubemap_v2.prototype = new CABLES.Op(); +CABLES.OPS["2db05f08-ea89-4e56-bba9-8668d963a461"]={f:Ops.Gl.CubeMap.RenderToCubemap_v2,objName:"Ops.Gl.CubeMap.RenderToCubemap_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.CubeMap.Skybox +// +// ************************************************************** + +Ops.Gl.CubeMap.Skybox = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"skybox_frag":"#define PI 3.14159265358 //97932384626433832795\n#define PI_TWO 2. * PI\n#define RECIPROCAL_PI 1./PI\n#define RECIPROCAL_PI2 RECIPROCAL_PI/2.\n\nUNI vec2 expGamma;\n\n\n#ifdef TEX_FORMAT_CUBEMAP\n UNI samplerCube skybox;\n #ifndef WEBGL1\n #define SAMPLETEX texture\n #endif\n #ifdef WEBGL1\n #define SAMPLETEX textureCubeLodEXT\n #endif\n#endif\n\n#ifndef TEX_FORMAT_CUBEMAP\n #define TEX_FORMAT_EQUIRECT\n UNI sampler2D skybox;\n #ifdef WEBGL1\n #ifdef GL_EXT_shader_texture_lod\n #define textureLod texture2DLodEXT\n #endif\n #endif\n #define SAMPLETEX sampleEquirect\n\n#endif\n\nIN vec3 worldPos;\n\nvec4 sampleEquirect(sampler2D tex, vec3 direction) {\n vec2 sampleUV;\n vec3 newDirection = normalize(direction);\n\n sampleUV.x = atan( newDirection.z, newDirection.x ) * RECIPROCAL_PI2 + 0.75;\n sampleUV.y = asin( clamp(newDirection.y, -1., 1.) ) * RECIPROCAL_PI + 0.5;\n\n return texture(tex, sampleUV);\n}\n\nhighp vec3 DecodeRGBE8(highp vec4 rgbe)\n{\n highp vec3 vDecoded = rgbe.rgb * pow(2.0, rgbe.a * 255.0-128.0);\n return vDecoded;\n}\n\nvoid main() {\n {{MODULE_BEGIN_FRAG}}\n vec4 col = vec4(1.);\n\n {{MODULE_COLOR}}\n\n vec3 newPos = worldPos;\n\n vec4 finalColor;\n #ifndef RGBE\n finalColor = vec4(SAMPLETEX(skybox, newPos));\n #endif\n\n #ifdef RGBE\n finalColor.rgb=DecodeRGBE8(SAMPLETEX(skybox, newPos));\n #endif\n\n float gamma=expGamma.x;\n float exposure=expGamma.y;\n finalColor.rgb = vec3(1.0) - exp(-finalColor.rgb * exposure);\n\n finalColor.rgb = pow(finalColor.rgb, vec3(1.0 / gamma));\n outColor=vec4(finalColor.rgb,1.0);\n\n}\n","skybox_vert":"{{MODULES_HEAD}}\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\nIN float attrVertIndex;\n\nOUT vec2 texCoord;\nOUT vec3 norm;\nOUT vec3 worldPos;\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\nvoid main()\n{\n texCoord=attrTexCoord;\n norm=attrVertNormal;\n vec4 pos=vec4(vPosition, 1.0);\n\n {{MODULE_VERTEX_POSITION}}\n\n mat4 mMatrix=modelMatrix;\n worldPos = vec3(mMatrix * pos);\n mat4 rotView = mat4(mat3(viewMatrix)); // remove translation from the view matrix\n vec4 clipPos = projMatrix * rotView * pos;\n\n gl_Position = clipPos.xyww;\n}",}; +const + cgl = op.patch.cgl, + inTrigger = op.inTrigger("Trigger In"), + inRender = op.inBool("Render", true), + inTexture = op.inTexture("Skybox"), + inRot = op.inFloat("Rotate", 0), + + inRGBE = op.inBool("RGBE Format", false), + inExposure = op.inFloat("Exposure", 1), + inGamma = op.inFloat("Gamma", 2.2), + + outTrigger = op.outTrigger("Trigger Out"); + +const geometry = new CGL.Geometry("unit cube"); + +geometry.vertices = new Float32Array([ + // * NOTE: tex coords not needed for cubemapping + -1.0, 1.0, -1.0, + -1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + 1.0, 1.0, -1.0, + -1.0, 1.0, -1.0, + + -1.0, -1.0, 1.0, + -1.0, -1.0, -1.0, + -1.0, 1.0, -1.0, + -1.0, 1.0, -1.0, + -1.0, 1.0, 1.0, + -1.0, -1.0, 1.0, + + 1.0, -1.0, -1.0, + 1.0, -1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, -1.0, + 1.0, -1.0, -1.0, + + -1.0, -1.0, 1.0, + -1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, + -1.0, -1.0, 1.0, + + -1.0, 1.0, -1.0, + 1.0, 1.0, -1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, + -1.0, 1.0, -1.0, + + -1.0, -1.0, -1.0, + -1.0, -1.0, 1.0, + 1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + -1.0, -1.0, 1.0, + 1.0, -1.0, 1.0 +]); + +const mesh = new CGL.Mesh(cgl, geometry); +const skyboxShader = new CGL.Shader(cgl, "skybox"); +const uniformSkybox = new CGL.Uniform(skyboxShader, "t", "skybox", 0); +const uniExposure = new CGL.Uniform(skyboxShader, "2f", "expGamma", inExposure, inGamma); + +skyboxShader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +skyboxShader.setSource(attachments.skybox_vert, attachments.skybox_frag); +skyboxShader.offScreenPass = true; + +inRGBE.onChange = () => +{ + skyboxShader.toggleDefine("RGBE", inRGBE.get()); +}; + +inTexture.onChange = () => +{ + const b = inTexture.get() && inTexture.get().cubemap; + + skyboxShader.toggleDefine("TEX_FORMAT_CUBEMAP", b); + skyboxShader.toggleDefine("TEX_FORMAT_EQUIRECT", !b); +}; + +inTrigger.onTriggered = () => +{ + if (!inTexture.get() || !inRender.get()) + { + outTrigger.trigger(); + return; + } + + skyboxShader.popTextures(); + + cgl.pushModelMatrix(); + + if (!inTexture.get().cubemap && inTexture.get().filter !== CGL.Texture.FILTER_LINEAR) + op.setUiError("linearFilter", "If there is a seam in the skybox, try changing the texture filter to linear!", 0); + else + op.setUiError("linearFilter", null); + + mat4.rotateY(cgl.mMatrix, cgl.mMatrix, inRot.get() * CGL.DEG2RAD); + + if (inTexture.get().tex) + skyboxShader.pushTexture(uniformSkybox, inTexture.get().tex); + else if (inTexture.get().cubemap) + skyboxShader.pushTexture(uniformSkybox, inTexture.get().cubemap, cgl.gl.TEXTURE_CUBE_MAP); + + mesh.render(skyboxShader); + + cgl.popModelMatrix(); + cgl.popDepthFunc(); + + outTrigger.trigger(); +}; + + +}; + +Ops.Gl.CubeMap.Skybox.prototype = new CABLES.Op(); +CABLES.OPS["97ce1d35-bd7a-43cb-a2bf-5b7e37fb8925"]={f:Ops.Gl.CubeMap.Skybox,objName:"Ops.Gl.CubeMap.Skybox"}; + + + + +// ************************************************************** +// +// Ops.Gl.DepthTest +// +// ************************************************************** + +Ops.Gl.DepthTest = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// todo:rename to depthtest + +const render = op.inTrigger("Render"); +const enable = op.inValueBool("Enable depth testing", true); +const meth = op.inValueSelect("Depth Test Method", ["never", "always", "less", "less or equal", "greater", "greater or equal", "equal", "not equal"], "less or equal"); +const write = op.inValueBool("Write to depth buffer", true); +const trigger = op.outTrigger("Next"); + +const cgl = op.patch.cgl; +let compareMethod = cgl.gl.LEQUAL; + +meth.onChange = updateFunc; + +function updateFunc() +{ + if (meth.get() == "never") compareMethod = cgl.gl.NEVER; + else if (meth.get() == "always") compareMethod = cgl.gl.ALWAYS; + else if (meth.get() == "less") compareMethod = cgl.gl.LESS; + else if (meth.get() == "less or equal") compareMethod = cgl.gl.LEQUAL; + else if (meth.get() == "greater") compareMethod = cgl.gl.GREATER; + else if (meth.get() == "greater or equal") compareMethod = cgl.gl.GEQUAL; + else if (meth.get() == "equal") compareMethod = cgl.gl.EQUAL; + else if (meth.get() == "not equal") compareMethod = cgl.gl.NOTEQUAL; +} + +render.onTriggered = function () +{ + cgl.pushDepthTest(enable.get()); + cgl.pushDepthWrite(write.get()); + cgl.pushDepthFunc(compareMethod); + + trigger.trigger(); + + cgl.popDepthTest(); + cgl.popDepthWrite(); + cgl.popDepthFunc(); +}; + + +}; + +Ops.Gl.DepthTest.prototype = new CABLES.Op(); +CABLES.OPS["3996ed5d-8143-4bec-9cfd-c1b193a295af"]={f:Ops.Gl.DepthTest,objName:"Ops.Gl.DepthTest"}; + + + + +// ************************************************************** +// +// Ops.Gl.DirectionalTranslate +// +// ************************************************************** + +Ops.Gl.DirectionalTranslate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Exec"), + inMatrix = op.inArray("Center Model Matrix"), + inAmount = op.inValue("Amount"), + next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; + +let va = vec3.create(); +let vb = vec3.create(); +let diff = vec3.create(); +let mm = mat3.create(); + +exec.onTriggered = function () +{ + let matbefore = inMatrix.get(); + if (!matbefore) return; + + mat4.getTranslation(va, matbefore); + mat4.getTranslation(vb, cgl.modelMatrix()); + + mat3.fromMat4(mm, cgl.modelMatrix()); + mat3.invert(mm, mm); + + vec3.sub(diff, vb, va); + vec3.normalize(diff, diff); + vec3.transformMat3(diff, diff, mm); + vec3.normalize(diff, diff); + + diff[0] *= inAmount.get(); + diff[1] *= inAmount.get(); + diff[2] *= inAmount.get(); + + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, diff); + next.trigger(); + cgl.popModelMatrix(); +}; + + +}; + +Ops.Gl.DirectionalTranslate.prototype = new CABLES.Op(); +CABLES.OPS["d0f3dbca-7457-44b5-987a-9d49e5310d98"]={f:Ops.Gl.DirectionalTranslate,objName:"Ops.Gl.DirectionalTranslate"}; + + + + +// ************************************************************** +// +// Ops.Gl.DownloadTexture_v2 +// +// ************************************************************** + +Ops.Gl.DownloadTexture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTex = op.inTexture("Texture"), + start = op.inTriggerButton("Download"), + fileName = op.inString("Filename", "screenshot"), + outFinished = op.outBoolNum("Finished"); + +const gl = op.patch.cgl.gl; +let fb = null; + +start.onTriggered = function () +{ + if (!inTex.get() || !inTex.get().tex) return; + outFinished.set(false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + + const width = inTex.get().width; + const height = inTex.get().height; + + if (inTex.get().textureType == CGL.Texture.TYPE_FLOAT) op.setUiError("fptex", "Texture is more than 8 bit, not possible to create files with high precision"); + else op.setUiError("fptex", null); + + if (!fb)fb = gl.createFramebuffer(); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, inTex.get().tex, 0); + + const canRead = (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + if (!canRead) + { + outFinished.set(true); + op.logError("cannot read texture!"); + return; + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + const data = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // Create a 2D canvas to store the result + const canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + const context = canvas.getContext("2d"); + + // Copy the pixels to a 2D canvas + const imageData = context.createImageData(width, height); + imageData.data.set(data); + + const data2 = imageData.data; + + // flip image + Array.from({ "length": height }, (val, i) => { return data2.slice(i * width * 4, (i + 1) * width * 4); }) + .forEach((val, i) => { return data2.set(val, (height - i - 1) * width * 4); }); + + context.putImageData(imageData, 0, 0); + + dataURIToBlob(canvas.toDataURL(), + function (blob) + { + const userAgent = navigator.userAgent || navigator.vendor || window.opera; + + if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) + { + const reader = new FileReader(); + // var out = new Blob([byte], {type: "application/pdf"}); + reader.onload = function (e) + { + window.location.href = reader.result; + // window.open(reader.result); + }; + reader.readAsDataURL(blob); + } + else + { + const anchor = document.createElement("a"); + anchor.download = fileName.get() + ".png"; + // anchor.target='_blank'; + anchor.href = URL.createObjectURL(blob); + document.body.appendChild(anchor); + anchor.click(); + } + outFinished.set(true); + }); +}; + +function dataURIToBlob(dataURI, callback) +{ + const binStr = atob(dataURI.split(",")[1]), + len = binStr.length, + arr = new Uint8Array(len); + for (let i = 0; i < len; i++) arr[i] = binStr.charCodeAt(i); + callback(new Blob([arr], { "type": "image/png" })); +} + + +}; + +Ops.Gl.DownloadTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["00d2a6ea-5843-43d0-9428-dbc47c112e6e"]={f:Ops.Gl.DownloadTexture_v2,objName:"Ops.Gl.DownloadTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.DrawTextureMapping +// +// ************************************************************** + +Ops.Gl.DrawTextureMapping = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + inGeom = op.inObject("Geometry", null, "geometry"), + numPoints = op.inValue("Num Points"), + next = op.outTrigger("Next"); + +let cgl = op.patch.cgl; + +let mesh = null; +let attr = null; +let points = []; + +inGeom.onChange = function () +{ + let geom = inGeom.get(); + + if (!geom || !geom.verticesIndices) return; + + points.length = 0; + let newgeom = new CGL.Geometry("texturemapping"); + + for (let i = 0; i < geom.verticesIndices.length; i += 3) + { + var index; + + index = geom.verticesIndices[i + 0]; + points.push(geom.texCoords[index * 2 + 0]); + points.push(geom.texCoords[index * 2 + 1]); + points.push(0); + + index = geom.verticesIndices[i + 1]; + points.push(geom.texCoords[index * 2 + 0]); + points.push(geom.texCoords[index * 2 + 1]); + points.push(0); + + index = geom.verticesIndices[i + 1]; + points.push(geom.texCoords[index * 2 + 0]); + points.push(geom.texCoords[index * 2 + 1]); + points.push(0); + + index = geom.verticesIndices[i + 2]; + points.push(geom.texCoords[index * 2 + 0]); + points.push(geom.texCoords[index * 2 + 1]); + points.push(0); + + index = geom.verticesIndices[i + 2]; + points.push(geom.texCoords[index * 2 + 0]); + points.push(geom.texCoords[index * 2 + 1]); + points.push(0); + + index = geom.verticesIndices[i + 0]; + points.push(geom.texCoords[index * 2 + 0]); + points.push(geom.texCoords[index * 2 + 1]); + points.push(0); + } + + newgeom.vertices = points; + + // if(!mesh) + mesh = new CGL.Mesh(cgl, newgeom); + + // else mesh.setGeom(geom ); + // if(!(points instanceof Float32Array)) + // { + // if(points.length!=buff.length) + // { + // buff=new Float32Array(points.length); + // buff.set(points); + // } + // else + // { + // buff.set(points); + // } + // } + // else + // { + // buff=points; + // } + // attr=mesh.setAttribute(CGL.SHADERVAR_VERTEX_POSITION,buff,3); +}; + +render.onTriggered = function () +{ + if (points.length === 0) return; + if (!mesh) return; + if (op.instanced(render)) return; + + let shader = cgl.getShader(); + if (!shader) return; + + let oldPrim = shader.glPrimitive; + + shader.glPrimitive = cgl.gl.LINES; + + // if(numPoints.get()<=0)attr.numItems=buff.length/3; + // else attr.numItems=Math.min(numPoints.get(),buff.length/3); + + mesh.render(shader); + + shader.glPrimitive = oldPrim; + + next.trigger(); +}; + + +}; + +Ops.Gl.DrawTextureMapping.prototype = new CABLES.Op(); +CABLES.OPS["f61e47de-9156-4243-9a83-79002aeaf7be"]={f:Ops.Gl.DrawTextureMapping,objName:"Ops.Gl.DrawTextureMapping"}; + + + + +// ************************************************************** +// +// Ops.Gl.FaceCulling_v2 +// +// ************************************************************** + +Ops.Gl.FaceCulling_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + STR_FRONT = "Front Sides", + STR_BACK = "Back Sides", + STR_BOTH = "All", + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + facing = op.inSwitch("Discard", [STR_BACK, STR_FRONT, STR_BOTH], STR_BACK), + enable = op.inValueBool("Active", true), + cgl = op.patch.cgl; + +op.setPortGroup("Face Fulling", [enable, facing]); +let whichFace = cgl.gl.BACK; + +render.onTriggered = function () +{ + cgl.pushCullFace(enable.get()); + cgl.pushCullFaceFacing(whichFace); + + trigger.trigger(); + + cgl.popCullFace(); + cgl.popCullFaceFacing(); +}; + +facing.onChange = function () +{ + whichFace = cgl.gl.BACK; + if (facing.get() == STR_FRONT) whichFace = cgl.gl.FRONT; + else if (facing.get() == STR_BOTH) whichFace = cgl.gl.FRONT_AND_BACK; +}; + + +}; + +Ops.Gl.FaceCulling_v2.prototype = new CABLES.Op(); +CABLES.OPS["9dfd0ee4-81e1-438c-8a99-4894c64f41cb"]={f:Ops.Gl.FaceCulling_v2,objName:"Ops.Gl.FaceCulling_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.FontMSDF_v2 +// +// ************************************************************** + +Ops.Gl.FontMSDF_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUUID = op.inString("Font Name", CABLES.uuid()), + urlData = op.inUrl("Font Data"), + urlTex = op.inUrl("Font Image"), + urlTex1 = op.inUrl("Font Image 1"), + urlTex2 = op.inUrl("Font Image 2"), + urlTex3 = op.inUrl("Font Image 3"), + outLoaded = op.outBool("Loaded"), + outNumChars = op.outNumber("Total Chars"), + outChars = op.outString("Chars"), + cgl = op.patch.cgl; + +let + loadedData = false, + loadedTex = false, + loadingId = 0; + +inUUID.onChange = +urlData.onChange = + urlTex.onChange = + urlTex1.onChange = + urlTex2.onChange = + urlTex3.onChange = load; + +const textures = []; + +function updateLoaded() +{ + const l = loadedData && loadedTex; + if (!outLoaded.get() && l) op.patch.emitEvent("FontLoadedMSDF"); + outLoaded.set(l); +} + +op.onFileChanged = function (fn) +{ + if ( + (urlTex.get() && urlTex.get().indexOf(fn) > -1) || + (urlTex1.get() && urlTex1.get().indexOf(fn) > -1) || + (urlTex2.get() && urlTex2.get().indexOf(fn) > -1) || + (urlTex3.get() && urlTex3.get().indexOf(fn) > -1)) + { + load(); + } +}; + +let oldUUID = ""; + +function load() +{ + if (!urlData.get() || !urlTex.get()) return; + + textures.length = 0; + op.patch.deleteVar("font_data_" + oldUUID); + op.patch.deleteVar("font_tex_" + oldUUID); + oldUUID = inUUID.get(); + + const varNameData = "font_data_" + inUUID.get(); + const varNameTex = "font_tex_" + inUUID.get(); + + op.patch.setVarValue(varNameData, {}); + op.patch.setVarValue(varNameTex, textures); + + op.patch.getVar(varNameData).type = "fontData"; + op.patch.getVar(varNameTex).type = "fontTexture"; + + loadedData = loadedTex = false; + updateLoaded(); + + op.patch.loading.finished(loadingId); + loadingId = op.patch.loading.start("jsonFile", "" + urlData.get()); + + op.setUiError("invaliddata", null); + op.setUiError("jsonerr", null); + op.setUiError("texurlerror", null); + + const urlDatastr = op.patch.getFilePath(String(urlData.get())); + + // load font data json + cgl.patch.loading.addAssetLoadingTask(() => + { + CABLES.ajax(urlDatastr, (err, _data, xhr) => + { + if (err) + { + op.logError(err); + return; + } + try + { + let data = _data; + if (typeof data === "string") data = JSON.parse(_data); + if (!data.chars || !data.info || !data.info.face) + { + op.setUiError("invaliddata", "data file is invalid"); + return; + } + + outNumChars.set(data.chars.length); + let allChars = ""; + for (let i = 0; i < data.chars.length; i++)allChars += data.chars[i].char; + outChars.set(allChars); + + op.setUiAttrib({ "extendTitle": data.info.face }); + op.patch.setVarValue(varNameData, null); + op.patch.setVarValue(varNameData, + { + "name": CABLES.basename(urlData.get()), + "basename": inUUID.get(), + "data": data + }); + + op.patch.loading.finished(loadingId); + loadedData = true; + updateLoaded(); + } + catch (e) + { + op.patch.setVarValue(varNameData, null); + op.patch.setVarValue(varNameTex, null); + + op.logError(e); + op.setUiError("jsonerr", "Problem while loading json:
    " + e); + op.patch.loading.finished(loadingId); + updateLoaded(); + outLoaded.set(false); + } + }); + }); + + // load font texture + + for (let i = 0; i < 4; i++) + { + const num = i; + + let texPort = urlTex; + if (i == 1)texPort = urlTex1; + if (i == 2)texPort = urlTex2; + if (i == 3)texPort = urlTex3; + + if (!texPort.get()) continue; + + const loadingIdTex = cgl.patch.loading.start("textureOp", texPort.get()); + const urlTexstr = op.patch.getFilePath(String(texPort.get())); + + CGL.Texture.load(cgl, urlTexstr, + function (err, tex) + { + if (err) + { + op.setUiError("texurlerror", "could not load texture"); + cgl.patch.loading.finished(loadingIdTex); + loadedTex = false; + return; + } + textures[num] = tex; + op.patch.setVarValue(varNameTex, null); + op.patch.setVarValue(varNameTex, textures); + + loadedTex = true; + cgl.patch.loading.finished(loadingIdTex); + updateLoaded(); + }, { + "filter": CGL.Texture.FILTER_LINEAR, + "flip": false + }); + } +} + + +}; + +Ops.Gl.FontMSDF_v2.prototype = new CABLES.Op(); +CABLES.OPS["6cbd5d67-25d5-4936-a2ad-3ee8ed478570"]={f:Ops.Gl.FontMSDF_v2,objName:"Ops.Gl.FontMSDF_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ForceCanvasSize +// +// ************************************************************** + +Ops.Gl.ForceCanvasSize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTrigger = op.inTrigger("Trigger"), + inActive = op.inBool("Active", true), + inWhat = op.inSwitch("Force", ["Resolution", "Aspect Ratio"], "Resolution"), + inCenter = op.inBool("Center In Parent", true), + inScaleFit = op.inBool("Scale to fit Parent", false), + inWidth = op.inInt("Set Width", 300), + inHeight = op.inInt("Set Height", 200), + inPresets = op.inDropDown("Aspect Ratio", ["Custom", "21:9", "2:1", "16:9", "16:10", "4:3", "1:1", "9:16", "1:2", "iPhoneXr Vert"], "16:9"), + inRatio = op.inFloat("Ratio", 0), + inStretch = op.inDropDown("Fill Parent", ["Auto", "Width", "Height", "Both"], "Auto"), + next = op.outTrigger("Next"), + outWidth = op.outNumber("Width"), + outHeight = op.outNumber("Height"), + outMarginLeft = op.outNumber("Margin Left"), + outMarginTop = op.outNumber("Margin Top"); + +op.setPortGroup("Size", [inWidth, inHeight]); +op.setPortGroup("Proportions", [inRatio, inStretch, inPresets]); + +let align = 0; +const ALIGN_NONE = 0; +const ALIGN_WIDTH = 1; +const ALIGN_HEIGHT = 2; +const ALIGN_BOTH = 3; +const ALIGN_AUTO = 4; + +inStretch.onChange = updateUi; +inWhat.onChange = updateMethod; +inCenter.onChange = + inTrigger.onLinkChanged = removeStyles; + +inPresets.onChange = updateRatioPreset; + +const cgl = op.patch.cgl; + +if (window.getComputedStyle(cgl.canvas).position === "absolute") +{ + cgl.canvas.style.position = "initial"; + op.warn("[cables forceCanvasSize] - canvas was positioned absolute, not compatible with Ops.Gl.ForceCanvasSize"); +} + +updateUi(); + +function updateMethod() +{ + if (inWhat.get() == "Aspect Ratio") + { + inRatio.set(100); + updateRatioPreset(); + } + updateUi(); +} + +function updateRatioPreset() +{ + const pr = inPresets.get(); + if (pr == "Custom") return; + else if (pr == "16:9")inRatio.set(16 / 9); + else if (pr == "4:3")inRatio.set(4 / 3); + else if (pr == "16:10")inRatio.set(16 / 10); + else if (pr == "21:9")inRatio.set(21 / 9); + else if (pr == "2:1")inRatio.set(2); + else if (pr == "1:1")inRatio.set(1); + else if (pr == "9:16")inRatio.set(9 / 16); + else if (pr == "1:2")inRatio.set(0.5); + else if (pr == "iPhoneXr Vert")inRatio.set(9 / 19.5); +} + +inRatio.onChange = () => +{ + removeStyles(); +}; + +inActive.onChange = function () +{ + if (!inActive.get())removeStyles(); +}; + +function updateUi() +{ + const forceRes = inWhat.get() == "Resolution"; + inWidth.setUiAttribs({ "greyout": !forceRes }); + inHeight.setUiAttribs({ "greyout": !forceRes }); + + inPresets.setUiAttribs({ "greyout": forceRes }); + inStretch.setUiAttribs({ "greyout": forceRes }); + inRatio.setUiAttribs({ "greyout": forceRes }); + + align = 0; + + if (!forceRes) + { + const strAlign = inStretch.get(); + if (strAlign == "Width")align = ALIGN_WIDTH; + else if (strAlign == "Height")align = ALIGN_HEIGHT; + else if (strAlign == "Both")align = ALIGN_BOTH; + else if (strAlign == "Auto")align = ALIGN_AUTO; + } +} + +function removeStyles() +{ + cgl.canvas.style["margin-top"] = ""; + cgl.canvas.style["margin-left"] = ""; + + outMarginLeft.set(0); + outMarginTop.set(0); + + const rect = cgl.canvas.parentNode.getBoundingClientRect(); + cgl.setSize(rect.width, rect.height); +} + +inTrigger.onTriggered = function () +{ + if (!inActive.get()) return next.trigger(); + + let w = inWidth.get(); + let h = inHeight.get(); + + let clientRect = cgl.canvas.parentNode.getBoundingClientRect(); + if (clientRect.height == 0) + { + cgl.canvas.parentNode.style.height = "100%"; + clientRect = cgl.canvas.parentNode.getBoundingClientRect(); + } + if (clientRect.width == 0) + { + cgl.canvas.parentNode.style.width = "100%"; + clientRect = cgl.canvas.parentNode.getBoundingClientRect(); + } + + if (align == ALIGN_WIDTH) + { + w = clientRect.width; + h = w * 1 / inRatio.get(); + } + else if (align == ALIGN_HEIGHT) + { + h = clientRect.height; + w = h * inRatio.get(); + } + else if (align == ALIGN_AUTO) + { + const rect = clientRect; + + h = rect.height; + w = h * inRatio.get(); + + if (w > rect.width) + { + w = rect.width; + h = w * 1 / inRatio.get(); + } + } + else if (align == ALIGN_BOTH) + { + const rect = clientRect; + h = rect.height; + w = h * inRatio.get(); + + if (w < rect.width) + { + w = rect.width; + h = w * 1 / inRatio.get(); + } + } + + w = Math.ceil(w); + h = Math.ceil(h); + + if (inCenter.get()) + { + const rect = clientRect; + + const t = (rect.height - h) / 2; + const l = (rect.width - w) / 2; + + outMarginLeft.set(l); + outMarginTop.set(t); + + cgl.canvas.style["margin-top"] = t + "px"; + cgl.canvas.style["margin-left"] = l + "px"; + } + else + { + cgl.canvas.style["margin-top"] = "0"; + cgl.canvas.style["margin-left"] = "0"; + + outMarginLeft.set(0); + outMarginTop.set(0); + } + + if (inScaleFit.get()) + { + const rect = clientRect; + const scX = rect.width / inWidth.get(); + const scY = rect.height / inHeight.get(); + cgl.canvas.style.transform = "scale(" + Math.min(scX, scY) + ")"; + } + else + { + cgl.canvas.style.transform = "scale(1)"; + } + + if (cgl.canvas.width / cgl.pixelDensity != w || cgl.canvas.height / cgl.pixelDensity != h) + { + outWidth.set(w); + outHeight.set(h); + cgl.setSize(w, h); + } + // else + next.trigger(); +}; + + +}; + +Ops.Gl.ForceCanvasSize.prototype = new CABLES.Op(); +CABLES.OPS["a8b3380e-cd4a-4000-9ee9-1c65a11027dd"]={f:Ops.Gl.ForceCanvasSize,objName:"Ops.Gl.ForceCanvasSize"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfAnimationArray +// +// ************************************************************** + +Ops.Gl.GLTF.GltfAnimationArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Render"), + inNodeName = op.inString("Node Name"), + inNum = op.inInt("Steps", 100), + inFullAnim = op.inBool("Full Animation", true), + inAnimStart = op.inFloat("Start", 0), + inAnimLen = op.inFloat("Length", 10), + next = op.outTrigger("Next"), + outFound = op.outBool("Found"), + outArr = op.outArray("Positions"); + +const cgl = op.patch.cgl; + +op.setPortGroup("Timing", [inFullAnim, inAnimStart, inAnimLen]); +let node = null; + +inFullAnim.onChange = function () +{ + inAnimStart.setUiAttribs({ "greyout": inFullAnim.get() }); + inAnimLen.setUiAttribs({ "greyout": inFullAnim.get() }); +}; + +inNodeName.onChange = function () +{ + outArr.set(null); + node = null; + outFound.set(false); +}; + +inExec.onTriggered = function () +{ + if (!cgl.frameStore.currentScene) return; + + if (!node) + { + const name = inNodeName.get(); + + if (!cgl.frameStore || !cgl.frameStore.currentScene || !cgl.frameStore.currentScene.nodes) + { + return; + } + + for (var i = 0; i < cgl.frameStore.currentScene.nodes.length; i++) + { + if (cgl.frameStore.currentScene.nodes[i].name == name) + { + node = cgl.frameStore.currentScene.nodes[i]; + outFound.set(true); + } + } + } + + // var idx=inNode.get(); + // idx=Math.max(0,idx); + // idx=Math.min(cgl.frameStore.currentScene.nodes.length-1,idx); + + const n = node; + const arr = []; + + if (n && n._animTrans && n._animTrans.length) + { + outArr.set(null); + + const num = inNum.get(); + let len = n._animTrans[0].getLength(); + let add = 0; + + if (!inFullAnim.get()) + { + len = inAnimLen.get(); + add = inAnimStart.get(); + } + + for (var i = 0; i < num; i++) + { + const t = len * i / num + add; + + arr[i * 3 + 0] = n._animTrans[0].getValue(t); + arr[i * 3 + 1] = n._animTrans[1].getValue(t); + arr[i * 3 + 2] = n._animTrans[2].getValue(t); + } + + outArr.set(arr); + } + + next.trigger(); +}; + + +}; + +Ops.Gl.GLTF.GltfAnimationArray.prototype = new CABLES.Op(); +CABLES.OPS["568ce7cd-a938-4498-9109-482564757a0e"]={f:Ops.Gl.GLTF.GltfAnimationArray,objName:"Ops.Gl.GLTF.GltfAnimationArray"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfCameraViewMatrix +// +// ************************************************************** + +Ops.Gl.GLTF.GltfCameraViewMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Update"), + inName = op.inString("Node Name", "default"), + outArr = op.outArray("Matrix"), + outFound = op.outBool("Found"); + +let camNode = null; + +inExec.onTriggered = () => +{ + if (!camNode) findCam(); + + if (camNode) + { + camNode.start(0); + camNode.end(); + outArr.set(camNode.vMat); + } +}; + +inName.onChange = () => { camNode = null; }; + +function findCam() +{ + const gltf = op.patch.cgl.frameStore.currentScene; + + if (gltf) + { + gltf.cameras = gltf.cameras || []; + for (let i = 0; i < gltf.cameras.length; i++) + { + if (gltf.cameras[i].name == inName.get()) + { + camNode = gltf.cameras[i]; + outFound.set(true); + return; + } + } + } + + outFound.set(false); +} + + +}; + +Ops.Gl.GLTF.GltfCameraViewMatrix.prototype = new CABLES.Op(); +CABLES.OPS["ab3c43f7-3d73-4f46-8034-454e9c4d51c5"]={f:Ops.Gl.GLTF.GltfCameraViewMatrix,objName:"Ops.Gl.GLTF.GltfCameraViewMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfDracoCompression +// +// ************************************************************** + +Ops.Gl.GLTF.GltfDracoCompression = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +class DracoDecoderClass +{ + constructor() + { + this.workerLimit = 4; + this.workerPool = []; + this.workerNextTaskID = 1; + this.workerSourceURL = ""; + + this.config = { + "wasm": Uint8Array.from(atob(DracoDecoderWASM), (c) => { return c.charCodeAt(0); }), + "wrapper": DracoWASMWrapperCode, + "decoderSettings": {}, + }; + + const dracoWorker = this._DracoWorker.toString(); + const workerCode = dracoWorker.substring(dracoWorker.indexOf("{") + 1, dracoWorker.lastIndexOf("}")); + + const jsContent = this.config.wrapper; + const body = [ + "/* draco decoder */", + jsContent, + "", + "/* worker */", + workerCode + ].join("\n"); + + this.workerSourceURL = URL.createObjectURL(new Blob([body])); + } + + _getWorker(taskID, taskCost) + { + if (this.workerPool.length < this.workerLimit) + { + const worker = new Worker(this.workerSourceURL); + worker._callbacks = {}; + worker._taskCosts = {}; + worker._taskLoad = 0; + worker.postMessage({ "type": "init", "decoderConfig": this.config }); + worker.onmessage = (e) => + { + const message = e.data; + + switch (message.type) + { + case "done": + worker._callbacks[message.taskID].finishedCallback(message.geometry); + break; + + case "error": + worker._callbacks[message.taskID].errorCallback(message); + break; + + default: + op.error("THREE.DRACOLoader: Unexpected message, \"" + message.type + "\""); + } + this._releaseTask(worker, message.taskID); + }; + this.workerPool.push(worker); + } + else + { + this.workerPool.sort(function (a, b) + { + return a._taskLoad > b._taskLoad ? -1 : 1; + }); + } + + const worker = this.workerPool[this.workerPool.length - 1]; + worker._taskCosts[taskID] = taskCost; + worker._taskLoad += taskCost; + return worker; + } + + decodeGeometry(buffer, finishedCallback, errorCallback = null) + { + const taskID = this.workerNextTaskID++; + const taskCost = buffer.byteLength; + + const worker = this._getWorker(taskID, taskCost); + worker._callbacks[taskID] = { finishedCallback, errorCallback }; + worker.postMessage({ "type": "decode", "taskID": taskID, buffer }, [buffer]); + } + + _releaseTask(worker, taskID) + { + worker._taskLoad -= worker._taskCosts[taskID]; + delete worker._callbacks[taskID]; + delete worker._taskCosts[taskID]; + } + + _DracoWorker() + { + let pendingDecoder; + + onmessage = function (e) + { + const message = e.data; + switch (message.type) + { + case "init": + const decoderConfig = message.decoderConfig; + const moduleConfig = decoderConfig.decoderSettings; + pendingDecoder = new Promise(function (resolve) + { + moduleConfig.onModuleLoaded = function (draco) + { + // Module is Promise-like. Wrap before resolving to avoid loop. + resolve({ "draco": draco }); + }; + moduleConfig.wasmBinary = decoderConfig.wasm; + DracoDecoderModule(moduleConfig); // eslint-disable-line no-undef + }); + break; + case "decode": + pendingDecoder.then((module) => + { + const draco = module.draco; + + const f = new draco.Decoder(); + const dataBuff = new Int8Array(message.buffer); + + const geometryType = f.GetEncodedGeometryType(dataBuff); + const buffer = new draco.DecoderBuffer(); + buffer.Init(dataBuff, dataBuff.byteLength); + + let outputGeometry = new draco.Mesh(); + const status = f.DecodeBufferToMesh(buffer, outputGeometry); + const attribute = f.GetAttributeByUniqueId(outputGeometry, 1); + const geometry = dracoAttributes(draco, f, outputGeometry, geometryType, name); + + this.postMessage({ "type": "done", "taskID": message.taskID, "geometry": geometry }); + + draco.destroy(f); + draco.destroy(buffer); + }); + break; + } + }; + + let dracoAttributes = function (draco, decoder, dracoGeometry, geometryType, name) + { + const attributeIDs = { + "position": draco.POSITION, + "normal": draco.NORMAL, + "color": draco.COLOR, + "uv": draco.TEX_COORD, + "joints": draco.GENERIC, + "weights": draco.GENERIC, + }; + const attributeTypes = { + "position": "Float32Array", + "normal": "Float32Array", + "color": "Float32Array", + "weights": "Float32Array", + "joints": "Uint8Array", + "uv": "Float32Array" + }; + + const geometry = { + "index": null, + "attributes": [] + }; + + let count = 0; + for (const attributeName in attributeIDs) + { + const attributeType = attributeTypes[attributeName]; + let attributeID = decoder.GetAttributeId(dracoGeometry, attributeIDs[attributeName]); + + count++; + if (attributeID != -1) + { + let attribute = decoder.GetAttribute(dracoGeometry, attributeID); + geometry.attributes.push(decodeAttribute(draco, decoder, dracoGeometry, attributeName, attributeType, attribute)); + } + } + + if (geometryType === draco.TRIANGULAR_MESH) geometry.index = decodeIndex(draco, decoder, dracoGeometry); + else op.warn("unknown draco geometryType", geometryType); + + draco.destroy(dracoGeometry); + return geometry; + }; + + let decodeIndex = function (draco, decoder, dracoGeometry) + { + const numFaces = dracoGeometry.num_faces(); + const numIndices = numFaces * 3; + const byteLength = numIndices * 4; + const ptr = draco._malloc(byteLength); + + decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr); + const index = new Uint32Array(draco.HEAPF32.buffer, ptr, numIndices).slice(); + + draco._free(ptr); + + return { + "array": index, + "itemSize": 1 + }; + }; + + let decodeAttribute = function (draco, decoder, dracoGeometry, attributeName, attributeType, attribute) + { + let bytesPerElement = 4; + if (attributeType === "Float32Array") bytesPerElement = 4; + else if (attributeType === "Uint8Array") bytesPerElement = 1; + else op.warn("unknown attrtype bytesPerElement", attributeType); + + const numComponents = attribute.num_components(); + const numPoints = dracoGeometry.num_points(); + const numValues = numPoints * numComponents; + const byteLength = numValues * bytesPerElement; + const dataType = getDracoDataType(draco, attributeType); + const ptr = draco._malloc(byteLength); + let array = null; + + decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, attribute, dataType, byteLength, ptr); + + if (attributeType === "Float32Array") array = new Float32Array(draco.HEAPF32.buffer, ptr, numValues).slice(); + else if (attributeType === "Uint8Array") array = new Uint8Array(draco.HEAPF32.buffer, ptr, numValues).slice(); + else op.warn("unknown attrtype", attributeType); + + draco._free(ptr); + + return { + "name": attributeName, + "array": array, + "itemSize": numComponents + }; + }; + + let getDracoDataType = function (draco, attributeType) + { + switch (attributeType) + { + case "Float32Array": return draco.DT_FLOAT32; + case "Int8Array": return draco.DT_INT8; + case "Int16Array": return draco.DT_INT16; + case "Int32Array": return draco.DT_INT32; + case "Uint8Array": return draco.DT_UINT8; + case "Uint16Array": return draco.DT_UINT16; + case "Uint32Array": return draco.DT_UINT32; + } + }; + } +} + +window.DracoDecoder = new DracoDecoderClass(); + + +}; + +Ops.Gl.GLTF.GltfDracoCompression.prototype = new CABLES.Op(); +CABLES.OPS["4ecdc2ef-a242-4548-ad74-13f617119a64"]={f:Ops.Gl.GLTF.GltfDracoCompression,objName:"Ops.Gl.GLTF.GltfDracoCompression"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfGeometry +// +// ************************************************************** + +Ops.Gl.GLTF.GltfGeometry = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Update"), + inNodeName = op.inString("Name", "default"), + inSubmesh = op.inInt("Submesh", 0), + next = op.outTrigger("Next"), + outGeom = op.outObject("Geometry", null, "geometry"), + outFound = op.outBoolNum("Found"); + +const cgl = op.patch.cgl; +let mesh = null; +let currentSceneLoaded = null; + +inSubmesh.onChange = +inNodeName.onChange = function () +{ + outGeom.set(null); + mesh = null; + outFound.set(false); + op.setUiAttrib({ "extendTitle": inNodeName.get() + "." + inSubmesh.get() }); +}; + +exec.onTriggered = () => +{ + if (!cgl.frameStore.currentScene) return; + if (currentSceneLoaded != cgl.frameStore.currentScene.loaded) mesh = null; + + if (!mesh) + { + if (!cgl.frameStore || !cgl.frameStore.currentScene || !cgl.frameStore.currentScene.nodes || !cgl.frameStore.currentScene.loaded) + { + return; + } + outFound.set(false); + outGeom.set(null); + const name = inNodeName.get(); + + currentSceneLoaded = cgl.frameStore.currentScene.loaded; + + for (let i = 0; i < cgl.frameStore.currentScene.meshes.length; i++) + { + if (cgl.frameStore.currentScene.meshes[i].name == name) + { + mesh = cgl.frameStore.currentScene.meshes[i]; + + const idx = Math.abs(inSubmesh.get()); + if (mesh.meshes[idx] && mesh.meshes[idx].geom) + { + outFound.set(true); + outGeom.set(mesh.meshes[idx].geom); + } + } + } + } + + next.trigger(); +}; + + +}; + +Ops.Gl.GLTF.GltfGeometry.prototype = new CABLES.Op(); +CABLES.OPS["2e59da07-455a-457c-99d8-1c23a1ddeea2"]={f:Ops.Gl.GLTF.GltfGeometry,objName:"Ops.Gl.GLTF.GltfGeometry"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfHierarchy +// +// ************************************************************** + +Ops.Gl.GLTF.GltfHierarchy = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec=op.inTrigger("Trigger"), + inNodeName=op.inString("Node name","default"), + next=op.outTrigger("Next"), + outArr=op.outArray("Bones Lines"); + +exec.onTriggered=update; + +const cgl=op.patch.cgl; + + +let node=null; +const tr = vec3.create(); + + + +inNodeName.onChange=()=> +{ + node=null; + update(); +}; + +function addChild(gltf,arr,parent,child) +{ + + if(parent) + { + + mat4.getTranslation(tr, parent.modelMatAbs()); + arr.push(tr[0], tr[1], tr[2]); + + mat4.getTranslation(tr, child.modelMatAbs()); + arr.push(tr[0], tr[1], tr[2]); + + } + + if(child && child.children) + { + for(let i=0;i= 0) + { + scene.nodes[i].addTranslate = null; + } + } +} + +inExec.onTriggered = function () +{ + if (!cgl.frameStore.currentScene) return; + + scene = cgl.frameStore.currentScene; + + let node = null; + + const ampl = inAmplitude.get(); + const offs = inOffset.get(); + const time = inTime.get(); + const ax = axisX.get(); + const ay = axisY.get(); + const az = axisZ.get(); + + Math.randomSeed = 5711; + let found = 0; + + for (let i = 0; i < cgl.frameStore.currentScene.nodes.length; i++) + { + if (cgl.frameStore.currentScene.nodes[i].name.indexOf(inFilter.get()) >= 0) + { + node = cgl.frameStore.currentScene.nodes[i]; + + found++; + + const v = Math.sin(time + (Math.seededRandom() * offs)) * ampl; + node.addTranslate = [v * ax, v * ay, v * az]; + } + } + + outNum.set(found); + + next.trigger(); +}; + + +}; + +Ops.Gl.GLTF.GltfNodeSineAnim.prototype = new CABLES.Op(); +CABLES.OPS["b4608e38-cc48-4748-a640-d653f53fc950"]={f:Ops.Gl.GLTF.GltfNodeSineAnim,objName:"Ops.Gl.GLTF.GltfNodeSineAnim"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfNodeTransform_v2 +// +// ************************************************************** + +Ops.Gl.GLTF.GltfNodeTransform_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Render"), + inNodeName = op.inString("Node Name"), + next = op.outTrigger("Next"), + inApply = op.inBool("Set Matrix", true), + outFound = op.outBool("Found"), + outMat = op.outArray("Matrix"); + +const cgl = op.patch.cgl; +const translate = vec3.create(); +let node = null; +let currentSceneLoaded = null; +const m = mat4.create(); + +inNodeName.onChange = function () +{ + node = null; + outFound.set(false); + op.setUiAttrib({ "extendTitle": inNodeName.get() }); +}; + +inExec.onTriggered = function () +{ + if (!cgl.frameStore.currentScene) return; + if (currentSceneLoaded != cgl.frameStore.currentScene.loaded) node = null; + let found = false; + + if (!node) + { + const name = inNodeName.get(); + + if (!cgl.frameStore || !cgl.frameStore.currentScene || !cgl.frameStore.currentScene.nodes) return; + + currentSceneLoaded = cgl.frameStore.currentScene.loaded; + + for (let i = 0; i < cgl.frameStore.currentScene.nodes.length; i++) + { + if (cgl.frameStore.currentScene.nodes[i].name == name) + { + node = cgl.frameStore.currentScene.nodes[i]; + found = true; + break; + } + } + } + else + { + found = true; + } + + outFound.set(found); + cgl.pushModelMatrix(); + + if (node) + { + mat4.copy(m, node.modelMatAbs()); + + mat4.multiply(cgl.mMatrix, cgl.mMatrix, m); + + outMat.set(null); + outMat.set(m); + } + + next.trigger(); + cgl.popModelMatrix(); +}; + + +}; + +Ops.Gl.GLTF.GltfNodeTransform_v2.prototype = new CABLES.Op(); +CABLES.OPS["5a36a0f2-a4cb-4ac4-a7af-95924f2a9558"]={f:Ops.Gl.GLTF.GltfNodeTransform_v2,objName:"Ops.Gl.GLTF.GltfNodeTransform_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfNodeTransforms_v2 +// +// ************************************************************** + +Ops.Gl.GLTF.GltfNodeTransforms_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Render"), + inStr = op.inString("Search", ""), + inSort = op.inSwitch("Order", ["None", "AlphaNumerical"], "None"), + inSpace = op.inSwitch("Space", ["GLTF", "World"], "GLTF"), + outPos = op.outArray("Positions"), + next = op.outTrigger("Next"), + outScale = op.outArray("Scale"), + outNames = op.outArray("Names"), + outRot = op.outArray("Rotation"); + +const cgl = op.patch.cgl; +let needsupdate = true; +outPos.onChange = function () { needsupdate = true; }; +inExec.onTriggered = exec; + +inStr.onChange = function () +{ + needsupdate = true; +}; +function exec() +{ + // if (needsupdate) + update(); + next.trigger(); +} + +function update() +{ + outPos.set(null); + outScale.set(null); + outNames.set(null); + // outRot.set(null); + + if (!cgl.frameStore.currentScene) return; + + const arrPos = []; + const arrRot = []; + const arrScale = []; + const arrNames = []; + + const worldspace = inSpace.get() == "World"; + + for (let i = 0; i < cgl.frameStore.currentScene.nodes.length; i++) + { + if (cgl.frameStore.currentScene.nodes[i].name.indexOf(inStr.get()) == 0) + { + const n = cgl.frameStore.currentScene.nodes[i]._node; + const node = cgl.frameStore.currentScene.nodes[i]; + arrNames.push(n.name); + + const tr = vec3.create(); + + let m = node.modelMatAbs(); + // const m=node.modelMatLocal(); + + if (!worldspace) + { + m = node.modelMatLocal(); + } + + mat4.getTranslation(tr, m); + + // const empty=vec3.create(); + // vec3.transformMat4(tr, empty, m); + + arrPos.push(tr[0], tr[1], tr[2]); + + const q = quat.create(); + mat4.getRotation(q, m); + arrRot.push(q[0], q[1], q[2], q[3]); + + if (node._tempAnimScale) arrScale.push(node._tempAnimScale[0], node._tempAnimScale[1], node._tempAnimScale[2]); + else if (n.scale) arrScale.push(n.scale[0], n.scale[1], n.scale[2]); + else arrScale.push(1, 1, 1); + } + } + + if (inSort.get()) + { + let list = []; + for (let j = 0; j < arrNames.length; j++) + list.push({ + "name": arrNames[j], + "pos": [arrPos[j * 3 + 0], arrPos[j * 3 + 1], arrPos[j * 3 + 2]], + "scale": [arrScale[j * 3 + 0], arrScale[j * 3 + 1], arrScale[j * 3 + 2]] + }); + + list.sort(function (a, b) + { + return ((a.name < b.name) ? -1 : ((a.name == b.name) ? 0 : 1)); + // Sort could be modified to, for example, sort on the age + // if the name is the same. + }); + + // 3) separate them back out: + for (let k = 0; k < list.length; k++) + { + arrNames[k] = list[k].name; + arrPos[k * 3 + 0] = list[k].pos[0]; + arrPos[k * 3 + 1] = list[k].pos[1]; + arrPos[k * 3 + 2] = list[k].pos[2]; + arrScale[k * 3 + 0] = list[k].scale[0]; + arrScale[k * 3 + 1] = list[k].scale[1]; + arrScale[k * 3 + 2] = list[k].scale[2]; + } + } + + outPos.set(arrPos); + outScale.set(arrScale); + outNames.set(arrNames); + outRot.set(arrRot); + + needsupdate = false; +} + + +}; + +Ops.Gl.GLTF.GltfNodeTransforms_v2.prototype = new CABLES.Op(); +CABLES.OPS["02247569-4a2d-4a1b-952d-3585b1c677a8"]={f:Ops.Gl.GLTF.GltfNodeTransforms_v2,objName:"Ops.Gl.GLTF.GltfNodeTransforms_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfNode_v2 +// +// ************************************************************** + +Ops.Gl.GLTF.GltfNode_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Render"), + inNodeName = op.inString("Node Name"), + inTrans = op.inBool("Transformation", true), + inDraw = op.inBool("Draw Mesh", true), + inChilds = op.inBool("Draw Childs", true), + inIgnMaterial = op.inBool("Ignore Material", true), + + inSceneTime = op.inBool("Use Scene Time", true), + inTime = op.inFloat("Time", 0), + + next = op.outTrigger("Next"), + outGeom = op.outObject("Geometry", null, "geometry"), + outFound = op.outBool("Found"); +const cgl = op.patch.cgl; + +let node = null; +let currentSceneLoaded = null; + +inNodeName.onChange = function () +{ + outGeom.set(null); + node = null; + outFound.set(false); + if (!inNodeName.isLinked())op.setUiAttrib({ "extendTitle": inNodeName.get() }); +}; + +inSceneTime.onChange = updateTimeInputs; + +updateTimeInputs(); + +function updateTimeInputs() +{ + inTime.setUiAttribs({ "greyout": inSceneTime.get() }); +} + +inExec.onTriggered = function () +{ + if (!cgl.frameStore.currentScene) return; + if (currentSceneLoaded != cgl.frameStore.currentScene.loaded) node = null; + + if (!node) + { + const name = inNodeName.get(); + + if (!cgl.frameStore || !cgl.frameStore.currentScene || !cgl.frameStore.currentScene.nodes) + { + return; + } + currentSceneLoaded = cgl.frameStore.currentScene.loaded; + + for (let i = 0; i < cgl.frameStore.currentScene.nodes.length; i++) + { + if (cgl.frameStore.currentScene.nodes[i].name == name) + { + node = cgl.frameStore.currentScene.nodes[i]; + outFound.set(true); + + if (node && node.mesh && node.mesh.meshes && node.mesh.meshes[0].geom) outGeom.set(node.mesh.meshes[0].geom); + else outGeom.set(null); + } + } + } + + cgl.pushModelMatrix(); + + if (node) + { + if (inTrans.get()) + { + cgl.pushModelMatrix(); + node.transform(cgl); + } + + // node.updateMatrix(); + + // render(cgl, dontTransform, dontDrawMesh, ignoreMaterial, ignoreChilds, drawHidden, _time) + + let time; + if (!inSceneTime.get()) time = inTime.get(); + + node.render(cgl, !inTrans.get(), !inDraw.get(), inIgnMaterial.get(), !inChilds.get(), true, time); + } + + next.trigger(); + + if (node) + { + if (inTrans.get()) cgl.popModelMatrix(); + } + + cgl.popModelMatrix(); +}; + + +}; + +Ops.Gl.GLTF.GltfNode_v2.prototype = new CABLES.Op(); +CABLES.OPS["10d09387-0085-4162-ab5b-b89eebbf3cf4"]={f:Ops.Gl.GLTF.GltfNode_v2,objName:"Ops.Gl.GLTF.GltfNode_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfScene_v3 +// +// ************************************************************** + +Ops.Gl.GLTF.GltfScene_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"inc_camera_js":"const gltfCamera = class\n{\n constructor(gltf, node)\n {\n this.node=node;\n this.name=node.name;\n // console.log(gltf);\n this.config=gltf.json.cameras[node.camera];\n\n this.pos = vec3.create();\n this.quat = quat.create();\n this.quatOr = quat.create();\n this.vCenter = vec3.create();\n this.vUp = vec3.create();\n this.vMat = mat4.create();\n\n }\n\n updateAnim(time)\n {\n if (this.node && this.node._animTrans)\n {\n\n vec3.set(this.pos,\n this.node._animTrans[0].getValue(time),\n this.node._animTrans[1].getValue(time),\n this.node._animTrans[2].getValue(time));\n\n quat.set(this.quat,\n this.node._animRot[0].getValue(time),\n this.node._animRot[1].getValue(time),\n this.node._animRot[2].getValue(time),\n this.node._animRot[3].getValue(time));\n }\n }\n\n start(time)\n {\n if (cgl.frameStore.shadowPass) return;\n\n this.updateAnim(time);\n const asp = cgl.getViewPort()[2] / cgl.getViewPort()[3];\n\n cgl.pushPMatrix();\n // mat4.perspective(\n // cgl.pMatrix,\n // this.config.perspective.yfov*0.5,\n // asp,\n // this.config.perspective.znear,\n // this.config.perspective.zfar);\n\n cgl.pushViewMatrix();\n // mat4.identity(cgl.vMatrix);\n\n // if(this.node && this.node.parent)\n // {\n // console.log(this.node.parent)\n // vec3.add(this.pos,this.pos,this.node.parent._node.translation);\n // vec3.sub(this.vCenter,this.vCenter,this.node.parent._node.translation);\n // mat4.translate(cgl.vMatrix,cgl.vMatrix,\n // [\n // -this.node.parent._node.translation[0],\n // -this.node.parent._node.translation[1],\n // -this.node.parent._node.translation[2]\n // ])\n // }\n\n\n\n // vec3.set(this.vUp, 0, 1, 0);\n // vec3.set(this.vCenter, 0, -1, 0);\n // // vec3.set(this.vCenter, 0, 1, 0);\n // vec3.transformQuat(this.vCenter, this.vCenter, this.quat);\n // vec3.normalize(this.vCenter, this.vCenter);\n // vec3.add(this.vCenter, this.vCenter, this.pos);\n\n\n // mat4.lookAt(cgl.vMatrix, this.pos, this.vCenter, this.vUp);\n\n\n let mv=mat4.create();\n mat4.invert(mv,this.node.modelMatAbs());\n\n // console.log(this.node.modelMatAbs());\n\n this.vMat=mv;\n\n mat4.identity(cgl.vMatrix);\n // console.log(mv);\n mat4.mul(cgl.vMatrix,cgl.vMatrix,mv);\n\n\n }\n\n end()\n {\n if (cgl.frameStore.shadowPass) return;\n cgl.popPMatrix();\n cgl.popViewMatrix();\n }\n};\n\n","inc_gltf_js":"const CHUNK_HEADER_SIZE = 8;\n\nconst Gltf = class\n{\n constructor()\n {\n this.json = {};\n this.accBuffers = [];\n this.meshes = [];\n this.nodes = [];\n this.shaders = [];\n this.timing = [];\n this.cams = [];\n this.startTime = performance.now();\n this.bounds = new CABLES.CG.BoundingBox();\n this.loaded = Date.now();\n this.accBuffersDelete = [];\n }\n\n getNode(n)\n {\n for (let i = 0; i < this.nodes.length; i++)\n {\n if (this.nodes[i].name == n) return this.nodes[i];\n }\n }\n\n unHideAll()\n {\n for (let i = 0; i < this.nodes.length; i++)\n {\n this.nodes[i].unHide();\n }\n }\n};\n\nfunction Utf8ArrayToStr(array)\n{\n if (window.TextDecoder) return new TextDecoder(\"utf-8\").decode(array);\n\n let out, i, len, c;\n let char2, char3;\n\n out = \"\";\n len = array.length;\n i = 0;\n while (i < len)\n {\n c = array[i++];\n switch (c >> 4)\n {\n case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:\n // 0xxxxxxx\n out += String.fromCharCode(c);\n break;\n case 12: case 13:\n // 110x xxxx 10xx xxxx\n char2 = array[i++];\n out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));\n break;\n case 14:\n // 1110 xxxx 10xx xxxx 10xx xxxx\n char2 = array[i++];\n char3 = array[i++];\n out += String.fromCharCode(((c & 0x0F) << 12) |\n ((char2 & 0x3F) << 6) |\n ((char3 & 0x3F) << 0));\n break;\n }\n }\n\n return out;\n}\n\nfunction readChunk(dv, bArr, arrayBuffer, offset)\n{\n const chunk = {};\n\n if (offset >= dv.byteLength)\n {\n op.log(\"could not read chunk...\");\n return;\n }\n chunk.size = dv.getUint32(offset + 0, le);\n\n // chunk.type = new TextDecoder(\"utf-8\").decode(bArr.subarray(offset+4, offset+4+4));\n chunk.type = Utf8ArrayToStr(bArr.subarray(offset + 4, offset + 4 + 4));\n\n if (chunk.type == \"BIN\\0\")\n {\n // console.log(chunk.size,arrayBuffer.length,offset);\n // try\n // {\n chunk.dataView = new DataView(arrayBuffer, offset + 8, chunk.size);\n // }\n // catch(e)\n // {\n // chunk.dataView = null;\n // console.log(e);\n // }\n }\n else\n if (chunk.type == \"JSON\")\n {\n const json = Utf8ArrayToStr(bArr.subarray(offset + 8, offset + 8 + chunk.size));\n\n try\n {\n const obj = JSON.parse(json);\n chunk.data = obj;\n outGenerator.set(obj.asset.generator);\n }\n catch (e)\n {\n }\n }\n else\n {\n op.warn(\"unknown type\", chunk.type);\n }\n\n return chunk;\n}\n\nfunction loadAnims(gltf)\n{\n const uniqueAnimNames = {};\n\n for (let i = 0; i < gltf.json.animations.length; i++)\n {\n const an = gltf.json.animations[i];\n\n for (let ia = 0; ia < an.channels.length; ia++)\n {\n const chan = an.channels[ia];\n\n const node = gltf.nodes[chan.target.node];\n const sampler = an.samplers[chan.sampler];\n\n const acc = gltf.json.accessors[sampler.input];\n const bufferIn = gltf.accBuffers[sampler.input];\n\n const accOut = gltf.json.accessors[sampler.output];\n const bufferOut = gltf.accBuffers[sampler.output];\n\n gltf.accBuffersDelete.push(sampler.output, sampler.input);\n\n if (bufferIn && bufferOut)\n {\n let numComps = 1;\n if (accOut.type == \"VEC2\")numComps = 2;\n else if (accOut.type == \"VEC3\")numComps = 3;\n else if (accOut.type == \"VEC4\")numComps = 4;\n else op.warn(\"unknown accOut.type\", accOut.type);\n\n const anims = [];\n\n uniqueAnimNames[an.name] = true;\n\n for (let k = 0; k < numComps; k++)\n {\n const newAnim = new CABLES.TL.Anim();\n // newAnim.name=an.name;\n anims.push(newAnim);\n }\n\n if (sampler.interpolation == \"LINEAR\") {}\n else if (sampler.interpolation == \"STEP\") for (let k = 0; k < numComps; k++) anims[k].defaultEasing = CABLES.EASING_ABSOLUTE;\n else if (sampler.interpolation == \"CUBICSPLINE\") for (let k = 0; k < numComps; k++) anims[k].defaultEasing = CABLES.EASING_CUBICSPLINE;\n else op.warn(\"unknown interpolation\", sampler.interpolation);\n\n // console.log(bufferOut)\n\n // if there is no keyframe for time 0 copy value of first keyframe at time 0\n if (bufferIn[0] !== 0.0)\n for (let k = 0; k < numComps; k++)\n anims[k].setValue(0, bufferOut[0 * numComps + k]);\n\n // console.log(sampler.interpolation,bufferOut.length/numComps)\n\n for (let j = 0; j < bufferIn.length; j++)\n {\n maxTime = Math.max(bufferIn[j], maxTime);\n\n for (let k = 0; k < numComps; k++)\n {\n if (anims[k].defaultEasing === CABLES.EASING_CUBICSPLINE)\n {\n const idx = ((j * numComps) * 3 + k);\n\n const key = anims[k].setValue(bufferIn[j], bufferOut[idx + numComps]);\n key.bezTangIn = bufferOut[idx];\n key.bezTangOut = bufferOut[idx + (numComps * 2)];\n\n // console.log(an.name,k,bufferOut[idx+1]);\n }\n else\n {\n // console.log(an.name,k,bufferOut[j * numComps + k]);\n anims[k].setValue(bufferIn[j], bufferOut[j * numComps + k]);\n }\n }\n }\n\n node.setAnim(chan.target.path, an.name, anims);\n }\n else\n {\n op.warn(\"loadAmins bufferIn undefined \", bufferIn === undefined);\n op.warn(\"loadAmins bufferOut undefined \", bufferOut === undefined);\n op.warn(\"loadAmins \", sampler, accOut);\n op.warn(\"loadAmins num accBuffers\", gltf.accBuffers.length);\n op.warn(\"loadAmins num accessors\", gltf.json.accessors.length);\n }\n }\n }\n\n gltf.uniqueAnimNames = uniqueAnimNames;\n\n outAnims.set(null);\n outAnims.set(Object.keys(uniqueAnimNames));\n}\n\nfunction loadCams(gltf)\n{\n if (!gltf || !gltf.json.cameras) return;\n\n gltf.cameras = gltf.cameras || [];\n\n for (let i = 0; i < gltf.nodes.length; i++)\n {\n if (gltf.nodes[i].hasOwnProperty(\"camera\"))\n {\n const cam = new gltfCamera(gltf, gltf.nodes[i]);\n gltf.cameras.push(cam);\n }\n }\n}\n\nfunction loadAfterDraco()\n{\n if (!window.DracoDecoder)\n {\n setTimeout(() =>\n {\n loadAfterDraco();\n }, 100);\n }\n\n reloadSoon();\n}\n\nfunction parseGltf(arrayBuffer)\n{\n let j = 0, i = 0;\n\n const gltf = new Gltf();\n gltf.timing.push(\"Start parsing\", Math.round((performance.now() - gltf.startTime)));\n\n if (!arrayBuffer) return;\n const byteArray = new Uint8Array(arrayBuffer);\n let pos = 0;\n\n // var string = new TextDecoder(\"utf-8\").decode(byteArray.subarray(pos, 4));\n const string = Utf8ArrayToStr(byteArray.subarray(pos, 4));\n pos += 4;\n if (string != \"glTF\") return;\n\n gltf.timing.push(\"dataview\", Math.round((performance.now() - gltf.startTime)));\n\n const dv = new DataView(arrayBuffer);\n const version = dv.getUint32(pos, le);\n pos += 4;\n const size = dv.getUint32(pos, le);\n pos += 4;\n\n outVersion.set(version);\n\n const chunks = [];\n gltf.chunks = chunks;\n\n chunks.push(readChunk(dv, byteArray, arrayBuffer, pos));\n pos += chunks[0].size + CHUNK_HEADER_SIZE;\n gltf.json = chunks[0].data;\n outJson.set(gltf.json);\n outExtensions.set(gltf.json.extensionsUsed || []);\n\n let ch = readChunk(dv, byteArray, arrayBuffer, pos);\n while (ch)\n {\n chunks.push(ch);\n pos += ch.size + CHUNK_HEADER_SIZE;\n ch = readChunk(dv, byteArray, arrayBuffer, pos);\n }\n\n gltf.chunks = chunks;\n\n const views = chunks[0].data.bufferViews;\n const accessors = chunks[0].data.accessors;\n\n gltf.timing.push(\"Parse buffers\", Math.round((performance.now() - gltf.startTime)));\n\n if (gltf.json.extensionsUsed && gltf.json.extensionsUsed.indexOf(\"KHR_draco_mesh_compression\") > -1)\n {\n if (!window.DracoDecoder)\n {\n op.setUiError(\"gltfdraco\", \"GLTF draco compression lib not found / add draco op to your patch!\");\n\n loadAfterDraco();\n return gltf;\n }\n else\n {\n gltf.useDraco = true;\n }\n }\n\n op.setUiError(\"gltfdraco\", null);\n // let accPos = (view.byteOffset || 0) + (acc.byteOffset || 0);\n\n if (views)\n {\n for (i = 0; i < accessors.length; i++)\n {\n const acc = accessors[i];\n const view = views[acc.bufferView];\n\n let numComps = 0;\n if (acc.type == \"SCALAR\")numComps = 1;\n else if (acc.type == \"VEC2\")numComps = 2;\n else if (acc.type == \"VEC3\")numComps = 3;\n else if (acc.type == \"VEC4\")numComps = 4;\n else if (acc.type == \"MAT4\")numComps = 16;\n else console.error(\"unknown accessor type\", acc.type);\n\n // const decoder = new decoderModule.Decoder();\n // const decodedGeometry = decodeDracoData(data, decoder);\n // // Encode mesh\n // encodeMeshToFile(decodedGeometry, decoder);\n\n // decoderModule.destroy(decoder);\n // decoderModule.destroy(decodedGeometry);\n\n // 5120 (BYTE)\t1\n // 5121 (UNSIGNED_BYTE)\t1\n // 5122 (SHORT)\t2\n\n if (chunks[1].dataView)\n {\n if (view)\n {\n const num = acc.count * numComps;\n let accPos = (view.byteOffset || 0) + (acc.byteOffset || 0);\n let stride = view.byteStride || 0;\n let dataBuff = null;\n\n if (acc.componentType == 5126 || acc.componentType == 5125) // 4byte FLOAT or INT\n {\n stride = stride || 4;\n\n const isInt = acc.componentType == 5125;\n if (isInt)dataBuff = new Uint32Array(num);\n else dataBuff = new Float32Array(num);\n\n for (j = 0; j < num; j++)\n {\n if (isInt) dataBuff[j] = chunks[1].dataView.getUint32(accPos, le);\n else dataBuff[j] = chunks[1].dataView.getFloat32(accPos, le);\n\n if (stride != 4 && (j + 1) % numComps === 0)accPos += stride - (numComps * 4);\n accPos += 4;\n }\n }\n else if (acc.componentType == 5123) // UNSIGNED_SHORT\n {\n stride = stride || 2;\n\n dataBuff = new Uint16Array(num);\n\n for (j = 0; j < num; j++)\n {\n dataBuff[j] = chunks[1].dataView.getUint16(accPos, le);\n\n if (stride != 2 && (j + 1) % numComps === 0) accPos += stride - (numComps * 2);\n\n accPos += 2;\n }\n }\n else if (acc.componentType == 5121) // UNSIGNED_BYTE\n {\n stride = stride || 1;\n\n dataBuff = new Uint8Array(num);\n\n for (j = 0; j < num; j++)\n {\n dataBuff[j] = chunks[1].dataView.getUint8(accPos, le);\n\n if (stride != 1 && (j + 1) % numComps === 0) accPos += stride - (numComps * 1);\n\n accPos += 1;\n }\n }\n\n else\n {\n console.error(\"unknown component type\", acc.componentType);\n }\n\n gltf.accBuffers.push(dataBuff);\n }\n else\n {\n // console.log(\"has no dataview\");\n }\n }\n }\n }\n\n gltf.timing.push(\"Parse mesh groups\", Math.round((performance.now() - gltf.startTime)));\n\n gltf.json.meshes = gltf.json.meshes || [];\n\n if (gltf.json.meshes)\n {\n for (i = 0; i < gltf.json.meshes.length; i++)\n {\n const mesh = new gltfMeshGroup(gltf, gltf.json.meshes[i]);\n gltf.meshes.push(mesh);\n }\n }\n\n gltf.timing.push(\"Parse nodes\", Math.round((performance.now() - gltf.startTime)));\n\n for (i = 0; i < gltf.json.nodes.length; i++)\n {\n if (gltf.json.nodes[i].children)\n for (j = 0; j < gltf.json.nodes[i].children.length; j++)\n {\n gltf.json.nodes[gltf.json.nodes[i].children[j]].isChild = true;\n }\n }\n\n for (i = 0; i < gltf.json.nodes.length; i++)\n {\n const node = new gltfNode(gltf.json.nodes[i], gltf);\n gltf.nodes.push(node);\n }\n\n for (i = 0; i < gltf.nodes.length; i++)\n {\n const node = gltf.nodes[i];\n\n if (!node.children) continue;\n for (let j = 0; j < node.children.length; j++)\n {\n gltf.nodes[node.children[j]].parent = node;\n }\n }\n\n for (i = 0; i < gltf.nodes.length; i++)\n {\n gltf.nodes[i].initSkin();\n }\n\n needsMatUpdate = true;\n\n gltf.timing.push(\"load anims\", Math.round((performance.now() - gltf.startTime)));\n\n if (gltf.json.animations) loadAnims(gltf);\n\n gltf.timing.push(\"load cameras\", Math.round((performance.now() - gltf.startTime)));\n\n if (gltf.json.cameras) loadCams(gltf);\n\n gltf.timing.push(\"finished\", Math.round((performance.now() - gltf.startTime)));\n\n return gltf;\n}\n","inc_mesh_js":"let gltfMesh = class\n{\n constructor(name, prim, gltf, finished)\n {\n this.POINTS = 0;\n this.LINES = 1;\n this.LINE_LOOP = 2;\n this.LINE_STRIP = 3;\n this.TRIANGLES = 4;\n this.TRIANGLE_STRIP = 5;\n this.TRIANGLE_FAN = 6;\n\n this.test = 0;\n this.name = name;\n this.submeshIndex = 0;\n this.material = prim.material;\n this.mesh = null;\n this.geom = new CGL.Geometry(\"gltf_\" + this.name);\n this.geom.verticesIndices = [];\n this.bounds = null;\n this.primitive = 4;\n if (prim.hasOwnProperty(\"mode\")) this.primitive = prim.mode;\n\n if (prim.hasOwnProperty(\"indices\")) this.geom.verticesIndices = gltf.accBuffers[prim.indices];\n\n gltf.loadingMeshes = gltf.loadingMeshes || 0;\n gltf.loadingMeshes++;\n\n this.materialJson =\n this._matPbrMetalness =\n this._matPbrRoughness =\n this._matDiffuseColor = null;\n\n if (gltf.json.materials)\n {\n if (this.material != -1) this.materialJson = gltf.json.materials[this.material];\n\n if (this.materialJson && this.materialJson.pbrMetallicRoughness)\n {\n if (!this.materialJson.pbrMetallicRoughness.hasOwnProperty(\"baseColorFactor\"))\n {\n this._matDiffuseColor = [1, 1, 1, 1];\n }\n else\n {\n this._matDiffuseColor = this.materialJson.pbrMetallicRoughness.baseColorFactor;\n }\n\n this._matDiffuseColor = this.materialJson.pbrMetallicRoughness.baseColorFactor;\n\n if (!this.materialJson.pbrMetallicRoughness.hasOwnProperty(\"metallicFactor\"))\n {\n this._matPbrMetalness = 1.0;\n }\n else\n {\n this._matPbrMetalness = this.materialJson.pbrMetallicRoughness.metallicFactor || null;\n }\n\n if (!this.materialJson.pbrMetallicRoughness.hasOwnProperty(\"roughnessFactor\"))\n {\n this._matPbrRoughness = 1.0;\n }\n else\n {\n this._matPbrRoughness = this.materialJson.pbrMetallicRoughness.roughnessFactor || null;\n }\n }\n }\n\n if (gltf.useDraco && prim.extensions.KHR_draco_mesh_compression)\n {\n const view = gltf.chunks[0].data.bufferViews[prim.extensions.KHR_draco_mesh_compression.bufferView];\n const num = view.byteLength;\n const dataBuff = new Int8Array(num);\n let accPos = (view.byteOffset || 0);// + (acc.byteOffset || 0);\n for (let j = 0; j < num; j++)\n {\n dataBuff[j] = gltf.chunks[1].dataView.getInt8(accPos, le);\n accPos++;\n }\n\n const dracoDecoder = window.DracoDecoder;\n dracoDecoder.decodeGeometry(dataBuff.buffer, (geometry) =>\n {\n const geom = new CGL.Geometry(\"draco mesh \" + name);\n\n for (let i = 0; i < geometry.attributes.length; i++)\n {\n const attr = geometry.attributes[i];\n\n if (attr.name === \"position\") geom.vertices = attr.array;\n else if (attr.name === \"normal\") geom.vertexNormals = attr.array;\n else if (attr.name === \"uv\") geom.texCoords = attr.array;\n else if (attr.name === \"color\") geom.vertexColors = this.calcVertexColors(attr.array);\n else if (attr.name === \"joints\") geom.setAttribute(\"attrJoints\", Array.from(attr.array), 4);\n else if (attr.name === \"weights\")\n {\n const arr4 = new Float32Array(attr.array.length / attr.itemSize * 4);\n\n for (let k = 0; k < attr.array.length / attr.itemSize; k++)\n {\n arr4[k * 4] = arr4[k * 4 + 1] = arr4[k * 4 + 2] = arr4[k * 4 + 3] = 0;\n for (let j = 0; j < attr.itemSize; j++)\n arr4[k * 4 + j] = attr.array[k * attr.itemSize + j];\n }\n geom.setAttribute(\"attrWeights\", arr4, 4);\n }\n else op.logWarn(\"unknown draco attrib\", attr);\n }\n\n geometry.attributes = null;\n geom.verticesIndices = geometry.index.array;\n\n this.setGeom(geom);\n\n this.mesh = null;\n gltf.loadingMeshes--;\n if (finished)finished(this);\n }, (error) => { op.logError(error); });\n }\n else\n {\n gltf.loadingMeshes--;\n this.fillGeomAttribs(gltf, this.geom, prim.attributes);\n\n if (prim.targets)\n {\n console.log(\"prim.targets\", prim.targets.length);\n for (let j = 0; j < prim.targets.length; j++)\n {\n // var tgeom=new CGL.Geometry(\"gltf_\"+this.name);\n let tgeom = this.geom.copy();\n\n if (prim.hasOwnProperty(\"indices\")) tgeom.verticesIndices = gltf.accBuffers[prim.indices];\n\n this.fillGeomAttribs(gltf, tgeom, prim.targets[j], false);\n\n { // calculate normals for final position of morphtarget for later...\n for (let i = 0; i < tgeom.vertices.length; i++) tgeom.vertices[i] += this.geom.vertices[i];\n tgeom.calculateNormals();\n for (let i = 0; i < tgeom.vertices.length; i++) tgeom.vertices[i] -= this.geom.vertices[i];\n }\n\n this.geom.morphTargets.push(tgeom);\n }\n }\n if (finished)finished(this);\n }\n }\n\n _linearToSrgb(x)\n {\n if (x <= 0)\n return 0;\n else if (x >= 1)\n return 1;\n else if (x < 0.0031308)\n return x * 12.92;\n else\n return Math.pow(x, 1 / 2.2) * 1.055 - 0.055;\n }\n\n calcVertexColors(arr)\n {\n let vertexColors = null;\n if (arr instanceof Float32Array)\n {\n let div = false;\n for (let i = 0; i < arr.length; i++)\n {\n if (arr[i] > 1)\n {\n div = true;\n continue;\n }\n }\n\n if (div)\n for (let i = 0; i < arr.length; i++) arr[i] /= 65535;\n\n vertexColors = arr;\n }\n\n else if (arr instanceof Uint16Array)\n {\n const fb = new Float32Array(arr.length);\n for (let i = 0; i < arr.length; i++) fb[i] = arr[i] / 65535;\n\n vertexColors = fb;\n }\n else vertexColors = arr;\n\n for (let i = 0; i < vertexColors.length; i++)\n {\n vertexColors[i] = this._linearToSrgb(vertexColors[i]);\n }\n\n return vertexColors;\n }\n\n fillGeomAttribs(gltf, tgeom, attribs, setGeom)\n {\n if (attribs.hasOwnProperty(\"POSITION\")) tgeom.vertices = gltf.accBuffers[attribs.POSITION];\n if (attribs.hasOwnProperty(\"NORMAL\")) tgeom.vertexNormals = gltf.accBuffers[attribs.NORMAL];\n if (attribs.hasOwnProperty(\"TANGENT\")) tgeom.tangents = gltf.accBuffers[attribs.TANGENT];\n\n if (attribs.hasOwnProperty(\"COLOR_0\")) tgeom.vertexColors = this.calcVertexColors(gltf.accBuffers[attribs.COLOR_0]);\n if (attribs.hasOwnProperty(\"COLOR_1\")) tgeom.setAttribute(\"attrVertColor1\", this.calcVertexColors(gltf.accBuffers[attribs.COLOR_1]), 4);\n if (attribs.hasOwnProperty(\"COLOR_2\")) tgeom.setAttribute(\"attrVertColor2\", this.calcVertexColors(gltf.accBuffers[attribs.COLOR_2]), 4);\n if (attribs.hasOwnProperty(\"COLOR_3\")) tgeom.setAttribute(\"attrVertColor3\", this.calcVertexColors(gltf.accBuffers[attribs.COLOR_3]), 4);\n if (attribs.hasOwnProperty(\"COLOR_4\")) tgeom.setAttribute(\"attrVertColor4\", this.calcVertexColors(gltf.accBuffers[attribs.COLOR_4]), 4);\n\n if (attribs.hasOwnProperty(\"TEXCOORD_0\"))tgeom.texCoords = gltf.accBuffers[attribs.TEXCOORD_0];\n if (attribs.hasOwnProperty(\"TEXCOORD_1\"))tgeom.setAttribute(\"attrTexCoord1\", gltf.accBuffers[attribs.TEXCOORD_1], 2);\n if (attribs.hasOwnProperty(\"TEXCOORD_2\"))tgeom.setAttribute(\"attrTexCoord2\", gltf.accBuffers[attribs.TEXCOORD_2], 2);\n if (attribs.hasOwnProperty(\"TEXCOORD_3\"))tgeom.setAttribute(\"attrTexCoord3\", gltf.accBuffers[attribs.TEXCOORD_3], 2);\n if (attribs.hasOwnProperty(\"TEXCOORD_4\"))tgeom.setAttribute(\"attrTexCoord4\", gltf.accBuffers[attribs.TEXCOORD_4], 2);\n\n if (attribs.hasOwnProperty(\"WEIGHTS_0\"))\n {\n tgeom.setAttribute(\"attrWeights\", gltf.accBuffers[attribs.WEIGHTS_0], 4);\n }\n if (attribs.hasOwnProperty(\"JOINTS_0\"))\n {\n if (!gltf.accBuffers[attribs.JOINTS_0])console.log(\"no !gltf.accBuffers[attribs.JOINTS_0]\");\n tgeom.setAttribute(\"attrJoints\", gltf.accBuffers[attribs.JOINTS_0], 4);\n }\n\n if (attribs.hasOwnProperty(\"POSITION\")) gltf.accBuffersDelete.push(attribs.POSITION);\n if (attribs.hasOwnProperty(\"NORMAL\")) gltf.accBuffersDelete.push(attribs.NORMAL);\n if (attribs.hasOwnProperty(\"TEXCOORD_0\")) gltf.accBuffersDelete.push(attribs.TEXCOORD_0);\n if (attribs.hasOwnProperty(\"TANGENT\")) gltf.accBuffersDelete.push(attribs.TANGENT);\n if (attribs.hasOwnProperty(\"COLOR_0\"))gltf.accBuffersDelete.push(attribs.COLOR_0);\n if (attribs.hasOwnProperty(\"COLOR_0\"))gltf.accBuffersDelete.push(attribs.COLOR_0);\n if (attribs.hasOwnProperty(\"COLOR_1\"))gltf.accBuffersDelete.push(attribs.COLOR_1);\n if (attribs.hasOwnProperty(\"COLOR_2\"))gltf.accBuffersDelete.push(attribs.COLOR_2);\n if (attribs.hasOwnProperty(\"COLOR_3\"))gltf.accBuffersDelete.push(attribs.COLOR_3);\n\n if (attribs.hasOwnProperty(\"TEXCOORD_1\")) gltf.accBuffersDelete.push(attribs.TEXCOORD_1);\n if (attribs.hasOwnProperty(\"TEXCOORD_2\")) gltf.accBuffersDelete.push(attribs.TEXCOORD_2);\n if (attribs.hasOwnProperty(\"TEXCOORD_3\")) gltf.accBuffersDelete.push(attribs.TEXCOORD_3);\n if (attribs.hasOwnProperty(\"TEXCOORD_4\")) gltf.accBuffersDelete.push(attribs.TEXCOORD_4);\n\n if (setGeom !== false) if (tgeom && tgeom.verticesIndices) this.setGeom(tgeom);\n }\n\n setGeom(geom)\n {\n if (inNormFormat.get() == \"X-ZY\")\n {\n for (let i = 0; i < geom.vertexNormals.length; i += 3)\n {\n let t = geom.vertexNormals[i + 2];\n geom.vertexNormals[i + 2] = geom.vertexNormals[i + 1];\n geom.vertexNormals[i + 1] = -t;\n }\n }\n\n if (inVertFormat.get() == \"XZ-Y\")\n {\n for (let i = 0; i < geom.vertices.length; i += 3)\n {\n let t = geom.vertices[i + 2];\n geom.vertices[i + 2] = -geom.vertices[i + 1];\n geom.vertices[i + 1] = t;\n }\n }\n\n if (this.primitive == this.TRIANGLES)\n {\n if (!geom.vertexNormals.length || inCalcNormals.get()) geom.calculateNormals();\n\n if ((!geom.biTangents || geom.biTangents.length == 0) && geom.tangents)\n {\n const bitan = vec3.create();\n const tan = vec3.create();\n\n const tangents = geom.tangents;\n geom.tangents = new Float32Array(tangents.length / 4 * 3);\n geom.biTangents = new Float32Array(tangents.length / 4 * 3);\n\n for (let i = 0; i < tangents.length; i += 4)\n {\n const idx = i / 4 * 3;\n\n vec3.cross(\n bitan,\n [geom.vertexNormals[idx], geom.vertexNormals[idx + 1], geom.vertexNormals[idx + 2]],\n [tangents[i], tangents[i + 1], tangents[i + 2]]\n );\n\n vec3.div(bitan, bitan, [tangents[i + 3], tangents[i + 3], tangents[i + 3]]);\n vec3.normalize(bitan, bitan);\n\n geom.biTangents[idx + 0] = bitan[0];\n geom.biTangents[idx + 1] = bitan[1];\n geom.biTangents[idx + 2] = bitan[2];\n\n geom.tangents[idx + 0] = tangents[i + 0];\n geom.tangents[idx + 1] = tangents[i + 1];\n geom.tangents[idx + 2] = tangents[i + 2];\n }\n }\n\n if (geom.tangents.length === 0 || inCalcNormals.get()) geom.calcTangentsBitangents();\n }\n\n this.geom = geom;\n\n this.bounds = geom.getBounds();\n }\n\n render(cgl, ignoreMaterial, skinRenderer)\n {\n if (!this.mesh && this.geom && this.geom.verticesIndices)\n {\n let g = this.geom;\n if (this.geom.vertices.length / 3 > 64000)\n {\n g = this.geom.copy();\n g.unIndex(false, true);\n }\n\n let glprim;\n if (this.primitive == this.TRIANGLES)glprim = cgl.gl.TRIANGLES;\n else if (this.primitive == this.LINES)glprim = cgl.gl.LINES;\n else if (this.primitive == this.LINE_STRIP)glprim = cgl.gl.LINE_STRIP;\n else if (this.primitive == this.POINTS)glprim = cgl.gl.POINTS;\n else\n {\n op.logWarn(\"unknown primitive type\", this);\n }\n\n this.mesh = new CGL.Mesh(cgl, g, glprim);\n // this.mesh._geom = null;\n }\n else\n {\n // update morphTargets\n if (this.geom && this.geom.morphTargets.length)\n {\n this.morphGeom = this.geom.copy();\n\n this.test = time * 11.7;\n\n if (this.test >= this.geom.morphTargets.length - 1) this.test = 0;\n\n const mt = this.geom.morphTargets[Math.floor(this.test)];\n const mt2 = this.geom.morphTargets[Math.floor(this.test + 1)];\n\n if (mt && mt.vertices && mt2)\n {\n if (this.morphGeom.vertexNormals.length != mt.vertexNormals.length)\n this.morphGeom.vertexNormals = new Float32Array(mt.vertexNormals.length);\n\n const fract = this.test % 1;\n for (let i = 0; i < this.morphGeom.vertices.length; i++)\n {\n this.morphGeom.vertices[i] =\n this.geom.vertices[i] +\n (1.0 - fract) * mt.vertices[i] +\n fract * mt2.vertices[i];\n\n this.morphGeom.vertexNormals[i] =\n (1.0 - fract) * mt.vertexNormals[i] +\n fract * mt2.vertexNormals[i];\n }\n\n this.mesh.updateNormals(this.morphGeom);\n this.mesh.updateVertices(this.morphGeom);\n }\n }\n\n let useMat = !ignoreMaterial && this.material != -1 && gltf.shaders[this.material];\n if (skinRenderer)useMat = false;\n\n if (useMat) cgl.pushShader(gltf.shaders[this.material]);\n\n const currentShader = cgl.getShader() || {};\n const uniDiff = currentShader.uniformColorDiffuse;\n\n const uniPbrMetalness = currentShader.uniformPbrMetalness;\n const uniPbrRoughness = currentShader.uniformPbrRoughness;\n\n if (!gltf.shaders[this.material] && inUseMatProps.get())\n {\n if (uniDiff && this._matDiffuseColor)\n {\n this._matDiffuseColorOrig = [uniDiff.getValue()[0], uniDiff.getValue()[1], uniDiff.getValue()[2], uniDiff.getValue()[3]];\n uniDiff.setValue(this._matDiffuseColor);\n }\n\n if (uniPbrMetalness)\n if (this._matPbrMetalness != null)\n {\n this._matPbrMetalnessOrig = uniPbrMetalness.getValue();\n uniPbrMetalness.setValue(this._matPbrMetalness);\n }\n else\n uniPbrMetalness.setValue(0);\n\n if (uniPbrRoughness)\n if (this._matPbrRoughness != null)\n {\n this._matPbrRoughnessOrig = uniPbrRoughness.getValue();\n uniPbrRoughness.setValue(this._matPbrRoughness);\n }\n else\n {\n uniPbrRoughness.setValue(0);\n }\n }\n\n if (this.mesh) this.mesh.render(cgl.getShader(), ignoreMaterial);\n\n if (inUseMatProps.get())\n {\n if (uniDiff && this._matDiffuseColor) uniDiff.setValue(this._matDiffuseColorOrig);\n if (uniPbrMetalness && this._matPbrMetalnessOrig != undefined) uniPbrMetalness.setValue(this._matPbrMetalnessOrig);\n if (uniPbrRoughness && this._matPbrRoughnessOrig != undefined) uniPbrRoughness.setValue(this._matPbrRoughnessOrig);\n }\n\n if (useMat) cgl.popShader();\n }\n }\n};\n","inc_meshGroup_js":"const gltfMeshGroup = class\n{\n constructor(gltf, m)\n {\n this.bounds = new CABLES.CG.BoundingBox();\n this.meshes = [];\n this.name = m.name;\n const prims = m.primitives;\n\n for (let i = 0; i < prims.length; i++)\n {\n const mesh = new gltfMesh(this.name, prims[i], gltf,\n (mesh) =>\n {\n this.bounds.apply(mesh.bounds);\n });\n\n mesh.submeshIndex = i;\n this.meshes.push(mesh);\n }\n }\n\n render(cgl, ignoreMat, skinRenderer, _time)\n {\n for (let i = 0; i < this.meshes.length; i++)\n {\n const useMat = gltf.shaders[this.meshes[i].material];\n\n if (!ignoreMat && useMat) cgl.pushShader(gltf.shaders[this.meshes[i].material]);\n // console.log(gltf.shaders[this.meshes[i].material],this.meshes[i].material)\n if (skinRenderer)skinRenderer.renderStart(cgl, _time);\n\n this.meshes[i].render(cgl, ignoreMat, skinRenderer, _time);\n if (skinRenderer)skinRenderer.renderFinish(cgl);\n if (!ignoreMat && useMat) cgl.popShader();\n }\n }\n};\n","inc_node_js":"const gltfNode = class\n{\n constructor(node, gltf)\n {\n this.isChild = node.isChild || false;\n this.name = node.name;\n if (node.hasOwnProperty(\"camera\")) this.camera = node.camera;\n this.hidden = false;\n this.mat = mat4.create();\n this._animMat = mat4.create();\n this._tempMat = mat4.create();\n this._tempQuat = quat.create();\n this._tempRotmat = mat4.create();\n this.mesh = null;\n this.children = [];\n this._node = node;\n this._gltf = gltf;\n this.absMat = mat4.create();\n this.addTranslate = null;\n this._tempAnimScale = null;\n this.addMulMat = null;\n this.updateMatrix();\n this._animActions = {};\n this.skinRenderer = null;\n this.copies = [];\n }\n\n get skin()\n {\n if (this._node.hasOwnProperty(\"skin\")) return this._node.skin;\n else return -1;\n }\n\n copy()\n {\n this.isCopy = true;\n const n = new gltfNode(this._node, this._gltf);\n n.copyOf = this;\n\n n._animActions = this._animActions;\n n.children = this.children;\n n.skinRenderer = new GltfSkin(n);\n\n this.updateMatrix();\n return n;\n }\n\n hasSkin()\n {\n if (this._node.hasOwnProperty(\"skin\")) return this._gltf.json.skins[this._node.skin].name || \"unknown\";\n return false;\n }\n\n initSkin()\n {\n if (this.skin > -1)\n {\n this.skinRenderer = new GltfSkin(this);\n }\n }\n\n updateMatrix()\n {\n mat4.identity(this.mat);\n if (this._node.translation) mat4.translate(this.mat, this.mat, this._node.translation);\n\n if (this._node.rotation)\n {\n const rotmat = mat4.create();\n this._rot = this._node.rotation;\n\n mat4.fromQuat(rotmat, this._node.rotation);\n mat4.mul(this.mat, this.mat, rotmat);\n }\n\n if (this._node.scale)\n {\n this._scale = this._node.scale;\n mat4.scale(this.mat, this.mat, this._scale);\n }\n\n if (this._node.hasOwnProperty(\"mesh\"))\n {\n this.mesh = this._gltf.meshes[this._node.mesh];\n if (this.isCopy)\n {\n console.log(this.mesh);\n }\n }\n\n if (this._node.children)\n {\n for (let i = 0; i < this._node.children.length; i++)\n {\n this._gltf.json.nodes[i].isChild = true;\n if (this._gltf.nodes[this._node.children[i]]) this._gltf.nodes[this._node.children[i]].isChild = true;\n this.children.push(this._node.children[i]);\n }\n }\n }\n\n unHide()\n {\n this.hidden = false;\n for (let i = 0; i < this.children.length; i++)\n if (this.children[i].unHide) this.children[i].unHide();\n }\n\n calcBounds(gltf, mat, bounds)\n {\n const localMat = mat4.create();\n\n if (mat) mat4.copy(localMat, mat);\n if (this.mat) mat4.mul(localMat, localMat, this.mat);\n\n if (this.mesh)\n {\n const bb = this.mesh.bounds.copy();\n bb.mulMat4(localMat);\n bounds.apply(bb);\n\n if (bounds.changed)\n {\n boundingPoints.push(\n bb._min[0] || 0, bb._min[1] || 0, bb._min[2] || 0,\n bb._max[0] || 0, bb._max[1] || 0, bb._max[2] || 0);\n }\n }\n\n for (let i = 0; i < this.children.length; i++)\n {\n if (gltf.nodes[this.children[i]] && gltf.nodes[this.children[i]].calcBounds)\n {\n const b = gltf.nodes[this.children[i]].calcBounds(gltf, localMat, bounds);\n\n bounds.apply(b);\n }\n }\n\n if (bounds.changed) return bounds;\n else return null;\n }\n\n setAnimAction(name)\n {\n // console.log(\"setAnimAction:\", name);\n if (!name) return;\n\n this._currentAnimaction = name;\n\n if (name && !this._animActions[name])\n {\n // console.log(\"no action found:\", name,this._animActions);\n return null;\n }\n\n // else console.log(\"YES action found:\", name);\n // console.log(this._animActions);\n\n for (let path in this._animActions[name])\n {\n if (path == \"translation\") this._animTrans = this._animActions[name][path];\n else if (path == \"rotation\") this._animRot = this._animActions[name][path];\n else if (path == \"scale\") this._animScale = this._animActions[name][path];\n else console.warn(\"unknown anim path\", path, this._animActions[name][path]);\n }\n }\n\n setAnim(path, name, anims)\n {\n if (!path || !name || !anims) return;\n\n // console.log(\"setanim\", this._node.name, path, name, anims);\n\n this._animActions[name] = this._animActions[name] || {};\n\n // console.log(this._animActions);\n // debugger;\n\n // for (let i = 0; i < this.copies.length; i++) this.copies[i]._animActions = this._animActions;\n\n if (this._animActions[name][path]) op.warn(\"animation action path already exists\", name, path, this._animActions[name][path]);\n\n this._animActions[name][path] = anims;\n\n if (path == \"translation\") this._animTrans = anims;\n else if (path == \"rotation\") this._animRot = anims;\n else if (path == \"scale\") this._animScale = anims;\n else console.warn(\"unknown anim path\", path, anims);\n }\n\n modelMatLocal()\n {\n return this._animMat || this.mat;\n }\n\n modelMatAbs()\n {\n return this.absMat;\n }\n\n transform(cgl, _time)\n {\n if (!_time && _time != 0)_time = time;\n\n this._lastTimeTrans = _time;\n\n // console.log(this._rot)\n\n gltfTransforms++;\n\n if (!this._animTrans && !this._animRot && !this._animScale)\n {\n mat4.mul(cgl.mMatrix, cgl.mMatrix, this.mat);\n this._animMat = null;\n }\n else\n {\n this._animMat = this._animMat || mat4.create();\n mat4.identity(this._animMat);\n\n const playAnims = true;\n\n if (playAnims && this._animTrans)\n {\n mat4.translate(this._animMat, this._animMat, [\n this._animTrans[0].getValue(_time),\n this._animTrans[1].getValue(_time),\n this._animTrans[2].getValue(_time)]);\n }\n else\n if (this._node.translation) mat4.translate(this._animMat, this._animMat, this._node.translation);\n\n if (playAnims && this._animRot)\n {\n if (this._animRot[0].defaultEasing == CABLES.EASING_LINEAR) CABLES.TL.Anim.slerpQuaternion(_time, this._tempQuat, this._animRot[0], this._animRot[1], this._animRot[2], this._animRot[3]);\n else if (this._animRot[0].defaultEasing == CABLES.EASING_ABSOLUTE)\n {\n this._tempQuat[0] = this._animRot[0].getValue(_time);\n this._tempQuat[1] = this._animRot[1].getValue(_time);\n this._tempQuat[2] = this._animRot[2].getValue(_time);\n this._tempQuat[3] = this._animRot[3].getValue(_time);\n }\n else if (this._animRot[0].defaultEasing == CABLES.EASING_CUBICSPLINE)\n {\n CABLES.TL.Anim.slerpQuaternion(_time, this._tempQuat, this._animRot[0], this._animRot[1], this._animRot[2], this._animRot[3]);\n }\n\n mat4.fromQuat(this._tempMat, this._tempQuat);\n mat4.mul(this._animMat, this._animMat, this._tempMat);\n }\n else if (this._rot)\n {\n mat4.fromQuat(this._tempRotmat, this._rot);\n mat4.mul(this._animMat, this._animMat, this._tempRotmat);\n }\n\n if (playAnims && this._animScale)\n {\n if (!this._tempAnimScale) this._tempAnimScale = [1, 1, 1];\n this._tempAnimScale[0] = this._animScale[0].getValue(_time);\n this._tempAnimScale[1] = this._animScale[1].getValue(_time);\n this._tempAnimScale[2] = this._animScale[2].getValue(_time);\n mat4.scale(this._animMat, this._animMat, this._tempAnimScale);\n }\n else if (this._scale) mat4.scale(this._animMat, this._animMat, this._scale);\n\n mat4.mul(cgl.mMatrix, cgl.mMatrix, this._animMat);\n }\n\n if (this.addTranslate) mat4.translate(cgl.mMatrix, cgl.mMatrix, this.addTranslate);\n\n if (this.addMulMat) mat4.mul(cgl.mMatrix, cgl.mMatrix, this.addMulMat);\n\n mat4.copy(this.absMat, cgl.mMatrix);\n }\n\n render(cgl, dontTransform, dontDrawMesh, ignoreMaterial, ignoreChilds, drawHidden, _time)\n {\n if (!dontTransform) cgl.pushModelMatrix();\n\n if (_time === undefined) _time = gltf.time;\n\n if (!dontTransform || this.skinRenderer) this.transform(cgl, _time);\n\n if (this.hidden && !drawHidden)\n {\n }\n else\n {\n if (this.skinRenderer)\n {\n this.skinRenderer.time = _time;\n if (!dontDrawMesh)\n this.mesh.render(cgl, ignoreMaterial, this.skinRenderer, _time);\n }\n else\n {\n if (this.mesh && !dontDrawMesh)\n this.mesh.render(cgl, ignoreMaterial, null, _time);\n }\n }\n\n if (!ignoreChilds && !this.hidden)\n for (let i = 0; i < this.children.length; i++)\n if (gltf.nodes[this.children[i]])\n gltf.nodes[this.children[i]].render(cgl, dontTransform, dontDrawMesh, ignoreMaterial, ignoreChilds, drawHidden, _time);\n\n if (!dontTransform)cgl.popModelMatrix();\n }\n};\n","inc_print_js":"let tab = null;\n\nfunction closeTab()\n{\n if (tab)gui.mainTabs.closeTab(tab.id);\n tab = null;\n}\n\nfunction formatVec(arr)\n{\n const nums = [];\n for (let i = 0; i < arr.length; i++)\n {\n nums.push(Math.round(arr[i] * 1000) / 1000);\n }\n\n return nums.join(\",\");\n}\n\nfunction printNode(html, node, level)\n{\n if (!gltf) return;\n\n html += \"\";\n\n let ident = \"\";\n let identSpace = \"\";\n\n for (let i = 1; i < level; i++)\n {\n identSpace += \"   \";\n let identClass = \"identBg\";\n if (i == 1)identClass = \"identBgLevel0\";\n ident += \"
    \";\n }\n let id = CABLES.uuid();\n html += ident;\n html += \"\";\n // html+='
    ';\n\n if (node.mesh && node.mesh.meshes.length)html += \" \";\n else html += \"  \";\n\n html += node.name + \"\";\n\n if (node.mesh)\n {\n html += \"\";\n for (let i = 0; i < node.mesh.meshes.length; i++)\n {\n if (i > 0)html += \", \";\n html += node.mesh.meshes[i].name;\n }\n\n html += \"\";\n\n html += \"\";\n html += node.hasSkin() || \"-\";\n html += \"\";\n\n html += \"\";\n let countMats = 0;\n for (let i = 0; i < node.mesh.meshes.length; i++)\n {\n if (countMats > 0)html += \", \";\n if (gltf.json.materials && node.mesh.meshes[i].hasOwnProperty(\"material\"))\n {\n if (gltf.json.materials[node.mesh.meshes[i].material])\n {\n html += gltf.json.materials[node.mesh.meshes[i].material].name;\n countMats++;\n }\n }\n }\n if (countMats == 0)html += \"none\";\n html += \"\";\n }\n else\n {\n html += \"---\";\n }\n\n html += \"\";\n\n if (node._node.translation || node._node.rotation || node._node.scale)\n {\n let info = \"\";\n\n if (node._node.translation)info += \"Translate: `\" + formatVec(node._node.translation) + \"` || \";\n if (node._node.rotation)info += \"Rotation: `\" + formatVec(node._node.rotation) + \"` || \";\n if (node._node.scale)info += \"Scale: `\" + formatVec(node._node.scale) + \"` || \";\n\n html += \"  \";\n }\n\n if (node._animRot || node._animScale || node._animTrans)\n {\n let info = \"Animated: \";\n if (node._animRot) info += \"Rot \";\n if (node._animScale) info += \"Scale \";\n if (node._animTrans) info += \"Trans \";\n\n html += \" \";\n }\n\n if (!node._node.translation && !node._node.rotation && !node._node.scale && !node._animRot && !node._animScale && !node._animTrans) html += \"-\";\n\n html += \"\";\n\n html += \"\";\n let hideclass = \"\";\n if (node.hidden)hideclass = \"node-hidden\";\n\n // html+='';\n html += \"Transform\";\n html += \" Hierarchy\";\n html += \" Node\";\n\n if (node.hasSkin())\n html += \" Skin\";\n\n html += \"\";\n html += \" \";\n html += \"\";\n\n html += \"\";\n\n if (node.children)\n {\n for (let i = 0; i < node.children.length; i++)\n html = printNode(html, gltf.nodes[node.children[i]], level + 1);\n }\n\n return html;\n}\n\nfunction printMaterial(mat, idx)\n{\n let html = \"\";\n html += \" \" + idx + \"\";\n html += \" \" + mat.name + \"\";\n // html+=' Assign';\n\n html += \" \";\n\n const info = JSON.stringify(mat, null, 4).replaceAll(\"\\\"\", \"\").replaceAll(\"\\n\", \"
    \");\n\n html += \"\" + info + \"', 'title': '\" + mat.name + \"' });\\\"> \";\n\n if (mat.pbrMetallicRoughness && mat.pbrMetallicRoughness.baseColorFactor)\n {\n let rgb = \"\";\n rgb += \"\" + Math.round(mat.pbrMetallicRoughness.baseColorFactor[0] * 255);\n rgb += \",\" + Math.round(mat.pbrMetallicRoughness.baseColorFactor[1] * 255);\n rgb += \",\" + Math.round(mat.pbrMetallicRoughness.baseColorFactor[2] * 255);\n\n html += \"
     \";\n }\n html += \" \" + (gltf.shaders[idx] ? \"-\" : \"Assign\") + \"\";\n html += \"\";\n\n html += \"\";\n return html;\n}\n\nfunction printInfo()\n{\n if (!gltf) return;\n\n const startTime = performance.now();\n const sizes = {};\n let html = \"
    \";\n\n html += \"generator:\" + gltf.json.asset.generator;\n\n let numNodes = 0;\n if (gltf.json.nodes)numNodes = gltf.json.nodes.length;\n html += \"
    Nodes (\" + numNodes + \")
    \";\n\n html += \"\";\n\n html += \"\";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \"\";\n\n for (let i = 0; i < gltf.nodes.length; i++)\n {\n if (!gltf.nodes[i].isChild)\n html = printNode(html, gltf.nodes[i], 1);\n }\n html += \"
    NameMeshSkinMaterialTransformExpose
    \";\n\n // / //////////////////\n\n let numMaterials = 0;\n if (gltf.json.materials)numMaterials = gltf.json.materials.length;\n html += \"
    Materials (\" + numMaterials + \")
    \";\n\n if (!gltf.json.materials || gltf.json.materials.length == 0)\n {\n }\n else\n {\n html += \"\";\n html += \"\";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \"\";\n for (let i = 0; i < gltf.json.materials.length; i++)\n {\n html += printMaterial(gltf.json.materials[i], i);\n }\n html += \"
    IndexNameColorFunction
    \";\n }\n\n // / ///////////////////////\n\n html += \"
    Meshes (\" + gltf.json.meshes.length + \")
    \";\n\n html += \"\";\n html += \"\";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \"\";\n\n let sizeBufferViews = [];\n sizes.meshes = 0;\n\n for (let i = 0; i < gltf.json.meshes.length; i++)\n {\n html += \"\";\n html += \"\";\n\n html += \"\";\n\n // -------\n\n html += \"\";\n\n html += \"\";\n\n html += \"\";\n html += \"\";\n\n for (let j = 0; j < gltf.json.meshes[i].primitives.length; j++)\n {\n const accessor = gltf.json.accessors[gltf.json.meshes[i].primitives[j].indices];\n if (accessor)\n {\n let bufView = accessor.bufferView;\n\n if (sizeBufferViews.indexOf(bufView) == -1)\n {\n sizeBufferViews.push(bufView);\n if (gltf.json.bufferViews[bufView])sizes.meshes += gltf.json.bufferViews[bufView].byteLength;\n }\n }\n\n for (let k in gltf.json.meshes[i].primitives[j].attributes)\n {\n const attr = gltf.json.meshes[i].primitives[j].attributes[k];\n const bufView2 = gltf.json.accessors[attr].bufferView;\n\n if (sizeBufferViews.indexOf(bufView2) == -1)\n {\n sizeBufferViews.push(bufView2);\n if (gltf.json.bufferViews[bufView2])sizes.meshes += gltf.json.bufferViews[bufView2].byteLength;\n }\n }\n }\n }\n html += \"
    NameNodeMaterialVerticesAttributes
    \" + gltf.json.meshes[i].name + \"\";\n let count = 0;\n let nodename = \"\";\n for (var j = 0; j < gltf.json.nodes.length; j++)\n {\n if (gltf.json.nodes[j].mesh == i)\n {\n count++;\n if (count == 1)\n {\n nodename = gltf.json.nodes[j].name;\n }\n }\n }\n if (count > 1) html += (count) + \" nodes (\" + nodename + \" ...)\";\n else html += nodename;\n html += \"\";\n for (var j = 0; j < gltf.json.meshes[i].primitives.length; j++)\n {\n if (gltf.json.meshes[i].primitives[j].hasOwnProperty(\"material\"))\n {\n if (gltf.json.materials[gltf.json.meshes[i]])\n {\n html += gltf.json.materials[gltf.json.meshes[i].primitives[j].material].name + \" \";\n }\n }\n else html += \"None\";\n }\n html += \"\";\n let numVerts = 0;\n for (var j = 0; j < gltf.json.meshes[i].primitives.length; j++)\n {\n if (gltf.json.meshes[i].primitives[j].attributes.POSITION != undefined)\n {\n let v = parseInt(gltf.json.accessors[gltf.json.meshes[i].primitives[j].attributes.POSITION].count);\n numVerts += v;\n html += \"\" + v + \"
    \";\n }\n else html += \"-
    \";\n }\n\n if (gltf.json.meshes[i].primitives.length > 0)\n html += \"=\" + numVerts;\n html += \"
    \";\n for (let j = 0; j < gltf.json.meshes[i].primitives.length; j++)\n {\n html += Object.keys(gltf.json.meshes[i].primitives[j].attributes);\n html += \" Geometry\";\n html += \"
    \";\n }\n html += \"
    \";\n\n // / //////////////////////////////////\n\n let numAnims = 0;\n if (gltf.json.animations)numAnims = gltf.json.animations.length;\n html += \"
    Animations (\" + numAnims + \")
    \";\n\n if (gltf.json.animations)\n {\n html += \"\";\n html += \"\";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \"\";\n\n sizes.animations = 0;\n\n for (let i = 0; i < gltf.json.animations.length; i++)\n {\n for (let j = 0; j < gltf.json.animations[i].samplers.length; j++)\n {\n let bufView = gltf.json.accessors[gltf.json.animations[i].samplers[j].input].bufferView;\n if (sizeBufferViews.indexOf(bufView) == -1)\n {\n sizeBufferViews.push(bufView);\n sizes.animations += gltf.json.bufferViews[bufView].byteLength;\n }\n\n bufView = gltf.json.accessors[gltf.json.animations[i].samplers[j].output].bufferView;\n if (sizeBufferViews.indexOf(bufView) == -1)\n {\n sizeBufferViews.push(bufView);\n sizes.animations += gltf.json.bufferViews[bufView].byteLength;\n }\n }\n\n for (let j = 0; j < gltf.json.animations[i].channels.length; j++)\n {\n html += \"\";\n html += \" \";\n\n html += \" \";\n html += \" \";\n\n const smplidx = gltf.json.animations[i].channels[j].sampler;\n const smplr = gltf.json.animations[i].samplers[smplidx];\n\n html += \" \";\n\n html += \" \";\n\n html += \"\";\n }\n }\n html += \"
    NameTarget nodePathInterpolationKeys
    Anim \" + i + \": \" + gltf.json.animations[i].name + \"\" + gltf.nodes[gltf.json.animations[i].channels[j].target.node].name + \"\";\n html += gltf.json.animations[i].channels[j].target.path + \" \";\n html += \" \" + smplr.interpolation + \"\" + gltf.json.accessors[smplr.output].count;\n\n html += \"  \";\n\n html += \"
    \";\n }\n else\n {\n\n }\n\n // / ///////////////////\n\n let numImages = 0;\n if (gltf.json.images)numImages = gltf.json.images.length;\n html += \"
    Images (\" + numImages + \")
    \";\n\n if (gltf.json.images)\n {\n html += \"\";\n\n html += \"\";\n html += \" \";\n html += \" \";\n html += \" \";\n\n html += \"\";\n\n sizes.images = 0;\n\n for (let i = 0; i < gltf.json.images.length; i++)\n {\n if (gltf.json.images[i].bufferView)\n sizes.images += gltf.json.bufferViews[gltf.json.images[i].bufferView].byteLength;\n\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n\n html += \"\";\n }\n html += \"
    nametypefunc
    \" + gltf.json.images[i].name + \"\" + gltf.json.images[i].mimeType + \"\";\n\n let name = gltf.json.images[i].name;\n if (name === undefined)name = gltf.json.images[i].bufferView;\n\n html += \"Expose\";\n html += \"
    \";\n }\n\n // / ///////////////////////\n\n let numCameras = 0;\n if (gltf.json.cameras)numCameras = gltf.json.cameras.length;\n html += \"
    Cameras (\" + numCameras + \")
    \";\n\n if (gltf.json.cameras)\n {\n html += \"\";\n\n html += \"\";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \"\";\n\n for (let i = 0; i < gltf.json.cameras.length; i++)\n {\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n\n html += \"\";\n }\n html += \"
    nametypeinfo
    \" + gltf.json.cameras[i].name + \"\" + gltf.json.cameras[i].type + \"\";\n\n if (gltf.json.cameras[i].perspective)\n {\n html += \"yfov: \" + Math.round(gltf.json.cameras[i].perspective.yfov * 100) / 100;\n html += \", \";\n html += \"zfar: \" + Math.round(gltf.json.cameras[i].perspective.zfar * 100) / 100;\n html += \", \";\n html += \"znear: \" + Math.round(gltf.json.cameras[i].perspective.znear * 100) / 100;\n }\n html += \"
    \";\n }\n\n // / ////////////////////////////////////\n\n let numSkins = 0;\n if (gltf.json.skins)numSkins = gltf.json.skins.length;\n html += \"
    Skins (\" + numSkins + \")
    \";\n\n if (gltf.json.skins)\n {\n // html += \"

    Skins (\" + gltf.json.skins.length + \")

    \";\n html += \"\";\n\n html += \"\";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \"\";\n\n for (let i = 0; i < gltf.json.skins.length; i++)\n {\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n }\n html += \"
    nametotal joints
    \" + gltf.json.skins[i].name + \"\" + \"\" + gltf.json.skins[i].joints.length + \"\";\n html += \"
    \";\n }\n\n // / //////////////////////////\n\n let sizeBin = 0;\n if (gltf.json.buffers)\n sizeBin = gltf.json.buffers[0].byteLength;\n\n html += \"
    File Size Allocation (\" + Math.round(sizeBin / 1024) + \"k )
    \";\n\n html += \"\";\n html += \"\";\n html += \" \";\n html += \" \";\n html += \" \";\n html += \"\";\n let sizeUnknown = sizeBin;\n for (let i in sizes)\n {\n // html+=i+':'+Math.round(sizes[i]/1024);\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n sizeUnknown -= sizes[i];\n }\n\n if (sizeUnknown != 0)\n {\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n }\n\n html += \"
    namesize%
    \" + i + \"\" + readableSize(sizes[i]) + \" \" + Math.round(sizes[i] / sizeBin * 100) + \"%
    unknown\" + readableSize(sizeUnknown) + \" \" + Math.round(sizeUnknown / sizeBin * 100) + \"%
    \";\n html += \"
    \";\n\n tab = new CABLES.UI.Tab(\"GLTF\", { \"icon\": \"cube\", \"infotext\": \"tab_gltf\", \"padding\": true, \"singleton\": true });\n gui.mainTabs.addTab(tab, true);\n\n tab.addEventListener(\"onClose\", closeTab);\n tab.html(html);\n\n CABLES.UI.Collapsable.setup(ele.byId(\"groupNodes\"), ele.byId(\"sectionNodes\"), false);\n CABLES.UI.Collapsable.setup(ele.byId(\"groupMaterials\"), ele.byId(\"materialtable\"), true);\n CABLES.UI.Collapsable.setup(ele.byId(\"groupAnims\"), ele.byId(\"sectionAnim\"), true);\n CABLES.UI.Collapsable.setup(ele.byId(\"groupMeshes\"), ele.byId(\"meshestable\"), true);\n CABLES.UI.Collapsable.setup(ele.byId(\"groupCameras\"), ele.byId(\"sectionCameras\"), true);\n CABLES.UI.Collapsable.setup(ele.byId(\"groupImages\"), ele.byId(\"sectionImages\"), true);\n CABLES.UI.Collapsable.setup(ele.byId(\"groupSkins\"), ele.byId(\"sectionSkins\"), true);\n CABLES.UI.Collapsable.setup(ele.byId(\"groupBinary\"), ele.byId(\"sectionBinary\"), true);\n\n gui.maintabPanel.show(true);\n}\n\nfunction readableSize(n)\n{\n if (n > 1024) return Math.round(n / 1024) + \" kb\";\n if (n > 1024 * 500) return Math.round(n / 1024) + \" mb\";\n else return n + \" bytes\";\n}\n","inc_skin_js":"const GltfSkin = class\n{\n constructor(node)\n {\n this._mod = null;\n this._node = node;\n this._lastTime = 0;\n this._matArr = [];\n this._m = mat4.create();\n this._invBindMatrix = mat4.create();\n this.identity = true;\n }\n\n renderFinish(cgl)\n {\n cgl.popModelMatrix();\n this._mod.unbind();\n }\n\n renderStart(cgl, time)\n {\n if (!this._mod)\n {\n this._mod = new CGL.ShaderModifier(cgl, op.name + this._node.name);\n\n this._mod.addModule({\n \"priority\": -2,\n \"name\": \"MODULE_VERTEX_POSITION\",\n \"srcHeadVert\": attachments.skin_head_vert || \"\",\n \"srcBodyVert\": attachments.skin_vert || \"\"\n });\n\n this._mod.addUniformVert(\"m4[]\", \"MOD_boneMats\", []);// bohnenmatze\n const tr = vec3.create();\n }\n\n const skinIdx = this._node.skin;\n const arrLength = gltf.json.skins[skinIdx].joints.length * 16;\n\n // if (this._lastTime != time || !time)\n {\n // this._lastTime=inTime.get();\n if (this._matArr.length != arrLength) this._matArr.length = arrLength;\n\n for (let i = 0; i < gltf.json.skins[skinIdx].joints.length; i++)\n {\n const i16 = i * 16;\n const jointIdx = gltf.json.skins[skinIdx].joints[i];\n const nodeJoint = gltf.nodes[jointIdx];\n\n for (let j = 0; j < 16; j++)\n this._invBindMatrix[j] = gltf.accBuffers[gltf.json.skins[skinIdx].inverseBindMatrices][i16 + j];\n\n mat4.mul(this._m, nodeJoint.modelMatAbs(), this._invBindMatrix);\n\n for (let j = 0; j < this._m.length; j++) this._matArr[i16 + j] = this._m[j];\n }\n\n this._mod.setUniformValue(\"MOD_boneMats\", this._matArr);\n this._lastTime = time;\n }\n\n this._mod.define(\"SKIN_NUM_BONES\", gltf.json.skins[skinIdx].joints.length);\n this._mod.bind();\n\n // draw mesh...\n cgl.pushModelMatrix();\n if (this.identity)mat4.identity(cgl.mMatrix);\n }\n};\n","skin_vert":"int index=int(attrJoints.x);\nvec4 newPos = (MOD_boneMats[index] * pos) * attrWeights.x;\nvec3 newNorm = (vec4((MOD_boneMats[index] * vec4(norm.xyz, 0.0)) * attrWeights.x).xyz);\n\nindex=int(attrJoints.y);\nnewPos += (MOD_boneMats[index] * pos) * attrWeights.y;\nnewNorm = (vec4((MOD_boneMats[index] * vec4(norm.xyz, 0.0)) * attrWeights.y).xyz)+newNorm;\n\nindex=int(attrJoints.z);\nnewPos += (MOD_boneMats[index] * pos) * attrWeights.z;\nnewNorm = (vec4((MOD_boneMats[index] * vec4(norm.xyz, 0.0)) * attrWeights.z).xyz)+newNorm;\n\nindex=int(attrJoints.w);\nnewPos += (MOD_boneMats[index] * pos) * attrWeights.w ;\nnewNorm = (vec4((MOD_boneMats[index] * vec4(norm.xyz, 0.0)) * attrWeights.w).xyz)+newNorm;\n\npos=newPos;\n\nnorm=normalize(newNorm.xyz);\n\n\n","skin_head_vert":"\nIN vec4 attrWeights;\nIN vec4 attrJoints;\nUNI mat4 MOD_boneMats[SKIN_NUM_BONES];\n",}; +const gltfCamera = class +{ + constructor(gltf, node) + { + this.node=node; + this.name=node.name; + // console.log(gltf); + this.config=gltf.json.cameras[node.camera]; + + this.pos = vec3.create(); + this.quat = quat.create(); + this.quatOr = quat.create(); + this.vCenter = vec3.create(); + this.vUp = vec3.create(); + this.vMat = mat4.create(); + + } + + updateAnim(time) + { + if (this.node && this.node._animTrans) + { + + vec3.set(this.pos, + this.node._animTrans[0].getValue(time), + this.node._animTrans[1].getValue(time), + this.node._animTrans[2].getValue(time)); + + quat.set(this.quat, + this.node._animRot[0].getValue(time), + this.node._animRot[1].getValue(time), + this.node._animRot[2].getValue(time), + this.node._animRot[3].getValue(time)); + } + } + + start(time) + { + if (cgl.frameStore.shadowPass) return; + + this.updateAnim(time); + const asp = cgl.getViewPort()[2] / cgl.getViewPort()[3]; + + cgl.pushPMatrix(); + // mat4.perspective( + // cgl.pMatrix, + // this.config.perspective.yfov*0.5, + // asp, + // this.config.perspective.znear, + // this.config.perspective.zfar); + + cgl.pushViewMatrix(); + // mat4.identity(cgl.vMatrix); + + // if(this.node && this.node.parent) + // { + // console.log(this.node.parent) + // vec3.add(this.pos,this.pos,this.node.parent._node.translation); + // vec3.sub(this.vCenter,this.vCenter,this.node.parent._node.translation); + // mat4.translate(cgl.vMatrix,cgl.vMatrix, + // [ + // -this.node.parent._node.translation[0], + // -this.node.parent._node.translation[1], + // -this.node.parent._node.translation[2] + // ]) + // } + + + + // vec3.set(this.vUp, 0, 1, 0); + // vec3.set(this.vCenter, 0, -1, 0); + // // vec3.set(this.vCenter, 0, 1, 0); + // vec3.transformQuat(this.vCenter, this.vCenter, this.quat); + // vec3.normalize(this.vCenter, this.vCenter); + // vec3.add(this.vCenter, this.vCenter, this.pos); + + + // mat4.lookAt(cgl.vMatrix, this.pos, this.vCenter, this.vUp); + + + let mv=mat4.create(); + mat4.invert(mv,this.node.modelMatAbs()); + + // console.log(this.node.modelMatAbs()); + + this.vMat=mv; + + mat4.identity(cgl.vMatrix); + // console.log(mv); + mat4.mul(cgl.vMatrix,cgl.vMatrix,mv); + + + } + + end() + { + if (cgl.frameStore.shadowPass) return; + cgl.popPMatrix(); + cgl.popViewMatrix(); + } +}; + +const CHUNK_HEADER_SIZE = 8; + +const Gltf = class +{ + constructor() + { + this.json = {}; + this.accBuffers = []; + this.meshes = []; + this.nodes = []; + this.shaders = []; + this.timing = []; + this.cams = []; + this.startTime = performance.now(); + this.bounds = new CABLES.CG.BoundingBox(); + this.loaded = Date.now(); + this.accBuffersDelete = []; + } + + getNode(n) + { + for (let i = 0; i < this.nodes.length; i++) + { + if (this.nodes[i].name == n) return this.nodes[i]; + } + } + + unHideAll() + { + for (let i = 0; i < this.nodes.length; i++) + { + this.nodes[i].unHide(); + } + } +}; + +function Utf8ArrayToStr(array) +{ + if (window.TextDecoder) return new TextDecoder("utf-8").decode(array); + + let out, i, len, c; + let char2, char3; + + out = ""; + len = array.length; + i = 0; + while (i < len) + { + c = array[i++]; + switch (c >> 4) + { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + // 0xxxxxxx + out += String.fromCharCode(c); + break; + case 12: case 13: + // 110x xxxx 10xx xxxx + char2 = array[i++]; + out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = array[i++]; + char3 = array[i++]; + out += String.fromCharCode(((c & 0x0F) << 12) | + ((char2 & 0x3F) << 6) | + ((char3 & 0x3F) << 0)); + break; + } + } + + return out; +} + +function readChunk(dv, bArr, arrayBuffer, offset) +{ + const chunk = {}; + + if (offset >= dv.byteLength) + { + op.log("could not read chunk..."); + return; + } + chunk.size = dv.getUint32(offset + 0, le); + + // chunk.type = new TextDecoder("utf-8").decode(bArr.subarray(offset+4, offset+4+4)); + chunk.type = Utf8ArrayToStr(bArr.subarray(offset + 4, offset + 4 + 4)); + + if (chunk.type == "BIN\0") + { + // console.log(chunk.size,arrayBuffer.length,offset); + // try + // { + chunk.dataView = new DataView(arrayBuffer, offset + 8, chunk.size); + // } + // catch(e) + // { + // chunk.dataView = null; + // console.log(e); + // } + } + else + if (chunk.type == "JSON") + { + const json = Utf8ArrayToStr(bArr.subarray(offset + 8, offset + 8 + chunk.size)); + + try + { + const obj = JSON.parse(json); + chunk.data = obj; + outGenerator.set(obj.asset.generator); + } + catch (e) + { + } + } + else + { + op.warn("unknown type", chunk.type); + } + + return chunk; +} + +function loadAnims(gltf) +{ + const uniqueAnimNames = {}; + + for (let i = 0; i < gltf.json.animations.length; i++) + { + const an = gltf.json.animations[i]; + + for (let ia = 0; ia < an.channels.length; ia++) + { + const chan = an.channels[ia]; + + const node = gltf.nodes[chan.target.node]; + const sampler = an.samplers[chan.sampler]; + + const acc = gltf.json.accessors[sampler.input]; + const bufferIn = gltf.accBuffers[sampler.input]; + + const accOut = gltf.json.accessors[sampler.output]; + const bufferOut = gltf.accBuffers[sampler.output]; + + gltf.accBuffersDelete.push(sampler.output, sampler.input); + + if (bufferIn && bufferOut) + { + let numComps = 1; + if (accOut.type == "VEC2")numComps = 2; + else if (accOut.type == "VEC3")numComps = 3; + else if (accOut.type == "VEC4")numComps = 4; + else op.warn("unknown accOut.type", accOut.type); + + const anims = []; + + uniqueAnimNames[an.name] = true; + + for (let k = 0; k < numComps; k++) + { + const newAnim = new CABLES.TL.Anim(); + // newAnim.name=an.name; + anims.push(newAnim); + } + + if (sampler.interpolation == "LINEAR") {} + else if (sampler.interpolation == "STEP") for (let k = 0; k < numComps; k++) anims[k].defaultEasing = CABLES.EASING_ABSOLUTE; + else if (sampler.interpolation == "CUBICSPLINE") for (let k = 0; k < numComps; k++) anims[k].defaultEasing = CABLES.EASING_CUBICSPLINE; + else op.warn("unknown interpolation", sampler.interpolation); + + // console.log(bufferOut) + + // if there is no keyframe for time 0 copy value of first keyframe at time 0 + if (bufferIn[0] !== 0.0) + for (let k = 0; k < numComps; k++) + anims[k].setValue(0, bufferOut[0 * numComps + k]); + + // console.log(sampler.interpolation,bufferOut.length/numComps) + + for (let j = 0; j < bufferIn.length; j++) + { + maxTime = Math.max(bufferIn[j], maxTime); + + for (let k = 0; k < numComps; k++) + { + if (anims[k].defaultEasing === CABLES.EASING_CUBICSPLINE) + { + const idx = ((j * numComps) * 3 + k); + + const key = anims[k].setValue(bufferIn[j], bufferOut[idx + numComps]); + key.bezTangIn = bufferOut[idx]; + key.bezTangOut = bufferOut[idx + (numComps * 2)]; + + // console.log(an.name,k,bufferOut[idx+1]); + } + else + { + // console.log(an.name,k,bufferOut[j * numComps + k]); + anims[k].setValue(bufferIn[j], bufferOut[j * numComps + k]); + } + } + } + + node.setAnim(chan.target.path, an.name, anims); + } + else + { + op.warn("loadAmins bufferIn undefined ", bufferIn === undefined); + op.warn("loadAmins bufferOut undefined ", bufferOut === undefined); + op.warn("loadAmins ", sampler, accOut); + op.warn("loadAmins num accBuffers", gltf.accBuffers.length); + op.warn("loadAmins num accessors", gltf.json.accessors.length); + } + } + } + + gltf.uniqueAnimNames = uniqueAnimNames; + + outAnims.set(null); + outAnims.set(Object.keys(uniqueAnimNames)); +} + +function loadCams(gltf) +{ + if (!gltf || !gltf.json.cameras) return; + + gltf.cameras = gltf.cameras || []; + + for (let i = 0; i < gltf.nodes.length; i++) + { + if (gltf.nodes[i].hasOwnProperty("camera")) + { + const cam = new gltfCamera(gltf, gltf.nodes[i]); + gltf.cameras.push(cam); + } + } +} + +function loadAfterDraco() +{ + if (!window.DracoDecoder) + { + setTimeout(() => + { + loadAfterDraco(); + }, 100); + } + + reloadSoon(); +} + +function parseGltf(arrayBuffer) +{ + let j = 0, i = 0; + + const gltf = new Gltf(); + gltf.timing.push("Start parsing", Math.round((performance.now() - gltf.startTime))); + + if (!arrayBuffer) return; + const byteArray = new Uint8Array(arrayBuffer); + let pos = 0; + + // var string = new TextDecoder("utf-8").decode(byteArray.subarray(pos, 4)); + const string = Utf8ArrayToStr(byteArray.subarray(pos, 4)); + pos += 4; + if (string != "glTF") return; + + gltf.timing.push("dataview", Math.round((performance.now() - gltf.startTime))); + + const dv = new DataView(arrayBuffer); + const version = dv.getUint32(pos, le); + pos += 4; + const size = dv.getUint32(pos, le); + pos += 4; + + outVersion.set(version); + + const chunks = []; + gltf.chunks = chunks; + + chunks.push(readChunk(dv, byteArray, arrayBuffer, pos)); + pos += chunks[0].size + CHUNK_HEADER_SIZE; + gltf.json = chunks[0].data; + outJson.set(gltf.json); + outExtensions.set(gltf.json.extensionsUsed || []); + + let ch = readChunk(dv, byteArray, arrayBuffer, pos); + while (ch) + { + chunks.push(ch); + pos += ch.size + CHUNK_HEADER_SIZE; + ch = readChunk(dv, byteArray, arrayBuffer, pos); + } + + gltf.chunks = chunks; + + const views = chunks[0].data.bufferViews; + const accessors = chunks[0].data.accessors; + + gltf.timing.push("Parse buffers", Math.round((performance.now() - gltf.startTime))); + + if (gltf.json.extensionsUsed && gltf.json.extensionsUsed.indexOf("KHR_draco_mesh_compression") > -1) + { + if (!window.DracoDecoder) + { + op.setUiError("gltfdraco", "GLTF draco compression lib not found / add draco op to your patch!"); + + loadAfterDraco(); + return gltf; + } + else + { + gltf.useDraco = true; + } + } + + op.setUiError("gltfdraco", null); + // let accPos = (view.byteOffset || 0) + (acc.byteOffset || 0); + + if (views) + { + for (i = 0; i < accessors.length; i++) + { + const acc = accessors[i]; + const view = views[acc.bufferView]; + + let numComps = 0; + if (acc.type == "SCALAR")numComps = 1; + else if (acc.type == "VEC2")numComps = 2; + else if (acc.type == "VEC3")numComps = 3; + else if (acc.type == "VEC4")numComps = 4; + else if (acc.type == "MAT4")numComps = 16; + else console.error("unknown accessor type", acc.type); + + // const decoder = new decoderModule.Decoder(); + // const decodedGeometry = decodeDracoData(data, decoder); + // // Encode mesh + // encodeMeshToFile(decodedGeometry, decoder); + + // decoderModule.destroy(decoder); + // decoderModule.destroy(decodedGeometry); + + // 5120 (BYTE) 1 + // 5121 (UNSIGNED_BYTE) 1 + // 5122 (SHORT) 2 + + if (chunks[1].dataView) + { + if (view) + { + const num = acc.count * numComps; + let accPos = (view.byteOffset || 0) + (acc.byteOffset || 0); + let stride = view.byteStride || 0; + let dataBuff = null; + + if (acc.componentType == 5126 || acc.componentType == 5125) // 4byte FLOAT or INT + { + stride = stride || 4; + + const isInt = acc.componentType == 5125; + if (isInt)dataBuff = new Uint32Array(num); + else dataBuff = new Float32Array(num); + + for (j = 0; j < num; j++) + { + if (isInt) dataBuff[j] = chunks[1].dataView.getUint32(accPos, le); + else dataBuff[j] = chunks[1].dataView.getFloat32(accPos, le); + + if (stride != 4 && (j + 1) % numComps === 0)accPos += stride - (numComps * 4); + accPos += 4; + } + } + else if (acc.componentType == 5123) // UNSIGNED_SHORT + { + stride = stride || 2; + + dataBuff = new Uint16Array(num); + + for (j = 0; j < num; j++) + { + dataBuff[j] = chunks[1].dataView.getUint16(accPos, le); + + if (stride != 2 && (j + 1) % numComps === 0) accPos += stride - (numComps * 2); + + accPos += 2; + } + } + else if (acc.componentType == 5121) // UNSIGNED_BYTE + { + stride = stride || 1; + + dataBuff = new Uint8Array(num); + + for (j = 0; j < num; j++) + { + dataBuff[j] = chunks[1].dataView.getUint8(accPos, le); + + if (stride != 1 && (j + 1) % numComps === 0) accPos += stride - (numComps * 1); + + accPos += 1; + } + } + + else + { + console.error("unknown component type", acc.componentType); + } + + gltf.accBuffers.push(dataBuff); + } + else + { + // console.log("has no dataview"); + } + } + } + } + + gltf.timing.push("Parse mesh groups", Math.round((performance.now() - gltf.startTime))); + + gltf.json.meshes = gltf.json.meshes || []; + + if (gltf.json.meshes) + { + for (i = 0; i < gltf.json.meshes.length; i++) + { + const mesh = new gltfMeshGroup(gltf, gltf.json.meshes[i]); + gltf.meshes.push(mesh); + } + } + + gltf.timing.push("Parse nodes", Math.round((performance.now() - gltf.startTime))); + + for (i = 0; i < gltf.json.nodes.length; i++) + { + if (gltf.json.nodes[i].children) + for (j = 0; j < gltf.json.nodes[i].children.length; j++) + { + gltf.json.nodes[gltf.json.nodes[i].children[j]].isChild = true; + } + } + + for (i = 0; i < gltf.json.nodes.length; i++) + { + const node = new gltfNode(gltf.json.nodes[i], gltf); + gltf.nodes.push(node); + } + + for (i = 0; i < gltf.nodes.length; i++) + { + const node = gltf.nodes[i]; + + if (!node.children) continue; + for (let j = 0; j < node.children.length; j++) + { + gltf.nodes[node.children[j]].parent = node; + } + } + + for (i = 0; i < gltf.nodes.length; i++) + { + gltf.nodes[i].initSkin(); + } + + needsMatUpdate = true; + + gltf.timing.push("load anims", Math.round((performance.now() - gltf.startTime))); + + if (gltf.json.animations) loadAnims(gltf); + + gltf.timing.push("load cameras", Math.round((performance.now() - gltf.startTime))); + + if (gltf.json.cameras) loadCams(gltf); + + gltf.timing.push("finished", Math.round((performance.now() - gltf.startTime))); + + return gltf; +} +let gltfMesh = class +{ + constructor(name, prim, gltf, finished) + { + this.POINTS = 0; + this.LINES = 1; + this.LINE_LOOP = 2; + this.LINE_STRIP = 3; + this.TRIANGLES = 4; + this.TRIANGLE_STRIP = 5; + this.TRIANGLE_FAN = 6; + + this.test = 0; + this.name = name; + this.submeshIndex = 0; + this.material = prim.material; + this.mesh = null; + this.geom = new CGL.Geometry("gltf_" + this.name); + this.geom.verticesIndices = []; + this.bounds = null; + this.primitive = 4; + if (prim.hasOwnProperty("mode")) this.primitive = prim.mode; + + if (prim.hasOwnProperty("indices")) this.geom.verticesIndices = gltf.accBuffers[prim.indices]; + + gltf.loadingMeshes = gltf.loadingMeshes || 0; + gltf.loadingMeshes++; + + this.materialJson = + this._matPbrMetalness = + this._matPbrRoughness = + this._matDiffuseColor = null; + + if (gltf.json.materials) + { + if (this.material != -1) this.materialJson = gltf.json.materials[this.material]; + + if (this.materialJson && this.materialJson.pbrMetallicRoughness) + { + if (!this.materialJson.pbrMetallicRoughness.hasOwnProperty("baseColorFactor")) + { + this._matDiffuseColor = [1, 1, 1, 1]; + } + else + { + this._matDiffuseColor = this.materialJson.pbrMetallicRoughness.baseColorFactor; + } + + this._matDiffuseColor = this.materialJson.pbrMetallicRoughness.baseColorFactor; + + if (!this.materialJson.pbrMetallicRoughness.hasOwnProperty("metallicFactor")) + { + this._matPbrMetalness = 1.0; + } + else + { + this._matPbrMetalness = this.materialJson.pbrMetallicRoughness.metallicFactor || null; + } + + if (!this.materialJson.pbrMetallicRoughness.hasOwnProperty("roughnessFactor")) + { + this._matPbrRoughness = 1.0; + } + else + { + this._matPbrRoughness = this.materialJson.pbrMetallicRoughness.roughnessFactor || null; + } + } + } + + if (gltf.useDraco && prim.extensions.KHR_draco_mesh_compression) + { + const view = gltf.chunks[0].data.bufferViews[prim.extensions.KHR_draco_mesh_compression.bufferView]; + const num = view.byteLength; + const dataBuff = new Int8Array(num); + let accPos = (view.byteOffset || 0);// + (acc.byteOffset || 0); + for (let j = 0; j < num; j++) + { + dataBuff[j] = gltf.chunks[1].dataView.getInt8(accPos, le); + accPos++; + } + + const dracoDecoder = window.DracoDecoder; + dracoDecoder.decodeGeometry(dataBuff.buffer, (geometry) => + { + const geom = new CGL.Geometry("draco mesh " + name); + + for (let i = 0; i < geometry.attributes.length; i++) + { + const attr = geometry.attributes[i]; + + if (attr.name === "position") geom.vertices = attr.array; + else if (attr.name === "normal") geom.vertexNormals = attr.array; + else if (attr.name === "uv") geom.texCoords = attr.array; + else if (attr.name === "color") geom.vertexColors = this.calcVertexColors(attr.array); + else if (attr.name === "joints") geom.setAttribute("attrJoints", Array.from(attr.array), 4); + else if (attr.name === "weights") + { + const arr4 = new Float32Array(attr.array.length / attr.itemSize * 4); + + for (let k = 0; k < attr.array.length / attr.itemSize; k++) + { + arr4[k * 4] = arr4[k * 4 + 1] = arr4[k * 4 + 2] = arr4[k * 4 + 3] = 0; + for (let j = 0; j < attr.itemSize; j++) + arr4[k * 4 + j] = attr.array[k * attr.itemSize + j]; + } + geom.setAttribute("attrWeights", arr4, 4); + } + else op.logWarn("unknown draco attrib", attr); + } + + geometry.attributes = null; + geom.verticesIndices = geometry.index.array; + + this.setGeom(geom); + + this.mesh = null; + gltf.loadingMeshes--; + if (finished)finished(this); + }, (error) => { op.logError(error); }); + } + else + { + gltf.loadingMeshes--; + this.fillGeomAttribs(gltf, this.geom, prim.attributes); + + if (prim.targets) + { + console.log("prim.targets", prim.targets.length); + for (let j = 0; j < prim.targets.length; j++) + { + // var tgeom=new CGL.Geometry("gltf_"+this.name); + let tgeom = this.geom.copy(); + + if (prim.hasOwnProperty("indices")) tgeom.verticesIndices = gltf.accBuffers[prim.indices]; + + this.fillGeomAttribs(gltf, tgeom, prim.targets[j], false); + + { // calculate normals for final position of morphtarget for later... + for (let i = 0; i < tgeom.vertices.length; i++) tgeom.vertices[i] += this.geom.vertices[i]; + tgeom.calculateNormals(); + for (let i = 0; i < tgeom.vertices.length; i++) tgeom.vertices[i] -= this.geom.vertices[i]; + } + + this.geom.morphTargets.push(tgeom); + } + } + if (finished)finished(this); + } + } + + _linearToSrgb(x) + { + if (x <= 0) + return 0; + else if (x >= 1) + return 1; + else if (x < 0.0031308) + return x * 12.92; + else + return Math.pow(x, 1 / 2.2) * 1.055 - 0.055; + } + + calcVertexColors(arr) + { + let vertexColors = null; + if (arr instanceof Float32Array) + { + let div = false; + for (let i = 0; i < arr.length; i++) + { + if (arr[i] > 1) + { + div = true; + continue; + } + } + + if (div) + for (let i = 0; i < arr.length; i++) arr[i] /= 65535; + + vertexColors = arr; + } + + else if (arr instanceof Uint16Array) + { + const fb = new Float32Array(arr.length); + for (let i = 0; i < arr.length; i++) fb[i] = arr[i] / 65535; + + vertexColors = fb; + } + else vertexColors = arr; + + for (let i = 0; i < vertexColors.length; i++) + { + vertexColors[i] = this._linearToSrgb(vertexColors[i]); + } + + return vertexColors; + } + + fillGeomAttribs(gltf, tgeom, attribs, setGeom) + { + if (attribs.hasOwnProperty("POSITION")) tgeom.vertices = gltf.accBuffers[attribs.POSITION]; + if (attribs.hasOwnProperty("NORMAL")) tgeom.vertexNormals = gltf.accBuffers[attribs.NORMAL]; + if (attribs.hasOwnProperty("TANGENT")) tgeom.tangents = gltf.accBuffers[attribs.TANGENT]; + + if (attribs.hasOwnProperty("COLOR_0")) tgeom.vertexColors = this.calcVertexColors(gltf.accBuffers[attribs.COLOR_0]); + if (attribs.hasOwnProperty("COLOR_1")) tgeom.setAttribute("attrVertColor1", this.calcVertexColors(gltf.accBuffers[attribs.COLOR_1]), 4); + if (attribs.hasOwnProperty("COLOR_2")) tgeom.setAttribute("attrVertColor2", this.calcVertexColors(gltf.accBuffers[attribs.COLOR_2]), 4); + if (attribs.hasOwnProperty("COLOR_3")) tgeom.setAttribute("attrVertColor3", this.calcVertexColors(gltf.accBuffers[attribs.COLOR_3]), 4); + if (attribs.hasOwnProperty("COLOR_4")) tgeom.setAttribute("attrVertColor4", this.calcVertexColors(gltf.accBuffers[attribs.COLOR_4]), 4); + + if (attribs.hasOwnProperty("TEXCOORD_0"))tgeom.texCoords = gltf.accBuffers[attribs.TEXCOORD_0]; + if (attribs.hasOwnProperty("TEXCOORD_1"))tgeom.setAttribute("attrTexCoord1", gltf.accBuffers[attribs.TEXCOORD_1], 2); + if (attribs.hasOwnProperty("TEXCOORD_2"))tgeom.setAttribute("attrTexCoord2", gltf.accBuffers[attribs.TEXCOORD_2], 2); + if (attribs.hasOwnProperty("TEXCOORD_3"))tgeom.setAttribute("attrTexCoord3", gltf.accBuffers[attribs.TEXCOORD_3], 2); + if (attribs.hasOwnProperty("TEXCOORD_4"))tgeom.setAttribute("attrTexCoord4", gltf.accBuffers[attribs.TEXCOORD_4], 2); + + if (attribs.hasOwnProperty("WEIGHTS_0")) + { + tgeom.setAttribute("attrWeights", gltf.accBuffers[attribs.WEIGHTS_0], 4); + } + if (attribs.hasOwnProperty("JOINTS_0")) + { + if (!gltf.accBuffers[attribs.JOINTS_0])console.log("no !gltf.accBuffers[attribs.JOINTS_0]"); + tgeom.setAttribute("attrJoints", gltf.accBuffers[attribs.JOINTS_0], 4); + } + + if (attribs.hasOwnProperty("POSITION")) gltf.accBuffersDelete.push(attribs.POSITION); + if (attribs.hasOwnProperty("NORMAL")) gltf.accBuffersDelete.push(attribs.NORMAL); + if (attribs.hasOwnProperty("TEXCOORD_0")) gltf.accBuffersDelete.push(attribs.TEXCOORD_0); + if (attribs.hasOwnProperty("TANGENT")) gltf.accBuffersDelete.push(attribs.TANGENT); + if (attribs.hasOwnProperty("COLOR_0"))gltf.accBuffersDelete.push(attribs.COLOR_0); + if (attribs.hasOwnProperty("COLOR_0"))gltf.accBuffersDelete.push(attribs.COLOR_0); + if (attribs.hasOwnProperty("COLOR_1"))gltf.accBuffersDelete.push(attribs.COLOR_1); + if (attribs.hasOwnProperty("COLOR_2"))gltf.accBuffersDelete.push(attribs.COLOR_2); + if (attribs.hasOwnProperty("COLOR_3"))gltf.accBuffersDelete.push(attribs.COLOR_3); + + if (attribs.hasOwnProperty("TEXCOORD_1")) gltf.accBuffersDelete.push(attribs.TEXCOORD_1); + if (attribs.hasOwnProperty("TEXCOORD_2")) gltf.accBuffersDelete.push(attribs.TEXCOORD_2); + if (attribs.hasOwnProperty("TEXCOORD_3")) gltf.accBuffersDelete.push(attribs.TEXCOORD_3); + if (attribs.hasOwnProperty("TEXCOORD_4")) gltf.accBuffersDelete.push(attribs.TEXCOORD_4); + + if (setGeom !== false) if (tgeom && tgeom.verticesIndices) this.setGeom(tgeom); + } + + setGeom(geom) + { + if (inNormFormat.get() == "X-ZY") + { + for (let i = 0; i < geom.vertexNormals.length; i += 3) + { + let t = geom.vertexNormals[i + 2]; + geom.vertexNormals[i + 2] = geom.vertexNormals[i + 1]; + geom.vertexNormals[i + 1] = -t; + } + } + + if (inVertFormat.get() == "XZ-Y") + { + for (let i = 0; i < geom.vertices.length; i += 3) + { + let t = geom.vertices[i + 2]; + geom.vertices[i + 2] = -geom.vertices[i + 1]; + geom.vertices[i + 1] = t; + } + } + + if (this.primitive == this.TRIANGLES) + { + if (!geom.vertexNormals.length || inCalcNormals.get()) geom.calculateNormals(); + + if ((!geom.biTangents || geom.biTangents.length == 0) && geom.tangents) + { + const bitan = vec3.create(); + const tan = vec3.create(); + + const tangents = geom.tangents; + geom.tangents = new Float32Array(tangents.length / 4 * 3); + geom.biTangents = new Float32Array(tangents.length / 4 * 3); + + for (let i = 0; i < tangents.length; i += 4) + { + const idx = i / 4 * 3; + + vec3.cross( + bitan, + [geom.vertexNormals[idx], geom.vertexNormals[idx + 1], geom.vertexNormals[idx + 2]], + [tangents[i], tangents[i + 1], tangents[i + 2]] + ); + + vec3.div(bitan, bitan, [tangents[i + 3], tangents[i + 3], tangents[i + 3]]); + vec3.normalize(bitan, bitan); + + geom.biTangents[idx + 0] = bitan[0]; + geom.biTangents[idx + 1] = bitan[1]; + geom.biTangents[idx + 2] = bitan[2]; + + geom.tangents[idx + 0] = tangents[i + 0]; + geom.tangents[idx + 1] = tangents[i + 1]; + geom.tangents[idx + 2] = tangents[i + 2]; + } + } + + if (geom.tangents.length === 0 || inCalcNormals.get()) geom.calcTangentsBitangents(); + } + + this.geom = geom; + + this.bounds = geom.getBounds(); + } + + render(cgl, ignoreMaterial, skinRenderer) + { + if (!this.mesh && this.geom && this.geom.verticesIndices) + { + let g = this.geom; + if (this.geom.vertices.length / 3 > 64000) + { + g = this.geom.copy(); + g.unIndex(false, true); + } + + let glprim; + if (this.primitive == this.TRIANGLES)glprim = cgl.gl.TRIANGLES; + else if (this.primitive == this.LINES)glprim = cgl.gl.LINES; + else if (this.primitive == this.LINE_STRIP)glprim = cgl.gl.LINE_STRIP; + else if (this.primitive == this.POINTS)glprim = cgl.gl.POINTS; + else + { + op.logWarn("unknown primitive type", this); + } + + this.mesh = new CGL.Mesh(cgl, g, glprim); + // this.mesh._geom = null; + } + else + { + // update morphTargets + if (this.geom && this.geom.morphTargets.length) + { + this.morphGeom = this.geom.copy(); + + this.test = time * 11.7; + + if (this.test >= this.geom.morphTargets.length - 1) this.test = 0; + + const mt = this.geom.morphTargets[Math.floor(this.test)]; + const mt2 = this.geom.morphTargets[Math.floor(this.test + 1)]; + + if (mt && mt.vertices && mt2) + { + if (this.morphGeom.vertexNormals.length != mt.vertexNormals.length) + this.morphGeom.vertexNormals = new Float32Array(mt.vertexNormals.length); + + const fract = this.test % 1; + for (let i = 0; i < this.morphGeom.vertices.length; i++) + { + this.morphGeom.vertices[i] = + this.geom.vertices[i] + + (1.0 - fract) * mt.vertices[i] + + fract * mt2.vertices[i]; + + this.morphGeom.vertexNormals[i] = + (1.0 - fract) * mt.vertexNormals[i] + + fract * mt2.vertexNormals[i]; + } + + this.mesh.updateNormals(this.morphGeom); + this.mesh.updateVertices(this.morphGeom); + } + } + + let useMat = !ignoreMaterial && this.material != -1 && gltf.shaders[this.material]; + if (skinRenderer)useMat = false; + + if (useMat) cgl.pushShader(gltf.shaders[this.material]); + + const currentShader = cgl.getShader() || {}; + const uniDiff = currentShader.uniformColorDiffuse; + + const uniPbrMetalness = currentShader.uniformPbrMetalness; + const uniPbrRoughness = currentShader.uniformPbrRoughness; + + if (!gltf.shaders[this.material] && inUseMatProps.get()) + { + if (uniDiff && this._matDiffuseColor) + { + this._matDiffuseColorOrig = [uniDiff.getValue()[0], uniDiff.getValue()[1], uniDiff.getValue()[2], uniDiff.getValue()[3]]; + uniDiff.setValue(this._matDiffuseColor); + } + + if (uniPbrMetalness) + if (this._matPbrMetalness != null) + { + this._matPbrMetalnessOrig = uniPbrMetalness.getValue(); + uniPbrMetalness.setValue(this._matPbrMetalness); + } + else + uniPbrMetalness.setValue(0); + + if (uniPbrRoughness) + if (this._matPbrRoughness != null) + { + this._matPbrRoughnessOrig = uniPbrRoughness.getValue(); + uniPbrRoughness.setValue(this._matPbrRoughness); + } + else + { + uniPbrRoughness.setValue(0); + } + } + + if (this.mesh) this.mesh.render(cgl.getShader(), ignoreMaterial); + + if (inUseMatProps.get()) + { + if (uniDiff && this._matDiffuseColor) uniDiff.setValue(this._matDiffuseColorOrig); + if (uniPbrMetalness && this._matPbrMetalnessOrig != undefined) uniPbrMetalness.setValue(this._matPbrMetalnessOrig); + if (uniPbrRoughness && this._matPbrRoughnessOrig != undefined) uniPbrRoughness.setValue(this._matPbrRoughnessOrig); + } + + if (useMat) cgl.popShader(); + } + } +}; +const gltfMeshGroup = class +{ + constructor(gltf, m) + { + this.bounds = new CABLES.CG.BoundingBox(); + this.meshes = []; + this.name = m.name; + const prims = m.primitives; + + for (let i = 0; i < prims.length; i++) + { + const mesh = new gltfMesh(this.name, prims[i], gltf, + (mesh) => + { + this.bounds.apply(mesh.bounds); + }); + + mesh.submeshIndex = i; + this.meshes.push(mesh); + } + } + + render(cgl, ignoreMat, skinRenderer, _time) + { + for (let i = 0; i < this.meshes.length; i++) + { + const useMat = gltf.shaders[this.meshes[i].material]; + + if (!ignoreMat && useMat) cgl.pushShader(gltf.shaders[this.meshes[i].material]); + // console.log(gltf.shaders[this.meshes[i].material],this.meshes[i].material) + if (skinRenderer)skinRenderer.renderStart(cgl, _time); + + this.meshes[i].render(cgl, ignoreMat, skinRenderer, _time); + if (skinRenderer)skinRenderer.renderFinish(cgl); + if (!ignoreMat && useMat) cgl.popShader(); + } + } +}; +const gltfNode = class +{ + constructor(node, gltf) + { + this.isChild = node.isChild || false; + this.name = node.name; + if (node.hasOwnProperty("camera")) this.camera = node.camera; + this.hidden = false; + this.mat = mat4.create(); + this._animMat = mat4.create(); + this._tempMat = mat4.create(); + this._tempQuat = quat.create(); + this._tempRotmat = mat4.create(); + this.mesh = null; + this.children = []; + this._node = node; + this._gltf = gltf; + this.absMat = mat4.create(); + this.addTranslate = null; + this._tempAnimScale = null; + this.addMulMat = null; + this.updateMatrix(); + this._animActions = {}; + this.skinRenderer = null; + this.copies = []; + } + + get skin() + { + if (this._node.hasOwnProperty("skin")) return this._node.skin; + else return -1; + } + + copy() + { + this.isCopy = true; + const n = new gltfNode(this._node, this._gltf); + n.copyOf = this; + + n._animActions = this._animActions; + n.children = this.children; + n.skinRenderer = new GltfSkin(n); + + this.updateMatrix(); + return n; + } + + hasSkin() + { + if (this._node.hasOwnProperty("skin")) return this._gltf.json.skins[this._node.skin].name || "unknown"; + return false; + } + + initSkin() + { + if (this.skin > -1) + { + this.skinRenderer = new GltfSkin(this); + } + } + + updateMatrix() + { + mat4.identity(this.mat); + if (this._node.translation) mat4.translate(this.mat, this.mat, this._node.translation); + + if (this._node.rotation) + { + const rotmat = mat4.create(); + this._rot = this._node.rotation; + + mat4.fromQuat(rotmat, this._node.rotation); + mat4.mul(this.mat, this.mat, rotmat); + } + + if (this._node.scale) + { + this._scale = this._node.scale; + mat4.scale(this.mat, this.mat, this._scale); + } + + if (this._node.hasOwnProperty("mesh")) + { + this.mesh = this._gltf.meshes[this._node.mesh]; + if (this.isCopy) + { + console.log(this.mesh); + } + } + + if (this._node.children) + { + for (let i = 0; i < this._node.children.length; i++) + { + this._gltf.json.nodes[i].isChild = true; + if (this._gltf.nodes[this._node.children[i]]) this._gltf.nodes[this._node.children[i]].isChild = true; + this.children.push(this._node.children[i]); + } + } + } + + unHide() + { + this.hidden = false; + for (let i = 0; i < this.children.length; i++) + if (this.children[i].unHide) this.children[i].unHide(); + } + + calcBounds(gltf, mat, bounds) + { + const localMat = mat4.create(); + + if (mat) mat4.copy(localMat, mat); + if (this.mat) mat4.mul(localMat, localMat, this.mat); + + if (this.mesh) + { + const bb = this.mesh.bounds.copy(); + bb.mulMat4(localMat); + bounds.apply(bb); + + if (bounds.changed) + { + boundingPoints.push( + bb._min[0] || 0, bb._min[1] || 0, bb._min[2] || 0, + bb._max[0] || 0, bb._max[1] || 0, bb._max[2] || 0); + } + } + + for (let i = 0; i < this.children.length; i++) + { + if (gltf.nodes[this.children[i]] && gltf.nodes[this.children[i]].calcBounds) + { + const b = gltf.nodes[this.children[i]].calcBounds(gltf, localMat, bounds); + + bounds.apply(b); + } + } + + if (bounds.changed) return bounds; + else return null; + } + + setAnimAction(name) + { + // console.log("setAnimAction:", name); + if (!name) return; + + this._currentAnimaction = name; + + if (name && !this._animActions[name]) + { + // console.log("no action found:", name,this._animActions); + return null; + } + + // else console.log("YES action found:", name); + // console.log(this._animActions); + + for (let path in this._animActions[name]) + { + if (path == "translation") this._animTrans = this._animActions[name][path]; + else if (path == "rotation") this._animRot = this._animActions[name][path]; + else if (path == "scale") this._animScale = this._animActions[name][path]; + else console.warn("unknown anim path", path, this._animActions[name][path]); + } + } + + setAnim(path, name, anims) + { + if (!path || !name || !anims) return; + + // console.log("setanim", this._node.name, path, name, anims); + + this._animActions[name] = this._animActions[name] || {}; + + // console.log(this._animActions); + // debugger; + + // for (let i = 0; i < this.copies.length; i++) this.copies[i]._animActions = this._animActions; + + if (this._animActions[name][path]) op.warn("animation action path already exists", name, path, this._animActions[name][path]); + + this._animActions[name][path] = anims; + + if (path == "translation") this._animTrans = anims; + else if (path == "rotation") this._animRot = anims; + else if (path == "scale") this._animScale = anims; + else console.warn("unknown anim path", path, anims); + } + + modelMatLocal() + { + return this._animMat || this.mat; + } + + modelMatAbs() + { + return this.absMat; + } + + transform(cgl, _time) + { + if (!_time && _time != 0)_time = time; + + this._lastTimeTrans = _time; + + // console.log(this._rot) + + gltfTransforms++; + + if (!this._animTrans && !this._animRot && !this._animScale) + { + mat4.mul(cgl.mMatrix, cgl.mMatrix, this.mat); + this._animMat = null; + } + else + { + this._animMat = this._animMat || mat4.create(); + mat4.identity(this._animMat); + + const playAnims = true; + + if (playAnims && this._animTrans) + { + mat4.translate(this._animMat, this._animMat, [ + this._animTrans[0].getValue(_time), + this._animTrans[1].getValue(_time), + this._animTrans[2].getValue(_time)]); + } + else + if (this._node.translation) mat4.translate(this._animMat, this._animMat, this._node.translation); + + if (playAnims && this._animRot) + { + if (this._animRot[0].defaultEasing == CABLES.EASING_LINEAR) CABLES.TL.Anim.slerpQuaternion(_time, this._tempQuat, this._animRot[0], this._animRot[1], this._animRot[2], this._animRot[3]); + else if (this._animRot[0].defaultEasing == CABLES.EASING_ABSOLUTE) + { + this._tempQuat[0] = this._animRot[0].getValue(_time); + this._tempQuat[1] = this._animRot[1].getValue(_time); + this._tempQuat[2] = this._animRot[2].getValue(_time); + this._tempQuat[3] = this._animRot[3].getValue(_time); + } + else if (this._animRot[0].defaultEasing == CABLES.EASING_CUBICSPLINE) + { + CABLES.TL.Anim.slerpQuaternion(_time, this._tempQuat, this._animRot[0], this._animRot[1], this._animRot[2], this._animRot[3]); + } + + mat4.fromQuat(this._tempMat, this._tempQuat); + mat4.mul(this._animMat, this._animMat, this._tempMat); + } + else if (this._rot) + { + mat4.fromQuat(this._tempRotmat, this._rot); + mat4.mul(this._animMat, this._animMat, this._tempRotmat); + } + + if (playAnims && this._animScale) + { + if (!this._tempAnimScale) this._tempAnimScale = [1, 1, 1]; + this._tempAnimScale[0] = this._animScale[0].getValue(_time); + this._tempAnimScale[1] = this._animScale[1].getValue(_time); + this._tempAnimScale[2] = this._animScale[2].getValue(_time); + mat4.scale(this._animMat, this._animMat, this._tempAnimScale); + } + else if (this._scale) mat4.scale(this._animMat, this._animMat, this._scale); + + mat4.mul(cgl.mMatrix, cgl.mMatrix, this._animMat); + } + + if (this.addTranslate) mat4.translate(cgl.mMatrix, cgl.mMatrix, this.addTranslate); + + if (this.addMulMat) mat4.mul(cgl.mMatrix, cgl.mMatrix, this.addMulMat); + + mat4.copy(this.absMat, cgl.mMatrix); + } + + render(cgl, dontTransform, dontDrawMesh, ignoreMaterial, ignoreChilds, drawHidden, _time) + { + if (!dontTransform) cgl.pushModelMatrix(); + + if (_time === undefined) _time = gltf.time; + + if (!dontTransform || this.skinRenderer) this.transform(cgl, _time); + + if (this.hidden && !drawHidden) + { + } + else + { + if (this.skinRenderer) + { + this.skinRenderer.time = _time; + if (!dontDrawMesh) + this.mesh.render(cgl, ignoreMaterial, this.skinRenderer, _time); + } + else + { + if (this.mesh && !dontDrawMesh) + this.mesh.render(cgl, ignoreMaterial, null, _time); + } + } + + if (!ignoreChilds && !this.hidden) + for (let i = 0; i < this.children.length; i++) + if (gltf.nodes[this.children[i]]) + gltf.nodes[this.children[i]].render(cgl, dontTransform, dontDrawMesh, ignoreMaterial, ignoreChilds, drawHidden, _time); + + if (!dontTransform)cgl.popModelMatrix(); + } +}; +let tab = null; + +function closeTab() +{ + if (tab)gui.mainTabs.closeTab(tab.id); + tab = null; +} + +function formatVec(arr) +{ + const nums = []; + for (let i = 0; i < arr.length; i++) + { + nums.push(Math.round(arr[i] * 1000) / 1000); + } + + return nums.join(","); +} + +function printNode(html, node, level) +{ + if (!gltf) return; + + html += ""; + + let ident = ""; + let identSpace = ""; + + for (let i = 1; i < level; i++) + { + identSpace += "   "; + let identClass = "identBg"; + if (i == 1)identClass = "identBgLevel0"; + ident += "
    "; + } + let id = CABLES.uuid(); + html += ident; + html += ""; + // html+='
    '; + + if (node.mesh && node.mesh.meshes.length)html += " "; + else html += "  "; + + html += node.name + ""; + + if (node.mesh) + { + html += ""; + for (let i = 0; i < node.mesh.meshes.length; i++) + { + if (i > 0)html += ", "; + html += node.mesh.meshes[i].name; + } + + html += ""; + + html += ""; + html += node.hasSkin() || "-"; + html += ""; + + html += ""; + let countMats = 0; + for (let i = 0; i < node.mesh.meshes.length; i++) + { + if (countMats > 0)html += ", "; + if (gltf.json.materials && node.mesh.meshes[i].hasOwnProperty("material")) + { + if (gltf.json.materials[node.mesh.meshes[i].material]) + { + html += gltf.json.materials[node.mesh.meshes[i].material].name; + countMats++; + } + } + } + if (countMats == 0)html += "none"; + html += ""; + } + else + { + html += "---"; + } + + html += ""; + + if (node._node.translation || node._node.rotation || node._node.scale) + { + let info = ""; + + if (node._node.translation)info += "Translate: `" + formatVec(node._node.translation) + "` || "; + if (node._node.rotation)info += "Rotation: `" + formatVec(node._node.rotation) + "` || "; + if (node._node.scale)info += "Scale: `" + formatVec(node._node.scale) + "` || "; + + html += "  "; + } + + if (node._animRot || node._animScale || node._animTrans) + { + let info = "Animated: "; + if (node._animRot) info += "Rot "; + if (node._animScale) info += "Scale "; + if (node._animTrans) info += "Trans "; + + html += " "; + } + + if (!node._node.translation && !node._node.rotation && !node._node.scale && !node._animRot && !node._animScale && !node._animTrans) html += "-"; + + html += ""; + + html += ""; + let hideclass = ""; + if (node.hidden)hideclass = "node-hidden"; + + // html+=''; + html += "Transform"; + html += " Hierarchy"; + html += " Node"; + + if (node.hasSkin()) + html += " Skin"; + + html += ""; + html += " "; + html += ""; + + html += ""; + + if (node.children) + { + for (let i = 0; i < node.children.length; i++) + html = printNode(html, gltf.nodes[node.children[i]], level + 1); + } + + return html; +} + +function printMaterial(mat, idx) +{ + let html = ""; + html += " " + idx + ""; + html += " " + mat.name + ""; + // html+=' Assign'; + + html += " "; + + const info = JSON.stringify(mat, null, 4).replaceAll("\"", "").replaceAll("\n", "
    "); + + html += "" + info + "', 'title': '" + mat.name + "' });\"> "; + + if (mat.pbrMetallicRoughness && mat.pbrMetallicRoughness.baseColorFactor) + { + let rgb = ""; + rgb += "" + Math.round(mat.pbrMetallicRoughness.baseColorFactor[0] * 255); + rgb += "," + Math.round(mat.pbrMetallicRoughness.baseColorFactor[1] * 255); + rgb += "," + Math.round(mat.pbrMetallicRoughness.baseColorFactor[2] * 255); + + html += "
     "; + } + html += " " + (gltf.shaders[idx] ? "-" : "Assign") + ""; + html += ""; + + html += ""; + return html; +} + +function printInfo() +{ + if (!gltf) return; + + const startTime = performance.now(); + const sizes = {}; + let html = "
    "; + + html += "generator:" + gltf.json.asset.generator; + + let numNodes = 0; + if (gltf.json.nodes)numNodes = gltf.json.nodes.length; + html += "
    Nodes (" + numNodes + ")
    "; + + html += ""; + + html += ""; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += ""; + + for (let i = 0; i < gltf.nodes.length; i++) + { + if (!gltf.nodes[i].isChild) + html = printNode(html, gltf.nodes[i], 1); + } + html += "
    NameMeshSkinMaterialTransformExpose
    "; + + // / ////////////////// + + let numMaterials = 0; + if (gltf.json.materials)numMaterials = gltf.json.materials.length; + html += "
    Materials (" + numMaterials + ")
    "; + + if (!gltf.json.materials || gltf.json.materials.length == 0) + { + } + else + { + html += ""; + html += ""; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += ""; + for (let i = 0; i < gltf.json.materials.length; i++) + { + html += printMaterial(gltf.json.materials[i], i); + } + html += "
    IndexNameColorFunction
    "; + } + + // / /////////////////////// + + html += "
    Meshes (" + gltf.json.meshes.length + ")
    "; + + html += ""; + html += ""; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += ""; + + let sizeBufferViews = []; + sizes.meshes = 0; + + for (let i = 0; i < gltf.json.meshes.length; i++) + { + html += ""; + html += ""; + + html += ""; + + // ------- + + html += ""; + + html += ""; + + html += ""; + html += ""; + + for (let j = 0; j < gltf.json.meshes[i].primitives.length; j++) + { + const accessor = gltf.json.accessors[gltf.json.meshes[i].primitives[j].indices]; + if (accessor) + { + let bufView = accessor.bufferView; + + if (sizeBufferViews.indexOf(bufView) == -1) + { + sizeBufferViews.push(bufView); + if (gltf.json.bufferViews[bufView])sizes.meshes += gltf.json.bufferViews[bufView].byteLength; + } + } + + for (let k in gltf.json.meshes[i].primitives[j].attributes) + { + const attr = gltf.json.meshes[i].primitives[j].attributes[k]; + const bufView2 = gltf.json.accessors[attr].bufferView; + + if (sizeBufferViews.indexOf(bufView2) == -1) + { + sizeBufferViews.push(bufView2); + if (gltf.json.bufferViews[bufView2])sizes.meshes += gltf.json.bufferViews[bufView2].byteLength; + } + } + } + } + html += "
    NameNodeMaterialVerticesAttributes
    " + gltf.json.meshes[i].name + ""; + let count = 0; + let nodename = ""; + for (var j = 0; j < gltf.json.nodes.length; j++) + { + if (gltf.json.nodes[j].mesh == i) + { + count++; + if (count == 1) + { + nodename = gltf.json.nodes[j].name; + } + } + } + if (count > 1) html += (count) + " nodes (" + nodename + " ...)"; + else html += nodename; + html += ""; + for (var j = 0; j < gltf.json.meshes[i].primitives.length; j++) + { + if (gltf.json.meshes[i].primitives[j].hasOwnProperty("material")) + { + if (gltf.json.materials[gltf.json.meshes[i]]) + { + html += gltf.json.materials[gltf.json.meshes[i].primitives[j].material].name + " "; + } + } + else html += "None"; + } + html += ""; + let numVerts = 0; + for (var j = 0; j < gltf.json.meshes[i].primitives.length; j++) + { + if (gltf.json.meshes[i].primitives[j].attributes.POSITION != undefined) + { + let v = parseInt(gltf.json.accessors[gltf.json.meshes[i].primitives[j].attributes.POSITION].count); + numVerts += v; + html += "" + v + "
    "; + } + else html += "-
    "; + } + + if (gltf.json.meshes[i].primitives.length > 0) + html += "=" + numVerts; + html += "
    "; + for (let j = 0; j < gltf.json.meshes[i].primitives.length; j++) + { + html += Object.keys(gltf.json.meshes[i].primitives[j].attributes); + html += " Geometry"; + html += "
    "; + } + html += "
    "; + + // / ////////////////////////////////// + + let numAnims = 0; + if (gltf.json.animations)numAnims = gltf.json.animations.length; + html += "
    Animations (" + numAnims + ")
    "; + + if (gltf.json.animations) + { + html += ""; + html += ""; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += ""; + + sizes.animations = 0; + + for (let i = 0; i < gltf.json.animations.length; i++) + { + for (let j = 0; j < gltf.json.animations[i].samplers.length; j++) + { + let bufView = gltf.json.accessors[gltf.json.animations[i].samplers[j].input].bufferView; + if (sizeBufferViews.indexOf(bufView) == -1) + { + sizeBufferViews.push(bufView); + sizes.animations += gltf.json.bufferViews[bufView].byteLength; + } + + bufView = gltf.json.accessors[gltf.json.animations[i].samplers[j].output].bufferView; + if (sizeBufferViews.indexOf(bufView) == -1) + { + sizeBufferViews.push(bufView); + sizes.animations += gltf.json.bufferViews[bufView].byteLength; + } + } + + for (let j = 0; j < gltf.json.animations[i].channels.length; j++) + { + html += ""; + html += " "; + + html += " "; + html += " "; + + const smplidx = gltf.json.animations[i].channels[j].sampler; + const smplr = gltf.json.animations[i].samplers[smplidx]; + + html += " "; + + html += " "; + + html += ""; + } + } + html += "
    NameTarget nodePathInterpolationKeys
    Anim " + i + ": " + gltf.json.animations[i].name + "" + gltf.nodes[gltf.json.animations[i].channels[j].target.node].name + ""; + html += gltf.json.animations[i].channels[j].target.path + " "; + html += " " + smplr.interpolation + "" + gltf.json.accessors[smplr.output].count; + + html += "  "; + + html += "
    "; + } + else + { + + } + + // / /////////////////// + + let numImages = 0; + if (gltf.json.images)numImages = gltf.json.images.length; + html += "
    Images (" + numImages + ")
    "; + + if (gltf.json.images) + { + html += ""; + + html += ""; + html += " "; + html += " "; + html += " "; + + html += ""; + + sizes.images = 0; + + for (let i = 0; i < gltf.json.images.length; i++) + { + if (gltf.json.images[i].bufferView) + sizes.images += gltf.json.bufferViews[gltf.json.images[i].bufferView].byteLength; + + html += ""; + html += ""; + html += ""; + html += ""; + + html += ""; + } + html += "
    nametypefunc
    " + gltf.json.images[i].name + "" + gltf.json.images[i].mimeType + ""; + + let name = gltf.json.images[i].name; + if (name === undefined)name = gltf.json.images[i].bufferView; + + html += "Expose"; + html += "
    "; + } + + // / /////////////////////// + + let numCameras = 0; + if (gltf.json.cameras)numCameras = gltf.json.cameras.length; + html += "
    Cameras (" + numCameras + ")
    "; + + if (gltf.json.cameras) + { + html += ""; + + html += ""; + html += " "; + html += " "; + html += " "; + html += ""; + + for (let i = 0; i < gltf.json.cameras.length; i++) + { + html += ""; + html += ""; + html += ""; + html += ""; + + html += ""; + } + html += "
    nametypeinfo
    " + gltf.json.cameras[i].name + "" + gltf.json.cameras[i].type + ""; + + if (gltf.json.cameras[i].perspective) + { + html += "yfov: " + Math.round(gltf.json.cameras[i].perspective.yfov * 100) / 100; + html += ", "; + html += "zfar: " + Math.round(gltf.json.cameras[i].perspective.zfar * 100) / 100; + html += ", "; + html += "znear: " + Math.round(gltf.json.cameras[i].perspective.znear * 100) / 100; + } + html += "
    "; + } + + // / //////////////////////////////////// + + let numSkins = 0; + if (gltf.json.skins)numSkins = gltf.json.skins.length; + html += "
    Skins (" + numSkins + ")
    "; + + if (gltf.json.skins) + { + // html += "

    Skins (" + gltf.json.skins.length + ")

    "; + html += ""; + + html += ""; + html += " "; + html += " "; + html += " "; + html += ""; + + for (let i = 0; i < gltf.json.skins.length; i++) + { + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + } + html += "
    nametotal joints
    " + gltf.json.skins[i].name + "" + "" + gltf.json.skins[i].joints.length + ""; + html += "
    "; + } + + // / ////////////////////////// + + let sizeBin = 0; + if (gltf.json.buffers) + sizeBin = gltf.json.buffers[0].byteLength; + + html += "
    File Size Allocation (" + Math.round(sizeBin / 1024) + "k )
    "; + + html += ""; + html += ""; + html += " "; + html += " "; + html += " "; + html += ""; + let sizeUnknown = sizeBin; + for (let i in sizes) + { + // html+=i+':'+Math.round(sizes[i]/1024); + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + sizeUnknown -= sizes[i]; + } + + if (sizeUnknown != 0) + { + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + } + + html += "
    namesize%
    " + i + "" + readableSize(sizes[i]) + " " + Math.round(sizes[i] / sizeBin * 100) + "%
    unknown" + readableSize(sizeUnknown) + " " + Math.round(sizeUnknown / sizeBin * 100) + "%
    "; + html += "
    "; + + tab = new CABLES.UI.Tab("GLTF", { "icon": "cube", "infotext": "tab_gltf", "padding": true, "singleton": true }); + gui.mainTabs.addTab(tab, true); + + tab.addEventListener("onClose", closeTab); + tab.html(html); + + CABLES.UI.Collapsable.setup(ele.byId("groupNodes"), ele.byId("sectionNodes"), false); + CABLES.UI.Collapsable.setup(ele.byId("groupMaterials"), ele.byId("materialtable"), true); + CABLES.UI.Collapsable.setup(ele.byId("groupAnims"), ele.byId("sectionAnim"), true); + CABLES.UI.Collapsable.setup(ele.byId("groupMeshes"), ele.byId("meshestable"), true); + CABLES.UI.Collapsable.setup(ele.byId("groupCameras"), ele.byId("sectionCameras"), true); + CABLES.UI.Collapsable.setup(ele.byId("groupImages"), ele.byId("sectionImages"), true); + CABLES.UI.Collapsable.setup(ele.byId("groupSkins"), ele.byId("sectionSkins"), true); + CABLES.UI.Collapsable.setup(ele.byId("groupBinary"), ele.byId("sectionBinary"), true); + + gui.maintabPanel.show(true); +} + +function readableSize(n) +{ + if (n > 1024) return Math.round(n / 1024) + " kb"; + if (n > 1024 * 500) return Math.round(n / 1024) + " mb"; + else return n + " bytes"; +} +const GltfSkin = class +{ + constructor(node) + { + this._mod = null; + this._node = node; + this._lastTime = 0; + this._matArr = []; + this._m = mat4.create(); + this._invBindMatrix = mat4.create(); + this.identity = true; + } + + renderFinish(cgl) + { + cgl.popModelMatrix(); + this._mod.unbind(); + } + + renderStart(cgl, time) + { + if (!this._mod) + { + this._mod = new CGL.ShaderModifier(cgl, op.name + this._node.name); + + this._mod.addModule({ + "priority": -2, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.skin_head_vert || "", + "srcBodyVert": attachments.skin_vert || "" + }); + + this._mod.addUniformVert("m4[]", "MOD_boneMats", []);// bohnenmatze + const tr = vec3.create(); + } + + const skinIdx = this._node.skin; + const arrLength = gltf.json.skins[skinIdx].joints.length * 16; + + // if (this._lastTime != time || !time) + { + // this._lastTime=inTime.get(); + if (this._matArr.length != arrLength) this._matArr.length = arrLength; + + for (let i = 0; i < gltf.json.skins[skinIdx].joints.length; i++) + { + const i16 = i * 16; + const jointIdx = gltf.json.skins[skinIdx].joints[i]; + const nodeJoint = gltf.nodes[jointIdx]; + + for (let j = 0; j < 16; j++) + this._invBindMatrix[j] = gltf.accBuffers[gltf.json.skins[skinIdx].inverseBindMatrices][i16 + j]; + + mat4.mul(this._m, nodeJoint.modelMatAbs(), this._invBindMatrix); + + for (let j = 0; j < this._m.length; j++) this._matArr[i16 + j] = this._m[j]; + } + + this._mod.setUniformValue("MOD_boneMats", this._matArr); + this._lastTime = time; + } + + this._mod.define("SKIN_NUM_BONES", gltf.json.skins[skinIdx].joints.length); + this._mod.bind(); + + // draw mesh... + cgl.pushModelMatrix(); + if (this.identity)mat4.identity(cgl.mMatrix); + } +}; +// https://raw.githubusercontent.com/KhronosGroup/glTF/master/specification/2.0/figures/gltfOverview-2.0.0b.png + +const + inExec = op.inTrigger("Render"), + dataPort = op.inString("data"), + inFile = op.inUrl("glb File", [".glb"]), + inRender = op.inBool("Draw", true), + inCamera = op.inDropDown("Camera", ["None"], "None"), + inAnimation = op.inString("Animation", ""), + inShow = op.inTriggerButton("Show Structure"), + inCenter = op.inSwitch("Center", ["None", "XYZ", "XZ"], "XYZ"), + inRescale = op.inBool("Rescale", true), + inRescaleSize = op.inFloat("Rescale Size", 2.5), + + inTime = op.inFloat("Time"), + inTimeLine = op.inBool("Sync to timeline", false), + inLoop = op.inBool("Loop", true), + + inNormFormat = op.inSwitch("Normals Format", ["XYZ", "X-ZY"], "XYZ"), + inVertFormat = op.inSwitch("Vertices Format", ["XYZ", "XZ-Y"], "XYZ"), + inCalcNormals = op.inBool("Calc Normals", false), + + inMaterials = op.inObject("Materials"), + inHideNodes = op.inArray("Hide Nodes"), + inUseMatProps = op.inBool("Use Material Properties", false), + inActive = op.inBool("Active", true), + + nextBefore = op.outTrigger("Render Before"), + next = op.outTrigger("Next"), + outGenerator = op.outString("Generator"), + outVersion = op.outNumber("GLTF Version"), + outExtensions = op.outArray("GLTF Extensions Used"), + outAnimLength = op.outNumber("Anim Length", 0), + outAnimTime = op.outNumber("Anim Time", 0), + outJson = op.outObject("Json"), + outAnims = op.outArray("Anims"), + outPoints = op.outArray("BoundingPoints"), + outBounds = op.outObject("Bounds"), + outAnimFinished = op.outTrigger("Finished"), + outLoading = op.outBool("Loading"); + +op.setPortGroup("Timing", [inTime, inTimeLine, inLoop]); + +const le = true; // little endian +const cgl = op.patch.cgl; +inFile.onChange = + inVertFormat.onChange = + inCalcNormals.onChange = + inNormFormat.onChange = reloadSoon; + +let gltfTransforms = 0; +let finishedLoading = false; +let cam = null; +let boundingPoints = []; +let gltf = null; +let maxTime = 0; +let time = 0; +let needsMatUpdate = true; +let timedLoader = null; +let loadingId = null; +let data = null; +const scale = vec3.create(); +let lastTime = 0; +let doCenter = false; + +const boundsCenter = vec3.create(); + +inShow.onTriggered = printInfo; +dataPort.setUiAttribs({ "hideParam": true, "hidePort": true }); +dataPort.onChange = loadData; +inHideNodes.onChange = hideNodesFromData; +inAnimation.onChange = updateAnimation; + +op.setPortGroup("Transform", [inRescale, inRescaleSize, inCenter]); + +inCenter.onChange = updateCenter; + +function updateCamera() +{ + const arr = ["None"]; + if (gltf) + { + for (let i = 0; i < gltf.nodes.length; i++) + { + if (gltf.nodes[i].camera >= 0) + { + arr.push(gltf.nodes[i].name); + } + } + } + inCamera.uiAttribs.values = arr; +} + +function updateCenter() +{ + doCenter = inCenter.get() != "None"; + + if (gltf && gltf.bounds) + { + boundsCenter.set(gltf.bounds.center); + boundsCenter[0] = -boundsCenter[0]; + boundsCenter[1] = -boundsCenter[1]; + boundsCenter[2] = -boundsCenter[2]; + if (inCenter.get() == "XZ") boundsCenter[1] = -gltf.bounds.minY; + } +} + +inRescale.onChange = function () +{ + inRescaleSize.setUiAttribs({ "greyout": !inRescale.get() }); +}; + +inMaterials.onChange = function () +{ + needsMatUpdate = true; +}; + +op.onDelete = function () +{ + closeTab(); +}; + +inTimeLine.onChange = function () +{ + inTime.setUiAttribs({ "greyout": inTimeLine.get() }); +}; + +inCamera.onChange = setCam; + +function setCam() +{ + cam = null; + if (!gltf) return; + + for (let i = 0; i < gltf.nodes.length; i++) + { + if (gltf.nodes[i].name == inCamera.get())cam = new gltfCamera(gltf, gltf.nodes[i]); + } +} + +inExec.onTriggered = function () +{ + if (!finishedLoading) return; + if (!inActive.get()) return; + + gltfTransforms = 0; + if (inTimeLine.get()) time = op.patch.timer.getTime(); + else time = Math.max(0, inTime.get()); + + if (inLoop.get()) + { + time %= maxTime; + if (time < lastTime)outAnimFinished.trigger(); + } + else + { + if (maxTime > 0 && time >= maxTime)outAnimFinished.trigger(); + } + lastTime = time; + + cgl.pushModelMatrix(); + + outAnimTime.set(time || 0); + + if (finishedLoading && gltf && gltf.bounds) + { + if (inRescale.get()) + { + let sc = inRescaleSize.get() / gltf.bounds.maxAxis; + gltf.scale = sc; + vec3.set(scale, sc, sc, sc); + mat4.scale(cgl.mMatrix, cgl.mMatrix, scale); + } + if (doCenter) + { + mat4.translate(cgl.mMatrix, cgl.mMatrix, boundsCenter); + } + } + + let oldScene = cgl.frameStore.currentScene || null; + cgl.frameStore.currentScene = gltf; + + nextBefore.trigger(); + + if (finishedLoading) + { + if (needsMatUpdate) updateMaterials(); + + if (cam) cam.start(time); + + if (gltf) + { + gltf.time = time; + + { + if (gltf.bounds && cgl.shouldDrawHelpers(op)) + { + if (CABLES.UI.renderHelper)cgl.pushShader(CABLES.GL_MARKER.getDefaultShader(cgl)); + else cgl.pushShader(CABLES.GL_MARKER.getSelectedShader(cgl)); + gltf.bounds.render(cgl); + cgl.popShader(); + } + + // if (!gltf.renderMMatrix)gltf.renderMMatrix = mat4.create(); + // cgl.pushModelMatrix(); + // mat4.copy(gltf.renderMMatrix, cgl.mMatrix); + // mat4.identity(cgl.mMatrix); + + if (inRender.get()) + { + for (let i = 0; i < gltf.nodes.length; i++) + if (!gltf.nodes[i].isChild) + gltf.nodes[i].render(cgl); + } + else + { + for (let i = 0; i < gltf.nodes.length; i++) + if (!gltf.nodes[i].isChild) + gltf.nodes[i].render(cgl, false, true); + // render(cgl, dontTransform, dontDrawMesh, ignoreMaterial, ignoreChilds, drawHidden, _time) + } + + // cgl.popModelMatrix(); + } + } + } + + next.trigger(); + cgl.frameStore.currentScene = oldScene; + + cgl.popModelMatrix(); + + if (cam)cam.end(); +}; + +function finishLoading() +{ + if (!gltf) + { + op.setUiError("nogltf", "gltf not found"); + return; + } + op.setUiError("nogltf", null); + if (gltf.loadingMeshes > 0) + { + // op.log("waiting for async meshes..."); + setTimeout(finishLoading, 100); + return; + } + + gltf.timing.push("finishLoading()", Math.round((performance.now() - gltf.startTime))); + + needsMatUpdate = true; + // op.refreshParams(); + outAnimLength.set(maxTime); + + gltf.bounds = new CABLES.CG.BoundingBox(); + // gltf.bounds.applyPos(0, 0, 0); + + if (!gltf)op.setUiError("urlerror", "could not load gltf:
    \"" + inFile.get() + "\"", 2); + else op.setUiError("urlerror", null); + + gltf.timing.push("start calc bounds", Math.round((performance.now() - gltf.startTime))); + + for (let i = 0; i < gltf.nodes.length; i++) + { + const node = gltf.nodes[i]; + node.updateMatrix(); + if (!node.isChild) node.calcBounds(gltf, null, gltf.bounds); + } + + if (gltf.bounds)outBounds.set(gltf.bounds); + + gltf.timing.push("calced bounds", Math.round((performance.now() - gltf.startTime))); + + hideNodesFromData(); + + gltf.timing.push("hideNodesFromData", Math.round((performance.now() - gltf.startTime))); + + if (tab)printInfo(); + + gltf.timing.push("printinfo", Math.round((performance.now() - gltf.startTime))); + + updateCamera(); + setCam(); + outPoints.set(boundingPoints); + + if (gltf) + { + if (inFile.get() && !inFile.get().startsWith("data:")) + { + op.setUiAttrib({ "extendTitle": CABLES.basename(inFile.get()) }); + } + + gltf.loaded = Date.now(); + // if (gltf.bounds)outBounds.set(gltf.bounds); + } + + if (gltf) + { + for (let i = 0; i < gltf.nodes.length; i++) + { + if (!gltf.nodes[i].isChild) + { + gltf.nodes[i].render(cgl, false, true, true, false, true, 0); + } + } + + for (let i = 0; i < gltf.nodes.length; i++) + { + const node = gltf.nodes[i]; + node.children = uniqueArray(node.children); // stupid fix why are there too many children ?! + } + } + + updateCenter(); + updateAnimation(); + + outLoading.set(false); + + cgl.patch.loading.finished(loadingId); + loadingId = null; + + // if (gltf.chunks.length > 1) gltf.chunks[1] = null; + // if (gltf.chunks.length > 2) gltf.chunks[2] = null; + + op.setUiAttrib({ "accBuffersDelete": CABLES.basename(inFile.get()) }); + + if (gltf.accBuffersDelete) + { + for (let i = 0; i < gltf.accBuffersDelete.length; i++) + { + gltf.accBuffers[gltf.accBuffersDelete[i]] = null; + } + } + + finishedLoading = true; +} + +function loadBin(addCacheBuster) +{ + if (!inActive.get()) return; + + if (!loadingId)loadingId = cgl.patch.loading.start("gltfScene", inFile.get()); + + let fileToLoad = inFile.get(); + let url = op.patch.getFilePath(String(inFile.get())); + if (inFile.get() && !inFile.get().startsWith("data:")) + { + if (addCacheBuster === true)url += "?rnd=" + CABLES.generateUUID(); + } + finishedLoading = false; + outLoading.set(true); + fetch(url) + .then((res) => { return res.arrayBuffer(); }) + .then((arrayBuffer) => + { + if (inFile.get() != fileToLoad) + { + cgl.patch.loading.finished(loadingId); + loadingId = null; + return; + } + + boundingPoints = []; + + maxTime = 0; + gltf = parseGltf(arrayBuffer); + + finishLoading(); + }); + closeTab(); + + const oReq = new XMLHttpRequest(); + oReq.open("GET", url, true); + oReq.responseType = "arraybuffer"; + + cgl.patch.loading.addAssetLoadingTask(() => + { + + }); +} + +// op.onFileChanged = function (fn) +// { +// gltf.accBuffersDelete[i]; +// if (fn && fn.length > 3 && inFile.get() && inFile.get().indexOf(fn) > -1) reloadSoon(true); +// }; + +op.onFileChanged = function (fn) +{ + if (inFile.get() && inFile.get().indexOf(fn) > -1) + { + reloadSoon(true); + } +}; + +inActive.onChange = () => +{ + if (inActive.get()) reloadSoon(); + + if (!inActive.get()) + { + gltf = null; + } +}; + +function reloadSoon(nocache) +{ + clearTimeout(timedLoader); + timedLoader = setTimeout(function () { loadBin(nocache); }, 30); +} + +function updateMaterials() +{ + if (!gltf) return; + + gltf.shaders = {}; + + if (inMaterials.links.length == 1 && inMaterials.get()) + { + // just accept a associative object with shader in it + const op = inMaterials.links[0].portOut.parent; + + const portShader = op.getPort("Shader"); + const portName = op.getPort("Material Name"); + + if (!portShader && !portName) + { + const inMats = inMaterials.get(); + for (let matname in inMats) + { + if (inMats[matname] && gltf.json.materials) + for (let i = 0; i < gltf.json.materials.length; i++) + { + if (gltf.json.materials[i].name == matname) + { + if (gltf.shaders[i]) + { + op.warn("double material assignment:", name); + } + gltf.shaders[i] = inMats[matname]; + } + } + } + } + } + + if (inMaterials.get()) + { + for (let j = 0; j < inMaterials.links.length; j++) + { + const op = inMaterials.links[j].portOut.parent; + const portShader = op.getPort("Shader"); + const portName = op.getPort("Material Name"); + + if (portShader && portName && portShader.get()) + { + const name = portName.get(); + if (gltf.json.materials) + for (let i = 0; i < gltf.json.materials.length; i++) + if (gltf.json.materials[i].name == name) + { + if (gltf.shaders[i]) + { + op.warn("double material assignment:", name); + } + gltf.shaders[i] = portShader.get(); + } + } + } + } + needsMatUpdate = false; + if (tab)printInfo(); +} + +function hideNodesFromArray() +{ + const hideArr = inHideNodes.get(); + + if (!gltf || !data || !data.hiddenNodes) return; + if (!hideArr) + { + return; + } + + for (let i = 0; i < hideArr.length; i++) + { + const n = gltf.getNode(hideArr[i]); + if (n)n.hidden = true; + } +} + +function hideNodesFromData() +{ + if (!data)loadData(); + if (!gltf) return; + + gltf.unHideAll(); + + if (data && data.hiddenNodes) + { + for (const i in data.hiddenNodes) + { + const n = gltf.getNode(i); + if (n) n.hidden = true; + else op.verbose("node to be hidden not found", i, n); + } + } + hideNodesFromArray(); +} + +function loadData() +{ + data = dataPort.get(); + + if (!data || data === "")data = {}; + else data = JSON.parse(data); + + if (gltf)hideNodesFromData(); + + return data; +} + +function saveData() +{ + dataPort.set(JSON.stringify(data)); +} + +function updateAnimation() +{ + if (gltf && gltf.nodes) + { + for (let i = 0; i < gltf.nodes.length; i++) + { + gltf.nodes[i].setAnimAction(inAnimation.get()); + } + } +} + +function findParents(nodes, childNodeIndex) +{ + for (let i = 0; i < gltf.nodes.length; i++) + { + if (gltf.nodes[i].children.indexOf(childNodeIndex) >= 0) + { + nodes.push(gltf.nodes[i]); + if (gltf.nodes[i].isChild) findParents(nodes, i); + } + } +} + +op.exposeTexture = function (name) +{ + const newop = gui.corePatch().addOp("Ops.Gl.GLTF.GltfTexture"); + newop.getPort("Name").set(name); + setNewOpPosition(newop, 1); + op.patch.link(op, next.name, newop, "Render"); + gui.patchView.centerSelectOp(newop.id, true); + gui.patchView.testCollision(newop); +}; + +op.exposeGeom = function (name, idx) +{ + const newop = gui.corePatch().addOp("Ops.Gl.GLTF.GltfGeometry"); + newop.getPort("Name").set(name); + newop.getPort("Submesh").set(idx); + setNewOpPosition(newop, 1); + op.patch.link(op, next.name, newop, "Update"); + gui.patchView.centerSelectOp(newop.id, true); + gui.patchView.testCollision(newop); +}; + +function setNewOpPosition(newOp, num) +{ + num = num || 1; + + newOp.setUiAttrib({ "translate": { "x": op.uiAttribs.translate.x, "y": op.uiAttribs.translate.y + num * CABLES.GLUI.glUiConfig.newOpDistanceY } }); +} + +op.exposeNode = function (name, type, options) +{ + let tree = type == "hierarchy"; + if (tree) + { + let ops = []; + + for (let i = 0; i < gltf.nodes.length; i++) + { + if (gltf.nodes[i].name == name) + { + let arrHierarchy = []; + const node = gltf.nodes[i]; + findParents(arrHierarchy, i); + + arrHierarchy = arrHierarchy.reverse(); + arrHierarchy.push(node, node); + + let prevPort = next.name; + let prevOp = op; + for (let j = 0; j < arrHierarchy.length; j++) + { + const newop = gui.corePatch().addOp("Ops.Gl.GLTF.GltfNode_v2"); + newop.getPort("Node Name").set(arrHierarchy[j].name); + op.patch.link(prevOp, prevPort, newop, "Render"); + setNewOpPosition(newop, j); + + if (j == arrHierarchy.length - 1) + { + newop.getPort("Transformation").set(false); + } + else + { + newop.getPort("Draw Mesh").set(false); + newop.getPort("Draw Childs").set(false); + } + + prevPort = "Next"; + prevOp = newop; + ops.push(newop); + gui.patchView.testCollision(newop); + } + } + } + + for (let i = 0; i < ops.length; i++) + { + ops[i].selectChilds(); + } + } + else + { + let newopname = "Ops.Gl.GLTF.GltfNode_v2"; + if (options && options.skin)newopname = "Ops.Gl.GLTF.GltfSkin"; + if (type == "transform")newopname = "Ops.Gl.GLTF.GltfNodeTransform_v2"; + + gui.serverOps.loadOpLibs(newopname, () => + { + let newop = gui.corePatch().addOp(newopname); + + newop.getPort("Node Name").set(name); + setNewOpPosition(newop); + op.patch.link(op, next.name, newop, "Render"); + gui.patchView.centerSelectOp(newop.id, true); + gui.patchView.testCollision(newop); + }); + } + gui.closeModal(); +}; + +op.assignMaterial = function (name) +{ + const newop = gui.corePatch().addOp("Ops.Gl.GLTF.GltfSetMaterial"); + newop.getPort("Material Name").set(name); + op.patch.link(op, inMaterials.name, newop, "Material"); + gui.patchView.centerSelectOp(newop.id, true); + setNewOpPosition(newop); + gui.patchView.testCollision(newop); + + gui.closeModal(); +}; + +op.toggleNodeVisibility = function (name) +{ + const n = gltf.getNode(name); + n.hidden = !n.hidden; + data.hiddenNodes = data.hiddenNodes || {}; + + if (n) + if (n.hidden)data.hiddenNodes[name] = true; + else delete data.hiddenNodes[name]; + + saveData(); +}; + +op.showAnim = function (anim, channel) +{ + const an = gltf.json.animations[anim]; + const chan = gltf.json.animations[anim].channels[channel]; + + const node = gltf.nodes[chan.target.node]; + const sampler = an.samplers[chan.sampler]; + + const acc = gltf.json.accessors[sampler.input]; + const bufferIn = gltf.accBuffers[sampler.input]; + + const accOut = gltf.json.accessors[sampler.output]; + const bufferOut = gltf.accBuffers[sampler.output]; +}; + +function uniqueArray(arr) +{ + const u = {}, a = []; + for (let i = 0, l = arr.length; i < l; ++i) + { + if (!u.hasOwnProperty(arr[i])) + { + a.push(arr[i]); + u[arr[i]] = 1; + } + } + return a; +} + + +}; + +Ops.Gl.GLTF.GltfScene_v3.prototype = new CABLES.Op(); +CABLES.OPS["a9c59d94-8aea-4c20-ab5d-63ce46ecfa16"]={f:Ops.Gl.GLTF.GltfScene_v3,objName:"Ops.Gl.GLTF.GltfScene_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfSetMaterial +// +// ************************************************************** + +Ops.Gl.GLTF.GltfSetMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inShader = op.inObject("Shader", null, "shader"), + inName = op.inString("Material Name", "none"), + outMat = op.outObject("Material"); + +inName.onChange = +inShader.onChange = function () +{ + op.setTitle("Material " + inName.get()); + outMat.set(null); + outMat.set(inShader.get()); +}; + + +}; + +Ops.Gl.GLTF.GltfSetMaterial.prototype = new CABLES.Op(); +CABLES.OPS["baf968ea-e4df-4fca-9cda-e6ddd38a4200"]={f:Ops.Gl.GLTF.GltfSetMaterial,objName:"Ops.Gl.GLTF.GltfSetMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfSkin +// +// ************************************************************** + +Ops.Gl.GLTF.GltfSkin = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"joints_vert":"\n\n\n\n\n // if(attrJoints.x!=-1.0)\n {\n int index=int(attrJoints.x);\n vec4 newPos = (MOD_boneMats[index] * pos) * attrWeights.x;\n vec3 newNorm = (vec4((MOD_boneMats[index] * vec4(norm.xyz, 0.0)) * attrWeights.x).xyz);\n\n // if(attrJoints.y!=-1.0)\n {\n index=int(attrJoints.y);\n newPos += (MOD_boneMats[index] * pos) * attrWeights.y;\n newNorm = (vec4((MOD_boneMats[index] * vec4(norm.xyz, 0.0)) * attrWeights.y).xyz)+newNorm;\n }\n\n // if(attrJoints.z!=-1.0)\n {\n index=int(attrJoints.z);\n newPos += (MOD_boneMats[index] * pos) * attrWeights.z;\n newNorm = (vec4((MOD_boneMats[index] * vec4(norm.xyz, 0.0)) * attrWeights.z).xyz)+newNorm;\n }\n\n // if(attrJoints.w!=-1.0)\n {\n index=int(attrJoints.w);\n newPos += (MOD_boneMats[index] * pos) * attrWeights.w ;\n newNorm = (vec4((MOD_boneMats[index] * vec4(norm.xyz, 0.0)) * attrWeights.w).xyz)+newNorm;\n }\n\n pos=newPos;\n norm=normalize(newNorm.xyz);\n }\n\n\n\n// mat4 skinMatrix =\n// attrWeights.x * MOD_boneMats[int(attrJoints.x)] +\n// attrWeights.y * MOD_boneMats[int(attrJoints.y)] +\n// attrWeights.z * MOD_boneMats[int(attrJoints.z)] +\n// attrWeights.w * MOD_boneMats[int(attrJoints.w)];\n\n// mMatrix*=skinMatrix;\n\n\n\n\n\n\n\n\n\n","joints_head_vert":"IN vec4 attrWeights;\nIN vec4 attrJoints;\n\n\nUNI mat4 MOD_boneMats[SKIN_NUM_BONES];",}; +const + exec = op.inTrigger("Render"), + inNodeName = op.inString("Node Name", "default"), + inSceneTime = op.inBool("Scene Time", true), + inTime = op.inFloat("Time", 0), + inBlendAnims = op.inArray("Blend Anims"), + outFound = op.outBool("Found Node"), + outFoundSkin = op.outBool("Found Skin"), + next = op.outTrigger("Next"); + +exec.onTriggered = update; +const cgl = op.patch.cgl; +const mod = new CGL.ShaderModifier(cgl, op.name); +let arr = []; +let node = null; +const tr = vec3.create(); + +inSceneTime.onChange = updateTimeInputs; + +updateTimeInputs(); + +function updateTimeInputs() +{ + inTime.setUiAttribs({ "greyout": inSceneTime.get() }); +} + +inNodeName.onChange = () => +{ + op.setUiAttrib({ "extendTitle": inNodeName.get() }); + node = null; + update(); +}; + +function update() +{ + if (!cgl.frameStore || !cgl.frameStore.currentScene) return; + + const gltf = cgl.frameStore.currentScene; + const name = inNodeName.get(); + + for (let i = 0; i < cgl.frameStore.currentScene.nodes.length; i++) + { + if (cgl.frameStore.currentScene.nodes[i].name == name) + { + node = cgl.frameStore.currentScene.nodes[i]; + outFound.set(true); + } + } + + if (!node) + { + outFound.set(false); + return; + } + outFoundSkin.set(node.skin > -1); + + if (node.skin == -1) + { + return; + } + + if (!gltf.json.skins) + { + op.warn.log("no skins found..."); + return; + } + + let time = gltf.time; + if (!inSceneTime.get()) + { + time = inTime.get(); + } + + if (!inSceneTime.get()) + for (let i = 0; i < gltf.nodes.length; i++) + if (!gltf.nodes[i].isChild) + gltf.nodes[i].render(cgl, false, true, true, false, false, time); + + transformBlend(node, cgl, time); + + node.render(cgl, true, false, true, false, true, time); + + next.trigger(); +} + +function transformBlend(node, cgl, time) +{ + // let hasTrans = node._anims.trans.length > 0; + // let hasRot = node._anims.rot.length > 0; + // hasRot = false; + // let hasScale = node._anims.scale.length > 0; + // hasScale = false; + + const animnames = Object.keys(cgl.frameStore.currentScene.uniqueAnimNames); + + const weights = inBlendAnims.get(); + + // if ((!hasTrans && !hasRot && !hasScale) || _w === 0) + // { + // mat4.mul(cgl.mMatrix, cgl.mMatrix, node.mat); + // node._animMat = null; + // } + // else + { + // node._animActions[n][path] + // animnames + + node._animMat = node._animMat || mat4.create(); + mat4.identity(node._animMat); + + const playAnims = true; + + if (playAnims) + { + let _x = 0, _y = 0, _z = 0, _w = 0; + + for (let i = 0; i < animnames.length; i++) + { + const animName = animnames[i]; + if (!node._animActions[animName] || !node._animActions[animName].translation) continue; + + let _time = time;// times[animnames[i]]; + // let _anim = node._anims.trans[i]; + + let _anim = node._animActions[animName].translation; + let weight = weights[i]; + + if (_anim) + { + _x += _anim[0].getValue(_time) * weight + (_anim[0].getValue(0) * (1 - weight)); + _y += _anim[1].getValue(_time) * weight + (_anim[1].getValue(0) * (1 - weight)); + _z += _anim[2].getValue(_time) * weight + (_anim[2].getValue(0) * (1 - weight)); + _w += 1; + } + } + + if (_w > 0) + { + _x /= _w; + _y /= _w; + _z /= _w; + + mat4.translate(node._animMat, node._animMat, [_x, _y, _z]); + } + } + else + if (node._node.translation) mat4.translate(node._animMat, node._animMat, node._node.translation); + + if (playAnims) + { + node._tempQuats = node._tempQuats || []; + + for (let i = 0; i < animnames.length; i++) + { + const animName = animnames[i]; + if (!node._animActions[animName] || !node._animActions[animName].translation) continue; + + let _time = time;// times[animnames[i]]; + + let _tempQuat2 = node._tempQuats[i] || quat.create(); + + // let shouldAnim = node._anims.rot[i] && node._anims.trans[i]; + // let _anim = node._anims.rot[i]; + let _anim = node._animActions[animName].rotation; + let weight = weights[i]; + + if (_anim) + { + CABLES.TL.Anim.slerpQuaternion(_time, _tempQuat2, _anim[0], _anim[1], _anim[2], _anim[3]); + } + node._tempQuats[i] = (_tempQuat2); + } + + let _finalQuat = node._tempQuat; + + for (let i = 0; i < node._tempQuats.length; i++) + { + // if (weights[i] === 0) continue; + quat.slerp(_finalQuat, _finalQuat, node._tempQuats[i], node._anims.trans[i] ? weights[i] : 0); + } + + mat4.fromQuat(node._tempMat, _finalQuat); + mat4.mul(node._animMat, node._animMat, node._tempMat); + + node._tempQuat = _finalQuat; + } + else if (node._rot) + { + mat4.fromQuat(node._tempRotmat, node._rot); + mat4.mul(node._animMat, node._animMat, node._tempRotmat); + } + + if (playAnims) + { + let _x = 0, _y = 0, _z = 0, _w = 0; + + for (let i = 0; i < animnames.length; i++) + { + const animName = animnames[i]; + if (!node._animActions[animName] || !node._animActions[animName].translation) continue; + + let _time = time;// times[animnames[i]]; + + // let _anim = node._anims.scale[i]; + let _anim = node._animActions[animName].scaling; + let weight = weights[i]; + + if (_anim) + { + _x += _anim[0].getValue(_time) * weight; + _y += _anim[1].getValue(_time) * weight; + _z += _anim[2].getValue(_time) * weight; + _w += weight; + } + } + + if (_w > 0) + { + _x /= _w; + _y /= _w; + _z /= _w; + + mat4.scale(node._animMat, node._animMat, [_x, _y, _z]); + } + } + else if (node._scale) mat4.scale(node._animMat, node._animMat, node._scale); + + mat4.mul(cgl.mMatrix, cgl.mMatrix, node._animMat); + } + + if (node.addTranslate)mat4.translate(cgl.mMatrix, cgl.mMatrix, node.addTranslate); + + if (node.addMulMat) mat4.mul(cgl.mMatrix, cgl.mMatrix, node.addMulMat); + + mat4.copy(node.absMat, cgl.mMatrix); +} + + +}; + +Ops.Gl.GLTF.GltfSkin.prototype = new CABLES.Op(); +CABLES.OPS["0ce62e9b-d990-4ae2-b40f-a036a021aa9c"]={f:Ops.Gl.GLTF.GltfSkin,objName:"Ops.Gl.GLTF.GltfSkin"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfTexture +// +// ************************************************************** + +Ops.Gl.GLTF.GltfTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Render"), + imgName = op.inString("Name", ""), + tfilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"], "mipmap"), + wrap = op.inValueSelect("Wrap", ["repeat", "mirrored repeat", "clamp to edge"], "clamp to edge"), + aniso = op.inSwitch("Anisotropic", [0, 1, 2, 4, 8, 16], 0), + flip = op.inValueBool("Flip", false), + unpackAlpha = op.inValueBool("Pre Multiplied Alpha", false), + outTex = op.outTexture("Texture"), + width = op.outNumber("Width"), + height = op.outNumber("Height"), + type = op.outString("Type"), + outFound = op.outBool("Found"); + +const cgl = op.patch.cgl; +let tex = null; +let cgl_filter = 0; +let cgl_wrap = 0; +let cgl_aniso = 0; + +aniso.onChange = tfilter.onChange = onFilterChange; +wrap.onChange = onWrapChange; +imgName.onChange = flip.onChange = unpackAlpha.onChange = function () { reloadSoon(); }; + +function reloadSoon() +{ + tex = null; +} + +inExec.onTriggered = function () +{ + if (tex) return; + + if (!cgl.frameStore.currentScene || !cgl.frameStore.currentScene.json) return; + + if (cgl.frameStore.currentScene.chunks.length < 2) + { + return; + } + + if (!cgl.frameStore.currentScene.json.images) return; + + let img = null; + + for (let i = 0; i < cgl.frameStore.currentScene.json.images.length; i++) + { + if (cgl.frameStore.currentScene.json.images[i].name == imgName.get() || cgl.frameStore.currentScene.json.images[i].bufferView == parseFloat(imgName.get())) + { + img = cgl.frameStore.currentScene.json.images[i]; + } + } + if (!img) + { + tex = CGL.Texture.getEmptyTexture(cgl); + outFound.set(false); + outTex.set(tex); + width.set(tex.width); + height.set(tex.height); + return; + } + + const buffView = cgl.frameStore.currentScene.json.bufferViews[img.bufferView]; + let dv = cgl.frameStore.currentScene.chunks[1].dataView; + + if (!buffView) return; + const data = new Uint8Array(buffView.byteLength); + + for (let i = 0; i < buffView.byteLength; i++) + data[i] = dv.getUint8(buffView.byteOffset + i); + + const blob = new Blob([data.buffer], { "type": img.mimeType }); + const sourceURI = URL.createObjectURL(blob); + + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + else if (tfilter.get() == "Anisotropic") cgl_filter = CGL.Texture.FILTER_ANISOTROPIC; + + cgl_aniso = parseFloat(aniso.get()); + + tex = CGL.Texture.load(cgl, sourceURI, + function (err) + { + if (err) + { + outFound.set(false); + } + + outTex.set(tex); + + width.set(tex.width); + height.set(tex.height); + type.set(img.mimeType); + outTex.set(null); + outTex.set(tex); + outFound.set(true); + }, { + "anisotropic": cgl_aniso, + "wrap": cgl_wrap, + "flip": flip.get(), + "unpackAlpha": unpackAlpha.get(), + "filter": cgl_filter + }); + + outTex.set(null); + outTex.set(tex); +}; + +function onFilterChange() +{ + reloadSoon(); +} + +function onWrapChange() +{ + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + reloadSoon(); +} + + +}; + +Ops.Gl.GLTF.GltfTexture.prototype = new CABLES.Op(); +CABLES.OPS["6479a948-7a48-42a3-b40a-794f4364715f"]={f:Ops.Gl.GLTF.GltfTexture,objName:"Ops.Gl.GLTF.GltfTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfTransformNode +// +// ************************************************************** + +Ops.Gl.GLTF.GltfTransformNode = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Render"), + inNodeName = op.inString("Node Name"), + inPosX = op.inFloat("Translate X", 0), + inPosY = op.inFloat("Translate Y", 0), + inPosZ = op.inFloat("Translate Z", 0), + inRotX = op.inFloat("Rotation X", 0), + inRotY = op.inFloat("Rotation Y", 0), + inRotZ = op.inFloat("Rotation Z", 0), + next = op.outTrigger("Next"), + outFound = op.outBool("Found"); + +const cgl = op.patch.cgl; + +let q = quat.create(); +let mat = mat4.create(); +let qmat = mat4.create(); + +let node = null; +let currentSceneLoaded = null; + +inNodeName.onChange = function () +{ + node = null; + outFound.set(false); + op.setUiAttrib({ "extendTitle": inNodeName.get() }); +}; + +op.onDelete = function () +{ + // todo restore orig values! +}; + +inExec.onTriggered = function () +{ + if (!cgl.frameStore.currentScene) return; + if (currentSceneLoaded != cgl.frameStore.currentScene.loaded) node = null; + + if (!node) + { + const name = inNodeName.get(); + + if (!cgl.frameStore || !cgl.frameStore.currentScene || !cgl.frameStore.currentScene.nodes) return; + + currentSceneLoaded = cgl.frameStore.currentScene.loaded; + + for (let i = 0; i < cgl.frameStore.currentScene.nodes.length; i++) + { + if (cgl.frameStore.currentScene.nodes[i].name == name) + { + node = cgl.frameStore.currentScene.nodes[i]; + outFound.set(true); + } + } + } + + if (node) + { + mat4.identity(mat); + mat4.translate(mat, mat, [inPosX.get(), inPosY.get(), inPosZ.get()]); + + mat4.rotateX(mat, mat, inRotX.get()); + mat4.rotateY(mat, mat, inRotY.get()); + mat4.rotateZ(mat, mat, inRotZ.get()); + + node.addMulMat = mat; + } + + next.trigger(); +}; + + +}; + +Ops.Gl.GLTF.GltfTransformNode.prototype = new CABLES.Op(); +CABLES.OPS["bccdb19d-6786-4656-90d7-e798346ea644"]={f:Ops.Gl.GLTF.GltfTransformNode,objName:"Ops.Gl.GLTF.GltfTransformNode"}; + + + + +// ************************************************************** +// +// Ops.Gl.GLTF.GltfVertexAnim +// +// ************************************************************** + +Ops.Gl.GLTF.GltfVertexAnim = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Render"), + inNodeName = op.inString("Node Name", "default"), + inSceneTime = op.inBool("Scene Time", true), + inTime = op.inFloat("Time", 0), + outFound = op.outBoolNum("Found Node"), + next = op.outTrigger("Next"); + +exec.onTriggered = render; +const cgl = op.patch.cgl; +let node = null; +let needsUpdate = true; +let origGMesh = null; +let mesh = null; +let geom = null; +let morphGeom = null; +let origGeom = null; + +inSceneTime.onChange = updateTimeInputs; +updateTimeInputs(); + +let lastMorph1 = -1; +let lastMorph2 = -1; +let lastFract = -1; + +inNodeName.onChange = () => +{ + op.setUiAttrib({ "extendTitle": inNodeName.get() }); + node = null; + needsUpdate = true; + lastMorph1 = -1; + lastMorph2 = -1; + lastFract = -1; +}; + +function updateTimeInputs() +{ + inTime.setUiAttribs({ "greyout": inSceneTime.get() }); +} + +function morph(finalGeom, mt, mt2, fract) +{ + if (mt && mt.vertices && mt2) + { + if (finalGeom.vertexNormals.length != mt.vertexNormals.length) + finalGeom.vertexNormals = new Float32Array(mt.vertexNormals.length); + + for (let i = 0; i < finalGeom.vertices.length; i++) + { + finalGeom.vertices[i] = + origGeom.vertices[i] + + (1.0 - fract) * mt.vertices[i] + + fract * mt2.vertices[i]; + + finalGeom.vertexNormals[i] = + (1.0 - fract) * mt.vertexNormals[i] + + fract * mt2.vertexNormals[i]; + } + } +} + + +function update() +{ + if (!cgl.frameStore.currentScene) return; + + needsUpdate = false; + mesh = null; + + const gltf = cgl.frameStore.currentScene; + const name = inNodeName.get(); + for (let i = 0; i < cgl.frameStore.currentScene.nodes.length; i++) + { + op.log(cgl.frameStore.currentScene.nodes[i].name); + if (cgl.frameStore.currentScene.nodes[i].name == name) + { + node = cgl.frameStore.currentScene.nodes[i]; + outFound.set(true); + op.log(node); + + if (node.mesh && node.mesh.meshes) + { + // mesh=node.mesh.meshes[0].copy(); + origGMesh = node.mesh.meshes[0]; + origGeom = node.mesh.meshes[0].geom; + geom = node.mesh.meshes[0].geom.copy(); + morphGeom = geom.copy(); + mesh = new CGL.Mesh(cgl, geom); + // mesh.setGeom(); + } + else op.warn("[gltfvertexanim] node found but no meshes"); + + op.log("orig mesh", node.mesh.meshes[0]); + op.log("mesh", mesh); + } + } + + if (!node) + { + outFound.set(false); + } +} + +function render() +{ + if (!cgl.frameStore.currentScene) return; + + if (needsUpdate) update(); + + if (!geom) return; + if (!mesh) return; + + let time = cgl.frameStore.currentScene.time; + if (!inSceneTime.get())time = inTime.get(); + + if (time >= geom.morphTargets.length - 1) time = geom.morphTargets.length - 1; + + let morph1 = Math.floor(time); + let morph2 = Math.floor(time + 1); + const fract = time % 1; + + if (lastMorph1 != morph1 || lastMorph2 != morph2 || fract != lastFract) + { + const mt = geom.morphTargets[morph1]; + const mt2 = geom.morphTargets[morph2]; + + morph(morphGeom, mt, mt2, fract); + + mesh.updateNormals(morphGeom); + mesh.updateVertices(morphGeom); + + lastMorph1 = morph1; + lastMorph2 = morph2; + lastFract = fract; + } + + mesh.render(cgl.getShader()); + + next.trigger(); +} + + +}; + +Ops.Gl.GLTF.GltfVertexAnim.prototype = new CABLES.Op(); +CABLES.OPS["e64b035c-133e-43b0-9ff8-fcd03ee5f26e"]={f:Ops.Gl.GLTF.GltfVertexAnim,objName:"Ops.Gl.GLTF.GltfVertexAnim"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.AlignGeometry +// +// ************************************************************** + +Ops.Gl.Geometry.AlignGeometry = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + geometry = op.inObject("Geometry"), + x = op.inValueSelect("X", ["Ignore", "Center", "Max", "Min"], "Ignore"), + y = op.inValueSelect("Y", ["Ignore", "Center", "Max", "Min"], "Ignore"), + z = op.inValueSelect("Z", ["Ignore", "Center", "Max", "Min"], "Ignore"), + outGeom = op.outObject("Result"); + +x.onChange = y.onChange = z.onChange = geometry.onChange = update; + +const + axis = [0, 0, 0], + ALIGN_NONE = 0, + ALIGN_CENTER = 1, + ALIGN_MAX = 2, + ALIGN_MIN = 3; + +let oldGeom = null, + geom = null; + +function getAxisId(port) +{ + if (port.get() == "Ignore") return ALIGN_NONE; + if (port.get() == "Center") return ALIGN_CENTER; + if (port.get() == "Max") return ALIGN_MAX; + if (port.get() == "Min") return ALIGN_MIN; +} + +function update() +{ + if (oldGeom != geometry.get()) + { + geom = null; + oldGeom = geometry.get(); + } + + if (!oldGeom) + { + outGeom.set(null); + return; + } + + axis[0] = getAxisId(x); + axis[1] = getAxisId(y); + axis[2] = getAxisId(z); + + const bounds = oldGeom.getBounds(); + if (!geom) geom = oldGeom.copy(); + + for (let axi = 0; axi < 3; axi++) + { + let min = 0, max = 0; + if (axi === 0) + { + min = bounds.minX; + max = bounds.maxX; + } + else if (axi == 1) + { + min = bounds.minY; + max = bounds.maxY; + } + else if (axi == 2) + { + min = bounds.minZ; + max = bounds.maxZ; + } + + if (axis[axi] == ALIGN_NONE) + { + for (let i = 0; i < geom.vertices.length; i += 3) + geom.vertices[i + axi] = oldGeom.vertices[i + axi]; + } + else if (axis[axi] == ALIGN_CENTER) + { + const off = min + (max - min) / 2; + for (let i = 0; i < geom.vertices.length; i += 3) + geom.vertices[i + axi] = oldGeom.vertices[i + axi] - off; + } + else if (axis[axi] == ALIGN_MAX) + { + for (let i = 0; i < geom.vertices.length; i += 3) + geom.vertices[i + axi] = oldGeom.vertices[i + axi] - max; + } + else if (axis[axi] == ALIGN_MIN) + { + for (let i = 0; i < geom.vertices.length; i += 3) + geom.vertices[i + axi] = oldGeom.vertices[i + axi] - min; + } + } + + outGeom.set(null); + outGeom.set(geom); +} + + +}; + +Ops.Gl.Geometry.AlignGeometry.prototype = new CABLES.Op(); +CABLES.OPS["955c1a40-17ca-4944-8cf6-07e14212be2d"]={f:Ops.Gl.Geometry.AlignGeometry,objName:"Ops.Gl.Geometry.AlignGeometry"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.BoundingBoxVisible +// +// ************************************************************** + +Ops.Gl.Geometry.BoundingBoxVisible = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Exec"), + inBB = op.inObject("Boundings"), + inActive = op.inBool("Active", true), + inDraw = op.inBool("Draw", true), + + inWidth = op.inFloat("Width", 1), + inHeight = op.inFloat("Height", 1), + inLength = op.inFloat("Length", 1), + + next = op.outTrigger("Next"), + result = op.outBool("Visible"), + + cgl = op.patch.cgl, + trans = vec3.create(), + m = mat4.create(), + pos = vec3.create(), + identVec = vec3.create(); + +let + vp = null, + geom = null, + bb = null, + mesh = null; + +inBB.onLinkChanged = updateUi; +updateUi(); + +function isVisible(posi) +{ + // vec3.add(pos,pos,posi); + vec3.transformMat4(pos, posi, m); + + vec3.transformMat4(trans, pos, cgl.pMatrix); + + const xp = (trans[0] * vp[2] / 2) + vp[2] / 2; + const yp = (trans[1] * vp[3] / 2) + vp[3] / 2; + + return pos[2] < 0.0 && xp > 0 && xp < vp[2] && yp > 0 && yp < vp[3]; +} + +function updateUi() +{ + inWidth.setUiAttribs({ "greyout": inBB.isLinked() }); + inHeight.setUiAttribs({ "greyout": inBB.isLinked() }); + inLength.setUiAttribs({ "greyout": inBB.isLinked() }); +} + +inHeight.onChange = +inLength.onChange = +inWidth.onChange = +inBB.onChange = function () +{ + mesh = null; +}; +buildMesh(); + +function buildMesh() +{ + if (inBB.isLinked()) + { + bb = inBB.get(); + if (!bb || !bb._max || !bb._min) return; + } + else + { + bb = new CABLES.CG.BoundingBox(); + + bb.applyPos(inWidth.get() / 2, inHeight.get() / 2, inLength.get() / 2); + bb.applyPos(-inWidth.get() / 2, -inHeight.get() / 2, -inLength.get() / 2); + } + + geom = new CGL.Geometry(op.name); + geom.vertices = + [ + bb._max[0], bb._max[1], bb._max[2], + bb._min[0], bb._max[1], bb._max[2], + + bb._min[0], bb._max[1], bb._max[2], + bb._min[0], bb._min[1], bb._max[2], + + bb._min[0], bb._min[1], bb._max[2], + bb._max[0], bb._min[1], bb._max[2], + + bb._max[0], bb._min[1], bb._max[2], + bb._max[0], bb._max[1], bb._max[2], + + // + + bb._max[0], bb._max[1], bb._min[2], + bb._min[0], bb._max[1], bb._min[2], + + bb._min[0], bb._max[1], bb._min[2], + bb._min[0], bb._min[1], bb._min[2], + + bb._min[0], bb._min[1], bb._min[2], + bb._max[0], bb._min[1], bb._min[2], + + bb._max[0], bb._min[1], bb._min[2], + bb._max[0], bb._max[1], bb._min[2], + + // + + bb._max[0], bb._max[1], bb._min[2], + bb._max[0], bb._max[1], bb._max[2], + + bb._min[0], bb._max[1], bb._min[2], + bb._min[0], bb._max[1], bb._max[2], + + bb._max[0], bb._min[1], bb._min[2], + bb._max[0], bb._min[1], bb._max[2], + + bb._min[0], bb._min[1], bb._min[2], + bb._min[0], bb._min[1], bb._max[2], + + // + + bb._min[0], bb._min[1], bb._min[2], + bb._max[0], bb._max[1], bb._max[2], + + bb._max[0], bb._min[1], bb._min[2], + bb._min[0], bb._max[1], bb._max[2], + + ]; + + mesh = new CGL.Mesh(cgl, geom, cgl.gl.LINES); +} + +// LINE/LINE +function lineToScreen(posA, posB) +{ + vec3.transformMat4(pos, posA, m); + vec3.transformMat4(trans, pos, cgl.pMatrix); + + if (pos[2] > 0.0) return false; + + const x1 = (trans[0] * vp[2] / 2) + vp[2] / 2; + const y1 = (trans[1] * vp[3] / 2) + vp[3] / 2; + + vec3.transformMat4(pos, posB, m); + vec3.transformMat4(trans, pos, cgl.pMatrix); + + if (pos[2] > 0.0) return false; + + const x2 = (trans[0] * vp[2] / 2) + vp[2] / 2; + const y2 = (trans[1] * vp[3] / 2) + vp[3] / 2; + + const w = vp[2]; + const h = vp[3]; + + return lineline(x1, y1, x2, y2, 0, 0, w, h) || + + lineline(x1, y1, x2, y2, 0, 0, w, 0) || + lineline(x1, y1, x2, y2, 0, h, w, h) || + + lineline(x1, y1, x2, y2, 0, 0, 0, h) || + lineline(x1, y1, x2, y2, w, 0, w, h); +} + +function lineline(x1, y1, x2, y2, x3, y3, x4, y4) +{ + // calculate the direction of the lines + const uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); + const uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); + + // if uA and uB are between 0-1, lines are colliding + if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) + { + // const intersectionX =d x1 + (uA * (x2 - x1)); + // const intersectionY = y1 + (uA * (y2 - y1)); + + return true; + } + return false; +} + +exec.onTriggered = () => +{ + if (!inActive.get()) return; + + if (inBB.isLinked()) + { + bb = inBB.get(); + } + + if (inBB.isLinked() && (!bb || !bb._center || !bb._max)) + { + result.set(false); + return; + } + + if (inDraw.get()) + { + if (!geom || !mesh) buildMesh(); + if (mesh)mesh.render(cgl.getShader()); + } + + vp = cgl.getViewPort(); + + mat4.multiply(m, cgl.vMatrix, cgl.mMatrix); + + if (bb) + { + const isVis = + + isVisible(bb._center) || + + isVisible(bb._max) || + isVisible(bb._min) || + + lineToScreen(bb._max, bb._min) || + + lineToScreen([bb._max[0], bb._max[1], bb._max[2]], [bb._min[0], bb._max[1], bb._max[2]]) || + lineToScreen([bb._min[0], bb._max[1], bb._max[2]], [bb._min[0], bb._min[1], bb._max[2]]) || + lineToScreen([bb._min[0], bb._min[1], bb._max[2]], [bb._max[0], bb._min[1], bb._max[2]]) || + lineToScreen([bb._max[0], bb._min[1], bb._max[2]], [bb._max[0], bb._max[1], bb._max[2]]) || + + // + + lineToScreen([bb._max[0], bb._max[1], bb._min[2]], [bb._min[0], bb._max[1], bb._min[2]]) || + lineToScreen([bb._min[0], bb._max[1], bb._min[2]], [bb._min[0], bb._min[1], bb._min[2]]) || + lineToScreen([bb._min[0], bb._min[1], bb._min[2]], [bb._max[0], bb._min[1], bb._min[2]]) || + lineToScreen([bb._max[0], bb._min[1], bb._min[2]], [bb._max[0], bb._max[1], bb._min[2]]) || + + // + + lineToScreen([bb._max[0], bb._max[1], bb._min[2]], [bb._max[0], bb._max[1], bb._max[2]]) || + lineToScreen([bb._min[0], bb._max[1], bb._min[2]], [bb._min[0], bb._max[1], bb._max[2]]) || + lineToScreen([bb._max[0], bb._min[1], bb._min[2]], [bb._max[0], bb._min[1], bb._max[2]]) || + lineToScreen([bb._min[0], bb._min[1], bb._min[2]], [bb._min[0], bb._min[1], bb._max[2]]); + + result.set(isVis); + } + + next.trigger(); +}; + + +}; + +Ops.Gl.Geometry.BoundingBoxVisible.prototype = new CABLES.Op(); +CABLES.OPS["a4a8d0ca-296e-4bf5-a7be-4585f875bc06"]={f:Ops.Gl.Geometry.BoundingBoxVisible,objName:"Ops.Gl.Geometry.BoundingBoxVisible"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.DivideGeometry +// +// ************************************************************** + +Ops.Gl.Geometry.DivideGeometry = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +let geometry = op.inObject("Geometry"); +let outGeom = op.outObject("Result"); + +geometry.onChange = update; + + +function update() +{ + outGeom.set(null); + if (geometry.get()) + { + let geom = geometry.get(); + let newGeom = new CGL.Geometry(op.name); + + let newVerts = []; + let newFaces = []; + let newNormals = []; + let newTexCoords = []; + + for (let i = 0; i < geom.verticesIndices.length; i += 3) + { + newFaces.push(newVerts.length / 3); + newVerts.push(geom.vertices[geom.verticesIndices[i + 0] * 3 + 0]); + newVerts.push(geom.vertices[geom.verticesIndices[i + 0] * 3 + 1]); + newVerts.push(geom.vertices[geom.verticesIndices[i + 0] * 3 + 2]); + newNormals.push(geom.vertexNormals[geom.verticesIndices[i + 0] * 3 + 0]); + newNormals.push(geom.vertexNormals[geom.verticesIndices[i + 0] * 3 + 1]); + newNormals.push(geom.vertexNormals[geom.verticesIndices[i + 0] * 3 + 2]); + newTexCoords.push(geom.texCoords[geom.verticesIndices[i + 0] * 2 + 0]); + newTexCoords.push(geom.texCoords[geom.verticesIndices[i + 0] * 2 + 1]); + + newFaces.push(newVerts.length / 3); + newVerts.push(geom.vertices[geom.verticesIndices[i + 1] * 3 + 0]); + newVerts.push(geom.vertices[geom.verticesIndices[i + 1] * 3 + 1]); + newVerts.push(geom.vertices[geom.verticesIndices[i + 1] * 3 + 2]); + newNormals.push(geom.vertexNormals[geom.verticesIndices[i + 1] * 3 + 0]); + newNormals.push(geom.vertexNormals[geom.verticesIndices[i + 1] * 3 + 1]); + newNormals.push(geom.vertexNormals[geom.verticesIndices[i + 1] * 3 + 2]); + newTexCoords.push(geom.texCoords[geom.verticesIndices[i + 1] * 2 + 0]); + newTexCoords.push(geom.texCoords[geom.verticesIndices[i + 1] * 2 + 1]); + + newFaces.push(newVerts.length / 3); + newVerts.push(geom.vertices[geom.verticesIndices[i + 2] * 3 + 0]); + newVerts.push(geom.vertices[geom.verticesIndices[i + 2] * 3 + 1]); + newVerts.push(geom.vertices[geom.verticesIndices[i + 2] * 3 + 2]); + newNormals.push(geom.vertexNormals[geom.verticesIndices[i + 2] * 3 + 0]); + newNormals.push(geom.vertexNormals[geom.verticesIndices[i + 2] * 3 + 1]); + newNormals.push(geom.vertexNormals[geom.verticesIndices[i + 2] * 3 + 2]); + newTexCoords.push(geom.texCoords[geom.verticesIndices[i + 2] * 2 + 0]); + newTexCoords.push(geom.texCoords[geom.verticesIndices[i + 2] * 2 + 1]); + } + + newGeom.vertices = newVerts; + newGeom.vertexNormals = newNormals; + newGeom.verticesIndices = newFaces; + newGeom.setTexCoords(newTexCoords); + + outGeom.set(newGeom); + } +} + + +}; + +Ops.Gl.Geometry.DivideGeometry.prototype = new CABLES.Op(); +CABLES.OPS["ab0c768e-e684-47ba-b11f-f95d86532df2"]={f:Ops.Gl.Geometry.DivideGeometry,objName:"Ops.Gl.Geometry.DivideGeometry"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.FlipNormals +// +// ************************************************************** + +Ops.Gl.Geometry.FlipNormals = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + geometry = op.inObject("Geometry"), + outGeom = op.outObject("Result"), + doFlip = op.inValueBool("Flip", true), + doNormalize = op.inValueBool("Normalize", true); + +doFlip.onChange = + doNormalize.onChange = + geometry.onChange = flip; + +function flip() +{ + let oldGeom = geometry.get(); + + if (!oldGeom) + { + outGeom.set(null); + return; + } + + let geom = oldGeom.copy(); + + if (doFlip.get()) + { + for (let i = 0; i < geom.vertexNormals.length; i++) + geom.vertexNormals[i] *= -1; + + if (doNormalize.get()) + { + let vec = vec3.create(); + + for (let i = 0; i < geom.vertexNormals.length; i += 3) + { + vec3.set(vec, + geom.vertexNormals[i + 0], + geom.vertexNormals[i + 1], + geom.vertexNormals[i + 2]); + vec3.normalize(vec, vec); + + geom.vertexNormals[i + 0] = vec[0]; + geom.vertexNormals[i + 1] = vec[1]; + geom.vertexNormals[i + 2] = vec[2]; + } + } + } + + outGeom.set(geom); +} + + +}; + +Ops.Gl.Geometry.FlipNormals.prototype = new CABLES.Op(); +CABLES.OPS["0055f588-dde6-4232-958b-4c19cdc67abd"]={f:Ops.Gl.Geometry.FlipNormals,objName:"Ops.Gl.Geometry.FlipNormals"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.FreezeMeshes +// +// ************************************************************** + +Ops.Gl.Geometry.FreezeMeshes = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inCapture = op.inTriggerButton("Capture"), + outGeom = op.outObject("Geometry", null, "geometry"), + next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; +let shouldCapture = false; +let geom = null; + +inCapture.onTriggered = () => +{ + shouldCapture = true; + if (shouldCapture) + { + geom = new CGL.Geometry(); + + const old = CGL.Mesh.prototype.render; + CGL.Mesh.prototype.render = meshCapture; + + next.trigger(); + + CGL.Mesh.prototype.render = old; + shouldCapture = false; + + // geom.unIndex(false, true); + + outGeom.set(null); + outGeom.set(geom); + } +}; + +function meshCapture() +{ + if (!this._geom || !this._geom.copy) + { + return; + } + + const g = this._geom.copy(); + const normalMat = mat4.create(); + mat4.invert(normalMat, cgl.mMatrix); + mat4.transpose(normalMat, normalMat); + + for (let i = 0; i < g.vertices.length; i += 3) + { + const v = [g.vertices[i + 0], g.vertices[i + 1], g.vertices[i + 2]]; + + vec3.transformMat4(v, v, cgl.mMatrix); + + g.vertices[i + 0] = v[0]; + g.vertices[i + 1] = v[1]; + g.vertices[i + 2] = v[2]; + + // ---------- + + const vn = [g.vertexNormals[i + 0], g.vertexNormals[i + 1], g.vertexNormals[i + 2], 1]; + vec4.transformMat4(vn, vn, normalMat); + + g.vertexNormals[i + 0] = vn[0]; + g.vertexNormals[i + 1] = vn[1]; + g.vertexNormals[i + 2] = vn[2]; + } + + // g.unIndex(); + geom.merge(g); +} + + +}; + +Ops.Gl.Geometry.FreezeMeshes.prototype = new CABLES.Op(); +CABLES.OPS["6bcd033d-3a20-477f-93f1-33d0c701528a"]={f:Ops.Gl.Geometry.FreezeMeshes,objName:"Ops.Gl.Geometry.FreezeMeshes"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.GeometryBoundingBox +// +// ************************************************************** + +Ops.Gl.Geometry.GeometryBoundingBox = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inGeom = op.inObject("Geometry"), + outBB = op.outObject("Boundings"), + outMinX = op.outNumber("Min X"), + outMinY = op.outNumber("Min Y"), + outMinZ = op.outNumber("Min Z"), + outMaxX = op.outNumber("Max X"), + outMaxY = op.outNumber("Max Y"), + outMaxZ = op.outNumber("Max Z"), + outPoints = op.outArray("MaxMin Points"); + +const points = []; + +inGeom.onChange = () => +{ + const bb = new CGL.BoundingBox(inGeom.get()); + outBB.set(bb); + + outMinX.set(bb._min[0]); + outMinY.set(bb._min[1]); + outMinZ.set(bb._min[2]); + + outMaxX.set(bb._max[0]); + outMaxY.set(bb._max[1]); + outMaxZ.set(bb._max[2]); + + + points.length = 0; + points.push( + // bb._max[0],bb._max[1],bb._max[2], + // bb._min[0],bb._min[1],bb._min[2], + + bb._max[0], bb._max[1], bb._max[2], + bb._max[0], bb._min[1], bb._max[2], + bb._min[0], bb._min[1], bb._max[2], + bb._min[0], bb._max[1], bb._max[2], + + bb._max[0], bb._max[1], bb._min[2], + bb._max[0], bb._min[1], bb._min[2], + bb._min[0], bb._min[1], bb._min[2], + bb._min[0], bb._max[1], bb._min[2], + + bb._center[0], bb._center[1], bb._center[2] + ); + outPoints.set(null); + outPoints.set(points); +}; + + +}; + +Ops.Gl.Geometry.GeometryBoundingBox.prototype = new CABLES.Op(); +CABLES.OPS["7c13487b-87b2-40c9-b363-c5f699968579"]={f:Ops.Gl.Geometry.GeometryBoundingBox,objName:"Ops.Gl.Geometry.GeometryBoundingBox"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.GeometryExtrude +// +// ************************************************************** + +Ops.Gl.Geometry.GeometryExtrude = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inGeom = op.inObject("Geometry", null, "geometry"), + inHeight = op.inFloat("Height", 0.5), + inSmooth = op.inBool("Smooth", true), + inExtrudeWalls = op.inBool("Walls", true), + inCapTop = op.inBool("Top", true), + inCapBottom = op.inBool("Bottom", true), + outGeom = op.outObject("Result Geometry", null, "geometry"); + +function isClockwise(verts) +{ + let sum = 0.0; + for (let i = 0; i < verts.length - 3; i += 3) + { + // Vector v1 = verts[i]; + // Vector v2 = verts[(i + 1) % verts.length]; + sum += (verts[i + 3] - verts[i]) * (verts[i + 3 + 1] + verts[i]); + } + return sum > 0.0; +} + +inSmooth.onChange = +inExtrudeWalls.onChange = +inCapTop.onChange = +inCapBottom.onChange = +inHeight.onChange = +inGeom.onChange = () => +{ + const geom = inGeom.get(); + + if (!geom) + { + outGeom.set(null); + return; + } + + function edgesUsedMulti(idx1, idx2) + { + let count = 0; + for (let i = 0; i < geom.verticesIndices.length; i += 3) + { + if ( + ( + geom.verticesIndices[i] == idx1 || + geom.verticesIndices[i + 1] == idx1 || + geom.verticesIndices[i + 2] == idx1 + ) && + ( + geom.verticesIndices[i] == idx2 || + geom.verticesIndices[i + 1] == idx2 || + geom.verticesIndices[i + 2] == idx2 + )) + { + count++; + if (count == 2) return true; + } + } + + return false; + } + + let verts = []; + const indices = []; + const h = inHeight.get(); + + if (inExtrudeWalls.get()) + for (let i = 0; i < geom.verticesIndices.length; i += 3) + { + const vert1 = geom.verticesIndices[i]; + const vert2 = geom.verticesIndices[i + 1]; + const vert3 = geom.verticesIndices[i + 2]; + + // 1 + if (!edgesUsedMulti(vert1, vert2)) + { + const a = []; + a.push([geom.vertices[vert1 * 3 + 0], geom.vertices[vert1 * 3 + 1], geom.vertices[vert1 * 3 + 2]]); + a.push([geom.vertices[vert1 * 3 + 0], geom.vertices[vert1 * 3 + 1], geom.vertices[vert1 * 3 + 2] + h]); + a.push([geom.vertices[vert2 * 3 + 0], geom.vertices[vert2 * 3 + 1], geom.vertices[vert2 * 3 + 2]]); + + if (!isClockwise(a)) verts = verts.concat(a); + else verts = verts.concat(a.reverse()); + + a.length = 0; + a.push([geom.vertices[vert2 * 3 + 0], geom.vertices[vert2 * 3 + 1], geom.vertices[vert2 * 3 + 2] + h]); + a.push([geom.vertices[vert2 * 3 + 0], geom.vertices[vert2 * 3 + 1], geom.vertices[vert2 * 3 + 2]]); + a.push([geom.vertices[vert1 * 3 + 0], geom.vertices[vert1 * 3 + 1], geom.vertices[vert1 * 3 + 2] + h]); + + if (!isClockwise(a)) verts = verts.concat(a); + else verts = verts.concat(a.reverse()); + } + + // 2 + if (!edgesUsedMulti(vert3, vert2)) + { + const a = []; + a.push([geom.vertices[vert3 * 3 + 0], geom.vertices[vert3 * 3 + 1], geom.vertices[vert3 * 3 + 2]]); + a.push([geom.vertices[vert3 * 3 + 0], geom.vertices[vert3 * 3 + 1], geom.vertices[vert3 * 3 + 2] + h]); + a.push([geom.vertices[vert2 * 3 + 0], geom.vertices[vert2 * 3 + 1], geom.vertices[vert2 * 3 + 2]]); + + if (isClockwise(a)) verts = verts.concat(a); + else verts = verts.concat(a.reverse()); + + a.length = 0; + + a.push([geom.vertices[vert2 * 3 + 0], geom.vertices[vert2 * 3 + 1], geom.vertices[vert2 * 3 + 2] + h]); + a.push([geom.vertices[vert2 * 3 + 0], geom.vertices[vert2 * 3 + 1], geom.vertices[vert2 * 3 + 2]]); + a.push([geom.vertices[vert3 * 3 + 0], geom.vertices[vert3 * 3 + 1], geom.vertices[vert3 * 3 + 2] + h]); + + if (isClockwise(a)) verts = verts.concat(a); + else verts = verts.concat(a.reverse()); + } + // 3 + + if (!edgesUsedMulti(vert3, vert1)) + { + const a = []; + a.push([geom.vertices[vert3 * 3 + 0], geom.vertices[vert3 * 3 + 1], geom.vertices[vert3 * 3 + 2]]); + a.push([geom.vertices[vert3 * 3 + 0], geom.vertices[vert3 * 3 + 1], geom.vertices[vert3 * 3 + 2] + h]); + a.push([geom.vertices[vert1 * 3 + 0], geom.vertices[vert1 * 3 + 1], geom.vertices[vert1 * 3 + 2]]); + + if (!isClockwise(a)) verts = verts.concat(a); + else verts = verts.concat(a.reverse()); + + a.length = 0; + + a.push([geom.vertices[vert1 * 3 + 0], geom.vertices[vert1 * 3 + 1], geom.vertices[vert1 * 3 + 2] + h]); + a.push([geom.vertices[vert1 * 3 + 0], geom.vertices[vert1 * 3 + 1], geom.vertices[vert1 * 3 + 2]]); + a.push([geom.vertices[vert3 * 3 + 0], geom.vertices[vert3 * 3 + 1], geom.vertices[vert3 * 3 + 2] + h]); + + if (!isClockwise(a)) verts = verts.concat(a); + else verts = verts.concat(a.reverse()); + } + } + + const newGeom = CGL.Geometry.buildFromFaces(verts, "extrude", true); + + newGeom.calculateNormals(); + newGeom.calcTangentsBitangents(); + + if (inCapBottom.get()) + { + newGeom.merge(geom); + } + + if (inCapTop.get()) + { + const flippedgeo = geom.copy(); + + for (let i = 0; i < flippedgeo.vertices.length; i += 3) + flippedgeo.vertices[i + 2] += h; + + flippedgeo.flipVertDir(); + flippedgeo.flipNormals(); + newGeom.merge(flippedgeo); + } + + newGeom.flipVertDir(); + + if (!inSmooth.get()) + { + newGeom.unIndex(); + newGeom.calculateNormals({ "forceZUp": true }); + newGeom.flipNormals(); + } + + outGeom.set(null); + outGeom.set(newGeom); +}; + + +}; + +Ops.Gl.Geometry.GeometryExtrude.prototype = new CABLES.Op(); +CABLES.OPS["64a34a29-000d-4350-875f-5b72b97a314f"]={f:Ops.Gl.Geometry.GeometryExtrude,objName:"Ops.Gl.Geometry.GeometryExtrude"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.GeometryInfo +// +// ************************************************************** + +Ops.Gl.Geometry.GeometryInfo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + geometry = op.inObject("Geometry", null, "geometry"), + outIndexed = op.outBoolNum("Indexed", false), + outFaces = op.outNumber("Faces"), + outIndices = op.outNumber("Indices"), + outVertices = op.outNumber("Vertices"), + outNormals = op.outNumber("Normals"), + outTexCoords = op.outNumber("TexCoords"), + outTangents = op.outNumber("Tangents"), + outBiTangents = op.outNumber("BiTangents"), + outVertexColors = op.outNumber("VertexColors"), + outAttribs = op.outNumber("Other Attributes"); + +geometry.onLinkChanged = () => +{ +}; + +geometry.onChange = function () +{ + let geom = geometry.get(); + if (geom) + { + const info = geom.getInfo(); + outFaces.set(info.numFaces); + outIndices.set(info.indices || info.indices); + outVertices.set(info.numVerts); + outNormals.set(info.numNormals); + outTexCoords.set(info.numTexCoords); + outTangents.set(info.numTangents); + outBiTangents.set(info.numBiTangents); + outVertexColors.set(info.numVertexColors); + outAttribs.set(info.numAttribs); + outIndexed.set(info.isIndexed); + } + else + { + outIndexed.set(null); + outFaces.set(null); + outVertices.set(null); + outNormals.set(null); + outTexCoords.set(null); + outTangents.set(null); + outBiTangents.set(null); + outVertexColors.set(null); + outAttribs.set(null); + } +}; + + +}; + +Ops.Gl.Geometry.GeometryInfo.prototype = new CABLES.Op(); +CABLES.OPS["a9208e84-e957-43ac-9a79-9c719eff95eb"]={f:Ops.Gl.Geometry.GeometryInfo,objName:"Ops.Gl.Geometry.GeometryInfo"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.GeometryMerge +// +// ************************************************************** + +Ops.Gl.Geometry.GeometryMerge = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inGeom = op.inObject("Geometry"), + inGeom2 = op.inObject("Geometry 2"), + inMerge = op.inTriggerButton("Merge"), + inReset = op.inTriggerButton("Reset"), + outGeom = op.outObject("Geometry Result"); + +const geom = new CGL.Geometry(op.name); + +outGeom.set(geom); + +inReset.onTriggered = function () +{ + geom.clear(); + outGeom.set(null); + outGeom.set(geom); +}; + +inMerge.onTriggered = function () +{ + if (inGeom.get() || inGeom2.get()) + { + if (inGeom.get())geom.merge(inGeom.get()); + if (inGeom2.get())geom.merge(inGeom2.get()); + outGeom.set(null); + outGeom.set(geom); + } +}; + + +}; + +Ops.Gl.Geometry.GeometryMerge.prototype = new CABLES.Op(); +CABLES.OPS["f915eaab-92df-495e-84ed-500ec301b6f7"]={f:Ops.Gl.Geometry.GeometryMerge,objName:"Ops.Gl.Geometry.GeometryMerge"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.GeometryMergeSimple +// +// ************************************************************** + +Ops.Gl.Geometry.GeometryMergeSimple = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inGeom = op.inObject("Geometry"), + inGeom2 = op.inObject("Geometry 2"), + outGeom = op.outObject("Geometry Result"); + +let geom = new CGL.Geometry(op.name); +outGeom.set(geom); + +inGeom.onChange = +inGeom2.onChange = +function () +{ + if (inGeom.get() || inGeom2.get()) + { + geom = new CGL.Geometry(op.name); + if (inGeom.get()) + { + geom = inGeom.get().copy(); + } + if (inGeom2.get())geom.merge(inGeom2.get()); + outGeom.set(null); + outGeom.set(geom); + } +}; + + +}; + +Ops.Gl.Geometry.GeometryMergeSimple.prototype = new CABLES.Op(); +CABLES.OPS["e9a6c398-b6f2-4c53-a7ea-47aa835c6938"]={f:Ops.Gl.Geometry.GeometryMergeSimple,objName:"Ops.Gl.Geometry.GeometryMergeSimple"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.GeometryPoints +// +// ************************************************************** + +Ops.Gl.Geometry.GeometryPoints = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + geometry = op.inObject("Geometry"), + outFaces = op.outArray("Faces", 3), + outVertices = op.outArray("Vertices", 3), + outNormals = op.outArray("Normals", 3), + outTextcoords = op.outArray("TexCoords", 2), + outVertexColors = op.outArray("Vertex Colors", 4), + outTangents = op.outArray("Tangents", 3), + outBiTangents = op.outArray("BiTangents", 3); + +geometry.onChange = function () +{ + let geom = geometry.get(); + if (!geom) return; + + // convert float32array to array + let verts = Array.prototype.slice.call(geom.vertices); + + outVertices.set(verts); + outFaces.set(geom.verticesIndices); + outTextcoords.set(geom.texCoords); + outNormals.set(geom.vertexNormals); + outTangents.set(geom.tangents); + outBiTangents.set(geom.biTangents); + outVertexColors.set(geom.vertexColors); +}; + + +}; + +Ops.Gl.Geometry.GeometryPoints.prototype = new CABLES.Op(); +CABLES.OPS["b215118b-de1f-4be9-8890-d07a2ecff010"]={f:Ops.Gl.Geometry.GeometryPoints,objName:"Ops.Gl.Geometry.GeometryPoints"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.GeometryToObj +// +// ************************************************************** + +Ops.Gl.Geometry.GeometryToObj = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inGeo = op.inObject("Geometry", null, "geometry"), + outStr = op.outString("Obj"); + +function decimal(n) +{ + return Math.round(n * 10000000) / 10000000; +} + +inGeo.onChange = () => +{ + const geom = inGeo.get(); + + if (!geom) + { + outStr.set(null); + return; + } + + let str = "# made with cables.gl".endl().endl(); + + str += "# " + JSON.stringify(geom.getInfo()); + str += "".endl(); + str += "".endl(); + + // vertices + for (let i = 0; i < geom.vertices.length; i += 3) + { + str += "v " + decimal(geom.vertices[i + 0]) + " " + decimal(geom.vertices[i + 1]) + " " + decimal(geom.vertices[i + 2]) + "".endl(); + } + + str += "".endl(); + + // normals + for (let i = 0; i < geom.vertexNormals.length; i += 3) + { + str += "vn " + decimal(geom.vertexNormals[i + 0]) + " " + decimal(geom.vertexNormals[i + 1]) + " " + decimal(geom.vertexNormals[i + 2]) + "".endl(); + } + + str += "".endl(); + + // texcoords + for (let i = 0; i < geom.texCoords.length; i += 2) + { + str += "vt " + decimal(geom.texCoords[i + 0]) + " " + decimal(geom.texCoords[i + 1]) + " 0".endl(); + } + + str += "".endl(); + + // faces + if (geom.verticesIndices.length) + { + for (let i = 0; i < geom.verticesIndices.length; i += 3) + { + str += "f "; + for (let j = 0; j < 3; j++) + { + str += (geom.verticesIndices[i + j] + 1) + "/" + (geom.verticesIndices[i + j] + 1) + "/" + (geom.verticesIndices[i + j] + 1) + " "; + } + str += "".endl(); + } + } + else + { + for (let i = 0; i < geom.vertices.length / 3 - 1; i += 3) + { + str += "f " + i + " " + (i + 1) + " " + (i + 2) + " ".endl(); + } + } + + // str += "".endl(); + + outStr.set(str); +}; + + +}; + +Ops.Gl.Geometry.GeometryToObj.prototype = new CABLES.Op(); +CABLES.OPS["3890b676-9ba6-42c1-9a57-659b74958e20"]={f:Ops.Gl.Geometry.GeometryToObj,objName:"Ops.Gl.Geometry.GeometryToObj"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.GeometryUnIndex +// +// ************************************************************** + +Ops.Gl.Geometry.GeometryUnIndex = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + geometry = op.inObject("Geometry"), + result = op.outObject("Result"); + +geometry.onChange = function () +{ + let geom = geometry.get(); + + if (geom) + { + if (!geom.isIndexed()) + { + result.set(geom); + return; + } + + const newGeom = geom.copy(); + newGeom.unIndex(false, true); + result.set(newGeom); + } + else result.set(null); +}; + + +}; + +Ops.Gl.Geometry.GeometryUnIndex.prototype = new CABLES.Op(); +CABLES.OPS["b0b2430c-5206-4d1f-a0a6-bc5d79a29027"]={f:Ops.Gl.Geometry.GeometryUnIndex,objName:"Ops.Gl.Geometry.GeometryUnIndex"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.ReverseVertices +// +// ************************************************************** + +Ops.Gl.Geometry.ReverseVertices = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + geometry = op.inObject("Geometry"), + outGeom = op.outObject("Result"), + doFlip = op.inValueBool("Flip", true); + +doFlip.onChange = + geometry.onChange = flip; + +function flip() +{ + let oldGeom = geometry.get(); + + if (!oldGeom) + { + outGeom.set(null); + return; + } + + let geom = oldGeom.copy(); + + if(doFlip.get())geom.flipVertDir(); + + outGeom.set(null); + outGeom.set(geom); +} + + +}; + +Ops.Gl.Geometry.ReverseVertices.prototype = new CABLES.Op(); +CABLES.OPS["548079e7-616d-4f7a-98a0-509c9c7bc36d"]={f:Ops.Gl.Geometry.ReverseVertices,objName:"Ops.Gl.Geometry.ReverseVertices"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.ScaleGeometry +// +// ************************************************************** + +Ops.Gl.Geometry.ScaleGeometry = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let geometry = op.inObject("Geometry"); +let scale = op.inValue("Scale", 1); +let outGeom = op.outObject("Result"); + +scale.onChange = geometry.onChange = update; + +function update() +{ + let oldGeom = geometry.get(); + + if (oldGeom) + { + let geom = oldGeom.copy(); + let rotVec = vec3.create(); + let emptyVec = vec3.create(); + let transVec = vec3.create(); + let centerVec = vec3.create(); + let s = scale.get(); + + for (let i = 0; i < geom.vertices.length; i += 3) + { + geom.vertices[i + 0] *= s; + geom.vertices[i + 1] *= s; + geom.vertices[i + 2] *= s; + } + + outGeom.set(geom); + } + else outGeom.set(null); +} + + +}; + +Ops.Gl.Geometry.ScaleGeometry.prototype = new CABLES.Op(); +CABLES.OPS["7730e67b-5916-4481-bcd1-54fa6e3a1d02"]={f:Ops.Gl.Geometry.ScaleGeometry,objName:"Ops.Gl.Geometry.ScaleGeometry"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.SortGeometryAxis +// +// ************************************************************** + +Ops.Gl.Geometry.SortGeometryAxis = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + SORT_RANDOM = "Random", + SORT_X = "X Axis", + SORT_Y = "Y Axis", + SORT_Z = "Z Axis", + SORT_NONE = "None", + + geometry = op.inObject("Geometry"), + sorting = op.inValueSelect("Sort", [SORT_RANDOM, SORT_X, SORT_Y, SORT_Z, SORT_NONE], SORT_X), + reverse = op.inValueBool("Reverse", false), + outGeom = op.outObject("Result"); + +reverse.onChange = + geometry.onChange = + sorting.onChange = update; + +function update() +{ + if (geometry.get()) + { + const geom = geometry.get(); + let faces = []; + faces.length = geom.verticesIndices.length / 3; + + for (let i = 0; i < geom.verticesIndices.length; i += 3) + { + const face = [0, 0, 0]; + face[0] = geom.verticesIndices[i + 0]; + face[1] = geom.verticesIndices[i + 1]; + face[2] = geom.verticesIndices[i + 2]; + faces[i / 3] = face; + } + + if (sorting.get() == SORT_RANDOM) + { + faces = CABLES.shuffleArray(faces); + } + else + if (sorting.get() == SORT_Y) + { + faces.sort(function (a, b) + { + let avgA = 0; + avgA += geom.vertices[a[0] * 3 + 1]; + avgA += geom.vertices[a[1] * 3 + 1]; + avgA += geom.vertices[a[2] * 3 + 1]; + avgA /= 3; + + let avgB = 0; + avgB += geom.vertices[b[0] * 3 + 1]; + avgB += geom.vertices[b[1] * 3 + 1]; + avgB += geom.vertices[b[2] * 3 + 1]; + avgB /= 3; + + return avgA - avgB; + }); + } + else + if (sorting.get() == SORT_X) + { + faces.sort(function (a, b) + { + let avgA = 0; + avgA += geom.vertices[a[0] * 3 + 0]; + avgA += geom.vertices[a[1] * 3 + 0]; + avgA += geom.vertices[a[2] * 3 + 0]; + avgA /= 3; + + let avgB = 0; + avgB += geom.vertices[b[0] * 3 + 0]; + avgB += geom.vertices[b[1] * 3 + 0]; + avgB += geom.vertices[b[2] * 3 + 0]; + avgB /= 3; + + return avgA - avgB; + }); + } + else + if (sorting.get() == SORT_Z) + { + faces.sort(function (a, b) + { + let avgA = 0; + avgA += geom.vertices[a[0] * 3 + 2]; + avgA += geom.vertices[a[1] * 3 + 2]; + avgA += geom.vertices[a[2] * 3 + 2]; + avgA /= 3; + + let avgB = 0; + avgB += geom.vertices[b[0] * 3 + 2]; + avgB += geom.vertices[b[1] * 3 + 2]; + avgB += geom.vertices[b[2] * 3 + 2]; + avgB /= 3; + + return avgA - avgB; + }); + } + else + { + if (sorting.get() != SORT_NONE) op.error("No sorting found", sorting.get()); + } + + const newGeom = new CGL.Geometry(op.name); + const newVerts = []; + const newFaces = []; + const newNormals = []; + const newTexCoords = []; + const newTangents = []; + const newBiTangents = []; + + if (reverse.get()) + { + faces = faces.reverse(); + } + + faces = [].concat.apply([], faces); + + for (let i = 0; i < faces.length; i += 3) + { + newFaces.push( + newVerts.length / 3); + newVerts.push( + geom.vertices[faces[i + 0] * 3 + 0], + geom.vertices[faces[i + 0] * 3 + 1], + geom.vertices[faces[i + 0] * 3 + 2]); + newNormals.push( + geom.vertexNormals[faces[i + 0] * 3 + 0], + geom.vertexNormals[faces[i + 0] * 3 + 1], + geom.vertexNormals[faces[i + 0] * 3 + 2]); + newTexCoords.push( + geom.texCoords[faces[i + 0] * 2 + 0], + geom.texCoords[faces[i + 0] * 2 + 1]); + + if (geom.tangents) + newTangents.push( + geom.tangents[faces[i + 0] * 3 + 0], + geom.tangents[faces[i + 0] * 3 + 1], + geom.tangents[faces[i + 0] * 3 + 2]); + if (geom.biTangents) + newBiTangents.push( + geom.biTangents[faces[i + 0] * 3 + 0], + geom.biTangents[faces[i + 0] * 3 + 1], + geom.biTangents[faces[i + 0] * 3 + 2]); + + newFaces.push( + newVerts.length / 3); + newVerts.push( + geom.vertices[faces[i + 1] * 3 + 0], + geom.vertices[faces[i + 1] * 3 + 1], + geom.vertices[faces[i + 1] * 3 + 2]); + newNormals.push( + geom.vertexNormals[faces[i + 1] * 3 + 0], + geom.vertexNormals[faces[i + 1] * 3 + 1], + geom.vertexNormals[faces[i + 1] * 3 + 2]); + newTexCoords.push( + geom.texCoords[faces[i + 1] * 2 + 0], + geom.texCoords[faces[i + 1] * 2 + 1]); + if (geom.tangents) + newTangents.push( + geom.tangents[faces[i + 1] * 3 + 0], + geom.tangents[faces[i + 1] * 3 + 1], + geom.tangents[faces[i + 1] * 3 + 2]); + if (geom.biTangents) + newBiTangents.push( + geom.biTangents[faces[i + 1] * 3 + 0], + geom.biTangents[faces[i + 1] * 3 + 1], + geom.biTangents[faces[i + 1] * 3 + 2]); + + newFaces.push( + newVerts.length / 3); + newVerts.push( + geom.vertices[faces[i + 2] * 3 + 0], + geom.vertices[faces[i + 2] * 3 + 1], + geom.vertices[faces[i + 2] * 3 + 2]); + newNormals.push( + geom.vertexNormals[faces[i + 2] * 3 + 0], + geom.vertexNormals[faces[i + 2] * 3 + 1], + geom.vertexNormals[faces[i + 2] * 3 + 2]); + newTexCoords.push( + geom.texCoords[faces[i + 2] * 2 + 0], + geom.texCoords[faces[i + 2] * 2 + 1]); + if (geom.tangents) + newTangents.push( + geom.tangents[faces[i + 2] * 3 + 0], + geom.tangents[faces[i + 2] * 3 + 1], + geom.tangents[faces[i + 2] * 3 + 2]); + if (geom.biTangents) + newBiTangents.push( + geom.biTangents[faces[i + 2] * 3 + 0], + geom.biTangents[faces[i + 2] * 3 + 1], + geom.biTangents[faces[i + 2] * 3 + 2]); + } + + newGeom.vertices = newVerts; + newGeom.vertexNormals = newNormals; + newGeom.verticesIndices = newFaces; + newGeom.texCoords = newTexCoords; + newGeom.tangents = newTangents; + newGeom.biTangents = newBiTangents; + + outGeom.set(null); + outGeom.set(newGeom); + } +} + + +}; + +Ops.Gl.Geometry.SortGeometryAxis.prototype = new CABLES.Op(); +CABLES.OPS["8b0a635c-83f0-4d95-9b9c-1b5ca55c6724"]={f:Ops.Gl.Geometry.SortGeometryAxis,objName:"Ops.Gl.Geometry.SortGeometryAxis"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.SvgPathToGeometry_v2 +// +// ************************************************************** + +Ops.Gl.Geometry.SvgPathToGeometry_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("SVG Path"), + inStepSize = op.inFloat("Bezier Stepsize", 3), + inRescale = op.inFloat("Rescale", 1), + outGeom = op.outObject("Geometry", null, "geometry"); + +inStepSize.onChange = +inRescale.onChange = +inStr.onChange = () => +{ + let str = inStr.get(); + + if (!str || str.length < 2) + { + outGeom.set(null); + return; + } + + str = str.replace(/([A-Z,a-z])/g, " $1 "); + + const cmds = fromPathToArray(str); + + // create a list of closed contours + const polys = []; + cmds.forEach(({ type, x, y, x1, y1, x2, y2 }) => + { + switch (type) + { + case "M": + polys.push(new Polygon()); + polys[polys.length - 1].moveTo({ x, y }); + break; + case "L": + polys[polys.length - 1].moveTo({ x, y }); + break; + case "C": + polys[polys.length - 1].cubicTo({ x, y }, { "x": x1, "y": y1 }, { "x": x2, "y": y2 }); + break; + case "Q": + polys[polys.length - 1].conicTo({ x, y }, { "x": x1, "y": y1 }); + break; + case "Z": + polys[polys.length - 1].close(); + break; + } + }); + + // sort contours by descending area + polys.sort((a, b) => { return Math.abs(b.area) - Math.abs(a.area); }); + // classify contours to find holes and their 'parents' + const root = []; + for (let i = 0; i < polys.length; ++i) + { + let parent = null; + for (let j = i - 1; j >= 0; --j) + { + // a contour is a hole if it is inside its parent and has different winding + if (polys[j].inside(polys[i].points[0]) && polys[i].area * polys[j].area < 0) + { + parent = polys[j]; + break; + } + } + if (parent) + { + parent.children.push(polys[i]); + } + else + { + root.push(polys[i]); + } + } + + const totalPoints = polys.reduce((sum, p) => { return sum + p.points.length; }, 0); + const vertexData = new Float32Array(totalPoints * 2); + let vertexCount = 0; + const indices = []; + + function process(poly) + { + // construct input for earcut + const coords = []; + const holes = []; + + poly.points.forEach(({ x, y }) => { return coords.push(x, y); }); + + poly.children.forEach((child) => + { + // children's children are new, separate shapes + child.children.forEach(process); + + holes.push(coords.length / 2); + child.points.forEach(({ x, y }) => { return coords.push(x, y); }); + }); + + // add vertex data + vertexData.set(coords, vertexCount * 2); + // add index data + earcut(coords, holes).forEach((i) => { return indices.push(i + vertexCount); }); + vertexCount += coords.length / 2; + } + + root.forEach(process); + + const finalVertexData = new Float32Array(totalPoints * 3); + + let max = -99999; + + for (let i = 0; i < finalVertexData.length / 3; i++) + { + finalVertexData[i * 3 + 0] = vertexData[i * 2 + 0]; + finalVertexData[i * 3 + 1] = vertexData[i * 2 + 1] * -1; + max = Math.max(finalVertexData[i * 3 + 1], max); + + finalVertexData[i * 3 + 2] = 0; + } + + let resc = inRescale.get(); + + let geom = new CGL.Geometry("circle"); + geom.setVertices(finalVertexData); + geom.verticesIndices = indices; + + if (resc != 0) + { + const bounds = geom.getBounds(); + for (let i = 0; i < finalVertexData.length / 3; i++) + { + finalVertexData[i * 3 + 0] = (finalVertexData[i * 3 + 0] / (bounds.size[0] / 2)) * resc; + finalVertexData[i * 3 + 1] = (finalVertexData[i * 3 + 1] / (bounds.size[0] / 2)) * resc; + } + geom.setVertices(finalVertexData); + } + + geom.mapTexCoords2d(); + geom.flipVertDir(); + geom.calculateNormals(); + geom.calcTangentsBitangents(); + + outGeom.set(geom); +}; + +const PATH_COMMANDS = { + "M": ["x", "y"], + "m": ["dx", "dy"], + "H": ["x"], + "h": ["dx"], + "V": ["y"], + "v": ["dy"], + "L": ["x", "y"], + "l": ["dx", "dy"], + "Z": [], + "C": ["x1", "y1", "x2", "y2", "x", "y"], + "c": ["dx1", "dy1", "dx2", "dy2", "dx", "dy"], + "S": ["x2", "y2", "x", "y"], + "s": ["dx2", "dy2", "dx", "dy"], + "Q": ["x1", "y1", "x", "y"], + "q": ["dx1", "dy1", "dx", "dy"], + "T": ["x", "y"], + "t": ["dx", "dy"], + "A": ["rx", "ry", "rotation", "large-arc", "sweep", "x", "y"], + "a": ["rx", "ry", "rotation", "large-arc", "sweep", "dx", "dy"] +}; + +function fromPathToArray(path) +{ + const items = path.replace(/[\n\r]/g, "") + .replace(/-/g, " -") + .replace(/(\d*\.)(\d+)(?=\.)/g, "$1$2 ") + .trim() + .split(/\s*,|\s+/); + + const segments = []; + let currentCommand = ""; + let currentElement = {}; + while (items.length > 0) + { + let it = items.shift(); + if (PATH_COMMANDS.hasOwnProperty(it)) + { + currentCommand = it; + } + else + { + items.unshift(it); + } + + currentElement = { "type": currentCommand }; + PATH_COMMANDS[currentCommand].forEach((prop) => + { + it = items.shift(); // TODO sanity check + currentElement[prop] = parseFloat(it); + }); + if (currentCommand === "M") + { + currentCommand = "L"; + } + else if (currentCommand === "m") + { + currentCommand = "l"; + } + segments.push(currentElement); + } + return segments; +} + +// https://stackoverflow.com/questions/50554803/triangulate-path-data-from-opentype-js-using-earcut + +const MAX_BEZIER_STEPS = 15; +// this is for inside checks - doesn't have to be particularly +// small because glyphs have finite resolution +const EPSILON = 1e-6; + +class Polygon +{ + constructor() + { + this.points = []; + this.children = []; + this.area = 0.0; + + this.BEZIER_STEP_SIZE = inStepSize.get(); + } + + moveTo(p) + { + this.points.push(p); + } + + lineTo(p) + { + this.points.push(p); + } + + close() + { + let cur = this.points[this.points.length - 1]; + this.points.forEach((next) => + { + this.area += 0.5 * cross(cur, next); + cur = next; + }); + } + + conicTo(p, p1) + { + const p0 = this.points[this.points.length - 1]; + const dist = distance(p0, p1) + distance(p1, p); + const steps = Math.max(2, Math.min(MAX_BEZIER_STEPS, dist / this.BEZIER_STEP_SIZE)); + for (let i = 1; i <= steps; ++i) + { + const t = i / steps; + this.points.push(lerp(lerp(p0, p1, t), lerp(p1, p, t), t)); + } + } + + cubicTo(p, p1, p2) + { + const p0 = this.points[this.points.length - 1]; + const dist = distance(p0, p1) + distance(p1, p2) + distance(p2, p); + const steps = Math.max(2, Math.min(MAX_BEZIER_STEPS, dist / this.BEZIER_STEP_SIZE)); + for (let i = 1; i <= steps; ++i) + { + const t = i / steps; + const a = lerp(lerp(p0, p1, t), lerp(p1, p2, t), t); + const b = lerp(lerp(p1, p2, t), lerp(p2, p, t), t); + this.points.push(lerp(a, b, t)); + } + } + + inside(p) + { + let count = 0, cur = this.points[this.points.length - 1]; + this.points.forEach((next) => + { + const p0 = (cur.y < next.y ? cur : next); + const p1 = (cur.y < next.y ? next : cur); + if (p0.y < p.y + EPSILON && p1.y > p.y + EPSILON) + { + if ((p1.x - p0.x) * (p.y - p0.y) > (p.x - p0.x) * (p1.y - p0.y)) + { + count += 1; + } + } + cur = next; + }); + return (count % 2) !== 0; + } +} + +function distance(p1, p2) +{ + const dx = p1.x - p2.x, dy = p1.y - p2.y; + return Math.sqrt(dx * dx + dy * dy); +} + +function lerp(p1, p2, t) +{ + return { "x": (1 - t) * p1.x + t * p2.x, "y": (1 - t) * p1.y + t * p2.y }; +} + +function cross(p1, p2) +{ + return p1.x * p2.y - p1.y * p2.x; +} + + +}; + +Ops.Gl.Geometry.SvgPathToGeometry_v2.prototype = new CABLES.Op(); +CABLES.OPS["4267b3e7-1285-4a3e-acc8-ea92a72a6bc0"]={f:Ops.Gl.Geometry.SvgPathToGeometry_v2,objName:"Ops.Gl.Geometry.SvgPathToGeometry_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Geometry.TransformGeometry +// +// ************************************************************** + +Ops.Gl.Geometry.TransformGeometry = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + geometry = op.inObject("Geometry"), + transX = op.inValue("Translate X"), + transY = op.inValue("Translate Y"), + transZ = op.inValue("Translate Z"), + scaleX = op.inValueSlider("Scale X", 1), + scaleY = op.inValueSlider("Scale Y", 1), + scaleZ = op.inValueSlider("Scale Z", 1), + rotX = op.inValue("Rotation X"), + rotY = op.inValue("Rotation Y"), + rotZ = op.inValue("Rotation Z"), + outGeom = op.outObject("Result", null, "geometry"); + +transX.onChange = + transY.onChange = + transZ.onChange = + scaleX.onChange = + scaleY.onChange = + scaleZ.onChange = + rotX.onChange = + rotY.onChange = + rotZ.onChange = + geometry.onChange = update; + +const rotVec = vec3.create(); +const emptyVec = vec3.create(); +const transVec = vec3.create(); +const centerVec = vec3.create(); + +function update() +{ + const oldGeom = geometry.get(); + const i = 0; + + if (oldGeom) + { + const geom = oldGeom.copy(); + + for (let i = 0; i < geom.vertices.length; i += 3) + { + geom.vertices[i + 0] *= scaleX.get(); + geom.vertices[i + 1] *= scaleY.get(); + geom.vertices[i + 2] *= scaleZ.get(); + + geom.vertices[i + 0] += transX.get(); + geom.vertices[i + 1] += transY.get(); + geom.vertices[i + 2] += transZ.get(); + } + + for (let i = 0; i < geom.vertices.length; i += 3) + { + vec3.set(rotVec, + geom.vertices[i + 0], + geom.vertices[i + 1], + geom.vertices[i + 2]); + + vec3.rotateX(rotVec, rotVec, transVec, rotX.get() * CGL.DEG2RAD); + vec3.rotateY(rotVec, rotVec, transVec, rotY.get() * CGL.DEG2RAD); + vec3.rotateZ(rotVec, rotVec, transVec, rotZ.get() * CGL.DEG2RAD); + + geom.vertices[i + 0] = rotVec[0]; + geom.vertices[i + 1] = rotVec[1]; + geom.vertices[i + 2] = rotVec[2]; + } + + outGeom.set(null); + outGeom.set(geom); + } + else + { + outGeom.set(null); + } +} + + +}; + +Ops.Gl.Geometry.TransformGeometry.prototype = new CABLES.Op(); +CABLES.OPS["9678fee2-5436-499c-b94d-2603cdbeb380"]={f:Ops.Gl.Geometry.TransformGeometry,objName:"Ops.Gl.Geometry.TransformGeometry"}; + + + + +// ************************************************************** +// +// Ops.Gl.GlInfo +// +// ************************************************************** + +Ops.Gl.GlInfo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const gl = op.patch.cgl.gl; + +let mxsmpl = 0; +if (gl.MAX_SAMPLES !== undefined)mxsmpl = gl.getParameter(gl.MAX_SAMPLES); + +const + outGlVersion = op.outNumber("WebGl Version", gl.getParameter(gl.VERSION)), + outGlslVersion = op.outNumber("GLSL Version", gl.getParameter(gl.SHADING_LANGUAGE_VERSION)), + outFragUnis = op.outNumber("Max Frag uniforms", gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS)), + outVertUnis = op.outNumber("Max Vert uniforms", gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS)), + outTexSize = op.outNumber("Max Texture Size", gl.getParameter(gl.MAX_TEXTURE_SIZE)), + outTexUnits = op.outNumber("Max Texture Units", gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)), + outVarVec = op.outNumber("Max Varying Vectors", gl.getParameter(gl.MAX_VARYING_VECTORS)), + outMSAA = op.outNumber("Max MSAA Samples", mxsmpl), + outExts = op.outArray("Extensions", gl.getSupportedExtensions()), + outVendor = op.outString("Vendor"), + outRenderer = op.outString("Renderer"), + debugInfo = gl.getExtension("WEBGL_debug_renderer_info"); + +if (debugInfo) +{ + outVendor.set(gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL)); + outRenderer.set(gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)); +} + + +}; + +Ops.Gl.GlInfo.prototype = new CABLES.Op(); +CABLES.OPS["2a90bd0e-e743-475e-8e19-00793950b531"]={f:Ops.Gl.GlInfo,objName:"Ops.Gl.GlInfo"}; + + + + +// ************************************************************** +// +// Ops.Gl.GlPrimitive +// +// ************************************************************** + +Ops.Gl.GlPrimitive = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Execute"), + next = op.outTrigger("Next"), + + prim = op.inValueSelect("Primitive", ["LINES", "LINE_STRIP", "LINE_LOOP", "POINTS", "TRIANGLES", "TRIANGLE_FAN", "TRIANGLE_STRIP"], "LINES"); +const cgl = op.patch.cgl; + +let glPrim = cgl.gl.LINES; + +prim.onChange = function () +{ + if (prim.get() == "LINES")glPrim = cgl.gl.LINES; + if (prim.get() == "LINE_STRIP")glPrim = cgl.gl.LINE_STRIP; + if (prim.get() == "LINE_LOOP")glPrim = cgl.gl.LINE_LOOP; + if (prim.get() == "POINTS")glPrim = cgl.gl.POINTS; + if (prim.get() == "TRIANGLES")glPrim = cgl.gl.TRIANGLES; + if (prim.get() == "TRIANGLE_FAN")glPrim = cgl.gl.TRIANGLE_FAN; + if (prim.get() == "TRIANGLE_STRIP")glPrim = cgl.gl.TRIANGLE_STRIP; +}; + +exec.onTriggered = function () +{ + let shader = cgl.getShader(); + if (!shader) return; + let oldPrim = shader.glPrimitive; + shader.glPrimitive = glPrim; + + next.trigger(); + + shader.glPrimitive = oldPrim; +}; + + +}; + +Ops.Gl.GlPrimitive.prototype = new CABLES.Op(); +CABLES.OPS["ef0bae7e-32c9-4815-9ac3-9439fc1194ee"]={f:Ops.Gl.GlPrimitive,objName:"Ops.Gl.GlPrimitive"}; + + + + +// ************************************************************** +// +// Ops.Gl.GradientTexture +// +// ************************************************************** + +Ops.Gl.GradientTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inGrad = op.inGradient("Gradient"), + inDir = op.inValueSelect("Direction", ["X", "Y", "Radial"], "X"), + inSmoothstep = op.inValueBool("Smoothstep", false), + inStep = op.inBool("Step", false), + inFlip = op.inBool("Flip", false), + inSRGB = op.inBool("sRGB", false), + inOklab = op.inBool("Oklab", false), + inSize = op.inValueInt("Size", 256), + tfilter = op.inSwitch("filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inValueSelect("wrap", ["clamp to edge", "repeat", "mirrored repeat"], "clamp to edge"), + inGradArray = op.inArray("Gradient Array"), + inRandom = op.inTriggerButton("Randomize Colors"), + outTex = op.outTexture("Texture"), + outColors = op.outArray("Colors", null, 3), + outColorPos = op.outArray("Colors Pos", null, 1); + +const cgl = op.patch.cgl; + +twrap.onChange = + tfilter.onChange = + inStep.onChange = + inFlip.onChange = + inSRGB.onChange = + inOklab.onChange = + inSize.onChange = inGrad.onChange = inSmoothstep.onChange = inDir.onChange = inGradArray.onChange = update; + +inGrad.set("{\"keys\" : [{\"pos\":0,\"r\":0,\"g\":0,\"b\":0},{\"pos\":0.25,\"r\":0,\"g\":0,\"b\":0},{\"pos\":0.75,\"r\":1,\"g\":1,\"b\":1},{\"pos\":1,\"r\":1,\"g\":1,\"b\":1}]}"); + +op.onLoaded = update; + +inRandom.onTriggered = () => +{ + const keys = parseKeys(); + if (keys) + { + keys.forEach((key) => + { + key.r = Math.random(); + key.g = Math.random(); + key.b = Math.random(); + }); + const newKeys = JSON.stringify({ "keys": keys }); + inGrad.set(newKeys); + } +}; + +function rgbToOklab(r, g, b) +{ + let l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b; + let m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b; + let s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b; + l = Math.cbrt(l); m = Math.cbrt(m); s = Math.cbrt(s); + return [ + l * +0.2104542553 + m * +0.7936177850 + s * -0.0040720468, + l * +1.9779984951 + m * -2.4285922050 + s * +0.4505937099, + l * +0.0259040371 + m * +0.7827717662 + s * -0.8086757660 + ]; +} + +function clamp(value, min, max) +{ + return Math.max(Math.min(value, max), min); +} + +function oklabToRGB(L, a, b) +{ + let l = L + a * +0.3963377774 + b * +0.2158037573; + let m = L + a * -0.1055613458 + b * -0.0638541728; + let s = L + a * -0.0894841775 + b * -1.2914855480; + l **= 3; m **= 3; s **= 3; + let r = l * +4.0767416621 + m * -3.3077115913 + s * +0.2309699292; + let g = l * -1.2684380046 + m * +2.6097574011 + s * -0.3413193965; + var b = l * -0.0041960863 + m * -0.7034186147 + s * +1.7076147010; + r = clamp(r, 0, 1); g = clamp(g, 0, 1); b = clamp(b, 0, 1); + return [r, g, b]; +} + +function lin2srgb(r, g, b) +{ + r /= 255; + + const thr = 0.0031308; + + let c_loR = 12.92 * r; + + let c_hiR = 1.055 * Math.pow(r, 0.41666) - 0.055; + return ((r < thr) ? c_loR : c_hiR) * 255; +} + +function update() +{ + const keys = parseKeys(); + if (keys) updateGradient(keys); +} + +function parseKeys() +{ + let keys = null; + op.setUiError("nodata", null); + op.setUiError("parse", null); + + if (Array.isArray(inGradArray.get())) + { + keys = inGradArray.get(); + } + else + { + let grad = null; + if (!inGrad.get() || inGrad.get() === "") + { + op.setUiError("nodata", "gradient no data"); + return null; + } + + try + { + grad = JSON.parse(inGrad.get()); + } + catch (e) + { + op.setUiError("parse", "could not parse gradient data"); + } + + if (!grad || !grad.keys) + { + op.setUiError("nodata", "gradient no data"); + return null; + } + keys = grad.keys; + } + return keys; +} + +function updateGradient(keys) +{ + let width = Math.round(inSize.get()); + if (width < 4) width = 4; + + let selectedWrap = 0; + let selectedFilter = 0; + if (twrap.get() == "repeat") selectedWrap = CGL.Texture.WRAP_REPEAT; + else if (twrap.get() == "mirrored repeat") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + else if (twrap.get() == "clamp to edge") selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + if (tfilter.get() == "nearest") selectedFilter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") selectedFilter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") selectedFilter = CGL.Texture.FILTER_MIPMAP; + + const tex = new CGL.Texture(cgl); + + if (inDir.get() == "X" || inDir.get() == "Y") + { + const pixels = new Uint8Array(width * 4); + + for (let i = 0; i < keys.length - 1; i++) + { + const keyA = keys[i]; + const keyB = keys[i + 1]; + + for (let x = keyA.pos * width; x < keyB.pos * width; x++) + { + let p = CABLES.map(x, keyA.pos * width, keyB.pos * width, 0, 1); + if (inStep.get())p = Math.round(p); + if (inSmoothstep.get()) p = CABLES.smoothStep(p); + x = Math.round(x); + + let xx = x; + if (inFlip.get())xx = width - x - 1; + + if (inOklab.get()) + { + const klabA = rgbToOklab(keyA.r, keyA.g, keyA.b); + const labA_r = klabA[0]; + const labA_g = klabA[1]; + const labA_b = klabA[2]; + + const klabB = rgbToOklab(keyB.r, keyB.g, keyB.b); + const labB_r = klabB[0]; + const labB_g = klabB[1]; + const labB_b = klabB[2]; + + const l = ((p * labB_r + (1.0 - p) * labA_r)); + const a = ((p * labB_g + (1.0 - p) * labA_g)); + const b = ((p * labB_b + (1.0 - p) * labA_b)); + + const pixCol = oklabToRGB(l, a, b); + pixels[xx * 4 + 0] = Math.round(pixCol[0] * 255); + pixels[xx * 4 + 1] = Math.round(pixCol[1] * 255); + pixels[xx * 4 + 2] = Math.round(pixCol[2] * 255); + } + else + { + pixels[xx * 4 + 0] = Math.round((p * keyB.r + (1.0 - p) * keyA.r) * 255); + pixels[xx * 4 + 1] = Math.round((p * keyB.g + (1.0 - p) * keyA.g) * 255); + pixels[xx * 4 + 2] = Math.round((p * keyB.b + (1.0 - p) * keyA.b) * 255); + } + + if (typeof keyA.a !== "undefined" && typeof keyB.a !== "undefined") + { + const alpha = Math.round((p * keyB.a + (1.0 - p) * keyA.a) * 255); + pixels[xx * 4 + 3] = alpha; + } + else + { + pixels[xx * 4 + 3] = Math.round(255); + } + } + } + + if (inSRGB.get()) + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] = lin2srgb(pixels[i + 0]); + pixels[i + 1] = lin2srgb(pixels[i + 1]); + pixels[i + 2] = lin2srgb(pixels[i + 2]); + } + + if (inDir.get() == "X") tex.initFromData(pixels, width, 1, selectedFilter, selectedWrap); + if (inDir.get() == "Y") tex.initFromData(pixels, 1, width, selectedFilter, selectedWrap); + } + + if (inDir.get() == "Radial") + { + const pixels = new Uint8Array(width * width * 4); + + const animR = new CABLES.Anim(); + const animG = new CABLES.Anim(); + const animB = new CABLES.Anim(); + + for (let i = 0; i < keys.length - 1; i++) + { + animR.setValue(keys[i].pos, keys[i].r); + animG.setValue(keys[i].pos, keys[i].g); + animB.setValue(keys[i].pos, keys[i].b); + } + + for (let x = 0; x < width; x++) + { + for (let y = 0; y < width; y++) + { + const dx = x - (width - 1) / 2; + const dy = y - (width - 1) / 2; + let pos = Math.sqrt(dx * dx + dy * dy) / (width) * 2; + + if (inSmoothstep.get()) pos = CABLES.smoothStep(pos); + + pixels[(x * 4) + (y * 4 * width) + 0] = animR.getValue(pos) * 255; + pixels[(x * 4) + (y * 4 * width) + 1] = animG.getValue(pos) * 255; + pixels[(x * 4) + (y * 4 * width) + 2] = animB.getValue(pos) * 255; + pixels[(x * 4) + (y * 4 * width) + 3] = Math.round(255); + } + } + + if (inSRGB.get()) + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] = lin2srgb(pixels[i + 0]); + pixels[i + 1] = lin2srgb(pixels[i + 1]); + pixels[i + 2] = lin2srgb(pixels[i + 2]); + } + + tex.initFromData(pixels, width, width, selectedFilter, selectedWrap); + } + + const colorArr = []; + for (let i = 0; i < keys.length - 1; i++) + { + colorArr.push(keys[i].r, keys[i].g, keys[i].b); + } + + const colorPosArr = []; + for (let i = 0; i < keys.length - 1; i++) + { + colorPosArr.push(keys[i].pos); + } + + outColors.set(colorArr); + outColorPos.set(colorPosArr); + + outTex.set(null); + outTex.set(tex); +} + + +}; + +Ops.Gl.GradientTexture.prototype = new CABLES.Op(); +CABLES.OPS["01380a50-2dbb-4465-ae80-86349b0b717a"]={f:Ops.Gl.GradientTexture,objName:"Ops.Gl.GradientTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.GridTransform +// +// ************************************************************** + +Ops.Gl.GridTransform = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + numX = op.inValueInt("Num X", 5), + numY = op.inValueInt("Num Y", 5), + spaceX = op.inValue("Space X", 1), + spaceY = op.inValue("Space Y", 1), + next = op.outTrigger("Next"), + outIndex = op.outNumber("Index"), + outX = op.outNumber("x index"), + outY = op.outNumber("y index"); + +let matOrig = mat4.create(); +let vec = vec3.create(); + +let cgl = op.patch.cgl; + +render.onTriggered = function () +{ + cgl.pushModelMatrix(); + + mat4.copy(matOrig, cgl.modelMatrix()); + + let mx = spaceX.get(); + let my = spaceY.get(); + + let maxX = Math.floor(numX.get()); + let maxY = Math.floor(numY.get()); + + let alX = ((maxX - 1) * mx) / 2; + let alY = ((maxY - 1) * my) / 2; + + let i = 0; + for (let y = 0; y < maxY; y++) + { + outY.set(y); + + for (let x = 0; x < maxX; x++) + { + vec3.set(vec, + x * mx - alX, + y * my - alY, + 0); + outX.set(x); + mat4.translate(cgl.mMatrix, matOrig, vec); + outIndex.set(i); + i++; + next.trigger(); + } + } + + cgl.popModelMatrix(); +}; + + +}; + +Ops.Gl.GridTransform.prototype = new CABLES.Op(); +CABLES.OPS["242e9cfa-3f83-4ff7-b9e8-1c136938a270"]={f:Ops.Gl.GridTransform,objName:"Ops.Gl.GridTransform"}; + + + + +// ************************************************************** +// +// Ops.Gl.Identity +// +// ************************************************************** + +Ops.Gl.Identity = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe=op.inTrigger("exe"); +const trigger=op.outTrigger('trigger'); +const cgl=op.patch.cgl; + +exe.onTriggered=function() +{ + cgl.pushModelMatrix(); + + mat4.identity(cgl.mMatrix); + trigger.trigger(); + + cgl.popModelMatrix(); +}; + +}; + +Ops.Gl.Identity.prototype = new CABLES.Op(); +CABLES.OPS["59a5a21f-4498-4960-8527-86f75ab5ec11"]={f:Ops.Gl.Identity,objName:"Ops.Gl.Identity"}; + + + + +// ************************************************************** +// +// Ops.Gl.IdentityViewMatrix +// +// ************************************************************** + +Ops.Gl.IdentityViewMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe=op.inTrigger("exe"); +const trigger=op.outTrigger('trigger'); +const cgl=op.patch.cgl; + +exe.onTriggered=function() +{ + cgl.pushViewMatrix(); + mat4.identity(cgl.vMatrix); + trigger.trigger(); + cgl.popViewMatrix(); +}; + +}; + +Ops.Gl.IdentityViewMatrix.prototype = new CABLES.Op(); +CABLES.OPS["265c70e7-4d20-4ce0-9aa4-2c12ed867e8f"]={f:Ops.Gl.IdentityViewMatrix,objName:"Ops.Gl.IdentityViewMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.ImageSequenceAnim_v2 +// +// ************************************************************** + +Ops.Gl.ImageSequenceAnim_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTime = op.inValue("Time"), + inType = op.inSwitch("Unit", ["Seconds", "Frames"], "Seconds"), + fps = op.inValueFloat("FPS", 10), + numX = op.inValueFloat("Num X", 4), + numY = op.inValueFloat("Num Y", 4), + maxFrames = op.inInt("Max Frames", 0), + + texRepeatX = op.outNumber("Repeat X"), + texRepeatY = op.outNumber("Repeat Y"), + texU = op.outNumber("Offset X"), + texV = op.outNumber("Offset Y"), + + outFrame = op.outNumber("Frame"), + outProgress = op.outNumber("Progress"); + +numX.onChange = numY.onChange = setRepeat; + +texU.set(0); +texV.set(0); + +const posX = 0; +const posY = 0; +const lastSwitch = 0; +const frame = 0; +setRepeat(); + +function setRepeat() +{ + texRepeatY.set(1 / numY.get()); + texRepeatX.set(1 / numX.get()); + update(); +} + +inTime.onChange = update; + +function update() +{ + let frame = Math.ceil(Math.abs(inTime.get()) * (fps.get())); + if (inType.get() == "Frames") frame = inTime.get(); + let numFrames = numX.get() * numY.get(); + if (maxFrames.get() !== 0) numFrames = maxFrames.get(); + + frame %= numFrames; + + const row = Math.floor(frame / (numX.get())); + const col = frame - (row * (numX.get())); + + outFrame.set(frame); + outProgress.set((frame) / (numFrames - 1)); + + texU.set(texRepeatX.get() * col); + texV.set(texRepeatY.get() * row); +} + + +}; + +Ops.Gl.ImageSequenceAnim_v2.prototype = new CABLES.Op(); +CABLES.OPS["282203ae-8e8c-4ff7-a0a6-f0cb0ec1ca25"]={f:Ops.Gl.ImageSequenceAnim_v2,objName:"Ops.Gl.ImageSequenceAnim_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.InteractiveRectangle_v2 +// +// ************************************************************** + +Ops.Gl.InteractiveRectangle_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Trigger in"), + trigger = op.outTrigger("Trigger out"), + width = op.inValue("Width", 1), + height = op.inValue("Height", 1), + inId = op.inString("ID"), + classPort = op.inString("Class"), + pivotX = op.inValueSelect("Pivot x", ["center", "left", "right"]), + pivotY = op.inValueSelect("Pivot y", ["center", "top", "bottom"]), + axis = op.inValueSelect("Axis", ["xy", "xz"]), + isInteractive = op.inValueBool("Is Interactive", true), + renderRect = op.inValueBool("Render Rectangle", true), + divVisible = op.inValueBool("Show Boundings", true), + cursorPort = op.inValueSelect("Cursor", ["auto", "crosshair", "pointer", "Hand", "move", "n-resize", "ne-resize", "e-resize", "se-resize", "s-resize", "sw-resize", "w-resize", "nw-resize", "text", "wait", "help", "none"], "pointer"), + active = op.inValueBool("Render", true); + +const geomOut = op.outObject("geometry"); +geomOut.ignoreValueSerialize = true; + +const + mouseOver = op.outBoolNum("Pointer Hover", false), + mouseDown = op.outBoolNum("Pointer Down", false), + outX = op.outNumber("Pointer X"), + outY = op.outNumber("Pointer Y"), + outTop = op.outNumber("Top"), + outLeft = op.outNumber("Left"), + outRight = op.outNumber("Right"), + outBottom = op.outNumber("Bottom"), + mouseClick = op.outTrigger("Left Click"); + +const elementPort = op.outObject("Dom Element"); + +active.setUiAttribs({ "title": "Active" }); + +const cgl = op.patch.cgl; +axis.set("xy"); +pivotX.set("center"); +pivotY.set("center"); + +const geom = new CGL.Geometry(op.name); +let mesh = null; +let div = null; +const m = mat4.create(); +const trans = mat4.create(); +const pos = vec3.create(); +const divAlign = vec3.create(); +const divAlignSize = vec3.create(); + +axis.onChange = rebuild; +pivotX.onChange = rebuild; +pivotY.onChange = rebuild; +width.onChange = rebuild; +height.onChange = rebuild; +cursorPort.onChange = updateCursor; +rebuild(); + +const modelMatrix = mat4.create(); +const identViewMatrix = mat4.create(); +const zeroVec3 = vec3.create(); + +render.onTriggered = function () +{ + if (!div) + { + setUpDiv(); + addListeners(); + updateDivVisibility(); + updateIsInteractive(); + } + updateDivSize(); + + if (active.get() && renderRect.get() && mesh) mesh.render(cgl.getShader()); + + trigger.trigger(); +}; + +function rebuild() +{ + let w = width.get(); + let h = height.get(); + let x = 0; + let y = 0; + + if (typeof w == "string")w = parseFloat(w); + if (typeof h == "string")h = parseFloat(h); + + if (pivotX.get() == "center") + { + x = 0; + divAlign[0] = -w / 2; + } + if (pivotX.get() == "right") + { + x = -w / 2; + } + if (pivotX.get() == "left") + { + x = w / 2; + } + + if (pivotY.get() == "center") + { + y = 0; + divAlign[1] = -h / 2; + } + if (pivotY.get() == "top") y = -h / 2; + if (pivotY.get() == "bottom") y = +h / 2; + + const verts = []; + const tc = []; + const norms = []; + const indices = []; + + const numRows = 1; + const numColumns = 1; + + const stepColumn = w / numColumns; + const stepRow = h / numRows; + + let c, r; + + for (r = 0; r <= numRows; r++) + { + for (c = 0; c <= numColumns; c++) + { + verts.push(c * stepColumn - width.get() / 2 + x); + if (axis.get() == "xz") verts.push(0.0); + verts.push(r * stepRow - height.get() / 2 + y); + if (axis.get() == "xy") verts.push(0.0); + + tc.push(c / numColumns); + tc.push(1.0 - r / numRows); + + if (axis.get() == "xz") + { + norms.push(0); + norms.push(1); + norms.push(0); + } + + if (axis.get() == "xy") + { + norms.push(0); + norms.push(0); + norms.push(-1); + } + } + } + + for (c = 0; c < numColumns; c++) + { + for (r = 0; r < numRows; r++) + { + const ind = c + (numColumns + 1) * r; + const v1 = ind; + const v2 = ind + 1; + const v3 = ind + numColumns + 1; + const v4 = ind + 1 + numColumns + 1; + + indices.push(v1); + indices.push(v3); + indices.push(v2); + + indices.push(v2); + indices.push(v3); + indices.push(v4); + } + } + + geom.clear(); + geom.vertices = verts; + geom.texCoords = tc; + geom.verticesIndices = indices; + geom.vertexNormals = norms; + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); + + geomOut.set(null); + geomOut.set(geom); +} + +let divX = 0; +let divY = 0; +let divWidth = 0; +let divHeight = 0; + +const mMatrix = mat4.create(); +divVisible.onChange = updateDivVisibility; +inId.onChange = updateId; +classPort.onChange = updateClassNames; + +function updateDivVisibility() +{ + if (div) + { + if (divVisible.get()) div.style.border = "1px solid red"; + else div.style.border = "none"; + } +} + +function updateCursor() +{ + if (div) + { + div.style.cursor = cursorPort.get(); + } +} + +function updateId() +{ + if (div) + { + div.setAttribute("id", inId.get()); + } +} + +function updateDivSize() +{ + // var vp=cgl.getViewPort(); + + mat4.multiply(mMatrix, cgl.vMatrix, cgl.mMatrix); + vec3.transformMat4(pos, divAlign, mMatrix); + vec3.transformMat4(trans, pos, cgl.pMatrix); + + const x1 = (trans[0] * cgl.canvasWidth / 2) + cgl.canvasWidth / 2; + const y1 = (trans[1] * cgl.canvasHeight / 2) + cgl.canvasHeight / 2; + + divAlignSize[0] = divAlign[0] + width.get(); + divAlignSize[1] = divAlign[1]; + + vec3.transformMat4(pos, divAlignSize, mMatrix); + vec3.transformMat4(trans, pos, cgl.pMatrix); + + const x2 = ((trans[0] * cgl.canvasWidth / 2) + cgl.canvasWidth / 2); + const y2 = ((trans[1] * cgl.canvasHeight / 2) + cgl.canvasHeight / 2); + + divAlignSize[0] = divAlign[0]; + divAlignSize[1] = divAlign[1] + height.get(); + + vec3.transformMat4(pos, divAlignSize, mMatrix); + vec3.transformMat4(trans, pos, cgl.pMatrix); + + const x3 = ((trans[0] * cgl.canvasWidth / 2) + cgl.canvasWidth / 2); + const y3 = ((trans[1] * cgl.canvasHeight / 2) + cgl.canvasHeight / 2); + + divAlignSize[0] = divAlign[0] + width.get(); + divAlignSize[1] = divAlign[1] + height.get(); + + vec3.transformMat4(pos, divAlignSize, mMatrix); + vec3.transformMat4(trans, pos, cgl.pMatrix); + + const x4 = ((trans[0] * cgl.canvasWidth / 2) + cgl.canvasWidth / 2); + const y4 = ((trans[1] * cgl.canvasHeight / 2) + cgl.canvasHeight / 2); + + divX = Math.min(x1, x2, x3, x4); + divY = Math.min(cgl.canvasHeight - y1, cgl.canvasHeight - y2, cgl.canvasHeight - y3, cgl.canvasHeight - y4); + + const xb = Math.max(x1, x2, x3, x4); + const yb = Math.max(cgl.canvasHeight - y1, cgl.canvasHeight - y2, cgl.canvasHeight - y3, cgl.canvasHeight - y4); + + outTop.set(divY); + outLeft.set(divX); + outRight.set(xb); + outBottom.set(yb); + + divWidth = Math.abs(xb - divX); + divHeight = Math.abs(yb - divY); + + divX /= op.patch.cgl.pixelDensity; + divY /= op.patch.cgl.pixelDensity; + divWidth /= op.patch.cgl.pixelDensity; + divHeight /= op.patch.cgl.pixelDensity; + + // div.style.left=divX+'px'; + // div.style.top=divY+'px'; + // div.style.width=divWidth+'px'; + // div.style.height=divHeight+'px'; + + const divXpx = divX + "px"; + const divYpx = divY + "px"; + const divWidthPx = divWidth + "px"; + const divHeightPx = divHeight + "px"; + if (divXpx != div.style.left) div.style.left = divXpx; + if (divYpx != div.style.top) div.style.top = divYpx; + if (div.style.width != divWidthPx) div.style.width = divWidthPx; + if (div.style.height != divHeightPx) div.style.height = divHeightPx; +} + +function updateClassNames() +{ + if (div) + { + div.className = classPort.get(); + } +} + +op.onDelete = function () +{ + if (div)div.remove(); +}; + +function setUpDiv() +{ + if (!div) + { + div = document.createElement("div"); + div.dataset.op = op.id; + div.oncontextmenu = function (e) + { + e.preventDefault(); + }; + + div.style.padding = "0px"; + div.style.position = "absolute"; + div.style["box-sizing"] = "border-box"; + div.style.border = "1px solid red"; + // div.style['border-left']="1px solid blue"; + // div.style['border-top']="1px solid green"; + div.style["z-index"] = "500"; + + div.style["-webkit-user-select"] = "none"; + div.style["user-select"] = "none"; + div.style["-webkit-tap-highlight-color"] = "rgba(0,0,0,0)"; + div.style["-webkit-touch-callout"] = "none"; + + const canvas = op.patch.cgl.canvas.parentElement; + canvas.appendChild(div); + updateCursor(); + updateIsInteractive(); + updateId(); + updateClassNames(); + } + updateDivSize(); + elementPort.set(div); +} + +let listenerElement = null; + +function onMouseMove(e) +{ + const offsetX = -width.get() / 2; + const offsetY = -height.get() / 2; + + outX.set(Math.max(0.0, Math.min(1.0, e.offsetX / divWidth))); + outY.set(Math.max(0.0, Math.min(1.0, 1.0 - e.offsetY / divHeight))); +} + +function onMouseLeave(e) +{ + mouseDown.set(false); + mouseOver.set(false); +} + +function onMouseEnter(e) +{ + mouseOver.set(true); +} + +function onMouseDown(e) +{ + mouseDown.set(true); +} + +function onMouseUp(e) +{ + mouseDown.set(false); +} + +function onmouseclick(e) +{ + mouseClick.trigger(); +} + +function onTouchMove(e) +{ + const targetEle = document.elementFromPoint(e.targetTouches[0].pageX, e.targetTouches[0].pageY); + + if (targetEle == div) + { + mouseOver.set(true); + if (e.touches && e.touches.length > 0) + { + const rect = div.getBoundingClientRect(); // e.target + const x = e.targetTouches[0].pageX - rect.left; + const y = e.targetTouches[0].pageY - rect.top; + + const touch = e.touches[0]; + + outX.set(Math.max(0.0, Math.min(1.0, x / divWidth))); + outY.set(Math.max(0.0, Math.min(1.0, 1.0 - y / divHeight))); + + onMouseMove(touch); + } + } + else + { + mouseOver.set(false); + } +} + +active.onChange = updateActiveRender; +function updateActiveRender() +{ + if (active.get()) + { + addListeners(); + if (div) div.style.display = "block"; + } + else + { + removeListeners(); + if (div) div.style.display = "none"; + } +} + +isInteractive.onChange = updateIsInteractive; +function updateIsInteractive() +{ + if (isInteractive.get()) + { + addListeners(); + if (div)div.style["pointer-events"] = "initial"; + } + else + { + removeListeners(); + mouseDown.set(false); + mouseOver.set(false); + if (div)div.style["pointer-events"] = "none"; + } +} + +function removeListeners() +{ + if (listenerElement) + { + document.removeEventListener("touchmove", onTouchMove); + listenerElement.removeEventListener("touchend", onMouseUp); + listenerElement.removeEventListener("touchstart", onMouseDown); + + listenerElement.removeEventListener("click", onmouseclick); + listenerElement.removeEventListener("mousemove", onMouseMove); + listenerElement.removeEventListener("mouseleave", onMouseLeave); + listenerElement.removeEventListener("mousedown", onMouseDown); + listenerElement.removeEventListener("mouseup", onMouseUp); + listenerElement.removeEventListener("mouseenter", onMouseEnter); + // listenerElement.removeEventListener('contextmenu', onClickRight); + listenerElement = null; + } +} + +function addListeners() +{ + if (listenerElement)removeListeners(); + + listenerElement = div; + + if (listenerElement) + { + document.addEventListener("touchmove", onTouchMove); + listenerElement.addEventListener("touchend", onMouseUp); + listenerElement.addEventListener("touchstart", onMouseDown); + + listenerElement.addEventListener("click", onmouseclick); + listenerElement.addEventListener("mousemove", onMouseMove); + listenerElement.addEventListener("mouseleave", onMouseLeave); + listenerElement.addEventListener("mousedown", onMouseDown); + listenerElement.addEventListener("mouseup", onMouseUp); + listenerElement.addEventListener("mouseenter", onMouseEnter); + // listenerElement.addEventListener('contextmenu', onClickRight); + } +} + + +}; + +Ops.Gl.InteractiveRectangle_v2.prototype = new CABLES.Op(); +CABLES.OPS["334728ca-60a2-4a42-a059-d9b5f3fe4d32"]={f:Ops.Gl.InteractiveRectangle_v2,objName:"Ops.Gl.InteractiveRectangle_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.LayerSequence +// +// ************************************************************** + +Ops.Gl.LayerSequence = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + cleanup = op.inTriggerButton("Clean up connections"); + +const + cgl = op.patch.cgl, + triggers = [], + num = 16; + +let updateTimeout = null; + +exe.onTriggered = triggerAll; +cleanup.onTriggered = clean; +cleanup.setUiAttribs({ "hidePort": true }); +cleanup.setUiAttribs({ "hideParam": true }); + +for (let i = 0; i < num; i++) +{ + const p = op.outTrigger("trigger " + i); + triggers.push(p); + p.onLinkChanged = updateButton; +} + +function updateButton() +{ + clearTimeout(updateTimeout); + updateTimeout = setTimeout(() => + { + let show = false; + for (let i = 0; i < triggers.length; i++) + if (triggers[i].links.length > 1) show = true; + + cleanup.setUiAttribs({ "hideParam": !show }); + + if (op.isCurrentUiOp()) op.refreshParams(); + }, 60); +} + +function triggerAll() +{ + for (let i = 0; i < triggers.length; i++) + { + if (triggers[i].isLinked()) + { + cgl.gl.clear(cgl.gl.DEPTH_BUFFER_BIT); + + triggers[i].trigger(); + } + } +} + +function clean() +{ + let count = 0; + for (let i = 0; i < triggers.length; i++) + { + let removeLinks = []; + + if (triggers[i].links.length > 1) + for (let j = 1; j < triggers[i].links.length; j++) + { + while (triggers[count].links.length > 0) count++; + + removeLinks.push(triggers[i].links[j]); + const otherPort = triggers[i].links[j].getOtherPort(triggers[i]); + op.patch.link(op, "trigger " + count, otherPort.parent, otherPort.name); + count++; + } + + for (let j = 0; j < removeLinks.length; j++) removeLinks[j].remove(); + } + updateButton(); +} + + +}; + +Ops.Gl.LayerSequence.prototype = new CABLES.Op(); +CABLES.OPS["09c33c7e-e282-468d-93e1-b8a7a0a4c4d2"]={f:Ops.Gl.LayerSequence,objName:"Ops.Gl.LayerSequence"}; + + + + +// ************************************************************** +// +// Ops.Gl.LetterBox +// +// ************************************************************** + +Ops.Gl.LetterBox = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const render = op.inTrigger("render"); +const trigger = op.outTrigger("trigger"); + +const outWidth = op.outNumber("Width"); +const outHeight = op.outNumber("Height"); + +let ratio = op.inValueSelect("ratio", [1, 1.25, 1.3333333333, 1.777777777778, 1.88, 2.33333333333333, 2.4151, 3, 4]); + +ratio.set(1.777777777778); + +const cgl = op.patch.cgl; + +let blackBars = op.inValueBool("black bars"); +blackBars.set(true); + +let x = 0, y = 0, w = 1000, h = 1000; + +function resize() +{ + let _w = cgl.canvasHeight * ratio.get(); + let _h = cgl.canvasHeight; + let _x = 0; + let _y = 0; + if (_w > cgl.canvasWidth) + { + _w = cgl.canvasWidth; + _h = cgl.canvasWidth / ratio.get(); + } + + if (_w < cgl.canvasWidth) _x = (cgl.canvasWidth - _w) / 2; + if (_h < cgl.canvasHeight) _y = (cgl.canvasHeight - _h) / 2; + + _w = Math.ceil(_w); + _h = Math.ceil(_h); + _x = Math.ceil(_x); + _y = Math.ceil(_y); + + if (_w != w || _h != h || _x != x || _y != y) + { + w = _w; + h = _h; + x = _x; + y = _y; + + cgl.setViewPort(x, y, w, h); + + for (let i = 0; i < op.patch.ops.length; i++) + if (op.patch.ops[i].onResize)op.patch.ops[i].onResize(); + } + + outWidth.set(w); + outHeight.set(h); +} + +op.onDelete = function () +{ + // cgl.gl.disable(cgl.gl.SCISSOR_TEST); + cgl.resetViewPort(); +}; + +render.onTriggered = function () +{ + if (blackBars.get()) + { + cgl.gl.clearColor(0, 0, 0, 1); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + } + + resize(); + + x = Math.round(x); + y = Math.round(y); + w = Math.round(w + 0.5); + h = Math.round(h + 0.5); + + cgl.gl.scissor(x, y, w, h); + cgl.setViewPort(x, y, w, h); + + mat4.perspective(cgl.pMatrix, 45, ratio.get(), 0.1, 1100.0); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.LetterBox.prototype = new CABLES.Op(); +CABLES.OPS["6a62d24e-d0a6-4110-a042-15e3cda59db4"]={f:Ops.Gl.LetterBox,objName:"Ops.Gl.LetterBox"}; + + + + +// ************************************************************** +// +// Ops.Gl.LineFont_v2 +// +// ************************************************************** + +Ops.Gl.LineFont_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + string = op.inString("Text", "cables"), + letterSpacing = op.inValue("Letter Spacing", 1), + align = op.inSwitch("align", ["left", "center", "right"], "left"), + inDraw = op.inBool("Render", true), + outArr = op.outArray("Lines", null, 3); + +let lineArray = []; +let stringWidth = 0; +const meshes = []; +const vec = vec3.create(); +const cgl = op.patch.cgl; +const characters = + [ + { + // a + "l": [ + [182.667, 349.057, 164.167, 349.057], + [160.333, 360.557, 171.333, 326.89, 175.333, 326.89, 186, 360.557], + ] + }, + { + // b + "l": + [ + [174.333, 343.057, 160.255, 343.057], + [160.333, 326.89, 175.5, 326.89, 178.333, 330.724, 178.333, 340.724, 174.333, 343.057, 180.5, 346.807, 180.5, 357.474, 176, 360.557, 160.167, 360.557, 160.333, 326.89] + ] + }, + { + // c + "l": + [ + [180.583, 331.307, 175.917, 326.807, 166, 326.807, 160.083, 332.557, 160.083, 354.557, 165.833, 360.474, 175.917, 360.474, 180.5, 355.807], + ] + }, + { + // d + "l": + [ + [160.083, 327.057, 160.083, 360.557, 175.417, 360.557, 180.708, 355.265, 180.708, 332.974, 175.104, 327.057, 160.083, 327.057], + ] + }, + { + // e + "l": + [ + [175.167, 343.932, 160.436, 343.932], + [177.917, 326.807, 164.5, 326.807, 160.436, 330.872, 160.436, 356.845, 164.014, 360.422, 177.917, 360.422] + ] + }, + { + // f + "l": + [ + [176.792, 326.932, 164.125, 326.932, 160.167, 330.891, 160.167, 360.683], + [173.458, 345.599, 160.167, 345.599], + ] + }, + { + // g + "l": + [ + [180.455, 332.395, 175.391, 328.33, 166.194, 328.33, 160.167, 334.357, 160.167, 355.933, 165.792, 360.557, 176.038, 360.557, 181.62, 354.976, 181.62, 344.811, 173.122, 344.811], + ] + }, + { + // h + "l": + [ + [160.167, 326.89, 160.167, 360.557], + [160.5, 343.723, 182.333, 343.723], + [182.333, 326.89, 182.333, 360.974] + ] + }, + { + // i + "l": + [ + [160.167, 326.807, 160.167, 360.641] + ] + }, + { + // j + "l": + [ + [159.833, 326.89, 166.833, 326.89, 166.833, 362.057, 163.962, 364.928, 159.833, 364.928], + ] + }, + { + // k + "l": + [ + [160.167, 326.807, 160.167, 360.974], + [178.917, 326.807, 160.167, 348.474], + [164.905, 342.998, 180.333, 360.64] + ] + }, + { + // l + "l": + [ + [160.167, 326.974, 160.167, 360.558, 176.083, 360.558], + ] + }, + { + "l": + [ + [160.167, 360.557, 160.247, 326.89, 164.997, 326.89, 175.5, 360.557, 178, 360.557, 188.58, 326.89, 193.33, 326.89, 193.25, 360.557], + ] + }, + { + // n + "l": + [ + [160.167, 360.599, 160.167, 326.933, 164.629, 326.933, 178.333, 360.599, 182.083, 360.599, 182.083, 326.933], + ] + }, + { + "l": + [ + [160.283, 332.448, 165.764, 326.967, 178.405, 326.967, 183.668, 332.23, 183.668, 354.365, 177.434, 360.599, 166.367, 360.599, 160.167, 354.399, 160.283, 332.448], + ] + }, + + { + // p + "l": + [ + [160.167, 360.432, 160.167, 327.015, 175.955, 327.015, 179.667, 330.728, 179.667, 341.015, 175.602, 345.08, 160.167, 345.08], + ] + }, + + { + // q + "l": + [ + [184.504, 361.693, 180.517, 357.706], + [160.283, 332.413, 165.764, 326.932, 178.405, 326.932, 183.668, 332.195, 183.668, 354.33, 177.434, 360.564, 166.367, 360.564, 160.167, 354.364, 160.283, 332.413], + ] + }, + { + // r + "l": + [ + [179.667, 360.307, 173.5, 344.955], + [160.167, 360.307, 160.167, 326.89, 175.955, 326.89, 179.667, 330.603, 179.667, 340.89, 175.602, 344.955, 160.167, 344.955], + ] + }, + { + // s + "l": + [ + [179.979, 326.87, 165.895, 326.87, 160.167, 332.598, 160.167, 338.307, 179.292, 349.057, 179.292, 355.223, 173.917, 360.598, 160.167, 360.598], + ] + }, + { + // t + "l": + [ + [170.417, 326.89, 170.417, 360.974], + [180.5, 326.89, 160.167, 326.89] + ] + }, + { + // u + "l": + [ + [160.167, 327.14, 160.167, 356.845, 164.108, 360.786, 178.125, 360.786, 182.012, 356.899, 181.958, 327.14], + ] + }, + { + // v + "l": + [ + [160.167, 326.901, 170.167, 360.797, 174.417, 360.797, 184.667, 326.734], + ] + }, + { + // w + "l": + [ + [203.5, 326.89, 195.208, 360.557, 191.458, 360.557, 184, 326.89, 179.758, 326.89, 172.208, 360.557, 168.458, 360.557, 160.167, 326.89], + ] + }, + { + // x + "l": + [ + [181.333, 360.64, 159.667, 326.807], + [159.667, 360.557, 181.75, 326.807] + ] + }, + { + // y + "l": + [ + [160.167, 326.891, 168.508, 347.224, 173.992, 347.224, 182.917, 326.891], + [171.333, 347.224, 171.333, 360.641] + ] + }, + { + // z + "l": + [ + [161.167, 326.807, 180.5, 326.807, 180.5, 332.473, 161.167, 355.223, 161.167, 360.557, 180.5, 360.557], + ] + }, + + { + // 0 + "l": + [ + [167.591, 326.89, 173.076, 326.89, 180.5, 334.315, 180.5, 353.132, 173.076, 360.557, 167.591, 360.557, 160.167, 353.132, 160.167, 334.315, 167.591, 326.89], + ] + }, + { + // 1 + "l": + [ + [160.167, 334.315, 167.549, 326.932, 170.333, 326.89, 170.417, 360.557], + ] + }, + { + // 2 + "l": + [ + [164.066, 330.415, 167.591, 326.89, 180.5, 326.89, 180.5, 330.603, 160.167, 351.224, 160.167, 360.557, 180.5, 360.599], + ] + }, + { + // 3 + "l": + [ + [169.583, 342.932, 180.5, 342.932], + [163.129, 331.353, 167.591, 326.89, 180.5, 326.89, 180.5, 360.557, 167.591, 360.557, 162.837, 355.803], + ] + }, + { + // 4 + "l": + [ + [178.076, 326.89, 178.076, 360.599], + [160.167, 326.89, 160.167, 338.474, 165.104, 343.412, 178.5, 343.412], + ] + }, + { + // 5 + "l": + [ + [180.5, 326.89, 160.098, 326.958, 160.167, 342.932, 180.5, 342.932, 180.5, 353.132, 173.076, 360.557, 160.167, 360.557], + ] + }, + { + // 6 + "l": + [ + [173.076, 326.89, 167.591, 326.89, 160.167, 333.89, 160.167, 353.132, 167.591, 360.557, 173.417, 360.557, 180.671, 353.303, 180.671, 342.932, 160.167, 342.932], + ] + }, + { + // 7 + "l": + [ + [163.591, 326.89, 180.5, 326.89, 170.417, 360.557], + ] + }, + { + // 8 + "l": + [ + [180.5, 334.315, 173.076, 326.89, 167.591, 326.89, 160.167, 334.315, 180.5, 353.132, 173.076, 360.557, 167.591, 360.557, 160.167, 353.132, 180.5, 334.315], + ] + }, + { + // 9 + "l": + [ + [167.591, 360.557, 173.076, 360.557, 180.5, 353.132, 180.5, 334.315, 173.076, 326.89, 167.591, 326.89, 160.167, 334.315, 160.167, 342.932, 180.5, 342.932] + ] + }, + + { + // & + "l": + [ + [182.496, 351.137, 173.076, 360.557, 167.591, 360.557, 160.167, 353.132, 160.167, 348.087, 173.922, 339.533, 172.229, 326.89, 165.167, 326.89, 165.052, 339.515, 184.292, 359.432], + ] + }, + { + // ' + "l": + [ + [160.167, 326.932, 160.167, 333.557], + [162.879, 326.932, 162.879, 333.557] + + ] + }, + { + // ; + "l": + [ + [160.167, 342.932, 160.167, 346.224], + [160.167, 354.224, 160.167, 360.557] + ] + }, + { + // : + "l": + [ + [160.167, 342.932, 160.167, 346.224], + [160.167, 354.224, 160.167, 357.974] + ] + }, + { + // _ + "l": + [ + [160.167, 360.557, 170.417, 360.557] + ] + }, + { + // + + "l": + [ + [160.167, 342.932, 170, 342.932], + [164.833, 347.849, 164.833, 338.015] + ] + }, + { + // - + "l": + [ + [160.167, 342.932, 170, 342.932], + ] + }, + { + // / + "l": + [ + [180.5, 326.89, 160.167, 360.557], + ] + }, + { + // . + "l": + [ + [160.167, 360.599, 163.417, 360.599], + ] + }, + { + // , + "l": + [ + [165.163, 360.557, 160.167, 365.553], + ] + }, + { + // ) + "l": + [ + [160.167, 360.557, 167.591, 353.132, 167.591, 334.315, 160.167, 326.89], + ] + }, + { + // ( + "l": + [ + [167.591, 326.89, 160.167, 334.315, 160.167, 353.132, 167.591, 360.557], + ] + }, + { + // ? + "l": + [ + [170.333, 363.481, 170.333, 368.966], + [160.167, 334.315, 167.591, 326.89, 173.076, 326.89, 180.5, 334.315, 180.5, 342.932, 170.333, 353.132, 170.333, 360.557] + ] + }, + { + // ! + "l": + [ + [160.167, 353.557, 160.167, 326.89], + [160.167, 357.64, 160.167, 360.557] + ] + }, + + ]; + +function translateX(w) +{ + transX += w; + + vec3.set(vec, w, 0, 0); + mat4.translate(cgl.mMatrix, cgl.mMatrix, vec); +} + +let alignMode = 0; +align.onChange = function () +{ + if (align.get() == "left")alignMode = 0; + if (align.get() == "center")alignMode = 1; + if (align.get() == "right")alignMode = 2; +}; + +let oldPrim = 0; +let shader = null; + +let transX = 0; + +function renderChar(charIndex, simulate) +{ + shader = cgl.getShader(); + if (!shader) return; + oldPrim = shader.glPrimitive; + + shader.glPrimitive = cgl.gl.LINE_STRIP; + + if (charIndex >= characters.length)charIndex = 0; + + if (!simulate) + { + for (let m = 0; m < characters[charIndex].linesArr.length; m += 3) + { + lineArray.push( + characters[charIndex].linesArr[m + 0] + transX, + characters[charIndex].linesArr[m + 1], + characters[charIndex].linesArr[m + 2] + ); + + // characters[charIndex].m[m].render(op.patch.cgl.getShader()); + } + + for (let m = 0; m < characters[charIndex].m.length; m++) + { + if (inDraw.get()) + characters[charIndex].m[m].render(op.patch.cgl.getShader()); + } + translateX(characters[charIndex].w * letterSpacing.get()); + } + else + { + stringWidth += characters[charIndex].w * letterSpacing.get(); + } + shader.glPrimitive = oldPrim; +} + +render.onTriggered = function () +{ + stringWidth = 0; + if (!string.get()) return; + let spaceWidth = 0.15; + vec3.set(vec, 0.3, 0, 0); + cgl.pushModelMatrix(); + + let startCharacters = 97; + let startNumbers = 48; + + let str = string.get() + ""; + + // cgl.gl.lineWidth(lineWidth.get()); + + for (let sim = 0; sim < 2; sim++) + { + transX = 0; + lineArray.length = 0; + let simulate = sim === 0; + + if (!simulate) + { + if (alignMode == 1) translateX(-stringWidth / 2 + 0.04 * letterSpacing.get()); + if (alignMode == 2) translateX(-stringWidth + 0.08 * letterSpacing.get()); + } + + for (let i = 0; i < str.length; i++) + { + let w = 0; + let charIndex = str.toLowerCase().charCodeAt(i); + + if (charIndex == 38) renderChar(36, simulate); // & + else if (charIndex == 39) renderChar(37, simulate); // ' + else if (charIndex == 34) renderChar(37, simulate); // ' + else if (charIndex == 59) renderChar(38, simulate); // ; + else if (charIndex == 58) renderChar(39, simulate); // : + else if (charIndex == 95) renderChar(40, simulate); // _ + else if (charIndex == 43) renderChar(41, simulate); // + + else if (charIndex == 45) renderChar(42, simulate); // - + else if (charIndex == 47) renderChar(43, simulate); // / + else if (charIndex == 46) renderChar(44, simulate); // . + else if (charIndex == 44) renderChar(45, simulate); // , + else if (charIndex == 41) renderChar(46, simulate); // ) + else if (charIndex == 40) renderChar(47, simulate); // () + else if (charIndex == 63) renderChar(48, simulate); // ? + else if (charIndex == 33) renderChar(49, simulate); // ! + else + if (charIndex >= startNumbers && charIndex <= startNumbers + 10) + { + renderChar(charIndex - startNumbers + 26, simulate); + } + else + if (charIndex >= startCharacters && charIndex - startCharacters < characters.length) + { + renderChar(charIndex - startCharacters, simulate); + } + else + if (charIndex == 32) + { + if (simulate)stringWidth += spaceWidth; + else translateX(spaceWidth); + } + else + { + renderChar(48, simulate); + } + } + } + + outArr.set(null); + outArr.set(lineArray); + + cgl.popModelMatrix(); +}; + +function avg(which) +{ + let avgX = 0, avgY = 0; + let count = 0; + for (let l = 0; l < characters[which].l.length; l++) + { + for (let j = 0; j < characters[which].l[l].length; j += 2) + { + avgX += characters[which].l[l][j]; + avgY += characters[which].l[l][j + 1]; + count++; + } + } + avgX /= count; + avgY /= count; + return [avgX, avgY]; +} + +function min(which) +{ + let min = 9999999; + + for (let l = 0; l < characters[which].l.length; l++) + { + for (let j = 0; j < characters[which].l[l].length; j += 2) + { + min = Math.min(min, characters[which].l[l][j]); + } + } + return min; +} + +function width(which) +{ + let min = 9999999; + let max = -9999999; + + for (let l = 0; l < characters[which].l.length; l++) + { + for (let j = 0; j < characters[which].l[l].length; j += 2) + { + min = Math.min(min, characters[which].l[l][j]); + max = Math.max(max, characters[which].l[l][j]); + } + } + return ((max - min)); +} + +meshes.length = 0; + +let avgXY = []; +let avg1 = avg(0); +let avg2 = avg(1); + +avgXY = [(avg1[0] + avg2[0]) / 2, (avg1[1] + avg2[1]) / 2]; + +for (let i = 0; i < characters.length; i++) +{ + characters[i].w = width(i) * (0.002); + characters[i].m = []; + let lines = []; + + for (let l = 0; l < characters[i].l.length; l++) + { + let count = 0; + let indices = []; + let vertices = []; + + for (let j = 0; j < characters[i].l[l].length - 2; j += 2) + { + lines.push( + (characters[i].l[l][j] - min(i)) * 0.005, + (characters[i].l[l][j + 1] - avgXY[1]) * -0.005, + 0); + + lines.push( + (characters[i].l[l][j + 2] - min(i)) * 0.005, + (characters[i].l[l][j + 3] - avgXY[1]) * -0.005, + 0); + } + + for (let j = 0; j < characters[i].l[l].length; j += 2) + { + vertices.push((characters[i].l[l][j] - min(i)) * 0.005); + vertices.push((characters[i].l[l][j + 1] - avgXY[1]) * -0.005); + vertices.push(0); + + indices.push(count); + count++; + } + + characters[i].linesArr = lines; + let geom = new CGL.Geometry(op.name); + geom.vertices = vertices; + geom.verticesIndices = indices; + let mesh = new CGL.Mesh(op.patch.cgl, geom); + characters[i].m.push(mesh); + } + + outArr.set([]); + + characters[i].w += 0.1; +} + + +}; + +Ops.Gl.LineFont_v2.prototype = new CABLES.Op(); +CABLES.OPS["a3615010-05af-45bc-bf98-6972c2de21bc"]={f:Ops.Gl.LineFont_v2,objName:"Ops.Gl.LineFont_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.MainLoop +// +// ************************************************************** + +Ops.Gl.MainLoop = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + fpsLimit = op.inValue("FPS Limit", 0), + trigger = op.outTrigger("trigger"), + width = op.outNumber("width"), + height = op.outNumber("height"), + reduceFocusFPS = op.inValueBool("Reduce FPS not focussed", true), + reduceLoadingFPS = op.inValueBool("Reduce FPS loading"), + clear = op.inValueBool("Clear", true), + clearAlpha = op.inValueBool("ClearAlpha", true), + fullscreen = op.inValueBool("Fullscreen Button", false), + active = op.inValueBool("Active", true), + hdpi = op.inValueBool("Hires Displays", false), + inUnit = op.inSwitch("Pixel Unit", ["Display", "CSS"], "Display"); + +op.onAnimFrame = render; +hdpi.onChange = function () +{ + if (hdpi.get()) op.patch.cgl.pixelDensity = window.devicePixelRatio; + else op.patch.cgl.pixelDensity = 1; + + op.patch.cgl.updateSize(); + if (CABLES.UI) gui.setLayout(); + + // inUnit.setUiAttribs({ "greyout": !hdpi.get() }); + + // if (!hdpi.get())inUnit.set("CSS"); + // else inUnit.set("Display"); +}; + +active.onChange = function () +{ + op.patch.removeOnAnimFrame(op); + + if (active.get()) + { + op.setUiAttrib({ "extendTitle": "" }); + op.onAnimFrame = render; + op.patch.addOnAnimFrame(op); + op.log("adding again!"); + } + else + { + op.setUiAttrib({ "extendTitle": "Inactive" }); + } +}; + +const cgl = op.patch.cgl; +let rframes = 0; +let rframeStart = 0; + +if (!op.patch.cgl) op.uiAttr({ "error": "No webgl cgl context" }); + +const identTranslate = vec3.create(); +vec3.set(identTranslate, 0, 0, 0); +const identTranslateView = vec3.create(); +vec3.set(identTranslateView, 0, 0, -2); + +fullscreen.onChange = updateFullscreenButton; +setTimeout(updateFullscreenButton, 100); +let fsElement = null; + +let winhasFocus = true; +let winVisible = true; + +window.addEventListener("blur", () => { winhasFocus = false; }); +window.addEventListener("focus", () => { winhasFocus = true; }); +document.addEventListener("visibilitychange", () => { winVisible = !document.hidden; }); +testMultiMainloop(); + +inUnit.onChange = () => +{ + width.set(0); + height.set(0); +}; + +function getFpsLimit() +{ + if (reduceLoadingFPS.get() && op.patch.loading.getProgress() < 1.0) return 5; + + if (reduceFocusFPS.get()) + { + if (!winVisible) return 10; + if (!winhasFocus) return 30; + } + + return fpsLimit.get(); +} + +function updateFullscreenButton() +{ + function onMouseEnter() + { + if (fsElement)fsElement.style.display = "block"; + } + + function onMouseLeave() + { + if (fsElement)fsElement.style.display = "none"; + } + + op.patch.cgl.canvas.addEventListener("mouseleave", onMouseLeave); + op.patch.cgl.canvas.addEventListener("mouseenter", onMouseEnter); + + if (fullscreen.get()) + { + if (!fsElement) + { + fsElement = document.createElement("div"); + + const container = op.patch.cgl.canvas.parentElement; + if (container)container.appendChild(fsElement); + + fsElement.addEventListener("mouseenter", onMouseEnter); + fsElement.addEventListener("click", function (e) + { + if (CABLES.UI && !e.shiftKey) gui.cycleFullscreen(); + else cgl.fullScreen(); + }); + } + + fsElement.style.padding = "10px"; + fsElement.style.position = "absolute"; + fsElement.style.right = "5px"; + fsElement.style.top = "5px"; + fsElement.style.width = "20px"; + fsElement.style.height = "20px"; + fsElement.style.cursor = "pointer"; + fsElement.style["border-radius"] = "40px"; + fsElement.style.background = "#444"; + fsElement.style["z-index"] = "9999"; + fsElement.style.display = "none"; + fsElement.innerHTML = ""; + } + else + { + if (fsElement) + { + fsElement.style.display = "none"; + fsElement.remove(); + fsElement = null; + } + } +} + +op.onDelete = function () +{ + cgl.gl.clearColor(0, 0, 0, 0); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); +}; + +function render(time) +{ + if (!active.get()) return; + if (cgl.aborted || cgl.canvas.clientWidth === 0 || cgl.canvas.clientHeight === 0) return; + + op.patch.cg = cgl; + + const startTime = performance.now(); + + op.patch.config.fpsLimit = getFpsLimit(); + + if (cgl.canvasWidth == -1) + { + cgl.setCanvas(op.patch.config.glCanvasId); + return; + } + + if (cgl.canvasWidth != width.get() || cgl.canvasHeight != height.get()) + { + let div = 1; + if (inUnit.get() == "CSS")div = op.patch.cgl.pixelDensity; + + width.set(cgl.canvasWidth / div); + height.set(cgl.canvasHeight / div); + } + + if (CABLES.now() - rframeStart > 1000) + { + CGL.fpsReport = CGL.fpsReport || []; + if (op.patch.loading.getProgress() >= 1.0 && rframeStart !== 0)CGL.fpsReport.push(rframes); + rframes = 0; + rframeStart = CABLES.now(); + } + CGL.MESH.lastShader = null; + CGL.MESH.lastMesh = null; + + cgl.renderStart(cgl, identTranslate, identTranslateView); + + if (clear.get()) + { + cgl.gl.clearColor(0, 0, 0, 1); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + } + + trigger.trigger(); + + if (CGL.MESH.lastMesh)CGL.MESH.lastMesh.unBind(); + + if (CGL.Texture.previewTexture) + { + if (!CGL.Texture.texturePreviewer) CGL.Texture.texturePreviewer = new CGL.Texture.texturePreview(cgl); + CGL.Texture.texturePreviewer.render(CGL.Texture.previewTexture); + } + cgl.renderEnd(cgl); + + op.patch.cg = null; + + if (clearAlpha.get()) + { + cgl.gl.clearColor(1, 1, 1, 1); + cgl.gl.colorMask(false, false, false, true); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT); + cgl.gl.colorMask(true, true, true, true); + } + + if (!cgl.frameStore.phong)cgl.frameStore.phong = {}; + rframes++; + + op.patch.cgl.profileData.profileMainloopMs = performance.now() - startTime; +} + +function testMultiMainloop() +{ + setTimeout( + () => + { + if (op.patch.getOpsByObjName(op.name).length > 1) + { + op.setUiError("multimainloop", "there should only be one mainloop op!"); + op.patch.addEventListener("onOpDelete", testMultiMainloop); + } + else op.setUiError("multimainloop", null, 1); + }, 500); +} + + +}; + +Ops.Gl.MainLoop.prototype = new CABLES.Op(); +CABLES.OPS["b0472a1d-db16-4ba6-8787-f300fbdc77bb"]={f:Ops.Gl.MainLoop,objName:"Ops.Gl.MainLoop"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.AnimMatrix +// +// ************************************************************** + +Ops.Gl.Matrix.AnimMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTrigger = op.inTrigger("Update"), + inMat = op.inArray("Next Matrix"), + // inStart=op.inTriggerButton("Start Anim"), + inDur = op.inFloat("Duration", 1), + next = op.outTrigger("Next"), + outArr = op.outArray("Matrix"); + +let lastTime = 0; +let startTime = 0; +let firsttime = true; +let cycle = 1; + +const anim = new CABLES.Anim(); +anim.createPort(op, "easing", init); +anim.loop = false; + +let lastMat = null; + +inDur.onChange = inMat.onChange = init; + +const a = vec3.create(); +const b = vec3.create(); + +let arr1, arr2; +let result = mat4.create(); + +inTrigger.onTriggered = () => +{ + let t = CABLES.now() / 1000; + + const perc = anim.getValue(t); + + if (arr1 && arr2) ipMat(perc); +}; + +function matEquals(a, b) +{ + return ( + a[0] == b[0] && + a[1] == b[1] && + a[2] == b[2] && + a[3] == b[3] && + a[4] == b[4] && + a[5] == b[5] && + a[6] == b[6] && + a[7] == b[7] && + a[8] == b[8] && + a[9] == b[9] && + a[10] == b[10] && + a[11] == b[11] && + a[12] == b[12] && + a[13] == b[13] && + a[14] == b[14] && + a[15] == b[15]); +} + +function init() +{ + if (!inMat.get()) return; + if (inMat.get() == lastMat) + { + // mat4.copy(result,inMat.get()); + return; + } + + if (lastMat) + if (inMat.get() == lastMat && matEquals(inMat.get(), lastMat)) + { + return; + } + + lastMat = inMat.get(); + startTime = performance.now(); + anim.clear(CABLES.now() / 1000.0); + + anim.setValue(CABLES.now() / 1000.0, cycle); + + if (cycle == 1) cycle = 0; + else cycle = 1; + + if (cycle == 0) + { + arr1 = inMat.get(); + arr2 = mat4.create(); + mat4.copy(arr2, result); + } + else + { + arr1 = mat4.create(); + arr2 = inMat.get(); + mat4.copy(arr1, result); + } + + anim.setValue(inDur.get() + CABLES.now() / 1000.0, cycle, () => + { + // result=outArr.get(); + }); + + firsttime = false; +} + +function ip(val1, val2, perc) +{ + return ((val2 - val1) * perc + val1); +} + +function ipMat(perc) +{ + if (!arr1 || !arr2 || arr1.length != arr2.length) + { + outArr.set(null); + op.logError("arrays wrong", arr1.length, arr2.length); + } + else + { + getYPR(a, arr1); + getYPR(b, arr2); + + mat4.identity(result); + result[12] = ip(arr1[12], arr2[12], perc); + result[13] = ip(arr1[13], arr2[13], perc); + result[14] = ip(arr1[14], arr2[14], perc); + + vec3.lerp(a, a, b, perc); + + mat4.rotateZ(result, result, a[2]); + mat4.rotateY(result, result, a[1]); + mat4.rotateX(result, result, a[0]); + + outArr.set(null); + outArr.set(result); + } + next.trigger(); +} + +// 0 1 2 3 +// 4 5 6 7 +// 8 9 10 11 +// 12 13 14 15 + +function getYPR(v, m) +{ + const r1 = Math.atan2(m[6], m[10]); + const c2 = Math.sqrt(m[0] * m[0] + m[1] * m[1]); + const r2 = Math.atan2(-m[2], c2); + const s1 = Math.sin(r1); + const c1 = Math.cos(r1); + const r3 = Math.atan2(s1 * m[8] - c1 * m[4], c1 * m[5] - s1 * m[9]); + + v[0] = r1; + v[1] = r2; + v[2] = r3; + return v; +} + + +}; + +Ops.Gl.Matrix.AnimMatrix.prototype = new CABLES.Op(); +CABLES.OPS["7800dde0-0ce4-4acd-9605-d412931bb90e"]={f:Ops.Gl.Matrix.AnimMatrix,objName:"Ops.Gl.Matrix.AnimMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.ArrayPathFollow +// +// ************************************************************** + +Ops.Gl.Matrix.ArrayPathFollow = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTrigger("exe"); +const arrayIn = op.inArray("array"); +const time = op.inValueFloat("time"); + +const duration = op.inValueFloat("duration"); +duration.set(0.1); + +const offset = op.inValueFloat("offset"); +offset.set(0.0); + +const lookAhead = op.inValueFloat("look ahead"); +lookAhead.set(3.0); + +const trigger = op.outTrigger("trigger"); +const triggerLookat = op.outTrigger("transform lookat"); +const idx = op.addOutPort(new CABLES.Port(op, "index")); + +const vec = vec3.create(); +const vecn = vec3.create(); +const cgl = op.patch.cgl; + +const startTime = CABLES.now(); + +let animX = new CABLES.Anim(); +let animY = new CABLES.Anim(); +let animZ = new CABLES.Anim(); + +let animQX = new CABLES.Anim(); +let animQY = new CABLES.Anim(); +let animQZ = new CABLES.Anim(); +let animQW = new CABLES.Anim(); + +let animLength = 0; +let timeStep = 0.1; +function setup() +{ + animX = new CABLES.Anim(); + animY = new CABLES.Anim(); + animZ = new CABLES.Anim(); + + animQX = new CABLES.Anim(); + animQY = new CABLES.Anim(); + animQZ = new CABLES.Anim(); + animQW = new CABLES.Anim(); + + let i = 0; + const arr = arrayIn.get(); + if (!arr) return; + timeStep = parseFloat(duration.get()); + + for (i = 0; i < arr.length; i += 3) + { + animX.setValue(i / 3 * timeStep, arr[i + 0]); + animY.setValue(i / 3 * timeStep, arr[i + 1]); + animZ.setValue(i / 3 * timeStep, arr[i + 2]); + animLength = i / 3 * timeStep; + } + + for (i = 0; i < arr.length / 3; i++) + { + const t = i * timeStep; + const nt = (i * timeStep + timeStep) % animLength; + + vec3.set(vec, + animX.getValue(t), + animY.getValue(t), + animZ.getValue(t) + ); + vec3.set(vecn, + animX.getValue(nt), + animY.getValue(nt), + animZ.getValue(nt) + ); + + vec3.set(vec, vecn[0] - vec[0], vecn[1] - vec[1], vecn[2] - vec[2]); + vec3.normalize(vec, vec); + vec3.set(vecn, 0, 0, 1); + + quat.rotationTo(q, vecn, vec); + + animQX.setValue(i * timeStep, q[0]); + animQY.setValue(i * timeStep, q[1]); + animQZ.setValue(i * timeStep, q[2]); + animQW.setValue(i * timeStep, q[3]); + } +} + +arrayIn.onChange = duration.onChange = setup; + +let q = quat.create(); +const qMat = mat4.create(); + +function render() +{ + if (!arrayIn.get()) return; + + const t = (time.get() + parseFloat(offset.get())) % animLength; + const nt = (time.get() + timeStep * lookAhead.get() + parseFloat(offset.get())) % animLength; + + vec3.set(vec, + animX.getValue(t), + animY.getValue(t), + animZ.getValue(t) + ); + + idx.set(nt); + + if (triggerLookat.isLinked()) + { + vec3.set(vecn, + animX.getValue(nt), + animY.getValue(nt), + animZ.getValue(nt) + ); + + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, vecn); + triggerLookat.trigger(); + cgl.popModelMatrix(); + } + + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, vec); + + CABLES.TL.Anim.slerpQuaternion(t, q, animQX, animQY, animQZ, animQW); + mat4.fromQuat(qMat, q); + mat4.multiply(cgl.mMatrix, cgl.mMatrix, qMat); + + trigger.trigger(); + cgl.popModelMatrix(); +} + +exe.onTriggered = render; + + +}; + +Ops.Gl.Matrix.ArrayPathFollow.prototype = new CABLES.Op(); +CABLES.OPS["395bff14-d092-4e7e-a723-b6a69564add2"]={f:Ops.Gl.Matrix.ArrayPathFollow,objName:"Ops.Gl.Matrix.ArrayPathFollow"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.ArrayPathFollowParticles_v2 +// +// ************************************************************** + +Ops.Gl.Matrix.ArrayPathFollowParticles_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"pathfollow_vert":"\nfloat off=MOD_offset;\n\n#ifdef RANDOMSPEED\n off*=MOD_rand(pos.xy);\n#endif\n\nfloat fr=fract(abs(mod(off+rndOffset,float(PATHFOLLOW_POINTS))));\nint index=int(abs(mod(off+rndOffset,max(0.0,float(PATHFOLLOW_POINTS)))));\nint index2=int(abs(mod(off+1.0+rndOffset,max(0.0,float(PATHFOLLOW_POINTS)))));\n\nif(index2!=0)\n{\n pos.xyz = mix( MOD_pathPoints[index] ,MOD_pathPoints[index2] ,fr);\n\n #ifdef CHECK_DISTANCE\n if( distance(MOD_pathPoints[index] ,MOD_pathPoints[index2]) > MOD_maxDistance ) pos.xyz=vec3(9999999.0,9999999.0,9999999.0);\n #endif\n}\nelse\n{\n pos.xyz=MOD_pathPoints[0];\n}\n\npos.xyz+=rndPos;","pathfollow_head_vert":"UNI vec3 MOD_pathPoints[PATHFOLLOW_POINTS];\n\nIN vec3 rndPos;\nIN float rndOffset;\n\n\nfloat MOD_rand(vec2 co){\n return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n}\n",}; +const + exec = op.inTrigger("Exec"), + inPoints = op.inArray("Points"), + inParticles = op.inValue("Num Particles", 500), + inLength = op.inValue("Length", 20), + inSpread = op.inValue("Spread", 0.2), + inOffset = op.inValue("Offset"), + inMaxDistance = op.inValue("Max Distance", 0), + inRandomSpeed = op.inValueBool("RandomSpeed"), + next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; +let shaderModule = null; +let shader = null; +let mesh = null; +let needsRebuild = true; +let geom = null; +let updateUniformPoints = false; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": op.objName, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.pathfollow_head_vert, + "srcBodyVert": attachments.pathfollow_vert +}); + +mod.addUniform("f", "MOD_maxDistance", inMaxDistance); +mod.addUniform("f", "MOD_offset", inOffset); +mod.addUniform("3f[]", "MOD_pathPoints", inPoints); + +inParticles.onChange = + inLength.onChange = + inSpread.onChange = resetLater; + +inMaxDistance.onChange = updateDefines; + +function resetLater() +{ + needsRebuild = true; +} + +function getRandomVec(size) +{ + return [ + (Math.random() - 0.5) * 2 * size, + (Math.random() - 0.5) * 2 * size, + (Math.random() - 0.5) * 2 * size + ]; +} + +function rebuild() +{ + op.log("rebuild"); + + mesh = null; + needsRebuild = false; + let i = 0; + let verts = null; + const num = Math.abs(Math.floor(inParticles.get()) * 3); + if (!verts || verts.length != num) verts = new Float32Array(num); + + for (i = 0; i < verts.length; i += 3) + { + verts[i + 0] = (Math.random() - 0.5); + verts[i + 1] = (Math.random() - 0.5); + verts[i + 2] = (Math.random() - 0.5); + } + + if (!geom)geom = new CGL.Geometry(op.name); + geom.setPointVertices(verts); + + if (!mesh) + { + mesh = new CGL.Mesh(cgl, geom, cgl.gl.POINTS); + + mesh.addVertexNumbers = true; + mesh._verticesNumbers = null; + + op.log("NEW MESH"); + } + else + { + mesh.unBind(); + } + mesh.setGeom(geom); + + const rndArray = new Float32Array(num); + + let spread = inSpread.get(); + if (spread < 0)spread = 0; + + for (i = 0; i < num / 3; i++) + { + let v = getRandomVec(spread); + while (vec3.len(v) > spread / 2) v = getRandomVec(spread); + + rndArray[i * 3 + 0] = v[0]; + rndArray[i * 3 + 1] = v[1]; + rndArray[i * 3 + 2] = v[2]; + } + rndArray[i] = (Math.random() - 0.5) * spread; + + mesh.setAttribute("rndPos", rndArray, 3); + + // offset random + + var rndOffset = new Float32Array(num / 3); + for (i = 0; i < num / 3; i++) + rndOffset[i] = (Math.random()) * inLength.get(); + + mesh.setAttribute("rndOffset", rndOffset, 1); + + // speed random + + var rndOffset = new Float32Array(num / 3); + for (i = 0; i < num / 3; i++) + rndOffset[i] = (Math.random()) * inLength.get(); + + mesh.setAttribute("rndOffset", rndOffset, 1); + updateDefines(); +} + +mod.define("PATHFOLLOW_POINTS", 1); + +function updateDefines() +{ + mod.toggleDefine("CHECK_DISTANCE", inMaxDistance.get() != 0); + mod.toggleDefine("RANDOMSPEED", inRandomSpeed); +} + +exec.onTriggered = function () +{ + if (op.patch.isEditorMode()) + { + if (cgl.getShader().glPrimitive != cgl.gl.POINTS) op.setUiError("nopointmat", "Using a Material not made for point rendering. Try to use PointMaterial."); + else op.setUiError("nopointmat", null); + } + + if (!inPoints.get() || inPoints.get().length === 0) return; + if (needsRebuild)rebuild(); + + mod.bind(); + + if (inPoints.get()) + mod.define("PATHFOLLOW_POINTS", Math.floor(inPoints.get().length / 3)); + else mod.define("PATHFOLLOW_POINTS", 0); + + if (mesh) mesh.render(cgl.getShader()); + + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.Matrix.ArrayPathFollowParticles_v2.prototype = new CABLES.Op(); +CABLES.OPS["12a48cb2-a528-4498-8cda-18535af08f4a"]={f:Ops.Gl.Matrix.ArrayPathFollowParticles_v2,objName:"Ops.Gl.Matrix.ArrayPathFollowParticles_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.ArrayTranslate +// +// ************************************************************** + +Ops.Gl.Matrix.ArrayTranslate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + arrayIn = op.inArray("array"), + trigger = op.outTrigger("trigger"), + idx = op.addOutPort(new CABLES.Port(op, "index")); + +const cgl = op.patch.cgl; +let vec = vec3.create(); +exe.onTriggered = render; + +function render() +{ + if (!arrayIn.get()) return; + let arr = arrayIn.get(); + + for (let i = 0; i < arr.length; i += 3) + { + vec3.set(vec, arr[i], arr[i + 1], arr[i + 2]); + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, vec); + trigger.trigger(); + cgl.popModelMatrix(); + } +} + + +}; + +Ops.Gl.Matrix.ArrayTranslate.prototype = new CABLES.Op(); +CABLES.OPS["1371bc89-b4ac-4032-86e1-70ff2506665b"]={f:Ops.Gl.Matrix.ArrayTranslate,objName:"Ops.Gl.Matrix.ArrayTranslate"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.Billboard +// +// ************************************************************** + +Ops.Gl.Matrix.Billboard = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exec = op.inTrigger("Exec"); +const next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; + +let mm = mat4.create(); +let mv = mat4.create(); +let m = mat4.create(); +let mempty = mat4.create(); + +exec.onTriggered = function () +{ + mat4.invert(mm, cgl.mMatrix); + mat4.invert(mv, cgl.vMatrix); + + mat4.mul(mm, mm, mv); + + mm[12] = 0; + mm[13] = 0; + mm[14] = 0; + + cgl.pushModelMatrix(); + cgl.pushViewMatrix(); + mat4.mul(cgl.mMatrix, cgl.mMatrix, mm); + next.trigger(); + cgl.popViewMatrix(); + cgl.popModelMatrix(); +}; + + +}; + +Ops.Gl.Matrix.Billboard.prototype = new CABLES.Op(); +CABLES.OPS["d41e676e-d8a7-4a1e-8abf-f1bddfc982d5"]={f:Ops.Gl.Matrix.Billboard,objName:"Ops.Gl.Matrix.Billboard"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.Camera +// +// ************************************************************** + +Ops.Gl.Matrix.Camera = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const render = op.inTrigger("render"); +const trigger = op.outTrigger("trigger"); + +/* Inputs */ +// projection | prespective & ortogonal +const projectionMode = op.inValueSelect("projection mode", ["prespective", "ortogonal"], "prespective"); +const zNear = op.inValue("frustum near", 0.01); +const zFar = op.inValue("frustum far", 5000.0); + +const fov = op.inValue("fov", 45); +const autoAspect = op.inValueBool("Auto Aspect Ratio", true); +const aspect = op.inValue("Aspect Ratio", 1); + +// look at camera +const eyeX = op.inValue("eye X", 0); +const eyeY = op.inValue("eye Y", 0); +const eyeZ = op.inValue("eye Z", 5); + +const centerX = op.inValue("center X", 0); +const centerY = op.inValue("center Y", 0); +const centerZ = op.inValue("center Z", 0); + +// camera transform and movements +const posX = op.inValue("truck", 0); +const posY = op.inValue("boom", 0); +const posZ = op.inValue("dolly", 0); + +const rotX = op.inValue("tilt", 0); +const rotY = op.inValue("pan", 0); +const rotZ = op.inValue("roll", 0); + +/* Outputs */ +const outAsp = op.outNumber("Aspect"); +const outArr = op.outArray("Look At Array"); + +/* logic */ +const cgl = op.patch.cgl; + +let asp = 0; + +const vUp = vec3.create(); +const vEye = vec3.create(); +const vCenter = vec3.create(); +const transMatrix = mat4.create(); +mat4.identity(transMatrix); + +const arr = []; + +// Transform and move +const vPos = vec3.create(); +const transMatrixMove = mat4.create(); +mat4.identity(transMatrixMove); + +let updateCameraMovementMatrix = true; + +render.onTriggered = function () +{ + if (cgl.frameStore.shadowPass) return trigger.trigger(); + + // Aspect ration + if (!autoAspect.get()) asp = aspect.get(); + else asp = cgl.getViewPort()[2] / cgl.getViewPort()[3]; + outAsp.set(asp); + + // translation (truck, boom, dolly) + cgl.pushViewMatrix(); + + if (updateCameraMovementMatrix) + { + mat4.identity(transMatrixMove); + + vec3.set(vPos, posX.get(), posY.get(), posZ.get()); + if (posX.get() !== 0.0 || posY.get() !== 0.0 || posZ.get() !== 0.0) + mat4.translate(transMatrixMove, transMatrixMove, vPos); + + if (rotX.get() !== 0) + mat4.rotateX(transMatrixMove, transMatrixMove, rotX.get() * CGL.DEG2RAD); + if (rotY.get() !== 0) + mat4.rotateY(transMatrixMove, transMatrixMove, rotY.get() * CGL.DEG2RAD); + if (rotZ.get() !== 0) + mat4.rotateZ(transMatrixMove, transMatrixMove, rotZ.get() * CGL.DEG2RAD); + + updateCameraMovementMatrix = false; + } + + mat4.multiply(cgl.vMatrix, cgl.vMatrix, transMatrixMove); + + // projection (prespective / ortogonal) + cgl.pushPMatrix(); + + // look at + cgl.pushViewMatrix(); + + if (projectionMode.get() == "prespective") + { + mat4.perspective( + cgl.pMatrix, + fov.get() * 0.0174533, + asp, + zNear.get(), + zFar.get() + ); + } + else if (projectionMode.get() == "ortogonal") + { + mat4.ortho( + cgl.pMatrix, + -1 * (fov.get() / 14), + 1 * (fov.get() / 14), + -1 * (fov.get() / 14) / asp, + 1 * (fov.get() / 14) / asp, + zNear.get(), + zFar.get() + ); + } + + arr[0] = eyeX.get(); + arr[1] = eyeY.get(); + arr[2] = eyeZ.get(); + + arr[3] = centerX.get(); + arr[4] = centerY.get(); + arr[5] = centerZ.get(); + + arr[6] = 0; + arr[7] = 1; + arr[8] = 0; + + outArr.set(null); + outArr.set(arr); + + vec3.set(vUp, 0, 1, 0); + vec3.set(vEye, eyeX.get(), eyeY.get(), eyeZ.get()); + vec3.set(vCenter, centerX.get(), centerY.get(), centerZ.get()); + + mat4.lookAt(transMatrix, vEye, vCenter, vUp); + + mat4.multiply(cgl.vMatrix, cgl.vMatrix, transMatrix); + + trigger.trigger(); + + cgl.popViewMatrix(); + cgl.popPMatrix(); + + cgl.popViewMatrix(); + + // GUI for dolly, boom and truck + if (op.isCurrentUiOp()) + gui.setTransformGizmo({ + "posX": posX, + "posY": posY, + "posZ": posZ + }); +}; + +const updateUI = function () +{ + if (!autoAspect.get()) + { + aspect.setUiAttribs({ "greyout": false }); + } + else + { + aspect.setUiAttribs({ "greyout": true }); + } +}; + +const cameraMovementChanged = function () +{ + updateCameraMovementMatrix = true; +}; + +// listeners +posX.onChange = cameraMovementChanged; +posY.onChange = cameraMovementChanged; +posZ.onChange = cameraMovementChanged; + +rotX.onChange = cameraMovementChanged; +rotY.onChange = cameraMovementChanged; +rotZ.onChange = cameraMovementChanged; + +autoAspect.onChange = updateUI; +updateUI(); + + +}; + +Ops.Gl.Matrix.Camera.prototype = new CABLES.Op(); +CABLES.OPS["b24dbfdc-485c-49d2-92a1-7258efd9239a"]={f:Ops.Gl.Matrix.Camera,objName:"Ops.Gl.Matrix.Camera"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.CameraInfo +// +// ************************************************************** + +Ops.Gl.Matrix.CameraInfo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + cameraType = op.inSwitch("Camera Type", ["Perspective", "Orthographic"], "Perspective"), + trigger = op.outTrigger("trigger"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outZ = op.outNumber("Z"), + outRightX = op.outNumber("Right X"), + outRightY = op.outNumber("Right Y"), + outRightZ = op.outNumber("Right Z"), + outUpX = op.outNumber("Up X"), + outUpY = op.outNumber("Up Y"), + outUpZ = op.outNumber("Up Z"), + outForwardX = op.outNumber("Forward X"), + outForwardY = op.outNumber("Forward Y"), + outForwardZ = op.outNumber("Forward Z"), + outNear = op.outNumber("Near Frustum"), + outFar = op.outNumber("Far Frustum"), + outTop = op.outNumber("Bottom Frustum"), + outBottom = op.outNumber("Top Frustum"), + outLeft = op.outNumber("Left Frustum"), + outRight = op.outNumber("Right Frustum"), + outFov = op.outNumber("FOV"), + outAspect = op.outNumber("Aspect Ratio"); +const + cgl = op.patch.cgl, + pos = vec3.create(), + identVec = vec3.create(), + iViewMatrix = mat4.create(); +render.onTriggered = update; + +function update() +{ + mat4.invert(iViewMatrix, cgl.vMatrix); + + outRightX.set(iViewMatrix[0]); + outRightY.set(iViewMatrix[1]); + outRightZ.set(iViewMatrix[2]); + + outUpX.set(iViewMatrix[4]); + outUpY.set(iViewMatrix[5]); + outUpZ.set(iViewMatrix[6]); + + outForwardX.set(iViewMatrix[8]); + outForwardY.set(iViewMatrix[9]); + outForwardZ.set(iViewMatrix[10]); + + outX.set(iViewMatrix[12]); + outY.set(iViewMatrix[13]); + outZ.set(iViewMatrix[14]); + + // https://stackoverflow.com/questions/10830293/decompose-projection-matrix44-to-left-right-bottom-top-near-and-far-boundary/10836497#10836497 + const m11 = cgl.pMatrix[4 * 0 + 0]; + const m13 = cgl.pMatrix[4 * 2 + 0]; + const m14 = cgl.pMatrix[4 * 3 + 0]; + const m22 = cgl.pMatrix[4 * 1 + 1]; + const m23 = cgl.pMatrix[4 * 2 + 1]; + const m24 = cgl.pMatrix[4 * 3 + 1]; + const m33 = cgl.pMatrix[4 * 2 + 2]; + const m34 = cgl.pMatrix[4 * 3 + 2]; + + // https://stackoverflow.com/questions/46182845/field-of-view-aspect-ratio-view-matrix-from-projection-matrix-hmd-ost-calib + const FOV = 2 * Math.atan(1 / m22) * 180 / Math.PI; + const aspectRatio = m22 / m11; + + outFov.set(FOV); + outAspect.set(aspectRatio); + if (cameraType.get() === "Perspective") + { + const near = m34 / (m33 - 1); + const far = m34 / (m33 + 1); + const top = near * (m23 + 1) / m22; + const bottom = near * (m23 - 1) / m22; + const left = near * (m13 - 1) / m11; + const right = near * (m13 + 1) / m11; + + outNear.set(near); + outFar.set(far); + outTop.set(top); + outBottom.set(bottom); + outLeft.set(left); + outRight.set(right); + } + else if (cameraType.get() === "Orthographic") + { + const near = (1 + m34) / m33; + const far = -(1 - m34) / m33; + const bottom = near * (m23 - 1) / m22; + const top = near * (m23 + 1) / m22; + const left = near * (m13 - 1) / m11; + const right = near * (m13 + 1) / m11; + + outNear.set(near); + outFar.set(far); + outTop.set(top); + outBottom.set(bottom); + outLeft.set(left); + outRight.set(right); + } + + trigger.trigger(); +} + + +}; + +Ops.Gl.Matrix.CameraInfo.prototype = new CABLES.Op(); +CABLES.OPS["92a0c536-f2bf-4972-9f02-a9accf26c1df"]={f:Ops.Gl.Matrix.CameraInfo,objName:"Ops.Gl.Matrix.CameraInfo"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.CameraPosition +// +// ************************************************************** + +Ops.Gl.Matrix.CameraPosition = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outZ = op.outNumber("Z"); + +const + cgl = op.patch.cgl, + pos = vec3.create(), + identVec = vec3.create(), + iViewMatrix = mat4.create(); + +render.onTriggered = update; + +function update() +{ + mat4.invert(iViewMatrix, cgl.vMatrix); + vec3.transformMat4(pos, identVec, iViewMatrix); + + outX.set(pos[0]); + outY.set(pos[1]); + outZ.set(pos[2]); + + trigger.trigger(); +} + + +}; + +Ops.Gl.Matrix.CameraPosition.prototype = new CABLES.Op(); +CABLES.OPS["eff9a971-d9dd-4999-94b1-9a5cbba13581"]={f:Ops.Gl.Matrix.CameraPosition,objName:"Ops.Gl.Matrix.CameraPosition"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.Coordinates +// +// ************************************************************** + +Ops.Gl.Matrix.Coordinates = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outZ = op.outNumber("Z"), + pos = vec3.create(), + empty = vec3.create(); + +render.onTriggered = function () +{ + const cg = op.patch.cg; + + vec3.transformMat4(pos, empty, cg.mMatrix); + + outX.set(pos[0]); + outY.set(pos[1]); + outZ.set(pos[2]); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.Matrix.Coordinates.prototype = new CABLES.Op(); +CABLES.OPS["390d0214-92a9-48e9-85b4-f3092ee9e043"]={f:Ops.Gl.Matrix.Coordinates,objName:"Ops.Gl.Matrix.Coordinates"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.DeviceOrientationCamera +// +// ************************************************************** + +Ops.Gl.Matrix.DeviceOrientationCamera = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + next = op.outTrigger("Next"), + winOrient = op.outNumber("Window Orientation"), + cgl = op.patch.cgl, + vCenter = vec3.create(), + transMatrix = mat4.create(), + displayOrientQuat = quat.create(), + displayOrientMatrix = mat4.create(); + +window.addEventListener("deviceorientation", onOrientationChange, true); +let tempQuat = quat.create(); +mat4.identity(transMatrix); + +const viewDirQuat = quat.create(); + +render.onTriggered = function () +{ + // check for new permission request on IOS or android + if (DeviceMotionEvent && DeviceMotionEvent.requestPermission) + { + DeviceOrientationEvent.requestPermission() + .then((response) => + { + if (response == "granted") + { + window.addEventListener("deviceorientation", onOrientationChange, true); + } + }) + .catch(op.error); + } + else + { + window.addEventListener("deviceorientation", onOrientationChange, true); + } + + if (window.orientation === undefined) + { + next.trigger(); + return; + } + + cgl.pushViewMatrix(); + + tempQuat = quat.clone(viewDirQuat); + quat.invert(tempQuat, tempQuat); + + if (window.orientation == 90 || window.orientation == -90) + { + quat.setAxisAngle(displayOrientQuat, [0, 0, 1], window.orientation * CGL.DEG2RAD); + quat.multiply(tempQuat, displayOrientQuat, tempQuat); + } + + mat4.fromQuat(transMatrix, tempQuat); + // added rotateX by 90 as orientation was incorrect + mat4.rotateX(transMatrix, transMatrix, 90.0 * CGL.DEG2RAD); + mat4.rotateY(transMatrix, transMatrix, 90.0 * CGL.DEG2RAD); + mat4.multiply(cgl.vMatrix, cgl.vMatrix, transMatrix); + + // mat4.translate(cgl.vMatrix,cgl.vMatrix,[1,0,0]);//original code + + next.trigger(); + cgl.popViewMatrix(); +}; + +// frp, http://asterixcreative.com/blog/mobile-gyroscope-with-javascript-and-quaternions-programming-tutorial-part-1/ +function quatFromEuler(quat, alpha, beta, gamma) +{ + const x = CGL.DEG2RAD * beta; + const y = CGL.DEG2RAD * gamma; + const z = CGL.DEG2RAD * alpha; + + const cX = Math.cos(x / 2); + const cY = Math.cos(y / 2); + const cZ = Math.cos(z / 2); + const sX = Math.sin(x / 2); + const sY = Math.sin(y / 2); + const sZ = Math.sin(z / 2); + + quat[0] = sX * cY * cZ - cX * sY * sZ; + quat[1] = cX * sY * cZ + sX * cY * sZ; + quat[2] = cX * cY * sZ + sX * sY * cZ; + quat[3] = cX * cY * cZ - sX * sY * sZ; + + return quat; +} + +function onOrientationChange(event) +{ + const alpha = (event.alpha || 0); + const beta = (event.beta || 0); + const gamma = (event.gamma || 0); + + winOrient.set(window.orientation || 0); + quatFromEuler(viewDirQuat, alpha, beta, gamma); +} + + +}; + +Ops.Gl.Matrix.DeviceOrientationCamera.prototype = new CABLES.Op(); +CABLES.OPS["c213a5b4-6a47-425a-9457-ae0b14c88e9a"]={f:Ops.Gl.Matrix.DeviceOrientationCamera,objName:"Ops.Gl.Matrix.DeviceOrientationCamera"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.GetMatrixScaling +// +// ************************************************************** + +Ops.Gl.Matrix.GetMatrixScaling = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + inArr = op.inArray("Matrix"), + trigger = op.outTrigger("trigger"), + outX = op.outNumber("Scaling"); + +const cgl = op.patch.cgl; +const pos = vec3.create(); +const identVec = vec3.create(); +const iViewMatrix = mat4.create(); + +function getScaling(mat) +{ + let m31 = mat[8]; + let m32 = mat[9]; + let m33 = mat[10]; + + return Math.hypot(m31, m32, m33); +} + +render.onTriggered = function () +{ + if (!inArr.get()) return; + + outX.set(getScaling(cgl.mMatrix)); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.Matrix.GetMatrixScaling.prototype = new CABLES.Op(); +CABLES.OPS["3bcc8ae2-0837-4560-90dc-ae7836ee9db0"]={f:Ops.Gl.Matrix.GetMatrixScaling,objName:"Ops.Gl.Matrix.GetMatrixScaling"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.GetModelMatrix +// +// ************************************************************** + +Ops.Gl.Matrix.GetModelMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + matrix = op.outArray("matrix"); + +const m = mat4.create(); + +render.onTriggered = function () +{ + mat4.copy(m, op.patch.cg.mMatrix); + matrix.set(null); + matrix.set(m); + trigger.trigger(); +}; + +matrix.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + + +}; + +Ops.Gl.Matrix.GetModelMatrix.prototype = new CABLES.Op(); +CABLES.OPS["2d96f0a3-979a-4dc3-8c49-848f08efe0e0"]={f:Ops.Gl.Matrix.GetModelMatrix,objName:"Ops.Gl.Matrix.GetModelMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.GetProjectonMatrix +// +// ************************************************************** + +Ops.Gl.Matrix.GetProjectonMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + cgl = op.patch.cgl, + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + matrix = op.outArray("matrix", null, 16); + +let m = mat4.create(); +mat4.identity(m); +matrix.set(m); + +render.onTriggered = function () +{ + mat4.copy(m, cgl.pMatrix); + matrix.set(null); + matrix.set(m); + trigger.trigger(); +}; + + +}; + +Ops.Gl.Matrix.GetProjectonMatrix.prototype = new CABLES.Op(); +CABLES.OPS["3f048006-89e9-4e3f-a7d3-42aaa77ed630"]={f:Ops.Gl.Matrix.GetProjectonMatrix,objName:"Ops.Gl.Matrix.GetProjectonMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.GetViewMatrix +// +// ************************************************************** + +Ops.Gl.Matrix.GetViewMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let render = op.inTrigger("render"); +let trigger = op.outTrigger("trigger"); +let matrix = op.addOutPort(new CABLES.Port(op, "matrix", CABLES.OP_PORT_TYPE_ARRAY)); + +let m = mat4.create(); + +render.onTriggered = function () +{ + mat4.copy(m, op.patch.cg.vMatrix); + matrix.set(null); + matrix.set(m); + trigger.trigger(); +}; + +matrix.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + + +}; + +Ops.Gl.Matrix.GetViewMatrix.prototype = new CABLES.Op(); +CABLES.OPS["a58406ae-79b5-4dab-99fa-0a66c2325aab"]={f:Ops.Gl.Matrix.GetViewMatrix,objName:"Ops.Gl.Matrix.GetViewMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.InterpolateMatrix +// +// ************************************************************** + +Ops.Gl.Matrix.InterpolateMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTrigger("Exe"); +const inArr1 = op.inArray("Array 1"); +const inArr2 = op.inArray("Array 2"); +const inPerc = op.inValueSlider("perc"); +const next = op.outTrigger("Next"); +const outArr = op.outArray("Result"); + +const result = mat4.create(); + +function ip(val1, val2, perc) +{ + return ((val2 - val1) * perc + val1); +} + +const a = vec3.create(); +const b = vec3.create(); + +exe.onTriggered = function () +{ + const arr1 = inArr1.get(); + const arr2 = inArr2.get(); + + if (!arr1 || !arr2 || arr1.length != arr2.length) + { + outArr.set(null); + op.logError("arrays wrong"); + } + else + { + getYPR(a, arr1); + getYPR(b, arr2); + + const perc = inPerc.get(); + + mat4.identity(result); + result[12] = ip(arr1[12], arr2[12], perc); + result[13] = ip(arr1[13], arr2[13], perc); + result[14] = ip(arr1[14], arr2[14], perc); + + vec3.lerp(a, a, b, perc); + + mat4.rotateZ(result, result, a[2]); + mat4.rotateY(result, result, a[1]); + mat4.rotateX(result, result, a[0]); + + outArr.set(null); + outArr.set(result); + } + next.trigger(); +}; + +// 0 1 2 3 +// 4 5 6 7 +// 8 9 10 11 +// 12 13 14 15 + +function getYPR(v, m) +{ + const r1 = Math.atan2(m[6], m[10]); + const c2 = Math.sqrt(m[0] * m[0] + m[1] * m[1]); + const r2 = Math.atan2(-m[2], c2); + const s1 = Math.sin(r1); + const c1 = Math.cos(r1); + const r3 = Math.atan2(s1 * m[8] - c1 * m[4], c1 * m[5] - s1 * m[9]); + + v[0] = r1; + v[1] = r2; + v[2] = r3; + return v; +} + + +}; + +Ops.Gl.Matrix.InterpolateMatrix.prototype = new CABLES.Op(); +CABLES.OPS["84e1908b-fc17-47db-9ba1-bc38d7809076"]={f:Ops.Gl.Matrix.InterpolateMatrix,objName:"Ops.Gl.Matrix.InterpolateMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.InvertMatrix +// +// ************************************************************** + +Ops.Gl.Matrix.InvertMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inMat = op.inArray("Matrix"), + outMat = op.outArray("Result"); + +const m = mat4.create(); + +inMat.onChange = () => +{ + if (!inMat.get()) return; + mat4.invert(m, inMat.get()); + outMat.set(null); + outMat.set(m); +}; + + +}; + +Ops.Gl.Matrix.InvertMatrix.prototype = new CABLES.Op(); +CABLES.OPS["2ebd0d87-f67a-4101-bdf0-283c610b095a"]={f:Ops.Gl.Matrix.InvertMatrix,objName:"Ops.Gl.Matrix.InvertMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.LookatCamera +// +// ************************************************************** + +Ops.Gl.Matrix.LookatCamera = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const render = op.inTrigger("render"); +const trigger = op.outTrigger("trigger"); + +const eyeX = op.inValueFloat("eyeX"); +const eyeY = op.inValueFloat("eyeY"); +const eyeZ = op.inValueFloat("eyeZ"); + +const centerX = op.inValueFloat("centerX"); +const centerY = op.inValueFloat("centerY"); +const centerZ = op.inValueFloat("centerZ"); + +const vecUpX = op.inValueFloat("upX"); +const vecUpY = op.inValueFloat("upY"); +const vecUpZ = op.inValueFloat("upZ"); + +const outArr = op.outArray("Array"); + +centerX.set(0); +centerY.set(0); +centerZ.set(0); + +eyeX.set(5); +eyeY.set(5); +eyeZ.set(5); + +vecUpX.set(0); +vecUpY.set(1); +vecUpZ.set(0); + +const cgl = op.patch.cgl; +const vUp = vec3.create(); +const vEye = vec3.create(); +const vCenter = vec3.create(); +const transMatrix = mat4.create(); +mat4.identity(transMatrix); + +const arr = []; + + +render.onTriggered = function () +{ + if (cgl.frameStore.shadowPass) return trigger.trigger(); + + + if (op.isCurrentUiOp()) + gui.setTransformGizmo( + { + "posX": eyeX, + "posY": eyeY, + "posZ": eyeZ + }); + + + cgl.pushViewMatrix(); + + arr[0] = eyeX.get(); + arr[1] = eyeY.get(); + arr[2] = eyeZ.get(); + + arr[3] = centerX.get(); + arr[4] = centerY.get(); + arr[5] = centerZ.get(); + + arr[6] = vecUpX.get(); + arr[7] = vecUpY.get(); + arr[8] = vecUpZ.get(); + outArr.set(null); + outArr.set(arr); + + vec3.set(vUp, vecUpX.get(), vecUpY.get(), vecUpZ.get()); + vec3.set(vEye, eyeX.get(), eyeY.get(), eyeZ.get()); + vec3.set(vCenter, centerX.get(), centerY.get(), centerZ.get()); + + mat4.lookAt(transMatrix, vEye, vCenter, vUp); + + mat4.multiply(cgl.vMatrix, cgl.vMatrix, transMatrix); + + trigger.trigger(); + cgl.popViewMatrix(); +}; + + +}; + +Ops.Gl.Matrix.LookatCamera.prototype = new CABLES.Op(); +CABLES.OPS["e0f1bad6-7dfb-4665-b458-ca50a1bfe7fa"]={f:Ops.Gl.Matrix.LookatCamera,objName:"Ops.Gl.Matrix.LookatCamera"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.MatrixTranslation +// +// ************************************************************** + +Ops.Gl.Matrix.MatrixTranslation = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + inArr = op.inArray("Matrix"), + trigger = op.outTrigger("trigger"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outZ = op.outNumber("Z"); + +const cgl = op.patch.cgl; +const pos = vec3.create(); +const identVec = vec3.create(); +const iViewMatrix = mat4.create(); + +render.onTriggered = function () +{ + if (!inArr.get()) return; + + mat4.invert(iViewMatrix, inArr.get()); + vec3.transformMat4(pos, identVec, iViewMatrix); + + outX.set(pos[0]); + outY.set(pos[1]); + outZ.set(pos[2]); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.Matrix.MatrixTranslation.prototype = new CABLES.Op(); +CABLES.OPS["d5ba91a4-d419-4805-b37f-595c261615b3"]={f:Ops.Gl.Matrix.MatrixTranslation,objName:"Ops.Gl.Matrix.MatrixTranslation"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.MulViewMatrix +// +// ************************************************************** + +Ops.Gl.Matrix.MulViewMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + matrix = op.inArray("matrix"), + inIdentity = op.inValueBool("Identity", false), + trigger = op.outTrigger("trigger"); + +const m = mat4.create(); +const cgl = this.patch.cgl; + +matrix.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + +render.onTriggered = function () +{ + cgl.pushViewMatrix(); + + if (matrix.get()) + { + if (inIdentity.get()) mat4.identity(cgl.vMatrix); + + mat4.multiply(cgl.vMatrix, cgl.vMatrix, matrix.get()); + } + + trigger.trigger(); + cgl.popViewMatrix(); +}; + + +}; + +Ops.Gl.Matrix.MulViewMatrix.prototype = new CABLES.Op(); +CABLES.OPS["c2833486-717e-4f3c-9dd8-c62cc92ea46e"]={f:Ops.Gl.Matrix.MulViewMatrix,objName:"Ops.Gl.Matrix.MulViewMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.MultiplyModelMatrix +// +// ************************************************************** + +Ops.Gl.Matrix.MultiplyModelMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let cgl = op.patch.cgl; +let render = op.inTrigger("render"); +let inIdentity = op.inValueBool("Identity", false); +let next = op.outTrigger("trigger"); + +let m = mat4.create(); +let matrix = op.inArray("matrix"); + +render.onTriggered = function () +{ + cgl.pushModelMatrix(); + + if (inIdentity.get()) mat4.identity(cgl.mMatrix); + + const m = matrix.get(); + + if (m) mat4.multiply(cgl.mMatrix, cgl.mMatrix, m); + + next.trigger(); + cgl.popModelMatrix(); +}; + +matrix.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + + +}; + +Ops.Gl.Matrix.MultiplyModelMatrix.prototype = new CABLES.Op(); +CABLES.OPS["ec2784ab-9174-4052-894b-744a18e0d348"]={f:Ops.Gl.Matrix.MultiplyModelMatrix,objName:"Ops.Gl.Matrix.MultiplyModelMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.OrbitControls +// +// ************************************************************** + +Ops.Gl.Matrix.OrbitControls = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + minDist = op.inValueFloat("min distance"), + maxDist = op.inValueFloat("max distance"), + + minRotY = op.inValue("min rot y", 0), + maxRotY = op.inValue("max rot y", 0), + + initialRadius = op.inValue("initial radius", 0), + initialAxis = op.inValueSlider("initial axis y"), + initialX = op.inValueSlider("initial axis x"), + + mul = op.inValueFloat("mul"), + smoothness = op.inValueSlider("Smoothness", 1.0), + speedX = op.inValue("Speed X", 1), + speedY = op.inValue("Speed Y", 1), + + active = op.inValueBool("Active", true), + + allowPanning = op.inValueBool("Allow Panning", true), + allowZooming = op.inValueBool("Allow Zooming", true), + allowRotation = op.inValueBool("Allow Rotation", true), + restricted = op.inValueBool("restricted", true), + + trigger = op.outTrigger("trigger"), + outRadius = op.outNumber("radius"), + outXDeg = op.outNumber("Rot X"), + outYDeg = op.outNumber("Rot Y"), + + inReset = op.inTriggerButton("Reset"); + +op.setPortGroup("Initial Values", [initialAxis, initialX, initialRadius]); +op.setPortGroup("Interaction", [mul, smoothness, speedX, speedY]); +op.setPortGroup("Boundaries", [minRotY, maxRotY, minDist, maxDist]); + +mul.set(1); +minDist.set(0.05); +maxDist.set(99999); + +inReset.onTriggered = reset; + +let eye = vec3.create(); +const vUp = vec3.create(); +const vCenter = vec3.create(); +const viewMatrix = mat4.create(); +const tempViewMatrix = mat4.create(); +const vOffset = vec3.create(); +const finalEyeAbs = vec3.create(); + +initialAxis.set(0.5); + +let mouseDown = false; +let radius = 5; +outRadius.set(radius); + +let lastMouseX = 0, lastMouseY = 0; +let percX = 0, percY = 0; + +vec3.set(vCenter, 0, 0, 0); +vec3.set(vUp, 0, 1, 0); + +const tempEye = vec3.create(); +const finalEye = vec3.create(); +const tempCenter = vec3.create(); +const finalCenter = vec3.create(); + +let px = 0; +let py = 0; + +let divisor = 1; +let element = null; +updateSmoothness(); + +op.onDelete = unbind; + +const halfCircle = Math.PI; +const fullCircle = Math.PI * 2; + +function reset() +{ + let off = 0; + + if (px % fullCircle < -halfCircle) + { + off = -fullCircle; + px %= -fullCircle; + } + else + if (px % fullCircle > halfCircle) + { + off = fullCircle; + px %= fullCircle; + } + else px %= fullCircle; + + py %= (Math.PI); + + vec3.set(vOffset, 0, 0, 0); + vec3.set(vCenter, 0, 0, 0); + vec3.set(vUp, 0, 1, 0); + + percX = (initialX.get() * Math.PI * 2 + off); + percY = (initialAxis.get() - 0.5); + + radius = initialRadius.get(); + eye = circlePos(percY); +} + +function updateSmoothness() +{ + divisor = smoothness.get() * 10 + 1.0; +} + +smoothness.onChange = updateSmoothness; + +let initializing = true; + +function ip(val, goal) +{ + if (initializing) return goal; + return val + (goal - val) / divisor; +} + +let lastPy = 0; +const lastPx = 0; + +render.onTriggered = function () +{ + const cgl = op.patch.cg; + + if (!element) + { + setElement(cgl.canvas); + bind(); + } + + cgl.pushViewMatrix(); + + px = ip(px, percX); + py = ip(py, percY); + + let degY = (py + 0.5) * 180; + + if (minRotY.get() !== 0 && degY < minRotY.get()) + { + degY = minRotY.get(); + py = lastPy; + } + else if (maxRotY.get() !== 0 && degY > maxRotY.get()) + { + degY = maxRotY.get(); + py = lastPy; + } + else + { + lastPy = py; + } + + const degX = (px) * CGL.RAD2DEG; + + outYDeg.set(degY); + outXDeg.set(degX); + + circlePosi(eye, py); + + vec3.add(tempEye, eye, vOffset); + vec3.add(tempCenter, vCenter, vOffset); + + finalEye[0] = ip(finalEye[0], tempEye[0]); + finalEye[1] = ip(finalEye[1], tempEye[1]); + finalEye[2] = ip(finalEye[2], tempEye[2]); + + finalCenter[0] = ip(finalCenter[0], tempCenter[0]); + finalCenter[1] = ip(finalCenter[1], tempCenter[1]); + finalCenter[2] = ip(finalCenter[2], tempCenter[2]); + + const empty = vec3.create(); + + mat4.lookAt(viewMatrix, finalEye, finalCenter, vUp); + mat4.rotate(viewMatrix, viewMatrix, px, vUp); + + // finaly multiply current scene viewmatrix + mat4.multiply(cgl.vMatrix, cgl.vMatrix, viewMatrix); + + trigger.trigger(); + cgl.popViewMatrix(); + initializing = false; +}; + +function circlePosi(vec, perc) +{ + const mmul = mul.get(); + if (radius < minDist.get() * mmul) radius = minDist.get() * mmul; + if (radius > maxDist.get() * mmul) radius = maxDist.get() * mmul; + + outRadius.set(radius * mmul); + + let i = 0, degInRad = 0; + + degInRad = 360 * perc / 2 * CGL.DEG2RAD; + vec3.set(vec, + Math.cos(degInRad) * radius * mmul, + Math.sin(degInRad) * radius * mmul, + 0); + return vec; +} + +function circlePos(perc) +{ + const mmul = mul.get(); + if (radius < minDist.get() * mmul)radius = minDist.get() * mmul; + if (radius > maxDist.get() * mmul)radius = maxDist.get() * mmul; + + outRadius.set(radius * mmul); + + let i = 0, degInRad = 0; + const vec = vec3.create(); + degInRad = 360 * perc / 2 * CGL.DEG2RAD; + vec3.set(vec, + Math.cos(degInRad) * radius * mmul, + Math.sin(degInRad) * radius * mmul, + 0); + return vec; +} + +function onmousemove(event) +{ + if (!mouseDown) return; + + const x = event.clientX; + const y = event.clientY; + + let movementX = (x - lastMouseX); + let movementY = (y - lastMouseY); + + movementX *= speedX.get(); + movementY *= speedY.get(); + + if (event.buttons == 2 && allowPanning.get()) + { + vOffset[2] += movementX * 0.01 * mul.get(); + vOffset[1] += movementY * 0.01 * mul.get(); + } + else + if (event.buttons == 4 && allowZooming.get()) + { + radius += movementY * 0.05; + eye = circlePos(percY); + } + else + { + if (allowRotation.get()) + { + percX += movementX * 0.003; + percY += movementY * 0.002; + + if (restricted.get()) + { + if (percY > 0.5)percY = 0.5; + if (percY < -0.5)percY = -0.5; + } + } + } + + lastMouseX = x; + lastMouseY = y; +} + +function onMouseDown(event) +{ + lastMouseX = event.clientX; + lastMouseY = event.clientY; + mouseDown = true; + + try { element.setPointerCapture(event.pointerId); } + catch (e) {} +} + +function onMouseUp(e) +{ + mouseDown = false; + // cgl.canvas.style.cursor='url(/ui/img/rotate.png),pointer'; + + try { element.releasePointerCapture(e.pointerId); } + catch (e) {} +} + +function lockChange() +{ + const el = op.patch.cg.canvas; + + if (document.pointerLockElement === el || document.mozPointerLockElement === el || document.webkitPointerLockElement === el) + { + document.addEventListener("mousemove", onmousemove, false); + } +} + +function onMouseEnter(e) +{ + // cgl.canvas.style.cursor='url(/ui/img/rotate.png),pointer'; +} + +initialRadius.onChange = function () +{ + radius = initialRadius.get(); + reset(); +}; + +initialX.onChange = function () +{ + px = percX = (initialX.get() * Math.PI * 2); +}; + +initialAxis.onChange = function () +{ + py = percY = (initialAxis.get() - 0.5); + eye = circlePos(percY); +}; + +const onMouseWheel = function (event) +{ + if (allowZooming.get()) + { + const delta = CGL.getWheelSpeed(event) * 0.06; + radius += (parseFloat(delta)) * 1.2; + + eye = circlePos(percY); + } +}; + +const ontouchstart = function (event) +{ + if (event.touches && event.touches.length > 0) onMouseDown(event.touches[0]); +}; + +const ontouchend = function (event) +{ + onMouseUp(); +}; + +const ontouchmove = function (event) +{ + if (event.touches && event.touches.length > 0) onmousemove(event.touches[0]); +}; + +active.onChange = function () +{ + if (active.get())bind(); + else unbind(); +}; + +function setElement(ele) +{ + unbind(); + element = ele; + bind(); +} + +function bind() +{ + if (!element) return; + + element.addEventListener("pointermove", onmousemove); + element.addEventListener("pointerdown", onMouseDown); + element.addEventListener("pointerup", onMouseUp); + element.addEventListener("pointerleave", onMouseUp); + element.addEventListener("pointerenter", onMouseEnter); + element.addEventListener("contextmenu", function (e) { e.preventDefault(); }); + element.addEventListener("wheel", onMouseWheel, { "passive": true }); +} + +function unbind() +{ + if (!element) return; + + element.removeEventListener("pointermove", onmousemove); + element.removeEventListener("pointerdown", onMouseDown); + element.removeEventListener("pointerup", onMouseUp); + element.removeEventListener("pointerleave", onMouseUp); + element.removeEventListener("pointerenter", onMouseUp); + element.removeEventListener("wheel", onMouseWheel); +} + +eye = circlePos(0); + +initialX.set(0.25); +initialRadius.set(0.05); + + +}; + +Ops.Gl.Matrix.OrbitControls.prototype = new CABLES.Op(); +CABLES.OPS["eaf4f7ce-08a3-4d1b-b9f4-ebc0b7b1cde1"]={f:Ops.Gl.Matrix.OrbitControls,objName:"Ops.Gl.Matrix.OrbitControls"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.Quaternion +// +// ************************************************************** + +Ops.Gl.Matrix.Quaternion = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + x = op.inValueFloat("x"), + y = op.inValueFloat("y"), + z = op.inValueFloat("z"), + w = op.inValueFloat("w"); + +let q = quat.create(); +let qMat = mat4.create(); +let cgl = op.patch.cgl; +render.onTriggered = function () +{ + if (x.isAnimated()) + { + let time = op.patch.timer.getTime(); + CABLES.TL.Anim.slerpQuaternion(time, q, x.anim, y.anim, z.anim, w.anim); + } + else + { + quat.set(q, x.get(), y.get(), z.get(), w.get()); + } + cgl.pushModelMatrix(); + + mat4.fromQuat(qMat, q); + mat4.multiply(cgl.mMatrix, cgl.mMatrix, qMat); + + trigger.trigger(); + cgl.popModelMatrix(); +}; + + +}; + +Ops.Gl.Matrix.Quaternion.prototype = new CABLES.Op(); +CABLES.OPS["e256892f-7f68-4113-9eb8-cd981026ea8b"]={f:Ops.Gl.Matrix.Quaternion,objName:"Ops.Gl.Matrix.Quaternion"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.QuaternionCamera +// +// ************************************************************** + +Ops.Gl.Matrix.QuaternionCamera = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + + eyeX = op.inValueFloat("eyeX", 5), + eyeY = op.inValueFloat("eyeY", 5), + eyeZ = op.inValueFloat("eyeZ", 5), + + quatX = op.inValueFloat("quatX", 0), + quatY = op.inValueFloat("quatY", 0), + quatZ = op.inValueFloat("quatZ", 0), + quatW = op.inValueFloat("quatW", 0), + + vecUpX = op.inValueFloat("upX", 0), + vecUpY = op.inValueFloat("upY", 1), + vecUpZ = op.inValueFloat("upZ", 0); + +const cgl = op.patch.cgl; +const vUp = vec3.create(); +const vEye = vec3.create(); +const vCenter = vec3.create(); +const vQuat = quat.create(); +const transMatrix = mat4.create(); +mat4.identity(transMatrix); + + +// arr[i*3+0]=n._animTrans[0].getValue(t); + +// arrRot[i*4+3]=n._animRot[3].getValue(t); + + +const arr = []; + +render.onTriggered = function () +{ + if (cgl.frameStore.shadowPass) return trigger.trigger(); + + if (op.isCurrentUiOp()) + gui.setTransformGizmo( + { + "posX": eyeX, + "posY": eyeY, + "posZ": eyeZ + }); + + cgl.pushViewMatrix(); + + quat.set(vQuat, quatX.get(), quatY.get(), quatZ.get(), quatW.get()); + + vec3.set(vUp, vecUpX.get(), vecUpY.get(), vecUpZ.get()); + vec3.set(vEye, eyeX.get(), eyeY.get(), eyeZ.get()); + + vec3.set(vCenter, 0, -1, 0); + vec3.transformQuat(vCenter, vCenter, vQuat); + vec3.normalize(vCenter, vCenter); + vec3.add(vCenter, vCenter, vEye); + + mat4.lookAt(transMatrix, vEye, vCenter, vUp); + + mat4.multiply(cgl.vMatrix, cgl.vMatrix, transMatrix); + + trigger.trigger(); + cgl.popViewMatrix(); +}; + + +}; + +Ops.Gl.Matrix.QuaternionCamera.prototype = new CABLES.Op(); +CABLES.OPS["7141c048-615e-47b5-9d8b-d0ceaad9bd79"]={f:Ops.Gl.Matrix.QuaternionCamera,objName:"Ops.Gl.Matrix.QuaternionCamera"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.RandomGridPlacement +// +// ************************************************************** + +Ops.Gl.Matrix.RandomGridPlacement = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("Exe"), + maxDepth = op.inValue("max Depth", 4), + deeper = op.inValueSlider("Possibility"), + seed = op.inValue("Seed", 1), + inScale = op.inValueSlider("Scale", 1), + width = op.inValue("Width", 4), + height = op.inValue("Height", 3), + next = op.outTrigger("Next"), + outIndex = op.outNumber("Index"), + outDepth = op.outNumber("depth"); + +const cgl = op.patch.cgl; +let globalScale = 1; +let vPos = vec3.create(); +let vScale = vec3.create(); +let hhalf = 0; +let whalf = 0; +let index = 0; + +function drawSquare(x, y, depth, scale) +{ + let godeeper = Math.seededRandom() > deeper.get() * 0.9; + + if (depth > maxDepth.get())godeeper = false; + + if (godeeper) + { + depth++; + let st = 1 / (depth * depth); + + for (let _x = 0; _x < 2; _x++) + { + for (let _y = 0; _y < 2; _y++) + { + let xx = _x * scale / 2; + let yy = _y * scale / 2; + + drawSquare( + x + xx - scale / 4, + y + yy - scale / 4, + depth, + scale / 2 * globalScale); + } + } + } + else + { + cgl.pushModelMatrix(); + vec3.set(vScale, scale, scale, scale); + vec3.set(vPos, x - whalf + 0.5, y - hhalf + 0.5, 0); + index++; + outIndex.set(index); + outDepth.set(depth); + + mat4.translate(cgl.mMatrix, cgl.mMatrix, vPos); + mat4.scale(cgl.mMatrix, cgl.mMatrix, vScale); + next.trigger(); + + cgl.popModelMatrix(); + } +} + +exe.onTriggered = function () +{ + index = 0; + Math.randomSeed = seed.get(); + + whalf = width.get() / 2; + hhalf = height.get() / 2; + + globalScale = inScale.get(); + + for (let x = 0; x < width.get(); x++) + { + for (let y = 0; y < height.get(); y++) + { + drawSquare(x, y, 0, globalScale); + let sc = 1; + } + } +}; + + +}; + +Ops.Gl.Matrix.RandomGridPlacement.prototype = new CABLES.Op(); +CABLES.OPS["3353effa-851f-4c0f-bace-ba5e2e82ae49"]={f:Ops.Gl.Matrix.RandomGridPlacement,objName:"Ops.Gl.Matrix.RandomGridPlacement"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.RandomGridPlacementArrays +// +// ************************************************************** + +Ops.Gl.Matrix.RandomGridPlacementArrays = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("Exe"), + maxDepth = op.inValue("max Depth", 4), + deeper = op.inValueSlider("Possibility", 0.5), + seed = op.inValue("Seed", 1), + inScale = op.inValueSlider("Scale", 1), + width = op.inValue("Width", 4), + height = op.inValue("Height", 3), + outPosArr = op.outArray("Positions"), + outScaleArr = op.outArray("Scalings"), + outArrayLength = op.outNumber("Array Length"), + outArrayPoints = op.outNumber("Total Points"); + +const cgl = op.patch.cgl; +let needsChange = true; +let globalScale = 1; +let vPos = vec3.create(); +let vScale = vec3.create(); +let hhalf = 0; +let whalf = 0; +let index = 0; +let arrPos = []; +let arrScale = []; + +let count = 0; + +maxDepth.onChange = + deeper.onChange = + seed.onChange = + inScale.onChange = + width.onChange = + height.onChange = + function () + { + needsChange = true; + }; + +function drawSquare(x, y, depth, scale) +{ + let godeeper = Math.seededRandom() > deeper.get() * 0.9; + + if (depth > maxDepth.get())godeeper = false; + + if (godeeper) + { + depth++; + let st = 1 / (depth * depth); + + for (let _x = 0; _x < 2; _x++) + { + for (let _y = 0; _y < 2; _y++) + { + let xx = _x * scale / 2; + let yy = _y * scale / 2; + + drawSquare( + x + xx - scale / 4, + y + yy - scale / 4, + depth, + scale / 2 * globalScale); + } + } + } + else + { + vec3.set(vScale, scale, scale, scale); + vec3.set(vPos, x - whalf + 0.5, y - hhalf + 0.5, 0); + index++; + + arrPos[count * 3 + 0] = vPos[0]; + arrPos[count * 3 + 1] = vPos[1]; + arrPos[count * 3 + 2] = vPos[2]; + + arrScale[count * 3 + 0] = vScale[0]; + arrScale[count * 3 + 1] = vScale[1]; + arrScale[count * 3 + 2] = vScale[2]; + + count++; + } +} + +exe.onTriggered = function () +{ + if (!needsChange) return; + + needsChange = false; + index = 0; + Math.randomSeed = seed.get(); + + whalf = width.get() / 2; + hhalf = height.get() / 2; + + globalScale = inScale.get(); + + arrPos.length = 0; + arrScale.length = 0; + count = 0; + + for (let x = 0; x < width.get(); x++) + { + for (let y = 0; y < height.get(); y++) + { + drawSquare(x, y, 0, globalScale); + let sc = 1; + } + } + + outArrayLength.set(arrPos.length); + outArrayPoints.set(arrPos.length / 3); + + outPosArr.set(null); + outPosArr.set(arrPos); + outScaleArr.set(null); + outScaleArr.set(arrScale); +}; + + +}; + +Ops.Gl.Matrix.RandomGridPlacementArrays.prototype = new CABLES.Op(); +CABLES.OPS["81f910ec-a11c-4bee-bd63-8ae036632d7d"]={f:Ops.Gl.Matrix.RandomGridPlacementArrays,objName:"Ops.Gl.Matrix.RandomGridPlacementArrays"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.Scale +// +// ************************************************************** + +Ops.Gl.Matrix.Scale = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + scale = op.inValueFloat("scale", 1.0), + trigger = op.outTrigger("trigger"); + +const vScale = vec3.create(); + +scale.onChange = scaleChanged; +scaleChanged(); + +render.onTriggered = function () +{ + const cgl = op.patch.cg; + cgl.pushModelMatrix(); + mat4.scale(cgl.mMatrix, cgl.mMatrix, vScale); + trigger.trigger(); + cgl.popModelMatrix(); +}; + +function scaleChanged() +{ + const s = scale.get(); + vec3.set(vScale, s, s, s); +} + + +}; + +Ops.Gl.Matrix.Scale.prototype = new CABLES.Op(); +CABLES.OPS["50e7f565-0cdb-47ca-912b-87c04e2f00e3"]={f:Ops.Gl.Matrix.Scale,objName:"Ops.Gl.Matrix.Scale"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.ScaleXYZ +// +// ************************************************************** + +Ops.Gl.Matrix.ScaleXYZ = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + scaleX = op.inValueFloat("x", 1), + scaleY = op.inValueFloat("y", 1), + scaleZ = op.inValueFloat("z", 1), + trigger = op.outTrigger("trigger"); + +const vScale = vec3.create(); + +let hasChanged = true; + +scaleX.onChange = scaleY.onChange = scaleZ.onChange = scaleChanged; + +scaleChanged(); + +render.onTriggered = execrender; + +function execrender() +{ + const cgl = op.patch.cg || op.patch.cgl; + + if (hasChanged) + { + vec3.set(vScale, scaleX.get(), scaleY.get(), scaleZ.get()); + hasChanged = false; + } + + cgl.pushModelMatrix(); + mat4.scale(cgl.mMatrix, cgl.mMatrix, vScale); + trigger.trigger(); + cgl.popModelMatrix(); +} + +function scaleChanged() +{ + hasChanged = true; +} + + +}; + +Ops.Gl.Matrix.ScaleXYZ.prototype = new CABLES.Op(); +CABLES.OPS["9ba52457-5f0d-4b20-a97c-4ec4856b8e29"]={f:Ops.Gl.Matrix.ScaleXYZ,objName:"Ops.Gl.Matrix.ScaleXYZ"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.ScaleXYZViewMatrix +// +// ************************************************************** + +Ops.Gl.Matrix.ScaleXYZViewMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + scaleX = op.inValueFloat("x", 1), + scaleY = op.inValueFloat("y", 1), + scaleZ = op.inValueFloat("z", 1), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +let vScale = vec3.create(); +let transMatrix = mat4.create(); +mat4.identity(transMatrix); + +scaleX.onChange = scaleY.onChange = scaleZ.onChange = scaleChanged; +scaleChanged(); + +render.onTriggered = exec; + +function exec() +{ + cgl.pushViewMatrix(); + mat4.multiply(cgl.vMatrix, cgl.vMatrix, transMatrix); + trigger.trigger(); + cgl.popViewMatrix(); +} + +function scaleChanged() +{ + vec3.set(vScale, scaleX.get(), scaleY.get(), scaleZ.get()); + mat4.identity(transMatrix); + mat4.scale(transMatrix, transMatrix, vScale); +} + + +}; + +Ops.Gl.Matrix.ScaleXYZViewMatrix.prototype = new CABLES.Op(); +CABLES.OPS["8b1fa9c9-0c4d-41b6-9c4f-8f4b633a9d7f"]={f:Ops.Gl.Matrix.ScaleXYZViewMatrix,objName:"Ops.Gl.Matrix.ScaleXYZViewMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.ScreenCoordinates +// +// ************************************************************** + +Ops.Gl.Matrix.ScreenCoordinates = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Execute"), + inUnit = op.inSwitch("Pixel Unit", ["Display", "CSS"], "Display"), + trigger = op.outTrigger("Trigger"), + x = op.outNumber("X"), + y = op.outNumber("Y"), + visi = op.outNumber("Visible"); + +const cgl = op.patch.cgl; +const trans = vec3.create(); +const m = mat4.create(); +const pos = vec3.create(); +const identVec = vec3.create(); + +let div = Math.randomSeed = 1; +inUnit.onChange = () => +{ + if (inUnit.get() == "CSS")div = cgl.pixelDensity; + else div = 1; +}; + +exec.onTriggered = function () +{ + mat4.multiply(m, cgl.vMatrix, cgl.mMatrix); + vec3.transformMat4(pos, identVec, m); + + vec3.transformMat4(trans, pos, cgl.pMatrix); + + const vp = cgl.getViewPort(); + const xp = (trans[0] * vp[2] / 2) + vp[2] / 2; + const yp = (trans[1] * vp[3] / 2) + vp[3] / 2; + + visi.set(pos[2] < 0.0 && xp > 0 && xp < vp[2] && yp > 0 && yp < vp[3]); + + x.set(xp / div); + y.set(yp / div); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.Matrix.ScreenCoordinates.prototype = new CABLES.Op(); +CABLES.OPS["75cadc63-62bb-4ddd-b519-aa99568a1e6b"]={f:Ops.Gl.Matrix.ScreenCoordinates,objName:"Ops.Gl.Matrix.ScreenCoordinates"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.ScreenPosTo3d_v3 +// +// ************************************************************** + +Ops.Gl.Matrix.ScreenPosTo3d_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Exec"), + inX = op.inValue("X"), + inY = op.inValue("Y"), + inputType = op.inSwitch("Input Type", ["Pixel", "-1 to 1"], "Pixel"), + outTrigger = op.outTrigger("Trigger out"), + outX = op.outNumber("Result X"), + outY = op.outNumber("Result Y"); + +const mat = mat4.create(); +const cgl = op.patch.cgl; + +exec.onTriggered = calc; + +let inp = 0; +inputType.onChange = () => +{ + if (inputType.get() == "Pixel")inp = 0; + else if (inputType.get() == "-1 to 1")inp = 1; +}; + +function calc() +{ + let x = 0; + let y = 0; + + let aspect = cgl.canvas.clientWidth / cgl.canvas.clientHeight; + + if (inp === 0) // pixel + { + x = 2.0 * inX.get() / cgl.canvas.clientWidth - 1; + y = -2.0 * inY.get() / cgl.canvas.clientHeight + 1; + } + else if (inp === 1) // -1 to 1 + { + x = inX.get(); + y = inY.get(); + } + + let point3d = vec3.fromValues(x, y, 0); + + mat4.mul(mat, cgl.pMatrix, cgl.vMatrix); + + mat4.invert(mat, mat); + + vec3.transformMat4(point3d, point3d, mat); + + outX.set(point3d[0] * 10); + outY.set(point3d[1] * 10); + outTrigger.trigger(); +} + + +}; + +Ops.Gl.Matrix.ScreenPosTo3d_v3.prototype = new CABLES.Op(); +CABLES.OPS["48d72532-afa6-40c9-a895-00a43635a94b"]={f:Ops.Gl.Matrix.ScreenPosTo3d_v3,objName:"Ops.Gl.Matrix.ScreenPosTo3d_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.SetProjectionMatrix +// +// ************************************************************** + +Ops.Gl.Matrix.SetProjectionMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inExe=op.inTrigger("exe"); +const inMatrix=op.inArray('Matrix'); +const next=op.outTrigger("next"); +const cgl=op.patch.cgl; + +inExe.onTriggered=function() +{ + if(inMatrix.get()) + { + cgl.pushPMatrix(); + + mat4.copy(cgl.pMatrix,inMatrix.get() ); + + next.trigger(); + cgl.popPMatrix(); + + } + +}; + +}; + +Ops.Gl.Matrix.SetProjectionMatrix.prototype = new CABLES.Op(); +CABLES.OPS["2c614eb3-ab11-4793-a085-3ef8f517e30b"]={f:Ops.Gl.Matrix.SetProjectionMatrix,objName:"Ops.Gl.Matrix.SetProjectionMatrix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.Shear +// +// ************************************************************** + +Ops.Gl.Matrix.Shear = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render=op.inTrigger('render'), + shearX=op.inValueFloat("shearX",0.5), + shearY=op.inValueFloat("shearY"), + trigger=op.outTrigger('trigger'); + +const cgl=op.patch.cgl; +const shearMatrix = mat4.create(); + +shearY.onChange=shearX.onChange=update; + +function update() +{ + mat4.identity(shearMatrix); + shearMatrix[1]=Math.tan(shearX.get()); + shearMatrix[4]=Math.tan(shearY.get()); +} + +render.onTriggered=function() +{ + cgl.pushModelMatrix(); + + mat4.multiply(cgl.mMatrix,cgl.mMatrix,shearMatrix); + trigger.trigger(); + + cgl.popModelMatrix(); +}; + + + +}; + +Ops.Gl.Matrix.Shear.prototype = new CABLES.Op(); +CABLES.OPS["02d76d99-67f2-4146-b906-8bce2c3ad6bb"]={f:Ops.Gl.Matrix.Shear,objName:"Ops.Gl.Matrix.Shear"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.Transform +// +// ************************************************************** + +Ops.Gl.Matrix.Transform = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + posX = op.inValue("posX", 0), + posY = op.inValue("posY", 0), + posZ = op.inValue("posZ", 0), + scale = op.inValue("scale", 1), + rotX = op.inValue("rotX", 0), + rotY = op.inValue("rotY", 0), + rotZ = op.inValue("rotZ", 0), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Rotation", [rotX, rotY, rotZ]); +op.setPortGroup("Position", [posX, posY, posZ]); +op.setPortGroup("Scale", [scale]); +op.setUiAxisPorts(posX, posY, posZ); + +const vPos = vec3.create(); +const vScale = vec3.create(); +const transMatrix = mat4.create(); +mat4.identity(transMatrix); + +let + doScale = false, + doTranslate = false, + translationChanged = true, + scaleChanged = true, + rotChanged = true; + +rotX.onChange = rotY.onChange = rotZ.onChange = setRotChanged; +posX.onChange = posY.onChange = posZ.onChange = setTranslateChanged; +scale.onChange = setScaleChanged; + +render.onTriggered = function () +{ + // if(!CGL.TextureEffect.checkOpNotInTextureEffect(op)) return; + + let updateMatrix = false; + if (translationChanged) + { + updateTranslation(); + updateMatrix = true; + } + if (scaleChanged) + { + updateScale(); + updateMatrix = true; + } + if (rotChanged) updateMatrix = true; + + if (updateMatrix) doUpdateMatrix(); + + const cg = op.patch.cgl; + cg.pushModelMatrix(); + mat4.multiply(cg.mMatrix, cg.mMatrix, transMatrix); + + trigger.trigger(); + cg.popModelMatrix(); + + if (CABLES.UI && CABLES.UI.showCanvasTransforms) gui.setTransform(op.id, posX.get(), posY.get(), posZ.get()); + + if (op.isCurrentUiOp()) + gui.setTransformGizmo( + { + "posX": posX, + "posY": posY, + "posZ": posZ, + }); +}; + +op.transform3d = function () +{ + return { "pos": [posX, posY, posZ] }; +}; + +function doUpdateMatrix() +{ + mat4.identity(transMatrix); + if (doTranslate)mat4.translate(transMatrix, transMatrix, vPos); + + if (rotX.get() !== 0)mat4.rotateX(transMatrix, transMatrix, rotX.get() * CGL.DEG2RAD); + if (rotY.get() !== 0)mat4.rotateY(transMatrix, transMatrix, rotY.get() * CGL.DEG2RAD); + if (rotZ.get() !== 0)mat4.rotateZ(transMatrix, transMatrix, rotZ.get() * CGL.DEG2RAD); + + if (doScale)mat4.scale(transMatrix, transMatrix, vScale); + rotChanged = false; +} + +function updateTranslation() +{ + doTranslate = false; + if (posX.get() !== 0.0 || posY.get() !== 0.0 || posZ.get() !== 0.0) doTranslate = true; + vec3.set(vPos, posX.get(), posY.get(), posZ.get()); + translationChanged = false; +} + +function updateScale() +{ + // doScale=false; + // if(scale.get()!==0.0) + doScale = true; + vec3.set(vScale, scale.get(), scale.get(), scale.get()); + scaleChanged = false; +} + +function setTranslateChanged() +{ + translationChanged = true; +} + +function setScaleChanged() +{ + scaleChanged = true; +} + +function setRotChanged() +{ + rotChanged = true; +} + +doUpdateMatrix(); + + +}; + +Ops.Gl.Matrix.Transform.prototype = new CABLES.Op(); +CABLES.OPS["650baeb1-db2d-4781-9af6-ab4e9d4277be"]={f:Ops.Gl.Matrix.Transform,objName:"Ops.Gl.Matrix.Transform"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.TransformMul +// +// ************************************************************** + +Ops.Gl.Matrix.TransformMul = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const render=op.inTrigger("render"); +const mul=op.inValueFloat("mul"); +const trigger=op.outTrigger("trigger"); + +const cgl=op.patch.cgl; + +render.onTriggered=function() +{ + var pos=[0,0,0]; + vec3.transformMat4(pos, [0,0,0], cgl.mMatrix); + + cgl.pushModelMatrix(); + vec3.mul(pos,pos,[mul.get(),mul.get(),mul.get()] ); + + mat4.translate(cgl.mMatrix,cgl.mMatrix, pos ); + trigger.trigger(); + + cgl.popModelMatrix(); +}; + +}; + +Ops.Gl.Matrix.TransformMul.prototype = new CABLES.Op(); +CABLES.OPS["2a83f565-7c5c-4cce-862f-d38481eb3726"]={f:Ops.Gl.Matrix.TransformMul,objName:"Ops.Gl.Matrix.TransformMul"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.TransformView +// +// ************************************************************** + +Ops.Gl.Matrix.TransformView = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + posX = op.inValueFloat("posX"), + posY = op.inValueFloat("posY"), + posZ = op.inValueFloat("posZ"), + scale = op.inValueFloat("scale"), + rotX = op.inValueFloat("rotX"), + rotY = op.inValueFloat("rotY"), + rotZ = op.inValueFloat("rotZ"), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Position", [posX, posY, posZ]); +op.setPortGroup("Scale", [scale]); +op.setPortGroup("Rotation", [rotX, rotZ, rotY]); + +const vPos = vec3.create(); +const vScale = vec3.create(); +const transMatrix = mat4.create(); +mat4.identity(transMatrix); + +let doScale = false; +let doTranslate = false; + +let translationChanged = true; +let didScaleChanged = true; +let didRotChanged = true; + +render.onTriggered = function () +{ + const cg = op.patch.cg; + + let updateMatrix = false; + if (translationChanged) + { + updateTranslation(); + updateMatrix = true; + } + if (didScaleChanged) + { + updateScale(); + updateMatrix = true; + } + if (didRotChanged) + { + updateMatrix = true; + } + if (updateMatrix)doUpdateMatrix(); + + cg.pushViewMatrix(); + mat4.multiply(cg.vMatrix, cg.vMatrix, transMatrix); + + trigger.trigger(); + cg.popViewMatrix(); + + if (op.isCurrentUiOp()) + gui.setTransformGizmo( + { + "posX": posX, + "posY": posY, + "posZ": posZ, + }); +}; + +op.transform3d = function () +{ + return { + "pos": [posX, posY, posZ] + }; +}; + +function doUpdateMatrix() +{ + mat4.identity(transMatrix); + if (doTranslate)mat4.translate(transMatrix, transMatrix, vPos); + + if (rotX.get() !== 0)mat4.rotateX(transMatrix, transMatrix, rotX.get() * CGL.DEG2RAD); + if (rotY.get() !== 0)mat4.rotateY(transMatrix, transMatrix, rotY.get() * CGL.DEG2RAD); + if (rotZ.get() !== 0)mat4.rotateZ(transMatrix, transMatrix, rotZ.get() * CGL.DEG2RAD); + + if (doScale)mat4.scale(transMatrix, transMatrix, vScale); + rotChanged = false; +} + +function updateTranslation() +{ + doTranslate = false; + if (posX.get() !== 0.0 || posY.get() !== 0.0 || posZ.get() !== 0.0) doTranslate = true; + vec3.set(vPos, posX.get(), posY.get(), posZ.get()); + translationChanged = false; +} + +function updateScale() +{ + doScale = false; + if (scale.get() !== 0.0)doScale = true; + vec3.set(vScale, scale.get(), scale.get(), scale.get()); + scaleChanged = false; +} + +function translateChanged() +{ + translationChanged = true; +} + +function scaleChanged() +{ + didScaleChanged = true; +} + +function rotChanged() +{ + didRotChanged = true; +} + +rotX.onChange = +rotY.onChange = +rotZ.onChange = rotChanged; + +scale.onChange = scaleChanged; + +posX.onChange = +posY.onChange = +posZ.onChange = translateChanged; + +rotX.set(0.0); +rotY.set(0.0); +rotZ.set(0.0); + +scale.set(1.0); + +posX.set(0.0); +posY.set(0.0); +posZ.set(0.0); + +doUpdateMatrix(); + + +}; + +Ops.Gl.Matrix.TransformView.prototype = new CABLES.Op(); +CABLES.OPS["0b3e04f7-323e-4ac8-8a22-a21e2f36e0e9"]={f:Ops.Gl.Matrix.TransformView,objName:"Ops.Gl.Matrix.TransformView"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.Translate +// +// ************************************************************** + +Ops.Gl.Matrix.Translate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const render = op.inTrigger("render"); +const trigger = op.outTrigger("trigger"); +const x = op.inValue("x"); +const y = op.inValue("y"); +const z = op.inValue("z"); + +let vec = vec3.create(); + +render.onTriggered = function () +{ + const cgl = op.patch.cg; + + vec3.set(vec, x.get(), y.get(), z.get()); + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, vec); + trigger.trigger(); + cgl.popModelMatrix(); +}; + + +}; + +Ops.Gl.Matrix.Translate.prototype = new CABLES.Op(); +CABLES.OPS["1f89ba0e-e7eb-46d7-8c66-7814b7c528b9"]={f:Ops.Gl.Matrix.Translate,objName:"Ops.Gl.Matrix.Translate"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.TranslateView +// +// ************************************************************** + +Ops.Gl.Matrix.TranslateView = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render=op.inTrigger('render'), + x=op.inValueFloat("x"), + y=op.inValueFloat("y"), + z=op.inValueFloat("z"), + trigger=op.outTrigger('trigger'); + +const cgl=op.patch.cgl; +const vec=vec3.create(); + +render.onTriggered=function() +{ + vec3.set(vec, x.get(),y.get(),z.get()); + cgl.pushViewMatrix(); + mat4.translate(cgl.vMatrix,cgl.vMatrix, vec); + trigger.trigger(); + cgl.popViewMatrix(); +}; + + +}; + +Ops.Gl.Matrix.TranslateView.prototype = new CABLES.Op(); +CABLES.OPS["b15472e2-b895-4dde-95c3-239fa5e08afc"]={f:Ops.Gl.Matrix.TranslateView,objName:"Ops.Gl.Matrix.TranslateView"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.VectorTranslate +// +// ************************************************************** + +Ops.Gl.Matrix.VectorTranslate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exec = op.inTrigger("Exec"); +const speed = op.inValue("Speed"); +const vecX = op.inValue("Vector X"); +const vecY = op.inValue("Vector Y"); +const vecZ = op.inValue("Vector Z"); +const resetVecX = op.inFloat("Reset Position X"); +const resetVecY = op.inFloat("Reset Position Y"); +const resetVecZ = op.inFloat("Reset Position Z"); +const next = op.outTrigger("Next"); + +const reset = op.inTriggerButton("reset"); + +const max = op.inValue("max"); + +const cgl = op.patch.cgl; + +const vec = vec3.create(); +const pos = vec3.create(); +const mat = mat4.create(); + +let lastTime = 0; +let timeDiff = 0; + +reset.onTriggered = function () +{ + vec3.set(pos, + (resetVecX.get()), + (resetVecY.get()), + (resetVecZ.get()) + ); +}; + +let dir = false; +function changeDir(d) +{ + dir = !dir; + + move(); +} + +function isOutside() +{ + if ( + pos[0] > max.get() || pos[0] < -max.get() + || pos[1] > max.get() || pos[1] < -max.get() + || pos[2] > max.get() || pos[2] < -max.get()) + return true; + return false; +} + +function move() +{ +} + +exec.onTriggered = function () +{ + timeDiff = op.patch.freeTimer.get() - lastTime; + const m = speed.get() * timeDiff * 0.1; + + vec3.set(vec, + (vecX.get()), + (vecY.get()), + (vecZ.get()) + ); + + vec3.normalize(vec, vec); + + vec[0] *= m; + vec[1] *= m; + vec[2] *= m; + + lastTime = op.patch.freeTimer.get(); + + move(); + + // if(isOutside()) + // { + // op.log("OUTSIDE"); + // changeDir(); + // var count=0; + // while(isOutside() && count<10) + // { + // randomize(); + // count++; + // move(); + // } + // } + + // if(pos[0]>max.get() || pos[0]<-max.get()) changeDir(); + // else if(pos[1]>max.get() || pos[1]<-max.get()) changeDir(); + // else if(pos[2]>max.get() || pos[2]<-max.get()) changeDir(); + + vec3.add(pos, pos, vec); + + cgl.pushModelMatrix(); + + mat4.translate(cgl.mMatrix, cgl.mMatrix, pos); + + next.trigger(); + + cgl.popModelMatrix(); +}; + + +}; + +Ops.Gl.Matrix.VectorTranslate.prototype = new CABLES.Op(); +CABLES.OPS["ca65a1bc-3efa-4b73-96c4-087003b9470a"]={f:Ops.Gl.Matrix.VectorTranslate,objName:"Ops.Gl.Matrix.VectorTranslate"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.WASDCamera_v2 +// +// ************************************************************** + +Ops.Gl.Matrix.WASDCamera_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + enablePointerLock = op.inBool("Enable pointer lock", true), + trigger = op.outTrigger("trigger"), + isLocked = op.outBoolNum("isLocked", false), + + moveSpeed = op.inFloat("Speed", 1), + mouseSpeed = op.inFloat("Mouse Speed", 1), + fly = op.inValueBool("Allow Flying", true), + inActive = op.inBool("Active", true), + + inMoveXPos = op.inBool("Move X+"), + inMoveXNeg = op.inBool("Move X-"), + inMoveYPos = op.inBool("Move Y+"), + inMoveYNeg = op.inBool("Move Y-"), + + inReset = op.inTriggerButton("Reset"), + + outPosX = op.outNumber("posX"), + outPosY = op.outNumber("posY"), + outPosZ = op.outNumber("posZ"), + + outMouseDown = op.outTrigger("Mouse Left"), + outMouseDownRight = op.outTrigger("Mouse Right"), + + outDirX = op.outNumber("Dir X"), + outDirY = op.outNumber("Dir Y"), + outDirZ = op.outNumber("Dir Z"); + +const vPos = vec3.create(); +let speedx = 0, speedy = 0, speedz = 0; +const movementSpeedFactor = 0.5; + +op.setPortGroup("Move", [inMoveYNeg, inMoveYPos, inMoveXNeg, inMoveXPos]); + +let mouseNoPL = { "firstMove": true, + "deltaX": 0, + "deltaY": 0, +}; + +const DEG2RAD = 3.14159 / 180.0; + +let rotX = 0; +let rotY = 0; + +let posX = 0; +let posY = 0; +let posZ = 0; + +let pressedW = false; +let pressedA = false; +let pressedS = false; +let pressedD = false; + +const cgl = op.patch.cgl; + +const viewMatrix = mat4.create(); + +op.toWorkPortsNeedToBeLinked(render); +let lastMove = 0; + +initListener(); + +enablePointerLock.onChange = initListener; + +inReset.onTriggered = () => +{ + rotX = 0; + rotY = 0; + posX = 0; + posY = 0; + posZ = 0; +}; + +inActive.onChange = () => +{ + document.exitPointerLock(); + removeListener(); + + lockChangeCallback(); + + if (inActive.get()) + { + initListener(); + } +}; + +render.onTriggered = function () +{ + if (cgl.frameStore.shadowPass) return trigger.trigger(); + + calcCameraMovement(); + move(); + + if (!fly.get())posY = 0.0; + + if (speedx !== 0.0 || speedy !== 0.0 || speedz !== 0) + { + outPosX.set(posX); + outPosY.set(posY); + outPosZ.set(posZ); + } + + cgl.pushViewMatrix(); + + vec3.set(vPos, -posX, -posY, -posZ); + + mat4.identity(cgl.vMatrix); + + mat4.rotateX(cgl.vMatrix, cgl.vMatrix, DEG2RAD * rotX); + mat4.rotateY(cgl.vMatrix, cgl.vMatrix, DEG2RAD * rotY); + + mat4.translate(cgl.vMatrix, cgl.vMatrix, vPos); + + trigger.trigger(); + cgl.popViewMatrix(); + + // for dir vec + mat4.identity(viewMatrix); + mat4.rotateX(viewMatrix, viewMatrix, DEG2RAD * rotX); + mat4.rotateY(viewMatrix, viewMatrix, DEG2RAD * rotY); + mat4.transpose(viewMatrix, viewMatrix); + + const dir = vec4.create(); + vec4.transformMat4(dir, [0, 0, 1, 1], viewMatrix); + + vec4.normalize(dir, dir); + outDirX.set(-dir[0]); + outDirY.set(-dir[1]); + outDirZ.set(-dir[2]); +}; + +//-------------- + +function calcCameraMovement() +{ + let camMovementXComponent = 0.0, + camMovementYComponent = 0.0, + camMovementZComponent = 0.0, + pitchFactor = 0, + yawFactor = 0; + + if (pressedW) + { + // Control X-Axis movement + pitchFactor = Math.cos(DEG2RAD * rotX); + + camMovementXComponent += (movementSpeedFactor * (Math.sin(DEG2RAD * rotY))) * pitchFactor; + + // Control Y-Axis movement + camMovementYComponent += movementSpeedFactor * (Math.sin(DEG2RAD * rotX)) * -1.0; + + // Control Z-Axis movement + yawFactor = (Math.cos(DEG2RAD * rotX)); + camMovementZComponent += (movementSpeedFactor * (Math.cos(DEG2RAD * rotY)) * -1.0) * yawFactor; + } + + if (pressedS) + { + // Control X-Axis movement + pitchFactor = Math.cos(DEG2RAD * rotX); + camMovementXComponent += (movementSpeedFactor * (Math.sin(DEG2RAD * rotY)) * -1.0) * pitchFactor; + + // Control Y-Axis movement + camMovementYComponent += movementSpeedFactor * (Math.sin(DEG2RAD * rotX)); + + // Control Z-Axis movement + yawFactor = (Math.cos(DEG2RAD * rotX)); + camMovementZComponent += (movementSpeedFactor * (Math.cos(DEG2RAD * rotY))) * yawFactor; + } + + let yRotRad = DEG2RAD * rotY; + + if (pressedA) + { + // Calculate our Y-Axis rotation in radians once here because we use it twice + + camMovementXComponent += -movementSpeedFactor * (Math.cos(yRotRad)); + camMovementZComponent += -movementSpeedFactor * (Math.sin(yRotRad)); + } + + if (pressedD) + { + // Calculate our Y-Axis rotation in radians once here because we use it twice + + camMovementXComponent += movementSpeedFactor * (Math.cos(yRotRad)); + camMovementZComponent += movementSpeedFactor * (Math.sin(yRotRad)); + } + + const mulSpeed = 0.016; + + speedx = camMovementXComponent * mulSpeed; + speedy = camMovementYComponent * mulSpeed; + speedz = camMovementZComponent * mulSpeed; + + if (speedx > movementSpeedFactor) speedx = movementSpeedFactor; + if (speedx < -movementSpeedFactor) speedx = -movementSpeedFactor; + + if (speedy > movementSpeedFactor) speedy = movementSpeedFactor; + if (speedy < -movementSpeedFactor) speedy = -movementSpeedFactor; + + if (speedz > movementSpeedFactor) speedz = movementSpeedFactor; + if (speedz < -movementSpeedFactor) speedz = -movementSpeedFactor; +} + +function moveCallback(e) +{ + const mouseSensitivity = 0.1; + rotX += e.movementY * mouseSensitivity * mouseSpeed.get(); + rotY += e.movementX * mouseSensitivity * mouseSpeed.get(); + + if (rotX < -90.0) rotX = -90.0; + if (rotX > 90.0) rotX = 90.0; + if (rotY < -180.0) rotY += 360.0; + if (rotY > 180.0) rotY -= 360.0; +} + +const canvas = document.getElementById("glcanvas"); + +function mouseDown(e) +{ + if (e.which == 3) outMouseDownRight.trigger(); + else outMouseDown.trigger(); +} + +function lockChangeCallback(e) +{ + if (document.pointerLockElement === canvas || + document.mozPointerLockElement === canvas || + document.webkitPointerLockElement === canvas) + { + document.addEventListener("pointerdown", mouseDown, false); + document.addEventListener("pointermove", moveCallback, false); + document.addEventListener("keydown", keyDown, false); + document.addEventListener("keyup", keyUp, false); + isLocked.set(true); + } + else + { + document.removeEventListener("pointerdown", mouseDown, false); + document.removeEventListener("pointermove", moveCallback, false); + document.removeEventListener("keydown", keyDown, false); + document.removeEventListener("keyup", keyUp, false); + isLocked.set(false); + pressedW = false; + pressedA = false; + pressedS = false; + pressedD = false; + } +} + +function startPointerLock() +{ + const test = false; + if (render.isLinked() && enablePointerLock.get()) + { + document.addEventListener("pointermove", moveCallback, false); + canvas.requestPointerLock = canvas.requestPointerLock || + canvas.mozRequestPointerLock || + canvas.webkitRequestPointerLock; + canvas.requestPointerLock(); + } +} + +function removeListener() +{ + cgl.canvas.removeEventListener("pointermove", moveCallbackNoPL, false); + cgl.canvas.removeEventListener("pointerup", upCallbackNoPL, false); + cgl.canvas.removeEventListener("keydown", keyDown, false); + cgl.canvas.removeEventListener("keyup", keyUp, false); + + document.removeEventListener("pointerlockchange", lockChangeCallback, false); + document.removeEventListener("mozpointerlockchange", lockChangeCallback, false); + document.removeEventListener("webkitpointerlockchange", lockChangeCallback, false); + document.getElementById("glcanvas").removeEventListener("mousedown", startPointerLock); +} + +function initListener() +{ + if (enablePointerLock.get()) + { + document.addEventListener("pointerlockchange", lockChangeCallback, false); + document.addEventListener("mozpointerlockchange", lockChangeCallback, false); + document.addEventListener("webkitpointerlockchange", lockChangeCallback, false); + document.getElementById("glcanvas").addEventListener("mousedown", startPointerLock); + + cgl.canvas.removeEventListener("pointermove", moveCallbackNoPL, false); + cgl.canvas.removeEventListener("pointerup", upCallbackNoPL, false); + cgl.canvas.removeEventListener("keydown", keyDown, false); + cgl.canvas.removeEventListener("keyup", keyUp, false); + } + else + { + cgl.canvas.addEventListener("pointermove", moveCallbackNoPL, false); + cgl.canvas.addEventListener("pointerup", upCallbackNoPL, false); + cgl.canvas.addEventListener("keydown", keyDown, false); + cgl.canvas.addEventListener("keyup", keyUp, false); + } +} + +function upCallbackNoPL(e) +{ + try { cgl.canvas.releasePointerCapture(e.pointerId); } + catch (e) {} + mouseNoPL.firstMove = true; +} + +function moveCallbackNoPL(e) +{ + if (e && e.buttons == 1) + { + try { cgl.canvas.setPointerCapture(e.pointerId); } + catch (_e) {} + + if (!mouseNoPL.firstMove) + { + // outDragging.set(true); + const deltaX = (e.clientX - mouseNoPL.lastX) * mouseSpeed.get() * 0.5; + const deltaY = (e.clientY - mouseNoPL.lastY) * mouseSpeed.get() * 0.5; + + rotX += deltaY; + rotY += deltaX; + // outDeltaX.set(deltaX); + // outDeltaY.set(deltaY); + } + + mouseNoPL.firstMove = false; + + mouseNoPL.lastX = e.clientX; + mouseNoPL.lastY = e.clientY; + } +} + +function move() +{ + let timeOffset = window.performance.now() - lastMove; + timeOffset *= moveSpeed.get(); + posX += speedx * timeOffset; + posY += speedy * timeOffset; + posZ += speedz * timeOffset; + + lastMove = window.performance.now(); +} + +function keyDown(e) +{ + switch (e.which) + { + case 87: + pressedW = true; + break; + case 65: + pressedA = true; + break; + case 83: + pressedS = true; + break; + case 68: + pressedD = true; + break; + + default: + break; + } +} + +function keyUp(e) +{ + switch (e.which) + { + case 87: + pressedW = false; + break; + case 65: + pressedA = false; + break; + case 83: + pressedS = false; + break; + case 68: + pressedD = false; + break; + } +} + +inMoveXPos.onChange = () => { pressedD = inMoveXPos.get(); }; +inMoveXNeg.onChange = () => { pressedA = inMoveXNeg.get(); }; +inMoveYPos.onChange = () => { pressedW = inMoveYPos.get(); }; +inMoveYNeg.onChange = () => { pressedS = inMoveYNeg.get(); }; + + +}; + +Ops.Gl.Matrix.WASDCamera_v2.prototype = new CABLES.Op(); +CABLES.OPS["af6c3fe2-58a9-4f81-be41-4c21aabffde9"]={f:Ops.Gl.Matrix.WASDCamera_v2,objName:"Ops.Gl.Matrix.WASDCamera_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.MediaRecorder +// +// ************************************************************** + +Ops.Gl.MediaRecorder = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const videoTypes = ["webm", "mp4", "x-matroska"]; +const audioTypes = ["webm", "mp3", "x-matroska"]; +const videoCodecs = ["vp9", "vp8", "avc1", "av1", "h265", "h264", "mpeg", "mp4a"]; +const audioCodecs = ["opus", "pcm", "aac", "mp3", "ogg"]; + +const supportedVideos = getSupportedMimeTypes("video", videoTypes, videoCodecs, audioCodecs); + +function getSupportedMimeTypes(media, types, codecs, codecsB) +{ + const isSupported = MediaRecorder.isTypeSupported; + const supported = []; + + types.forEach((type) => + { + const mimeType = `${media}/${type}`; + if (isSupported(mimeType)) + supported.push(mimeType); + }); + + types.forEach((type) => + { + const mimeType = `${media}/${type}`; + codecs.forEach((codec) => + { + return [`${mimeType}; codecs=${codec}`].forEach((variation) => + { + if (isSupported(variation)) supported.push(variation); + + codecsB.forEach((codecB) => + { + return [`${mimeType}; codecs=${codec},${codecB}`].forEach((eachVariation) => + { + if (isSupported(eachVariation)) supported.push(eachVariation); + }); + }); + }); + }); + }); + return supported; +} + +// / //////////////// + +const + recordingToggle = op.inBool("Recording", false), + + inFilename = op.inString("Filename", "cables"), + inCodecs = op.inDropDown("Mimetype", supportedVideos), + inMbit = op.inFloat("MBit", 5), + inFPS = op.inFloat("FPS", 30), + + inMedia = op.inSwitch("Media", ["Video", "Audio", "Audio+Video"], "Video"), + inAudio = op.inObject("Audio In", null, "audioNode"), + inCanvasId = op.inString("Video Canvas Id", "glcanvas"), + + outState = op.outString("State"), + outError = op.outString("Error"), + outCodec = op.outString("Final Mimetype"), + outCodecs = op.outArray("Valid Mimetypes", supportedVideos); + +op.setPortGroup("Inputs", [inMedia, inAudio, inCanvasId]); +op.setPortGroup("Encoding", [inMbit, inCodecs, inFPS]); + +const gl = op.patch.cgl.gl; +let fb = null; +const cgl = op.patch.cgl; + +let cgl_filter = 0; +let cgl_wrap = 0; +let tex = null; +let timeout = null; +let firstTime = true; +let mediaRecorder; +let recordedBlobs; +let sourceBuffer; + +recordingToggle.onChange = toggleRecording; + +inFPS.onChange = + inMbit.onChange = + inMedia.onChange = + inAudio.onChange = + inCanvasId.onChange = + inCodecs.onChange = setupMediaRecorder; + +op.patch.cgl.on("resize", () => +{ + if (mediaRecorder && mediaRecorder.state === "active")mediaRecorder.stop(); + mediaRecorder = null; +}); + +setupMediaRecorder(); + +function handleDataAvailable(event) +{ + if (event.data && event.data.size > 0) + { + recordedBlobs.push(event.data); + } +} + +function toggleRecording() +{ + if (recordingToggle.get()) startRecording(); + else stopRecording(); +} + +function setupMediaRecorder() +{ + outCodec.set("unknown"); + + outState.set(""); + outCodec.set(""); + outError.set(""); + op.setUiError("constr", null); + op.setUiError("audionoaudio", null); + op.setUiError("nocanvas", null); + mediaRecorder = null; + + if (inCodecs.get() === "" || inCodecs.get() === 0) + { + return; + } + + let options = { "mimeType": inCodecs.get(), "videoBitsPerSecond": inMbit.get() * 1024 * 1024 }; + recordedBlobs = []; + try + { + const canvas = document.getElementById(inCanvasId.get()); + if (!canvas) + { + op.setUiError("nocanvas", "canvas not found "); + return; + } + canvas.getContext("2d"); + const streamVid = canvas.captureStream(inFPS.get()); + + let stream = streamVid; + if (inMedia.get() !== "Video") + { + const audioCtx = CABLES.WEBAUDIO.createAudioContext(op); + const streamAudio = audioCtx.createMediaStreamDestination(); + + if (!inAudio.get()) + { + op.setUiError("audionoaudio", "no audio connected "); + return; + } + inAudio.get().connect(streamAudio); + + if (inMedia.get() === "Audio+Video")stream = new MediaStream([...streamVid.getTracks(), ...streamAudio.stream.getTracks()]); + else stream = streamAudio; + } + + mediaRecorder = new MediaRecorder(stream, options); + } + catch (err) + { + op.error(err); + op.error("error mr constructor: ", err); + outError.set(err.message); + op.setUiError("contr", "MediaRecorder error: " + err.message); + } + if (mediaRecorder) + { + outState.set(mediaRecorder.state); + outCodec.set(mediaRecorder.mimeType); + } + else + { + op.warn("no mediarecorder created..."); + } +} + +// The nested try blocks will be simplified when Chrome 47 moves to Stable +function startRecording() +{ + if (!mediaRecorder)setupMediaRecorder(); + if (!mediaRecorder) + { + op.setUiError("noobj", "could not create mediarecorder, try setting all parameters"); + return; + } + + recordedBlobs = []; + + op.setUiError("noobj", null); + + op.verbose("start recording: ", inCodecs.get()); + + mediaRecorder.ondataavailable = handleDataAvailable; + mediaRecorder.start(1000); + + outState.set(mediaRecorder.state); + op.log("MediaRecorder started", mediaRecorder); +} + +function stopRecording() +{ + if (!mediaRecorder) + { + // op.warn("cant stop no mediarecorder"); + return; + } + op.verbose("mediaRecorder.state", mediaRecorder.state); + if (mediaRecorder.state === "inactive") return; + + op.verbose("mediaRecorder.videoBitsPerSecond ", mediaRecorder.videoBitsPerSecond / 1024 / 1024); + op.verbose("mediaRecorder.mimeType ", mediaRecorder.mimeType); + + mediaRecorder.onstop = download; + + mediaRecorder.stop(); + outState.set(mediaRecorder.state); + + op.verbose("Recorded Blobs: ", recordedBlobs); + // download(); +} + +function download() +{ + if (recordedBlobs.length === 0) + { + op.warn("download canceled, no recordedBlobs"); + } + + if (!mediaRecorder) return; + + const blob = new Blob(recordedBlobs, { "type": "application/octet-stream" }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + a.style.display = "none"; + a.href = url; + const codec = mediaRecorder.mimeType; + let ext = "webm"; + if (codec.indexOf("video/x-matroska") >= 0)ext = "mkv"; + if (codec.indexOf("video/mp4") >= 0)ext = "mp4"; + a.download = (inFilename.get() || "cables") + "." + ext; + document.body.appendChild(a); + a.click(); + setTimeout(() => + { + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + }, 100); +} + + +}; + +Ops.Gl.MediaRecorder.prototype = new CABLES.Op(); +CABLES.OPS["ff12e50f-e2bd-4554-9aec-dab9de4cba8b"]={f:Ops.Gl.MediaRecorder,objName:"Ops.Gl.MediaRecorder"}; + + + + +// ************************************************************** +// +// Ops.Gl.MeshInstancer_v4 +// +// ************************************************************** + +Ops.Gl.MeshInstancer_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"instancer_body_frag":"#ifdef COLORIZE_INSTANCES\n #ifdef BLEND_MODE_MULTIPLY\n col.rgb *= frag_instColor.rgb;\n col.a *= frag_instColor.a;\n #endif\n\n #ifdef BLEND_MODE_ADD\n col.rgb += frag_instColor.rgb;\n col.a += frag_instColor.a;\n #endif\n\n #ifdef BLEND_MODE_NONE\n col.rgb = frag_instColor.rgb;\n col.a = frag_instColor.a;\n #endif\n#endif\n","instancer_body_vert":"\n\n#ifdef HAS_TEXCOORDS\ntexCoord=(texCoord*instTexCoords.zw)+instTexCoords.xy;\n#endif\n\nmMatrix*=instMat;\npos.xyz*=MOD_scale;\n\n#ifdef HAS_COLORS\nfrag_instColor=instColor;\n#endif\n#ifndef HAS_COLORS\nfrag_instColor=vec4(1.0);\n#endif\n\n","instancer_head_frag":"IN vec4 frag_instColor;\n","instancer_head_vert":"\nIN vec4 instColor;\nIN mat4 instMat;\nIN vec4 instTexCoords;\nOUT mat4 instModelMat;\nOUT vec4 frag_instColor;\n\n#define INSTANCING\n\n",}; +const + exe = op.inTrigger("exe"), + geom = op.inObject("geom", null, "geometry"), + inScale = op.inValue("Scale", 1), + doLimit = op.inValueBool("Limit Instances", false), + inLimit = op.inValueInt("Limit", 100), + inTranslates = op.inArray("positions", 3), + inScales = op.inArray("Scale Array", 3), + inRot = op.inArray("Rotations", 3), + inRotMeth = op.inSwitch("Rotation Type", ["Euler", "Quaternions"], "Euler"), + inBlendMode = op.inSwitch("Material blend mode", ["Multiply", "Add", "Normal"], "Multiply"), + inColor = op.inArray("Colors", 4), + inTexCoords = op.inArray("TexCoords", 4), + outTrigger = op.outTrigger("Trigger Out"), + outNum = op.outNumber("Num"); + +op.setPortGroup("Limit Number of Instances", [inLimit, doLimit]); +op.setPortGroup("Parameters", [inScales, inRot, inTranslates, inRotMeth]); +op.toWorkPortsNeedToBeLinked(geom); +op.toWorkPortsNeedToBeLinked(exe); + +geom.ignoreValueSerialize = true; + +const cgl = op.patch.cgl; +const m = mat4.create(); +let + matrixArray = new Float32Array(1), + instColorArray = new Float32Array(1), + instTexcoordArray = new Float32Array(1), + mesh = null, + recalc = true, + num = 0, + arrayChangedColor = true, + arrayChangedTexcoords = true, + arrayChangedTrans = true; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "name": "MODULE_VERTEX_POSITION", + "title": op.name, + "priority": -2, + "srcHeadVert": attachments.instancer_head_vert, + "srcBodyVert": attachments.instancer_body_vert +}); + +mod.addModule({ + "name": "MODULE_COLOR", + "priority": -2, + "title": op.name, + "srcHeadFrag": attachments.instancer_head_frag, + "srcBodyFrag": attachments.instancer_body_frag, +}); + +mod.addUniformVert("f", "MOD_scale", inScale); + +let needsUpdateDefines = true; +inBlendMode.onChange = () => { needsUpdateDefines = true; }; +doLimit.onChange = updateLimit; +exe.onTriggered = doRender; +exe.onLinkChanged = function () +{ + if (!exe.isLinked()) removeModule(); +}; + +updateLimit(); + +inRot.onChange = +inScales.onChange = +inTranslates.onChange = +inRotMeth.onChange = + function () + { + arrayChangedTrans = true; + recalc = true; + }; + +inTexCoords.onChange = function () +{ + arrayChangedTexcoords = true; + recalc = true; + needsUpdateDefines = true; +}; + +inColor.onChange = function () +{ + arrayChangedColor = true; + recalc = true; + needsUpdateDefines = true; +}; + +function reset() +{ + arrayChangedColor = true, + arrayChangedTrans = true; + recalc = true; +} + +function updateDefines() +{ + mod.toggleDefine("COLORIZE_INSTANCES", inColor.get()); + mod.toggleDefine("TEXCOORDS_INSTANCES", inTexCoords.get()); + mod.toggleDefine("BLEND_MODE_MULTIPLY", inBlendMode.get() === "Multiply"); + mod.toggleDefine("BLEND_MODE_ADD", inBlendMode.get() === "Add"); + mod.toggleDefine("BLEND_MODE_NONE", inBlendMode.get() === "Normal"); + needsUpdateDefines = false; +} + +geom.onChange = function () +{ + if (mesh)mesh.dispose(); + if (!geom.get()) + { + mesh = null; + return; + } + + mesh = new CGL.Mesh(cgl, geom.get()); + reset(); +}; + +function removeModule() +{ + +} + +function setupArray() +{ + if (!mesh) return; + + let transforms = inTranslates.get(); + if (!transforms) transforms = [0, 0, 0]; + + num = Math.floor(transforms.length / 3); + + if (needsUpdateDefines)updateDefines(); + + const colArr = inColor.get(); + const tcArr = inTexCoords.get(); + const scales = inScales.get(); + const useQuats = inRotMeth.get() == "Quaternions"; + const useNormals = inRotMeth.get() == "Normals"; + + let stride = 3; + if (useQuats)stride = 4; + inRot.setUiAttribs({ "stride": stride }); + + if (matrixArray.length != num * 16) matrixArray = new Float32Array(num * 16); + if (instColorArray.length != num * 4) + { + arrayChangedColor = true; + instColorArray = new Float32Array(num * 4); + } + if (instTexcoordArray.length != num * 4) + { + arrayChangedTexcoords = true; + instTexcoordArray = new Float32Array(num * 4); + } + + const rotArr = inRot.get(); + + for (let i = 0; i < num; i++) + { + mat4.identity(m); + + mat4.translate(m, m, + [ + transforms[i * 3], + transforms[i * 3 + 1], + transforms[i * 3 + 2] + ]); + + if (rotArr) + { + if (useQuats) + { + const mq = mat4.create(); + const q = [rotArr[i * 4 + 0], rotArr[i * 4 + 1], rotArr[i * 4 + 2], rotArr[i * 4 + 3]]; + quat.normalize(q, q); + mat4.fromQuat(mq, q); + mat4.mul(m, m, mq); + } + if (useNormals) + { + // let q = quat.create(); + // if (rotArr[i + 1] > 0.99999) + // { + // q.set(0, 0, 0, 1); + // } + // else if (rotArr[i + 1] < -0.99999) + // { + // q.set(1, 0, 0, 0); + // } + // else + // { + // let axis = vec3.create(); + // vec3.set(axis, rotArr[i + 2], 0, -rotArr[i + 0]); + // vec3.normalize(axis, axis); + // let radians = Math.acos(rotArr[i + 1]); + // quat.setAxisAngle(q, axis, radians); + // } + + // const mq = mat4.create(); + + // quat.normalize(q, q); + // mat4.fromQuat(mq, q); + // mat4.mul(m, m, mq); + } + else + { + mat4.rotateX(m, m, rotArr[i * 3 + 0] * CGL.DEG2RAD); + mat4.rotateY(m, m, rotArr[i * 3 + 1] * CGL.DEG2RAD); + mat4.rotateZ(m, m, rotArr[i * 3 + 2] * CGL.DEG2RAD); + } + } + + if (arrayChangedColor && colArr) + { + instColorArray[i * 4 + 0] = colArr[i * 4 + 0]; + instColorArray[i * 4 + 1] = colArr[i * 4 + 1]; + instColorArray[i * 4 + 2] = colArr[i * 4 + 2]; + instColorArray[i * 4 + 3] = colArr[i * 4 + 3]; + } + + if (arrayChangedTexcoords && tcArr) + { + instTexcoordArray[i * 4 + 0] = tcArr[i * 4 + 0]; + instTexcoordArray[i * 4 + 1] = tcArr[i * 4 + 1]; + instTexcoordArray[i * 4 + 2] = tcArr[i * 4 + 2]; + instTexcoordArray[i * 4 + 3] = tcArr[i * 4 + 3]; + } + + if (scales && scales.length > i) mat4.scale(m, m, [scales[i * 3], scales[i * 3 + 1], scales[i * 3 + 2]]); + else mat4.scale(m, m, [1, 1, 1]); + + for (let a = 0; a < 16; a++) matrixArray[i * 16 + a] = m[a]; + } + + mesh.numInstances = num; + + if (arrayChangedTrans) mesh.addAttribute("instMat", matrixArray, 16); + if (arrayChangedColor) mesh.addAttribute("instColor", instColorArray, 4, { "instanced": true }); + if (arrayChangedTexcoords) mesh.addAttribute("instTexCoords", instTexcoordArray, 4, { "instanced": true }); + + mod.toggleDefine("HAS_TEXCOORDS", tcArr); + mod.toggleDefine("HAS_COLORS", colArr); + + arrayChangedColor = false; + recalc = false; +} + +function updateLimit() +{ + inLimit.setUiAttribs({ "greyout": !doLimit.get() }); +} + +function doRender() +{ + if (!mesh) return; + if (recalc) setupArray(); + + mod.bind(); + + if (doLimit.get()) mesh.numInstances = Math.min(num, inLimit.get()); + else mesh.numInstances = num; + + outNum.set(this.name, mesh.numInstances); + + if (mesh.numInstances > 0) mesh.render(cgl.getShader()); + + outTrigger.trigger(); + + mod.unbind(); +} + + +}; + +Ops.Gl.MeshInstancer_v4.prototype = new CABLES.Op(); +CABLES.OPS["322d8c8d-851b-481d-9bee-ec1cf7d57a35"]={f:Ops.Gl.MeshInstancer_v4,objName:"Ops.Gl.MeshInstancer_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.MeshMorph +// +// ************************************************************** + +Ops.Gl.MeshMorph = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + NUMGEOMS = 16, + render = op.inTrigger("render"), + inMethod = op.inSwitch("Method", ["Auto Anim", "Interpolate Index", "Interpolate Indices"], "Auto Anim"), + nextGeom = op.inValueInt("Geometry"), + duration = op.inValue("Duration", 1.0), + inIndex = op.inFloat("Index", 0), + inIndex2 = op.inFloat("Index 2", 0), + inFade = op.inFloatSlider("Fade", 0), + finished = op.outBoolNum("Finished"), + next = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const inGeoms = []; +let mesh = null; +let method_autoAnim = true; +let method_manualFade = false; +let method_interpolate = false; +let method_interpolate2 = false; +let oldGeom = 0; + +const anim = new CABLES.Anim(); +anim.clear(); +const inEase = anim.createPort(op, "Easing", updateGeom); + +window.meshsequencecounter = window.meshsequencecounter || 1; +window.meshsequencecounter++; +const prfx = String.fromCharCode(97 + window.meshsequencecounter); + +const srcHeadVert = "" + .endl() + "IN vec3 " + prfx + "attrMorphTargetA;" + .endl() + "IN vec3 " + prfx + "attrMorphTargetB;" + .endl(); + +const srcBodyVert = "" + .endl() + " pos = vec4(" + prfx + "attrMorphTargetA * MOD_fade + " + prfx + "attrMorphTargetB * (1.0 - MOD_fade), 1. );" + + .endl(); + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addUniform("f", "MOD_fade", 1); +mod.addUniform("f", "MOD_doMorph", 1); +mod.addModule({ + "title": op.objName, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert +}); + +inIndex2.onChange = + inIndex.onChange = + nextGeom.onChange = updateGeom; +render.onTriggered = doRender; +inMethod.onChange = updateUi; +updateUi(); + +for (let i = 0; i < NUMGEOMS; i++) +{ + const inGeom = op.inObject("Geometry " + (i)); + inGeom.onChange = updateMeshes; + inGeoms.push(inGeom); +} + +function updateUi() +{ + method_interpolate = inMethod.get() == "Interpolate Index"; + method_autoAnim = inMethod.get() == "Auto Anim"; + method_interpolate2 = inMethod.get() == "Interpolate Indices"; + + duration.setUiAttribs({ "greyout": !method_autoAnim }); + nextGeom.setUiAttribs({ "greyout": !(method_autoAnim || method_manualFade) }); + inIndex.setUiAttribs({ "greyout": !(method_interpolate || method_interpolate2) }); + inIndex2.setUiAttribs({ "greyout": !method_interpolate2 }); + inIndex2.setUiAttribs({ "greyout": !method_interpolate2 }); + inEase.setUiAttribs({ "greyout": !method_autoAnim }); + + inFade.setUiAttribs({ "greyout": !(method_manualFade || method_interpolate2) }); +} + +function checkLength() +{ + op.setUiError("nosamelength", null); + let oldGeomLength = 0; + + for (let i = 0; i < inGeoms.length; i++) + { + const geom = inGeoms[i].get(); + if (geom && geom._vertices) + { + if (oldGeomLength != 0 && oldGeomLength != geom._vertices.length) op.setUiError("nosamelength", "Geometries must have the same number of vertices!", 1); + oldGeomLength = geom._vertices.length; + } + } +} + +function updateMeshes() +{ + checkLength(); + if (mesh) return; + + for (let i = 0; i < inGeoms.length; i++) + { + const geom = inGeoms[i].get(); + if (geom && geom._vertices) + { + if (i === 0) + { + mesh = new CGL.Mesh(cgl, geom); + + mesh.addAttribute(prfx + "attrMorphTargetA", geom._vertices, 3); + mesh.addAttribute(prfx + "attrMorphTargetB", geom._vertices, 3); + updateGeom(); + } + } + } +} + +function updateGeom() +{ + let geom1; + let geom2; + + if (method_autoAnim || method_manualFade) + { + let getGeom = nextGeom.get(); + if (getGeom < 0) getGeom = 0; + else if (getGeom >= NUMGEOMS) getGeom = NUMGEOMS; + let temp = 0; + + if (oldGeom === getGeom) return; + + anim.clear(); + anim.setValue(op.patch.freeTimer.get(), 0); + anim.setValue(op.patch.freeTimer.get() + duration.get(), 1, + function () + { + oldGeom = getGeom; + finished.set(true); + }); + finished.set(false); + + geom1 = inGeoms[oldGeom].get(); + geom2 = inGeoms[getGeom].get(); + + if (method_manualFade) + { + if (inFade.get() != 0) geom2 = inGeoms[oldGeom].get(); + if (inFade.get() == 1) geom1 = inGeoms[getGeom].get(); + + oldGeom = getGeom; + } + } + + if (method_interpolate2) + { + const b = Math.max(Math.floor(inIndex.get()), 0); + const a = Math.min(Math.ceil(inIndex2.get()), NUMGEOMS); + + if (inGeoms[a]) geom1 = inGeoms[a].get(); + else geom1 = null; + + if (inGeoms[b]) geom2 = inGeoms[b].get(); + else geom2 = null; + } + + if (method_interpolate) + { + const a = Math.max(Math.floor(inIndex.get()), 0); + const b = Math.min(Math.ceil(inIndex.get()), NUMGEOMS); + + if (inGeoms[a]) geom1 = inGeoms[a].get(); + else geom1 = null; + + if (inGeoms[b]) geom2 = inGeoms[b].get(); + else geom2 = null; + } + + if (mesh && geom1 && geom2 && geom1._vertices && geom2._vertices) + { + mesh.updateAttribute(prfx + "attrMorphTargetB", geom1._vertices); + mesh.updateAttribute(prfx + "attrMorphTargetA", geom2._vertices); + } +} + +function doRender() +{ + if (method_autoAnim) mod.setUniformValue("MOD_fade", anim.getValue(op.patch.freeTimer.get())); + else if (method_interpolate) + { + let f = inIndex.get() % 1; + mod.setUniformValue("MOD_fade", f); + } + else if (method_manualFade) mod.setUniformValue("MOD_fade", inFade.get()); + else if (method_interpolate2) mod.setUniformValue("MOD_fade", inFade.get()); + + if (mesh) + { + mod.bind(); + mesh.render(cgl.getShader()); + next.trigger(); + mod.unbind(); + } +} + + +}; + +Ops.Gl.MeshMorph.prototype = new CABLES.Op(); +CABLES.OPS["41bc544e-a0c9-450b-b657-405d80b1298c"]={f:Ops.Gl.MeshMorph,objName:"Ops.Gl.MeshMorph"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.CablesLogo +// +// ************************************************************** + +Ops.Gl.Meshes.CablesLogo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + scale = op.inValue("Scale", 1), + draw = op.inBool("Draw", true), + trigger = op.outTrigger("trigger"), + geomOut = op.outObject("Geometry"); + +const verts = [14.4166, 163.754, 0, 18.4809, 210.763, 0, 54.9491, 204.346, 0, 42.7849, 158.762, 0, 22.5453, 257.772, 0, 67.1133, 249.93, 0, 109.532, 234.597, 0, 89.7001, 191.777, 0, 69.8685, 148.956, 0, 121.577, 173.384, 0, 94.8187, 134.563, 0, 148.336, 212.204, 0, 185.332, 186.28, 0, 150.011, 149.481, 0, 116.915, 116.086, 0, 174.431, 120.383, 0, 135.438, 94.0268, 0, 213.424, 146.739, 0, 241.515, 107.197, 0, 193.515, 87.7918, 0, 149.882, 69.1061, 0, 205.939, 53.4095, 0, 159.743, 42.0425, 0, 252.134, 64.7765, 0, 259.112, 21.5601, 0, 211.953, 17.6223, 0, 164.793, 13.6845, 0, 211.807, -19.1835, 0, 164.803, -15.1197, 0, 258.81, -23.2474, 0, 251.027, -67.8259, 0, 205.438, -55.6602, 0, 159.849, -43.4946, 0, 192.786, -90.46, 0, 150.007, -70.5651, 0, 235.565, -110.355, 0, 213.005, -149.583, 0, 174.326, -122.559, 0, 135.648, -95.5346, 0, 150.535, -150.933, 0, 117.14, -117.606, 0, 183.929, -184.259, 0, 149.313, -213.406, 0, 122.21, -174.782, 0, 95.1064, -136.159, 0, 90.1486, -193.307, 0, 70.1663, -150.569, 0, 110.131, -236.046, 0, 67.6335, -251.595, 0, 55.3747, -206.031, 0, 43.116, -160.467, 0, 18.9111, -212.474, 0, 14.7512, -165.479, 0, 23.071, -259.469, 0, -22.1816, -259.545, 0, -18.1173, -212.536, 0, -14.0529, -165.527, 0, -54.5854, -206.119, 0, -42.4212, -160.536, 0, -66.7496, -251.703, 0, -108.973, -236.672, 0, -89.239, -193.701, 0, -69.5049, -150.73, 0, -120.824, -175.76, 0, -94.4551, -136.336, 0, -147.193, -215.185, 0, -183.41, -190.469, 0, -148.964, -152.339, 0, -116.552, -117.859, 0, -173.286, -123.476, 0, -135.074, -95.8, 0, -211.498, -151.152, 0, -239.586, -111.835, 0, -192.467, -90.79, 0, -149.518, -70.8793, 0, -205.184, -55.8988, 0, -159.38, -43.8157, 0, -250.988, -67.9818, 0, -258.553, -23.6913, 0, -211.491, -19.5745, 0, -164.429, -15.4577, 0, -211.443, 17.4104, 0, -164.439, 13.3465, 0, -258.446, 21.4742, 0, -250.664, 66.0527, 0, -205.075, 53.8871, 0, -159.486, 41.7214, 0, -192.423, 88.6869, 0, -149.644, 68.7919, 0, -235.201, 108.582, 0, -212.641, 147.81, 0, -173.962, 120.786, 0, -135.284, 93.7614, 0, -150.171, 149.16, 0, -116.777, 115.833, 0, -183.566, 182.486, 0, -148.949, 211.632, 0, -121.846, 173.009, 0, -94.7427, 134.385, 0, -89.7849, 191.534, 0, -69.8026, 148.796, 0, -109.767, 234.272, 0, -67.2698, 249.822, 0, -55.011, 204.258, 0, -42.7523, 158.694, 0, -18.5474, 210.7, 0, -14.3875, 163.705, 0, -22.7074, 257.695, 0, 22.5453, 257.772, 0, 18.4809, 210.763, 0, 14.4166, 163.754, 0, 256.822, 264.163, 0, 296.476, 236.296, 0, 276.952, 212.586, 0, 276.952, 212.586, 0, 240.805, 244.919, 0, 256.822, 264.163, 0, 336.13, 208.429, 0, 313.099, 180.253, 0, 276.952, 212.586, 0, 276.952, 212.586, 0, 296.476, 236.296, 0, 336.13, 208.429, 0, 288.309, 152.686, 0, 255.898, 189.327, 0, 313.099, 180.253, 0, 223.486, 225.968, 0, 240.805, 244.919, 0, 285.635, 306.513, 0, 328.243, 283.496, 0, 313.087, 259.335, 0, 313.087, 259.335, 0, 271.643, 284.706, 0, 285.635, 306.513, 0, 370.852, 260.478, 0, 354.53, 233.964, 0, 313.087, 259.335, 0, 313.087, 259.335, 0, 328.243, 283.496, 0, 370.852, 260.478, 0, 336.13, 208.429, 0, 296.476, 236.296, 0, 354.53, 233.964, 0, 336.13, 208.429, 0, 256.822, 264.163, 0, 271.643, 284.706, 0, 296.476, 236.296, 0, 256.822, 264.163, 0, 311.134, 352.806, 0, 357.268, 338.992, 0, 343.405, 310.57, 0, 343.405, 310.57, 0, 299.162, 329.55, 0, 311.134, 352.806, 0, 403.403, 325.177, 0, 387.649, 291.589, 0, 343.405, 310.57, 0, 343.405, 310.57, 0, 357.268, 338.992, 0, 403.403, 325.177, 0, 370.852, 260.478, 0, 328.243, 283.496, 0, 387.649, 291.589, 0, 370.852, 260.478, 0, 285.635, 306.513, 0, 299.162, 329.55, 0, 328.243, 283.496, 0, 285.635, 306.513, 0, 328.173, 398.813, 0, 377.479, 395.259, 0, 368.527, 367.195, 0, 368.527, 367.195, 0, 320.46, 375.27, 0, 328.173, 398.813, 0, 426.784, 391.705, 0, 416.594, 359.12, 0, 368.527, 367.195, 0, 368.527, 367.195, 0, 377.479, 395.259, 0, 426.784, 391.705, 0, 403.403, 325.177, 0, 357.268, 338.992, 0, 416.594, 359.12, 0, 403.403, 325.177, 0, 311.134, 352.806, 0, 320.46, 375.27, 0, 357.268, 338.992, 0, 311.134, 352.806, 0, 341.169, 453.101, 0, 389.523, 451.215, 0, 384.421, 423.263, 0, 384.421, 423.263, 0, 335.304, 425.309, 0, 341.169, 453.101, 0, 437.877, 449.328, 0, 433.538, 421.218, 0, 384.421, 423.263, 0, 384.421, 423.263, 0, 389.523, 451.215, 0, 437.877, 449.328, 0, 426.784, 391.705, 0, 377.479, 395.259, 0, 433.538, 421.218, 0, 426.784, 391.705, 0, 328.173, 398.813, 0, 335.304, 425.309, 0, 377.479, 395.259, 0, 328.173, 398.813, 0, 347.768, 502.233, 0, 394.824, 501.411, 0, 392.954, 479.119, 0, 392.954, 479.119, 0, 345.086, 480.533, 0, 347.768, 502.233, 0, 441.88, 500.589, 0, 440.822, 477.705, 0, 392.954, 479.119, 0, 392.954, 479.119, 0, 394.824, 501.411, 0, 441.88, 500.589, 0, 437.877, 449.328, 0, 389.523, 451.215, 0, 440.822, 477.705, 0, 437.877, 449.328, 0, 341.169, 453.101, 0, 345.086, 480.533, 0, 389.523, 451.215, 0, 341.169, 453.101, 0, 352.384, 517.252, 0, 394.985, 517.267, 0, 395.241, 512.525, 0, 395.241, 512.525, 0, 349.927, 512.83, 0, 352.384, 517.252, 0, 437.585, 517.282, 0, 440.555, 512.22, 0, 395.241, 512.525, 0, 395.241, 512.525, 0, 394.985, 517.267, 0, 437.585, 517.282, 0, 441.88, 500.589, 0, 394.824, 501.411, 0, 440.555, 512.22, 0, 441.88, 500.589, 0, 347.768, 502.233, 0, 349.927, 512.83, 0, 394.824, 501.411, 0, 347.768, 502.233, 0, 360.098, 522.975, 0, 394.737, 522.834, 0, 394.834, 520.442, 0, 394.834, 520.442, 0, 355.961, 520.425, 0, 360.098, 522.975, 0, 429.375, 522.693, 0, 433.707, 520.459, 0, 394.834, 520.442, 0, 394.834, 520.442, 0, 394.737, 522.834, 0, 429.375, 522.693, 0, 437.585, 517.282, 0, 394.985, 517.267, 0, 433.707, 520.459, 0, 437.585, 517.282, 0, 355.961, 520.425, 0, 394.834, 520.442, 0, 394.985, 517.267, 0, 352.384, 517.252, 0, 185.332, 186.28, 0, 204.761, 206.303, 0, 234.697, 167.64, 0, 213.424, 146.739, 0, 264.633, 128.977, 0, 241.515, 107.197, 0, -255.099, -268.612, 0, -294.753, -240.745, 0, -275.229, -217.035, 0, -275.229, -217.035, 0, -239.082, -249.368, 0, -255.099, -268.612, 0, -334.408, -212.878, 0, -311.376, -184.702, 0, -275.229, -217.035, 0, -275.229, -217.035, 0, -294.753, -240.745, 0, -334.408, -212.878, 0, -286.56, -157.158, 0, -254.149, -193.771, 0, -311.376, -184.702, 0, -221.739, -230.385, 0, -239.082, -249.368, 0, -283.912, -310.962, 0, -326.521, -287.944, 0, -311.364, -263.784, 0, -311.364, -263.784, 0, -269.92, -289.154, 0, -283.912, -310.962, 0, -369.129, -264.927, 0, -352.807, -238.413, 0, -311.364, -263.784, 0, -311.364, -263.784, 0, -326.521, -287.944, 0, -369.129, -264.927, 0, -334.408, -212.878, 0, -294.753, -240.745, 0, -352.807, -238.413, 0, -334.408, -212.878, 0, -255.099, -268.612, 0, -269.92, -289.154, 0, -294.753, -240.745, 0, -255.099, -268.612, 0, -309.411, -357.255, 0, -355.546, -343.44, 0, -341.683, -315.018, 0, -341.683, -315.018, 0, -297.439, -333.999, 0, -309.411, -357.255, 0, -401.681, -329.626, 0, -385.927, -296.038, 0, -341.683, -315.018, 0, -341.683, -315.018, 0, -355.546, -343.44, 0, -401.681, -329.626, 0, -369.129, -264.927, 0, -326.521, -287.944, 0, -385.927, -296.038, 0, -369.129, -264.927, 0, -283.912, -310.962, 0, -297.439, -333.999, 0, -326.521, -287.944, 0, -283.912, -310.962, 0, -326.45, -403.262, 0, -375.756, -399.708, 0, -366.804, -371.644, 0, -366.804, -371.644, 0, -318.737, -379.718, 0, -326.45, -403.262, 0, -425.061, -396.154, 0, -414.871, -363.569, 0, -366.804, -371.644, 0, -366.804, -371.644, 0, -375.756, -399.708, 0, -425.061, -396.154, 0, -401.681, -329.626, 0, -355.546, -343.44, 0, -414.871, -363.569, 0, -401.681, -329.626, 0, -309.411, -357.255, 0, -318.737, -379.718, 0, -355.546, -343.44, 0, -309.411, -357.255, 0, -339.446, -457.55, 0, -387.8, -455.663, 0, -382.698, -427.712, 0, -382.698, -427.712, 0, -333.581, -429.758, 0, -339.446, -457.55, 0, -436.154, -453.776, 0, -431.815, -425.666, 0, -382.698, -427.712, 0, -382.698, -427.712, 0, -387.8, -455.663, 0, -436.154, -453.776, 0, -425.061, -396.154, 0, -375.756, -399.708, 0, -431.815, -425.666, 0, -425.061, -396.154, 0, -326.45, -403.262, 0, -333.581, -429.758, 0, -375.756, -399.708, 0, -326.45, -403.262, 0, -346.045, -506.682, 0, -393.101, -505.86, 0, -391.232, -483.568, 0, -391.232, -483.568, 0, -343.363, -484.982, 0, -346.045, -506.682, 0, -440.157, -505.037, 0, -439.1, -482.153, 0, -391.232, -483.568, 0, -391.232, -483.568, 0, -393.101, -505.86, 0, -440.157, -505.037, 0, -436.154, -453.776, 0, -387.8, -455.663, 0, -439.1, -482.153, 0, -436.154, -453.776, 0, -339.446, -457.55, 0, -343.363, -484.982, 0, -387.8, -455.663, 0, -339.446, -457.55, 0, -350.661, -521.701, 0, -393.262, -521.716, 0, -393.518, -516.974, 0, -393.518, -516.974, 0, -348.204, -517.279, 0, -350.661, -521.701, 0, -435.862, -521.731, 0, -438.832, -516.669, 0, -393.518, -516.974, 0, -393.518, -516.974, 0, -393.262, -521.716, 0, -435.862, -521.731, 0, -440.157, -505.037, 0, -393.101, -505.86, 0, -438.832, -516.669, 0, -440.157, -505.037, 0, -346.045, -506.682, 0, -348.204, -517.279, 0, -393.101, -505.86, 0, -346.045, -506.682, 0, -358.375, -527.424, 0, -393.014, -527.283, 0, -393.111, -524.891, 0, -393.111, -524.891, 0, -354.238, -524.874, 0, -358.375, -527.424, 0, -427.653, -527.142, 0, -431.985, -524.908, 0, -393.111, -524.891, 0, -393.111, -524.891, 0, -393.014, -527.283, 0, -427.653, -527.142, 0, -435.862, -521.731, 0, -393.262, -521.716, 0, -431.985, -524.908, 0, -435.862, -521.731, 0, -354.238, -524.874, 0, -393.111, -524.891, 0, -393.262, -521.716, 0, -350.661, -521.701, 0, -183.41, -190.469, 0, -202.939, -210.621, 0, -232.873, -172.071, 0, -211.498, -151.152, 0, -262.807, -133.52, 0, -239.586, -111.835, 0]; +const indices = [0, 1, 2, 2, 3, 0, 2, 1, 4, 4, 5, 2, 6, 7, 2, 2, 5, 6, 8, 3, 2, 2, 7, 8, 6, 11, 9, 9, 7, 6, 8, 7, 9, 9, 10, 8, 12, 13, 9, 9, 11, 12, 14, 10, 9, 9, 13, 14, 12, 17, 15, 15, 13, 12, 14, 13, 15, 15, 16, 14, 18, 19, 15, 15, 17, 18, 20, 16, 15, 15, 19, 20, 18, 23, 21, 21, 19, 18, 20, 19, 21, 21, 22, 20, 24, 25, 21, 21, 23, 24, 26, 22, 21, 21, 25, 26, 24, 29, 27, 27, 25, 24, 26, 25, 27, 27, 28, 26, 30, 31, 27, 27, 29, 30, 32, 28, 27, 27, 31, 32, 30, 35, 33, 33, 31, 30, 32, 31, 33, 33, 34, 32, 36, 37, 33, 33, 35, 36, 38, 34, 33, 33, 37, 38, 36, 41, 39, 39, 37, 36, 38, 37, 39, 39, 40, 38, 42, 43, 39, 39, 41, 42, 44, 40, 39, 39, 43, 44, 42, 47, 45, 45, 43, 42, 44, 43, 45, 45, 46, 44, 48, 49, 45, 45, 47, 48, 50, 46, 45, 45, 49, 50, 48, 53, 51, 51, 49, 48, 50, 49, 51, 51, 52, 50, 54, 55, 51, 51, 53, 54, 56, 52, 51, 51, 55, 56, 54, 59, 57, 57, 55, 54, 56, 55, 57, 57, 58, 56, 60, 61, 57, 57, 59, 60, 62, 58, 57, 57, 61, 62, 60, 65, 63, 63, 61, 60, 62, 61, 63, 63, 64, 62, 66, 67, 63, 63, 65, 66, 68, 64, 63, 63, 67, 68, 66, 71, 69, 69, 67, 66, 68, 67, 69, 69, 70, 68, 72, 73, 69, 69, 71, 72, 74, 70, 69, 69, 73, 74, 72, 77, 75, 75, 73, 72, 74, 73, 75, 75, 76, 74, 78, 79, 75, 75, 77, 78, 80, 76, 75, 75, 79, 80, 78, 83, 81, 81, 79, 78, 80, 79, 81, 81, 82, 80, 84, 85, 81, 81, 83, 84, 86, 82, 81, 81, 85, 86, 84, 89, 87, 87, 85, 84, 86, 85, 87, 87, 88, 86, 90, 91, 87, 87, 89, 90, 92, 88, 87, 87, 91, 92, 90, 95, 93, 93, 91, 90, 92, 91, 93, 93, 94, 92, 96, 97, 93, 93, 95, 96, 98, 94, 93, 93, 97, 98, 96, 101, 99, 99, 97, 96, 98, 97, 99, 99, 100, 98, 102, 103, 99, 99, 101, 102, 104, 100, 99, 99, 103, 104, 102, 107, 105, 105, 103, 102, 104, 103, 105, 105, 106, 104, 108, 109, 105, 105, 107, 108, 110, 106, 105, 105, 109, 110, 111, 112, 113, 123, 124, 113, 113, 125, 123, 126, 127, 113, 113, 124, 126, 123, 272, 270, 270, 124, 123, 126, 124, 270, 270, 269, 126, 268, 269, 270, 270, 271, 268, 273, 271, 270, 270, 272, 273, 114, 115, 116, 117, 118, 119, 120, 121, 122, 128, 129, 130, 130, 142, 143, 131, 132, 133, 140, 141, 131, 134, 135, 136, 136, 146, 147, 137, 138, 139, 144, 145, 137, 148, 149, 150, 150, 162, 163, 151, 152, 153, 160, 161, 151, 154, 155, 156, 156, 166, 167, 157, 158, 159, 164, 165, 157, 168, 169, 170, 170, 182, 183, 171, 172, 173, 180, 181, 171, 174, 175, 176, 176, 186, 187, 177, 178, 179, 184, 185, 177, 188, 189, 190, 190, 202, 203, 191, 192, 193, 200, 201, 191, 194, 195, 196, 196, 206, 207, 197, 198, 199, 204, 205, 197, 208, 209, 210, 210, 222, 223, 211, 212, 213, 220, 221, 211, 214, 215, 216, 216, 226, 227, 217, 218, 219, 224, 225, 217, 228, 229, 230, 228, 264, 250, 230, 242, 243, 248, 249, 250, 250, 262, 263, 231, 232, 233, 240, 241, 231, 234, 235, 236, 236, 246, 247, 237, 238, 239, 244, 245, 237, 251, 252, 253, 260, 261, 251, 254, 255, 256, 257, 258, 259, 265, 266, 267, 274, 275, 276, 286, 287, 276, 276, 288, 286, 289, 290, 276, 276, 287, 289, 286, 435, 433, 433, 287, 286, 289, 287, 433, 433, 432, 289, 431, 432, 433, 433, 434, 431, 436, 434, 433, 433, 435, 436, 277, 278, 279, 280, 281, 282, 283, 284, 285, 291, 292, 293, 293, 305, 306, 294, 295, 296, 303, 304, 294, 297, 298, 299, 299, 309, 310, 300, 301, 302, 307, 308, 300, 311, 312, 313, 313, 325, 326, 314, 315, 316, 323, 324, 314, 317, 318, 319, 319, 329, 330, 320, 321, 322, 327, 328, 320, 331, 332, 333, 333, 345, 346, 334, 335, 336, 343, 344, 334, 337, 338, 339, 339, 349, 350, 340, 341, 342, 347, 348, 340, 351, 352, 353, 353, 365, 366, 354, 355, 356, 363, 364, 354, 357, 358, 359, 359, 369, 370, 360, 361, 362, 367, 368, 360, 371, 372, 373, 373, 385, 386, 374, 375, 376, 383, 384, 374, 377, 378, 379, 379, 389, 390, 380, 381, 382, 387, 388, 380, 391, 392, 393, 391, 427, 413, 393, 405, 406, 411, 412, 413, 413, 425, 426, 394, 395, 396, 403, 404, 394, 397, 398, 399, 399, 409, 410, 400, 401, 402, 407, 408, 400, 414, 415, 416, 423, 424, 414, 417, 418, 419, 420, 421, 422, 428, 429, 430]; + +let geom = new CGL.Geometry(op.name); +let mesh = null; +scale.onChange = build; +build(); + +function build() +{ + let sc = scale.get(); + let vertices = verts.slice(0); + + for (let i = 0; i < vertices.length; i++) vertices[i] = vertices[i] / 1000 * sc; + + geom.vertices = vertices; + geom.mapTexCoords2d(); + geom.verticesIndices = indices; + geom.tangents = vertices.map(function (v, i) { return i % 3 == 0 ? 1 : 0; }); + geom.biTangents = vertices.map(function (v, i) { return i % 3 == 1 ? 1 : 0; }); + geom.vertexNormals = vertices.map(function (v, i) { return i % 3 == 2 ? 1 : 0; }); + geomOut.set(null); + geomOut.set(geom); + + if (!mesh)mesh = new CGL.Mesh(op.patch.cgl, geom); + else mesh.setGeom(geom); +} + +render.onTriggered = function () +{ + if (draw.get()) + mesh.render(op.patch.cgl.getShader()); + trigger.trigger(); +}; + + +}; + +Ops.Gl.Meshes.CablesLogo.prototype = new CABLES.Op(); +CABLES.OPS["d53b5d7b-99b5-420e-b5a1-2d7c2c1f1ebb"]={f:Ops.Gl.Meshes.CablesLogo,objName:"Ops.Gl.Meshes.CablesLogo"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Circle +// +// ************************************************************** + +Ops.Gl.Meshes.Circle = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + radius = op.inValue("radius", 0.5), + innerRadius = op.inValueSlider("innerRadius", 0), + segments = op.inValueInt("segments", 40), + percent = op.inValueSlider("percent", 1), + steps = op.inValue("steps", 0), + invertSteps = op.inValueBool("invertSteps", false), + mapping = op.inSwitch("mapping", ["flat", "round"]), + drawSpline = op.inValueBool("Spline", false), + inDraw = op.inValueBool("Draw", true), + trigger = op.outTrigger("trigger"), + geomOut = op.outObject("geometry", null, "geometry"); + +op.setPortGroup("Size", [radius, innerRadius]); +op.setPortGroup("Display", [percent, steps, invertSteps]); + +mapping.set("flat"); + +mapping.onChange = + segments.onChange = + radius.onChange = + innerRadius.onChange = + percent.onChange = + steps.onChange = + invertSteps.onChange = + drawSpline.onChange = calcLater; + +geomOut.ignoreValueSerialize = true; +const cgl = op.patch.cgl; + +let geom = new CGL.Geometry("circle"); +let mesh = null; +const lastSegs = -1; + +let oldPrim = 0; +let shader = null; +let needsCalc = true; + +render.onTriggered = renderMesh; + +op.preRender = () => +{ + renderMesh(); +}; + +function renderMesh() +{ + if (!CGL.TextureEffect.checkOpNotInTextureEffect(op)) return; + + if (needsCalc)calc(); + shader = cgl.getShader(); + if (!shader) return; + oldPrim = shader.glPrimitive; + + if (drawSpline.get()) shader.glPrimitive = cgl.gl.LINE_STRIP; + + if (inDraw.get())mesh.render(shader); + trigger.trigger(); + + shader.glPrimitive = oldPrim; +} + +function calc() +{ + const segs = Math.max(3, Math.floor(segments.get())); + + geom.clear(); + + const faces = []; + const texCoords = []; + const vertexNormals = []; + const tangents = []; + const biTangents = []; + + let i = 0, degInRad = 0; + let oldPosX = 0, oldPosY = 0; + let oldPosXTexCoord = 0, oldPosYTexCoord = 0; + + let oldPosXIn = 0, oldPosYIn = 0; + let oldPosXTexCoordIn = 0, oldPosYTexCoordIn = 0; + + let posxTexCoordIn = 0, posyTexCoordIn = 0; + let posxTexCoord = 0, posyTexCoord = 0; + let posx = 0, posy = 0; + + const perc = Math.max(0.0, percent.get()); + const verts = []; + + if (drawSpline.get()) + { + let lastX = 0; + let lastY = 0; + const tc = []; + for (i = 0; i <= segs * perc; i++) + { + degInRad = (360 / segs) * i * CGL.DEG2RAD; + posx = Math.cos(degInRad) * radius.get(); + posy = Math.sin(degInRad) * radius.get(); + + posyTexCoord = 0.5; + + if (i > 0) + { + verts.push(lastX); + verts.push(lastY); + verts.push(0); + posxTexCoord = 1.0 - (i - 1) / segs; + + tc.push(posxTexCoord, posyTexCoord); + } + verts.push(posx); + verts.push(posy); + verts.push(0); + + lastX = posx; + lastY = posy; + } + geom.setPointVertices(verts); + } + else + if (innerRadius.get() <= 0) + { + for (i = 0; i <= segs * perc; i++) + { + degInRad = (360 / segs) * i * CGL.DEG2RAD; + posx = Math.cos(degInRad) * radius.get(); + posy = Math.sin(degInRad) * radius.get(); + + if (mapping.get() == "flat") + { + posxTexCoord = (Math.cos(degInRad) + 1.0) / 2; + posyTexCoord = 1.0 - (Math.sin(degInRad) + 1.0) / 2; + posxTexCoordIn = 0.5; + posyTexCoordIn = 0.5; + } + else if (mapping.get() == "round") + { + posxTexCoord = 1.0 - i / segs; + posyTexCoord = 0; + posxTexCoordIn = posxTexCoord; + posyTexCoordIn = 1; + } + + faces.push( + [posx, posy, 0], + [oldPosX, oldPosY, 0], + [0, 0, 0] + ); + + texCoords.push(posxTexCoord, posyTexCoord, oldPosXTexCoord, oldPosYTexCoord, posxTexCoordIn, posyTexCoordIn); + vertexNormals.push(0, 0, 1, 0, 0, 1, 0, 0, 1); + tangents.push(1, 0, 0, 1, 0, 0, 1, 0, 0); + biTangents.push(0, 1, 0, 0, 1, 0, 0, 1, 0); + + oldPosXTexCoord = posxTexCoord; + oldPosYTexCoord = posyTexCoord; + + oldPosX = posx; + oldPosY = posy; + } + + geom = CGL.Geometry.buildFromFaces(faces, "circle"); + geom.vertexNormals = vertexNormals; + geom.tangents = tangents; + geom.biTangents = biTangents; + geom.texCoords = texCoords; + } + else + { + let count = 0; + const numSteps = segs * perc; + const pos = 0; + + for (i = 0; i <= numSteps; i++) + { + count++; + + degInRad = (360 / segs) * i * CGL.DEG2RAD; + posx = Math.cos(degInRad) * radius.get(); + posy = Math.sin(degInRad) * radius.get(); + + const posxIn = Math.cos(degInRad) * innerRadius.get() * radius.get(); + const posyIn = Math.sin(degInRad) * innerRadius.get() * radius.get(); + + if (mapping.get() == "round") + { + posxTexCoord = 1.0 - i / segs; + posyTexCoord = 0; + posxTexCoordIn = posxTexCoord; + posyTexCoordIn = 1; + } + + if (steps.get() === 0.0 || + (count % parseInt(steps.get(), 10) === 0 && !invertSteps.get()) || + (count % parseInt(steps.get(), 10) !== 0 && invertSteps.get())) + { + faces.push( + [posx, posy, 0], + [oldPosX, oldPosY, 0], + [posxIn, posyIn, 0] + ); + + faces.push( + [posxIn, posyIn, 0], + [oldPosX, oldPosY, 0], + [oldPosXIn, oldPosYIn, 0] + ); + + texCoords.push( + posxTexCoord, 0, + oldPosXTexCoord, 0, + posxTexCoordIn, 1, + + posxTexCoord, 1, + oldPosXTexCoord, 0, + oldPosXTexCoordIn, 1); + + vertexNormals.push( + 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 1, 0, 0, 1 + ); + tangents.push( + 1, 0, 0, 1, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 0, 0 + ); + biTangents.push( + 0, 1, 0, 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, 0, 1, 0 + ); + } + + oldPosXTexCoordIn = posxTexCoordIn; + oldPosYTexCoordIn = posyTexCoordIn; + + oldPosXTexCoord = posxTexCoord; + oldPosYTexCoord = posyTexCoord; + + oldPosX = posx; + oldPosY = posy; + + oldPosXIn = posxIn; + oldPosYIn = posyIn; + } + + geom = CGL.Geometry.buildFromFaces(faces, "circle"); + geom.vertexNormals = vertexNormals; + geom.tangents = tangents; + geom.biTangents = biTangents; + + if (mapping.get() == "flat") geom.mapTexCoords2d(); + else geom.texCoords = texCoords; + } + + geomOut.set(null); + geomOut.set(geom); + + if (geom.vertices.length == 0) return; + if (mesh) mesh.dispose(); + mesh = null; + mesh = new CGL.Mesh(cgl, geom); + needsCalc = false; +} + +function calcLater() +{ + needsCalc = true; +} + +op.onDelete = function () +{ + if (mesh)mesh.dispose(); +}; + + +}; + +Ops.Gl.Meshes.Circle.prototype = new CABLES.Op(); +CABLES.OPS["4db917cc-2cef-43f4-83d5-38c4572fe943"]={f:Ops.Gl.Meshes.Circle,objName:"Ops.Gl.Meshes.Circle"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Cone +// +// ************************************************************** + +Ops.Gl.Meshes.Cone = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// adapted from the FreeGLUT project +const + render = op.inTrigger("render"), + slices = op.inValue("slices", 32), + stacks = op.inValue("stacks", 5), + radius = op.inValue("radius", 1), + height = op.inValue("height", 2), + active = op.inValueBool("Active", true), + trigger = op.outTrigger("trigger"), + geomOut = op.outObject("geometry"); + +geomOut.ignoreValueSerialize = true; + +const cgl = op.patch.cgl; +let mesh = null; +const geom = null; +let i = 0, j = 0, idx = 0, offset = 0; + +let needsRebuild = true; + +stacks.onChange = slices.onChange = radius.onChange = height.onChange = updateMeshLater; + +function updateMeshLater() +{ + needsRebuild = true; +} + +render.onTriggered = function () +{ + if (needsRebuild) updateMesh(); + if (active.get() && mesh) + { + mesh.render(cgl.getShader()); + } + else + { + trigger.trigger(); + return; + } + // mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +function updateMesh() +{ + let nstacks = Math.round(stacks.get()); + let nslices = Math.round(slices.get()); + if (nstacks < 1)nstacks = 1; + if (nslices < 3)nslices = 3; + const r = radius.get(); + generateCone(r, Math.max(0.01, height.get()), nstacks, nslices); + needsRebuild = false; +} + +function circleTable(n, halfCircle) +{ + let i; + /* Table size, the sign of n flips the circle direction */ + const size = Math.abs(n); + + /* Determine the angle between samples */ + const angle = (halfCircle ? 1 : 2) * Math.PI / n;// ( n === 0 ) ? 1 : n ; + + /* Allocate memory for n samples, plus duplicate of first entry at the end */ + const sint = []; + const cost = []; + + /* Compute cos and sin around the circle */ + sint[0] = 0.0; + cost[0] = 1.0; + + for (i = 1; i < size; i++) + { + sint[i] = Math.sin(angle * i); + cost[i] = Math.cos(angle * i); + } + + if (halfCircle) + { + sint[size] = 0.0; /* sin PI */ + cost[size] = -1.0; /* cos PI */ + } + else + { + /* Last sample is duplicate of the first (sin or cos of 2 PI) */ + sint[size] = sint[0]; + cost[size] = cost[0]; + } + return { "cost": cost, "sint": sint }; +} + +function generateCone(base, height, stacks, slices) +{ + let r = base; + let z = 0; + const geom = new CGL.Geometry(op.name); + geom.glPrimitive = cgl.gl.TRIANGLE_STRIP; + geom.tangents = []; + geom.biTangents = []; + + const table = circleTable(-slices + 1, false); + + const zStep = height / ((stacks > 0) ? stacks : 1); + const rStep = base / ((stacks > 0) ? stacks : 1); + + /* Scaling factors for vertex normals */ + const cosn = (height / Math.sqrt(height * height + base * base)); + const sinn = (base / Math.sqrt(height * height + base * base)); + const texCoords = []; + + /* bottom */ + geom.vertices[0] = 0; + geom.vertices[1] = 0; + geom.vertices[2] = z; + geom.vertexNormals[0] = 0; + geom.vertexNormals[1] = 0; + geom.vertexNormals[2] = -1; + geom.tangents.push(1, 0, 0); + geom.biTangents.push(0, 1, 0); + + idx = 3; // index carried over through all for loops - allows mesh buildup + /* other on bottom (get normals right) */ + + for (j = 0; j < slices; j++, idx += 3) + { + geom.vertices[idx] = table.cost[j] * r; + geom.vertices[idx + 1] = table.sint[j] * r; + geom.vertices[idx + 2] = z; + geom.vertexNormals[idx] = 0; + geom.vertexNormals[idx + 1] = 0; + geom.vertexNormals[idx + 2] = -1; + geom.tangents[idx] = 1; + geom.tangents[idx + 1] = 0; + geom.tangents[idx + 2] = 0; + geom.biTangents[idx] = 0; + geom.biTangents[idx + 1] = 1; + geom.biTangents[idx + 2] = 0; + } + + /* each stack */ + for (i = 0; i < stacks + 1; i++) + { + for (j = 0; j < slices; j++, idx += 3) + { + // gets texcoords from textured material + // xyz converts to xy for uv + texCoords[idx / 3 * 2] = 1 - j / slices; + texCoords[idx / 3 * 2 + 1] = 1 - i / stacks; + + geom.vertices[idx] = table.cost[j] * r; + geom.vertices[idx + 1] = table.sint[j] * r; + geom.vertices[idx + 2] = z; + geom.vertexNormals[idx] = table.cost[j] * cosn; + geom.vertexNormals[idx + 1] = table.sint[j] * cosn; + geom.vertexNormals[idx + 2] = sinn; + geom.tangents[idx] = -table.sint[j] * cosn; + geom.tangents[idx + 1] = table.cost[j] * cosn; + geom.tangents[idx + 2] = sinn; + geom.biTangents[idx] = table.sint[j] * cosn * sinn - table.cost[j] * cosn * sinn; + geom.biTangents[idx + 1] = sinn * (-table.sint[j] * cosn) - sinn * table.cost[j] * cosn; + geom.biTangents[idx + 2] = table.cost[j] * cosn * table.cost[j] * cosn - (-table.sint[j] * cosn) * table.sint[j] * cosn; + } + + z += zStep; + r -= rStep; + } + + /* top stack - bottom section */ + for (j = 0, idx = 0; j < slices; j++, idx += 2) + { + // makes the texture cartopol + texCoords[0] = (geom.vertices[0] / radius.get()) * 0.5 + 0.5; + texCoords[1] = 1 - (geom.vertices[1] / radius.get()) * 0.5 + 0.5; + + texCoords[j * 2 + 0] = (geom.vertices[(j) * 3] / radius.get()) * 0.5 + 0.5; + texCoords[j * 2 + 1] = 1 - (geom.vertices[(j) * 3 + 1] / radius.get()) * 0.5 + 0.5; + + geom.verticesIndices[idx] = 0; + geom.verticesIndices[idx + 1] = j + 1; /* 0 is top vertex, 1 is first for first stack */ + } + + geom.verticesIndices[idx] = 0; /* repeat first slice's idx for closing off shape */ + geom.verticesIndices[idx + 1] = 1; + + texCoords[0] = (geom.vertices[0] / radius.get()) * 0.5 + 0.5; + texCoords[1] = 1 - (geom.vertices[1] / radius.get()) * 0.5 + 0.5; + + texCoords[j * 2 + 0] = (geom.vertices[(j) * 3] / radius.get()) * 0.5 + 0.5; + texCoords[j * 2 + 1] = 1 - (geom.vertices[(j) * 3 + 1] / radius.get()) * 0.5 + 0.5; + + idx += 2; + + /* middle stacks: */ + /* Strip indices are relative to first index belonging to strip, NOT relative to first vertex/normal pair in array */ + for (i = 0; i < stacks; i++, idx += 2) + { + offset = 1 + (i + 1) * slices; /* triangle_strip indices start at 1 (0 is top vertex), and we advance one stack down as we go along */ + for (j = 0; j < slices; j++, idx += 2) + { + geom.verticesIndices[idx] = offset + j; + geom.verticesIndices[idx + 1] = offset + j + slices; + } + geom.verticesIndices[idx] = offset; /* repeat first slice's idx for closing off shape */ + geom.verticesIndices[idx + 1] = offset + slices; + } + + geom.setTexCoords(texCoords); + + mesh = new CGL.Mesh(cgl, geom); + geomOut.set(geom); +} + + +}; + +Ops.Gl.Meshes.Cone.prototype = new CABLES.Op(); +CABLES.OPS["b67027f5-8f56-4aa0-8bb3-18d42ff15ffe"]={f:Ops.Gl.Meshes.Cone,objName:"Ops.Gl.Meshes.Cone"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Corner +// +// ************************************************************** + +Ops.Gl.Meshes.Corner = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let render = op.inTrigger("Render"); +let width = op.inValueFloat("Width", 1); +let height = op.inValueFloat("Height", 1); +let thickness = op.inValueFloat("Thickness", -0.1); +let dodraw = op.inValueBool("Draw", true); +let pivotX = op.inValueSelect("pivot x", ["center", "left", "right"]); +let pivotY = op.inValueSelect("pivot y", ["center", "top", "bottom"]); + +let trigger = op.outTrigger("trigger"); +let geomOut = op.outObject("Geometry"); + +op.setPortGroup("Size", [width, height]); +op.setPortGroup("Align", [pivotX, pivotY]); + +let cgl = op.patch.cgl; +let mesh = null; +let geom = new CGL.Geometry(op.name); +geom.tangents = []; +geom.biTangents = []; + +pivotX.set("center"); +pivotY.set("center"); + +geomOut.ignoreValueSerialize = true; + +width.onChange = + pivotX.onChange = + pivotY.onChange = + height.onChange = + thickness.onChange = create; + +create(); + +render.onTriggered = function () +{ + if (dodraw.get()) mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +function create() +{ + let w = width.get(); + let h = height.get(); + let x = -w / 2; + let y = -h / 2; + let th = thickness.get(); + + let pivot = pivotX.get(); + if (pivot == "right") x = -w; + else if (pivot == "left") x = 0; + + pivot = pivotY.get(); + if (pivot == "top") y = -w; + else if (pivot == "bottom") y = 0; + + geom.vertices.length = 0; + geom.vertices.push( + x, y, 0, + x + w, y, 0, + x + w, y + h, 0, + x, y + h, 0, + x - th, y, 0, + x + w + th, y - th, 0, + x + w, y + h + th, 0, + x - th, y + h + th, 0 + ); + + if (geom.vertexNormals.length === 0) geom.vertexNormals = [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]; + if (geom.tangents.length === 0) geom.tangents = [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0]; + if (geom.biTangents.length === 0) geom.biTangents = [0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]; + + geom.verticesIndices = [7, 6, 3, 6, 2, 3, 0, 4, 3, 4, 7, 3]; + + if (geom.texCoords.length === 0) + { + const tc = []; + for (let i = 0; i < geom.vertices.length; i += 3) + { + tc[i / 3 * 2] = geom.vertices[i + 0] / w - 0.5; + tc[i / 3 * 2 + 1] = geom.vertices[i + 1] / h - 0.5; + } + geom.texCoords = tc; + } + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); + + geomOut.set(null); + geomOut.set(geom); +} + + +}; + +Ops.Gl.Meshes.Corner.prototype = new CABLES.Op(); +CABLES.OPS["7624c3bb-d22f-47f3-8581-2ecca73bb12f"]={f:Ops.Gl.Meshes.Corner,objName:"Ops.Gl.Meshes.Corner"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Cross +// +// ************************************************************** + +Ops.Gl.Meshes.Cross = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + size = op.inValue("Size", 1), + thick = op.inValue("Thickness", 0.25), + target = op.inValueBool("Crosshair"), + active = op.inValueBool("Active", true), + + trigger = op.outTrigger("Next"), + geomOut = op.outObject("Geometry"); + +const cgl = op.patch.cgl; +let geom = null; +let mesh = null; + +size.onChange = + thick.onChange = + target.onChange = buildMeshLater; + +render.onTriggered = function () +{ + if (!mesh)buildMesh(); + if (active.get() && mesh) mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +function buildMesh() +{ + if (!geom)geom = new CGL.Geometry("crossmesh"); + geom.clear(); + + let ext = size.get() / 2.0; + let thi = thick.get(); + + if (thi < 0.0) + { + thi = 0.0; + } + else if (thi > ext) + { + thi = ext; + } + + if (ext < 0.0) + { + ext = 0.0; + thi = 0.0; + } + + // center verts + let cx = thi; + let cy = thi; + + // o is outer verts from center + let ox = ext; + let oy = ext; + + geom.vertices = [ + // center piece + -cx, -cy, 0, // 0 + -cx, cy, 0, // 1 + cx, cy, 0, // 2 + cx, -cy, 0, // 3 + + // left piece + -ox, -cy, 0, // 4 + -ox, cy, 0, // 5 + -cx, cy, 0, // 6 + -cx, -cy, 0, // 7 + + // right piece + cx, -cy, 0, // 8 + cx, cy, 0, // 9 + ox, cy, 0, // 10 + ox, -cy, 0, // 11 + + // top piece + -cx, cy, 0, // 12 + -cx, oy, 0, // 13 + cx, oy, 0, // 14 + cx, cy, 0, // 15 + + // bottom piece + -cx, -oy, 0, // 12 + -cx, -cy, 0, // 13 + cx, -cy, 0, // 14 + cx, -oy, 0 // 15 + ]; + + let texCoords = []; + texCoords.length = (geom.vertices.length / 3.0) * 2.0; + + for (let i = 0; i < geom.vertices.length; i += 3) + { + let vx = (geom.vertices[i] / (ox) + 1) / 2; + let vy = (geom.vertices[i + 1] / (oy) + 1) / 2; + let index = (i / 3.0) * 2.0; + + texCoords[index] = vx; + texCoords[index + 1] = vy; + } + + geom.setTexCoords(texCoords); + geom.tangents = geom.vertices.map(function (v, i) { return i % 3 == 0 ? 1 : 0; }); + geom.biTangents = geom.vertices.map(function (v, i) { return i % 3 == 1 ? 1 : 0; }); + geom.vertexNormals = geom.vertices.map(function (v, i) { return i % 3 == 2 ? 1 : 0; }); + + geom.vertexNormals = [ + // center piece + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + + // left + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + // right + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + // top + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + // bottom + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0 + ]; + + if (target.get() == true) + { + // draws a crosshair + geom.verticesIndices = [ + // left + 4, 5, 6, 4, 6, 7, + // right + 8, 9, 10, 8, 10, 11, + // top + 12, 13, 14, 12, 14, 15, + // bottom + 16, 17, 18, 16, 18, 19 + ]; + } + else + { + // draws a solid cross + geom.verticesIndices = [ + // center + 0, 1, 2, 0, 2, 3, + // left + 4, 5, 6, 4, 6, 7, + // right + 8, 9, 10, 8, 10, 11, + // top + 12, 13, 14, 12, 14, 15, + // bottom + 16, 17, 18, 16, 18, 19 + ]; + } + + mesh = new CGL.Mesh(cgl, geom); + geomOut.set(null); + geomOut.set(geom); +} + +function buildMeshLater() +{ + if (mesh)mesh.dispose(); + mesh = null; +} + + +}; + +Ops.Gl.Meshes.Cross.prototype = new CABLES.Op(); +CABLES.OPS["eab5773b-dd10-4617-a8a0-e3990b10477a"]={f:Ops.Gl.Meshes.Cross,objName:"Ops.Gl.Meshes.Cross"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Cube_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.Cube_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + active = op.inValueBool("Render Mesh", true), + width = op.inValue("Width", 1), + len = op.inValue("Length", 1), + height = op.inValue("Height", 1), + center = op.inValueBool("Center", true), + mapping = op.inSwitch("Mapping", ["Side", "Cube +-"], "Side"), + mappingBias = op.inValue("Bias", 0), + inFlipX = op.inValueBool("Flip X", true), + sideTop = op.inValueBool("Top", true), + sideBottom = op.inValueBool("Bottom", true), + sideLeft = op.inValueBool("Left", true), + sideRight = op.inValueBool("Right", true), + sideFront = op.inValueBool("Front", true), + sideBack = op.inValueBool("Back", true), + trigger = op.outTrigger("Next"), + geomOut = op.outObject("geometry", null, "geometry"); + +const cgl = op.patch.cgl; +op.toWorkPortsNeedToBeLinked(render); + +op.setPortGroup("Mapping", [mapping, mappingBias, inFlipX]); +op.setPortGroup("Geometry", [width, height, len, center]); +op.setPortGroup("Sides", [sideTop, sideBottom, sideLeft, sideRight, sideFront, sideBack]); + +let geom = null, + mesh = null, + meshvalid = true, + needsRebuild = true; + +mappingBias.onChange = + inFlipX.onChange = + sideTop.onChange = + sideBottom.onChange = + sideLeft.onChange = + sideRight.onChange = + sideFront.onChange = + sideBack.onChange = + mapping.onChange = + width.onChange = + height.onChange = + len.onChange = + center.onChange = buildMeshLater; + +function buildMeshLater() +{ + needsRebuild = true; +} + +render.onLinkChanged = function () +{ + if (!render.isLinked()) + { + geomOut.set(null); + return; + } + buildMesh(); +}; + +render.onTriggered = function () +{ + if (needsRebuild)buildMesh(); + if (active.get() && mesh && meshvalid) mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +op.preRender = function () +{ + buildMesh(); + mesh.render(cgl.getShader()); +}; + +function buildMesh() +{ + if (!geom)geom = new CGL.Geometry("cubemesh"); + geom.clear(); + + let x = width.get(); + let nx = -1 * width.get(); + let y = height.get(); + let ny = -1 * height.get(); + let z = len.get(); + let nz = -1 * len.get(); + + if (!center.get()) + { + nx = 0; + ny = 0; + nz = 0; + } + else + { + x *= 0.5; + nx *= 0.5; + y *= 0.5; + ny *= 0.5; + z *= 0.5; + nz *= 0.5; + } + + if (mapping.get() == "Side") sideMappedCube(geom, x, y, z, nx, ny, nz); + else cubeMappedCube(geom, x, y, z, nx, ny, nz); + + geom.verticesIndices = []; + if (sideTop.get()) geom.verticesIndices.push(8, 9, 10, 8, 10, 11); // Top face + if (sideBottom.get()) geom.verticesIndices.push(12, 13, 14, 12, 14, 15); // Bottom face + if (sideLeft.get()) geom.verticesIndices.push(20, 21, 22, 20, 22, 23); // Left face + if (sideRight.get()) geom.verticesIndices.push(16, 17, 18, 16, 18, 19); // Right face + if (sideBack.get()) geom.verticesIndices.push(4, 5, 6, 4, 6, 7); // Back face + if (sideFront.get()) geom.verticesIndices.push(0, 1, 2, 0, 2, 3); // Front face + + if (geom.verticesIndices.length === 0) meshvalid = false; + else meshvalid = true; + + if (mesh)mesh.dispose(); + mesh = new CGL.Mesh(cgl, geom); + geomOut.set(null); + geomOut.set(geom); + + needsRebuild = false; +} + +op.onDelete = function () +{ + if (mesh)mesh.dispose(); +}; + +function sideMappedCube(geom, x, y, z, nx, ny, nz) +{ + geom.vertices = [ + // Front face + nx, ny, z, + x, ny, z, + x, y, z, + nx, y, z, + // Back face + nx, ny, nz, + nx, y, nz, + x, y, nz, + x, ny, nz, + // Top face + nx, y, nz, + nx, y, z, + x, y, z, + x, y, nz, + // Bottom face + nx, ny, nz, + x, ny, nz, + x, ny, z, + nx, ny, z, + // Right face + x, ny, nz, + x, y, nz, + x, y, z, + x, ny, z, + // zeft face + nx, ny, nz, + nx, ny, z, + nx, y, z, + nx, y, nz + ]; + + const bias = mappingBias.get(); + + let fone = 1.0; + let fzero = 0.0; + if (inFlipX.get()) + { + fone = 0.0; + fzero = 1.0; + } + + geom.setTexCoords([ + // Front face + fzero + bias, 1 - bias, + fone - bias, 1 - bias, + fone - bias, 0 + bias, + fzero + bias, 0 + bias, + // Back face + fone - bias, 1 - bias, + fone - bias, 0 + bias, + fzero + bias, 0 + bias, + fzero + bias, 1 - bias, + // Top face + fzero + bias, 0 + bias, + fzero + bias, 1 - bias, + fone - bias, 1 - bias, + fone - bias, 0 + bias, + // Bottom face + fone - bias, 0 + bias, + fzero + bias, 0 + bias, + fzero + bias, 1 - bias, + fone - bias, 1 - bias, + // Right face + fone - bias, 1 - bias, + fone - bias, 0 + bias, + fzero + bias, 0 + bias, + fzero + bias, 1 - bias, + // Left face + fzero + bias, 1 - bias, + fone - bias, 1 - bias, + fone - bias, 0 + bias, + fzero + bias, 0 + bias, + ]); + + geom.vertexNormals = new Float32Array([ + // Front face + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + + // Back face + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + + // Top face + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + + // Bottom face + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + + // Right face + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + + // Left face + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0 + ]); + geom.tangents = new Float32Array([ + + // front face + -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + // back face + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, + // top face + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, + // bottom face + -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + // right face + 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, + // left face + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1 + ]); + geom.biTangents = new Float32Array([ + // front face + 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, + // back face + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, + // top face + 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, + // bottom face + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + // right face + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, + // left face + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 + ]); +} + +function cubeMappedCube(geom, x, y, z, nx, ny, nz) +{ + geom.vertices = [ + // Front face + nx, ny, z, + x, ny, z, + x, y, z, + nx, y, z, + // Back face + nx, ny, nz, + nx, y, nz, + x, y, nz, + x, ny, nz, + // Top face + nx, y, nz, + nx, y, z, + x, y, z, + x, y, nz, + // Bottom face + nx, ny, nz, + x, ny, nz, + x, ny, z, + nx, ny, z, + // Right face + x, ny, nz, + x, y, nz, + x, y, z, + x, ny, z, + // zeft face + nx, ny, nz, + nx, ny, z, + nx, y, z, + nx, y, nz + ]; + + const sx = 0.25; + const sy = 1 / 3; + const bias = mappingBias.get(); + + let flipx = 0.0; + if (inFlipX.get()) flipx = 1.0; + + const tc = []; + tc.push( + // Front face Z+ + flipx + sx + bias, sy * 2 - bias, + flipx + sx * 2 - bias, sy * 2 - bias, + flipx + sx * 2 - bias, sy + bias, + flipx + sx + bias, sy + bias, + // Back face Z- + flipx + sx * 4 - bias, sy * 2 - bias, + flipx + sx * 4 - bias, sy + bias, + flipx + sx * 3 + bias, sy + bias, + flipx + sx * 3 + bias, sy * 2 - bias); + + if (inFlipX.get()) + tc.push( + // Top face + sx + bias, 0 - bias, + sx * 2 - bias, 0 - bias, + sx * 2 - bias, sy * 1 + bias, + sx + bias, sy * 1 + bias, + // Bottom face + sx + bias, sy * 3 + bias, + sx + bias, sy * 2 - bias, + sx * 2 - bias, sy * 2 - bias, + sx * 2 - bias, sy * 3 + bias + ); + + else + tc.push( + // Top face + sx + bias, 0 + bias, + sx + bias, sy * 1 - bias, + sx * 2 - bias, sy * 1 - bias, + sx * 2 - bias, 0 + bias, + // Bottom face + sx + bias, sy * 3 - bias, + sx * 2 - bias, sy * 3 - bias, + sx * 2 - bias, sy * 2 + bias, + sx + bias, sy * 2 + bias); + + tc.push( + // Right face + flipx + sx * 3 - bias, 1.0 - sy - bias, + flipx + sx * 3 - bias, 1.0 - sy * 2 + bias, + flipx + sx * 2 + bias, 1.0 - sy * 2 + bias, + flipx + sx * 2 + bias, 1.0 - sy - bias, + // Left face + flipx + sx * 0 + bias, 1.0 - sy - bias, + flipx + sx * 1 - bias, 1.0 - sy - bias, + flipx + sx * 1 - bias, 1.0 - sy * 2 + bias, + flipx + sx * 0 + bias, 1.0 - sy * 2 + bias); + + geom.setTexCoords(tc); + + geom.vertexNormals = [ + // Front face + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + + // Back face + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + + // Top face + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + + // Bottom face + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + + // Right face + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + + // Left face + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0 + ]; + geom.tangents = new Float32Array([ + // front face + -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + // back face + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, + // top face + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, + // bottom face + -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, + // right face + 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, + // left face + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1 + ]); + geom.biTangents = new Float32Array([ + // front face + 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, + // back face + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, + // top face + 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, + // bottom face + 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + // right face + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, + // left face + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 + ]); +} + + +}; + +Ops.Gl.Meshes.Cube_v2.prototype = new CABLES.Op(); +CABLES.OPS["37b92ba4-cea5-42ae-bf28-a513ca28549c"]={f:Ops.Gl.Meshes.Cube_v2,objName:"Ops.Gl.Meshes.Cube_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Cylinder_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.Cylinder_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inRender = op.inTrigger("render"), + inDraw = op.inValueBool("Draw", true), + inSegments = op.inValueInt("segments", 40), + inStacks = op.inValueInt("stacks", 1), + inLength = op.inValueFloat("length", 1), + inOuterRadius = op.inValueFloat("outer radius", 0.5), + inInnerRadius = op.inValueFloat("inner radius", 0), + inUVMode = op.inValueSelect("UV mode", ["simple", "atlas"], "simple"), + flipSideMapping = op.inValueBool("Flip Mapping", false), + inCaps = op.inValueBool("Caps", true), + inFlat = op.inValueBool("Flat Normals", false), + outTrigger = op.outTrigger("next"), + outGeometry = op.outObject("geometry"), + geom = new CGL.Geometry("cylinder"); + +const + TAU = Math.PI * 2, + cgl = op.patch.cgl; + +let needsRebuild = true; +let mesh = null; + +inUVMode.setUiAttribs({ "hidePort": true }); + + +op.preRender = buildMesh; + +function buildMesh() +{ + const flipTex = flipSideMapping.get(); + + const + segments = Math.max(inSegments.get(), 3) | 0, + innerRadius = Math.max(inInnerRadius.get(), 0), + outerRadius = Math.max(inOuterRadius.get(), innerRadius), + stacks = Math.max(inStacks.get(), inStacks.defaultValue) | 0, + length = inLength.get(), + stackLength = length / stacks, + segmentRadians = TAU / segments, + uvMode = inUVMode.get(); + let + positions = [], + normals = [], + tangents = [], + biTangents = [], + texcoords = [], + indices = [], + x, y, z, i, j, + a, d, o; + if (uvMode == "atlas") o = 0.5; + else o = 1; + + // for each stack + for ( + i = 0, z = -length / 2; + i <= stacks; + i++, z += stackLength + ) + { + // for each segment + for ( + j = a = 0; + j <= segments; + j++, a += segmentRadians + ) + { + positions.push( + (x = Math.sin(a)) * outerRadius, + (y = Math.cos(a)) * outerRadius, + z + ); + d = Math.sqrt(x * x + y * y); + x /= d; + y /= d; + normals.push(x, y, 0); + tangents.push(-y, x, 0); + biTangents.push(0, 0, 1); + + if (flipTex) + texcoords.push( + j / segments, + 1.0 - ((z / length + 0.5) * o) + ); + + else + texcoords.push( + (z / length + 0.5) * o, + j / segments + ); + } + } + + // create indices + for (j = 0; j < stacks; j++) + { + for ( + i = 0, d = j * (segments + 1); + i < segments; + i++, d++ + ) + { + a = d + 1; + indices.push( + d + (segments + 1), a, d, + d + (segments + 1), a + (segments + 1), a + ); + } + } + + // create inner shell + if (innerRadius) + { + d = positions.length; + for (i = j = 0; i < d; i += 3, j += 2) + { + positions.push( + (positions[i] / outerRadius) * innerRadius, + (positions[i + 1] / outerRadius) * innerRadius, + positions[i + 2] + ); + normals.push( + -normals[i], + -normals[i + 1], + 0 + ); + tangents.push( + -tangents[i], + -tangents[i + 1], + 0 + ); + biTangents.push( + 0, + -biTangents[i + 1], + -biTangents[i + 2] + ); + texcoords.push( + texcoords[j], + 1 - texcoords[j + 1] + ); + } + a = d / 3; + d = indices.length; + for (i = 0; i < d; i += 6) + { + indices.push( + a + indices[i], + a + indices[i + 2], + a + indices[i + 1], + a + indices[i + 3], + a + indices[i + 5], + a + indices[i + 4] + ); + } + + if (inCaps.get()) + { + // create caps + a = positions.length; + o = a / 2; + d = segments * 3; + + // cap positions + Array.prototype.push.apply(positions, positions.slice(0, d)); + Array.prototype.push.apply(positions, positions.slice(o, o + d)); + Array.prototype.push.apply(positions, positions.slice(o - d, o)); + Array.prototype.push.apply(positions, positions.slice(a - d, a)); + + // cap normals + d = segments * 2; + for (i = 0; i < d; i++) normals.push(0, 0, -1), tangents.push(-1, 0, 0), biTangents.push(0, -1, 0); + for (i = 0; i < d; i++) normals.push(0, 0, 1), tangents.push(1, 0, 0), biTangents.push(0, 1, 0); + + // cap uvs + if (uvMode == "atlas") + { + d = (innerRadius / outerRadius) * 0.5; + for (i = o = 0; i < segments; i++, o += segmentRadians) + texcoords.push( + Math.sin(o) * 0.25 + 0.75, + Math.cos(o) * 0.25 + 0.25 + ); + for (i = o = 0; i < segments; i++, o += segmentRadians) + texcoords.push( + (Math.sin(o) * d + 0.5) * 0.5 + 0.5, + (Math.cos(o) * d + 0.5) * 0.5 + ); + for (i = o = 0; i < segments; i++, o += segmentRadians) + texcoords.push( + Math.sin(o) * 0.25 + 0.75, + Math.cos(o) * 0.25 + 0.75 + ); + for (i = o = 0; i < segments; i++, o += segmentRadians) + texcoords.push( + (Math.sin(o) * d + 0.5) * 0.5 + 0.5, + (Math.cos(o) * d + 0.5) * 0.5 + 0.5 + ); + } + else + { + for (i = 0; i < d; i++) texcoords.push(0, 0); + for (i = 0; i < d; i++) texcoords.push(1, 1); + } + + // cap indices + for ( + i = 0, o = a / 3 + x; + i < segments - 1; + i++, o++ + ) + { + indices.push( + o + 1, o + segments, o, + o + segments + 1, o + segments, o + 1 + ); + } + indices.push( + o + segments, a / 3 + x, a / 3 + segments + x, + o + segments, o, a / 3 + x + ); + x += segments * 2; + for ( + i = 0, o = a / 3 + x; + i < segments - 1; + i++, o++ + ) + { + indices.push( + o, o + segments, o + 1, + o + 1, o + segments, o + segments + 1 + ); + } + indices.push( + a / 3 + segments + x, a / 3 + x, o + segments, + a / 3 + x, o, o + segments + ); + } + } + else + { + a = positions.length; + d = a / 3; + + positions.push(0, 0, -length / 2); + Array.prototype.push.apply(positions, positions.slice(0, segments * 3)); + for (i = 0; i <= segments; i++) normals.push(0, 0, -1), tangents.push(-1, 0, 0), biTangents.push(0, -1, 0); + + if (inCaps.get()) + { + positions.push(0, 0, length / 2); + Array.prototype.push.apply(positions, positions.slice(a - segments * 3, a)); + for (i = 0; i <= segments; i++) normals.push(0, 0, 1), tangents.push(1, 0, 0), biTangents.push(0, 1, 0); + if (uvMode == "atlas") + { + texcoords.push(0.75, 0.25); + for (i = a = 0; i < segments; i++, a += segmentRadians) + texcoords.push(Math.sin(a) * 0.25 + 0.75, Math.cos(a) * 0.25 + 0.25); + texcoords.push(0.75, 0.75); + for (i = a = 0; i < segments; i++, a += segmentRadians) + texcoords.push(Math.sin(a) * 0.25 + 0.75, Math.cos(a) * 0.25 + 0.75); + } + else + { + for (i = 0; i <= segments; i++) texcoords.push(0, 0); + for (i = 0; i <= segments; i++) texcoords.push(1, 1); + } + indices.push(d + 1, d, d + segments); + for (i = 1; i < segments; i++) + indices.push(d, d + i, d + i + 1); + d += segments + 1; + indices.push(d, d + 1, d + segments); + for (i = 1; i < segments; i++) + indices.push(d, d + i + 1, d + i); + d += segments + 1; + } + } + + // set geometry + geom.clear(); + geom.vertices = positions; + geom.texCoords = texcoords; + geom.vertexNormals = normals; + geom.tangents = tangents; + geom.biTangents = biTangents; + geom.verticesIndices = indices; + + if (inFlat.get()) geom.unIndex(); + + outGeometry.set(null); + outGeometry.set(geom); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); + + needsRebuild = false; +} + +// set event handlers +inRender.onTriggered = function () +{ + if (needsRebuild) buildMesh(); + if (inDraw.get()) mesh.render(cgl.getShader()); + outTrigger.trigger(); +}; + +inSegments.onChange = +inOuterRadius.onChange = +inInnerRadius.onChange = +inCaps.onChange = +inLength.onChange = +flipSideMapping.onChange = +inStacks.onChange = +inFlat.onChange = +inUVMode.onChange = function () +{ + // only calculate once, even after multiple settings could were changed + needsRebuild = true; +}; + +// set lifecycle handlers +op.onDelete = function () { if (mesh)mesh.dispose(); }; + + +}; + +Ops.Gl.Meshes.Cylinder_v2.prototype = new CABLES.Op(); +CABLES.OPS["2899ad67-1e64-4692-af2a-c3b9078f1b5f"]={f:Ops.Gl.Meshes.Cylinder_v2,objName:"Ops.Gl.Meshes.Cylinder_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.FloorGrid +// +// ************************************************************** + +Ops.Gl.Meshes.FloorGrid = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"grid_frag":"IN vec4 posColor;\nIN vec3 posFrag;\n\nvoid main()\n{\n outColor=posColor;\n outColor.a*=(1.0-(length(posFrag)/30.0));\n}","grid_vert":"IN vec3 vPosition;\nIN vec3 attrVertNormal;\nIN vec2 attrTexCoord;\n\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\nOUT vec4 posColor;\nOUT vec3 posFrag;\n\nvoid main()\n{\n vec4 pos = vec4( vPosition, 1. );\n mat4 mMatrix=modelMatrix;\n\n mat4 mvMatrix=viewMatrix*mMatrix;\n posFrag=vPosition;\n posColor=vec4(0.6,0.6,0.6,0.4);\n\n if(pos.x==0.0) posColor=vec4(0.3,0.3,1.0,1.0);\n else if(pos.y==0.0 && pos.z==0.0) posColor=vec4(1.0,0.3,0.3,1.0);\n else if(mod(pos.z,10.0)==0.0 && mod(pos.x,10.0)==0.0 ) posColor.a=1.0;\n\n if(pos.y>0.0 && pos.x==0.0) posColor=vec4(0.3,1.0,0.3,1.0);\n\n gl_Position = projMatrix * mvMatrix * pos;\n}\n",}; +const + render = op.inTrigger("Render"), + inActive = op.inBool("Active", true), + next = op.outTrigger("Next"); + +const num = 100; + +const cgl = op.patch.cgl; +let mesh = null; + +const shader = new CGL.Shader(cgl, "gridMaterial"); +shader.setSource(attachments.grid_vert, attachments.grid_frag); + +function init() +{ + let geomVertical = new CGL.Geometry(op.name); + + const space = 1.0; + let l = space * num / 2; + + let tc = []; + + for (var i = -num / 2; i < num / 2 + 1; i++) + { + geomVertical.vertices.push(-l); + geomVertical.vertices.push(0); + geomVertical.vertices.push(i * space); + + geomVertical.vertices.push(l); + geomVertical.vertices.push(0); + geomVertical.vertices.push(i * space); + + geomVertical.vertices.push(i * space); + geomVertical.vertices.push(0); + geomVertical.vertices.push(-l); + + geomVertical.vertices.push(i * space); + geomVertical.vertices.push(0); + geomVertical.vertices.push(l); + + if (i == 0) + { + tc.push(0, 1); + tc.push(0, 1); + tc.push(0, 0.5); + tc.push(0, 0.5); + } + else + { + tc.push(0, 0); + tc.push(0, 0); + tc.push(0, 0); + tc.push(0, 0); + } + } + + geomVertical.vertices.push(0); + geomVertical.vertices.push(0.001); + geomVertical.vertices.push(0); + + geomVertical.vertices.push(0); + geomVertical.vertices.push(10); + geomVertical.vertices.push(0); + + tc.push(0, 0, 0, 0); + + for (var i = 0; i <= 10; i++) + { + geomVertical.vertices.push(-0.25); + geomVertical.vertices.push(i); + geomVertical.vertices.push(0); + + geomVertical.vertices.push(0.25); + geomVertical.vertices.push(i); + geomVertical.vertices.push(0); + + tc.push(0, 0, 0, 0); + } + + geomVertical.setTexCoords(tc); + geomVertical.calculateNormals(); + + if (!mesh) mesh = new CGL.Mesh(cgl, geomVertical); + else mesh.setGeom(geomVertical); +} + +render.onTriggered = function () +{ + if (!mesh)init(); + + if (cgl.frameStore.shadowPass) return next.trigger(); + + cgl.pushShader(shader); + if (!shader) return; + + let oldPrim = shader.glPrimitive; + + shader.glPrimitive = cgl.gl.LINES; + + if (inActive.get()) mesh.render(shader); + cgl.popShader(); + + shader.glPrimitive = oldPrim; + + next.trigger(); +}; + + +}; + +Ops.Gl.Meshes.FloorGrid.prototype = new CABLES.Op(); +CABLES.OPS["645b3877-4fdd-42e5-a369-d9506a65e2f0"]={f:Ops.Gl.Meshes.FloorGrid,objName:"Ops.Gl.Meshes.FloorGrid"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.FreeFormPlane +// +// ************************************************************** + +Ops.Gl.Meshes.FreeFormPlane = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + + x1 = op.inValue("x 1", -1), + y1 = op.inValue("y 1", 1), + z1 = op.inValue("z 1", 0), + + x2 = op.inValue("x 2", 1), + y2 = op.inValue("y 2", 1), + z2 = op.inValue("z 2", 0), + + x3 = op.inValue("x 3", -1), + y3 = op.inValue("y 3", -1), + z3 = op.inValue("z 3", 0), + + x4 = op.inValue("x 4", 1), + y4 = op.inValue("y 4", -1), + z4 = op.inValue("z 4", 0), + + tcx1 = op.inValue("tc x 1", 0), + tcy1 = op.inValue("tc y 1", 1), + + tcx2 = op.inValue("tc x 2", 1), + tcy2 = op.inValue("tc y 2", 1), + + tcx3 = op.inValue("tc x 3", 0), + tcy3 = op.inValue("tc y 3", 0), + + tcx4 = op.inValue("tc x 4", 1), + tcy4 = op.inValue("tc y 4", 0), + trigger = op.outTrigger("trigger"); + +let geom = new CGL.Geometry(op.name); +let mesh = null; +let cgl = op.patch.cgl; + +let arrverts = []; +arrverts.length = 12; +let verts = new Float32Array(arrverts); +let indices = [2, 1, 0, 1, 2, 3]; +let tc = new Float32Array([0, 0, 0, 0, 0, 0, 0, 0]); + +let geomOut = op.addOutPort(new CABLES.Port(op, "geometry", CABLES.OP_PORT_TYPE_OBJECT)); +geomOut.ignoreValueSerialize = true; + +tcx1.onChange = + tcy1.onChange = + tcx2.onChange = + tcy2.onChange = + tcx3.onChange = + tcy3.onChange = + tcx4.onChange = + tcy4.onChange = + x1.onChange = + x2.onChange = + x3.onChange = + x4.onChange = + y1.onChange = + y2.onChange = + y3.onChange = + y4.onChange = + z1.onChange = + z2.onChange = + z3.onChange = + z4.onChange = rebuild; + +rebuild(); + +render.onTriggered = function () +{ + mesh.render(cgl.getShader()); + + if (op.isCurrentUiOp()) + { + gui.setTransformGizmo({ "posX": x1, "posY": y1, "posZ": z1 }, 0); + gui.setTransformGizmo({ "posX": x2, "posY": y2, "posZ": z2 }, 1); + gui.setTransformGizmo({ "posX": x3, "posY": y3, "posZ": z3 }, 2); + gui.setTransformGizmo({ "posX": x4, "posY": y4, "posZ": z4 }, 3); + } + + trigger.trigger(); +}; + +function rebuild() +{ + verts[0] = x1.get(); + verts[1] = y1.get(); + verts[2] = z1.get(); + + verts[3] = x2.get(); + verts[4] = y2.get(); + verts[5] = z2.get(); + + verts[6] = x3.get(); + verts[7] = y3.get(); + verts[8] = z3.get(); + + verts[9] = x4.get(); + verts[10] = y4.get(); + verts[11] = z4.get(); + + // var tc=[0,0, 1,0, 0,1, 1,1]; + + tc[0] = tcx1.get(); + tc[1] = tcy1.get(); + + tc[2] = tcx2.get(); + tc[3] = tcy2.get(); + + tc[4] = tcx3.get(); + tc[5] = tcy3.get(); + + tc[6] = tcx4.get(); + tc[7] = tcy4.get(); + + geom.vertices = verts; + geom.texCoords = tc; + geom.verticesIndices = indices; + geom.calcNormals(true); + geom.calcTangentsBitangents(); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); + + geomOut.set(null); + geomOut.set(geom); +} + + +}; + +Ops.Gl.Meshes.FreeFormPlane.prototype = new CABLES.Op(); +CABLES.OPS["7f03c044-ae95-4273-8d90-0e1c91ecffc7"]={f:Ops.Gl.Meshes.FreeFormPlane,objName:"Ops.Gl.Meshes.FreeFormPlane"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.FullscreenRectangle +// +// ************************************************************** + +Ops.Gl.Meshes.FullscreenRectangle = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"shader_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\n\nvoid main()\n{\n outColor= texture(tex,texCoord);\n}\n\n","shader_vert":"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\n\nOUT vec2 texCoord;\nIN vec2 attrTexCoord;\n\nvoid main()\n{\n vec4 pos=vec4(vPosition, 1.0);\n\n texCoord=vec2(attrTexCoord.x,(1.0-attrTexCoord.y));\n\n gl_Position = projMatrix * mvMatrix * pos;\n}\n",}; +const + render = op.inTrigger("render"), + inScale = op.inSwitch("Scale", ["Stretch", "Fit"], "Fit"), + flipY = op.inValueBool("Flip Y"), + flipX = op.inValueBool("Flip X"), + inTexture = op.inTexture("Texture"), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +let mesh = null; +let geom = new CGL.Geometry("fullscreen rectangle"); +let x = 0, y = 0, z = 0, w = 0, h = 0; + +flipX.onChange = rebuildFlip; +flipY.onChange = rebuildFlip; +render.onTriggered = doRender; +inTexture.onLinkChanged = updateUi; +op.toWorkPortsNeedToBeLinked(render); + +const shader = new CGL.Shader(cgl, "fullscreenrectangle"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); + +shader.setSource(attachments.shader_vert, attachments.shader_frag); +shader.fullscreenRectUniform = new CGL.Uniform(shader, "t", "tex", 0); +shader.aspectUni = new CGL.Uniform(shader, "f", "aspectTex", 0); + +let useShader = false; +let updateShaderLater = true; +let fitImageAspect = false; +let oldVp = []; + +updateUi(); + +inTexture.onChange = function () +{ + updateShaderLater = true; +}; + +function updateUi() +{ + if (!CABLES.UI) return; + flipY.setUiAttribs({ "greyout": !inTexture.isLinked() }); + flipX.setUiAttribs({ "greyout": !inTexture.isLinked() }); + inScale.setUiAttribs({ "greyout": !inTexture.isLinked() }); +} + +function updateShader() +{ + let tex = inTexture.get(); + if (tex) useShader = true; + else useShader = false; +} + +op.preRender = function () +{ + updateShader(); + shader.bind(); + if (mesh)mesh.render(shader); + doRender(); +}; + +inScale.onChange = () => +{ + fitImageAspect = inScale.get() == "Fit"; +}; + +function doRender() +{ + if (cgl.getViewPort()[2] != w || cgl.getViewPort()[3] != h || !mesh) rebuild(); + + if (updateShaderLater) updateShader(); + + cgl.pushPMatrix(); + mat4.identity(cgl.pMatrix); + mat4.ortho(cgl.pMatrix, 0, w, h, 0, -10.0, 1000); + + cgl.pushModelMatrix(); + mat4.identity(cgl.mMatrix); + + cgl.pushViewMatrix(); + mat4.identity(cgl.vMatrix); + + if (fitImageAspect && inTexture.get()) + { + const rat = inTexture.get().width / inTexture.get().height; + + let _h = h; + let _w = h * rat; + + if (_w > w) + { + _h = w * 1 / rat; + _w = w; + } + + oldVp[0] = cgl.getViewPort()[0]; + oldVp[1] = cgl.getViewPort()[1]; + oldVp[2] = cgl.getViewPort()[2]; + oldVp[3] = cgl.getViewPort()[3]; + + cgl.setViewPort((w - _w) / 2, (h - _h) / 2, _w, _h); + // cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + } + + if (useShader) + { + if (inTexture.get()) + cgl.setTexture(0, inTexture.get().tex); + + mesh.render(shader); + } + else + { + mesh.render(cgl.getShader()); + } + + cgl.gl.clear(cgl.gl.DEPTH_BUFFER_BIT); + + cgl.popPMatrix(); + cgl.popModelMatrix(); + cgl.popViewMatrix(); + + if (fitImageAspect && inTexture.get()) + cgl.setViewPort(oldVp[0], oldVp[1], oldVp[2], oldVp[3]); + + trigger.trigger(); +} + +function rebuildFlip() +{ + mesh = null; +} + +function rebuild() +{ + const currentViewPort = cgl.getViewPort(); + + if (currentViewPort[2] == w && currentViewPort[3] == h && mesh) return; + + let xx = 0, xy = 0; + + w = currentViewPort[2]; + h = currentViewPort[3]; + + geom.vertices = new Float32Array([ + xx + w, xy + h, 0.0, + xx, xy + h, 0.0, + xx + w, xy, 0.0, + xx, xy, 0.0 + ]); + + let tc = null; + + if (flipY.get()) + tc = new Float32Array([ + 1.0, 0.0, + 0.0, 0.0, + 1.0, 1.0, + 0.0, 1.0 + ]); + else + tc = new Float32Array([ + 1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 0.0, 0.0 + ]); + + if (flipX.get()) + { + tc[0] = 0.0; + tc[2] = 1.0; + tc[4] = 0.0; + tc[6] = 1.0; + } + + geom.setTexCoords(tc); + + geom.verticesIndices = new Uint16Array([ + 2, 1, 0, + 3, 1, 2 + ]); + + geom.vertexNormals = new Float32Array([ + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + ]); + geom.tangents = new Float32Array([ + -1, 0, 0, + -1, 0, 0, + -1, 0, 0, + -1, 0, 0]); + geom.biTangents == new Float32Array([ + 0, -1, 0, + 0, -1, 0, + 0, -1, 0, + 0, -1, 0]); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); +} + + +}; + +Ops.Gl.Meshes.FullscreenRectangle.prototype = new CABLES.Op(); +CABLES.OPS["255bd15b-cc91-4a12-9b4e-53c710cbb282"]={f:Ops.Gl.Meshes.FullscreenRectangle,objName:"Ops.Gl.Meshes.FullscreenRectangle"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.GeometryToTexture_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.GeometryToTexture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"fragpos_frag":"col=vec4(MOD_pos.xyz,1.0);\n","vertpos_vert":"\n#ifdef MOD_ATTRIB_POS\nMOD_pos=pos.xyz;\n#endif\n#ifdef MOD_ATTRIB_NORMAL\nMOD_pos=norm.xyz;\n#endif\n#ifdef MOD_ATTRIB_TC\nMOD_pos=vec3(attrTexCoord,1.0);\n#endif\n\nfloat tx=mod(attrVertIndex,MOD_texSize)+1.0/MOD_texSize;\nfloat ty=floor(attrVertIndex/MOD_texSize);\n\ngl_PointSize=1.0;\n\npos=vec4(tx,ty+1.0,0.0,1.0);\n",}; +const + exec = op.inTrigger("Render"), + inGeom = op.inObject("Geometry", null, "geometry"), + + inOrder = op.inDropDown("Order", ["Sequential", "Random", "Vertex X", "Vertex Y", "Vertex Z"], "Sequential"), + inAttrib = op.inSwitch("Content", ["Vertex Pos", "Normals", "TexCoords"], "Vertex Pos"), + + inSize = op.inSwitch("Size", ["Auto", "Manual"], "Auto"), + inWidth = op.inValueInt("Tex Width", 256), + tfilter = op.inValueSelect("filter", ["nearest", "linear"], "nearest"), + twrap = op.inValueSelect("wrap", ["clamp to edge", "repeat", "mirrored repeat"], "clamp to edge"), + + next = op.outTrigger("Next"), + outNumVerts = op.outNumber("Total Vertices"), + outTex = op.outTexture("Texture"); + +op.setPortGroup("Texture settings", [tfilter, twrap, inWidth, inSize]); + +const cgl = op.patch.cgl; +const prevViewPort = [0, 0, 0, 0]; +const effect = null; + +let autoSize = true; +let needsUpdate = true; +let needsUpdateSize = true; +let shader = null; +let size = 0; +let fb = null; +let needInitFb = true; +let mesh = null; +let vertNums = new Float32Array(1); +let numVerts = 1; + +tfilter.onChange = + twrap.onChange = initFbLater; + +inWidth.onChange = + inSize.onChange = updateSize; + +inAttrib.onChange = updateAttrib; + +updateUI(); + +const vertModTitle = "vert_" + op.name; +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "priority": 200, + "title": vertModTitle, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": "OUT vec3 MOD_pos;", + "srcBodyVert": attachments.vertpos_vert +}); + +mod.addModule({ + "title": op.name, + "name": "MODULE_COLOR", + "srcHeadFrag": "IN vec3 MOD_pos;", + "srcBodyFrag": attachments.fragpos_frag +}); +mod.addUniformVert("f", "MOD_texSize", 0); +updateAttrib(); + +function shuffleArray(array) +{ + let i = 0; + let j = 0; + let temp = null; + + for (i = array.length - 1; i > 0; i -= 1) + { + j = Math.floor(Math.seededRandom() * (i + 1)); + temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } +} + +inGeom.onChange = function () +{ + needsUpdateSize = true; + needsUpdate = true; +}; + +function updateAttrib() +{ + mod.toggleDefine("MOD_ATTRIB_POS", inAttrib.get() == "Vertex Pos"); + mod.toggleDefine("MOD_ATTRIB_TC", inAttrib.get() == "TexCoords"); + mod.toggleDefine("MOD_ATTRIB_NORMAL", inAttrib.get() == "Normals"); + + needsUpdate = true; +} + +inOrder.onChange = () => +{ + needsUpdate = true; +}; + +function warning() +{ +} + +function updateUI() +{ + inWidth.setUiAttribs({ "greyout": inSize.get() == "Auto" }); +} + +function initFbLater() +{ + needInitFb = true; + needsUpdate = true; + warning(); + updateUI(); +} + +function updateSize() +{ + const oldSize = size; + size = inWidth.get(); + + autoSize = inSize.get() == "Auto"; + + const geo = inGeom.get(); + + if (autoSize && !geo) + { + needsUpdateSize = true; + size = -1; + return; + } + if (autoSize && geo && geo.vertices) + { + size = Math.ceil(Math.sqrt(geo.vertices.length / 3)); + } + + size = Math.ceil(Math.max(1, size)); + + updateUI(); + if (oldSize != size) needsUpdate = true; + needsUpdateSize = false; + op.log("size", size); +} + +function initFb() +{ + if (fb) fb = fb.delete(); + outTex.set(CGL.Texture.getEmptyTexture(cgl)); + if (size < 1) return; + + let filter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "linear") filter = CGL.Texture.FILTER_LINEAR; + + let selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + if (twrap.get() == "repeat") selectedWrap = CGL.Texture.WRAP_REPEAT; + if (twrap.get() == "mirrored repeat") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + + if (cgl.glVersion >= 2) + { + fb = new CGL.Framebuffer2(cgl, size, size, + { + "isFloatingPointTexture": true, + "multisampling": false, + "wrap": selectedWrap, + "filter": filter, + "depth": true, + "multisamplingSamples": 0, + "clear": true + }); + } + else + { + fb = new CGL.Framebuffer(cgl, size, size, + { + "isFloatingPointTexture": true, + "filter": filter, + "wrap": selectedWrap + }); + } + needInitFb = false; +} + +exec.onTriggered = function () +{ + updateSize(); + + if (!fb || needInitFb) initFb(); + if (!needsUpdate) return next.trigger(); + if (outTex.get() != CGL.Texture.getEmptyTexture(cgl)) outTex.set(CGL.Texture.getEmptyTexture(cgl)); + + const geo = inGeom.get(); + + if (!geo || !geo.copy || !geo.vertices) return next.trigger(); + + if (size < 1) + { + needsUpdate = true; + return; + } + + if (fb && fb.getWidth() != size) fb.setSize(size, size); + + const g = geo.copy(); + + if (!mesh)mesh = new CGL.Mesh(cgl, new CGL.Geometry("a"), cgl.gl.POINTS); + + g.glPrimitive = cgl.gl.POINTS; + mesh.setGeom(g); + numVerts = g.vertices.length / 3; + + if (vertNums.length != numVerts) vertNums = new Float32Array(numVerts); + + for (let i = 0; i < numVerts; i++) vertNums[i] = i; + + if (inOrder.get() == "Random") shuffleArray(vertNums); + if (inOrder.get() == "Vertex X") + vertNums.sort(function (a, b) { return g.vertices[a * 3 + 0] - g.vertices[b * 3 + 0]; }); + if (inOrder.get() == "Vertex Y") + vertNums.sort(function (a, b) { return g.vertices[a * 3 + 1] - g.vertices[b * 3 + 1]; }); + if (inOrder.get() == "Vertex Z") + vertNums.sort(function (a, b) { return g.vertices[a * 3 + 2] - g.vertices[b * 3 + 2]; }); + + mesh._setVertexNumbers(vertNums); + + outNumVerts.set(numVerts); + + render(); + + needsUpdate = false; + next.trigger(); +}; + +function render() +{ + if (!cgl.getShader()) + { + op.setUiError("not in mainloop", "Needs to be connected to mainloop branch"); + return; + } + else op.setUiError("not in mainloop", null); + + const vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + fb.renderStart(cgl); + + cgl.pushPMatrix(); + mat4.identity(cgl.pMatrix); + + cgl.pushViewMatrix(); + mat4.identity(cgl.vMatrix); + + cgl.pushModelMatrix(); + mat4.identity(cgl.mMatrix); + + cgl.gl.viewport(0, 0, size, size); + + mat4.ortho( + cgl.pMatrix, + 0, size, + 0, size, + -1.00, 100); + + mod.bind(); + + mod.setUniformValue("MOD_texSize", size); + mesh.render(cgl.getShader()); + + mod.unbind(); + + cgl.popPMatrix(); + cgl.popModelMatrix(); + cgl.popViewMatrix(); + fb.renderEnd(cgl); + + outTex.set(fb.getTextureColor()); + + cgl.gl.viewport(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); +} + + +}; + +Ops.Gl.Meshes.GeometryToTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["d3435879-6b19-460b-911c-87ae5a8d00ad"]={f:Ops.Gl.Meshes.GeometryToTexture_v2,objName:"Ops.Gl.Meshes.GeometryToTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Grid +// +// ************************************************************** + +Ops.Gl.Meshes.Grid = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + inNum = op.inValue("Num", 10), + inSpacing = op.inValue("Spacing", 1), + inCenter = op.inBool("Center", true), + next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; +let mesh = null; + +inCenter.onChange = + inNum.onChange = + inSpacing.onChange = function () + { + if (mesh)mesh.dispose(); + mesh = null; + }; + +function init() +{ + const geomStepsOne = new CGL.Geometry(op.name); + const geomX = new CGL.Geometry(op.name); + + const space = inSpacing.get(); + const num = Math.floor(inNum.get()); + const l = space * num / 2; + + const tc = []; + + let start = -num / 2; + let end = num / 2 + 1; + + for (let i = start; i < end; i++) + { + geomStepsOne.vertices.push(-l); + geomStepsOne.vertices.push(i * space); + geomStepsOne.vertices.push(0); + + geomStepsOne.vertices.push(l); + geomStepsOne.vertices.push(i * space); + geomStepsOne.vertices.push(0); + + geomStepsOne.vertices.push(i * space); + geomStepsOne.vertices.push(-l); + geomStepsOne.vertices.push(0); + + geomStepsOne.vertices.push(i * space); + geomStepsOne.vertices.push(l); + geomStepsOne.vertices.push(0); + + tc.push(0, 0); + tc.push(0, 0); + tc.push(0, 0); + tc.push(0, 0); + } + + if (!inCenter.get()) + { + for (let i = 0; i < geomStepsOne.vertices.length; i += 3) + { + geomStepsOne.vertices[i + 0] += l; + geomStepsOne.vertices[i + 1] += l; + } + } + + geomStepsOne.setTexCoords(tc); + geomStepsOne.calculateNormals(); + + if (!mesh) mesh = new CGL.Mesh(cgl, geomStepsOne); + else mesh.setGeom(geomStepsOne); +} + +render.onTriggered = function () +{ + if (!mesh)init(); + let shader = cgl.getShader(); + if (!shader) return; + + let oldPrim = shader.glPrimitive; + + shader.glPrimitive = cgl.gl.LINES; + + mesh.render(shader); + + shader.glPrimitive = oldPrim; + + next.trigger(); +}; + + +}; + +Ops.Gl.Meshes.Grid.prototype = new CABLES.Op(); +CABLES.OPS["677a7c03-6885-46b4-8a64-e4ea54ee5d7f"]={f:Ops.Gl.Meshes.Grid,objName:"Ops.Gl.Meshes.Grid"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.HeightMap +// +// ************************************************************** + +Ops.Gl.Meshes.HeightMap = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const render = op.inTrigger("render"), + filename = op.inFile("file"), + extrude = op.inValueFloat("extrude", 1), + mWidth = op.inValueFloat("width", 3), + mHeight = op.inValueFloat("height", 3), + nRows = op.inValueInt("rows", 20), + nColumns = op.inValueInt("columns", 20), + sliceTex = op.inValueBool("texCoords slice"), + flat = op.inValueBool("flat"), + trigger = op.outTrigger("trigger"); + +let outGeom = op.outObject("geometry"); +outGeom.ignoreValueSerialize = true; + +let geom = new CGL.Geometry(op.name); +let mesh = null; +let cgl = op.patch.cgl; +let image = new Image(); + +render.onTriggered = function () +{ + if (mesh) mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +extrude.onChange = mHeight.onChange = mWidth.onChange = + nRows.onChange = nColumns.onChange = flat.onChange = rebuildGeom; + +filename.onChange = reload; + +function rebuildGeom() +{ + geom.clear(); + + let verts = []; + let tc = []; + let indices = []; + + let width = image.width; + let height = image.height; + let canvas = document.createElement("canvas"); + let ctx = canvas.getContext("2d"); + canvas.width = width; + canvas.height = height; + ctx.drawImage(image, 0, 0); + + let meshWidth = mWidth.get(); + let meshHeight = mHeight.get(); + + let count = 0; + + let vertStepX = meshWidth / width; + let vertStepY = meshHeight / height; + + let numRows = parseFloat(nRows.get()); + let numColumns = parseFloat(nColumns.get()); + let rowStepX = width / numColumns; + let rowStepY = height / numRows; + let heightMul = extrude.get() * 0.001; + + let stepRow = meshWidth / numRows; + let stepColumn = meshHeight / numColumns; + + let cycleTex = 0; + let oldh = 0; + + for (var r = 0; r <= numRows; r++) + { + for (var c = 0; c <= numColumns; c++) + { + let h = ctx.getImageData(Math.round(c * rowStepX), Math.round(r * rowStepY), 1, 1).data[1] * heightMul; + // verts.push( c*stepColumn - meshWidth/2 ); + // verts.push( r*stepRow - meshHeight/2 ); + verts.push(c * stepColumn); + verts.push(r * stepRow); + verts.push(h); + + if (sliceTex.get()) + { + if (h != oldh) + { + if (c % 2 == 0) tc.push(0.5); + else tc.push(1); + + tc.push(1.0 - r / numRows); + } + else + { + tc.push(1); + tc.push(0); + } + oldh = h; + } + else + { + tc.push(c / numColumns); + tc.push(1.0 - r / numRows); + } + } + } + + for (c = 0; c < numColumns; c++) + { + for (r = 0; r < numRows; r++) + { + let ind = c + (numColumns + 1) * r; + let v1 = ind; + let v2 = ind + 1; + let v3 = ind + numColumns + 1; + let v4 = ind + 1 + numColumns + 1; + + indices.push(v1); + indices.push(v2); + indices.push(v3); + + indices.push(v2); + indices.push(v3); + indices.push(v4); + } + } + + geom.vertices = verts; + geom.texCoords = tc; + geom.verticesIndices = indices; + if (flat.get())geom.unIndex(); + geom.calculateNormals({ "forceZUp": true }); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + mesh.setGeom(geom); + outGeom.set(null); + outGeom.set(geom); +} + +function reload() +{ + image.crossOrigin = ""; + let url = op.patch.getFilePath(filename.get()); + + let loadingId = op.patch.loading.start("heightmapImage", url); + + image.onabort = image.onerror = function (e) + { + op.patch.loading.finished(loadingId); + op.log("error loading heightmap image..."); + }; + + image.onload = function (e) + { + rebuildGeom(); + op.patch.loading.finished(loadingId); + }; + image.src = url; +} + + +}; + +Ops.Gl.Meshes.HeightMap.prototype = new CABLES.Op(); +CABLES.OPS["81264799-d92b-4b71-a3f1-ad1da8331e62"]={f:Ops.Gl.Meshes.HeightMap,objName:"Ops.Gl.Meshes.HeightMap"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Helix +// +// ************************************************************** + +Ops.Gl.Meshes.Helix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + draw = op.inValueBool("Draw", true), + segments = op.inValue("Segments", 40), + freq = op.inValue("Frequency", 1), + radius = op.inValue("Radius", 1), + radiusEnd = op.inValue("Radius End", 1), + height = op.inValue("Height"), + next = op.outTrigger("Next"), + outPoints = op.outArray("Points"); + +let cgl = op.patch.cgl; +let pos = []; +let needsCalc = true; +let mesh = null; + +segments.onChange = + radius.onChange = + height.onChange = + freq.onChange = + radiusEnd.onChange = + draw.onChange = calcLater; + +render.onTriggered = doRender; + +function doRender() +{ + if (needsCalc)calc(); + + if (mesh) + { + let shader = cgl.getShader(); + if (!shader) return; + let oldPrim = shader.glPrimitive; + shader.glPrimitive = cgl.gl.LINE_STRIP; + mesh.render(shader); + shader.glPrimitive = oldPrim; + } + + next.trigger(); +} + +function calcLater() +{ + needsCalc = true; +} + +function calc() +{ + needsCalc = false; + pos.length = 0; + + let i = 0, degInRad = 0; + let segs = Math.floor(segments.get()); + if (segs < 1)segs = 1; + + for (i = 0; i < segs; i++) + { + let perc = (i / segs); + let z = perc * height.get(); + let rad = (perc * radiusEnd.get()) + ((1.0 - perc) * radius.get()); + degInRad = (360 / segs) * i * CGL.DEG2RAD; + pos.push( + Math.sin(degInRad * freq.get()) * rad, + Math.cos(degInRad * freq.get()) * rad, + z); + } + + if (draw.get()) + { + let buff = new Float32Array(pos); + let geom = new CGL.Geometry("helix"); + geom.vertices = buff; + + mesh = new CGL.Mesh(cgl, geom); + } + else mesh = null; + + outPoints.set(null); + outPoints.set(pos); +} + + +}; + +Ops.Gl.Meshes.Helix.prototype = new CABLES.Op(); +CABLES.OPS["f41fabfc-14c0-4472-a00a-6ab1dcdcc4bc"]={f:Ops.Gl.Meshes.Helix,objName:"Ops.Gl.Meshes.Helix"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Icosahedron +// +// ************************************************************** + +Ops.Gl.Meshes.Icosahedron = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// from: http://blog.andreaskahler.com/search/label/3D + +let render = op.inTrigger("render"); +let smooth = op.inValueBool("smooth"); +let trigger = op.outTrigger("trigger"); +let geomOut = op.outObject("geometry"); + +geomOut.ignoreValueSerialize = true; + +smooth.onChange = generate; + +let mesh = null; +let cgl = op.patch.cgl; +smooth.set(false); +generate(); + +render.onTriggered = function () +{ + if (mesh) mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +function generate() +{ + let t = Math.sqrt(5.0) / 2; + let tc = []; + let verts = []; + verts.push(-1, t, 0); + verts.push(1, t, 0); + verts.push(-1, -t, 0); + verts.push(1, -t, 0); + + verts.push(0, -1, t); + verts.push(0, 1, t); + verts.push(0, -1, -t); + verts.push(0, 1, -t); + + verts.push(t, 0, -1); + verts.push(t, 0, 1); + verts.push(-t, 0, -1); + verts.push(-t, 0, 1); + + let geom = new CGL.Geometry(op.name); + + geom.vertices = verts; + geom.verticesIndices = []; + + // 5 faces around point 0 + geom.verticesIndices.push(0, 11, 5); + geom.verticesIndices.push(0, 5, 1); + geom.verticesIndices.push(0, 1, 7); + geom.verticesIndices.push(0, 7, 10); + geom.verticesIndices.push(0, 10, 11); + + // 5 adjacent faces + geom.verticesIndices.push(1, 5, 9); + geom.verticesIndices.push(5, 11, 4); + geom.verticesIndices.push(11, 10, 2); + geom.verticesIndices.push(10, 7, 6); + geom.verticesIndices.push(7, 1, 8); + + // 5 faces around point 3 + geom.verticesIndices.push(3, 9, 4); + geom.verticesIndices.push(3, 4, 2); + geom.verticesIndices.push(3, 2, 6); + geom.verticesIndices.push(3, 6, 8); + geom.verticesIndices.push(3, 8, 9); + + // 5 adjacent faces + geom.verticesIndices.push(4, 9, 5); + geom.verticesIndices.push(2, 4, 11); + geom.verticesIndices.push(6, 2, 10); + geom.verticesIndices.push(8, 6, 7); + geom.verticesIndices.push(9, 8, 1); + + geom.texCoords = tc; + + geom.calcNormals(smooth.get()); + mesh = new CGL.Mesh(cgl, geom); + geomOut.set(geom); +} + + +}; + +Ops.Gl.Meshes.Icosahedron.prototype = new CABLES.Op(); +CABLES.OPS["109cead5-8eda-4726-9e3d-b23afe201abd"]={f:Ops.Gl.Meshes.Icosahedron,objName:"Ops.Gl.Meshes.Icosahedron"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Line +// +// ************************************************************** + +Ops.Gl.Meshes.Line = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + x1 = op.inValue("X 1"), + y1 = op.inValue("Y 1"), + z1 = op.inValue("Z 1"), + x2 = op.inValue("X 2", 1), + y2 = op.inValue("Y 2", 1), + z2 = op.inValue("Z 2", 1), + next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; + +const geom = new CGL.Geometry("simplespline"); +geom.vertices = [x1.get(), y1.get(), z1.get(), x2.get(), y2.get(), x2.get()]; +const mesh = new CGL.Mesh(cgl, geom, cgl.gl.LINES); + +let changed = true; + +x1.onChange = function () { geom.vertices[0] = x1.get(); changed = true; }; +y1.onChange = function () { geom.vertices[1] = y1.get(); changed = true; }; +z1.onChange = function () { geom.vertices[2] = z1.get(); changed = true; }; + +x2.onChange = function () { geom.vertices[3] = x2.get(); changed = true; }; +y2.onChange = function () { geom.vertices[4] = y2.get(); changed = true; }; +z2.onChange = function () { geom.vertices[5] = z2.get(); changed = true; }; + +render.onTriggered = function () +{ + if (changed) + { + mesh.updateVertices(geom); + changed = false; + } + + let shader = cgl.getShader(); + mesh.render(shader); + next.trigger(); +}; + + +}; + +Ops.Gl.Meshes.Line.prototype = new CABLES.Op(); +CABLES.OPS["c6a0d570-a0ac-4655-b17d-74d0870b0799"]={f:Ops.Gl.Meshes.Line,objName:"Ops.Gl.Meshes.Line"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.LinesArray +// +// ************************************************************** + +Ops.Gl.Meshes.LinesArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + width = op.inValueFloat("width", 10), + height = op.inValueFloat("height", 1), + doLog = op.inValueBool("Logarithmic", false), + pivotX = op.inValueSelect("pivot x", ["center", "left", "right"], "center"), + pivotY = op.inValueSelect("pivot y", ["center", "top", "bottom"], "center"), + nColumns = op.inValueInt("num columns", 10), + nRows = op.inValueInt("num rows", 10), + axis = op.inValueSelect("axis", ["xy", "xz"], "xy"), + trigger = op.outTrigger("trigger"), + outPointArrays = op.outArray("Point Arrays"); + +const cgl = op.patch.cgl; +let meshes = []; + +op.setPortGroup("Size", [width, height]); +op.setPortGroup("Alignment", [pivotX, pivotY]); + +axis.onChange = + pivotX.onChange = + pivotY.onChange = + width.onChange = + height.onChange = + nRows.onChange = + nColumns.onChange = + doLog.onChange = rebuildDelayed; + +rebuild(); + +render.onTriggered = function () +{ + for (let i = 0; i < meshes.length; i++) meshes[i].render(cgl.getShader()); + trigger.trigger(); +}; + +let delayRebuild = 0; +function rebuildDelayed() +{ + clearTimeout(delayRebuild); + delayRebuild = setTimeout(rebuild, 60); +} + +function rebuild() +{ + let x = 0; + let y = 0; + + if (pivotX.get() == "center") x = 0; + if (pivotX.get() == "right") x = -width.get() / 2; + if (pivotX.get() == "left") x = +width.get() / 2; + + if (pivotY.get() == "center") y = 0; + if (pivotY.get() == "top") y = -height.get() / 2; + if (pivotY.get() == "bottom") y = +height.get() / 2; + + let numRows = parseInt(nRows.get(), 10); + let numColumns = parseInt(nColumns.get(), 10); + + let stepColumn = width.get() / numColumns; + let stepRow = height.get() / numRows; + + let c, r; + meshes.length = 0; + + let vx, vy, vz; + let verts = []; + let tc = []; + let indices = []; + let count = 0; + + function addMesh() + { + let geom = new CGL.Geometry(op.name); + geom.vertices = verts; + geom.texCoords = tc; + geom.verticesIndices = indices; + + let mesh = new CGL.Mesh(cgl, geom, cgl.gl.LINES); + mesh.setGeom(geom); + meshes.push(mesh); + + verts.length = 0; + tc.length = 0; + indices.length = 0; + count = 0; + lvx = null; + } + + let min = Math.log(1 / numRows); + let max = Math.log(1); + // op.log(min,max); + + let lines = []; + + for (r = numRows; r >= 0; r--) + { + // op.log(r/numRows); + var lvx = null, lvy = null, lvz = null; + let ltx = null, lty = null; + let log = 0; + let doLoga = doLog.get(); + + let linePoints = []; + lines.push(linePoints); + + + for (c = numColumns; c >= 0; c--) + { + vx = c * stepColumn - width.get() / 2 + x; + if (doLoga) + vy = (Math.log((r / numRows)) / min) * height.get() - height.get() / 2 + y; + else + vy = r * stepRow - height.get() / 2 + y; + + let tx = c / numColumns; + let ty = 1.0 - r / numRows; + if (doLoga) ty = (Math.log((r / numRows)) / min); + + vz = 0.0; + + if (axis.get() == "xz") + { + vz = vy; + vy = 0.0; + } + if (axis.get() == "xy") vz = 0.0; + + if (lvx !== null) + { + verts.push(lvx); + verts.push(lvy); + verts.push(lvz); + + linePoints.push(lvx, lvy, lvz); + + verts.push(vx); + verts.push(vy); + verts.push(vz); + + tc.push(ltx); + tc.push(lty); + + tc.push(tx); + tc.push(ty); + + indices.push(count); + count++; + indices.push(count); + count++; + } + + if (count > 64000) + { + addMesh(); + } + + ltx = tx; + lty = ty; + + lvx = vx; + lvy = vy; + lvz = vz; + } + } + + outPointArrays.set(lines); + + addMesh(); + + // op.log(meshes.length,' meshes'); +} + + +}; + +Ops.Gl.Meshes.LinesArray.prototype = new CABLES.Op(); +CABLES.OPS["a75265c2-957b-4719-9d03-7bbf00ace364"]={f:Ops.Gl.Meshes.LinesArray,objName:"Ops.Gl.Meshes.LinesArray"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.MeshInstancerFromTexture_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.MeshInstancerFromTexture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"instancer_body_frag":"#ifdef USE_TEX_COLOR\n #ifdef BLEND_MODE_MULTIPLY\n col.rgb *= frag_instColor.rgb;\n col.a *= frag_instColor.a;\n #endif\n\n #ifdef BLEND_MODE_ADD\n col.rgb += frag_instColor.rgb;\n col.a += frag_instColor.a;\n #endif\n\n #ifdef BLEND_MODE_NONE\n col.rgb = frag_instColor.rgb;\n col.a = frag_instColor.a;\n #endif\n#endif\n","instancer_body_vert":"float tx=mod(instanceIndex,(MOD_texSizeX))/MOD_texSizeX+(1.0/MOD_texSizeX*0.5);\nfloat ty=float(int((instanceIndex/(MOD_texSizeX))))/MOD_texSizeY+(1.0/MOD_texSizeY*0.5);\n\nvec3 MOD_texPos=texture(MOD_texTrans,vec2(tx,ty)).rgb*MOD_mulRGB;\nmat4 texInstMat;\nvec3 scale=vec3(1.0);\n\n#ifdef USE_TEX_SCALE\n scale*=texture(MOD_texScale,vec2(tx,ty)).rgb;\n#endif\n\ntexInstMat[0][0]=\ntexInstMat[1][1]=\ntexInstMat[2][2]=\ntexInstMat[3][3]=1.0;\n\n#ifdef USE_TEX_ROT\n vec3 MOD_texRota=texture(MOD_texRot,vec2(tx,ty)).rgb;\n texInstMat*=rotationMatrix(vec3(1.0,0.0,0.0),MOD_texRota.r*PI*2.0);\n texInstMat*=rotationMatrix(vec3(0.0,1.0,0.0),MOD_texRota.g*PI*2.0);\n texInstMat*=rotationMatrix(vec3(0.0,0.0,1.0),MOD_texRota.b*PI*2.0);\n#endif\n\ntexInstMat[3][0]=MOD_texPos.x;\ntexInstMat[3][1]=MOD_texPos.y;\ntexInstMat[3][2]=MOD_texPos.z;\n\nmat4 scalem;\nscalem[0][0]=MOD_scale*scale.x;\nscalem[1][1]=MOD_scale*scale.y;\nscalem[2][2]=MOD_scale*scale.z;\nscalem[3][3]=1.0;\ntexInstMat*=scalem;\n\nmMatrix*=texInstMat;\n\n#ifdef USE_TEX_COLOR\n\n vec4 instColor=texture(MOD_texColor,vec2(tx,ty));\n\n frag_instColor=instColor;\n#endif\n\n#ifdef USE_TEX_TC\n vec4 instTexCoords=texture(MOD_texCoords,vec2(tx,ty));\n\n texCoord=(texCoord*instTexCoords.zw)+instTexCoords.xy;\n#endif\n\n\n\n\n","instancer_head_frag":"IN vec4 frag_instColor;\n","instancer_head_vert":"IN mat4 instMat;\nIN vec4 instColor;\nIN float instanceIndex;\nOUT mat4 instModelMat;\nOUT vec4 frag_instColor;\n\n#define INSTANCING\n#define PI 3.14159265358\n\nmat3 ntorot(vec3 r)\n{\n float cx = cos(radians(r.x));\n float sx = sin(radians(r.x));\n float cy = cos(radians(r.y));\n float sy = sin(radians(r.y));\n float cz = cos(radians(r.z));\n float sz = sin(radians(r.z));\n\n return mat3(cy * cz, \tcx * sz + sx * sy * cz, \tsx * sz - cx * sy * cz,\n \t\t\t-cy * sz,\tcx * cz - sx * sy * sz,\t\tsx * cz + cx * sy * sz,\n \t\t\tsy,\t\t\t-sx * cy,\t\t\t\t\tcx * cy);\n}\n\nmat4 rotationMatrix(vec3 axis, float angle)\n{\n axis = normalize(axis);\n float s = sin(angle);\n float c = cos(angle);\n float oc = 1.0 - c;\n\n return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,\n oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,\n oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,\n 0.0, 0.0, 0.0, 1.0);\n}\n\n// vec4 MOD_rot(vec4 pos, vec3 rot, mat4 modelMatrix)\n// {\n// // pos=pos*rotationX(rot.x)*rotationY(rot.y)*rotationZ(rot.z);\n// pos.xyz*=ntorot( (rot-0.5) * 3.14*2.0 );\n\n// return pos;\n// }\n",}; +const + exe = op.inTrigger("exe"), + geom = op.inObject("Geometry", null, "geometry"), + inNum = op.inInt("Num Instances", 1000), + inTex = op.inTexture("Position Texture", null, "texture"), + inTex2 = op.inTexture("Rotation Texture", null, "texture"), + inTex3 = op.inTexture("Scale Texture", null, "texture"), + inTex4 = op.inTexture("Color Texture", null, "texture"), + inTex5 = op.inTexture("TexCoord Texture", null, "texture"), + inBlendMode = op.inSwitch("Color Texture Blendmode", ["Multiply", "Add", "Normal"], "Multiply"), + inScale = op.inValue("Scale", 1), + inMulR = op.inValue("Multiply Pos X", 1), + inMulG = op.inValue("Multiply Pos Y", 1), + inMulB = op.inValue("Multiply Pos Z", 1), + outTrigger = op.outTrigger("Trigger Out"), + outNum = op.outNumber("Num"); + +op.toWorkPortsNeedToBeLinked(geom); +op.toWorkPortsNeedToBeLinked(exe); + +geom.ignoreValueSerialize = true; + +const cgl = op.patch.cgl; +const m = mat4.create(); +let + // matrixArray = new Float32Array(1), + // instColorArray = new Float32Array(1), + mesh = null, + recalc = true, + num = 0, + arrayChangedTrans = true; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "name": "MODULE_VERTEX_POSITION", + "title": op.name, + "priority": -2, + "srcHeadVert": attachments.instancer_head_vert, + "srcBodyVert": attachments.instancer_body_vert +}); + +mod.addModule({ + "name": "MODULE_COLOR", + "priority": -2, + "title": op.name, + "srcHeadFrag": attachments.instancer_head_frag, + "srcBodyFrag": attachments.instancer_body_frag, +}); + +mod.addUniformVert("f", "MOD_scale", inScale); +mod.addUniformVert("t", "MOD_texTrans"); +mod.addUniformVert("t", "MOD_texRot"); +mod.addUniformVert("t", "MOD_texScale"); +mod.addUniformVert("t", "MOD_texCoords"); +mod.addUniformVert("t", "MOD_texColor"); +mod.addUniformVert("f", "MOD_texSizeX", 0); +mod.addUniformVert("f", "MOD_texSizeY", 0); +mod.addUniformVert("3f", "MOD_mulRGB", inMulR, inMulG, inMulB); + +inBlendMode.onChange = +inTex.onChange = +inTex3.onChange = +inTex4.onChange = +inTex5.onChange = +inTex2.onChange = updateDefines; + +// inBlendMode.onChange = updateDefines; +// doLimit.onChange = updateLimit; +exe.onTriggered = doRender; +exe.onLinkChanged = function () +{ + if (!exe.isLinked()) removeModule(); +}; + +inNum.onChange = + function () + { + arrayChangedTrans = true; + recalc = true; + }; + +function reset() +{ + arrayChangedTrans = true; + recalc = true; +} + +function updateDefines() +{ + mod.toggleDefine("BLEND_MODE_MULTIPLY", inBlendMode.get() === "Multiply"); + mod.toggleDefine("BLEND_MODE_ADD", inBlendMode.get() === "Add"); + mod.toggleDefine("BLEND_MODE_NONE", inBlendMode.get() === "Normal"); + + mod.toggleDefine("USE_TEX_ROT", inTex2.get()); + mod.toggleDefine("USE_TEX_SCALE", inTex3.get()); + mod.toggleDefine("USE_TEX_COLOR", inTex4.get()); + mod.toggleDefine("USE_TEX_TC", inTex5.get()); +} + +geom.onChange = function () +{ + if (mesh)mesh.dispose(); + + if (!geom.get() || !geom.get().vertices) + { + mesh = null; + return; + } + mesh = new CGL.Mesh(cgl, geom.get()); + reset(); +}; + +function removeModule() +{ + +} + +function setupArray() +{ + if (!mesh) return; + + num = Math.max(0, Math.floor(inNum.get())); + + // if (matrixArray.length != num * 16) matrixArray = new Float32Array(num * 16); + + // for (let i = 0; i < num; i++) + // { + // mat4.identity(m); + // for (let a = 0; a < 16; a++) matrixArray[i * 16 + a] = m[a]; + // } + + mesh.numInstances = num; + // mesh.addAttribute("instMat", matrixArray, 16); + + recalc = false; +} + +function doRender() +{ + if (!mesh) return; + if (recalc) setupArray(); + + if (!inTex.get()) return; + if (inTex.get())mod.pushTexture("MOD_texTrans", inTex.get().tex); + if (inTex2.get())mod.pushTexture("MOD_texRot", inTex2.get().tex); + if (inTex3.get())mod.pushTexture("MOD_texScale", inTex3.get().tex); + if (inTex4.get())mod.pushTexture("MOD_texColor", inTex4.get().tex); + if (inTex5.get())mod.pushTexture("MOD_texCoords", inTex5.get().tex); + + mod.bind(); + mod.setUniformValue("MOD_texSizeX", inTex.get().width); + mod.setUniformValue("MOD_texSizeY", inTex.get().height); + + // mesh.numInstances = num; + + outNum.set(mesh.numInstances); + + if (mesh.numInstances > 0) mesh.render(cgl.getShader()); + + outTrigger.trigger(); + + mod.unbind(); +} + + +}; + +Ops.Gl.Meshes.MeshInstancerFromTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["10771b66-4b63-4f47-b050-3b9c44cb2780"]={f:Ops.Gl.Meshes.MeshInstancerFromTexture_v2,objName:"Ops.Gl.Meshes.MeshInstancerFromTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.ParametricSurface +// +// ************************************************************** + +Ops.Gl.Meshes.ParametricSurface = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +/* presets for parametric surface */ +const parametricBodies = [ + { + "title": "Rectangle", + "xFunction": "u", + "yFunction": "v", + "zFunction": "0", + "uMin": 0, + "uMax": 2, + "vMin": 0, + "vMax": 1, + "isPiU": false, + "isPiV": false, + "uSegments": 24, + "vSegments": 24, + "scaleX": 1, + "scaleY": 1, + "scaleZ": 1, + "displaceU": -1, + "displaceV": -0.5 + }, + { + "title": "Archimedic Spiral", + "xFunction": "u*cos(u)", + "yFunction": "v", + "zFunction": "u*sin(u)", + + "uMin": 0, + "uMax": 50, + "vMin": 0, + "vMax": 1, + "isPiU": false, + "isPiV": false, + + "uSegments": 300, + "vSegments": 20, + + "scaleX": 0.05, + "scaleY": 0.05, + "scaleZ": 0.05, + + "displaceU": 0, + "displaceV": -3 + }, + { + "title": "Cylinder", + "xFunction": "0.5*cos(u)", + "yFunction": "v", + "zFunction": "0.5*sin(u)", + + "uMin": -1, + "uMax": 1, + "vMin": 0, + "vMax": 1, + "isPiU": true, + "isPiV": false, + + "uSegments": 50, + "vSegments": 50, + + "scaleX": 1, + "scaleY": 1, + "scaleZ": 1, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Pillow", + "zFunction": "cos(u)", + "yFunction": "-1*cos(v)", + "xFunction": "0.8*sin(u)*sin(v)", + + "uMin": 0, + "uMax": 1, + "vMin": -1, + "vMax": 1, + "isPiU": true, + "isPiV": true, + + "uSegments": 50, + "vSegments": 50, + + "scaleX": 0.8, + "scaleY": 0.8, + "scaleZ": 0.8, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Sine Surface", + "zFunction": "sin(u)", + "yFunction": "sin(v)", + "xFunction": "sin(u+v)", + + "uMin": -1, + "uMax": 1, + "vMin": -1, + "vMax": 1, + "isPiU": true, + "isPiV": true, + + "uSegments": 50, + "vSegments": 50, + + "scaleX": 0.8, + "scaleY": 0.8, + "scaleZ": 0.8, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Steinbach Screw", + "xFunction": "v*cos(u)", + "yFunction": "u*sin(v)", + "zFunction": "u*cos(v)", + + + "uMin": -4, + "uMax": 4, + "vMin": 0, + "vMax": 2, + "isPiU": false, + "isPiV": true, + + "uSegments": 50, + "vSegments": 50, + + "scaleX": 0.18, + "scaleY": 0.18, + "scaleZ": 0.18, + + "displaceU": 0, + "displaceV": 0 + }, + + { + "title": "Moebius Band", + "xFunction": "cos(v)*(1 + u*cos(v/2))", + "yFunction": "sin(v)*(1 + u*cos(v/2))", + "zFunction": "u*sin(v/2)", + + + "uMin": -0.3, + "uMax": 0.3, + "vMin": 0, + "vMax": 2, + "isPiU": false, + "isPiV": true, + + "uSegments": 50, + "vSegments": 50, + + "scaleX": 0.6, + "scaleY": 0.6, + "scaleZ": 0.6, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Wavy Sphere", + "xFunction": "u*cos(cos(u))*cos(v)", + "yFunction": "u*cos(cos(u))*sin(v)", + "zFunction": "u*sin(cos(u))", + + "uMin": 0, + "uMax": 45, + "vMin": 0, + "vMax": 2, + "isPiU": false, + "isPiV": true, + + "uSegments": 100, + "vSegments": 100, + + "scaleX": 0.02, + "scaleY": 0.02, + "scaleZ": 0.02, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Spring", + "xFunction": "(2 + 0.5*cos(v))*cos(u)", + "yFunction": "(2 + 0.5*cos(v))*sin(u)", + "zFunction": "0.5*(sin(v) + 2*u/PI)", + + "uMin": 0, + "uMax": 15, + "vMin": 0, + "vMax": 2, + "isPiU": true, + "isPiV": true, + + "uSegments": 100, + "vSegments": 100, + + "scaleX": 0.1, + "scaleY": 0.1, + "scaleZ": 0.1, + + "displaceU": 0, + "displaceV": 0 + }, + + { + "title": "Folium", + "xFunction": "cos(u)*(2*v/PI - tanh(v))", + "yFunction": "cos(u + 2*PI/3)/cosh(v)", + "zFunction": "cos(u - 2*PI/3)/cosh(v)", + + "uMin": -1, + "uMax": 1, + "vMin": -1, + "vMax": 1, + "isPiU": true, + "isPiV": true, + + "uSegments": 50, + "vSegments": 50, + + "scaleX": 1.3, + "scaleY": 1.3, + "scaleZ": 1.3, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Hyperbolic Octahedron", + "xFunction": "pow(cos(u)*cos(v), 3)", + "yFunction": "pow(sin(u)*cos(v), 3)", + "zFunction": "pow(sin(v), 3)", + + "uMin": -1 / 2, + "uMax": 1 / 2, + "vMin": -1, + "vMax": 1, + "isPiU": true, + "isPiV": true, + + "uSegments": 50, + "vSegments": 50, + + "scaleX": 1.3, + "scaleY": 1.3, + "scaleZ": 1.3, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Maeder's Owl", + "xFunction": "v*cos(u)-0.5*v*v*cos(2*u)", + "yFunction": "-1*v*sin(u) - 0.5*v*v*sin(2*u)", + "zFunction": "4*exp(1.5*log(v))*cos(3*u/2)/3", + + "uMin": 0, + "uMax": 4, + "vMin": 0.001, + "vMax": 4, + "isPiU": true, + "isPiV": false, + + "uSegments": 100, + "vSegments": 100, + + "scaleX": 0.071, + "scaleY": 0.071, + "scaleZ": 0.071, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Tranguloid Trefoil", + "xFunction": "2*sin(3*u)/(2+cos(v))", + "yFunction": "2*(sin(u) + 2*sin(2*u))/(2+cos(v+2*PI/3))", + "zFunction": "(cos(u)-2*cos(2*u))*(2+cos(v+2*PI/3))/4", + + "uMin": -1, + "uMax": 1, + "vMin": -1, + "vMax": 1, + "isPiU": true, + "isPiV": true, + + "uSegments": 50, + "vSegments": 50, + + "scaleX": 0.3, + "scaleY": 0.2, + "scaleZ": 0.3, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Apple", + "xFunction": "cos(u)*(5 + 4.8 *cos(v)) + pow(v/PI, 20)", + "yFunction": "-2.3*log(1-v*0.3157) + 6*sin(v) + 2*cos(v) ", + "zFunction": "sin(u)*(5 + 4.8*cos(v)) + 0.25*cos(5*u)", + + "uMin": -1, + "uMax": 1, + "vMin": -1, + "vMax": 1, + "isPiU": true, + "isPiV": true, + + "uSegments": 50, + "vSegments": 50, + + "scaleX": 0.06, + "scaleY": 0.06, + "scaleZ": 0.06, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Kuen's Surface", + "xFunction": "(2*(cos(u) + u*sin(u))*sin(v))/(1+u*u*sin(v)*sin(v))", + "yFunction": "(2*(-u*cos(u) + sin(u))*sin(v))/(1+u*u*sin(v)*sin(v))", + "zFunction": "log(tan(v/2)) + 2*cos(v)/(1+u*u*sin(v)*sin(v))", + + "uMin": -4.3, + "uMax": 4.3, + "vMin": 0.03, + "vMax": 31.11, + "isPiU": false, + "isPiV": false, + + "uSegments": 100, + "vSegments": 100, + + "scaleX": 0.7, + "scaleY": 0.7, + "scaleZ": 0.7, + + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Henneberg's Surface", + "xFunction": "2*cos(v)*sinh(u)-0.667*cos(3*v)*sinh(3*u)", + "yFunction": "2*sin(v)*sinh(u)+0.667*sin(3*v)*sinh(3*u)", + "zFunction": "2*cos(2*v)*cosh(2*u)", + + "uMin": -1, + "uMax": 1, + "vMin": -0.5, + "vMax": 0.5, + "isPiU": false, + "isPiV": true, + + "uSegments": 100, + "vSegments": 100, + + "scaleX": 0.1, + "scaleY": 0.1, + "scaleZ": 0.1, + + "displaceU": 0, + "displaceV": 0 + }, + { + /* +a : 1.6 +b : 1.6 +c : 1.0 +h : 1.5 +k : -7.0 +w : 0.075 +umin : -50.0 +umax : -1.0 + */ + "title": "Pseudoheliceras subcatenatum", + + "xFunction": "exp(0.075*u)*(1.5 + 1.6 * cos(v))*cos(1.0*u)", + "yFunction": "-1 * exp(0.075*u)*(1.5 + 1.6 * cos(v))*sin(1.0*u)", + "zFunction": "exp(0.075*u)*(-7 + 1.6 * sin(v))", + + "uMin": -50, + "uMax": -1, + "vMin": 0, + "vMax": 2, + "isPiU": false, + "isPiV": true, + + "uSegments": 50, + "vSegments": 50, + + "scaleX": 0.3, + "scaleY": 0.3, + "scaleZ": 0.3, + + "displaceU": 0, + "displaceV": 0 + + }, + { + "title": "Little Cycloid", + "xFunction": "cos(u/2)*cos(u/5)*(10 + cos(v)) + sin(u/5)*sin(v)*cos(v)", + "yFunction": "sin(u/2)*cos(u/5)*(10 + cos(v)) + sin(u/5)*sin(v)*cos(v)", + "zFunction": "-sin(u/5)*(10+cos(v))*sin(v)*cos(v)", + + "uMin": 0, + // 2*b*c + "uMax": 2 * 5 * 2, + "vMin": 0, + "vMax": 4, + "isPiU": true, + "isPiV": true, + + "uSegments": 100, + "vSegments": 100, + + "scaleX": 0.05, + "scaleY": 0.05, + "scaleZ": 0.05, + + "displaceU": 0, + "displaceV": -3 + }, + { + // R=50, r=12.5, p=7 und q=3 + "title": "Torus Knot", + "xFunction": "(50+90*cos(7*u) + 12.5*cos(v)) * cos(8*u)", + "yFunction": "12.5*sin(v) + 90*sin(7*u)", + "zFunction": "(50+90*cos(7*u) + 12.5*cos(v)) * sin(8*u)", + "uMin": 0, + "uMax": 2, + "vMin": 0, + "vMax": 2, + "isPiU": true, + "isPiV": true, + "uSegments": 200, + "vSegments": 200, + "scaleX": 0.005, + "scaleY": 0.005, + "scaleZ": 0.005, + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Triaxial Tritorus", + "yFunction": "sin(u)*(1 + cos(v))", + "xFunction": "sin(u+2*PI/3)*(1+cos(v+2*PI/3))", + "zFunction": "sin(u+4*PI/3)*(1+cos(v+4*PI/3))", + "uMin": -1, + "uMax": 1, + "vMin": -1, + "vMax": 1, + "isPiU": true, + "isPiV": true, + "uSegments": 100, + "vSegments": 100, + "scaleX": 0.7, + "scaleY": 0.7, + "scaleZ": 0.7, + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Triaxial Hexatorus", + "xFunction": "sin(u)/(sqrt(2)+cos(v))", + "yFunction": "sin(u+2*PI/3)/(sqrt(2)+cos(v+2*PI/3))", + "zFunction": "cos(u-2*PI/3)/(sqrt(2)+cos(v-2*PI/3))", + "uMin": 0, + "uMax": 2, + "vMin": 0, + "vMax": 2, + "isPiU": true, + "isPiV": true, + "uSegments": 100, + "vSegments": 100, + "scaleX": 0.8, + "scaleY": 0.8, + "scaleZ": 0.8, + "displaceU": 0, + "displaceV": 0 + }, + { + "title": "Hyperbolic Helicoid", + "xFunction": "sinh(v)*cos(4.13*u)/(1+cosh(u)*cosh(v))", + "yFunction": "sinh(v)*sin(4.13*u)/(1+cosh(u)*cosh(v))", + "zFunction": "cosh(v)*sinh(u)/(1+cosh(u)*cosh(v))", + "uMin": -4, + "uMax": 4, + "vMin": -4, + "vMax": 4, + "isPiU": false, + "isPiV": false, + "uSegments": 100, + "vSegments": 100, + "scaleX": 1, + "scaleY": 1, + "scaleZ": 1, + "displaceU": 0, + "displaceV": 0 + }, + + { + "title": "Triple Corkscrew III", + "xFunction": "u*1.3", + "yFunction": "0.5*(0.4*(1 - abs(u)) * cos(v) + cos(0.4)*cos(u*PI/2)*cos(u*10*PI))", + "zFunction": "0.5*(0.4*(1-abs(u))*sin(v) + cos(0.4)*cos(u*PI/2)*sin(u*10*PI))", + "uMin": -1, + "uMax": 1, + "vMin": -1, + "vMax": 1, + "isPiU": false, + "isPiV": true, + "uSegments": 190, + "vSegments": 190, + "scaleX": 1, + "scaleY": 1, + "scaleZ": 1, + "displaceU": 0, + "displaceV": 0 + }, + { + // a = 0.4 + // uMin = -13.2 + // uMax = 13.2 + // vMin = -37.4 + // vMax = 37.4 + + + // r = 1-0.4*0.4 + // w = sqrt(r) + // d = a( pow(w * cosh(a*u), 2) + pow(a*sin(w*v), 2) ) + // x = -u + (2*r*cosh(a*u)*sinh(a*u)/d) + // y = 2*w*cosh(a*u)*(-1*(w*cos(v)*cos(w*v)) - (sin(v) * sin(w*v)))/d + // z = 2*w*cosh(a*u) * (-1* (w*sin(v)*cos(w*v)) + (cos(v)*sin(w*v)))/d + /* + w = sqrt(1-0.4*0.4) + d = 0.4( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) ) + x = -u + (2*(1-0.4*0.4)*cosh(0.4*u)*sinh(0.4*u)/(0.4*( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) ))) + y = 2*(sqrt(1-0.4*0.4))*cosh(0.4*u)*(-1*((sqrt(1-0.4*0.4))*cos(v)*cos((sqrt(1-0.4*0.4))*v)) - (sin(v) * sin((sqrt(1-0.4*0.4))*v)))/(0.4*( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) )) + z = 2*sqrt(1-0.4*0.4)*cosh(0.4*u) * (-1* (sqrt(1-0.4*0.4)*sin(v)*cos(sqrt(1-0.4*0.4)*v)) + (cos(v)*sin(sqrt(1-0.4*0.4)*v)))/(0.4*( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) )) + */ + "title": "Breather Surface", + "xFunction": "-u + (2*(1-0.4*0.4)*cosh(0.4*u)*sinh(0.4*u)/(0.4*( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) )))", + "yFunction": "2*(sqrt(1-0.4*0.4))*cosh(0.4*u)*(-1*((sqrt(1-0.4*0.4))*cos(v)*cos((sqrt(1-0.4*0.4))*v)) - (sin(v) * sin((sqrt(1-0.4*0.4))*v)))/(0.4*( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) ))", + "zFunction": "2*sqrt(1-0.4*0.4)*cosh(0.4*u) * (-1* (sqrt(1-0.4*0.4)*sin(v)*cos(sqrt(1-0.4*0.4)*v)) + (cos(v)*sin(sqrt(1-0.4*0.4)*v)))/(0.4*( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) ))", + "uMin": -13.2, + "uMax": 13.2, + "vMin": -37.4, + "vMax": 37.4, + "isPiU": false, + "isPiV": false, + "uSegments": 190, + "vSegments": 190, + "scaleX": 0.2, + "scaleY": 0.2, + "scaleZ": 0.2, + "displaceU": 0, + "displaceV": 0 + }, + { + // a = 0.4 + // uMin = -13.2 + // uMax = 13.2 + // vMin = -37.4 + // vMax = 37.4 + + + // r = 1-0.4*0.4 + // w = sqrt(r) + // d = a( pow(w * cosh(a*u), 2) + pow(a*sin(w*v), 2) ) + // x = -u + (2*r*cosh(a*u)*sinh(a*u)/d) + // y = 2*w*cosh(a*u)*(-1*(w*cos(v)*cos(w*v)) - (sin(v) * sin(w*v)))/d + // z = 2*w*cosh(a*u) * (-1* (w*sin(v)*cos(w*v)) + (cos(v)*sin(w*v)))/d + /* + w = sqrt(1-0.4*0.4) + d = 0.4( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) ) + x = -u + (2*(1-0.4*0.4)*cosh(0.4*u)*sinh(0.4*u)/(0.4*( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) ))) + y = 2*(sqrt(1-0.4*0.4))*cosh(0.4*u)*(-1*((sqrt(1-0.4*0.4))*cos(v)*cos((sqrt(1-0.4*0.4))*v)) - (sin(v) * sin((sqrt(1-0.4*0.4))*v)))/(0.4*( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) )) + z = 2*sqrt(1-0.4*0.4)*cosh(0.4*u) * (-1* (sqrt(1-0.4*0.4)*sin(v)*cos(sqrt(1-0.4*0.4)*v)) + (cos(v)*sin(sqrt(1-0.4*0.4)*v)))/(0.4*( pow(sqrt(1-0.4*0.4) * cosh(0.4*u), 2) + pow(0.4*sin(sqrt(1-0.4*0.4)*v), 2) )) + */ + "title": "Breather Surface II", + "xFunction": "-u + (2*(1-0.6*0.6)*cosh(0.6*u)*sinh(0.6*u)/(0.6*( pow(sqrt(1-0.6*0.6) * cosh(0.6*u), 2) + pow(0.6*sin(sqrt(1-0.6*0.6)*v), 2) )))", + "yFunction": "2*(sqrt(1-0.6*0.6))*cosh(0.6*u)*(-1*((sqrt(1-0.6*0.6))*cos(v)*cos((sqrt(1-0.6*0.6))*v)) - (sin(v) * sin((sqrt(1-0.6*0.6))*v)))/(0.6*( pow(sqrt(1-0.6*0.6) * cosh(0.6*u), 2) + pow(0.6*sin(sqrt(1-0.6*0.6)*v), 2) ))", + "zFunction": "2*sqrt(1-0.6*0.6)*cosh(0.6*u) * (-1* (sqrt(1-0.6*0.6)*sin(v)*cos(sqrt(1-0.6*0.6)*v)) + (cos(v)*sin(sqrt(1-0.6*0.6)*v)))/(0.6*( pow(sqrt(1-0.6*0.6) * cosh(0.6*u), 2) + pow(0.6*sin(sqrt(1-0.6*0.6)*v), 2) ))", + "uMin": -8, + "uMax": 8, + "vMin": -15.55, + "vMax": 15.55, + "isPiU": false, + "isPiV": false, + "uSegments": 190, + "vSegments": 190, + "scaleX": 0.2, + "scaleY": 0.2, + "scaleZ": 0.2, + "displaceU": 0, + "displaceV": 0 + } +]; + + +let shouldRender = true; +let shouldScale = true; + +const shapes = op.inDropDown("shapes", parametricBodies.map((bod) => { return bod.title; })); + +const render = op.inTrigger("render"); +const inSegmentsU = op.inValueInt("u Segments", 48); +const inSegmentsV = op.inValueInt("v Segments", 48); +const multByPiU = op.inValueBool("Multiple of PI - u", false); +const inMinU = op.inValueFloat("uMin", -2); +const inMaxU = op.inValueFloat("uMax", 2); +const inDisplaceU = op.inValueFloat("Displace U", 0); + +const multByPiV = op.inValueBool("Multiple of PI - v", false); +const inMinV = op.inValueFloat("vMin", -1); +const inMaxV = op.inValueFloat("vMax", 1); +const inDisplaceV = op.inValueFloat("Displace V", 0); + +op.setPortGroup("Segments", [inSegmentsU, inSegmentsV]); +op.setPortGroup("V range", [multByPiV, inMaxV, inMinV, inDisplaceV]); +op.setPortGroup("U range", [multByPiU, inMaxU, inMinU, inDisplaceU]); +const inFunctionX = op.inString("X Function", "u"); +const inFunctionY = op.inString("Y Function", "v"); + +const inFunctionZ = op.inString("Z Function", "0"); +const inScaleX = op.inFloatSlider("Scale X", 1); +const inScaleY = op.inFloatSlider("Scale Y", 1); +const inScaleZ = op.inFloatSlider("Scale Z", 1); + +op.setPortGroup("XYZ Functions", [inFunctionX, inScaleX, inFunctionY, inScaleY, inFunctionZ, inScaleZ]); +op.setPortGroup("Scaling", [inScaleX, inScaleY, inScaleZ]); +const draw = op.inValueBool("Draw", true); +op.setPortGroup("Draw", [draw]); + + +const inObj = { + "xFunction": inFunctionX, + "yFunction": inFunctionY, + "zFunction": inFunctionZ, + + "isPiU": multByPiU, + "uMin": inMinU, + "uMax": inMaxU, + + "isPiV": multByPiV, + "vMin": inMinV, + "vMax": inMaxV, + + "uSegments": inSegmentsU, + "vSegments": inSegmentsV, + + "scaleX": inScaleX, + "scaleY": inScaleY, + "scaleZ": inScaleZ, + + "displaceU": inDisplaceU, + "displaceV": inDisplaceV +}; + + +const trigger = op.outTrigger("trigger"); + + +const geomOut = op.outObject("geometry"); +const outPosition = op.outArray("Position"); +const outLength = op.outNumber("Position Amount"); +geomOut.ignoreValueSerialize = true; + + +Object.keys(inObj).forEach((key) => +{ + inObj[key].set(parametricBodies[0][key]); +}); + +shapes.set("Rectangle"); + +shapes.onChange = ({ value }) => +{ + const [shape] = parametricBodies.filter((s) => { return s.title === value; }); + Object.keys(inObj).forEach((key) => + { + inObj[key].set(shape[key]); + }); + + op.refreshParams(); + create(); +}; + +const cgl = op.patch.cgl; +let mesh = null; +let geom = null; + +const create = () => +{ + if (shouldRender) + { + const uSegments = inSegmentsU.get(); + const vSegments = inSegmentsV.get(); + + let uMax = inMinU.get() > inMaxU.get() ? inMinU.get() : inMaxU.get(); + let vMax = inMinV.get() > inMaxV.get() ? inMinV.get() : inMaxV.get(); + let uMin = inMaxU.get() < inMinU.get() ? inMaxU.get() : inMinU.get(); + let vMin = inMaxV.get() < inMinV.get() ? inMaxV.get() : inMinV.get(); + + uMax = multByPiU.get() ? uMax * Math.PI : uMax; + vMax = multByPiV.get() ? vMax * Math.PI : vMax; + + uMin = multByPiU.get() ? uMin * Math.PI : uMin; + vMin = multByPiV.get() ? vMin * Math.PI : vMin; + + const displaceU = inDisplaceU.get(); + const displaceV = inDisplaceV.get(); + + const xFunctionString = inFunctionX.get(); + const yFunctionString = inFunctionY.get(); + const zFunctionString = inFunctionZ.get(); + + const scaleX = inScaleX.get(); + const scaleY = inScaleY.get(); + const scaleZ = inScaleZ.get(); + let xFunction; + let yFunction; + let zFunction; + + const coords = []; + const texCoords = []; + const paramVertexIndices = []; + let normals; + let tangents; + let bitangents; + + try + { + xFunction = new Function("m", "u", "v", `with(m) { return ${xFunctionString} }`); + yFunction = new Function("m", "u", "v", `with(m) { return ${yFunctionString} }`); + zFunction = new Function("m", "u", "v", `with(m) { return ${zFunctionString} }`); + + for (let i = 0; i <= uSegments; i += 1) + { + for (let j = 0; j <= vSegments; j += 1) + { + const u_tex = uMin + (i * (uMax - uMin) / uSegments); + const v_tex = vMin + (j * (vMax - vMin) / vSegments); + + const u = displaceU + u_tex; + const v = displaceV + v_tex; + + let x = xFunction(Math, u, v); + let y = yFunction(Math, u, v); + let z = zFunction(Math, u, v); + + if (shouldScale) + { + x *= scaleX; + y *= scaleY; + z *= scaleZ; + } + + coords.push(x, y, z); + + + texCoords.push(CABLES.map(u_tex, uMin, uMax, 0, 1), CABLES.map(v_tex, vMin, vMax, 1, 0)); + + if (i < uSegments && j < vSegments) + { + paramVertexIndices.push(i * (vSegments + 1) + j); + paramVertexIndices.push((i + 1) * (vSegments + 1) + j); + paramVertexIndices.push((i) * (vSegments + 1) + j + 1); + + paramVertexIndices.push((i + 1) * (vSegments + 1) + j); + paramVertexIndices.push((i + 1) * (vSegments + 1) + j + 1); + paramVertexIndices.push((i) * (vSegments + 1) + j + 1); + } + } + } + + tangents = []; + bitangents = []; + normals = []; + } + catch (e) + { + if (e instanceof ReferenceError || e instanceof SyntaxError) { op.logError(e); return; } + op.log(e); + } + geom = new CGL.Geometry("parametric surface"); + geom.clear(); + geom.vertices = coords || []; + geom.texCoords = texCoords; + geom.verticesIndices = paramVertexIndices; + + geom.calculateNormals(); + geom.calcTangentsBitangents(); + + if (geom.vertices.length == 0) return; + + if (!mesh) + { + mesh = new CGL.Mesh(cgl, geom); + } + else + { + mesh.setGeom(geom); + } + geomOut.set(null); + geomOut.set(geom); + } + outPosition.set(null); + outPosition.set(geom.vertices); + outLength.set(geom.vertices.length); + shouldRender = false; + shouldScale = false; +}; + +create(); + +const setRender = () => +{ + shouldScale = true; + shouldRender = true; +}; +const setScale = () => +{ + shouldScale = true; + shouldRender = true; +}; + +draw.onChange = + inSegmentsU.onChange = + inSegmentsV.onChange = + inMaxU.onChange = + inMaxV.onChange = + inMinU.onChange = + inMinV.onChange = + inDisplaceV.onChange = + inDisplaceU.onChange = + multByPiV.onChange = + multByPiU.onChange = + inFunctionX.onChange = + inFunctionY.onChange = + inFunctionZ.onChange = setRender; + +inScaleX.onChange = +inScaleY.onChange = +inScaleZ.onChange = setScale; + +render.onTriggered = function () +{ + if (shouldRender) create(); + if (draw.get()) + { + mesh.render(cgl.getShader()); + } + trigger.trigger(); +}; + + +}; + +Ops.Gl.Meshes.ParametricSurface.prototype = new CABLES.Op(); +CABLES.OPS["5d3211a6-801d-4e59-ba6f-546ffe5fd34a"]={f:Ops.Gl.Meshes.ParametricSurface,objName:"Ops.Gl.Meshes.ParametricSurface"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.PointCloudFromArray +// +// ************************************************************** + +Ops.Gl.Meshes.PointCloudFromArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + arr = op.inArray("Array", 3), + numPoints = op.inValueInt("Num Points"), + outTrigger = op.outTrigger("Trigger out"), + outGeom = op.outObject("Geometry"), + pTexCoordRand = op.inValueBool("Scramble Texcoords", true), + seed = op.inValue("Seed", 1), + inCoords = op.inArray("Coordinates", 2), + vertCols = op.inArray("Vertex Colors", 4); + +op.toWorkPortsNeedToBeLinked(arr, exe); +op.setPortGroup("Texture Coordinates", [pTexCoordRand, seed, inCoords]); + +const cgl = op.patch.cgl; +const geom = new CGL.Geometry("pointcloudfromarray"); +let deactivated = false; +let mesh = null; +let texCoords = []; +let needsRebuild = true; +let showingError = false; + +arr.setUiAttribs({ "title": "Positions" }); +inCoords.setUiAttribs({ "title": "Texture Coordinates" }); + +inCoords.onChange = + pTexCoordRand.onChange = updateTexCoordsPorts; +vertCols.onChange = updateVertCols; +numPoints.onChange = updateNumVerts; +seed.onChange = arr.onChange = vertCols.onLinkChanged = reset; + +exe.onTriggered = doRender; + +function doRender() +{ + outTrigger.trigger(); + if (CABLES.UI) + { + let shader = cgl.getShader(); + if (shader.glPrimitive != cgl.gl.POINTS) op.setUiError("nopointmat", "Using a Material not made for point rendering. Try to use PointMaterial."); + else op.setUiError("nopointmat", null); + } + + if (needsRebuild || !mesh) rebuild(); + if (!deactivated && mesh) mesh.render(cgl.getShader()); +} + +function reset() +{ + deactivated = arr.get() == null; + + if (!deactivated)needsRebuild = true; + else needsRebuild = false; +} + +function updateTexCoordsPorts() +{ + if (inCoords.isLinked()) + { + seed.setUiAttribs({ "greyout": true }); + pTexCoordRand.setUiAttribs({ "greyout": true }); + } + else + { + pTexCoordRand.setUiAttribs({ "greyout": false }); + + if (!pTexCoordRand.get()) seed.setUiAttribs({ "greyout": true }); + else seed.setUiAttribs({ "greyout": false }); + } + + mesh = null; + needsRebuild = true; +} + +function updateVertCols() +{ + if (!vertCols.get()) return; + if (!geom.vertexColors) reset(); + + if (mesh)mesh.setAttribute(CGL.SHADERVAR_VERTEX_COLOR, vertCols.get(), 4); +} + +function updateNumVerts() +{ + if (mesh) + { + mesh.setNumVertices(Math.min(geom.vertices.length / 3, numPoints.get())); + if (numPoints.get() == 0)mesh.setNumVertices(geom.vertices.length / 3); + } +} + +function rebuild() +{ + let verts = arr.get(); + + if (!verts || verts.length == 0) + { + // mesh=null; + return; + } + + if (verts.length % 3 !== 0) + { + // if (!showingError) + // { + op.setUiError("div3", "Array length not multiple of 3"); + + // op.uiAttr({ "error": "Array length not divisible by 3!" }); + // showingError = true; + // } + return; + } + else op.setUiError("div3", null); + + if (geom.vertices.length == verts.length && mesh && !inCoords.isLinked() && !vertCols.isLinked()) + { + mesh.setAttribute(CGL.SHADERVAR_VERTEX_POSITION, verts, 3); + geom.vertices = verts; + needsRebuild = false; + + return; + } + + geom.clear(); + let num = verts.length / 3; + num = Math.abs(Math.floor(num)); + + if (num == 0) return; + + if (!texCoords || texCoords.length != num * 2) texCoords = new Float32Array(num * 2); // num*2;//= + + let changed = true; + let rndTc = pTexCoordRand.get(); + + if (!inCoords.isLinked()) + { + Math.randomSeed = seed.get(); + texCoords = []; // needed otherwise its using the reference to input incoords port + // let genCoords = !inCoords.isLinked(); + + for (let i = 0; i < num; i++) + { + if (geom.vertices[i * 3] != verts[i * 3] || + geom.vertices[i * 3 + 1] != verts[i * 3 + 1] || + geom.vertices[i * 3 + 2] != verts[i * 3 + 2]) + { + // if (genCoords) + if (rndTc) + { + texCoords[i * 2] = Math.seededRandom(); + texCoords[i * 2 + 1] = Math.seededRandom(); + } + else + { + texCoords[i * 2] = i / num; + texCoords[i * 2 + 1] = i / num; + } + } + } + } + + if (vertCols.get()) + { + if (!showingError && vertCols.get().length != num * 4) + { + op.uiAttr({ "error": "Color array does not have the correct length! (should be " + num * 4 + ")" }); + showingError = true; + mesh = null; + return; + } + + geom.vertexColors = vertCols.get(); + } + else geom.vertexColors = []; + + if (changed) + { + if (inCoords.isLinked()) texCoords = inCoords.get(); + + geom.setPointVertices(verts); + geom.setTexCoords(texCoords); + // geom.verticesIndices = []; + + if (mesh)mesh.dispose(); + mesh = new CGL.Mesh(cgl, geom, cgl.gl.POINTS); + + mesh.addVertexNumbers = true; + mesh.setGeom(geom); + + outGeom.set(null); + outGeom.set(geom); + } + + updateNumVerts(); + needsRebuild = false; +} + + +}; + +Ops.Gl.Meshes.PointCloudFromArray.prototype = new CABLES.Op(); +CABLES.OPS["0a6d9c6f-6459-45ca-88ad-268a1f7304db"]={f:Ops.Gl.Meshes.PointCloudFromArray,objName:"Ops.Gl.Meshes.PointCloudFromArray"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.PointCloudFromTexture +// +// ************************************************************** + +Ops.Gl.Meshes.PointCloudFromTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"vertposbody_vert":"\n// float tx=mod(attrVertIndex,(MOD_texSize))/(MOD_texSize);\n// float ty=float(int((attrVertIndex/(MOD_texSize))))/(MOD_texSize);\n\nhighp vec4 col=texture(MOD_tex,texCoord);//vec2(tx,ty));\n\n// vec4 col=texture(MOD_tex,texCoord);\n\n#ifdef MOD_HAS_PS_TEX\n psMul*=texture(MOD_texPointSize,texCoord).r;\n // psMul*=attrVertIndex/21000.0;\n#endif\n\nvec3 MOD_pos=col.xyz+pos.xyz;\n\n#ifdef MOD_NORMALIZE\n MOD_pos=(MOD_pos.xyz-0.5)*2.0;\n#endif\n\n#ifdef MOD_AXIS_XYZ\n pos.xyz=MOD_pos.xyz+pos.xyz;\n#endif\n\n#ifdef MOD_AXIS_XY\n pos.xy=MOD_pos.xy+pos.xy;\n pos.z=0.0+pos.z;\n pos.w=1.0;\n#endif\n\n#ifdef MOD_REMOVEZERO\n if(MOD_pos.x==0.0 && MOD_pos.y==0.0 && MOD_pos.z==0.0) psMul=0.0;\n#endif\n",}; +const + render = op.inTrigger("render"), + inNum = op.inInt("Num Points", 0), + inAxis = op.inSwitch("Axis", ["XYZ", "XY"], "XYZ"), + inTex = op.inTexture("Texture", null, "texture"), + inTexPS = op.inTexture("Point Size", null, "texture"), + inNorm = op.inBool("Normalize", false), + inRemove0 = op.inBool("Remove Point at 0", false), + trigger = op.outTrigger("Trigger"), + outNumPoints = op.outNumber("Total Points", 0); + +const cgl = op.patch.cgl; +let mesh = null; +let numVerts = 0; +let currentNum = 0; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "priority": -10, + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": "", + "srcBodyVert": attachments.vertposbody_vert +}); + +mod.addUniformVert("t", "MOD_tex"); +mod.addUniformVert("t", "MOD_texPointSize"); +render.onTriggered = doRender; +updateDefines(); + +mod.addUniformVert("f", "MOD_texSize", 0); + +inNorm.onChange = + inTexPS.onChange = + inRemove0.onChange = + inAxis.onChange = updateDefines; + +let needsMeshSetup = true; + +inTex.onChange = () => { if (inTex.get() != CGL.Texture.getEmptyTexture(cgl))needsMeshSetup = true; }; +inNum.onChange = () => { needsMeshSetup = true; }; +updateDefines(); + +function updateDefines() +{ + mod.toggleDefine("MOD_REMOVEZERO", inRemove0); + mod.toggleDefine("MOD_AXIS_XY", inAxis.get() == "XY"); + mod.toggleDefine("MOD_AXIS_XYZ", inAxis.get() == "XYZ"); + mod.toggleDefine("MOD_NORMALIZE", inNorm.get()); + mod.toggleDefine("MOD_HAS_PS_TEX", inTexPS.get()); +} + +function doRender() +{ + if (inTex.get() == CGL.Texture.getEmptyTexture(cgl)) + { + trigger.trigger(); + + return; + } + + if (needsMeshSetup)setupMesh(); + if (!inTex.get() || !inTex.get().tex) return; + mod.bind(); + if (inTex.get())mod.pushTexture("MOD_tex", inTex.get().tex); + if (inTexPS.get())mod.pushTexture("MOD_texPointSize", inTexPS.get().tex); + + mod.setUniformValue("MOD_texSize", inTex.get().width + 1); + + if (numVerts > 0 && inNum.get() >= 0 && mesh) + { + if (inNum.get() > 0)mesh.setNumVertices(Math.min(numVerts, inNum.get())); + else mesh.setNumVertices(numVerts); + + mesh.render(cgl.getShader()); + } + + trigger.trigger(); + mod.unbind(); +} + +function setupMesh() +{ + if (!inTex.get()) + { + outNumPoints.set(0); + return; + } + const num = inTex.get().width * inTex.get().height; + outNumPoints.set(num); + + if (num == currentNum) return; + currentNum = num; + + let verts = new Float32Array(num * 3); + let texCoords = new Float32Array(num * 2); + + let bias = 0.5 * (1.0 / inTex.get().width); + + for (let x = 0; x < inTex.get().width; x++) + for (let y = 0; y < inTex.get().height; y++) + { + texCoords[(x + y * inTex.get().width) * 2] = (x / inTex.get().width) + bias; + texCoords[(x + y * inTex.get().width) * 2 + 1] = (y / inTex.get().height) + bias; + } + + const geom = new CGL.Geometry("pointcloudfromTexture"); + geom.setPointVertices(verts); + geom.setTexCoords(texCoords); + geom.verticesIndices = []; + numVerts = verts.length / 3; + + if (mesh)mesh.dispose(); + + if (numVerts > 0) mesh = new CGL.Mesh(cgl, geom, cgl.gl.POINTS); + + if (!mesh) return; + mesh.addVertexNumbers = true; + mesh.setGeom(geom); + needsMeshSetup = false; +} + + +}; + +Ops.Gl.Meshes.PointCloudFromTexture.prototype = new CABLES.Op(); +CABLES.OPS["105d8812-e641-47f2-bbaf-e45d5bc3ea8b"]={f:Ops.Gl.Meshes.PointCloudFromTexture,objName:"Ops.Gl.Meshes.PointCloudFromTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Polyhedron_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.Polyhedron_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const notation = op.inString("Receipt", "djmeD"); + +const outGeom = op.outObject("Geometry"); + +let obj = {}; + +let faces = []; +let vertices = []; +let vertexColors = []; + +notation.onChange = buildMesh; +buildMesh(); + +function getCellVertices(cellArr) +{ + const verts = []; + for (let i = 0; i < cellArr.length; i++) + { + verts.push(obj.positions[cellArr[i]]); + } + return verts; +} + +function addFace(verts) +{ + const colR = Math.random(); + const colG = Math.random(); + const colB = Math.random(); + + if (verts.length == 3) + { + for (var i = 0; i < verts.length; i++) + { + vertices.push(verts[i][0], verts[i][1], verts[i][2]); + + var index = vertices.length / 3 - 1; + faces.push(index); + vertexColors.push(colR, colG, colB, 1); + } + } + else + if (verts.length == 4) + { + for (var i = 0; i < verts.length; i++) + { + vertices.push(verts[i][0], verts[i][1], verts[i][2]); + vertexColors.push(colR, colG, colB, 1); + } + + var index = vertices.length / 3 - 4; + faces.push(index); + faces.push(index + 1); + faces.push(index + 2); + + faces.push(index + 2); + faces.push(index + 3); + faces.push(index + 0); + } + else + { + let avgX = 0; + let avgY = 0; + let avgZ = 0; + + for (var i = 0; i < verts.length; i++) + { + avgX += verts[i][0]; + avgY += verts[i][1]; + avgZ += verts[i][2]; + } + avgX /= verts.length; + avgY /= verts.length; + avgZ /= verts.length; + + vertices.push(avgX, avgY, avgZ); + vertexColors.push(colR, colG, colB, 1); + + var index = vertices.length / 3 - 1; + + for (var i = 0; i < verts.length; i++) + { + vertices.push(verts[i][0], verts[i][1], verts[i][2]); + vertexColors.push(colR, colG, colB, 1); + } + + const indexEnd = vertices.length / 3 - 1; + + for (var i = index; i < indexEnd; i++) + { + faces.push(index); + faces.push(i); + faces.push(i + 1); + } + + faces.push(index); + faces.push(indexEnd); + faces.push(index + 1); + } +} + +function buildMesh() +{ + op.setUiError("mesh", null); + + obj = {}; + + faces = []; + vertices = []; + vertexColors = []; + const geom = new CGL.Geometry(op.name); + + try + { + obj = conwayhart(String(notation.get())); + } + catch (ex) + { + op.setUiError("mesh", ex); + op.logError(ex); + return; + } + + for (let i = 0; i < obj.cells.length; i++) + { + const verts = getCellVertices(obj.cells[i]); + addFace(verts, geom); + } + + geom.vertices = vertices; + geom.verticesIndices = faces; + geom.vertexColors = vertexColors; + geom.calculateNormals(); + geom.calcTangentsBitangents(); + + outGeom.set(null); + outGeom.set(geom); +} + + +}; + +Ops.Gl.Meshes.Polyhedron_v2.prototype = new CABLES.Op(); +CABLES.OPS["203a3bcd-7fff-4791-aab7-378f2b68bf29"]={f:Ops.Gl.Meshes.Polyhedron_v2,objName:"Ops.Gl.Meshes.Polyhedron_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Pyramid +// +// ************************************************************** + +Ops.Gl.Meshes.Pyramid = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let render = op.inTrigger("Render"); + +let sizeW = op.inValue("Width", 1); +let sizeL = op.inValue("Length", 1); +let sizeH = op.inValue("Height", 2); + +let inSmooth = op.inValueBool("Smooth", false); + +let inDraw = op.inValueBool("Draw", true); + +let trigger = op.outTrigger("trigger"); +let geomOut = op.outObject("geometry"); + +let geom = null; +let cgl = op.patch.cgl; +let mesh = null; + +sizeW.onChange = create; +sizeH.onChange = create; +sizeL.onChange = create; +inSmooth.onChange = create; +create(); + +render.onTriggered = function () +{ + if (inDraw.get())mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +function create() +{ + if (!geom)geom = new CGL.Geometry(op.name); + let w = sizeW.get(); + let h = sizeH.get(); + let l = sizeL.get(); + + geom.vertices = [ + // -w,-l,0, + // w,-l,0, + // w,l,0, + // -w,l,0, + // 0,0,h, + -w, 0, -l, + w, 0, -l, + w, 0, l, + -w, 0, l, + 0, h, 0 + ]; + + geom.vertexNormals = [ + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0 + ]; + + geom.texCoords = [ + 0.5, 0.0, + 1.0, 1.0, + 0.0, 1.0, + 0.0, 1.0, + 0.0, 1.0, + ]; + + geom.verticesIndices = [ + 0, 1, 2, + 0, 2, 3, // bottom + + 4, 1, 0, + 4, 3, 2, + 0, 3, 4, + 4, 2, 1 + ]; + + if (!inSmooth.get())geom.unIndex(); + geom.calculateNormals({ "forceZUp": false }); + + mesh = new CGL.Mesh(cgl, geom); + geomOut.set(null); + geomOut.set(geom); +} + + +}; + +Ops.Gl.Meshes.Pyramid.prototype = new CABLES.Op(); +CABLES.OPS["22b175c4-591b-47be-9ac7-2eaf5ebaf641"]={f:Ops.Gl.Meshes.Pyramid,objName:"Ops.Gl.Meshes.Pyramid"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.QuadWarpTexture +// +// ************************************************************** + +Ops.Gl.Meshes.QuadWarpTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"shader_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\n\nUNI vec2 a;\nUNI vec2 b;\nUNI vec2 c;\nUNI vec2 d;\n{{MODULES_HEAD}}\n\nfloat cross2d( in vec2 a, in vec2 b ) { return a.x*b.y - a.y*b.x; }\n\n// original function by iq : https://www.shadertoy.com/view/lsBSDm\nvec2 invBilinear( in vec2 p, in vec2 a, in vec2 b, in vec2 c, in vec2 d )\n{\n vec2 e = b-a;\n vec2 f = d-a;\n vec2 g = a-b+c-d;\n vec2 h = p-a;\n\n float k2 = cross2d( g, f );\n float k1 = cross2d( e, f ) + cross2d( h, g );\n float k0 = cross2d( h, e );\n\n float w = k1*k1 - 4.0*k0*k2;\n if( w<0.0 ) return vec2(-1.0);\n w = sqrt( w );\n\n // will fail for k0=0, which is only on the ba edge\n float v = 2.0*k0/(-k1 - w);\n if( v<0.0 || v>1.0 ) v = 2.0*k0/(-k1 + w);\n\n float u = (h.x - f.x*v)/(e.x + g.x*v);\n if( u<0.0 || u>1.0 || v<0.0 || v>1.0 ) return vec2(-1.0);\n return vec2( u, v );\n}\n\nvoid main()\n{\n vec2 uv=invBilinear(texCoord,a,b,c,d);\n vec4 col=texture(tex,1.0-uv);\n if(uv.x==-1.0 || uv.y==-1.0)discard;\n {{MODULE_COLOR}}\n outColor = col;\n}\n","shader_vert":"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\n\nOUT vec2 texCoord;\nIN vec2 attrTexCoord;\n\nvoid main()\n{\n vec4 pos=vec4(vPosition, 1.0);\n\n texCoord=attrTexCoord;\n\n gl_Position = projMatrix * mvMatrix * pos;\n}\n",}; +const + render = op.inTrigger("render"), + x1 = op.inFloat("A X", 0), + y1 = op.inFloat("A Y", 0), + x2 = op.inFloat("B X", 1), + y2 = op.inFloat("B Y", 0), + x3 = op.inFloat("C X", 1), + y3 = op.inFloat("C Y", 1), + x4 = op.inFloat("D X", 0), + y4 = op.inFloat("D Y", 1), + flipY = op.inValueBool("Flip Y"), + flipX = op.inValueBool("Flip X"), + inTexture = op.inTexture("Texture"), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +let mesh = null; +const geom = new CGL.Geometry("fullscreen rectangle"); +let x = 0, y = 0, z = 0, w = 0, h = 0; +let updateShaderLater = true; + +flipX.onChange = flipY.onChange = rebuildFlip; + +const shader = new CGL.Shader(cgl, "fullscreenrectangle"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +shader.setSource(attachments.shader_vert, attachments.shader_frag); +shader.fullscreenRectUniform = new CGL.Uniform(shader, "t", "tex", 0); +shader.addUniformFrag("2f", "a", x1, y1); +shader.addUniformFrag("2f", "b", x2, y2); +shader.addUniformFrag("2f", "c", x3, y3); +shader.addUniformFrag("2f", "d", x4, y4); + +render.onTriggered = doRender; +op.toWorkPortsNeedToBeLinked(render); +op.toWorkPortsNeedToBeLinked(inTexture); + +inTexture.onChange = function () +{ + updateShaderLater = true; +}; + +function updateShader() +{ + let tex = inTexture.get(); +} + +op.preRender = function () +{ + updateShader(); + shader.bind(); + if (mesh)mesh.render(shader); + doRender(); +}; + +function doRender() +{ + if (cgl.getViewPort()[2] != w || cgl.getViewPort()[3] != h || !mesh) rebuild(); + + if (updateShaderLater) updateShader(); + + cgl.pushPMatrix(); + mat4.identity(cgl.pMatrix); + mat4.ortho(cgl.pMatrix, 0, w, h, 0, -10.0, 1000); + + cgl.pushModelMatrix(); + mat4.identity(cgl.mMatrix); + + cgl.pushViewMatrix(); + mat4.identity(cgl.vMatrix); + + + if (inTexture.get()) cgl.setTexture(0, inTexture.get().tex); + else cgl.setTexture(0, CGL.Texture.getTempTexture(cgl).tex); + + + cgl.gl.clear(cgl.gl.DEPTH_BUFFER_BIT); + mesh.render(shader); + cgl.gl.clear(cgl.gl.DEPTH_BUFFER_BIT); + + cgl.popPMatrix(); + cgl.popModelMatrix(); + cgl.popViewMatrix(); + + trigger.trigger(); +} + +function rebuildFlip() +{ + mesh = null; +} + +function rebuild() +{ + const currentViewPort = cgl.getViewPort(); + + if (currentViewPort[2] == w && currentViewPort[3] == h && mesh) return; + + let xx = 0, xy = 0; + + w = currentViewPort[2]; + h = currentViewPort[3]; + + geom.vertices = new Float32Array([ + xx + w, xy + h, 0.0, + xx, xy + h, 0.0, + xx + w, xy, 0.0, + xx, xy, 0.0 + ]); + + let tc = null; + + if (flipY.get()) + tc = new Float32Array([ + 1.0, 0.0, + 0.0, 0.0, + 1.0, 1.0, + 0.0, 1.0 + ]); + else + tc = new Float32Array([ + 1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 0.0, 0.0 + ]); + + if (flipX.get()) + { + tc[0] = 0.0; + tc[2] = 1.0; + tc[4] = 0.0; + tc[6] = 1.0; + } + + geom.setTexCoords(tc); + + geom.verticesIndices = new Uint16Array([ + 2, 1, 0, + 3, 1, 2 + ]); + + geom.vertexNormals = new Float32Array([ + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + ]); + geom.tangents = new Float32Array([ + -1, 0, 0, + -1, 0, 0, + -1, 0, 0, + -1, 0, 0]); + geom.biTangents == new Float32Array([ + 0, -1, 0, + 0, -1, 0, + 0, -1, 0, + 0, -1, 0]); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); +} + + +}; + +Ops.Gl.Meshes.QuadWarpTexture.prototype = new CABLES.Op(); +CABLES.OPS["14521baa-af4e-44d5-a92c-3cb54e9aa697"]={f:Ops.Gl.Meshes.QuadWarpTexture,objName:"Ops.Gl.Meshes.QuadWarpTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.RectangleFrame +// +// ************************************************************** + +Ops.Gl.Meshes.RectangleFrame = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + width = op.inValueFloat("Width", 1), + height = op.inValueFloat("Height", 1), + thickness = op.inValueFloat("Thickness", -0.1), + pivotX = op.inSwitch("pivot x", ["center", "left", "right"], "center"), + pivotY = op.inSwitch("pivot y", ["center", "top", "bottom"], "center"), + + trigger = op.outTrigger("trigger"), + geomOut = op.outObject("Geometry"), + + drawTop = op.inValueBool("Draw Top", true), + drawBottom = op.inValueBool("Draw Bottom", true), + drawLeft = op.inValueBool("Draw Left", true), + drawRight = op.inValueBool("Draw Right", true), + active = op.inValueBool("Active", true); + +op.setPortGroup("Geometry", [width, height, thickness]); +op.setPortGroup("Transform", [pivotX, pivotY]); +op.setPortGroup("Sections", [drawTop, drawBottom, drawLeft, drawRight]); + +const cgl = op.patch.cgl; +let mesh = null; +const geom = new CGL.Geometry(op.name); +geom.tangents = []; +geom.biTangents = []; + +geomOut.ignoreValueSerialize = true; + +width.onChange = + pivotX.onChange = + pivotY.onChange = + height.onChange = + thickness.onChange = + drawTop.onChange = + drawBottom.onChange = + drawLeft.onChange = + drawRight.onChange = create; + +create(); + +render.onTriggered = function () +{ + if (active.get()) mesh.render(cgl.getShader()); + + trigger.trigger(); +}; + +function create() +{ + const w = width.get(); + const h = height.get(); + let x = -w / 2; + let y = -h / 2; + const th = thickness.get();//* Math.min(height.get(),width.get())*-0.5; + + if (pivotX.get() == "right") x = -w; + else if (pivotX.get() == "left") x = 0; + + if (pivotY.get() == "top") y = -h; + else if (pivotY.get() == "bottom") y = 0; + + geom.vertices.length = 0; + geom.vertices.push( + x, y, 0, + x + w, y, 0, + x + w, y + h, 0, + x, y + h, 0, + x - th, y - th, 0, + x + w + th, y - th, 0, + x + w + th, y + h + th, 0, + x - th, y + h + th, 0 + ); + + if (geom.vertexNormals.length === 0) geom.vertexNormals = [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]; + if (geom.tangents.length === 0) geom.tangents = [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0]; + if (geom.biTangents.length === 0) geom.biTangents = [0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]; + + if (geom.verticesIndices)geom.verticesIndices.length = 0; + else geom.verticesIndices = []; + + const vertInd = []; + if (drawBottom.get()) vertInd.push(0, 1, 4, 1, 5, 4); + if (drawRight.get()) vertInd.push(1, 2, 5, 5, 2, 6); + if (drawTop.get()) vertInd.push(7, 6, 3, 6, 2, 3); + if (drawLeft.get()) vertInd.push(0, 4, 3, 4, 7, 3); + geom.verticesIndices = vertInd; + + if (geom.texCoords.length === 0) + { + const tc = []; + for (let i = 0, j = 0; i < geom.vertices.length; i += 3, j += 2) + { + tc[j] = geom.vertices[i + 0] / w - 0.5; + tc[j + 1] = geom.vertices[i + 1] / h - 0.5; + } + geom.texCoords = tc; + } + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); + + geomOut.set(null); + geomOut.set(geom); +} + + +}; + +Ops.Gl.Meshes.RectangleFrame.prototype = new CABLES.Op(); +CABLES.OPS["e3a24a1a-a74b-4c38-b492-63abca68f6d1"]={f:Ops.Gl.Meshes.RectangleFrame,objName:"Ops.Gl.Meshes.RectangleFrame"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.RectangleRounded +// +// ************************************************************** + +Ops.Gl.Meshes.RectangleRounded = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const render = op.inTrigger('render'); +const inSegments = op.inValueInt('Segments', 24); +const trigger = op.outTrigger('trigger'); +const sizeW = op.inValueFloat('width', 1); +const sizeH = op.inValueFloat('height', 1); +const borderRadius = op.inValueSlider('border radius', 0.5); + +const geom = new CGL.Geometry('triangle'); +const geomOut = op.outObject('geometry'); + +geomOut.ignoreValueSerialize = true; + +op.setPortGroup('Size', [sizeW, sizeH, borderRadius, inSegments]); + +const inTopLeftCorner = op.inValueBool('Top Left', true); +const inTopRightCorner = op.inValueBool('Top Right', true); +const inBottomLeftCorner = op.inValueBool('Bottom Left', true); +const inBottomRightCorner = op.inValueBool('Bottom Right', true); +const CORNER_PORTS = [inTopLeftCorner, inTopRightCorner, inBottomLeftCorner, inBottomRightCorner]; +CORNER_PORTS.forEach((port) => { + port.onChange = create; +}); + +op.setPortGroup('Round Corner', CORNER_PORTS); + +const draw = op.inValueBool('Draw', true); +op.setPortGroup('Draw', [draw]); + +const cgl = op.patch.cgl; +let mesh = null; +sizeW.onChange = create; +sizeH.onChange = create; +borderRadius.onChange = create; +inSegments.onChange = create; + +create(); + +render.onTriggered = function () { + if (draw.get()) mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +function create() { + const w = Math.abs(sizeW.get()); + const h = Math.abs(sizeH.get()); + + const r = w < h ? (borderRadius.get() * w) / 2 : (borderRadius.get() * h) / 2; + + const wi = w - 2 * r; + const hi = h - 2 * r; + const wiHalf = wi / 2; + const hiHalf = hi / 2; + + const segments = Math.abs(inSegments.get() || 1); + + const topLeftCircleMiddle = [-1 * wiHalf, hiHalf, 0]; + const bottomLeftCircleMiddle = [-1 * wiHalf, -1 * hiHalf, 0]; + const topRightCircleMiddle = [wiHalf, hiHalf, 0]; + const bottomRightCircleMiddle = [wiHalf, -1 * hiHalf, 0]; + + const circleVerts = []; + let lastX = 0; + let lastY = 0; + + // top left circle + if (inTopLeftCorner.get()) { + for (let i = 0; i <= segments; i += 1) { + const x = topLeftCircleMiddle[0] + (0 - r * Math.cos((i * Math.PI) / 2 / segments)); + const y = topLeftCircleMiddle[1] + r * Math.sin((i * Math.PI) / 2 / segments); + + circleVerts.push(x, y, 0); + + if (i > 1) { + circleVerts.push(lastX, lastY, 0); + } + + if (i <= segments - 1) circleVerts.push(...topLeftCircleMiddle); + + lastX = x; + lastY = y; + } + } else { + circleVerts.push(...topLeftCircleMiddle); + circleVerts.push(-wiHalf, hiHalf + r, 0); + circleVerts.push(-wiHalf - r, hiHalf + r, 0); + + circleVerts.push(...topLeftCircleMiddle); + circleVerts.push(-wiHalf - r, hiHalf + r, 0); + circleVerts.push(-wiHalf - r, hiHalf, 0); + } + + if (inTopRightCorner.get()) { + // top right circle + for (let i = 0; i <= segments; i += 1) { + const x = topRightCircleMiddle[0] + r * Math.cos((i * Math.PI) / 2 / segments); + const y = topRightCircleMiddle[1] + r * Math.sin((i * Math.PI) / 2 / segments); + + if (i > 1) { + circleVerts.push(...topRightCircleMiddle, lastX, lastY, 0); + } + + circleVerts.push(x, y, 0); + + if (i === segments - 1) circleVerts.push(...topRightCircleMiddle); + + lastX = x; + lastY = y; + } + } else { + circleVerts.push(...topRightCircleMiddle); + circleVerts.push(wiHalf + r, hiHalf, 0); + circleVerts.push(wiHalf + r, hiHalf + r, 0); + + circleVerts.push(...topRightCircleMiddle); + circleVerts.push(wiHalf + r, hiHalf + r, 0); + circleVerts.push(wiHalf, hiHalf + r, 0); + } + + if (inBottomRightCorner.get()) { + // bottom right circle + for (let i = 0; i <= segments; i += 1) { + const x = bottomRightCircleMiddle[0] + r * Math.cos((i * Math.PI) / 2 / segments); + const y = bottomRightCircleMiddle[1] + r * -1 * Math.sin((i * Math.PI) / 2 / segments); + + circleVerts.push(x, y, 0); + + if (i > 1) { + circleVerts.push(lastX, lastY, 0); + } + + if (i <= segments - 1) circleVerts.push(...bottomRightCircleMiddle); + + lastX = x; + lastY = y; + } + } else { + circleVerts.push(...bottomRightCircleMiddle); + circleVerts.push(wiHalf + r, -hiHalf - r, 0); + circleVerts.push(wiHalf + r, -hiHalf, 0); + + circleVerts.push(...bottomRightCircleMiddle); + circleVerts.push(wiHalf, -hiHalf - r, 0); + circleVerts.push(wiHalf + r, -hiHalf - r, 0); + } + + if (inBottomLeftCorner.get()) { + // bottom left circle + for (let i = 0; i <= segments; i += 1) { + const x = bottomLeftCircleMiddle[0] + r * -1 * Math.cos((i * Math.PI) / 2 / segments); + const y = bottomLeftCircleMiddle[1] + r * -1 * Math.sin((i * Math.PI) / 2 / segments); + + if (i > 1) { + circleVerts.push(lastX, lastY, 0); + } + + circleVerts.push(x, y, 0); + + if (i <= segments - 1) circleVerts.push(...bottomLeftCircleMiddle); + + lastX = x; + lastY = y; + } + } else { + circleVerts.push(...bottomLeftCircleMiddle); + circleVerts.push(-wiHalf - r, -hiHalf - r, 0); + circleVerts.push(-wiHalf, -hiHalf - r, 0); + + circleVerts.push(...bottomLeftCircleMiddle); + circleVerts.push(-wiHalf - r, -hiHalf, 0); + circleVerts.push(-wiHalf - r, -hiHalf - r, 0); + } + + geom.vertices = [ + //inner rectangle + + -1*wiHalf, -hiHalf, 0, + wiHalf, hiHalf, 0, + -1*wiHalf, hiHalf, 0, + + + -1*wiHalf, -1*hiHalf,0, + wiHalf, -1*hiHalf, 0, + wiHalf, hiHalf, 0, + + // left rectangle + + -1*wiHalf-r, -1*hiHalf, 0, + -1*wiHalf, -1*hiHalf, 0, + -1*wiHalf-r, hiHalf, 0, + + -1*wiHalf-r, hiHalf, 0, + -1*wiHalf, -1*hiHalf, 0, + -1*wiHalf, hiHalf, 0, + + // top rectangle + + -1*wiHalf, hiHalf, 0, + wiHalf, hiHalf+r, 0, + -1*wiHalf, hiHalf+r, 0, + + wiHalf, hiHalf + r, 0, + -1*wiHalf, hiHalf, 0, + wiHalf, hiHalf, 0, + + // bottom rectangle + -1*wiHalf, -1*hiHalf, 0, + -1*wiHalf, -1*hiHalf-r, 0, + wiHalf, -1*hiHalf-r, 0, + + wiHalf, -1*hiHalf, 0, + -1*wiHalf, -1*hiHalf, 0, + wiHalf, -1*hiHalf-r, 0, + + // right rectangle + + wiHalf+r, hiHalf, 0, + wiHalf, hiHalf, 0, + wiHalf+r, -1*hiHalf, 0, + + wiHalf+r, -1*hiHalf, 0, + wiHalf, hiHalf, 0, + wiHalf, -1*hiHalf, 0, + ...circleVerts + ] + + geom.texCoords = []; + const wAbs = Math.abs(w); + const hAbs = Math.abs(h); + + for (let i = 0; i < geom.vertices.length; i += 3) { + geom.texCoords[(i / 3) * 2 + 0] = Math.abs(geom.vertices[i + 0] / -wAbs - 0.5); + geom.texCoords[(i / 3) * 2 + 1] = Math.abs(geom.vertices[i + 1] / hAbs - 0.5); + } + + + geom.vertexNormals = geom.vertices.map((vert, i) => (i % 3 === 2 ? 1.0 : 0.0)); + geom.tangents = geom.vertices.map((vert, i) => (i % 3 === 0 ? -1.0 : 0.0)); + geom.biTangents = geom.vertices.map((vert, i) => (i % 3 === 1 ? -1.0 : 0.0)); + + if (geom.vertices.length == 0) return; + if (mesh) mesh.dispose(); + mesh = null; + mesh = new CGL.Mesh(cgl, geom); + geomOut.set(null); + geomOut.set(geom); +} + + +}; + +Ops.Gl.Meshes.RectangleRounded.prototype = new CABLES.Op(); +CABLES.OPS["86c99074-4929-44d0-a826-49e7f8bdf5c4"]={f:Ops.Gl.Meshes.RectangleRounded,objName:"Ops.Gl.Meshes.RectangleRounded"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Rectangle_v3 +// +// ************************************************************** + +Ops.Gl.Meshes.Rectangle_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + doRender = op.inValueBool("dorender", true), + width = op.inValue("width", 1), + height = op.inValue("height", 1), + pivotX = op.inSwitch("pivot x", ["left", "center", "right"], "center"), + pivotY = op.inSwitch("pivot y", ["top", "center", "bottom"], "center"), + axis = op.inSwitch("axis", ["xy", "xz"], "xy"), + flipTcX = op.inBool("Flip TexCoord X", false), + flipTcY = op.inBool("Flip TexCoord Y", true), + nColumns = op.inValueInt("num columns", 1), + nRows = op.inValueInt("num rows", 1), + trigger = op.outTrigger("trigger"), + geomOut = op.outObject("geometry", null, "geometry"); + +geomOut.ignoreValueSerialize = true; + +const geom = new CGL.Geometry("rectangle"); + +doRender.setUiAttribs({ "title": "Render" }); +render.setUiAttribs({ "title": "Trigger" }); +trigger.setUiAttribs({ "title": "Next" }); +op.setPortGroup("Pivot", [pivotX, pivotY, axis]); +op.setPortGroup("Size", [width, height]); +op.setPortGroup("Structure", [nColumns, nRows]); +op.toWorkPortsNeedToBeLinked(render); + +let mesh = null; +let needsRebuild = true; + +axis.onChange = + pivotX.onChange = + pivotY.onChange = + flipTcX.onChange = + flipTcY.onChange = + width.onChange = + height.onChange = + nRows.onChange = + nColumns.onChange = rebuildLater; + +function rebuildLater() +{ + needsRebuild = true; +} + +render.onLinkChanged = () => +{ + if (!trigger.isLinked()) + { + if (mesh) mesh.dispose(); + mesh = null; + geomOut.set(null); + rebuildLater(); + } +}; + +op.preRender = +render.onTriggered = function () +{ + if (needsRebuild) rebuild(); + if (mesh && doRender.get()) mesh.render(op.patch.cg.getShader()); + trigger.trigger(); +}; + +op.onDelete = function () +{ + if (mesh)mesh.dispose(); + rebuildLater(); +}; + +function rebuild() +{ + let w = width.get(); + let h = parseFloat(height.get()); + let x = 0; + let y = 0; + + if (typeof w == "string")w = parseFloat(w); + if (typeof h == "string")h = parseFloat(h); + + if (pivotX.get() == "center") x = 0; + else if (pivotX.get() == "right") x = -w / 2; + else if (pivotX.get() == "left") x = +w / 2; + + if (pivotY.get() == "center") y = 0; + else if (pivotY.get() == "top") y = -h / 2; + else if (pivotY.get() == "bottom") y = +h / 2; + + const verts = []; + const tc = []; + const norms = []; + const tangents = []; + const biTangents = []; + const indices = []; + + const numRows = Math.round(nRows.get()); + const numColumns = Math.round(nColumns.get()); + + const stepColumn = w / numColumns; + const stepRow = h / numRows; + + op.log("rect build"); + + let c, r, a; + a = axis.get(); + for (r = 0; r <= numRows; r++) + { + for (c = 0; c <= numColumns; c++) + { + verts.push(c * stepColumn - width.get() / 2 + x); + if (a == "xz") verts.push(0.0); + verts.push(r * stepRow - height.get() / 2 + y); + if (a == "xy") verts.push(0.0); + + tc.push(c / numColumns); + tc.push(r / numRows); + + if (a == "xy") // default + { + norms.push(0, 0, 1); + tangents.push(1, 0, 0); + biTangents.push(0, 1, 0); + } + else if (a == "xz") + { + norms.push(0, 1, 0); + tangents.push(1, 0, 0); + biTangents.push(0, 0, 1); + } + } + } + + for (c = 0; c < numColumns; c++) + { + for (r = 0; r < numRows; r++) + { + const ind = c + (numColumns + 1) * r; + const v1 = ind; + const v2 = ind + 1; + const v3 = ind + numColumns + 1; + const v4 = ind + 1 + numColumns + 1; + + if (a == "xy") // default + { + indices.push(v1); + indices.push(v2); + indices.push(v3); + + indices.push(v3); + indices.push(v2); + indices.push(v4); + } + else + if (a == "xz") + { + indices.push(v1); + indices.push(v3); + indices.push(v2); + + indices.push(v2); + indices.push(v3); + indices.push(v4); + } + } + } + + if (flipTcY.get()) for (let i = 0; i < tc.length; i += 2)tc[i + 1] = 1.0 - tc[i + 1]; + if (flipTcX.get()) for (let i = 0; i < tc.length; i += 2)tc[i] = 1.0 - tc[i]; + + geom.clear(); + geom.vertices = verts; + geom.texCoords = tc; + geom.verticesIndices = indices; + geom.vertexNormals = norms; + geom.tangents = tangents; + geom.biTangents = biTangents; + + if (numColumns * numRows > 64000)geom.unIndex(); + + const cgl = op.patch.cgl; + + if (!mesh) mesh = op.patch.cg.createMesh(geom); + else mesh.setGeom(geom); + + geomOut.set(null); + geomOut.set(geom); + needsRebuild = false; +} + + +}; + +Ops.Gl.Meshes.Rectangle_v3.prototype = new CABLES.Op(); +CABLES.OPS["82bb2f8a-77f4-4218-a3ae-8f158f1b7fb1"]={f:Ops.Gl.Meshes.Rectangle_v3,objName:"Ops.Gl.Meshes.Rectangle_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.SimpleSpline +// +// ************************************************************** + +Ops.Gl.Meshes.SimpleSpline = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + inPoints = op.inArray("Points"), + numPoints = op.inValueInt("Num Points"), + strip = op.inBool("Line Strip", true), + outGeom = op.outObject("Geometry"), + // a=op.inSwitch("Mode",["Line Strip","Line Loop","Lines"]), // next version! + texCoords = op.inSwitch("TexCoords", ["0", "0-1", "Random", "Fill"], "0"), + inTexCoords = op.inArray("TexCoords Array"), + inVertCols = op.inArray("Vertex Colors"), + next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; +const geom = new CGL.Geometry("simplespline"); +outGeom.set(geom); + +geom.vertices = [0, 0, 0, 0, 0, 0, 0, 0, 0]; +const mesh = new CGL.Mesh(cgl, geom); +let buff = new Float32Array(); +let bufTexCoord = new Float32Array(); +let bufNorms = new Float32Array(); +let needsRebuild = true; +let attr; +let currentTexCoords = ""; + +inVertCols.onChange = +inTexCoords.onChange = +texCoords.onChange = + numPoints.onChange = + inPoints.onChange = + function () { needsRebuild = true; }; + +op.toWorkPortsNeedToBeLinked(inPoints); + +function rebuild() +{ + const points = inPoints.get(); + + if (!points || points.length === 0) return; + + const newLength = points.length; + + if (!(points instanceof Float32Array)) + { + if (newLength != buff.length) + { + buff = new Float32Array(newLength); + bufNorms = new Float32Array(newLength); + mesh.setAttribute(CGL.SHADERVAR_VERTEX_NORMAL, bufNorms, 3); + + buff.set(points); + } + else + { + buff.set(points); + } + } + else + { + buff = points; + } + + attr = mesh.setAttribute(CGL.SHADERVAR_VERTEX_POSITION, buff, 3); + + if (inVertCols.get()) + { + const attrTc = mesh.setAttribute(CGL.SHADERVAR_VERTEX_COLOR || "attrVertColor", inVertCols.get(), 4); + } + + const numTc = (newLength / 3) * 2; + + if (inTexCoords.get()) + { + const intc = inTexCoords.get(); + const attrTc = mesh.setAttribute(CGL.SHADERVAR_VERTEX_TEXCOORD, intc, 2); + } + else + if (currentTexCoords != texCoords.get() || mesh.getAttribute(CGL.SHADERVAR_VERTEX_TEXCOORD).numItems != numTc / 2) + { + currentTexCoords = texCoords.get(); + + if (bufTexCoord.length != numTc) bufTexCoord = new Float32Array(numTc); + + if (texCoords.get() == "0-1") + { + for (let i = 0; i < numTc; i += 2) + { + bufTexCoord[i] = i / numTc; + // bufTexCoord[i+1] = i / numTc; + bufTexCoord[i + 1] = 0.5; + } + } + else if (texCoords.get() == "Fill") + { + const sizel = Math.sqrt(numTc / 2); + + let idx = 0; + for (let j = 0; j < sizel; j++) + { + for (let i = 0; i < sizel; i++) + { + idx++; + bufTexCoord[idx * 2 + 0] = i / sizel; + bufTexCoord[idx * 2 + 1] = j / sizel; + } + } + } + else if (texCoords.get() == "Random") + { + for (let i = 0; i < numTc; i += 2) + { + bufTexCoord[i] = Math.random(); + bufTexCoord[i + 1] = Math.random(); + } + } + else + { + for (let i = 0; i < numTc; i += 2) + { + bufTexCoord[i] = 0; + bufTexCoord[i + 1] = 0; + } + } + const attrTc = mesh.setAttribute(CGL.SHADERVAR_VERTEX_TEXCOORD, bufTexCoord, 2); + } + + needsRebuild = false; +} + +render.onTriggered = function () +{ + if (!inPoints.get()) return; + + if (needsRebuild)rebuild(); + const shader = cgl.getShader(); + if (!shader) return; + + const oldPrim = shader.glPrimitive; + if (strip.get()) shader.glPrimitive = cgl.gl.LINE_STRIP; // LINE_LOOP + else shader.glPrimitive = cgl.gl.LINES; + + if (attr) + if (numPoints.get() <= 0)attr.numItems = buff.length / 3; + else attr.numItems = Math.min(numPoints.get(), buff.length / 3); + + if (mesh && buff.length !== 0) mesh.render(shader); + + shader.glPrimitive = oldPrim; + + next.trigger(); +}; + + +}; + +Ops.Gl.Meshes.SimpleSpline.prototype = new CABLES.Op(); +CABLES.OPS["af3fd0e8-ef3d-4124-a6ee-3482e9a85b45"]={f:Ops.Gl.Meshes.SimpleSpline,objName:"Ops.Gl.Meshes.SimpleSpline"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.SimpleWireframe +// +// ************************************************************** + +Ops.Gl.Meshes.SimpleWireframe = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + inGeom = op.inObject("Geometry", null, "geometry"), + next = op.outTrigger("Next"); +const cgl = op.patch.cgl; + +op.toWorkPortsNeedToBeLinked(inGeom); + +render.onTriggered = doRender; + +let mesh = null; +let verts = []; +let tc = []; +let normals = []; +let prim = cgl.gl.LINE_STRIP; + +inGeom.onChange = function () +{ + let geom = inGeom.get(); + if (!geom) + { + mesh = null; + return; + } + + verts.length = 0; + normals.length = 0; + tc.length = 0; + let i = 0; + + if (geom.isIndexed()) + { + for (i = 0; i < geom.verticesIndices.length; i += 3) + { + let index = geom.verticesIndices[i + 0]; + let index1 = geom.verticesIndices[i + 1]; + let index2 = geom.verticesIndices[i + 2]; + + verts.push(geom.vertices[index * 3 + 0], geom.vertices[index * 3 + 1], geom.vertices[index * 3 + 2]); + verts.push(geom.vertices[index1 * 3 + 0], geom.vertices[index1 * 3 + 1], geom.vertices[index1 * 3 + 2]); + verts.push(geom.vertices[index1 * 3 + 0], geom.vertices[index1 * 3 + 1], geom.vertices[index1 * 3 + 2]); + verts.push(geom.vertices[index2 * 3 + 0], geom.vertices[index2 * 3 + 1], geom.vertices[index2 * 3 + 2]); + verts.push(geom.vertices[index2 * 3 + 0], geom.vertices[index2 * 3 + 1], geom.vertices[index2 * 3 + 2]); + verts.push(geom.vertices[index * 3 + 0], geom.vertices[index * 3 + 1], geom.vertices[index * 3 + 2]); + + normals.push(geom.vertexNormals[index * 3 + 0], geom.vertexNormals[index * 3 + 1], geom.vertexNormals[index * 3 + 2]); + normals.push(geom.vertexNormals[index1 * 3 + 0], geom.vertexNormals[index1 * 3 + 1], geom.vertexNormals[index1 * 3 + 2]); + normals.push(geom.vertexNormals[index1 * 3 + 0], geom.vertexNormals[index1 * 3 + 1], geom.vertexNormals[index1 * 3 + 2]); + normals.push(geom.vertexNormals[index2 * 3 + 0], geom.vertexNormals[index2 * 3 + 1], geom.vertexNormals[index2 * 3 + 2]); + normals.push(geom.vertexNormals[index2 * 3 + 0], geom.vertexNormals[index2 * 3 + 1], geom.vertexNormals[index2 * 3 + 2]); + normals.push(geom.vertexNormals[index * 3 + 0], geom.vertexNormals[index * 3 + 1], geom.vertexNormals[index * 3 + 2]); + + tc.push(geom.texCoords[index * 2 + 0], geom.texCoords[index * 2 + 1]); + tc.push(geom.texCoords[index1 * 2 + 0], geom.texCoords[index1 * 2 + 1]); + tc.push(geom.texCoords[index1 * 2 + 0], geom.texCoords[index1 * 2 + 1]); + tc.push(geom.texCoords[index2 * 2 + 0], geom.texCoords[index2 * 2 + 1]); + tc.push(geom.texCoords[index2 * 2 + 0], geom.texCoords[index2 * 2 + 1]); + tc.push(geom.texCoords[index * 2 + 0], geom.texCoords[index * 2 + 1]); + } + prim = cgl.gl.LINES; + } + else + { + for (i = 0; i < geom.vertices.length; i += 9) + { + verts.push(geom.vertices[i + 0], geom.vertices[i + 1], geom.vertices[i + 2]); + verts.push(geom.vertices[i + 3], geom.vertices[i + 4], geom.vertices[i + 5]); + + verts.push(geom.vertices[i + 3], geom.vertices[i + 4], geom.vertices[i + 5]); + verts.push(geom.vertices[i + 6], geom.vertices[i + 7], geom.vertices[i + 8]); + + verts.push(geom.vertices[i + 6], geom.vertices[i + 7], geom.vertices[i + 8]); + verts.push(geom.vertices[i + 0], geom.vertices[i + 1], geom.vertices[i + 2]); + } + + prim = cgl.gl.LINES; + } + + geom = new CGL.Geometry("wireframelinegeom"); + // if(verts.length>60000)geom.setVertices(verts); + // else + geom.setVertices(verts); + geom.setTexCoords(tc); + geom.vertexNormals = normals; + + mesh = new CGL.Mesh(cgl, geom, prim); +}; + +function doRender() +{ + let shader = cgl.getShader(); + if (!shader) return; + + let oldPrim = shader.glPrimitive; + shader.glPrimitive = prim; + if (mesh) mesh.render(shader); + shader.glPrimitive = oldPrim; + next.trigger(); +} + + +}; + +Ops.Gl.Meshes.SimpleWireframe.prototype = new CABLES.Op(); +CABLES.OPS["89264e0a-512f-48a4-a7bb-ec8f12aa19c1"]={f:Ops.Gl.Meshes.SimpleWireframe,objName:"Ops.Gl.Meshes.SimpleWireframe"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Sphere_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.Sphere_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + TAU = Math.PI * 2, + cgl = op.patch.cgl, + inTrigger = op.inTrigger("render"), + inRadius = op.inValue("radius", 0.5), + inStacks = op.inValue("stacks", 32), + inSlices = op.inValue("slices", 32), + inStacklimit = op.inValueSlider("Filloffset", 1), + inDraw = op.inValueBool("Render", true), + outTrigger = op.outTrigger("trigger"), + outGeometry = op.outObject("geometry", null, "geometry"), + UP = vec3.fromValues(0, 1, 0), + RIGHT = vec3.fromValues(1, 0, 0); + +let + geom = new CGL.Geometry("Sphere"), + tmpNormal = vec3.create(), + tmpVec = vec3.create(), + needsRebuild = true, + mesh; + +function buildMesh() +{ + const + stacks = Math.ceil(Math.max(inStacks.get(), 2)), + slices = Math.ceil(Math.max(inSlices.get(), 3)), + stackLimit = Math.min(Math.max(inStacklimit.get() * stacks, 1), stacks), + radius = inRadius.get(); + let + positions = [], + texcoords = [], + normals = [], + tangents = [], + biTangents = [], + indices = [], + x, y, z, d, t, a, + o, u, v, i, j; + for (i = o = 0; i < stacks + 1; i++) + { + v = (i / stacks - 0.5) * Math.PI; + y = Math.sin(v); + a = Math.cos(v); + // for (j = 0; j < slices+1; j++) { + for (j = slices; j >= 0; j--) + { + u = (j / slices) * TAU; + x = Math.cos(u) * a; + z = Math.sin(u) * a; + + positions.push(x * radius, y * radius, z * radius); + // texcoords.push(i/(stacks+1),j/slices); + texcoords.push(j / slices, i / (stacks + 1)); + + d = Math.sqrt(x * x + y * y + z * z); + normals.push( + tmpNormal[0] = x / d, + tmpNormal[1] = y / d, + tmpNormal[2] = z / d + ); + + if (y == d) t = RIGHT; + else t = UP; + vec3.cross(tmpVec, tmpNormal, t); + vec3.normalize(tmpVec, tmpVec); + Array.prototype.push.apply(tangents, tmpVec); + vec3.cross(tmpVec, tmpVec, tmpNormal); + Array.prototype.push.apply(biTangents, tmpVec); + } + if (i == 0 || i > stackLimit) continue; + for (j = 0; j < slices; j++, o++) + { + indices.push( + o, o + 1, o + slices + 1, + o + 1, o + slices + 2, o + slices + 1 + ); + } + o++; + } + + // set geometry + geom.clear(); + geom.vertices = positions; + geom.texCoords = texcoords; + geom.vertexNormals = normals; + geom.tangents = tangents; + geom.biTangents = biTangents; + geom.verticesIndices = indices; + + outGeometry.set(null); + outGeometry.set(geom); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); + + needsRebuild = false; +} + +// set event handlers +inTrigger.onTriggered = function () +{ + if (needsRebuild) buildMesh(); + if (inDraw.get()) mesh.render(cgl.getShader()); + outTrigger.trigger(); +}; + +inStacks.onChange = +inSlices.onChange = +inStacklimit.onChange = +inRadius.onChange = function () +{ + // only calculate once, even after multiple settings could were changed + needsRebuild = true; +}; + +// set lifecycle handlers +op.onDelete = function () { if (mesh)mesh.dispose(); }; + + +}; + +Ops.Gl.Meshes.Sphere_v2.prototype = new CABLES.Op(); +CABLES.OPS["450b4d68-2278-4d9f-9849-0abdfa37ef69"]={f:Ops.Gl.Meshes.Sphere_v2,objName:"Ops.Gl.Meshes.Sphere_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.SplineMeshMaterial_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.SplineMeshMaterial_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"splinemat_frag":"IN vec2 texCoord;\nIN float splineDoDrawFrag;\nUNI vec4 color;\nUNI sampler2D tex;\nUNI sampler2D texMask;\n\n{{MODULES_HEAD}}\n\nvoid main()\n{\n vec4 col=color;\n\n #ifdef USE_TEXTURE\n #ifdef TEX_COLORIZE\n col*=texture(tex,texCoord);\n #endif\n #ifndef TEX_COLORIZE\n col=texture(tex,texCoord);\n #endif\n #endif\n\n col.a=color.a;\n\n #ifdef USE_TEXMASK\n col.a*=texture(texMask,texCoord).r;\n if(col.a==0.0) discard;\n #endif\n\n {{MODULE_COLOR}}\n\n // if(splineDoDrawFrag==0.0) col.rgb=vec3(1.0,0.0,0.0);\n if(splineDoDrawFrag==0.0) discard;\n\n outColor = col;\n}","splinemat_vert":"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nIN float attrVertIndex;\nIN float splineProgress;\nIN vec3 spline,spline2,spline3;\nIN float splineDoDraw;\n\nOUT float splineDoDrawFrag;\nOUT vec2 texCoord;\nOUT vec3 norm;\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\nUNI float width;\nUNI float texOffset;\n\n#define PI 3.1415926538\n\nvec2 rotate(vec2 v, float a)\n{\n\tfloat s = sin(a);\n\tfloat c = cos(a);\n\tmat2 m = mat2(c, -s, s, c);\n\treturn m * v;\n}\n\nfloat aspect=1.7777; // todo uniform ?\n\nvec2 fix( vec4 i )\n{\n vec2 res = i.xy / i.w;\n res.x *= aspect;\n return res;\n}\n\nvoid main()\n{\n texCoord=vPosition.xy;\n texCoord.y=texCoord.y*0.5+0.5;\n #ifdef TEX_MAP_FULL\n texCoord.x=splineProgress;\n #endif\n texCoord.x+=texOffset;\n\n mat4 mMatrix=modelMatrix;\n mat4 mvMatrix=viewMatrix * mMatrix;\n\n splineDoDrawFrag=splineDoDraw;\n\n // vec4 pos=vec4((spline2+spline3+spline)/3.0*vPosition,1.0);\n vec4 pos=vec4(spline2,1.0);\n\n {{MODULE_VERTEX_POSITION}}\n\n vec4 finalPosition = projMatrix * mvMatrix * (vec4(spline2,1.0));\n vec4 finalPosition2 = projMatrix * mvMatrix * (vec4(spline3,1.0));\n\n vec2 screenPos =fix(projMatrix * mvMatrix * vec4(spline,1.0));\n vec2 screenPos2=fix(projMatrix * mvMatrix * vec4(spline2,1.0));\n vec2 screenPos3=fix(projMatrix * mvMatrix * vec4(spline3,1.0));\n\n float wid=width/10.0;\n\n #ifndef PERSPWIDTH\n wid=width*finalPosition.w*0.0025;\n #endif\n\n vec2 dir1 = normalize( screenPos2 - screenPos );\n vec2 dir2 = normalize( screenPos3 - screenPos2 );\n\n\tif( screenPos2 == screenPos ) dir1 = normalize( screenPos3 - screenPos2 );\n\n vec2 normal = vec2( -dir1.y, dir1.x ) * 0.5 * wid;\n vec2 normal2 = vec2( -dir2.y, dir2.x ) * 0.5 * wid;\n\n vec4 offset = vec4( mix(normal,normal2,vPosition.x) * vPosition.y, 0.0, 1.0 );\n\n finalPosition = mix(finalPosition,finalPosition2,vPosition.x);\n\tfinalPosition.xy += offset.xy;\n\n gl_Position = finalPosition;\n}\n\n\n\n\n\n\n\n",}; +const + render = op.inTrigger("Render"), + inWidth = op.inFloat("Width", 0.2), + inPerspective = op.inBool("Width Perspective", true), + inTexture = op.inTexture("Texture"), + inTextureMask = op.inTexture("Texture Mask"), + inTexMap = op.inSwitch("Mapping", ["Full", "Face"], "Full"), + inTexColorize = op.inBool("Colorize Texture", false), + inTexOffset = op.inFloat("Offset", 0), + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + a = op.inValueSlider("a", 1), + trigger = op.outTrigger("Trigger"), + shaderOut = op.outObject("Shader"); + +r.setUiAttribs({ "colorPick": true }); +shaderOut.ignoreValueSerialize = true; + +const cgl = op.patch.cgl; + +op.toWorkPortsNeedToBeLinked(render); +op.setPortGroup("Color", [r, g, b, a]); +op.setPortGroup("Texture", [inTexture, inTexMap, inTexColorize]); + +const shader = new CGL.Shader(cgl, "splinemesh_material"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +shader.setSource(attachments.splinemat_vert, attachments.splinemat_frag); +shaderOut.set(shader); + +const uniTex = shader.addUniformFrag("t", "tex"); +const uniTexMask = shader.addUniformFrag("t", "texMask"); + +shader.addUniformFrag("4f", "color", r, g, b, a); +shader.addUniformFrag("f", "width", inWidth); +shader.addUniformFrag("f", "texOffset", inTexOffset); +shader.toggleDefine("PERSPWIDTH", inPerspective); +shader.toggleDefine("USE_TEXTURE", inTexture); +shader.toggleDefine("TEX_COLORIZE", inTexColorize); +shader.toggleDefine("USE_TEXMASK", inTextureMask); + +inTexMap.on("change", updateMapping); + +render.onTriggered = doRender; +updateMapping(); + +function doRender() +{ + if (!shader) return; + + cgl.pushShader(shader); + shader.popTextures(); + + if (uniTex && inTexture.get()) shader.pushTexture(uniTex, inTexture.get().tex); + if (uniTexMask && inTextureMask.get()) shader.pushTexture(uniTexMask, inTextureMask.get().tex); + + trigger.trigger(); + + cgl.popShader(); +} + +function updateMapping() +{ + shader.toggleDefine("TEX_MAP_FULL", inTexMap.get() === "Full"); +} + + +}; + +Ops.Gl.Meshes.SplineMeshMaterial_v2.prototype = new CABLES.Op(); +CABLES.OPS["5ff7c643-cbea-44cc-9f34-fb18a44bcfff"]={f:Ops.Gl.Meshes.SplineMeshMaterial_v2,objName:"Ops.Gl.Meshes.SplineMeshMaterial_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.SplineMesh_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.SplineMesh_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + inPoints = op.inArray("Points"), + inHardEdges = op.inBool("Tesselate Edges", false), + inRenderMesh = op.inBool("Render Mesh", true), + next = op.outTrigger("Next"); + +const geom = new CGL.Geometry("splinemesh_2"); +geom.vertices = []; +geom.clear(); + +let thePoints = []; +const cgl = op.patch.cgl; +let points = new Float32Array(); +let points2 = new Float32Array(); +let points3 = new Float32Array(); +let doDraw = new Float32Array(); +let splineIndex = null; + +let pointsProgress = new Float32Array(); +const pointsDoDraw = new Float32Array(); +const arrEdges = []; + +const verts = [0, 0, 0]; + +let mesh = new CGL.Mesh(cgl, geom); +mesh.addVertexNumbers = true; + +let rebuildLater = true; + +inHardEdges.onChange = + inPoints.onChange = () => { rebuildLater = true; }; + +render.onTriggered = renderMesh; + +let shader = null; + +function renderMesh() +{ + if (rebuildLater)rebuild(); + if (mesh && inRenderMesh.get()) + { + if (shader != cgl.getShader()) + { + shader = cgl.getShader(); + if (!shader) return; + if (shader.getName() != "splinemesh_material") op.setUiError("nosplinemat", "Splinemesh needs a SplineMeshMaterial!"); + else op.setUiError("nosplinemat"); + + shader = cgl.getShader(); + } + + if (verts.length > 0) mesh.render(shader); + } + + next.trigger(); +} + +function buildMesh() +{ + verts.length = 0; + + const max = 1; + const min = -max; + + for (let i = 0; i < thePoints.length / 3; i++) + { + verts.push( + max, min, 0, 0, min, 0, max, max, 0, + 0, min, 0, 0, max, 0, max, max, 0 + ); + } + geom.vertices = verts; + + // if(mesh)mesh.dispose(); + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + + mesh.addVertexNumbers = true; + mesh.setGeom(geom); + mesh.addVertexNumbers = true; +} + +function rebuild() +{ + const inpoints = inPoints.get(); + + if (!inpoints || inpoints.length === 0) + { + mesh = null; + return; + } + + if (inpoints[0].length) + { + const arr = []; + splineIndex = []; + let count = 0; + + for (let i = 0; i < inpoints.length; i++) + { + for (let j = 0; j < inpoints[i].length / 3; j++) + { + splineIndex[(count - 3) / 3] = i;// (i) / inpoints.length; + + arr[count++] = inpoints[i][j * 3 + 0]; + arr[count++] = inpoints[i][j * 3 + 1]; + arr[count++] = inpoints[i][j * 3 + 2]; + } + } + thePoints = arr; + } + else + { + splineIndex = null; + thePoints = inpoints; + } + + if (inHardEdges.get()) thePoints = tessEdges(thePoints); + + buildMesh(); + + const newLength = thePoints.length * 6; + let count = 0; + let lastIndex = 0; + let drawable = 0; + + if (points.length != newLength) + { + points = new Float32Array(newLength); + points2 = new Float32Array(newLength); + points3 = new Float32Array(newLength); + + doDraw = new Float32Array(newLength / 3); + pointsProgress = new Float32Array(newLength / 3); + + for (let i = 0; i < newLength / 3; i++) pointsProgress[i] = i / (newLength / 3); + } + + for (let i = 0; i < thePoints.length / 3; i++) + { + if (splineIndex) + { + if (i > 1 && lastIndex != splineIndex[i]) drawable = 0.0; + else drawable = 1.0; + lastIndex = splineIndex[i]; + } + else drawable = 1.0; + + for (let j = 0; j < 6; j++) + { + doDraw[count / 3] = drawable; + + for (let k = 0; k < 3; k++) + { + points[count] = thePoints[(Math.max(0, i - 1)) * 3 + k]; + points2[count] = thePoints[(i + 0) * 3 + k]; + points3[count] = thePoints[(i + 1) * 3 + k]; + count++; + } + } + } + + mesh.setAttribute("spline", points, 3); + mesh.setAttribute("spline2", points2, 3); + mesh.setAttribute("spline3", points3, 3); + mesh.setAttribute("splineDoDraw", doDraw, 1); + mesh.setAttribute("splineProgress", pointsProgress, 1); + + rebuildLater = false; +} + +function ip(a, b, p) +{ + return a + p * (b - a); +} + +function tessEdges(oldArr) +{ + let count = 0; + const step = 0.001; + const oneMinusStep = 1 - step; + const l = oldArr.length * 3 - 3; + arrEdges.length = l; + + const tessSplineIndex = []; + + if (splineIndex) tessSplineIndex[0] = splineIndex[1]; + + for (let i = 0; i < oldArr.length - 3; i += 3) + { + arrEdges[count++] = oldArr[i + 0]; + arrEdges[count++] = oldArr[i + 1]; + arrEdges[count++] = oldArr[i + 2]; + if (splineIndex) tessSplineIndex[count / 3] = splineIndex[i / 3]; + + arrEdges[count++] = ip(oldArr[i + 0], oldArr[i + 3], step); + arrEdges[count++] = ip(oldArr[i + 1], oldArr[i + 4], step); + arrEdges[count++] = ip(oldArr[i + 2], oldArr[i + 5], step); + if (splineIndex) tessSplineIndex[count / 3] = splineIndex[i / 3]; + + arrEdges[count++] = ip(oldArr[i + 0], oldArr[i + 3], oneMinusStep); + arrEdges[count++] = ip(oldArr[i + 1], oldArr[i + 4], oneMinusStep); + arrEdges[count++] = ip(oldArr[i + 2], oldArr[i + 5], oneMinusStep); + if (splineIndex) tessSplineIndex[count / 3] = splineIndex[i / 3]; + } + + if (splineIndex) splineIndex = tessSplineIndex; + + return arrEdges; +} + + +}; + +Ops.Gl.Meshes.SplineMesh_v2.prototype = new CABLES.Op(); +CABLES.OPS["287abf6c-5501-4bc9-a627-70ec3c3766d2"]={f:Ops.Gl.Meshes.SplineMesh_v2,objName:"Ops.Gl.Meshes.SplineMesh_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Star +// +// ************************************************************** + +Ops.Gl.Meshes.Star = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + segments = op.inValue("segments", 40), + radius = op.inValue("radius", 1), + shape = op.inValueSelect("Shape", ["Star", "Saw", "Gear"], "Star"), + outerRadius = op.inValue("Length", 1.5), + zDiff = op.inFloat("Peak Z Pos", 0), + percent = op.inValueSlider("percent"), + fill = op.inValueBool("Fill"), + trigger = op.outTrigger("trigger"), + geomOut = op.addOutPort(new CABLES.Port(op, "geometry", CABLES.OP_PORT_TYPE_OBJECT)); + +geomOut.ignoreValueSerialize = true; +let cgl = op.patch.cgl; + +let oldPrim = 0; +let shader = null; +percent.set(1); + +let geom = new CGL.Geometry("circle"); +let mesh = null; +let lastSegs = -1; + +zDiff.onChange = + segments.onChange = + radius.onChange = + percent.onChange = + shape.onChange = + fill.onChange = + outerRadius.onChange = calc; +calc(); + +render.onTriggered = function () +{ + if (op.instanced(render)) return; + + shader = cgl.getShader(); + if (!shader) return; + oldPrim = shader.glPrimitive; + + mesh.render(shader); + trigger.trigger(); + + shader.glPrimitive = oldPrim; +}; + +function calc() +{ + let segs = Math.max(3, Math.floor(segments.get())); + + geom.clear(); + + let + faces = [], + normals = [], + tangents = [], + biTangents = []; + let i = 0, degInRad = 0; + let oldPosX = 0, oldPosY = 0; + let oldPosXTexCoord = 0, oldPosYTexCoord = 0; + + let oldPosXIn = 0, oldPosYIn = 0; + let oldPosXTexCoordIn = 0, oldPosYTexCoordIn = 0; + + let posxTexCoordIn = 0, posyTexCoordIn = 0; + let posxTexCoord = 0, posyTexCoord = 0; + let posx = 0, posy = 0; + + let verts = []; + let outX = 0, outY = 0; + + let imode = 0; + if (shape.get() == "Saw")imode = 1; + if (shape.get() == "Gear")imode = 2; + + let cycleGear = true; + + for (i = 0; i <= segs * percent.get(); i++) + { + degInRad = (360 / segs) * i * CGL.DEG2RAD; + posx = Math.cos(degInRad) * radius.get(); + posy = Math.sin(degInRad) * radius.get(); + + // saw mode + cycleGear = !cycleGear; + + switch (imode) + { + case 0: + outX = ((posx + oldPosX) * 0.5) * outerRadius.get(); + outY = ((posy + oldPosY) * 0.5) * outerRadius.get(); + break; + + case 1: + outX = (posx) * outerRadius.get(); + outY = (posy) * outerRadius.get(); + break; + + case 2: + if (cycleGear) + { + outX = (posx) * outerRadius.get(); + outY = (posy) * outerRadius.get(); + + degInRad = (360 / segs) * (i - 1) * CGL.DEG2RAD; + let ooutX = Math.cos(degInRad) * radius.get(); + let ooutY = Math.sin(degInRad) * radius.get(); + + ooutX *= outerRadius.get(); + ooutY *= outerRadius.get(); + + faces.push( + [ooutX, ooutY, 0], + [outX, outY, 0], + [oldPosX, oldPosY, 0] + ); + normals.push(0, 0, 1, 0, 0, 1, 0, 0, 1); + tangents.push(1, 0, 0, 1, 0, 0, 1, 0, 0); + biTangents.push(0, 1, 0, 0, 1, 0, 0, 1, 0); + } + + break; + } + + if (fill.get()) + { + faces.push( + [posx, posy, 0], + [oldPosX, oldPosY, 0], + [0, 0, 0] + ); + normals.push(0, 0, 1, 0, 0, 1, 0, 0, 1); + tangents.push(1, 0, 0, 1, 0, 0, 1, 0, 0); + biTangents.push(0, 1, 0, 0, 1, 0, 0, 1, 0); + } + + if (imode != 2 || cycleGear) + { + faces.push( + [posx, posy, 0], + [oldPosX, oldPosY, 0], + [outX, outY, zDiff.get()] + ); + normals.push(0, 0, 1, 0, 0, 1, 0, 0, 1); + tangents.push(1, 0, 0, 1, 0, 0, 1, 0, 0); + biTangents.push(0, 1, 0, 0, 1, 0, 0, 1, 0); + } + + oldPosX = posx; + oldPosY = posy; + } + + geom = CGL.Geometry.buildFromFaces(faces); + geom.vertexNormals = normals; + geom.tangents = tangents; + geom.biTangents = biTangents; + geom.mapTexCoords2d(); + + geomOut.set(null); + geomOut.set(geom); + + if (geom.vertices.length == 0) return; + if (!mesh)mesh = new CGL.Mesh(cgl, geom); + mesh.setGeom(geom); +} + + +}; + +Ops.Gl.Meshes.Star.prototype = new CABLES.Op(); +CABLES.OPS["25feb18a-8df6-473f-b548-59c514c6f949"]={f:Ops.Gl.Meshes.Star,objName:"Ops.Gl.Meshes.Star"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.SuperShape +// +// ************************************************************** + +Ops.Gl.Meshes.SuperShape = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// from https://github.com/ahoiin/supershape.js/blob/master/js/objects.js + +const render = op.inTrigger("render"); + +let pNormalizeSize = op.inValueBool("Normalize Size", true); +let asPointCloud = op.inValueBool("Point Cloud", false); +let pStep = op.inFloat("Step", 0.05); + +let a1 = op.inFloat("a1", 1); +let b1 = op.inFloat("b1", 1); +let m1 = op.inFloat("m1", 5); +let n11 = op.inFloat("n11", 1); +let n21 = op.inFloat("n21", 1); +let n31 = op.inFloat("n31", 2); + +let a2 = op.inFloat("a2", 1); +let b2 = op.inFloat("b2", 1); +let m2 = op.inFloat("m2", 5); +let n12 = op.inFloat("n12", 1); +let n22 = op.inFloat("n22", 1); +let n32 = op.inFloat("n32", 3); + +let trigger = op.outTrigger("Trigger"); +let outNumVerts = op.outNumber("Num Vertices"); +let outGeom = op.outObject("geom", null, "geometry"); + +let needsUpdate = true; +let geometry = new CGL.Geometry(op.name); +let mesh = null; +let verts = []; + +pNormalizeSize.onChange = + pStep.onChange = + a1.onChange = + b1.onChange = + m1.onChange = + n11.onChange = + n21.onChange = + n31.onChange = + a2.onChange = + b2.onChange = + m2.onChange = + n12.onChange = + n22.onChange = + n32.onChange = doUpdate; + +function doUpdate() +{ + needsUpdate = true; +} + +asPointCloud.onChange = function () +{ + mesh = null; + needsUpdate = true; +}; + +render.onTriggered = function () +{ + if (needsUpdate)update(); + if (mesh) mesh.render(op.patch.cgl.getShader()); + + trigger.trigger(); +}; +function update() +{ + verts.length = 0; + geometry.clear(); + // geometry=new CGL.Geometry(op.name); + needsUpdate = false; + // geometry.dynamic = true; + let step = pStep.get(); + let q = parseInt(2 * Math.PI / step + 1.3462); + let o = parseInt(Math.PI / step + 1.5); + + let resize = pNormalizeSize.get(); + let max = 0; + + for (let l = 0; l < (q); l++) + { + var u = -Math.PI + l * step; + for (let h = 0; h < (o); h++) + { + var s = -Math.PI / 2 + h * step; + var m, k, n, g, v, e, t; + let f = 0; + let p = 0; + let w = 0; + m = Math.cos(m1.get() * u / 4); + m = 1 / a1.get() * Math.abs(m); + m = Math.abs(m); + k = Math.sin(m1.get() * u / 4); + k = 1 / b1.get() * Math.abs(k); + k = Math.abs(k); + g = Math.pow(m, n21.get()) + Math.pow(k, n31.get()); + v = Math.abs(g); + v = Math.pow(v, (-1 / n11.get())); + m = Math.cos(m2.get() * s / 4); + m = 1 / a2.get() * Math.abs(m); + m = Math.abs(m); + k = Math.sin(m2.get() * s / 4); + k = 1 / b2.get() * Math.abs(k); + k = Math.abs(k); + e = Math.pow(m, n22.get()) + Math.pow(k, n32.get()); + t = Math.abs(e); + t = Math.pow(t, (-1 / n12.get())); + f = v * Math.cos(u) * t * Math.cos(s); + p = v * Math.sin(u) * t * Math.cos(s); + w = t * Math.sin(s); + verts.push(f); + verts.push(p); + verts.push(w); + + if (resize) + { + max = Math.max(max, Math.abs(f)); + max = Math.max(max, Math.abs(p)); + max = Math.max(max, Math.abs(w)); + } + } + } + + if (resize && max > 1) for (let i = 0; i < verts.length; i++) verts[i] /= max; + + if (asPointCloud.get()) + { + geometry.setPointVertices(verts); + geometry.mapTexCoords2d(); + + mesh = new CGL.Mesh(op.patch.cgl, geometry, op.patch.cgl.gl.POINTS); + mesh.addVertexNumbers = true; + mesh.setGeom(geometry); + } + else + { + for (var u = 0; u < (q - 1); u++) + { + for (var s = 0; s < (o - 1); s++) + { + let d = u * o + s; + let c = u * o + s + 1; + let b = (u + 1) * o + s + 1; + let a = (u + 1) * o + s; + // geometry.faces.push(new THREE.Face4(d, c, b, a)); + geometry.verticesIndices.push(d); + geometry.verticesIndices.push(c); + geometry.verticesIndices.push(b); + + geometry.verticesIndices.push(a); + geometry.verticesIndices.push(b); + geometry.verticesIndices.push(d); + } + } + geometry.vertices = verts; + geometry.mapTexCoords2d(); + + outNumVerts.set(verts.length); + + geometry.calculateNormals({ "forceZUp": true }); + + if (!mesh) mesh = new CGL.Mesh(op.patch.cgl, geometry); + else mesh.setGeom(geometry); + } + + outGeom.set(null); + outGeom.set(geometry); +} + + +}; + +Ops.Gl.Meshes.SuperShape.prototype = new CABLES.Op(); +CABLES.OPS["ed8b5db0-b928-4f5d-87d7-4b2491ae7b00"]={f:Ops.Gl.Meshes.SuperShape,objName:"Ops.Gl.Meshes.SuperShape"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.TextMesh_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.TextMesh_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"textmesh_frag":"UNI sampler2D tex;\n#ifdef DO_MULTEX\n UNI sampler2D texMul;\n#endif\n#ifdef DO_MULTEX_MASK\n UNI sampler2D texMulMask;\n#endif\nIN vec2 texCoord;\nIN vec2 texPos;\nUNI float r;\nUNI float g;\nUNI float b;\nUNI float a;\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n col.a=col.r;\n col.r*=r;\n col.g*=g;\n col.b*=b;\n col*=a;\n\n if(col.a==0.0)discard;\n\n #ifdef DO_MULTEX\n col*=texture(texMul,texPos);\n #endif\n\n #ifdef DO_MULTEX_MASK\n col*=texture(texMulMask,texPos).r;\n #endif\n\n outColor=col;\n}","textmesh_vert":"UNI sampler2D tex;\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\nUNI float scale;\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN mat4 instMat;\nIN vec2 attrTexOffsets;\nIN vec2 attrTexSize;\nIN vec2 attrTexPos;\nOUT vec2 texPos;\n\nOUT vec2 texCoord;\n\nvoid main()\n{\n texCoord=(attrTexCoord*(attrTexSize)) + attrTexOffsets;\n mat4 instMVMat=instMat;\n instMVMat[3][0]*=scale;\n\n texPos=attrTexPos;\n\n vec4 vert=vec4( vPosition.x*(attrTexSize.x/attrTexSize.y)*scale,vPosition.y*scale,vPosition.z*scale, 1. );\n\n mat4 mvMatrix=viewMatrix * modelMatrix * instMVMat;\n\n gl_Position = projMatrix * mvMatrix * vert;\n}\n",}; +const + render = op.inTrigger("Render"), + str = op.inString("Text", "cables"), + scale = op.inValueFloat("Scale", 1), + inFont = op.inString("Font", "Arial"), + align = op.inValueSelect("align", ["left", "center", "right"], "center"), + valign = op.inValueSelect("vertical align", ["Top", "Middle", "Bottom"], "Middle"), + lineHeight = op.inValueFloat("Line Height", 1), + letterSpace = op.inValueFloat("Letter Spacing"), + + tfilter = op.inSwitch("filter", ["nearest", "linear", "mipmap"], "mipmap"), + aniso = op.inSwitch("Anisotropic", [0, 1, 2, 4, 8, 16], 0), + + inMulTex = op.inTexture("Texture Color"), + inMulTexMask = op.inTexture("Texture Mask"), + next = op.outTrigger("Next"), + textureOut = op.outTexture("texture"), + outLines = op.outNumber("Total Lines", 0), + outWidth = op.outNumber("Width", 0), + loaded = op.outBoolNum("Font Available", 0); + +const cgl = op.patch.cgl; + +op.setPortGroup("Masking", [inMulTex, inMulTexMask]); + +const textureSize = 1024; +let fontLoaded = false; +let needUpdate = true; + +align.onChange = + str.onChange = + lineHeight.onChange = generateMeshLater; + +function generateMeshLater() +{ + needUpdate = true; +} + +let canvasid = null; +CABLES.OpTextureMeshCanvas = {}; +let valignMode = 0; + +const geom = null; +let mesh = null; + +let createMesh = true; +let createTexture = true; + +aniso.onChange = +tfilter.onChange = () => +{ + getFont().texture = null; + createTexture = true; +}; + +inMulTexMask.onChange = +inMulTex.onChange = function () +{ + shader.toggleDefine("DO_MULTEX", inMulTex.get()); + shader.toggleDefine("DO_MULTEX_MASK", inMulTexMask.get()); +}; + +textureOut.set(null); +inFont.onChange = function () +{ + createTexture = true; + createMesh = true; + checkFont(); +}; + +op.patch.on("fontLoaded", (fontName) => +{ + if (fontName == inFont.get()) + { + createTexture = true; + createMesh = true; + } +}); + +function checkFont() +{ + const oldFontLoaded = fontLoaded; + try + { + fontLoaded = document.fonts.check("20px \"" + inFont.get() + "\""); + } + catch (ex) + { + op.logError(ex); + } + + if (!oldFontLoaded && fontLoaded) + { + loaded.set(true); + createTexture = true; + createMesh = true; + } + + if (!fontLoaded) setTimeout(checkFont, 250); +} + +valign.onChange = function () +{ + if (valign.get() == "Middle")valignMode = 0; + else if (valign.get() == "Top")valignMode = 1; + else if (valign.get() == "Bottom")valignMode = 2; +}; + +function getFont() +{ + canvasid = "" + inFont.get(); + if (CABLES.OpTextureMeshCanvas.hasOwnProperty(canvasid)) + return CABLES.OpTextureMeshCanvas[canvasid]; + + const fontImage = document.createElement("canvas"); + fontImage.dataset.font = inFont.get(); + fontImage.id = "texturetext_" + CABLES.generateUUID(); + fontImage.style.display = "none"; + const body = document.getElementsByTagName("body")[0]; + body.appendChild(fontImage); + const _ctx = fontImage.getContext("2d"); + CABLES.OpTextureMeshCanvas[canvasid] = + { + "ctx": _ctx, + "canvas": fontImage, + "chars": {}, + "characters": "", + "fontSize": 320 + }; + return CABLES.OpTextureMeshCanvas[canvasid]; +} + +op.onDelete = function () +{ + if (canvasid && CABLES.OpTextureMeshCanvas[canvasid]) + CABLES.OpTextureMeshCanvas[canvasid].canvas.remove(); +}; + +const shader = new CGL.Shader(cgl, "TextMesh"); +shader.setSource(attachments.textmesh_vert, attachments.textmesh_frag); +const uniTex = new CGL.Uniform(shader, "t", "tex", 0); +const uniTexMul = new CGL.Uniform(shader, "t", "texMul", 1); +const uniTexMulMask = new CGL.Uniform(shader, "t", "texMulMask", 2); +const uniScale = new CGL.Uniform(shader, "f", "scale", scale); + +const + r = op.inValueSlider("r", 1), + g = op.inValueSlider("g", 1), + b = op.inValueSlider("b", 1), + a = op.inValueSlider("a", 1), + runiform = new CGL.Uniform(shader, "f", "r", r), + guniform = new CGL.Uniform(shader, "f", "g", g), + buniform = new CGL.Uniform(shader, "f", "b", b), + auniform = new CGL.Uniform(shader, "f", "a", a); +r.setUiAttribs({ "colorPick": true }); + +op.setPortGroup("Display", [scale, inFont]); +op.setPortGroup("Alignment", [align, valign]); +op.setPortGroup("Color", [r, g, b, a]); + +let height = 0; +const vec = vec3.create(); +let lastTextureChange = -1; +let disabled = false; + +render.onTriggered = function () +{ + if (needUpdate) + { + generateMesh(); + needUpdate = false; + } + const font = getFont(); + if (font.lastChange != lastTextureChange) + { + createMesh = true; + lastTextureChange = font.lastChange; + } + + if (createTexture) generateTexture(); + if (createMesh)generateMesh(); + + if (mesh && mesh.numInstances > 0) + { + cgl.pushBlendMode(CGL.BLEND_NORMAL, true); + cgl.pushShader(shader); + cgl.setTexture(0, textureOut.get().tex); + + const mulTex = inMulTex.get(); + if (mulTex)cgl.setTexture(1, mulTex.tex); + + const mulTexMask = inMulTexMask.get(); + if (mulTexMask)cgl.setTexture(2, mulTexMask.tex); + + if (valignMode === 2) vec3.set(vec, 0, height, 0); + else if (valignMode === 1) vec3.set(vec, 0, 0, 0); + else if (valignMode === 0) vec3.set(vec, 0, height / 2, 0); + + vec[1] -= lineHeight.get(); + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, vec); + if (!disabled)mesh.render(cgl.getShader()); + + cgl.popModelMatrix(); + + cgl.setTexture(0, null); + cgl.popShader(); + cgl.popBlendMode(); + } + + next.trigger(); +}; + +letterSpace.onChange = function () +{ + createMesh = true; +}; + +function generateMesh() +{ + const theString = String(str.get() + ""); + if (!textureOut.get()) return; + + const font = getFont(); + if (!font.geom) + { + font.geom = new CGL.Geometry("textmesh"); + + font.geom.vertices = [ + 1.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 1.0, 0.0, 0.0, + 0.0, 0.0, 0.0 + ]; + + font.geom.texCoords = new Float32Array([ + 1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 0.0, 0.0 + ]); + + font.geom.verticesIndices = [ + 0, 1, 2, + 2, 1, 3 + ]; + } + + if (!mesh)mesh = new CGL.Mesh(cgl, font.geom); + + const strings = (theString).split("\n"); + outLines.set(strings.length); + + const transformations = []; + const tcOffsets = [];// new Float32Array(str.get().length*2); + const tcSize = [];// new Float32Array(str.get().length*2); + const texPos = []; + let charCounter = 0; + createTexture = false; + const m = mat4.create(); + + let maxWidth = 0; + + for (let s = 0; s < strings.length; s++) + { + const txt = strings[s]; + const numChars = txt.length; + + let pos = 0; + let offX = 0; + let width = 0; + + for (let i = 0; i < numChars; i++) + { + const chStr = txt.substring(i, i + 1); + const char = font.chars[String(chStr)]; + if (char) + { + width += (char.texCoordWidth / char.texCoordHeight); + width += letterSpace.get(); + } + } + + width -= letterSpace.get(); + + height = 0; + + if (align.get() == "left") offX = 0; + else if (align.get() == "right") offX = width; + else if (align.get() == "center") offX = width / 2; + + height = (s + 1) * lineHeight.get(); + + for (let i = 0; i < numChars; i++) + { + const chStr = txt.substring(i, i + 1); + const char = font.chars[String(chStr)]; + + if (!char) + { + createTexture = true; + return; + } + else + { + texPos.push(pos / width * 0.99 + 0.005, (1.0 - (s / (strings.length - 1))) * 0.99 + 0.005); + tcOffsets.push(char.texCoordX, 1 - char.texCoordY - char.texCoordHeight); + tcSize.push(char.texCoordWidth, char.texCoordHeight); + + mat4.identity(m); + mat4.translate(m, m, [pos - offX, 0 - s * lineHeight.get(), 0]); + + pos += (char.texCoordWidth / char.texCoordHeight) + letterSpace.get(); + maxWidth = Math.max(maxWidth, pos - offX); + + transformations.push(Array.prototype.slice.call(m)); + + charCounter++; + } + } + } + + const transMats = [].concat.apply([], transformations); + + disabled = false; + if (transMats.length == 0)disabled = true; + + mesh.numInstances = transMats.length / 16; + + if (mesh.numInstances == 0) + { + disabled = true; + return; + } + + outWidth.set(maxWidth * scale.get()); + mesh.setAttribute("instMat", new Float32Array(transMats), 16, { "instanced": true }); + mesh.setAttribute("attrTexOffsets", new Float32Array(tcOffsets), 2, { "instanced": true }); + mesh.setAttribute("attrTexSize", new Float32Array(tcSize), 2, { "instanced": true }); + mesh.setAttribute("attrTexPos", new Float32Array(texPos), 2, { "instanced": true }); + + createMesh = false; + + if (createTexture) generateTexture(); +} + +function printChars(fontSize, simulate) +{ + const font = getFont(); + if (!simulate) font.chars = {}; + + const ctx = font.ctx; + + ctx.font = fontSize + "px " + inFont.get(); + ctx.textAlign = "left"; + + let posy = 0; + let posx = 0; + const lineHeight = fontSize * 1.4; + const result = + { + "fits": true + }; + + for (let i = 0; i < font.characters.length; i++) + { + const chStr = String(font.characters.substring(i, i + 1)); + const chWidth = (ctx.measureText(chStr).width); + + if (posx + chWidth >= textureSize) + { + posy += lineHeight + 2; + posx = 0; + } + + if (!simulate) + { + font.chars[chStr] = + { + "str": chStr, + "texCoordX": posx / textureSize, + "texCoordY": posy / textureSize, + "texCoordWidth": chWidth / textureSize, + "texCoordHeight": lineHeight / textureSize, + }; + + ctx.fillText(chStr, posx, posy + fontSize); + } + + posx += chWidth + 12; + } + + if (posy > textureSize - lineHeight) + { + result.fits = false; + } + + result.spaceLeft = textureSize - posy; + + return result; +} + +function generateTexture() +{ + let filter = CGL.Texture.FILTER_LINEAR; + if (tfilter.get() == "nearest") filter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "mipmap") filter = CGL.Texture.FILTER_MIPMAP; + + const font = getFont(); + let string = String(str.get()); + if (string == null || string == undefined)string = ""; + for (let i = 0; i < string.length; i++) + { + const ch = string.substring(i, i + 1); + if (font.characters.indexOf(ch) == -1) + { + font.characters += ch; + createTexture = true; + } + } + + const ctx = font.ctx; + font.canvas.width = font.canvas.height = textureSize; + + if (!font.texture) + font.texture = CGL.Texture.createFromImage(cgl, font.canvas, + { + "filter": filter, + "anisotropic": parseFloat(aniso.get()) + }); + + font.texture.setSize(textureSize, textureSize); + + ctx.fillStyle = "transparent"; + ctx.clearRect(0, 0, textureSize, textureSize); + ctx.fillStyle = "rgba(255,255,255,255)"; + + let fontSize = font.fontSize + 40; + let simu = printChars(fontSize, true); + + while (!simu.fits) + { + fontSize -= 5; + simu = printChars(fontSize, true); + } + + printChars(fontSize, false); + + ctx.restore(); + + font.texture.initTexture(font.canvas, filter); + font.texture.unpackAlpha = true; + textureOut.set(font.texture); + + font.lastChange = CABLES.now(); + + createMesh = true; + createTexture = false; +} + + +}; + +Ops.Gl.Meshes.TextMesh_v2.prototype = new CABLES.Op(); +CABLES.OPS["2390f6b3-2122-412e-8c8d-5c2f574e8bd1"]={f:Ops.Gl.Meshes.TextMesh_v2,objName:"Ops.Gl.Meshes.TextMesh_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Torus +// +// ************************************************************** + +Ops.Gl.Meshes.Torus = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + sides = op.inValue("sides", 32), + rings = op.inValue("rings", 32), + innerRadius = op.inValue("innerRadius", 0.5), + outerRadius = op.inValue("outerRadius", 1), + indraw = op.inBool("Draw", true), + trigger = op.outTrigger("trigger"), + geomOut = op.outObject("geometry"); + +const UP = vec3.fromValues(0, 1, 0), RIGHT = vec3.fromValues(1, 0, 0); +let tmpNormal = vec3.create(), tmpVec = vec3.create(); + +geomOut.ignoreValueSerialize = true; + +let cgl = op.patch.cgl; +let mesh = null; +let geom = null; +let j = 0, i = 0, idx = 0; +let needsUpdate = true; + +rings.onChange = +sides.onChange = +innerRadius.onChange = +outerRadius.onChange = function () +{ + needsUpdate = true; +}; + +render.onTriggered = function () +{ + if (needsUpdate) updateMesh(); + if (indraw.get() && mesh !== null) mesh.render(cgl.getShader()); + + trigger.trigger(); +}; + +function updateMesh() +{ + let nrings = Math.round(rings.get()); + let nsides = Math.round(sides.get()); + if (nrings < 2)nrings = 2; + if (nsides < 2)nsides = 2; + let r = innerRadius.get(); + let r2 = outerRadius.get(); + generateTorus(r, r2, nrings, nsides); + needsUpdate = false; +} + +function circleTable(n, halfCircle) +{ + let i; + /* Table size, the sign of n flips the circle direction */ + let size = Math.abs(n); + + /* Determine the angle between samples */ + let angle = (halfCircle ? 1 : 2) * Math.PI / n;// ( n === 0 ) ? 1 : n ; + + /* Allocate memory for n samples, plus duplicate of first entry at the end */ + let sint = []; + let cost = []; + + /* Compute cos and sin around the circle */ + sint[0] = 0.0; + cost[0] = 1.0; + + for (i = 1; i < size; i++) + { + sint[i] = Math.sin(angle * i); + cost[i] = Math.cos(angle * i); + } + + if (halfCircle) + { + sint[size] = 0.0; /* sin PI */ + cost[size] = -1.0; /* cos PI */ + } + else + { + /* Last sample is duplicate of the first (sin or cos of 2 PI) */ + sint[size] = sint[0]; + cost[size] = cost[0]; + } + return { "cost": cost, "sint": sint }; +} + +function generateTorus(iradius, oradius, nRings, nSides) +{ + let table1 = circleTable(nRings, false); + let table2 = circleTable(-nSides, false); + let t; + + geom = new CGL.Geometry("torus"); + geom.glPrimitive = cgl.gl.TRIANGLE_STRIP; + let tangents = []; + let biTangents = []; + let vertexNormals = []; + let tc = []; + + for (j = 0; j < nRings; j++) + { + for (i = 0; i < nSides; i++) + { + var offset = 3 * (j * nSides + i); + var offset2 = 2 * (j * nSides + i); + + geom.vertices[offset] = table1.cost[j] * (oradius + table2.cost[i] * iradius); + geom.vertices[offset + 1] = table1.sint[j] * (oradius + table2.cost[i] * iradius); + geom.vertices[offset + 2] = table2.sint[i] * iradius; + vertexNormals[offset] = tmpNormal[0] = table1.cost[j] * table2.cost[i]; + vertexNormals[offset + 1] = tmpNormal[1] = table1.sint[j] * table2.cost[i]; + vertexNormals[offset + 2] = tmpNormal[2] = table2.sint[i]; + + if (Math.abs(tmpNormal[1]) == 1) t = RIGHT; + else t = UP; + + vec3.cross(tmpVec, tmpNormal, t); + vec3.normalize(tmpVec, tmpVec); + tangents[offset] = tmpVec[0]; + tangents[offset + 1] = tmpVec[1]; + tangents[offset + 2] = tmpVec[2]; + vec3.cross(tmpVec, tmpVec, tmpNormal); + biTangents[offset] = tmpVec[0]; + biTangents[offset + 1] = tmpVec[1]; + biTangents[offset + 2] = tmpVec[2]; + + tc[offset2] = j / (nRings - 1); + tc[offset2 + 1] = i / (nSides - 1); + } + } + + for (i = 0, idx = 0; i < nSides; i++) + { + let ioff = 1; + if (i == nSides - 1) ioff = -i; + + for (j = 0; j < nRings; j++, idx += 2) + { + var offset = j * nSides + i; + geom.verticesIndices[idx] = offset; + geom.verticesIndices[idx + 1] = offset + ioff; + + tc[offset2] = j / (nRings + 1); + tc[offset2 + 1] = i / (nSides + 1); + } + + /* repeat first to close off shape */ + geom.verticesIndices[idx] = i; + geom.verticesIndices[idx + 1] = i + ioff; + + idx += 2; + } + + if (geom.biTangents.length == biTangents.length)geom.biTangents.set(biTangents); + else geom.biTangents = new Float32Array(biTangents); + + if (geom.tangents.length == tangents.length)geom.tangents.set(tangents); + else geom.tangents = new Float32Array(tangents); + + if (geom.vertexNormals.length == vertexNormals.length)geom.vertexNormals.set(vertexNormals); + else geom.vertexNormals = new Float32Array(vertexNormals); + + geom.setTexCoords(tc); + + geomOut.set(null); + geomOut.set(geom); + + if (!mesh)mesh = new CGL.Mesh(cgl, geom, cgl.gl.TRIANGLE_STRIP); + else mesh.setGeom(geom); +} + + +}; + +Ops.Gl.Meshes.Torus.prototype = new CABLES.Op(); +CABLES.OPS["d921e008-21b9-4cf5-84a2-4dedca34f0c8"]={f:Ops.Gl.Meshes.Torus,objName:"Ops.Gl.Meshes.Torus"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Triangle +// +// ************************************************************** + +Ops.Gl.Meshes.Triangle = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + sizeW = op.inValueFloat("width", 1), + sizeH = op.inValueFloat("height", 1), + draw = op.inValueBool("Draw", true), + geom = new CGL.Geometry("triangle"), + geomOut = op.outObject("geometry"); + +geomOut.ignoreValueSerialize = true; + +op.setPortGroup("Size", [sizeW, sizeH]); + +const cgl = op.patch.cgl; +let mesh = null; +sizeW.onChange = create; +sizeH.onChange = create; + +create(); + +op.preRender = create; + +render.onTriggered = function () +{ + if (draw.get())mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +function create() +{ + geom.vertices = [ + 0.0, sizeH.get(), 0.0, + -sizeW.get(), -sizeH.get(), 0.0, + sizeW.get(), -sizeH.get(), 0.0 + ]; + + geom.vertexNormals = [ + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0 + ]; + geom.tangents = [ + 1, 0, 0, + 1, 0, 0, + 1, 0, 0 + ]; + geom.biTangents = [ + 0, 1, 0, + 0, 1, 0, + 0, 1, 0 + ]; + + geom.texCoords = [ + 0.5, 0.0, + 1.0, 1.0, + 0.0, 1.0, + ]; + + geom.verticesIndices = [ + 0, 1, 2 + ]; + + mesh = new CGL.Mesh(cgl, geom); + geomOut.set(null); + geomOut.set(geom); +} + + +}; + +Ops.Gl.Meshes.Triangle.prototype = new CABLES.Op(); +CABLES.OPS["331bc9dc-d44c-43bd-be18-a2673fba124e"]={f:Ops.Gl.Meshes.Triangle,objName:"Ops.Gl.Meshes.Triangle"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.TriangleArray_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.TriangleArray_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + inArr = op.inArray("Points", 3), + inVertCols = op.inArray("Vertex Colors", 4), + inTexCoords = op.inArray("TexCoords", 2), + inFlat = op.inValueBool("Flat", false), + inRenderMesh = op.inBool("Render Mesh", true), + next = op.outTrigger("Next"), + geomOut = op.outObject("Geometry"); + +let geom = new CGL.Geometry("triangle array"); + +let mesh = null; +let verts = null; +let needsUpdate = true; +const cgl = op.patch.cgl; + +op.toWorkPortsNeedToBeLinked(inArr, render); + +inArr.onChange = +inVertCols.onChange = +inTexCoords.onChange = () => +{ + needsUpdate = true; +}; +inFlat.onChange = () => +{ + geom = new CGL.Geometry("triangle array"); + update(); +}; + +function update() +{ + op.setUiError("coordsMismatch", null); + op.setUiError("colorsMismatch", null); + + verts = inArr.get(); + if (!verts) return; + + let num = verts.length / 3; + num = Math.abs(Math.floor(num)); + + let tc = []; + const texCoords = inTexCoords.get(); + if (texCoords) + { + if (texCoords.length !== (num * 2)) + { + op.setUiError("coordsMismatch", "Coords array does not have the correct length! (should be " + num * 2 + ")"); + } + tc = texCoords; + } + else + { + for (let i = 0; i < verts.length / 3; i++) + { + tc.push(0, 0, 0, 1, 1, 1); + } + } + + geom.vertices = verts; + geom.texCoords = tc; + + mesh = new CGL.Mesh(cgl, geom); + + const vertexColors = inVertCols.get(); + if (vertexColors) + { + if (vertexColors.length !== (num * 4)) + { + op.setUiError("colorsMismatch", "Color array does not have the correct length! (should be " + num * 4 + ")"); + return; + } + geom.vertexColors = vertexColors; + } + + if (!inFlat.get()) index(verts, geom); + else + { + geom.vertices = verts; + } + + geom.calculateNormals(); + geomOut.set(null); + geomOut.set(geom); + needsUpdate = false; +} + +render.onTriggered = function () +{ + if (needsUpdate)update(); + if (mesh && verts && inRenderMesh.get()) mesh.render(cgl.getShader()); + next.trigger(); +}; + +function index(vertsToIndex, geometry) +{ + const num = vertsToIndex.length / 3; + const arr = []; + const ind = []; + let delta = 0.0001; + + for (let i = 0; i < num; i++) + { + let found = false; + + for (let j = 0; j < arr.length; j += 3) + { + if ( + arr[j] < vertsToIndex[i * 3] + delta && + arr[j + 1] < vertsToIndex[i * 3 + 1] + delta && + arr[j + 2] < vertsToIndex[i * 3 + 2] + delta && + arr[j] > vertsToIndex[i * 3] - delta && + arr[j + 1] > vertsToIndex[i * 3 + 1] - delta && + arr[j + 2] > vertsToIndex[i * 3 + 2] - delta) + { + ind.push(j / 3); + found = true; + } + } + + if (!found) + { + arr.push(vertsToIndex[i * 3]); + arr.push(vertsToIndex[i * 3 + 1]); + arr.push(vertsToIndex[i * 3 + 2]); + ind.push(arr.length / 3 - 1); + } + } + + geometry.verticesIndices = ind; + geometry.vertices = arr; +} + + +}; + +Ops.Gl.Meshes.TriangleArray_v2.prototype = new CABLES.Op(); +CABLES.OPS["ee4d9fa8-996d-44a4-80a0-4f14bbd85502"]={f:Ops.Gl.Meshes.TriangleArray_v2,objName:"Ops.Gl.Meshes.TriangleArray_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.TriangleSphere +// +// ************************************************************** + +Ops.Gl.Meshes.TriangleSphere = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// http://paulbourke.net/geometry/circlesphere/ + +// http://paulbourke.net/geometry/circlesphere/csource3.c +//! !!! http://paulbourke.net/geometry/circlesphere/csource2.c + +const render = op.inTrigger("render"); +const next = op.outTrigger("next"); + +const inIterations = op.inValue("Iterations", 4); +const geomOut = op.outObject("Geometry"); + +const flat = op.inValueBool("Flat", false); +const inDraw = op.inValueBool("Draw", true); + +let verts = []; +let geom = null; +let mesh = null; +const cgl = op.patch.cgl; + +inIterations.onChange = generate; + +generate(); + +flat.onChange = generate; + +render.onTriggered = function () +{ + // cgl.gl.cullFace(cgl.gl.BACK); + // cgl.gl.enable(cgl.gl.CULL_FACE); + + if (inDraw.get() && mesh) mesh.render(cgl.getShader()); + next.trigger(); + + // cgl.gl.disable(cgl.gl.CULL_FACE); +}; + +function normalize(v) +{ + const len = Math.sqrt((v[0] * v[0]) + (v[1] * v[1]) + (v[2] * v[2])); + v[0] /= len; + v[1] /= len; + v[2] /= len; + return v; +} + +function index(verts, geom) +{ + const num = verts.length / 3; + const arr = []; + const ind = [], tc = []; + + for (let i = 0; i < num; i++) + { + let found = false; + + for (let j = 0; j < arr.length; j += 3) + { + if ( + arr[j + 0] == verts[i * 3 + 0] && + arr[j + 1] == verts[i * 3 + 1] && + arr[j + 2] == verts[i * 3 + 2]) + { + ind.push(j / 3); + found = true; + continue; + } + } + + if (!found) + { + arr.push(verts[i * 3 + 0]); + arr.push(verts[i * 3 + 1]); + arr.push(verts[i * 3 + 2]); + ind.push(arr.length / 3 - 1); + + tc.push(verts[i * 3 + 0]); + tc.push(verts[i * 3 + 1]); + } + } + + geom.verticesIndices = ind; + geom.vertices = arr; + geom.texCoords = tc; +} + +function generate() +{ + let iterations = Math.max(1, Math.floor(inIterations.get())); + iterations = Math.min(6, iterations); + const f = []; + let i, it; + const p = [[0, 0, 1], [0, 0, -1], [-1, -1, 0], [1, -1, 0], [1, 1, 0], [-1, 1, 0]]; + + let nt = 0, ntold; + + /* Create the level 0 object */ + const a = 1 / Math.sqrt(2.0); + for (i = 0; i < 6; i++) + { + p[i][0] *= a; + p[i][1] *= a; + } + + for (i = 0; i < 8; i++) + { + f[i] = [[], [], []]; + } + f[0][0] = p[0]; f[0][1] = p[3]; f[0][2] = p[4]; + f[1][0] = p[0]; f[1][1] = p[4]; f[1][2] = p[5]; + f[2][0] = p[0]; f[2][1] = p[5]; f[2][2] = p[2]; + f[3][0] = p[0]; f[3][1] = p[2]; f[3][2] = p[3]; + f[4][0] = p[1]; f[4][1] = p[4]; f[4][2] = p[3]; + f[5][0] = p[1]; f[5][1] = p[5]; f[5][2] = p[4]; + f[6][0] = p[1]; f[6][1] = p[2]; f[6][2] = p[5]; + f[7][0] = p[1]; f[7][1] = p[3]; f[7][2] = p[2]; + nt = 8; + + if (iterations > 1) + { + /* Bisect each edge and move to the surface of a unit sphere */ + for (it = 0; it < iterations; it++) + { + ntold = nt; + for (i = 0; i < ntold; i++) + { + const pa = [], pb = [], pc = []; + pa[0] = (f[i][0][0] + f[i][1][0]) / 2; + pa[1] = (f[i][0][1] + f[i][1][1]) / 2; + pa[2] = (f[i][0][2] + f[i][1][2]) / 2; + pb[0] = (f[i][1][0] + f[i][2][0]) / 2; + pb[1] = (f[i][1][1] + f[i][2][1]) / 2; + pb[2] = (f[i][1][2] + f[i][2][2]) / 2; + pc[0] = (f[i][2][0] + f[i][0][0]) / 2; + pc[1] = (f[i][2][1] + f[i][0][1]) / 2; + pc[2] = (f[i][2][2] + f[i][0][2]) / 2; + + normalize(pa); + normalize(pb); + normalize(pc); + + f.push([]); + f[nt][0] = f[i][0]; f[nt][1] = pa; f[nt][2] = pc; nt++; + f.push([]); + f[nt][0] = pa; f[nt][1] = f[i][1]; f[nt][2] = pb; nt++; + f.push([]); + f[nt][0] = pb; f[nt][1] = f[i][2]; f[nt][2] = pc; nt++; + + f[i][0] = pa; + f[i][1] = pb; + f[i][2] = pc; + } + } + } + + if (!geom)geom = new CGL.Geometry(op.name); + geom.clear(); + + verts = f.flat(Infinity); + + if (!flat.get()) index(verts, geom); + else + { + geom.unIndex(); + const indices = []; + for (i = 0; i < verts.length / 3; i++)indices.push(i); + geom.vertices = verts; + geom.verticesIndices = indices; + } + + geom.calculateNormals({ "forceZUp": false }); + geom.calcTangentsBitangents(); + + mesh = new CGL.Mesh(cgl, geom); + geomOut.set(null); + geomOut.set(geom); +} + + +}; + +Ops.Gl.Meshes.TriangleSphere.prototype = new CABLES.Op(); +CABLES.OPS["ad0e8831-9554-46b3-ac03-c5c166921eda"]={f:Ops.Gl.Meshes.TriangleSphere,objName:"Ops.Gl.Meshes.TriangleSphere"}; + + + + +// ************************************************************** +// +// Ops.Gl.NormalizeScreenCoordinates +// +// ************************************************************** + +Ops.Gl.NormalizeScreenCoordinates = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inX = op.inValue("X"), + inY = op.inValue("Y"), + outX = op.outNumber("Result X"), + outY = op.outNumber("Result Y"), + range = op.inValueBool("-1 to 1"); + +inX.onChange = + inY.onChange = + range.onChange = update; + +function update() +{ + let x = inX.get() / op.patch.cgl.canvas.width; + let y = inY.get() / op.patch.cgl.canvas.height; + + outX.set(x); + outY.set(y); + + if (range.get()) + { + x = x * 2 - 1; + y = y * 2 - 1; + } + + outX.set(x); + outY.set(y); +} + + +}; + +Ops.Gl.NormalizeScreenCoordinates.prototype = new CABLES.Op(); +CABLES.OPS["84d4f2a6-5ab7-4c97-85e0-2985bfcba70d"]={f:Ops.Gl.NormalizeScreenCoordinates,objName:"Ops.Gl.NormalizeScreenCoordinates"}; + + + + +// ************************************************************** +// +// Ops.Gl.OrTexture +// +// ************************************************************** + +Ops.Gl.OrTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + str0 = op.inTexture("Texture 1"), + str1 = op.inTexture("Texture 2"), + str2 = op.inTexture("Texture 3"), + str3 = op.inTexture("Texture 4"), + str4 = op.inTexture("Texture 5"), + str5 = op.inTexture("Texture 6"), + str6 = op.inTexture("Texture 7"), + str7 = op.inTexture("Texture 8"), + result = op.outTexture("Result"); + +str0.onChange = + str1.onChange = + str2.onChange = + str3.onChange = + str4.onChange = + str5.onChange = + str6.onChange = + str7.onChange = exec; + +const emptyTex = CGL.Texture.getEmptyTexture(op.patch.cgl); +const defaultTex = CGL.Texture.getTempTexture(op.patch.cgl); + +function exec() +{ + if (str0.get() && str0.get() != emptyTex && str0.get() != defaultTex) result.set(str0.get()); + else if (str1.get() && str1.get() != emptyTex && str1.get() != defaultTex) result.set(str1.get()); + else if (str2.get() && str2.get() != emptyTex && str2.get() != defaultTex) result.set(str2.get()); + else if (str3.get() && str3.get() != emptyTex && str3.get() != defaultTex) result.set(str3.get()); + else if (str4.get() && str4.get() != emptyTex && str4.get() != defaultTex) result.set(str4.get()); + else if (str5.get() && str5.get() != emptyTex && str5.get() != defaultTex) result.set(str5.get()); + else if (str6.get() && str6.get() != emptyTex && str6.get() != defaultTex) result.set(str6.get()); + else if (str7.get() && str7.get() != emptyTex && str7.get() != defaultTex) result.set(str7.get()); + else result.set(emptyTex); +} + + +}; + +Ops.Gl.OrTexture.prototype = new CABLES.Op(); +CABLES.OPS["9a383587-272d-429b-95a3-5c9b6007f8ea"]={f:Ops.Gl.OrTexture,objName:"Ops.Gl.OrTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Orthogonal_v2 +// +// ************************************************************** + +Ops.Gl.Orthogonal_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + bounds = op.inValue("bounds", 2), + fixAxis = op.inSwitch("Axis", ["X", "Y", "None"], "X"), + zNear = op.inValue("frustum near", -100), + zFar = op.inValue("frustum far", 100), + trigger = op.outTrigger("trigger"), + outRatio = op.outNumber("Ratio"), + outWidth = op.outNumber("Width"), + outHeight = op.outNumber("Height"); +const cgl = op.patch.cgl; + +render.onTriggered = function () +{ + const vp = cgl.getViewPort(); + + if (fixAxis.get() == "X") + { + const ratio = vp[3] / vp[2]; + + cgl.pushPMatrix(); + mat4.ortho( + cgl.pMatrix, + -bounds.get(), + bounds.get(), + -bounds.get() * ratio, + bounds.get() * ratio, + parseFloat(zNear.get()), + parseFloat(zFar.get()) + ); + + outWidth.set(bounds.get() * 2); + outHeight.set(bounds.get() * ratio * 2); + outRatio.set(ratio); + } + else if (fixAxis.get() == "Y") + { + const ratio = vp[2] / vp[3]; + + cgl.pushPMatrix(); + mat4.ortho( + cgl.pMatrix, + -bounds.get() * ratio, + bounds.get() * ratio, + -bounds.get(), + bounds.get(), + parseFloat(zNear.get()), + parseFloat(zFar.get()) + ); + + outWidth.set(bounds.get() * ratio * 2); + outHeight.set(bounds.get() * 2); + outRatio.set(ratio); + } + else + { + cgl.pushPMatrix(); + mat4.ortho( + cgl.pMatrix, + -bounds.get(), + bounds.get(), + -bounds.get(), + bounds.get(), + parseFloat(zNear.get()), + parseFloat(zFar.get()) + ); + + outWidth.set(bounds.get() * 2); + outHeight.set(bounds.get() * 2); + outRatio.set(1); + } + + trigger.trigger(); + cgl.popPMatrix(); +}; + + +}; + +Ops.Gl.Orthogonal_v2.prototype = new CABLES.Op(); +CABLES.OPS["b9235490-eaf2-4960-b1be-4279a4051ec6"]={f:Ops.Gl.Orthogonal_v2,objName:"Ops.Gl.Orthogonal_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.OverwriteViewportSize +// +// ************************************************************** + +Ops.Gl.OverwriteViewportSize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Exec"), + next = op.outTrigger("next"), + w = op.inValueInt("Width", 1920), + h = op.inValueInt("Height", 1080); + +render.onTriggered = doit; + +let oldFunc = null; +let cgl = op.patch.cgl; +let vp = [0, 0, 0, 0]; + +function newGetViewPort() +{ + return vp; +} + +function doit() +{ + let oldWidth = cgl.getViewPort()[2]; + let oldHeight = cgl.getViewPort()[3]; + + // cgl.forceViewPortSize(0,0,w.get(),h.get()); + + cgl.setViewPort(0, 0, w.get(), h.get()); + + next.trigger(); + + cgl.setViewPort(0, 0, oldWidth, oldHeight); +} + + +}; + +Ops.Gl.OverwriteViewportSize.prototype = new CABLES.Op(); +CABLES.OPS["20e370c7-d6e9-4e29-acab-1dd2eb4b921f"]={f:Ops.Gl.OverwriteViewportSize,objName:"Ops.Gl.OverwriteViewportSize"}; + + + + +// ************************************************************** +// +// Ops.Gl.Pbr.PbrEnvironmentLight +// +// ************************************************************** + +Ops.Gl.Pbr.PbrEnvironmentLight = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"IBLLUT_frag":"precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n\n#ifndef WEBGL1\n#define NUM_SAMPLES 1024u\n#else\n#define NUM_SAMPLES 1024\n#endif\n#define PI 3.14159265358\n\nIN vec3 P;\n{{MODULES_HEAD}}\n\n// from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/ShadersInclude/hdrFilteringFunctions.fx\n// modified to use different syntax for a number of variables\n#if NUM_SAMPLES > 0\n #ifndef WEBGL1\n // https://learnopengl.com/PBR/IBL/Specular-IBL\n // Hammersley\n float radicalInverse_VdC(uint bits)\n {\n bits = (bits << 16u) | (bits >> 16u);\n bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);\n bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);\n bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);\n bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);\n return float(bits) * 2.3283064365386963e-10; // / 0x100000000\n }\n\n vec2 hammersley(uint i, uint N)\n {\n return vec2(float(i)/float(N), radicalInverse_VdC(i));\n }\n #else\n float vanDerCorpus(int n, int base)\n {\n float invBase = 1.0 / float(base);\n float denom = 1.0;\n float result = 0.0;\n\n for(int i = 0; i < 32; ++i)\n {\n if(n > 0)\n {\n denom = mod(float(n), 2.0);\n result += denom * invBase;\n invBase = invBase / 2.0;\n n = int(float(n) / 2.0);\n }\n }\n\n return result;\n }\n\n vec2 hammersley(int i, int N)\n {\n return vec2(float(i)/float(N), vanDerCorpus(i, 2));\n }\n #endif\n\n\t// from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/ShadersInclude/importanceSampling.fx\n\tvec3 hemisphereImportanceSampleDggx(vec2 u, float a) {\n\t\t// pdf = D(a) * cosTheta\n\t\tfloat phi = 2. * PI * u.x;\n\n\t\t// NOTE: (aa-1) == (a-1)(a+1) produces better fp accuracy\n\t\tfloat cosTheta2 = (1. - u.y) / (1. + (a + 1.) * ((a - 1.) * u.y));\n\t\tfloat cosTheta = sqrt(cosTheta2);\n\t\tfloat sinTheta = sqrt(1. - cosTheta2);\n\n\t\treturn vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);\n\t}\n\n\t// from https://google.github.io/filament/Filament.md.html#toc9.5\n\t// modified to use different syntax for a number of variables\n const float NUM_SAMPLES_FLOAT = float(NUM_SAMPLES);\n const float NUM_SAMPLES_FLOAT_INVERSED = 1. / NUM_SAMPLES_FLOAT;\n const float NUM_SAMPLES_FLOAT_INVERSED4 = 4. / NUM_SAMPLES_FLOAT;\n\n float Visibility(float NdotV, float NdotL, float alphaG)\n {\n // from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx\n #ifdef WEBGL1\n // Appply simplification as all squared root terms are below 1 and squared\n float GGXV = NdotL * (NdotV * (1.0 - alphaG) + alphaG);\n float GGXL = NdotV * (NdotL * (1.0 - alphaG) + alphaG);\n return 0.5 / (GGXV + GGXL);\n #else\n float a2 = alphaG * alphaG;\n float GGXV = NdotL * sqrt(NdotV * (NdotV - a2 * NdotV) + a2);\n float GGXL = NdotV * sqrt(NdotL * (NdotL - a2 * NdotL) + a2);\n return 0.5 / (GGXV + GGXL);\n #endif\n }\n\n\tvoid main()\n\t{\n\t // actual implementation (not documentation) here: https://github.com/google/filament/blob/94ff2ea6b1e39d909e9066459f2ce8c2942eb876/libs/ibl/src/CubemapIBL.cpp\n\t\t{{MODULE_BEGIN_FRAG}}\n\t\tfloat NoV = P.x;\n\t\tfloat a = P.y;\n\n\t\tvec3 V;\n\t\tV.x = sqrt(1.0 - NoV*NoV);\n\t\tV.y = 0.0;\n\t\tV.z = NoV;\n\n\t\tvec2 r = vec2(0.0);\n\n #ifndef WEBGL1\n for(uint i = 0u; i < NUM_SAMPLES; i++)\n #else\n for(int i = 0; i < NUM_SAMPLES; i++)\n #endif\n {\n\t\t\tvec2 Xi = hammersley(i, NUM_SAMPLES);\n\t\t\tvec3 H = hemisphereImportanceSampleDggx(Xi, a);\n\t\t\tvec3 L = 2.0 * dot(V, H) * H - V;\n\n\t\t\tfloat VoH = clamp(dot(V, H), 0.0, 1.0);\n\t\t\tfloat NoL = clamp(L.z, 0.0, 1.0);\n\t\t\tfloat NoH = clamp(H.z, 0.0, 1.0);\n\n\t\t\tif (NoL > 0.0) {\n\t\t\t\tfloat Gv = Visibility(NoV, NoL, a) * NoL * (VoH / NoH);\n\t\t\t\tfloat Fc = pow(1.0 - VoH, 5.0);\n\n\t\t\t\t// modified for multiscattering https://google.github.io/filament/Filament.md.html#toc5.3.4.7\n\t\t\t r.x += Gv * Fc;\n\t\t\t\tr.y += Gv;\n\t\t\t}\n\t\t}\n\t\tr *= NUM_SAMPLES_FLOAT_INVERSED4;\n\n\t\t{{MODULE_COLOR}}\n\t\toutColor = vec4(r.x, r.y, 0.0, 1.0);\n\t}\n#endif\n","IBLLUT_vert":"precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n\n{{MODULES_HEAD}}\nIN vec3 vPosition;\nOUT vec3 P;\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\nvoid main()\n{\n vec4 pos = vec4(vPosition, 1.0);\n mat4 mMatrix = modelMatrix;\n\n {{MODULE_VERTEX_POSITION}}\n\n gl_Position = pos;\n\n P = (vPosition + 1.0) * 0.5;\n}\n","irradiance_frag":"precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n\n\n// from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/ShadersInclude/hdrFilteringFunctions.fx\n// modified to use different syntax for a number of variables, equirectangular projection and rgbe encoding\n{{MODULES_HEAD}}\n#ifndef WEBGL1\n#define NUM_SAMPLES 2048u\n#else\n#define NUM_SAMPLES 2048\n#endif\n#define PI 3.14159265358\n#define PI_TWO 2.*PI\n#define RECIPROCAL_PI 1./PI\n#define RECIPROCAL_PI2 RECIPROCAL_PI/2.\n\n\n#ifdef WEBGL1\n #ifdef GL_EXT_shader_texture_lod\n #define textureLod texture2DLodEXT\n #endif\n#endif\n#define SAMPLETEX textureLod\n\n// set by cables\nUNI vec3 camPos;\n\nIN vec3 FragPos;\nUNI float rotation;\nUNI vec2 filteringInfo;\nUNI sampler2D EquiCubemap;\n\nvec2 SampleSphericalMap(vec3 direction, float rotation)\n{\n #ifndef WEBGL1\n vec3 newDirection = normalize(direction);\n\t\tvec2 sampleUV;\n\t\tsampleUV.x = -1. * (atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5);\n\t\tsampleUV.y = asin( clamp(direction.y, -1., 1.) ) * RECIPROCAL_PI + 0.5;\n #endif\n\n #ifdef WEBGL1\n vec3 newDirection = normalize(direction);\n\t\tvec2 sampleUV = vec2(atan(newDirection.z, newDirection.x), asin(newDirection.y+1e-6));\n sampleUV *= vec2(-0.1591, 0.3183);\n sampleUV += 0.5;\n #endif\n sampleUV.x += rotation;\n return sampleUV * vec2(-1.,1.);\n}\n\n// https://community.khronos.org/t/addition-of-two-hdr-rgbe-values/55669\nvec4 EncodeRGBE8(vec3 rgb)\n{\n vec4 vEncoded;\n float maxComponent = max(max(rgb.r, rgb.g), rgb.b);\n float fExp = ceil(log2(maxComponent));\n vEncoded.rgb = rgb / exp2(fExp);\n vEncoded.a = (fExp + 128.0) / 255.0;\n return vEncoded;\n}\n// https://enkimute.github.io/hdrpng.js/\nvec3 DecodeRGBE8(vec4 rgbe)\n{\n vec3 vDecoded = rgbe.rgb * pow(2.0, rgbe.a * 255.0 - 128.0);\n return vDecoded;\n}\n\n// from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/ShadersInclude/importanceSampling.fx\nvec3 hemisphereCosSample(vec2 u) {\n // pdf = cosTheta / M_PI;\n float phi = 2. * PI * u.x;\n\n float cosTheta2 = 1. - u.y;\n float cosTheta = sqrt(cosTheta2);\n float sinTheta = sqrt(1. - cosTheta2);\n\n return vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);\n}\n\n#ifndef WEBGL1\n // https://learnopengl.com/PBR/IBL/Specular-IBL\n // Hammersley\n float radicalInverse_VdC(uint bits)\n {\n bits = (bits << 16u) | (bits >> 16u);\n bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);\n bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);\n bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);\n bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);\n return float(bits) * 2.3283064365386963e-10; // / 0x100000000\n }\n\n vec2 hammersley(uint i, uint N)\n {\n return vec2(float(i)/float(N), radicalInverse_VdC(i));\n }\n#else\n float vanDerCorpus(int n, int base)\n {\n float invBase = 1.0 / float(base);\n float denom = 1.0;\n float result = 0.0;\n\n for(int i = 0; i < 32; ++i)\n {\n if(n > 0)\n {\n denom = mod(float(n), 2.0);\n result += denom * invBase;\n invBase = invBase / 2.0;\n n = int(float(n) / 2.0);\n }\n }\n\n return result;\n }\n\n vec2 hammersley(int i, int N)\n {\n return vec2(float(i)/float(N), vanDerCorpus(i, 2));\n }\n#endif\n\n// from https://github.com/google/filament/blob/main/shaders/src/light_indirect.fs\nfloat prefilteredImportanceSampling(float ipdf, float omegaP)\n{\n // See: \"Real-time Shading with Filtered Importance Sampling\", Jaroslav Krivanek\n // Prefiltering doesn't work with anisotropy\n const float numSamples = float(NUM_SAMPLES);\n const float invNumSamples = 1.0 / float(numSamples);\n const float K = 4.0;\n float omegaS = invNumSamples * ipdf;\n float mipLevel = log2(K * omegaS / omegaP) * 0.5; // log4\n return mipLevel;\n}\n\nconst float NUM_SAMPLES_FLOAT = float(NUM_SAMPLES);\nconst float NUM_SAMPLES_FLOAT_INVERSED = 1. / NUM_SAMPLES_FLOAT;\n\nconst float K = 4.;\n\nvoid main()\n{\n {{MODULE_BEGIN_FRAG}}\n vec4 col = vec4(0.0, 0.0, 0.0, 0.0);\n\n vec3 n = normalize(FragPos);\n vec3 tangent = normalize(cross(vec3(0.0, 0.0, 1.0), n));\n vec3 bitangent = cross(n, tangent);\n mat3 tbn = mat3(tangent, bitangent, n);\n\n float maxLevel = filteringInfo.y;\n float dim0 = filteringInfo.x;\n float omegaP = (4. * PI) / (6. * dim0 * dim0);\n\n #ifndef WEBGL1\n for(uint i = 0u; i < NUM_SAMPLES; ++i)\n #else\n for(int i = 0; i < NUM_SAMPLES; ++i)\n #endif\n {\n vec2 Xi = hammersley(i, NUM_SAMPLES);\n vec3 Ls = hemisphereCosSample(Xi);\n\n Ls = normalize(Ls);\n\n vec3 Ns = vec3(0., 0., 1.);\n\n float NoL = dot(Ns, Ls);\n\n if (NoL > 0.) {\n float pdf_inversed = PI / NoL;\n\n float omegaS = NUM_SAMPLES_FLOAT_INVERSED * pdf_inversed;\n // from https://github.com/google/filament/blob/main/shaders/src/light_indirect.fs\n float l = log2(K * omegaS / omegaP) * 0.5;\n float mipLevel = clamp(l + 1.0, 0.0, maxLevel);\n\n #ifndef DONT_USE_RGBE_CUBEMAPS\n vec3 c = DecodeRGBE8(SAMPLETEX(EquiCubemap, SampleSphericalMap(tbn * Ls, rotation), mipLevel)).rgb;\n #else\n vec3 c = SAMPLETEX(EquiCubemap, SampleSphericalMap(tbn * Ls, rotation), mipLevel).rgb;\n #endif\n col.rgb += c;\n }\n }\n\n col = EncodeRGBE8(col.rgb * PI * NUM_SAMPLES_FLOAT_INVERSED);\n\n {{MODULE_COLOR}}\n outColor = col;\n}\n","irradiance_vert":"precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n\n\n{{MODULES_HEAD}}\nIN vec3 vPosition;\nIN float attrVertIndex;\n\nOUT vec3 FragPos;\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\n\nvoid main()\n{\n FragPos = vPosition;\n\n {{MODULE_VERTEX_POSITION}}\n gl_Position = projMatrix * viewMatrix * modelMatrix * vec4(vPosition, 1.0);\n gl_Position = gl_Position.xyww;\n}\n","prefiltering_frag":"precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n\n\n// from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/ShadersInclude/hdrFilteringFunctions.fx\n// modified to use different syntax for a number of variables, equirectangular projection and rgbe encoding\n{{MODULES_HEAD}}\n#ifndef WEBGL1\n#define NUM_SAMPLES 2048u\n#else\n#define NUM_SAMPLES 2048\n#endif\n#define PI 3.14159265358\n#define PI_TWO 2.*PI\n#define RECIPROCAL_PI 1./PI\n#define RECIPROCAL_PI2 RECIPROCAL_PI/2.\n#define MINIMUMVARIANCE 0.0005\n\n\n#ifdef WEBGL1\n #ifdef GL_EXT_shader_texture_lod\n #define textureLod texture2DLodEXT\n #endif\n#endif\n#define SAMPLETEX textureLod\n\nIN vec3 FragPos;\nUNI float roughness;\nUNI float rotation;\nUNI vec2 filteringInfo;\nUNI sampler2D EquiCubemap;\n\nvec2 SampleSphericalMap(vec3 direction, float rotation)\n{\n #ifndef WEBGL1\n vec3 newDirection = normalize(direction);\n\t\tvec2 sampleUV;\n\t\tsampleUV.x = -1. * (atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5);\n\t\tsampleUV.y = asin( clamp(direction.y, -1., 1.) ) * RECIPROCAL_PI + 0.5;\n #endif\n\n #ifdef WEBGL1\n vec3 newDirection = normalize(direction);\n\t\tvec2 sampleUV = vec2(atan(newDirection.z, newDirection.x), asin(newDirection.y+1e-6));\n sampleUV *= vec2(-0.1591, 0.3183);\n sampleUV += 0.5;\n #endif\n sampleUV.x += rotation;\n return sampleUV * vec2(-1.,1.);\n}\n\n// https://community.khronos.org/t/addition-of-two-hdr-rgbe-values/55669\nvec4 EncodeRGBE8(vec3 rgb)\n{\n vec4 vEncoded;\n float maxComponent = max(max(rgb.r, rgb.g), rgb.b);\n float fExp = ceil(log2(maxComponent));\n vEncoded.rgb = rgb / exp2(fExp);\n vEncoded.a = (fExp + 128.0) / 255.0;\n return vEncoded;\n}\n// https://enkimute.github.io/hdrpng.js/\nvec3 DecodeRGBE8(vec4 rgbe)\n{\n vec3 vDecoded = rgbe.rgb * pow(2.0, rgbe.a * 255.0-128.0);\n return vDecoded;\n}\n\n// from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/ShadersInclude/importanceSampling.fx\nvec3 hemisphereImportanceSampleDggx(vec2 u, float a) {\n // pdf = D(a) * cosTheta\n float phi = 2. * PI * u.x;\n\n // NOTE: (aa-1) == (a-1)(a+1) produces better fp accuracy\n float cosTheta2 = (1. - u.y) / (1. + (a + 1.) * ((a - 1.) * u.y));\n float cosTheta = sqrt(cosTheta2);\n float sinTheta = sqrt(1. - cosTheta2);\n\n return vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);\n}\n\n// from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx\nfloat normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)\n{\n // Note: alphaG is average slope (gradient) of the normals in slope-space.\n // It is also the (trigonometric) tangent of the median distribution value, i.e. 50% of normals have\n // a tangent (gradient) closer to the macrosurface than this slope.\n float a2 = alphaG * alphaG;\n float d = NdotH * NdotH * (a2 - 1.0) + 1.0;\n return a2 / (PI * d * d);\n}\n\n// from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/ShadersInclude/pbrHelperFunctions.fx\nfloat convertRoughnessToAverageSlope(float roughness)\n{\n // Calculate AlphaG as square of roughness (add epsilon to avoid numerical issues)\n return (roughness * roughness) + MINIMUMVARIANCE;\n}\n\n\n#ifndef WEBGL1\n // https://learnopengl.com/PBR/IBL/Specular-IBL\n // Hammersley\n float radicalInverse_VdC(uint bits)\n {\n bits = (bits << 16u) | (bits >> 16u);\n bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);\n bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);\n bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);\n bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);\n return float(bits) * 2.3283064365386963e-10; // / 0x100000000\n }\n\n vec2 hammersley(uint i, uint N)\n {\n return vec2(float(i)/float(N), radicalInverse_VdC(i));\n }\n#else\n float vanDerCorpus(int n, int base)\n {\n float invBase = 1.0 / float(base);\n float denom = 1.0;\n float result = 0.0;\n\n for(int i = 0; i < 32; ++i)\n {\n if(n > 0)\n {\n denom = mod(float(n), 2.0);\n result += denom * invBase;\n invBase = invBase / 2.0;\n n = int(float(n) / 2.0);\n }\n }\n\n return result;\n }\n\n vec2 hammersley(int i, int N)\n {\n return vec2(float(i)/float(N), vanDerCorpus(i, 2));\n }\n#endif\n\nfloat log4(float x)\n{\n return log2(x) / 2.;\n}\n\nconst float NUM_SAMPLES_FLOAT = float(NUM_SAMPLES);\nconst float NUM_SAMPLES_FLOAT_INVERSED = 1. / NUM_SAMPLES_FLOAT;\n\nconst float K = 4.;\n\nvoid main()\n{\n {{MODULE_BEGIN_FRAG}}\n vec3 n = normalize(FragPos);\n float alphaG = convertRoughnessToAverageSlope(roughness);\n vec4 result = vec4(0.);\n\n if (alphaG == 0.)\n {\n result = SAMPLETEX(EquiCubemap, SampleSphericalMap(n, rotation), 0.0);\n }\n else\n {\n vec3 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);\n tangent = normalize(cross(tangent, n));\n vec3 bitangent = cross(n, tangent);\n mat3 tbn = mat3(tangent, bitangent, n);\n\n float maxLevel = filteringInfo.y;\n float dim0 = filteringInfo.x;\n float omegaP = (4. * PI) / (6. * dim0 * dim0);\n\n float weight = 0.;\n #if defined(WEBGL2)\n for(uint i = 0u; i < NUM_SAMPLES; ++i)\n #else\n for(int i = 0; i < NUM_SAMPLES; ++i)\n #endif\n {\n vec2 Xi = hammersley(i, NUM_SAMPLES);\n vec3 H = hemisphereImportanceSampleDggx(Xi, alphaG);\n\n float NoV = 1.;\n float NoH = H.z;\n float NoH2 = H.z * H.z;\n float NoL = 2. * NoH2 - 1.;\n vec3 L = vec3(2. * NoH * H.x, 2. * NoH * H.y, NoL);\n L = normalize(L);\n\n if (NoL > 0.)\n {\n float pdf_inversed = 4. / normalDistributionFunction_TrowbridgeReitzGGX(NoH, alphaG);\n\n float omegaS = NUM_SAMPLES_FLOAT_INVERSED * pdf_inversed;\n float l = log4(omegaS) - log4(omegaP) + log4(K);\n float mipLevel = clamp(l, 0.0, maxLevel);\n\n weight += NoL;\n\n #ifndef DONT_USE_RGBE_CUBEMAPS\n vec3 c = DecodeRGBE8(SAMPLETEX(EquiCubemap, SampleSphericalMap(tbn * L, rotation), mipLevel)).rgb;\n #else\n vec3 c = SAMPLETEX(EquiCubemap, SampleSphericalMap(tbn * L, rotation), mipLevel).rgb;\n #endif\n result.rgb += c * NoL;\n }\n }\n\n result = result / weight;\n result = EncodeRGBE8(result.rgb);\n }\n\n {{MODULE_COLOR}}\n outColor = result;\n}\n","prefiltering_vert":"precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n\n{{MODULES_HEAD}}\nIN vec3 vPosition;\nIN float attrVertIndex;\n\nOUT vec3 FragPos;\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\n\nvoid main()\n{\n FragPos = vPosition;\n\n {{MODULE_VERTEX_POSITION}}\n gl_Position = projMatrix * viewMatrix * modelMatrix * vec4(vPosition, 1.0);\n gl_Position = gl_Position.xyww;\n}\n",}; +// utility +const cgl = op.patch.cgl; +const IS_WEBGL_1 = cgl.glVersion == 1; + +const BB = new CABLES.CG.BoundingBox(); +const geometry = new CGL.Geometry("unit cube"); +geometry.vertices = new Float32Array([ + -1.0, 1.0, -1.0, + -1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + 1.0, 1.0, -1.0, + -1.0, 1.0, -1.0, + + -1.0, -1.0, 1.0, + -1.0, -1.0, -1.0, + -1.0, 1.0, -1.0, + -1.0, 1.0, -1.0, + -1.0, 1.0, 1.0, + -1.0, -1.0, 1.0, + + 1.0, -1.0, -1.0, + 1.0, -1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, -1.0, + 1.0, -1.0, -1.0, + + -1.0, -1.0, 1.0, + -1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, + -1.0, -1.0, 1.0, + + -1.0, 1.0, -1.0, + 1.0, 1.0, -1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, + -1.0, 1.0, -1.0, + + -1.0, -1.0, -1.0, + -1.0, -1.0, 1.0, + 1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + -1.0, -1.0, 1.0, + 1.0, -1.0, 1.0 +]); +const mesh = new CGL.Mesh(cgl, geometry); +const fullscreenRectangle = CGL.MESHES.getSimpleRect(cgl, "fullscreenRectangle"); +// inputs +const inTrigger = op.inTrigger("render"); +// const inTriggerRecapture = op.inTriggerButton("recapture"); + +const inCubemap = op.inTexture("RGBE Environment map"); + +const inIrradianceSize = op.inDropDown("Size Irradiance map", [16, 32, 64], 64); +const inPrefilteredSize = op.inDropDown("Size pre-filtered environment", [64, 128], 128); +const inIBLLUTSize = op.inDropDown("Size IBL LUT", [128, 256, 512, 1024], 256); +const inToggleRGBE = op.inBool("Environment map does not contain RGBE data", false); +const inRotation = op.inFloat("Rotation", 0.0); +const inUseParallaxCorrection = op.inValueBool("Use parallax correction", false); + +const inPCOriginX = op.inFloat("center X", 0); +const inPCOriginY = op.inFloat("center Y", 1.8); +const inPCOriginZ = op.inFloat("center Z", 0); +const inPCboxMinX = op.inFloat("Box min X", -1); +const inPCboxMinY = op.inFloat("Box min Y", -1); +const inPCboxMinZ = op.inFloat("Box min Z", -1); +const inPCboxMaxX = op.inFloat("Box max X", 1); +const inPCboxMaxY = op.inFloat("Box max Y", 1); +const inPCboxMaxZ = op.inFloat("Box max Z", 1); + +op.setPortGroup("Parallax Correction", [ + inUseParallaxCorrection, + inPCOriginX, + inPCOriginY, + inPCOriginZ, + inPCboxMinX, + inPCboxMinY, + inPCboxMinZ, + inPCboxMaxX, + inPCboxMaxY, + inPCboxMaxZ +]); + +let IrradianceSizeChanged = true; +let PrefilteredSizeChanged = true; +let IBLLUTSizeChanged = true; +inIrradianceSize.onChange = () => { IrradianceSizeChanged = true; }; +inPrefilteredSize.onChange = () => { PrefilteredSizeChanged = true; }; +inIBLLUTSize.onChange = () => { IBLLUTSizeChanged = true; }; + +// outputs +const outTrigger = op.outTrigger("next"); + +const outTexIBLLUT = op.outTexture("IBL LUT"); +const outTexIrradiance = op.outTexture("cubemap (diffuse irradiance)"); +const outTexPrefiltered = op.outTexture("cubemap (pre-filtered environment map)"); +const outMipLevels = op.outNumber("Number of Pre-filtered mip levels"); +// UI stuff +op.toWorkPortsNeedToBeLinked(inCubemap); + +// globals +let irradianceFrameBuffer = null; +let PrefilteredTexture = null; +let prefilteredFrameBuffer = null; +let iblLutFrameBuffer = null; +let maxMipLevels = null; +const pbrEnv = {}; +const IrradianceShader = new CGL.Shader(cgl, "IrradianceShader"); +const PrefilteringShader = new CGL.Shader(cgl, "PrefilteringShader"); +IrradianceShader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +PrefilteringShader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); + +if (cgl.glVersion == 1) +{ + if (!cgl.gl.getExtension("EXT_shader_texture_lod")) + { + op.log("no EXT_shader_texture_lod texture extension"); + throw "no EXT_shader_texture_lod texture extension"; + } + else + { + IrradianceShader.enableExtension("GL_EXT_shader_texture_lod"); + PrefilteringShader.enableExtension("GL_EXT_shader_texture_lod"); + cgl.gl.getExtension("OES_texture_float"); + cgl.gl.getExtension("OES_texture_float_linear"); + cgl.gl.getExtension("OES_texture_half_float"); + cgl.gl.getExtension("OES_texture_half_float_linear"); + + cgl.gl.getExtension("WEBGL_color_buffer_float"); + + IrradianceShader.enableExtension("GL_OES_standard_derivatives"); + IrradianceShader.enableExtension("GL_OES_texture_float"); + IrradianceShader.enableExtension("GL_OES_texture_float_linear"); + IrradianceShader.enableExtension("GL_OES_texture_half_float"); + IrradianceShader.enableExtension("GL_OES_texture_half_float_linear"); + PrefilteringShader.enableExtension("GL_OES_standard_derivatives"); + PrefilteringShader.enableExtension("GL_OES_texture_float"); + PrefilteringShader.enableExtension("GL_OES_texture_float_linear"); + PrefilteringShader.enableExtension("GL_OES_texture_half_float"); + PrefilteringShader.enableExtension("GL_OES_texture_half_float_linear"); + } +} + +let filteringInfo = [0, 0]; +IrradianceShader.offScreenPass = true; +const uniformIrradianceCubemap = new CGL.Uniform(IrradianceShader, "t", "EquiCubemap", 0); +const uniformFilteringInfo = new CGL.Uniform(IrradianceShader, "2f", "filteringInfo", filteringInfo); +const uniformRotation = new CGL.Uniform(IrradianceShader, "f", "rotation", 0); +IrradianceShader.setSource(attachments.irradiance_vert, attachments.irradiance_frag); + +let prefilteringInfo = [0, 0]; +PrefilteringShader.offScreenPass = true; +const uniformPrefilteringCubemap = new CGL.Uniform(PrefilteringShader, "t", "EquiCubemap", 0); +const uniformPrefilteringRoughness = new CGL.Uniform(PrefilteringShader, "f", "roughness", 0); +const uniformPrefilteringRotation = new CGL.Uniform(PrefilteringShader, "f", "rotation", 0); +const uniformPrefilteringInfo = new CGL.Uniform(PrefilteringShader, "2f", "filteringInfo", prefilteringInfo); +PrefilteringShader.setSource(attachments.prefiltering_vert, attachments.prefiltering_frag); + +const IBLLUTShader = new CGL.Shader(cgl, "IBLLUTShader"); +IBLLUTShader.offScreenPass = true; +IBLLUTShader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +IBLLUTShader.setSource(attachments.IBLLUT_vert, attachments.IBLLUT_frag); + +inToggleRGBE.onChange = () => +{ + IrradianceShader.toggleDefine("DONT_USE_RGBE_CUBEMAPS", inToggleRGBE); + PrefilteringShader.toggleDefine("DONT_USE_RGBE_CUBEMAPS", inToggleRGBE); + + IrradianceSizeChanged = true; + PrefilteredSizeChanged = true; +}; + +inRotation.onChange = () => +{ + PrefilteredSizeChanged = + IrradianceSizeChanged = true; +}; + +// utility functions +function captureIrradianceCubemap(size) +{ + if (irradianceFrameBuffer) irradianceFrameBuffer.dispose(); + + irradianceFrameBuffer = new CGL.CubemapFramebuffer(cgl, Number(size), Number(size), { + "isFloatingPointTexture": false, + "clear": false, + "filter": CGL.Texture.FILTER_NEAREST, // due to banding with rgbe + "wrap": CGL.Texture.WRAP_CLAMP_TO_EDGE + }); + + filteringInfo[0] = size; + filteringInfo[1] = 1.0 + Math.floor(Math.log(size) * 1.44269504088896340736); + + IrradianceShader.popTextures(); + IrradianceShader.pushTexture(uniformIrradianceCubemap, inCubemap.get().tex); + uniformRotation.setValue(inRotation.get() / 360.0); + + irradianceFrameBuffer.renderStart(cgl); + for (let i = 0; i < 6; i += 1) + { + irradianceFrameBuffer.renderStartCubemapFace(i); + + // cgl.gl.clearColor(0, 0, 0, 0); + // if(i==0) cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + mesh.render(IrradianceShader); + irradianceFrameBuffer.renderEndCubemapFace(); + } + irradianceFrameBuffer.renderEnd(); + + outTexIrradiance.set(null); // pandur + outTexIrradiance.set(irradianceFrameBuffer.getTextureColor()); +} + +function capturePrefilteredCubemap(size) +{ + size = Number(size); + let captureFBO = new CGL.CubemapFramebuffer(cgl, size, size, { + // "isFloatingPointTexture": true, + "clear": false, + "filter": CGL.Texture.FILTER_LINEAR, + "wrap": CGL.Texture.WRAP_CLAMP_TO_EDGE + }); + + if (prefilteredFrameBuffer) prefilteredFrameBuffer.dispose(); + + prefilteredFrameBuffer = new CGL.CubemapFramebuffer(cgl, size, size, { + "isFloatingPointTexture": false, + "clear": false, + "filter": CGL.Texture.FILTER_MIPMAP, + "wrap": CGL.Texture.WRAP_CLAMP_TO_EDGE + }); + + cgl.gl.bindTexture(cgl.gl.TEXTURE_CUBE_MAP, prefilteredFrameBuffer.getTextureColor().tex); + cgl.gl.texParameteri(cgl.gl.TEXTURE_CUBE_MAP, cgl.gl.TEXTURE_WRAP_R, cgl.gl.CLAMP_TO_EDGE); + + cgl.gl.texParameteri(cgl.gl.TEXTURE_CUBE_MAP, cgl.gl.TEXTURE_MIN_FILTER, cgl.gl.LINEAR_MIPMAP_LINEAR); + cgl.gl.texParameteri(cgl.gl.TEXTURE_CUBE_MAP, cgl.gl.TEXTURE_MAG_FILTER, cgl.gl.LINEAR); + cgl.gl.generateMipmap(cgl.gl.TEXTURE_CUBE_MAP); // make sure memory is assigned for mips + + maxMipLevels = 1.0 + Math.floor(Math.log(size) * 1.44269504088896340736); + outMipLevels.set(maxMipLevels); + prefilteringInfo[0] = size; + prefilteringInfo[1] = maxMipLevels; + + PrefilteringShader.popTextures(); + PrefilteringShader.pushTexture(uniformPrefilteringCubemap, inCubemap.get().tex); + uniformPrefilteringRotation.setValue(inRotation.get() / 360.0); + + for (let mip = 0; mip <= maxMipLevels; ++mip) + { + let currentMipSize = size * Math.pow(0.5, mip); + let roughness = mip / (maxMipLevels - 1); + uniformPrefilteringRoughness.setValue(roughness); + + captureFBO.setSize(Number(currentMipSize), Number(currentMipSize)); + captureFBO.renderStart(cgl); + for (let i = 0; i < 6; i += 1) + { + captureFBO.renderStartCubemapFace(i); + + // if(i==0)cgl.gl.clearColor(0, 0.1, 0.2, 0); + // if(i==0)cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + + mesh.render(PrefilteringShader); + cgl.gl.bindTexture(cgl.gl.TEXTURE_CUBE_MAP, prefilteredFrameBuffer.getTextureColor().tex); + cgl.gl.copyTexImage2D(cgl.gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, mip, cgl.gl.RGBA, 0, 0, Number(currentMipSize), Number(currentMipSize), null); + captureFBO.renderEndCubemapFace(); + } + captureFBO.renderEnd(); + } + captureFBO.delete(); + cgl.setTexture(0, null); + + outTexPrefiltered.set(null); + outTexPrefiltered.set(prefilteredFrameBuffer.getTextureColor()); +} + +function computeIBLLUT(size) +{ + size = Number(size); + if (iblLutFrameBuffer) iblLutFrameBuffer.dispose(); + + if (IS_WEBGL_1) + { + iblLutFrameBuffer = new CGL.Framebuffer(cgl, size, size, { + "isFloatingPointTexture": true, + "filter": CGL.Texture.FILTER_LINEAR, + "wrap": CGL.Texture.WRAP_CLAMP_TO_EDGE + }); + } + else + { + iblLutFrameBuffer = new CGL.Framebuffer2(cgl, size, size, { + "isFloatingPointTexture": true, + "filter": CGL.Texture.FILTER_LINEAR, + "wrap": CGL.Texture.WRAP_CLAMP_TO_EDGE, + }); + } + + cgl.frameStore.renderOffscreen = true; + iblLutFrameBuffer.renderStart(cgl); + fullscreenRectangle.render(IBLLUTShader); + iblLutFrameBuffer.renderEnd(); + cgl.frameStore.renderOffscreen = false; + outTexIBLLUT.set(iblLutFrameBuffer.getTextureColor()); +} + +inCubemap.onChange = () => +{ + if (inCubemap.get()) + op.setUiError("nocubemapinput", null); + + PrefilteredSizeChanged = + IrradianceSizeChanged = true; +}; + +function drawHelpers() +{ + gui.setTransformGizmo({ + "posX": inPCOriginX, + "posY": inPCOriginY, + "posZ": inPCOriginZ, + }); + gui.setTransformGizmo({ + "posX": inPCboxMinX, + "posY": inPCboxMinY, + "posZ": inPCboxMinZ, + }, 1); + gui.setTransformGizmo({ + "posX": inPCboxMaxX, + "posY": inPCboxMaxY, + "posZ": inPCboxMaxZ, + }, 2); + if (CABLES.UI.renderHelper) + { + cgl.pushShader(CABLES.GL_MARKER.getDefaultShader(cgl)); + } + else + { + cgl.pushShader(CABLES.GL_MARKER.getSelectedShader(cgl)); + } + cgl.pushModelMatrix(); + // translate + mat4.translate(cgl.mMatrix, cgl.mMatrix, [(inPCboxMinX.get() + inPCboxMaxX.get()) / 2.0, (inPCboxMinY.get() + inPCboxMaxY.get()) / 2.0, (inPCboxMinZ.get() + inPCboxMaxZ.get()) / 2.0]); + // scale to bounds + mat4.scale(cgl.mMatrix, cgl.mMatrix, [(inPCboxMaxX.get() - inPCboxMinX.get()) / 2.0, (inPCboxMaxY.get() - inPCboxMinY.get()) / 2.0, (inPCboxMaxZ.get() - inPCboxMinZ.get()) / 2.0]); + // draw + BB.render(cgl); + cgl.popShader(); + cgl.popModelMatrix(); +} + +inUseParallaxCorrection.onChange = () => +{ + const active = inUseParallaxCorrection.get(); + inPCOriginX.setUiAttribs({ "greyout": !active }); + inPCOriginY.setUiAttribs({ "greyout": !active }); + inPCOriginZ.setUiAttribs({ "greyout": !active }); + inPCboxMinX.setUiAttribs({ "greyout": !active }); + inPCboxMinY.setUiAttribs({ "greyout": !active }); + inPCboxMinZ.setUiAttribs({ "greyout": !active }); + inPCboxMaxX.setUiAttribs({ "greyout": !active }); + inPCboxMaxY.setUiAttribs({ "greyout": !active }); + inPCboxMaxZ.setUiAttribs({ "greyout": !active }); +}; + +// onTriggered +inTrigger.onTriggered = function () +{ + if (!inCubemap.get()) + { + outTrigger.trigger(); + op.setUiError("nocubemapinput", "No Environment Texture connected"); + return; + } + + uniformFilteringInfo.setValue(filteringInfo); + uniformPrefilteringInfo.setValue(prefilteringInfo); + + if (!cgl.frameStore.shadowPass) + { + if (IBLLUTSizeChanged) + { + computeIBLLUT(Number(inIBLLUTSize.get())); + IBLLUTSizeChanged = false; + } + + if (PrefilteredSizeChanged) + { + capturePrefilteredCubemap(Number(inPrefilteredSize.get())); + PrefilteredSizeChanged = false; + } + + if (IrradianceSizeChanged) + { + captureIrradianceCubemap(Number(inIrradianceSize.get())); + IrradianceSizeChanged = false; + } + } + + pbrEnv.texIBLLUT = outTexIBLLUT.get(); + pbrEnv.texDiffIrr = outTexIrradiance.get(); + pbrEnv.texPreFiltered = outTexPrefiltered.get(); + pbrEnv.texPreFilteredMipLevels = outMipLevels.get(); + + pbrEnv.UseParallaxCorrection = inUseParallaxCorrection.get(); + pbrEnv.PCOrigin = [inPCOriginX.get(), inPCOriginY.get(), inPCOriginZ.get()]; + pbrEnv.PCboxMin = [inPCboxMinX.get(), inPCboxMinY.get(), inPCboxMinZ.get()]; + pbrEnv.PCboxMax = [inPCboxMaxX.get(), inPCboxMaxY.get(), inPCboxMaxZ.get()]; + + cgl.frameStore.pbrEnvStack = cgl.frameStore.pbrEnvStack || []; + cgl.frameStore.pbrEnvStack.push(pbrEnv); + + if (cgl.shouldDrawHelpers(op) && pbrEnv.UseParallaxCorrection && !cgl.frameStore.shadowPass) drawHelpers(); + + outTrigger.trigger(); + cgl.frameStore.pbrEnvStack.pop(); +}; + + +}; + +Ops.Gl.Pbr.PbrEnvironmentLight.prototype = new CABLES.Op(); +CABLES.OPS["7110f169-adfd-4649-a77a-c825751eaa9b"]={f:Ops.Gl.Pbr.PbrEnvironmentLight,objName:"Ops.Gl.Pbr.PbrEnvironmentLight"}; + + + + +// ************************************************************** +// +// Ops.Gl.Pbr.PbrMaterial +// +// ************************************************************** + +Ops.Gl.Pbr.PbrMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"BasicPBR_frag":"precision highp float;\nprecision highp int;\n{{MODULES_HEAD}}\n\n// set by cables\nUNI vec3 camPos;\n// utility maps\n#ifdef USE_ENVIRONMENT_LIGHTING\n UNI sampler2D IBL_BRDF_LUT;\n#endif\n// mesh maps\n#ifdef USE_ALBEDO_TEX\n UNI sampler2D _AlbedoMap;\n#else\n UNI vec4 _Albedo;\n#endif\n#ifdef USE_NORMAL_TEX\n UNI sampler2D _NormalMap;\n#endif\n#ifdef USE_HEIGHT_TEX\n UNI sampler2D _HeightMap;\n#endif\n#ifdef USE_AORM_TEX\n UNI sampler2D _AORMMap;\n#else\n UNI float _Roughness;\n UNI float _Metalness;\n#endif\n\n#ifdef USE_LIGHTMAP\n #ifndef VERTEX_COLORS\n UNI sampler2D _Lightmap;\n #else\n #ifndef VCOL_LIGHTMAP\n UNI sampler2D _Lightmap;\n #endif\n #endif\n#endif\n#ifdef USE_CLEAR_COAT\n UNI float _ClearCoatRoughness;\n#endif\n// IBL inputs\n#ifdef USE_ENVIRONMENT_LIGHTING\n UNI samplerCube _irradiance;\n UNI samplerCube _prefilteredEnvironmentColour;\n UNI float MAX_REFLECTION_LOD;\n UNI float diffuseIntensity;\n UNI float specularIntensity;\n#endif\n#ifdef USE_LIGHTMAP\n UNI float lightmapIntensity;\n#endif\nUNI float tonemappingExposure;\n#ifdef USE_HEIGHT_TEX\n UNI float _HeightDepth;\n #ifndef USE_OPTIMIZED_HEIGHT\n UNI mat4 modelMatrix;\n #endif\n#endif\n#ifdef USE_PARALLAX_CORRECTION\n UNI vec3 _PCOrigin;\n UNI vec3 _PCboxMin;\n UNI vec3 _PCboxMax;\n#endif\n\nIN vec2 texCoord;\n#ifdef USE_LIGHTMAP\n #ifndef ATTRIB_texCoord1\n #ifndef VERTEX_COLORS\n IN vec2 texCoord1;\n #else\n #ifndef VCOL_LIGHTMAP\n IN vec2 texCoord1;\n #endif\n #endif\n #endif\n#endif\nIN vec4 FragPos;\nIN mat3 TBN;\nIN vec3 norm;\nIN vec3 normM;\n#ifdef VERTEX_COLORS\n IN vec4 vertCol;\n#endif\n#ifdef USE_HEIGHT_TEX\n #ifdef USE_OPTIMIZED_HEIGHT\n IN vec3 fragTangentViewDir;\n #else\n IN mat3 invTBN;\n #endif\n#endif\n\n// structs\nstruct Light {\n vec3 color;\n vec3 position;\n vec3 specular;\n\n #define INTENSITY x\n #define ATTENUATION y\n #define FALLOFF z\n #define RADIUS w\n vec4 lightProperties;\n\n int castLight;\n\n vec3 conePointAt;\n #define COSCONEANGLE x\n #define COSCONEANGLEINNER y\n #define SPOTEXPONENT z\n vec3 spotProperties;\n};\n\n\n#ifdef WEBGL1\n #ifdef GL_EXT_shader_texture_lod\n #define textureLod textureCubeLodEXT\n #endif\n#endif\n#define SAMPLETEX textureLod\n\n// https://community.khronos.org/t/addition-of-two-hdr-rgbe-values/55669\nhighp vec4 EncodeRGBE8(highp vec3 rgb)\n{\n highp vec4 vEncoded;\n float maxComponent = max(max(rgb.r, rgb.g), rgb.b);\n float fExp = ceil(log2(maxComponent));\n vEncoded.rgb = rgb / exp2(fExp);\n vEncoded.a = (fExp + 128.0) / 255.0;\n return vEncoded;\n}\n// https://enkimute.github.io/hdrpng.js/\nhighp vec3 DecodeRGBE8(highp vec4 rgbe)\n{\n highp vec3 vDecoded = rgbe.rgb * pow(2.0, rgbe.a * 255.0-128.0);\n return vDecoded;\n}\n\n// from https://github.com/BabylonJS/Babylon.js/blob/master/src/Shaders/ShadersInclude/pbrIBLFunctions.fx\nfloat environmentRadianceOcclusion(float ambientOcclusion, float NdotVUnclamped) {\n // Best balanced (implementation time vs result vs perf) analytical environment specular occlusion found.\n // http://research.tri-ace.com/Data/cedec2011_RealtimePBR_Implementation_e.pptx\n float temp = NdotVUnclamped + ambientOcclusion;\n return clamp(temp * temp - 1.0 + ambientOcclusion, 0.0, 1.0);\n}\nfloat environmentHorizonOcclusion(vec3 view, vec3 normal, vec3 geometricNormal) {\n // http://marmosetco.tumblr.com/post/81245981087\n vec3 reflection = reflect(view, normal);\n float temp = clamp(1.0 + 1.1 * dot(reflection, geometricNormal), 0.0, 1.0);\n return temp * temp;\n}\n#ifdef ALPHA_DITHERED\n// from https://github.com/google/filament/blob/main/shaders/src/dithering.fs\n// modified to use this to discard based on factor instead of dithering\nfloat interleavedGradientNoise(highp vec2 n) {\n return fract(52.982919 * fract(dot(vec2(0.06711, 0.00584), n)));\n}\nfloat Dither_InterleavedGradientNoise(float a) {\n // Jimenez 2014, \"Next Generation Post-Processing in Call of Duty\"\n highp vec2 uv = gl_FragCoord.xy;\n\n // The noise variable must be highp to workaround Adreno bug #1096.\n highp float noise = interleavedGradientNoise(uv);\n\n return step(noise, a);\n}\n#endif\n\n#ifdef USE_HEIGHT_TEX\n#ifndef WEBGL1\n// based on Jasper Flicks great tutorials (:\nfloat getSurfaceHeight(sampler2D surfaceHeightMap, vec2 UV)\n{\n\treturn texture(surfaceHeightMap, UV).r;\n}\n\nvec2 RaymarchedParallax(vec2 UV, sampler2D surfaceHeightMap, float strength, vec3 viewDir)\n{\n #ifndef USE_OPTIMIZED_HEIGHT\n\t#define PARALLAX_RAYMARCHING_STEPS 50\n #else\n #define PARALLAX_RAYMARCHING_STEPS 20\n #endif\n\tvec2 uvOffset = vec2(0.0);\n\tfloat stepSize = 1.0 / float(PARALLAX_RAYMARCHING_STEPS);\n\tvec2 uvDelta = vec2(viewDir * (stepSize * strength));\n\tfloat stepHeight = 1.0;\n\tfloat surfaceHeight = getSurfaceHeight(surfaceHeightMap, UV);\n\n\tvec2 prevUVOffset = uvOffset;\n\tfloat prevStepHeight = stepHeight;\n\tfloat prevSurfaceHeight = surfaceHeight;\n\n // doesnt work with webgl 1.0 as the && condition is not fixed length for loop\n\tfor (int i = 1; i < PARALLAX_RAYMARCHING_STEPS && stepHeight > surfaceHeight; ++i)\n\t{\n\t\tprevUVOffset = uvOffset;\n\t\tprevStepHeight = stepHeight;\n\t\tprevSurfaceHeight = surfaceHeight;\n\n\t\tuvOffset -= uvDelta;\n\t\tstepHeight -= stepSize;\n\t\tsurfaceHeight = getSurfaceHeight(surfaceHeightMap, UV + uvOffset);\n\t}\n\n\tfloat prevDifference = prevStepHeight - prevSurfaceHeight;\n\tfloat difference = surfaceHeight - stepHeight;\n\tfloat t = prevDifference / (prevDifference + difference);\n\tuvOffset = mix(prevUVOffset, uvOffset, t);\n\treturn uvOffset;\n}\n#endif // TODO: use non raymarched parallax mapping here if webgl 1.0?\n#endif\n\n#ifdef USE_PARALLAX_CORRECTION\nvec3 BoxProjection(vec3 direction, vec3 position, vec3 cubemapPosition, vec3 boxMin, vec3 boxMax)\n{\n\tboxMin -= position;\n\tboxMax -= position;\n\tfloat x = (direction.x > 0.0 ? boxMax.x : boxMin.x) / direction.x;\n\tfloat y = (direction.y > 0.0 ? boxMax.y : boxMin.y) / direction.y;\n\tfloat z = (direction.z > 0.0 ? boxMax.z : boxMin.z) / direction.z;\n\tfloat scalar = min(min(x, y), z);\n\n\treturn direction * scalar + (position - cubemapPosition);\n}\n#endif\n\n{{PBR_FRAGMENT_HEAD}}\nvoid main()\n{\n vec4 col;\n\n // set up interpolated vertex data\n vec2 UV0 = texCoord;\n #ifdef USE_LIGHTMAP\n #ifndef VERTEX_COLORS\n vec2 UV1 = texCoord1;\n #else\n #ifndef VCOL_LIGHTMAP\n vec2 UV1 = texCoord1;\n #endif\n #endif\n #endif\n vec3 V = normalize(camPos - FragPos.xyz);\n\n #ifdef USE_HEIGHT_TEX\n #ifndef USE_OPTIMIZED_HEIGHT\n vec3 fragTangentViewDir = normalize(invTBN * (camPos - FragPos.xyz));\n #endif\n #ifndef WEBGL1\n UV0 += RaymarchedParallax(UV0, _HeightMap, _HeightDepth * 0.1, fragTangentViewDir);\n #endif\n #endif\n\n // load relevant mesh maps\n #ifdef USE_ALBEDO_TEX\n vec4 AlbedoMap = texture(_AlbedoMap, UV0);\n #else\n vec4 AlbedoMap = _Albedo;\n #endif\n #ifdef ALPHA_MASKED\n\tif ( AlbedoMap.a <= 0.5 )\n\t discard;\n\t#endif\n\n\t#ifdef ALPHA_DITHERED\n\tif ( Dither_InterleavedGradientNoise(AlbedoMap.a) <= 0.5 )\n\t discard;\n\t#endif\n\n #ifdef USE_AORM_TEX\n vec4 AORM = texture(_AORMMap, UV0);\n #else\n vec4 AORM = vec4(1.0, _Roughness, _Metalness, 1.0);\n #endif\n #ifdef USE_NORMAL_TEX\n vec3 internalNormals = texture(_NormalMap, UV0).rgb;\n internalNormals = internalNormals * 2.0 - 1.0;\n internalNormals = normalize(TBN * internalNormals);\n #else\n vec3 internalNormals = normM;\n #endif\n\t#ifdef USE_LIGHTMAP\n \t#ifndef VERTEX_COLORS\n\t #ifndef LIGHTMAP_IS_RGBE\n vec3 Lightmap = texture(_Lightmap, UV1).rgb;\n #else\n vec3 Lightmap = DecodeRGBE8(texture(_Lightmap, UV1));\n #endif\n #else\n #ifdef VCOL_LIGHTMAP\n vec3 Lightmap = pow(vertCol.rgb, vec3(2.2));\n #else\n \t #ifndef LIGHTMAP_IS_RGBE\n vec3 Lightmap = texture(_Lightmap, UV1).rgb;\n #else\n vec3 Lightmap = DecodeRGBE8(texture(_Lightmap, UV1));\n #endif\n #endif\n #endif\n #endif\n // initialize texture values\n float AO = AORM.r;\n float specK = AORM.g;\n float metalness = AORM.b;\n vec3 N = normalize(internalNormals);\n vec3 albedo = pow(AlbedoMap.rgb, vec3(2.2));\n\n #ifdef VERTEX_COLORS\n #ifdef VCOL_COLOUR\n albedo.rgb *= pow(vertCol.rgb, vec3(2.2));\n AlbedoMap.rgb *= pow(vertCol.rgb, vec3(2.2));\n #endif\n #ifdef VCOL_AORM\n AO = vertCol.r;\n specK = vertCol.g;\n metalness = vertCol.b;\n #endif\n #ifdef VCOL_AO\n AO = vertCol.r;\n #endif\n #ifdef VCOL_R\n specK = vertCol.g;\n #endif\n #ifdef VCOL_M\n metalness = vertCol.b;\n #endif\n #endif\n\n // set up values for later calculations\n float NdotV = abs(dot(N, V));\n vec3 F0 = mix(vec3(0.04), AlbedoMap.rgb, metalness);\n\n #ifndef WEBGL1\n #ifndef DONT_USE_GR\n // from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/ShadersInclude/pbrHelperFunctions.fx\n // modified to fit variable names\n #ifndef DONT_USE_NMGR\n vec3 nDfdx = dFdx(normM.xyz);\n vec3 nDfdy = dFdy(normM.xyz);\n #else\n vec3 nDfdx = dFdx(N.xyz) + dFdx(normM.xyz);\n vec3 nDfdy = dFdy(N.xyz) + dFdy(normM.xyz);\n #endif\n float slopeSquare = max(dot(nDfdx, nDfdx), dot(nDfdy, nDfdy));\n\n // Vive analytical lights roughness factor.\n float geometricRoughnessFactor = pow(clamp(slopeSquare, 0.0, 1.0), 0.333);\n\n specK = max(specK, geometricRoughnessFactor);\n #endif\n #endif\n\n \t// IBL\n \t// from https://github.com/google/filament/blob/df6a100fcba66d9c99328a49d41fe3adecc0165d/shaders/src/light_indirect.fs\n \t// and https://github.com/google/filament/blob/df6a100fcba66d9c99328a49d41fe3adecc0165d/shaders/src/shading_lit.fs\n \t// modified to fit structure/variable names\n \t#ifdef USE_ENVIRONMENT_LIGHTING\n \tvec2 envBRDF = texture(IBL_BRDF_LUT, vec2(NdotV, specK)).xy;\n \tvec3 E = mix(envBRDF.xxx, envBRDF.yyy, F0);\n #endif\n\n float specOcclusion = environmentRadianceOcclusion(AO, NdotV);\n float horizonOcclusion = environmentHorizonOcclusion(-V, N, normM);\n\n #ifdef USE_ENVIRONMENT_LIGHTING\n float envSampleSpecK = specK * MAX_REFLECTION_LOD;\n vec3 R = reflect(-V, N);\n\n #ifdef USE_PARALLAX_CORRECTION\n R = BoxProjection(R, FragPos.xyz, _PCOrigin, _PCboxMin, _PCboxMax);\n #endif\n\n \t vec3 prefilteredEnvColour = DecodeRGBE8(SAMPLETEX(_prefilteredEnvironmentColour, R, envSampleSpecK)) * specularIntensity;\n\n \tvec3 Fr = E * prefilteredEnvColour;\n \tFr *= specOcclusion * horizonOcclusion * (1.0 + F0 * (1.0 / envBRDF.y - 1.0));\n \tFr *= 1.0 + F0; // TODO: this might be wrong, figure this out\n\n \t#ifdef USE_LIGHTMAP\n vec3 IBLIrradiance = Lightmap * lightmapIntensity;\n #else\n vec3 IBLIrradiance = DecodeRGBE8(SAMPLETEX(_irradiance, N, 0.0)) * diffuseIntensity;\n #endif\n\t vec3 Fd = (1.0 - metalness) * albedo * IBLIrradiance * (1.0 - E) * AO;\n #endif\n vec3 directLighting = vec3(0.0);\n\n {{PBR_FRAGMENT_BODY}}\n\n // combine IBL\n col.rgb = directLighting;\n #ifdef USE_ENVIRONMENT_LIGHTING\n col.rgb += Fr + Fd;\n #ifdef USE_CLEAR_COAT\n float CCEnvSampleSpecK = _ClearCoatRoughness * MAX_REFLECTION_LOD;\n vec3 CCR = reflect(-V, normM);\n\n #ifdef USE_PARALLAX_CORRECTION\n CCR = BoxProjection(CCR, FragPos.xyz, _PCOrigin, _PCboxMin, _PCboxMax);\n #endif\n\n \tvec3 CCPrefilteredEnvColour = DecodeRGBE8(SAMPLETEX(_prefilteredEnvironmentColour, CCR, CCEnvSampleSpecK));\n \tvec3 CCFr = E * CCPrefilteredEnvColour;\n \tCCFr *= specOcclusion * horizonOcclusion * (0.96 + (0.04 / envBRDF.y));\n \tCCFr *= 1.04;\n \tcol.rgb += CCFr;\n #endif\n #else\n #ifdef USE_LIGHTMAP\n col.rgb += (1.0 - metalness) * albedo * Lightmap * lightmapIntensity;\n #endif\n #endif\n col.a = 1.0;\n\n #ifdef ALPHA_BLEND\n col.a = AlbedoMap.a;\n #endif\n\n // from https://github.com/BabylonJS/Babylon.js/blob/5e6321d887637877d8b28b417410abbbeb651c6e/src/Shaders/tonemap.fragment.fx\n // modified to fit variable names\n #ifdef TONEMAP_HejiDawson\n col.rgb *= tonemappingExposure;\n\n vec3 X = max(vec3(0.0, 0.0, 0.0), col.rgb - 0.004);\n vec3 retColor = (X * (6.2 * X + 0.5)) / (X * (6.2 * X + 1.7) + 0.06);\n\n col.rgb = retColor * retColor;\n #elif defined(TONEMAP_Photographic)\n col.rgb = vec3(1.0, 1.0, 1.0) - exp2(-tonemappingExposure * col.rgb);\n #else\n col.rgb *= tonemappingExposure;\n //col.rgb = clamp(col.rgb, vec3(0.0), vec3(1.0));\n #endif\n\n col.rgb = pow(col.rgb, vec3(1.0/2.2));\n {{MODULE_COLOR}}\n\n outColor = col;\n}\n","BasicPBR_vert":"precision highp float;\nprecision highp int;\n\nUNI vec3 camPos;\n\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\n#ifdef USE_LIGHTMAP\n #ifndef ATTRIB_attrTexCoord1\n IN vec2 attrTexCoord1;\n OUT vec2 texCoord1;\n #define ATTRIB_attrTexCoord1\n #define ATTRIB_texCoord1\n #endif\n#endif\nIN vec3 attrVertNormal;\nIN vec3 attrTangent;\nIN vec3 attrBiTangent;\nIN float attrVertIndex;\n#ifdef VERTEX_COLORS\nIN vec4 attrVertColor;\n#endif\n\n{{MODULES_HEAD}}\n\nOUT vec2 texCoord;\n\nOUT vec4 FragPos;\nOUT mat3 TBN;\nOUT vec3 norm;\nOUT vec3 normM;\n#ifdef VERTEX_COLORS\nOUT vec4 vertCol;\n#endif\n#ifdef USE_HEIGHT_TEX\n#ifdef USE_OPTIMIZED_HEIGHT\nOUT vec3 fragTangentViewDir;\n#else\nOUT mat3 invTBN;\n#endif\n#endif\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\nvoid main()\n{\n mat4 mMatrix = modelMatrix; // needed to make vertex effects work\n #ifdef USE_LIGHTMAP\n texCoord1 = attrTexCoord1;\n #endif\n texCoord = attrTexCoord;\n texCoord.y = 1.0 - texCoord.y;\n vec4 pos = vec4(vPosition, 1.0);\n norm = attrVertNormal;\n vec3 tangent = attrTangent;\n vec3 bitangent = attrBiTangent;\n\n {{MODULE_VERTEX_POSITION}}\n\n #ifndef INSTANCING\n FragPos = mMatrix * pos;\n #else\n FragPos = instMat * pos;\n #endif\n\n #ifndef INSTANCING\n tangent = normalize(vec3(mMatrix * vec4(tangent, 0.0)));\n vec3 N = normalize(vec3(mMatrix * vec4(norm, 0.0)));\n #else\n tangent = normalize(vec3(instMat * vec4(tangent, 0.0)));\n vec3 N = normalize(vec3(instMat * vec4(norm, 0.0)));\n #endif\n\n #ifndef INSTANCING\n bitangent = normalize(vec3(mMatrix * vec4(bitangent, 0.0)));\n #else\n bitangent = normalize(vec3(instMat * vec4(bitangent, 0.0)));\n #endif\n\n #ifdef VERTEX_COLORS\n vertCol = attrVertColor;\n #endif\n\n TBN = mat3(tangent, bitangent, N);\n\n #ifdef USE_HEIGHT_TEX\n #ifndef WEBGL1\n #ifdef USE_OPTIMIZED_HEIGHT\n fragTangentViewDir = normalize(transpose(TBN) * (camPos - FragPos.xyz));\n #else\n invTBN = transpose(TBN);\n #endif\n #endif\n #endif\n\n normM = N;\n gl_Position = projMatrix * (viewMatrix*mMatrix) * pos;\n}\n","light_body_directional_frag":"\nvec3 L{{LIGHT_INDEX}} = normalize(lightOP{{LIGHT_INDEX}}.position);\n#ifdef USE_ENVIRONMENT_LIGHTING\ndirectLighting += evaluateLighting(lightOP{{LIGHT_INDEX}}, L{{LIGHT_INDEX}}, FragPos, V, N, albedo, specK, NdotV, F0, envBRDF.y, AO, false);\n#else\ndirectLighting += evaluateLighting(lightOP{{LIGHT_INDEX}}, L{{LIGHT_INDEX}}, FragPos, V, N, albedo, specK, NdotV, F0, AO, false);\n#endif\n","light_body_point_frag":"\nvec3 L{{LIGHT_INDEX}} = normalize(lightOP{{LIGHT_INDEX}}.position - FragPos.xyz);\n#ifdef USE_ENVIRONMENT_LIGHTING\ndirectLighting += evaluateLighting(lightOP{{LIGHT_INDEX}}, L{{LIGHT_INDEX}}, FragPos, V, N, albedo, specK, NdotV, F0, envBRDF.y, AO, true);\n#else\ndirectLighting += evaluateLighting(lightOP{{LIGHT_INDEX}}, L{{LIGHT_INDEX}}, FragPos, V, N, albedo, specK, NdotV, F0, AO, true);\n#endif\n","light_body_spot_frag":"\nvec3 L{{LIGHT_INDEX}} = normalize(lightOP{{LIGHT_INDEX}}.position - FragPos.xyz);\nfloat spotIntensity{{LIGHT_INDEX}} = CalculateSpotLightEffect(\n lightOP{{LIGHT_INDEX}}.position, lightOP{{LIGHT_INDEX}}.conePointAt, lightOP{{LIGHT_INDEX}}.spotProperties.COSCONEANGLE,\n lightOP{{LIGHT_INDEX}}.spotProperties.COSCONEANGLEINNER, lightOP{{LIGHT_INDEX}}.spotProperties.SPOTEXPONENT,\n L{{LIGHT_INDEX}}\n);\n#ifdef USE_ENVIRONMENT_LIGHTING\ndirectLighting += evaluateLighting(lightOP{{LIGHT_INDEX}}, L{{LIGHT_INDEX}}, FragPos, V, N, albedo, specK, NdotV, F0, envBRDF.y, AO * spotIntensity{{LIGHT_INDEX}}, true);\n#else\ndirectLighting += evaluateLighting(lightOP{{LIGHT_INDEX}}, L{{LIGHT_INDEX}}, FragPos, V, N, albedo, specK, NdotV, F0, AO * spotIntensity{{LIGHT_INDEX}}, true);\n#endif\n","light_head_frag":"UNI Light lightOP{{LIGHT_INDEX}};\n","light_includes_frag":"\n#define PI 3.14159265359\n\n// from https://github.com/google/filament/blob/036bfa9b20d730bb8e5852ed449b024570167648/shaders/src/brdf.fs\n// modified to fit variable names / structure\nfloat F_Schlick(float f0, float f90, float VoH)\n{\n return f0 + (f90 - f0) * pow(1.0 - VoH, 5.0);\n}\nvec3 F_Schlick(const vec3 f0, float VoH)\n{\n float f = pow(1.0 - VoH, 5.0);\n return f + f0 * (1.0 - f);\n}\nfloat Fd_Burley(float roughness, float NoV, float NoL, float LoH)\n{\n // Burley 2012, \"Physically-Based Shading at Disney\"\n float f90 = 0.5 + 2.0 * roughness * LoH * LoH;\n float lightScatter = F_Schlick(1.0, f90, NoL);\n float viewScatter = F_Schlick(1.0, f90, NoV);\n return lightScatter * viewScatter * (1.0 / PI);\n}\nfloat D_GGX(float roughness, float NoH, const vec3 h)\n{\n float oneMinusNoHSquared = 1.0 - NoH * NoH;\n\n float a = NoH * roughness;\n float k = roughness / (oneMinusNoHSquared + a * a);\n float d = k * k * (1.0 / PI);\n return clamp(d, 0.0, 1.0);\n}\nfloat V_SmithGGXCorrelated(float roughness, float NoV, float NoL)\n{\n // Heitz 2014, \"Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs\"\n float a2 = roughness * roughness;\n // TODO: lambdaV can be pre-computed for all the lights, it should be moved out of this function\n float lambdaV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);\n float lambdaL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);\n float v = 0.5 / (lambdaV + lambdaL);\n // a2=0 => v = 1 / 4*NoL*NoV => min=1/4, max=+inf\n // a2=1 => v = 1 / 2*(NoL+NoV) => min=1/4, max=+inf\n // clamp to the maximum value representable in mediump\n return clamp(v, 0.0, 1.0);\n}\n// from https://github.com/google/filament/blob/73e339b05d67749e3b1d1d243650441162c10f8a/shaders/src/light_punctual.fs\n// modified to fit variable names\nfloat getSquareFalloffAttenuation(float distanceSquare, float falloff)\n{\n float factor = distanceSquare * falloff;\n float smoothFactor = clamp(1.0 - factor * factor, 0.0, 1.0);\n // We would normally divide by the square distance here\n // but we do it at the call site\n return smoothFactor * smoothFactor;\n}\n\nfloat getDistanceAttenuation(vec3 posToLight, float falloff, vec3 V, float volume)\n{\n float distanceSquare = dot(posToLight, posToLight);\n float attenuation = getSquareFalloffAttenuation(distanceSquare, falloff);\n // light far attenuation\n float d = dot(V, V);\n float f = 100.0; // CONFIG_Z_LIGHT_FAR, ttps://github.com/google/filament/blob/df6a100fcba66d9c99328a49d41fe3adecc0165d/filament/src/details/Engine.h\n vec2 lightFarAttenuationParams = 0.5 * vec2(10.0, 10.0 / (f * f));\n attenuation *= clamp(lightFarAttenuationParams.x - d * lightFarAttenuationParams.y, 0.0, 1.0);\n // Assume a punctual light occupies a min volume of 1cm to avoid a division by 0\n return attenuation / max(distanceSquare, max(1e-4, volume));\n}\n\n#ifdef USE_CLEAR_COAT\n// from https://github.com/google/filament/blob/73e339b05d67749e3b1d1d243650441162c10f8a/shaders/src/shading_model_standard.fs\n// modified to fit variable names / structure\nfloat clearCoatLobe(vec3 shading_clearCoatNormal, vec3 h, float LoH, float CCSpecK)\n{\n float clearCoatNoH = clamp(dot(shading_clearCoatNormal, h), 0.0, 1.0);\n\n // clear coat specular lobe\n float D = D_GGX(CCSpecK, clearCoatNoH, h);\n // from https://github.com/google/filament/blob/036bfa9b20d730bb8e5852ed449b024570167648/shaders/src/brdf.fs\n float V = clamp(0.25 / (LoH * LoH), 0.0, 1.0);\n float F = F_Schlick(0.04, 1.0, LoH); // fix IOR to 1.5\n\n return D * V * F;\n}\n#endif\n\n#ifdef USE_ENVIRONMENT_LIGHTING\nvec3 evaluateLighting(Light light, vec3 L, vec4 FragPos, vec3 V, vec3 N, vec3 albedo, float specK, float NdotV, vec3 F0, float envBRDFY, float AO, bool hasFalloff)\n#else\nvec3 evaluateLighting(Light light, vec3 L, vec4 FragPos, vec3 V, vec3 N, vec3 albedo, float specK, float NdotV, vec3 F0, float AO, bool hasFalloff)\n#endif\n{\n vec3 directLightingResult = vec3(0.0);\n if (light.castLight == 1)\n {\n specK = max(0.08, specK);\n // from https://github.com/google/filament/blob/73e339b05d67749e3b1d1d243650441162c10f8a/shaders/src/shading_model_standard.fs\n // modified to fit variable names / structure\n vec3 H = normalize(V + L);\n\n float NdotL = clamp(dot(N, L), 0.0, 1.0);\n float NdotH = clamp(dot(N, H), 0.0, 1.0);\n float LdotH = clamp(dot(L, H), 0.0, 1.0);\n\n vec3 Fd = albedo * Fd_Burley(specK, NdotV, NdotL, LdotH);\n\n float D = D_GGX(specK, NdotH, H);\n float V2 = V_SmithGGXCorrelated(specK, NdotV, NdotL);\n vec3 F = F_Schlick(F0, LdotH);\n\n // TODO: modify this with the radius\n vec3 Fr = (D * V2) * F;\n\n #ifdef USE_ENVIRONMENT_LIGHTING\n vec3 directLighting = Fd + Fr * (1.0 + F0 * (1.0 / envBRDFY - 1.0));\n #else\n vec3 directLighting = Fd + Fr;\n #endif\n\n float attenuation = getDistanceAttenuation(L, hasFalloff ? light.lightProperties.FALLOFF : 0.0, V, light.lightProperties.RADIUS);\n\n directLightingResult = (directLighting * light.color) *\n (light.lightProperties.INTENSITY * attenuation * NdotL * AO);\n\n #ifdef USE_CLEAR_COAT\n directLightingResult += clearCoatLobe(normM, H, LdotH, _ClearCoatRoughness);\n #endif\n }\n return directLightingResult;\n}\n\n// from phong OP to make sure the light parameters change lighting similar to what people are used to\nfloat CalculateSpotLightEffect(vec3 lightPosition, vec3 conePointAt, float cosConeAngle, float cosConeAngleInner, float spotExponent, vec3 lightDirection) {\n vec3 spotLightDirection = normalize(lightPosition-conePointAt);\n float spotAngle = dot(-lightDirection, spotLightDirection);\n float epsilon = cosConeAngle - cosConeAngleInner;\n\n float spotIntensity = clamp((spotAngle - cosConeAngle)/epsilon, 0.0, 1.0);\n spotIntensity = pow(spotIntensity, max(0.01, spotExponent));\n\n return max(0., spotIntensity);\n}\n",}; +// utility +const cgl = op.patch.cgl; +// inputs +const inTrigger = op.inTrigger("render"); + +const inDiffuseR = op.inFloat("R", Math.random()); +const inDiffuseG = op.inFloat("G", Math.random()); +const inDiffuseB = op.inFloat("B", Math.random()); +const inDiffuseA = op.inFloatSlider("A", 1); +const diffuseColors = [inDiffuseR, inDiffuseG, inDiffuseB, inDiffuseA]; +op.setPortGroup("Diffuse Color", diffuseColors); + +const inRoughness = op.inFloatSlider("Roughness", 0.5); +const inMetalness = op.inFloatSlider("Metalness", 0.0); +const inAlphaMode = op.inSwitch("Alpha Mode", ["Opaque", "Masked", "Dithered", "Blend"], "Blend"); + +const inTonemapping = op.inSwitch("Tonemapping", ["sRGB", "HejiDawson", "Photographic"], "sRGB"); +const inTonemappingExposure = op.inFloat("Exposure", 1.0); + +const inToggleGR = op.inBool("Disable geometric roughness", false); +const inToggleNMGR = op.inBool("Use roughness from normal map", false); +const inUseVertexColours = op.inValueBool("Use Vertex Colours", false); +const inVertexColourMode = op.inSwitch("Vertex Colour Mode", ["colour", "AORM", "AO", "R", "M", "lightmap"], "colour"); +const inHeightDepth = op.inFloat("Height Intensity", 1.0); +const inUseOptimizedHeight = op.inValueBool("Faster heightmapping", false); +const inUseClearCoat = op.inValueBool("Use Clear Coat", false); +const inClearCoatRoughness = op.inFloatSlider("Clear Coat Roughness", 0.5); + +// texture inputs +const inTexIBLLUT = op.inTexture("IBL LUT"); +const inTexIrradiance = op.inTexture("Diffuse Irradiance"); +const inTexPrefiltered = op.inTexture("Pre-filtered envmap"); +const inMipLevels = op.inInt("Num mip levels"); + +const inTexAlbedo = op.inTexture("Albedo"); +const inTexAORM = op.inTexture("AORM"); +const inTexNormal = op.inTexture("Normal map"); +const inTexHeight = op.inTexture("Height"); +const inLightmap = op.inTexture("Lightmap"); +const inDiffuseIntensity = op.inFloat("Diffuse Intensity", 1.0); +const inSpecularIntensity = op.inFloat("Specular Intensity", 1.0); +const inLightmapRGBE = op.inBool("Lightmap is RGBE", false); +const inLightmapIntensity = op.inFloat("Lightmap Intensity", 1.0); + +inTrigger.onTriggered = doRender; + +// outputs +const outTrigger = op.outTrigger("Next"); +const shaderOut = op.outObject("Shader"); +shaderOut.ignoreValueSerialize = true; +// UI stuff +op.toWorkPortsNeedToBeLinked(inTrigger); + +inDiffuseR.setUiAttribs({ "colorPick": true }); +op.setPortGroup("Shader Parameters", [inRoughness, inMetalness, inAlphaMode]); +op.setPortGroup("Advanced Shader Parameters", [inToggleGR, inToggleNMGR, inUseVertexColours, inVertexColourMode, inHeightDepth, inUseOptimizedHeight, inUseClearCoat, inClearCoatRoughness]); +op.setPortGroup("Textures", [inTexAlbedo, inTexAORM, inTexNormal, inTexHeight, inLightmap]); +op.setPortGroup("Lighting", [inDiffuseIntensity, inSpecularIntensity, inLightmapIntensity, inLightmapRGBE, inTexIBLLUT, inTexIrradiance, inTexPrefiltered, inMipLevels]); +op.setPortGroup("Tonemapping", [inTonemapping, inTonemappingExposure]); +// globals +const PBRShader = new CGL.Shader(cgl, "PBRShader"); +PBRShader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +// light sources (except IBL) +let PBRLightStack = []; +const lightUniforms = []; +const LIGHT_INDEX_REGEX = new RegExp("{{LIGHT_INDEX}}", "g"); +const FRAGMENT_HEAD_REGEX = new RegExp("{{PBR_FRAGMENT_HEAD}}", "g"); +const FRAGMENT_BODY_REGEX = new RegExp("{{PBR_FRAGMENT_BODY}}", "g"); +const lightFragmentHead = attachments.light_head_frag; +const lightFragmentBodies = { + "point": attachments.light_body_point_frag, + "directional": attachments.light_body_directional_frag, + "spot": attachments.light_body_spot_frag, +}; +const createLightFragmentHead = (n) => { return lightFragmentHead.replace("{{LIGHT_INDEX}}", n); }; +const createLightFragmentBody = (n, type) => +{ return (lightFragmentBodies[type] || "").replace(LIGHT_INDEX_REGEX, n); }; +let currentLightCount = -1; +const defaultLightStack = [{ + "type": "point", + "position": [5, 5, 5], + "color": [1, 1, 1], + "specular": [1, 1, 1], + "intensity": 120, + "attenuation": 0, + "falloff": 0.5, + "radius": 60, + "castLight": 1, +}]; + +if (cgl.glVersion == 1) +{ + if (!cgl.gl.getExtension("EXT_shader_texture_lod")) + { + op.log("no EXT_shader_texture_lod texture extension"); + throw "no EXT_shader_texture_lod texture extension"; + } + else + { + PBRShader.enableExtension("GL_EXT_shader_texture_lod"); + cgl.gl.getExtension("OES_texture_float"); + cgl.gl.getExtension("OES_texture_float_linear"); + cgl.gl.getExtension("OES_texture_half_float"); + cgl.gl.getExtension("OES_texture_half_float_linear"); + + PBRShader.enableExtension("GL_OES_standard_derivatives"); + PBRShader.enableExtension("GL_OES_texture_float"); + PBRShader.enableExtension("GL_OES_texture_float_linear"); + PBRShader.enableExtension("GL_OES_texture_half_float"); + PBRShader.enableExtension("GL_OES_texture_half_float_linear"); + } +} + +buildShader(); +// uniforms +const inAlbedoUniform = new CGL.Uniform(PBRShader, "t", "_AlbedoMap", 0); +const inAORMUniform = new CGL.Uniform(PBRShader, "t", "_AORMMap", 0); +const inNormalUniform = new CGL.Uniform(PBRShader, "t", "_NormalMap", 0); +const inIBLLUTUniform = new CGL.Uniform(PBRShader, "t", "IBL_BRDF_LUT", 0); +const inIrradianceUniform = new CGL.Uniform(PBRShader, "tc", "_irradiance", 1); +const inPrefilteredUniform = new CGL.Uniform(PBRShader, "tc", "_prefilteredEnvironmentColour", 1); +const inMipLevelsUniform = new CGL.Uniform(PBRShader, "f", "MAX_REFLECTION_LOD", 0); + +const inTonemappingExposureUniform = new CGL.Uniform(PBRShader, "f", "tonemappingExposure", inTonemappingExposure); +const inDiffuseIntensityUniform = new CGL.Uniform(PBRShader, "f", "diffuseIntensity", inDiffuseIntensity); +const inSpecularIntensityUniform = new CGL.Uniform(PBRShader, "f", "specularIntensity", inSpecularIntensity); + +const inHeightUniform = new CGL.Uniform(PBRShader, "t", "_HeightMap", 0); +const inLightmapUniform = new CGL.Uniform(PBRShader, "t", "_Lightmap", 0); +const inLightmapIntensityUniform = new CGL.Uniform(PBRShader, "f", "lightmapIntensity", inLightmapIntensity); + +const inDiffuseColor = new CGL.Uniform(PBRShader, "4f", "_Albedo", inDiffuseR, inDiffuseG, inDiffuseB, inDiffuseA); +const inRoughnessUniform = new CGL.Uniform(PBRShader, "f", "_Roughness", inRoughness); +const inMetalnessUniform = new CGL.Uniform(PBRShader, "f", "_Metalness", inMetalness); +const inHeightDepthUniform = new CGL.Uniform(PBRShader, "f", "_HeightDepth", inHeightDepth); +const inClearCoatRoughnessUniform = new CGL.Uniform(PBRShader, "f", "_ClearCoatRoughness", inClearCoatRoughness); + +const inPCOrigin = new CGL.Uniform(PBRShader, "3f", "_PCOrigin", [0, 0, 0]); +const inPCboxMin = new CGL.Uniform(PBRShader, "3f", "_PCboxMin", [-1, -1, -1]); +const inPCboxMax = new CGL.Uniform(PBRShader, "3f", "_PCboxMax", [1, 1, 1]); + +PBRShader.uniformColorDiffuse = inDiffuseColor; +PBRShader.uniformPbrMetalness = inMetalnessUniform; +PBRShader.uniformPbrRoughness = inRoughnessUniform; + +inTexPrefiltered.onChange = updateIBLTexDefines; + +inTexAORM.onChange = + inLightmapRGBE.onChange = + inUseClearCoat.onChange = + inTexAlbedo.onChange = + inTexNormal.onChange = + inTexHeight.onChange = + inAlphaMode.onChange = + inToggleNMGR.onChange = + inTonemapping.onChange = + inLightmap.onChange = + inUseOptimizedHeight.onChange = + inUseVertexColours.onChange = + inToggleGR.onChange = + inVertexColourMode.onChange = updateDefines; + +function updateDefines() +{ + PBRShader.toggleDefine("USE_OPTIMIZED_HEIGHT", inUseOptimizedHeight.get()); + PBRShader.toggleDefine("USE_CLEAR_COAT", inUseClearCoat.get()); + PBRShader.toggleDefine("LIGHTMAP_IS_RGBE", inLightmapRGBE.get()); + PBRShader.toggleDefine("USE_LIGHTMAP", inLightmap.isLinked() || inVertexColourMode.get() === "lightmap"); + PBRShader.toggleDefine("USE_NORMAL_TEX", inTexNormal.isLinked()); + PBRShader.toggleDefine("USE_HEIGHT_TEX", inTexHeight.isLinked()); + PBRShader.toggleDefine("DONT_USE_NMGR", inToggleNMGR.get()); + PBRShader.toggleDefine("DONT_USE_GR", inToggleGR.get()); + + // VERTEX_COLORS + PBRShader.toggleDefine("VCOL_COLOUR", inVertexColourMode.get() === "colour"); + PBRShader.toggleDefine("VCOL_AORM", inVertexColourMode.get() === "AORM"); + PBRShader.toggleDefine("VCOL_AO", inVertexColourMode.get() === "AO"); + PBRShader.toggleDefine("VCOL_R", inVertexColourMode.get() === "R"); + PBRShader.toggleDefine("VCOL_M", inVertexColourMode.get() === "M"); + PBRShader.toggleDefine("VCOL_LIGHTMAP", inVertexColourMode.get() === "lightmap"); + + // ALBEDO TEX + PBRShader.toggleDefine("USE_ALBEDO_TEX", inTexAlbedo.get()); + inDiffuseR.setUiAttribs({ "greyout": inTexAlbedo.isLinked() }); + inDiffuseG.setUiAttribs({ "greyout": inTexAlbedo.isLinked() }); + inDiffuseB.setUiAttribs({ "greyout": inTexAlbedo.isLinked() }); + inDiffuseA.setUiAttribs({ "greyout": inTexAlbedo.isLinked() }); + + // AORM + PBRShader.toggleDefine("USE_AORM_TEX", inTexAORM.get()); + inRoughness.setUiAttribs({ "greyout": inTexAORM.isLinked() }); + inMetalness.setUiAttribs({ "greyout": inTexAORM.isLinked() }); + + // lightmaps + PBRShader.toggleDefine("VERTEX_COLORS", inUseVertexColours.get()); + + if (!inUseVertexColours.get()) + { + PBRShader.toggleDefine("USE_LIGHTMAP", inLightmap.get()); + } + else + { + if (inVertexColourMode.get() === "lightmap") + { + PBRShader.define("USE_LIGHTMAP"); + } + } + + // alpha mode + PBRShader.toggleDefine("ALPHA_MASKED", inAlphaMode.get() === "Masked"); + PBRShader.toggleDefine("ALPHA_DITHERED", inAlphaMode.get() === "Dithered"); + PBRShader.toggleDefine("ALPHA_BLEND", inAlphaMode.get() === "Blend"); + + // tonemapping + PBRShader.toggleDefine("TONEMAP_sRGB", inTonemapping.get() === "sRGB"); + PBRShader.toggleDefine("TONEMAP_HejiDawson", inTonemapping.get() === "HejiDawson"); + PBRShader.toggleDefine("TONEMAP_Photographic", inTonemapping.get() === "Photographic"); +} + +updateDefines(); + +function setEnvironmentLighting(enabled) +{ + PBRShader.toggleDefine("USE_ENVIRONMENT_LIGHTING", enabled); +} + +op.preRender = function () +{ + PBRShader.bind(); + doRender(); +}; + +function updateIBLTexDefines() +{ + inMipLevels.setUiAttribs({ "greyout": !inTexPrefiltered.get() }); +} + +function updateLightUniforms() +{ + for (let i = 0; i < PBRLightStack.length; i += 1) + { + const light = PBRLightStack[i]; + light.isUsed = true; + + lightUniforms[i].position.setValue(light.position); + lightUniforms[i].color.setValue(light.color); + lightUniforms[i].specular.setValue(light.specular); + + lightUniforms[i].lightProperties.setValue([ + light.intensity, + light.attenuation, + light.falloff, + light.radius, + ]); + + lightUniforms[i].conePointAt.setValue(light.conePointAt); + lightUniforms[i].spotProperties.setValue([ + light.cosConeAngle, + light.cosConeAngleInner, + light.spotExponent, + ]); + + lightUniforms[i].castLight.setValue(light.castLight); + } +} + +function buildShader() +{ + const vertexShader = attachments.BasicPBR_vert; + const lightIncludes = attachments.light_includes_frag; + let fragmentShader = attachments.BasicPBR_frag; + + let fragmentHead = ""; + let fragmentBody = ""; + + if (PBRLightStack.length > 0) + { + fragmentHead = fragmentHead.concat(lightIncludes); + } + + for (let i = 0; i < PBRLightStack.length; i += 1) + { + const light = PBRLightStack[i]; + const type = light.type; + + fragmentHead = fragmentHead.concat(createLightFragmentHead(i) || ""); + fragmentBody = fragmentBody.concat(createLightFragmentBody(i, light.type) || ""); + } + + fragmentShader = fragmentShader.replace(FRAGMENT_HEAD_REGEX, fragmentHead || ""); + fragmentShader = fragmentShader.replace(FRAGMENT_BODY_REGEX, fragmentBody || ""); + + PBRShader.setSource(vertexShader, fragmentShader); + shaderOut.set(PBRShader); + + for (let i = 0; i < PBRLightStack.length; i += 1) + { + lightUniforms[i] = null; + if (!lightUniforms[i]) + { + lightUniforms[i] = { + "color": new CGL.Uniform(PBRShader, "3f", "lightOP" + i + ".color", [1, 1, 1]), + "position": new CGL.Uniform(PBRShader, "3f", "lightOP" + i + ".position", [0, 11, 0]), + "specular": new CGL.Uniform(PBRShader, "3f", "lightOP" + i + ".specular", [1, 1, 1]), + "lightProperties": new CGL.Uniform(PBRShader, "4f", "lightOP" + i + ".lightProperties", [1, 1, 1, 1]), + + "conePointAt": new CGL.Uniform(PBRShader, "3f", "lightOP" + i + ".conePointAt", vec3.create()), + "spotProperties": new CGL.Uniform(PBRShader, "3f", "lightOP" + i + ".spotProperties", [0, 0, 0, 0]), + "castLight": new CGL.Uniform(PBRShader, "i", "lightOP" + i + ".castLight", 1), + + }; + } + } +} + +function updateLights() +{ + if (cgl.frameStore.lightStack) + { + let changed = currentLightCount !== cgl.frameStore.lightStack.length; + + if (!changed) + { + for (let i = 0; i < cgl.frameStore.lightStack.length; i++) + { + if (PBRLightStack[i] != cgl.frameStore.lightStack[i]) + { + changed = true; + break; + } + } + } + + if (changed) + { + PBRLightStack = cgl.frameStore.lightStack; + buildShader(); + + currentLightCount = cgl.frameStore.lightStack.length; + } + } +} + +function doRender() +{ + cgl.pushShader(PBRShader); + let useDefaultLight = false; + + PBRShader.popTextures(); + + let numLights = 0; + if (cgl.frameStore.lightStack)numLights = cgl.frameStore.lightStack.length; + + if ((!cgl.frameStore.pbrEnvStack || cgl.frameStore.pbrEnvStack.length == 0) && + !inLightmap.isLinked() && numLights == 0) + + { + useDefaultLight = true; + op.setUiError("deflight", "Default light is enabled. Please add lights or PBREnvironmentLights to your patch to make this warning disappear.", 1); + } + else op.setUiError("deflight", null); + + if (cgl.frameStore.pbrEnvStack && cgl.frameStore.pbrEnvStack.length > 0 && + cgl.frameStore.pbrEnvStack[cgl.frameStore.pbrEnvStack.length - 1].texIBLLUT.tex && cgl.frameStore.pbrEnvStack[cgl.frameStore.pbrEnvStack.length - 1].texDiffIrr.tex && cgl.frameStore.pbrEnvStack[cgl.frameStore.pbrEnvStack.length - 1].texPreFiltered.tex) + { + const pbrEnv = cgl.frameStore.pbrEnvStack[cgl.frameStore.pbrEnvStack.length - 1]; + + PBRShader.pushTexture(inIBLLUTUniform, pbrEnv.texIBLLUT.tex); + PBRShader.pushTexture(inIrradianceUniform, pbrEnv.texDiffIrr.tex, cgl.gl.TEXTURE_CUBE_MAP); + PBRShader.pushTexture(inPrefilteredUniform, pbrEnv.texPreFiltered.tex, cgl.gl.TEXTURE_CUBE_MAP); + inMipLevelsUniform.setValue(pbrEnv.texPreFilteredMipLevels || 7); + + PBRShader.toggleDefine("USE_PARALLAX_CORRECTION", pbrEnv.UseParallaxCorrection); + if (pbrEnv.UseParallaxCorrection) + { + inPCOrigin.setValue(pbrEnv.PCOrigin); + inPCboxMin.setValue(pbrEnv.PCboxMin); + inPCboxMax.setValue(pbrEnv.PCboxMax); + } + + setEnvironmentLighting(true); + } + else + { + setEnvironmentLighting(false); + } + + if (useDefaultLight) + { + const iViewMatrix = mat4.create(); + mat4.invert(iViewMatrix, cgl.vMatrix); + + defaultLightStack[0].position = [iViewMatrix[12], iViewMatrix[13], iViewMatrix[14]]; + cgl.frameStore.lightStack = defaultLightStack; + } + + if (inTexIBLLUT.get()) + { + setEnvironmentLighting(true); + PBRShader.pushTexture(inIBLLUTUniform, inTexIBLLUT.get().tex); + inMipLevelsUniform.setValue(inMipLevels.get()); + if (inTexIrradiance.get()) PBRShader.pushTexture(inIrradianceUniform, inTexIrradiance.get().cubemap, cgl.gl.TEXTURE_CUBE_MAP); + if (inTexPrefiltered.get()) PBRShader.pushTexture(inPrefilteredUniform, inTexPrefiltered.get().cubemap, cgl.gl.TEXTURE_CUBE_MAP); + } + + if (inTexAlbedo.get()) PBRShader.pushTexture(inAlbedoUniform, inTexAlbedo.get().tex); + if (inTexAORM.get()) PBRShader.pushTexture(inAORMUniform, inTexAORM.get().tex); + if (inTexNormal.get()) PBRShader.pushTexture(inNormalUniform, inTexNormal.get().tex); + if (inTexHeight.get()) PBRShader.pushTexture(inHeightUniform, inTexHeight.get().tex); + if (inLightmap.get()) PBRShader.pushTexture(inLightmapUniform, inLightmap.get().tex); + + updateLights(); + updateLightUniforms(); + + outTrigger.trigger(); + cgl.popShader(); + + if (useDefaultLight) cgl.frameStore.lightStack = []; +} + + +}; + +Ops.Gl.Pbr.PbrMaterial.prototype = new CABLES.Op(); +CABLES.OPS["a5234947-f65a-41e2-a691-b81382903a71"]={f:Ops.Gl.Pbr.PbrMaterial,objName:"Ops.Gl.Pbr.PbrMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.Performance +// +// ************************************************************** + +Ops.Gl.Performance = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + inShow = op.inValueBool("Visible", true), + next = op.outTrigger("childs"), + position = op.inSwitch("Position", ["top", "bottom"], "top"), + openDefault = op.inBool("Open", false), + smoothGraph = op.inBool("Smooth Graph", true), + inScaleGraph = op.inFloat("Scale", 4), + inSizeGraph = op.inFloat("Size", 128), + outCanv = op.outObject("Canvas"), + outFPS = op.outNumber("FPS"); + +const cgl = op.patch.cgl; +const element = document.createElement("div"); + +let elementMeasures = null; +let ctx = null; +let opened = false; +let frameCount = 0; +let fps = 0; +let fpsStartTime = 0; +let childsTime = 0; +let avgMsChilds = 0; +const queue = []; +const timesMainloop = []; +const timesOnFrame = []; +const timesGPU = []; +let avgMs = 0; +let selfTime = 0; +let canvas = null; +let lastTime = 0; +let loadingCounter = 0; +const loadingChars = ["|", "/", "-", "\\"]; +let initMeasures = true; + +const colorRAFSlow = "#ffffff"; +const colorBg = "#222222"; +const colorRAF = "#003f5c"; // color: https://learnui.design/tools/data-color-picker.html +const colorMainloop = "#7a5195"; +const colorOnFrame = "#ef5675"; +const colorGPU = "#ffa600"; + +let startedQuery = false; + +let currentTimeGPU = 0; +let currentTimeMainloop = 0; +let currentTimeOnFrame = 0; + +const gl = op.patch.cgl.gl; +const glQueryExt = gl.getExtension("EXT_disjoint_timer_query_webgl2"); +// let query = null; + +exe.onLinkChanged = + inShow.onChange = () => + { + updateOpened(); + updateVisibility(); + }; + +position.onChange = updatePos; +inSizeGraph.onChange = updateSize; + +element.id = "performance"; +element.style.position = "absolute"; +element.style.left = "0px"; +element.style.opacity = "0.8"; +element.style.padding = "10px"; +element.style.cursor = "pointer"; +element.style.background = "#222"; +element.style.color = "white"; +element.style["font-family"] = "monospace"; +element.style["font-size"] = "12px"; +element.style["z-index"] = "99999"; + +element.innerHTML = " "; +element.addEventListener("click", toggleOpened); + +const container = op.patch.cgl.canvas.parentElement; +container.appendChild(element); + +updateSize(); +updateOpened(); +updatePos(); +updateVisibility(); + +op.onDelete = function () +{ + if (canvas)canvas.remove(); + if (element)element.remove(); +}; + +function updatePos() +{ + canvas.style["pointer-events"] = "none"; + if (position.get() == "top") + { + canvas.style.top = element.style.top = "0px"; + canvas.style.bottom = element.style.bottom = "initial"; + } + else + { + canvas.style.bottom = element.style.bottom = "0px"; + canvas.style.top = element.style.top = "initial"; + } +} + +function updateVisibility() +{ + if (!inShow.get() || !exe.isLinked()) + { + element.style.display = "none"; + element.style.opacity = 0; + canvas.style.display = "none"; + } + else + { + element.style.display = "block"; + element.style.opacity = 1; + canvas.style.display = "block"; + } +} + +function updateSize() +{ + if (!canvas) return; + + const num = Math.max(0, parseInt(inSizeGraph.get())); + + canvas.width = num; + canvas.height = num; + element.style.left = num + "px"; + + queue.length = 0; + timesMainloop.length = 0; + timesOnFrame.length = 0; + timesGPU.length = 0; + + for (let i = 0; i < num; i++) + { + queue[i] = -1; + timesMainloop[i] = -1; + timesOnFrame[i] = -1; + timesGPU[i] = -1; + } +} + +openDefault.onChange = function () +{ + opened = openDefault.get(); + updateOpened(); +}; + +function toggleOpened() +{ + if (!inShow.get()) return; + element.style.opacity = 1; + opened = !opened; + updateOpened(); +} + +function updateOpened() +{ + updateText(); + if (!canvas)createCanvas(); + if (opened) + { + canvas.style.display = "block"; + element.style.left = inSizeGraph.get() + "px"; + element.style["min-height"] = "56px"; + } + else + { + canvas.style.display = "none"; + element.style.left = "0px"; + element.style["min-height"] = "auto"; + } +} + +function updateCanvas() +{ + const height = canvas.height; + const hmul = inScaleGraph.get(); + + ctx.fillStyle = colorBg; + ctx.fillRect(0, 0, canvas.width, height); + ctx.fillStyle = colorRAF; + + let k = 0; + const numBars = Math.max(0, parseInt(inSizeGraph.get())); + + for (k = numBars; k >= 0; k--) + { + if (queue[k] > 30)ctx.fillStyle = colorRAFSlow; + ctx.fillRect(numBars - k, height - queue[k] * hmul, 1, queue[k] * hmul); + if (queue[k] > 30)ctx.fillStyle = colorRAF; + } + + for (k = numBars; k >= 0; k--) + { + let sum = 0; + ctx.fillStyle = colorMainloop; + sum = timesMainloop[k]; + ctx.fillRect(numBars - k, height - sum * hmul, 1, timesMainloop[k] * hmul); + + ctx.fillStyle = colorOnFrame; + sum += timesOnFrame[k]; + ctx.fillRect(numBars - k, height - sum * hmul, 1, timesOnFrame[k] * hmul); + + ctx.fillStyle = colorGPU; + sum += timesGPU[k]; + ctx.fillRect(numBars - k, height - sum * hmul, 1, timesGPU[k] * hmul); + } +} + +function createCanvas() +{ + canvas = document.createElement("canvas"); + canvas.id = "performance_" + op.patch.config.glCanvasId; + canvas.width = inSizeGraph.get(); + canvas.height = inSizeGraph.get(); + canvas.style.display = "block"; + canvas.style.opacity = 0.9; + canvas.style.position = "absolute"; + canvas.style.left = "0px"; + canvas.style.cursor = "pointer"; + canvas.style.top = "-64px"; + canvas.style["z-index"] = "99998"; + container.appendChild(canvas); + ctx = canvas.getContext("2d"); + + canvas.addEventListener("click", toggleOpened); + + updateSize(); +} + +function updateText() +{ + if (!inShow.get()) return; + let warn = ""; + + if (op.patch.cgl.profileData.profileShaderCompiles > 0)warn += "Shader compile (" + op.patch.cgl.profileData.profileShaderCompileName + ") "; + if (op.patch.cgl.profileData.profileShaderGetUniform > 0)warn += "Shader get uni loc! (" + op.patch.cgl.profileData.profileShaderGetUniformName + ")"; + if (op.patch.cgl.profileData.profileTextureResize > 0)warn += "Texture resize! "; + if (op.patch.cgl.profileData.profileFrameBuffercreate > 0)warn += "Framebuffer create! "; + if (op.patch.cgl.profileData.profileEffectBuffercreate > 0)warn += "Effectbuffer create! "; + if (op.patch.cgl.profileData.profileTextureDelete > 0)warn += "Texture delete! "; + if (op.patch.cgl.profileData.profileNonTypedAttrib > 0)warn += "Not-Typed Buffer Attrib! " + op.patch.cgl.profileData.profileNonTypedAttribNames; + if (op.patch.cgl.profileData.profileTextureNew > 0)warn += "new texture created! "; + if (op.patch.cgl.profileData.profileGenMipMap > 0)warn += "generating mip maps!"; + + if (warn.length > 0) + { + warn = "| WARNING: " + warn + ""; + } + + let html = ""; + + if (opened) + { + html += " " + fps + " fps "; + html += " " + Math.round(currentTimeMainloop * 100) / 100 + "ms mainloop "; + html += " " + Math.round((currentTimeOnFrame) * 100) / 100 + "ms onframe "; + if (currentTimeGPU) html += " " + Math.round(currentTimeGPU * 100) / 100 + "ms GPU"; + html += warn; + element.innerHTML = html; + } + else + { + html += fps + " fps / "; + html += "CPU: " + Math.round((currentTimeMainloop + op.patch.cgl.profileData.profileOnAnimFrameOps) * 100) / 100 + "ms / "; + if (currentTimeGPU)html += "GPU: " + Math.round(currentTimeGPU * 100) / 100 + "ms "; + element.innerHTML = html; + } + + if (op.patch.loading.getProgress() != 1.0) + { + element.innerHTML += "
    loading " + Math.round(op.patch.loading.getProgress() * 100) + "% " + loadingChars[(++loadingCounter) % loadingChars.length]; + } + + if (opened) + { + let count = 0; + avgMs = 0; + avgMsChilds = 0; + for (let i = queue.length; i > queue.length - queue.length / 3; i--) + { + if (queue[i] > -1) + { + avgMs += queue[i]; + count++; + } + + if (timesMainloop[i] > -1) avgMsChilds += timesMainloop[i]; + } + + avgMs /= count; + avgMsChilds /= count; + + element.innerHTML += "
    " + cgl.canvasWidth + " x " + cgl.canvasHeight + " (x" + cgl.pixelDensity + ") "; + element.innerHTML += "
    frame avg: " + Math.round(avgMsChilds * 100) / 100 + " ms (" + Math.round(avgMsChilds / avgMs * 100) + "%) / " + Math.round(avgMs * 100) / 100 + " ms"; + element.innerHTML += " (self: " + Math.round((selfTime) * 100) / 100 + " ms) "; + + element.innerHTML += "
    shader binds: " + Math.ceil(op.patch.cgl.profileData.profileShaderBinds / fps) + + " uniforms: " + Math.ceil(op.patch.cgl.profileData.profileUniformCount / fps) + + " mvp_uni_mat4: " + Math.ceil(op.patch.cgl.profileData.profileMVPMatrixCount / fps) + + " num glPrimitives: " + Math.ceil(op.patch.cgl.profileData.profileMeshNumElements / (fps)) + + + " mesh.setGeom: " + op.patch.cgl.profileData.profileMeshSetGeom + + " videos: " + op.patch.cgl.profileData.profileVideosPlaying + + " tex preview: " + op.patch.cgl.profileData.profileTexPreviews; + + element.innerHTML += + " draw meshes: " + Math.ceil(op.patch.cgl.profileData.profileMeshDraw / fps) + + " framebuffer blit: " + Math.ceil(op.patch.cgl.profileData.profileFramebuffer / fps) + + " texeffect blit: " + Math.ceil(op.patch.cgl.profileData.profileTextureEffect / fps); + + element.innerHTML += " all shader compiletime: " + Math.round(op.patch.cgl.profileData.shaderCompileTime * 100) / 100; + } + + op.patch.cgl.profileData.clear(); +} + +function styleMeasureEle(ele) +{ + ele.style.padding = "0px"; + ele.style.margin = "0px"; +} + +function addMeasureChild(m, parentEle, timeSum, level) +{ + const height = 20; + m.usedAvg = (m.usedAvg || m.used); + + if (!m.ele || initMeasures) + { + const newEle = document.createElement("div"); + m.ele = newEle; + + if (m.childs && m.childs.length > 0) newEle.style.height = "500px"; + else newEle.style.height = height + "px"; + + newEle.style.overflow = "hidden"; + newEle.style.display = "inline-block"; + + if (!m.isRoot) + { + newEle.innerHTML = "
     " + m.name + "
    "; + newEle.style["background-color"] = "rgb(" + m.colR + "," + m.colG + "," + m.colB + ")"; + newEle.style["border-left"] = "1px solid black"; + } + + parentEle.appendChild(newEle); + } + + if (!m.isRoot) + { + if (performance.now() - m.lastTime > 200) + { + m.ele.style.display = "none"; + m.hidden = true; + } + else + { + if (m.hidden) + { + m.ele.style.display = "inline-block"; + m.hidden = false; + } + } + + m.ele.style.float = "left"; + m.ele.style.width = Math.floor((m.usedAvg / timeSum) * 98.0) + "%"; + } + else + { + m.ele.style.width = "100%"; + m.ele.style.clear = "both"; + m.ele.style.float = "none"; + } + + if (m && m.childs && m.childs.length > 0) + { + let thisTimeSum = 0; + for (var i = 0; i < m.childs.length; i++) + { + m.childs[i].usedAvg = (m.childs[i].usedAvg || m.childs[i].used) * 0.95 + m.childs[i].used * 0.05; + thisTimeSum += m.childs[i].usedAvg; + } + for (var i = 0; i < m.childs.length; i++) + { + addMeasureChild(m.childs[i], m.ele, thisTimeSum, level + 1); + } + } +} + +function clearMeasures(p) +{ + for (let i = 0; i < p.childs.length; i++) clearMeasures(p.childs[i]); + p.childs.length = 0; +} + +function measures() +{ + if (!CGL.performanceMeasures) return; + + if (!elementMeasures) + { + op.log("create measure ele"); + elementMeasures = document.createElement("div"); + elementMeasures.style.width = "100%"; + elementMeasures.style["background-color"] = "#444"; + elementMeasures.style.bottom = "10px"; + elementMeasures.style.height = "100px"; + elementMeasures.style.opacity = "1"; + elementMeasures.style.position = "absolute"; + elementMeasures.style["z-index"] = "99999"; + elementMeasures.innerHTML = ""; + container.appendChild(elementMeasures); + } + + let timeSum = 0; + const root = CGL.performanceMeasures[0]; + + for (let i = 0; i < root.childs.length; i++) timeSum += root.childs[i].used; + + addMeasureChild(CGL.performanceMeasures[0], elementMeasures, timeSum, 0); + + root.childs.length = 0; + + clearMeasures(CGL.performanceMeasures[0]); + + CGL.performanceMeasures.length = 0; + initMeasures = false; +} + +exe.onTriggered = render; + +function render() +{ + const selfTimeStart = performance.now(); + frameCount++; + + if (glQueryExt && inShow.get())op.patch.cgl.profileData.doProfileGlQuery = true; + + if (fpsStartTime === 0)fpsStartTime = Date.now(); + if (Date.now() - fpsStartTime >= 1000) + { + // query=null; + fps = frameCount; + frameCount = 0; + // frames = 0; + outFPS.set(fps); + if (inShow.get())updateText(); + + fpsStartTime = Date.now(); + } + + const glQueryData = op.patch.cgl.profileData.glQueryData; + currentTimeGPU = 0; + if (glQueryData) + { + let count = 0; + for (let i in glQueryData) + { + count++; + if (glQueryData[i].time) + currentTimeGPU += glQueryData[i].time; + } + } + + if (inShow.get()) + { + measures(); + + if (opened && !op.patch.cgl.profileData.pause) + { + const timeUsed = performance.now() - lastTime; + queue.push(timeUsed); + queue.shift(); + + timesMainloop.push(childsTime); + timesMainloop.shift(); + + timesOnFrame.push(op.patch.cgl.profileData.profileOnAnimFrameOps - op.patch.cgl.profileData.profileMainloopMs); + timesOnFrame.shift(); + + timesGPU.push(currentTimeGPU); + timesGPU.shift(); + + updateCanvas(); + } + } + + lastTime = performance.now(); + selfTime = performance.now() - selfTimeStart; + const startTimeChilds = performance.now(); + + outCanv.set(null); + outCanv.set(canvas); + + // startGlQuery(); + next.trigger(); + // endGlQuery(); + + const nChildsTime = performance.now() - startTimeChilds; + const nCurrentTimeMainloop = op.patch.cgl.profileData.profileMainloopMs; + const nCurrentTimeOnFrame = op.patch.cgl.profileData.profileOnAnimFrameOps - op.patch.cgl.profileData.profileMainloopMs; + + if (smoothGraph.get()) + { + childsTime = childsTime * 0.9 + nChildsTime * 0.1; + currentTimeMainloop = currentTimeMainloop * 0.5 + nCurrentTimeMainloop * 0.5; + currentTimeOnFrame = currentTimeOnFrame * 0.5 + nCurrentTimeOnFrame * 0.5; + } + else + { + childsTime = nChildsTime; + currentTimeMainloop = nCurrentTimeMainloop; + currentTimeOnFrame = nCurrentTimeOnFrame; + } + + op.patch.cgl.profileData.clearGlQuery(); +} + + +}; + +Ops.Gl.Performance.prototype = new CABLES.Op(); +CABLES.OPS["9cd2d9de-000f-4a14-bd13-e7d5f057583c"]={f:Ops.Gl.Performance,objName:"Ops.Gl.Performance"}; + + + + +// ************************************************************** +// +// Ops.Gl.PerformanceMeasure +// +// ************************************************************** + +Ops.Gl.PerformanceMeasure = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Execute"), + inName = op.inString("Name"), + outNext = op.outTrigger("Childs"), + outUsed = op.outNumber("Time Used"); + +inExec.onTriggered = update; + +// var Measurement=function(_name) +// { +// this.name=_name; +// this.childs=[]; +// this.used=0; +// this.lastTime=performance.now(); +// this.colR=Math.floor(Math.random()*100)+128; +// this.colG=Math.floor(Math.random()*100)+128; +// this.colB=Math.floor(Math.random()*100)+128; +// }; + +// inExec.onLinkChanged=removeEle; +// outNext.onLinkChanged=removeEle; +// op.onDelete=removeEle; + +// function removeEle() +// { +// theMeasure.used=0; +// if(theMeasure.ele) +// theMeasure.ele.remove(); + +// theMeasure.ele=null; +// }; + +// inName.onChange=function() +// { +// theMeasure.name=inName.get(); +// removeEle(); +// }; + +// var theMeasure=new Measurement("m"); + +function update() +{ + // if(!CGL.performanceMeasures||CGL.performanceMeasures.length===0) + // { + + // CGL.rootMeasure=CGL.rootMeasure||new Measurement("Root"); + // CGL.rootMeasure.isRoot=true; + // CGL.performanceMeasures=[]; + // CGL.performanceMeasures.push(CGL.rootMeasure); + // CGL.currentPerfMeasurement=CGL.performanceMeasures[0]; + // } + + // theMeasure.childs.length=0; + // var prevMeasure=CGL.currentPerfMeasurement; + + // CGL.currentPerfMeasurement.childs.push(theMeasure); + // CGL.currentPerfMeasurement=theMeasure; + const startTime = performance.now(); + outNext.trigger(); + const used = performance.now() - startTime; + // theMeasure.used=(theMeasure.used*0.8+used*0.2); + // theMeasure.used=used; + // theMeasure.lastTime=performance.now(); + + // CGL.currentPerfMeasurement=prevMeasure; + outUsed.set(used); +} + + +}; + +Ops.Gl.PerformanceMeasure.prototype = new CABLES.Op(); +CABLES.OPS["a2f90e82-a907-4984-a3a1-8e036cf2eee5"]={f:Ops.Gl.PerformanceMeasure,objName:"Ops.Gl.PerformanceMeasure"}; + + + + +// ************************************************************** +// +// Ops.Gl.Perspective +// +// ************************************************************** + +Ops.Gl.Perspective = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + inAxis = op.inSwitch("Axis", ["Vertical", "Horizontal"], "Vertical"), + fovY = op.inValueFloat("fov y", 45), + zNear = op.inValueFloat("frustum near", 0.1), + zFar = op.inValueFloat("frustum far", 20), + autoAspect = op.inValueBool("Auto Aspect Ratio", true), + aspect = op.inValue("Aspect Ratio"), + trigger = op.outTrigger("trigger"), + outAsp = op.outNumber("Aspect"); + +fovY.onChange = zFar.onChange = zNear.onChange = changed; +fovY.setUiAttribs({ "title": "FOV Degrees" }); + +op.setPortGroup("Field of View", [fovY]); +op.setPortGroup("Frustrum", [zNear, zFar]); + +let asp = 0; +let axis = 0; + +changed(); + +inAxis.onChange = () => +{ + axis = 0; + if (inAxis.get() == "Horizontal")axis = 1; +}; + +render.onTriggered = function () +{ + const cg = op.patch.cg; + + asp = cg.getViewPort()[2] / cg.getViewPort()[3]; + if (!autoAspect.get())asp = aspect.get(); + outAsp.set(asp); + + cg.pushPMatrix(); + + if (axis == 0) + mat4.perspective(cg.pMatrix, fovY.get() * 0.0174533, asp, zNear.get(), zFar.get()); + else + perspectiveFovX(cg.pMatrix, fovY.get() * 0.0174533, asp, zNear.get(), zFar.get()); + + trigger.trigger(); + + cg.popPMatrix(); +}; + +function changed() +{ + op.patch.cgl.frameStore.perspective = + { + "fovy": fovY.get(), + "zFar": zFar.get(), + "zNear": zNear.get(), + }; +} + +function perspectiveFovX(out, fovx, aspect, near, far) +{ + let nf; + let f = 1 / (fovx) * 2; + // Math.tan(1 / fovx * 2), + // f=Math.max(0,f); + + op.log(f); + out[0] = f; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f / (1.0 / aspect); + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[15] = 0; + + if (far != null && far !== Infinity) + { + nf = 1 / (near - far); + out[10] = (far + near) * nf; + out[14] = 2 * far * near * nf; + } + else + { + out[10] = -1; + out[14] = -2 * near; + } + return out; +} + + +}; + +Ops.Gl.Perspective.prototype = new CABLES.Op(); +CABLES.OPS["7a78e163-d28c-4f70-a6d0-6d952da79f50"]={f:Ops.Gl.Perspective,objName:"Ops.Gl.Perspective"}; + + + + +// ************************************************************** +// +// Ops.Gl.Phong.AmbientLight_v4 +// +// ************************************************************** + +Ops.Gl.Phong.AmbientLight_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inTrigger = op.inTrigger("Trigger In"); +const inR = op.inValueSlider("R", 0.1); +inR.setUiAttribs({ "colorPick": true }); +const inG = op.inValueSlider("G", 0.125); +const inB = op.inValueSlider("B", 0.15); +const inIntensity = op.inValueSlider("Intensity", 1); +const colorIn = [inR, inG, inB]; +op.setPortGroup("Color", colorIn); + +const outTrigger = op.outTrigger("Trigger Out"); +// your new op +// have a look at the documentation at: +// https://docs.cables.gl/dev_hello_op/dev_hello_op.html + +function Light(config) +{ + this.type = config.type || "point"; + this.color = config.color || [1, 1, 1]; + this.specular = config.specular || [0, 0, 0]; + this.position = config.position || null; + this.intensity = config.intensity || 1; + this.constantAttenuation = config.constantAttenuation || 0; + this.linearAttenuation = config.linearAttenuation || 0; + this.quadraticAttenuation = config.quadraticAttenuation || 0; + this.radius = config.radius || 1; + this.falloff = config.falloff || 1; + this.spotExponent = config.spotExponent || 1; + this.coneAngleInner = config.coneAngleInner || 0; // spot light + this.coneAngle = config.coneAngle || 0; // spot light + this.cosConeAngle = Math.cos(CGL.DEG2RAD * this.coneAngle); + this.conePointAt = config.conePointAt || [0, 0, 0]; + + // * shadow + this.nearFar = [0, 0]; + this.shadowStrength = 0; + this.shadowBias = 0; + + return this; +} + +const cgl = op.patch.cgl; + +const light = new Light({ + "type": "ambient", + "color": [0, 1, 2].map(function (i) { return colorIn[i].get(); }), + "intensity": inIntensity.get(), +}); + +const inLight = { + "color": [inR, inG, inB], + "intensity": inIntensity, +}; + +Object.keys(inLight).forEach(function (key) +{ + if (inLight[key].length) + { + for (let i = 0; i < inLight[key].length; i += 1) + { + inLight[key][i].onChange = function () + { + light[key][i] = inLight[key][i].get(); + }; + } + } + else + { + inLight[key].onChange = function () + { + light[key] = inLight[key].get(); + }; + } +}); + +inTrigger.onTriggered = function () +{ + if (!cgl.frameStore.lightStack) cgl.frameStore.lightStack = []; + + cgl.frameStore.lightStack.push(light); + outTrigger.trigger(); + cgl.frameStore.lightStack.pop(); +}; + + +}; + +Ops.Gl.Phong.AmbientLight_v4.prototype = new CABLES.Op(); +CABLES.OPS["235a18d1-a8de-4034-962d-2848017e27cd"]={f:Ops.Gl.Phong.AmbientLight_v4,objName:"Ops.Gl.Phong.AmbientLight_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.Phong.DirectionalLight_v5 +// +// ************************************************************** + +Ops.Gl.Phong.DirectionalLight_v5 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +// * OP START * +const inTrigger = op.inTrigger("Trigger In"); + +const inCastLight = op.inBool("Cast Light", true); +const inIntensity = op.inFloat("Intensity", 1); +const attribIns = [inCastLight, inIntensity]; +op.setPortGroup("Light Attributes", attribIns); + +const inPosX = op.inFloat("X", 0); +const inPosY = op.inFloat("Y", 3); +const inPosZ = op.inFloat("Z", 5); + +const positionIn = [inPosX, inPosY, inPosZ]; +op.setPortGroup("Direction", positionIn); + +const inR = op.inFloat("R", 1); +const inG = op.inFloat("G", 1); +const inB = op.inFloat("B", 1); + +inR.setUiAttribs({ "colorPick": true }); +const colorIn = [inR, inG, inB]; +op.setPortGroup("Color", colorIn); + +const inSpecularR = op.inFloat("Specular R", 0.2); +const inSpecularG = op.inFloat("Specular G", 0.2); +const inSpecularB = op.inFloat("Specular B", 0.2); + +inSpecularR.setUiAttribs({ "colorPick": true }); +const colorSpecularIn = [inSpecularR, inSpecularG, inSpecularB]; +op.setPortGroup("Specular Color", colorSpecularIn); + +const inCastShadow = op.inBool("Cast Shadow", false); +const inRenderMapActive = op.inBool("Rendering Active", true); +const inMapSize = op.inSwitch("Map Size", [256, 512, 1024, 2048], 512); +const inShadowStrength = op.inFloatSlider("Shadow Strength", 1); +const inLRBT = op.inFloat("LR-BottomTop", 8); +const inNear = op.inFloat("Near", 0.1); +const inFar = op.inFloat("Far", 30); +const inBias = op.inFloatSlider("Bias", 0.004); +const inPolygonOffset = op.inInt("Polygon Offset", 0); +const inNormalOffset = op.inFloatSlider("Normal Offset", 0); +const inBlur = op.inFloatSlider("Blur Amount", 0); +op.setPortGroup("", [inCastShadow]); +op.setPortGroup("Shadow Map Settings", [inMapSize, inRenderMapActive, inShadowStrength, inLRBT, inNear, inFar, inBias, inPolygonOffset, inNormalOffset, inBlur]); + +inMapSize.setUiAttribs({ "greyout": true }); +inRenderMapActive.setUiAttribs({ "greyout": true }); +inShadowStrength.setUiAttribs({ "greyout": true }); +inLRBT.setUiAttribs({ "greyout": true, "hidePort": true }); +inNear.setUiAttribs({ "greyout": true, "hidePort": true }); +inFar.setUiAttribs({ "greyout": true, "hidePort": true }); +inBias.setUiAttribs({ "greyout": true, "hidePort": true }); +inNormalOffset.setUiAttribs({ "greyout": true, "hidePort": true }); +inPolygonOffset.setUiAttribs({ "greyout": true, "hidePort": true }); +inBlur.setUiAttribs({ "greyout": true }); + +const inAdvanced = op.inBool("Enable Advanced", false); +const inMSAA = op.inSwitch("MSAA", ["none", "2x", "4x", "8x"], "none"); +const inFilterType = op.inSwitch("Texture Filter", ["Linear", "Nearest", "Mip Map"], "Linear"); +const inAnisotropic = op.inSwitch("Anisotropic", [0, 1, 2, 4, 8, 16], "0"); +inMSAA.setUiAttribs({ "greyout": true, "hidePort": true }); +inFilterType.setUiAttribs({ "greyout": true, "hidePort": true }); +inAnisotropic.setUiAttribs({ "greyout": true, "hidePort": true }); +op.setPortGroup("Advanced Options", [inAdvanced, inMSAA, inFilterType, inAnisotropic]); + +inAdvanced.onChange = function () +{ + inMSAA.setUiAttribs({ "greyout": !inAdvanced.get() }); + inFilterType.setUiAttribs({ "greyout": !inAdvanced.get() }); + inAnisotropic.setUiAttribs({ "greyout": !inAdvanced.get() }); +}; + +const outTrigger = op.outTrigger("Trigger Out"); +const outTexture = op.outTexture("Shadow Map"); + +let texelSize = 1 / Number(inMapSize.get()); + +const newLight = new CGL.Light(cgl, { + "type": "directional", + "position": [0, 1, 2].map(function (i) { return positionIn[i].get(); }), + "color": [0, 1, 2].map(function (i) { return colorIn[i].get(); }), + "specular": [0, 1, 2].map(function (i) { return colorSpecularIn[i].get(); }), + "intensity": inIntensity.get(), + "castShadow": false, + "shadowStrength": inShadowStrength.get(), +}); +newLight.castLight = inCastLight.get(); + +let updating = false; + +function updateBuffers() +{ + updating = true; + const MSAA = Number(inMSAA.get().charAt(0)); + + let filterType = null; + const anisotropyFactor = Number(inAnisotropic.get()); + + if (inFilterType.get() == "Linear") + { + filterType = CGL.Texture.FILTER_LINEAR; + } + else if (inFilterType.get() == "Nearest") + { + filterType = CGL.Texture.FILTER_NEAREST; + } + else if (inFilterType.get() == "Mip Map") + { + filterType = CGL.Texture.FILTER_MIPMAP; + } + + const mapSize = Number(inMapSize.get()); + const textureOptions = { + "isFloatingPointTexture": true, + "filter": filterType, + }; + + if (MSAA) Object.assign(textureOptions, { "multisampling": true, "multisamplingSamples": MSAA }); + Object.assign(textureOptions, { "anisotropic": anisotropyFactor }); + + newLight.createFramebuffer(mapSize, mapSize, textureOptions); + newLight.createBlurEffect(textureOptions); + updating = false; +} + +function updateShadowMapFramebuffer() +{ + const size = Number(inMapSize.get()); + texelSize = 1 / size; + + if (inCastShadow.get()) + { + newLight.createFramebuffer(Number(inMapSize.get()), Number(inMapSize.get()), {}); + newLight.createShadowMapShader(); + newLight.createBlurEffect({}); + newLight.createBlurShader(); + newLight.updateProjectionMatrix(inLRBT.get(), inNear.get(), inFar.get(), null); + } + + if (inAdvanced.get()) updateBuffers(); + + updating = false; + updateLight = true; +} + +inMSAA.onChange = inAnisotropic.onChange = inFilterType.onChange = inMapSize.onChange = function () +{ + updating = true; +}; + +inR.onChange = inG.onChange = inB.onChange = inSpecularR.onChange = inSpecularG.onChange = inSpecularB.onChange += inPosX.onChange = inPosY.onChange = inPosZ.onChange += inBias.onChange = inIntensity.onChange = inCastLight.onChange = inShadowStrength.onChange = inNormalOffset.onChange = updateLightParameters; + +let updateLight = false; +function updateLightParameters(param) +{ + updateLight = true; +} + +inCastShadow.onChange = function () +{ + updating = true; + updateLight = true; + + const castShadow = inCastShadow.get(); + + inMapSize.setUiAttribs({ "greyout": !castShadow }); + inRenderMapActive.setUiAttribs({ "greyout": !castShadow }); + inShadowStrength.setUiAttribs({ "greyout": !castShadow }); + inLRBT.setUiAttribs({ "greyout": !castShadow }); + inNear.setUiAttribs({ "greyout": !castShadow }); + inFar.setUiAttribs({ "greyout": !castShadow }); + inBlur.setUiAttribs({ "greyout": !castShadow }); + inBias.setUiAttribs({ "greyout": !castShadow }); + inNormalOffset.setUiAttribs({ "greyout": !castShadow }); + inPolygonOffset.setUiAttribs({ "greyout": !castShadow }); +}; + +inLRBT.onChange = inNear.onChange = inFar.onChange = function () +{ + updateLight = true; +}; + +function drawHelpers() +{ + if (cgl.shouldDrawHelpers(op)) + { + gui.setTransformGizmo({ + "posX": inPosX, + "posY": inPosY, + "posZ": inPosZ, + }); + CABLES.GL_MARKER.drawLineSourceDest( + op, + -200 * newLight.position[0], + -200 * newLight.position[1], + -200 * newLight.position[2], + 200 * newLight.position[0], + 200 * newLight.position[1], + 200 * newLight.position[2], + ); + } +} + +let errorActive = false; +inTrigger.onTriggered = function () +{ + if (updating) + { + if (cgl.frameStore.shadowPass) return; + updateShadowMapFramebuffer(); + } + + if (!cgl.frameStore.shadowPass) + { + if (!newLight.isUsed && !errorActive) + { + op.setUiError("lightUsed", "No operator is using this light. Make sure this op is positioned before an operator that uses lights. Also make sure there is an operator that uses lights after this.", 1); // newLight.isUsed = false; + errorActive = true; + } + else if (!newLight.isUsed && errorActive) {} + else if (newLight.isUsed && errorActive) + { + op.setUiError("lightUsed", null); + errorActive = false; + } + else if (newLight.isUsed && !errorActive) {} + newLight.isUsed = false; + } + + if (updateLight) + { + newLight.color = [inR.get(), inG.get(), inB.get()]; + newLight.specular = [inSpecularR.get(), inSpecularG.get(), inSpecularB.get()]; + newLight.intensity = inIntensity.get(); + newLight.castLight = inCastLight.get(); + newLight.position = [inPosX.get(), inPosY.get(), inPosZ.get()]; + newLight.updateProjectionMatrix(inLRBT.get(), inNear.get(), inFar.get(), null); + newLight.castShadow = inCastShadow.get(); + + newLight.normalOffset = inNormalOffset.get(); + newLight.shadowBias = inBias.get(); + newLight.shadowStrength = inShadowStrength.get(); + updateLight = false; + } + + if (!cgl.frameStore.lightStack) cgl.frameStore.lightStack = []; + + if (!cgl.frameStore.shadowPass) drawHelpers(); + + cgl.frameStore.lightStack.push(newLight); + + if (inCastShadow.get()) + { + const blurAmount = 1.5 * inBlur.get() * texelSize; + if (inRenderMapActive.get()) newLight.renderPasses(inPolygonOffset.get(), blurAmount, function () { outTrigger.trigger(); }); + newLight.blurAmount = inBlur.get(); + outTexture.set(null); + outTexture.set(newLight.getShadowMapDepth()); + // remove light from stack and readd it with shadow map & mvp matrix + cgl.frameStore.lightStack.pop(); + + cgl.frameStore.lightStack.push(newLight); + } + else + { + outTexture.set(null); + } + + outTrigger.trigger(); + + cgl.frameStore.lightStack.pop(); +}; + + +}; + +Ops.Gl.Phong.DirectionalLight_v5.prototype = new CABLES.Op(); +CABLES.OPS["9f41bf91-f4e0-4ce4-89d8-72627b76261e"]={f:Ops.Gl.Phong.DirectionalLight_v5,objName:"Ops.Gl.Phong.DirectionalLight_v5"}; + + + + +// ************************************************************** +// +// Ops.Gl.Phong.LambertMaterial_v2 +// +// ************************************************************** + +Ops.Gl.Phong.LambertMaterial_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"lambert_frag":"{{MODULES_HEAD}}\n\n#define AMBIENT 0\n#define POINT 1\n#define DIRECTIONAL 2\n#define SPOT 3\n\nIN vec3 norm;\nIN vec4 modelPos;\n\n// UNI mat4 normalMatrix;\nIN mat3 normalMatrix; // when instancing...\n\nIN vec2 texCoord;\n\nIN vec3 mvNormal;\nIN vec3 mvTangent;\nIN vec3 mvBiTangent;\n\nUNI vec4 materialColor;//r,g,b,a;\n\nstruct Light {\n vec3 position;\n vec3 color;\n // * SPOT LIGHT * //\n #ifdef HAS_SPOT\n vec3 conePointAt;\n #define COSCONEANGLE x\n #define COSCONEANGLEINNER y\n #define SPOTEXPONENT z\n vec3 spotProperties;\n #endif\n\n #define INTENSITY x\n #define ATTENUATION y\n #define FALLOFF z\n #define RADIUS w\n vec4 lightProperties;\n\n int type;\n int castLight;\n #define CASTLIGHT x\n #define TYPE y\n ivec2 castLightType;\n};\n#ifdef HAS_TEXTURES\n #ifdef HAS_TEXTURE_DIFFUSE\n UNI sampler2D texDiffuse;\n #endif\n#endif\n\nUNI Light lights[NUM_LIGHTS];\n\n// * UTILITY FUNCTIONS */\nfloat when_gt(float x, float y) { return max(sign(x - y), 0.0); } // comparator function\nfloat when_eq(float x, float y) { return 1. - abs(sign(x - y)); } // comparator function\nfloat when_neq(float x, float y) { return abs(sign(x - y)); } // comparator function\n\n\n// * LIGHT CALCULATIONS */\nfloat CalculateFalloff(float radius, float falloff, float distLight)\n{\n float denom = distLight / radius + 1.0;\n float attenuation = 1.0 / (denom*denom);\n float t = (attenuation - 0.1) / (1.0 - 0.1);\n\n t = t * (20.0 * (1. - falloff) * 20.0 * (1. - falloff));\n\n return min(1.0,max(t, 0.0));\n}\n\nfloat Falloff2(vec3 lightDirection, float falloff) {\n float distanceSquared = dot(lightDirection, lightDirection);\n float factor = distanceSquared * falloff;\n float smoothFactor = clamp(1. - factor * factor, 0., 1.);\n float attenuation = smoothFactor * smoothFactor;\n\n return attenuation * 1. / max(distanceSquared, 0.00001);\n}\n\n#ifdef HAS_SPOT\n float CalculateSpotLightEffect(vec3 lightPosition, vec3 conePointAt, float cosConeAngle, float cosConeAngleInner, float spotExponent, vec3 lightDirection) {\n vec3 spotLightDirection = normalize(lightPosition-conePointAt);\n float spotAngle = dot(-lightDirection, spotLightDirection);\n float epsilon = cosConeAngle - cosConeAngleInner;\n\n float spotIntensity = clamp((spotAngle - cosConeAngle)/epsilon, 0.0, 1.0);\n spotIntensity = pow(spotIntensity, max(0.01, spotExponent));\n\n return max(0., spotIntensity);\n }\n#endif\nvec3 CalculateDiffuseColor(vec3 lightDirection, vec3 normal, vec3 lightColor, vec3 materialColor, inout float lambert) {\n lambert = clamp(dot(lightDirection, normal), 0., 1.);\n vec3 diffuseColor = lambert * lightColor * materialColor;\n return diffuseColor;\n}\n\n\n// MAIN\nvoid main()\n{\n {{MODULE_BEGIN_FRAG}}\n\n vec4 col=vec4(0.0);\n vec3 normal = normalize(mat3(normalMatrix)*norm);\n\n #ifdef DOUBLE_SIDED\n if(!gl_FrontFacing) normal = normal*-1.0;\n #endif\n\n vec3 matColor = materialColor.rgb;\n\n #ifdef HAS_TEXTURES\n #ifdef HAS_TEXTURE_DIFFUSE\n matColor = texture(texDiffuse, texCoord).rgb;\n #ifdef COLORIZE_TEXTURE\n matColor *= materialColor.rgb;\n #endif\n #endif\n #endif\n\n for(int l=0;l { return attachmentFragmentHead.replace("{{LIGHT_INDEX}}", n); }; +const createFragmentBody = (n, type) => { return snippets[type].replace(LIGHT_INDEX_REGEX, n); }; + +function createDefaultShader() +{ + const vertexShader = attachments.phong_vert; + let fragmentShader = attachments.phong_frag; + let fragmentHead = ""; + let fragmentBody = ""; + + fragmentHead = fragmentHead.concat(createFragmentHead(0)); + fragmentBody = fragmentBody.concat(createFragmentBody(0, DEFAULT_LIGHTSTACK[0].type)); + + fragmentShader = fragmentShader.replace(FRAGMENT_HEAD_REGEX, fragmentHead); + fragmentShader = fragmentShader.replace(FRAGMENT_BODY_REGEX, fragmentBody); + + shader.setSource(vertexShader, fragmentShader); + shader.define("HAS_POINT"); + shader.removeDefine("HAS_SPOT"); + shader.removeDefine("HAS_DIRECTIONAL"); + shader.removeDefine("HAS_AMBIENT"); +} + +const inTrigger = op.inTrigger("Trigger In"); + +// * DIFFUSE * +const inDiffuseR = op.inFloat("R", Math.random()); +const inDiffuseG = op.inFloat("G", Math.random()); +const inDiffuseB = op.inFloat("B", Math.random()); +const inDiffuseA = op.inFloatSlider("A", 1); +const diffuseColors = [inDiffuseR, inDiffuseG, inDiffuseB, inDiffuseA]; +op.setPortGroup("Diffuse Color", diffuseColors); + +const inToggleOrenNayar = op.inBool("Enable", false); +const inAlbedo = op.inFloatSlider("Albedo", 0.707); +const inRoughness = op.inFloatSlider("Roughness", 0.835); + +inToggleOrenNayar.setUiAttribs({ "hidePort": true }); +inAlbedo.setUiAttribs({ "greyout": true }); +inRoughness.setUiAttribs({ "greyout": true }); +inDiffuseR.setUiAttribs({ "colorPick": true }); +op.setPortGroup("Oren-Nayar Diffuse", [inToggleOrenNayar, inAlbedo, inRoughness]); + +inToggleOrenNayar.onChange = function () +{ + shader.toggleDefine("ENABLE_OREN_NAYAR_DIFFUSE", inToggleOrenNayar); + inAlbedo.setUiAttribs({ "greyout": !inToggleOrenNayar.get() }); + inRoughness.setUiAttribs({ "greyout": !inToggleOrenNayar.get() }); +}; + +// * FRESNEL * +const inToggleFresnel = op.inValueBool("Active", false); +inToggleFresnel.setUiAttribs({ "hidePort": true }); +const inFresnel = op.inValueSlider("Fresnel Intensity", 0.7); +const inFresnelWidth = op.inFloat("Fresnel Width", 1); +const inFresnelExponent = op.inFloat("Fresnel Exponent", 6); +const inFresnelR = op.inFloat("Fresnel R", 1); +const inFresnelG = op.inFloat("Fresnel G", 1); +const inFresnelB = op.inFloat("Fresnel B", 1); +inFresnelR.setUiAttribs({ "colorPick": true }); + +const fresnelArr = [inFresnel, inFresnelWidth, inFresnelExponent, inFresnelR, inFresnelG, inFresnelB]; +fresnelArr.forEach(function (port) { port.setUiAttribs({ "greyout": true }); }); +op.setPortGroup("Fresnel", fresnelArr.concat([inToggleFresnel])); + +let uniFresnel = null; +let uniFresnelWidthExponent = null; +inToggleFresnel.onChange = function () +{ + shader.toggleDefine("ENABLE_FRESNEL", inToggleFresnel); + if (inToggleFresnel.get()) + { + if (!uniFresnel) uniFresnel = new CGL.Uniform(shader, "4f", "inFresnel", inFresnelR, inFresnelG, inFresnelB, inFresnel); + if (!uniFresnelWidthExponent) uniFresnelWidthExponent = new CGL.Uniform(shader, "2f", "inFresnelWidthExponent", inFresnelWidth, inFresnelExponent); + } + else + { + if (uniFresnel) + { + shader.removeUniform("inFresnel"); + uniFresnel = null; + } + + if (uniFresnelWidthExponent) + { + shader.removeUniform("inFresnelWidthExponent"); + uniFresnelWidthExponent = null; + } + } + + fresnelArr.forEach(function (port) { port.setUiAttribs({ "greyout": !inToggleFresnel.get() }); }); +}; +// * EMISSIVE * +const inEmissiveActive = op.inBool("Emissive Active", false); +const inEmissiveColorIntensity = op.inFloatSlider("Color Intensity", 0.3); +const inEmissiveR = op.inFloatSlider("Emissive R", Math.random()); +const inEmissiveG = op.inFloatSlider("Emissive G", Math.random()); +const inEmissiveB = op.inFloatSlider("Emissive B", Math.random()); +inEmissiveR.setUiAttribs({ "colorPick": true }); +op.setPortGroup("Emissive Color", [inEmissiveActive, inEmissiveColorIntensity, inEmissiveR, inEmissiveG, inEmissiveB]); + +inEmissiveColorIntensity.setUiAttribs({ "greyout": !inEmissiveActive.get() }); +inEmissiveR.setUiAttribs({ "greyout": !inEmissiveActive.get() }); +inEmissiveG.setUiAttribs({ "greyout": !inEmissiveActive.get() }); +inEmissiveB.setUiAttribs({ "greyout": !inEmissiveActive.get() }); + +let uniEmissiveColor = null; + +inEmissiveActive.onChange = () => +{ + shader.toggleDefine("ADD_EMISSIVE_COLOR", inEmissiveActive); + + if (inEmissiveActive.get()) + { + uniEmissiveColor = new CGL.Uniform(shader, "4f", "inEmissiveColor", inEmissiveR, inEmissiveG, inEmissiveB, inEmissiveColorIntensity); + inEmissiveTexture.setUiAttribs({ "greyout": false }); + inEmissiveMaskTexture.setUiAttribs({ "greyout": false }); + + if (inEmissiveTexture.get()) inEmissiveIntensity.setUiAttribs({ "greyout": false }); + if (inEmissiveMaskTexture.get()) inEmissiveMaskIntensity.setUiAttribs({ "greyout": false }); + } + else + { + op.log("ayayay"); + inEmissiveTexture.setUiAttribs({ "greyout": true }); + inEmissiveMaskTexture.setUiAttribs({ "greyout": true }); + inEmissiveIntensity.setUiAttribs({ "greyout": true }); + inEmissiveMaskIntensity.setUiAttribs({ "greyout": true }); + + shader.removeUniform("inEmissiveColor"); + uniEmissiveColor = null; + } + + if (inEmissiveTexture.get()) + { + inEmissiveColorIntensity.setUiAttribs({ "greyout": true }); + inEmissiveR.setUiAttribs({ "greyout": true }); + inEmissiveG.setUiAttribs({ "greyout": true }); + inEmissiveB.setUiAttribs({ "greyout": true }); + } + else + { + if (inEmissiveActive.get()) + { + inEmissiveColorIntensity.setUiAttribs({ "greyout": false }); + inEmissiveR.setUiAttribs({ "greyout": false }); + inEmissiveG.setUiAttribs({ "greyout": false }); + inEmissiveB.setUiAttribs({ "greyout": false }); + } + else + { + inEmissiveColorIntensity.setUiAttribs({ "greyout": true }); + inEmissiveR.setUiAttribs({ "greyout": true }); + inEmissiveG.setUiAttribs({ "greyout": true }); + inEmissiveB.setUiAttribs({ "greyout": true }); + } + } +}; +// * SPECULAR * +const inShininess = op.inFloat("Shininess", 4); +const inSpecularCoefficient = op.inFloatSlider("Specular Amount", 0.5); +const inSpecularMode = op.inSwitch("Specular Model", ["Blinn", "Schlick", "Phong", "Gauss"], "Blinn"); + +inSpecularMode.setUiAttribs({ "hidePort": true }); +const specularColors = [inShininess, inSpecularCoefficient, inSpecularMode]; +op.setPortGroup("Specular", specularColors); + +// * LIGHT * +const inEnergyConservation = op.inValueBool("Energy Conservation", false); +const inToggleDoubleSided = op.inBool("Double Sided Material", false); +const inFalloffMode = op.inSwitch("Falloff Mode", ["A", "B", "C", "D"], "A"); +inEnergyConservation.setUiAttribs({ "hidePort": true }); +inToggleDoubleSided.setUiAttribs({ "hidePort": true }); +inFalloffMode.setUiAttribs({ "hidePort": true }); +inFalloffMode.onChange = () => +{ + const MODES = ["A", "B", "C", "D"]; + shader.define("FALLOFF_MODE_" + inFalloffMode.get()); + MODES.filter((mode) => { return mode !== inFalloffMode.get(); }) + .forEach((mode) => { return shader.removeDefine("FALLOFF_MODE_" + mode); }); +}; + +const lightProps = [inEnergyConservation, inToggleDoubleSided, inFalloffMode]; +op.setPortGroup("Light Options", lightProps); + +// TEXTURES +const inDiffuseTexture = op.inTexture("Diffuse Texture"); +const inSpecularTexture = op.inTexture("Specular Texture"); +const inNormalTexture = op.inTexture("Normal Map"); +const inAoTexture = op.inTexture("AO Texture"); +const inEmissiveTexture = op.inTexture("Emissive Texture"); +const inEmissiveMaskTexture = op.inTexture("Emissive Mask"); +const inAlphaTexture = op.inTexture("Opacity Texture"); +const inEnvTexture = op.inTexture("Environment Map"); +const inLuminanceMaskTexture = op.inTexture("Env Map Mask"); +op.setPortGroup("Textures", [inDiffuseTexture, inSpecularTexture, inNormalTexture, inAoTexture, inEmissiveTexture, inEmissiveMaskTexture, inAlphaTexture, inEnvTexture, inLuminanceMaskTexture]); + +// TEXTURE TRANSFORMS +const inColorizeTexture = op.inBool("Colorize Texture", false); +const inDiffuseRepeatX = op.inFloat("Diffuse Repeat X", 1); +const inDiffuseRepeatY = op.inFloat("Diffuse Repeat Y", 1); +const inTextureOffsetX = op.inFloat("Texture Offset X", 0); +const inTextureOffsetY = op.inFloat("Texture Offset Y", 0); + +const inSpecularIntensity = op.inFloatSlider("Specular Intensity", 1); +const inNormalIntensity = op.inFloatSlider("Normal Map Intensity", 0.5); +const inAoIntensity = op.inFloatSlider("AO Intensity", 1); +const inEmissiveIntensity = op.inFloatSlider("Emissive Intensity", 1); +const inEmissiveMaskIntensity = op.inFloatSlider("Emissive Mask Intensity", 1); +const inEnvMapIntensity = op.inFloatSlider("Env Map Intensity", 1); +const inEnvMapBlend = op.inSwitch("Env Map Blend", ["Add", "Multiply", "Mix"], "Add"); +const inLuminanceMaskIntensity = op.inFloatSlider("Env Mask Intensity", 1); + +inColorizeTexture.setUiAttribs({ "hidePort": true }); +op.setPortGroup("Texture Transforms", [inColorizeTexture, inDiffuseRepeatY, inDiffuseRepeatX, inTextureOffsetY, inTextureOffsetX]); +op.setPortGroup("Texture Intensities", [inNormalIntensity, inAoIntensity, inSpecularIntensity, inEmissiveIntensity, inEnvMapBlend, inEmissiveMaskIntensity, inEnvMapIntensity, inLuminanceMaskIntensity]); +const alphaMaskSource = op.inSwitch("Alpha Mask Source", ["Luminance", "R", "G", "B", "A"], "Luminance"); +alphaMaskSource.setUiAttribs({ "greyout": true }); + +const discardTransPxl = op.inValueBool("Discard Transparent Pixels"); +discardTransPxl.setUiAttribs({ "hidePort": true }); + +op.setPortGroup("Opacity Texture", [alphaMaskSource, discardTransPxl]); + +const outTrigger = op.outTrigger("Trigger Out"); +const shaderOut = op.outObject("Shader", null, "shader"); +shaderOut.ignoreValueSerialize = true; + +const shader = new CGL.Shader(cgl, "phongmaterial_" + op.id); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG", "MODULE_BASE_COLOR"]); +shader.setSource(attachments.simosphong_vert, attachments.simosphong_frag); +let recompileShader = false; +shader.define("FALLOFF_MODE_A"); + +if (cgl.glVersion < 2) +{ + shader.enableExtension("GL_OES_standard_derivatives"); + + if (cgl.gl.getExtension("OES_texture_float")) shader.enableExtension("GL_OES_texture_float"); + else op.log("error loading extension OES_texture_float"); + + if (cgl.gl.getExtension("OES_texture_float_linear")) shader.enableExtension("GL_OES_texture_float_linear"); + else op.log("error loading extention OES_texture_float_linear"); + + if (cgl.gl.getExtension("GL_OES_texture_half_float")) shader.enableExtension("GL_OES_texture_half_float"); + else op.log("error loading extention GL_OES_texture_half_float"); + + if (cgl.gl.getExtension("GL_OES_texture_half_float_linear")) shader.enableExtension("GL_OES_texture_half_float_linear"); + else op.log("error loading extention GL_OES_texture_half_float_linear"); +} + +const FRAGMENT_HEAD_REGEX = new RegExp("{{PHONG_FRAGMENT_HEAD}}", "g"); +const FRAGMENT_BODY_REGEX = new RegExp("{{PHONG_FRAGMENT_BODY}}", "g"); + +const hasLight = { + "directional": false, + "spot": false, + "ambient": false, + "point": false, +}; + +function createShader(lightStack) +{ + let fragmentShader = attachments.phong_frag; + + let fragmentHead = ""; + let fragmentBody = ""; + + hasLight.directional = false; + hasLight.spot = false; + hasLight.ambient = false; + hasLight.point = false; + + for (let i = 0; i < lightStack.length; i += 1) + { + const light = lightStack[i]; + + const type = light.type; + + if (!hasLight[type]) + { + hasLight[type] = true; + } + + fragmentHead = fragmentHead.concat(createFragmentHead(i)); + fragmentBody = fragmentBody.concat(createFragmentBody(i, light.type)); + } + + fragmentShader = fragmentShader.replace(FRAGMENT_HEAD_REGEX, fragmentHead); + fragmentShader = fragmentShader.replace(FRAGMENT_BODY_REGEX, fragmentBody); + + shader.setSource(attachments.phong_vert, fragmentShader); + + for (let i = 0, keys = Object.keys(hasLight); i < keys.length; i += 1) + { + const key = keys[i]; + + if (hasLight[key]) + { + if (!shader.hasDefine("HAS_" + key.toUpperCase())) + { + shader.define("HAS_" + key.toUpperCase()); + } + } + else + { + if (shader.hasDefine("HAS_" + key.toUpperCase())) + { + shader.removeDefine("HAS_" + key.toUpperCase()); + } + } + } +} + +shaderOut.set(shader); + +let diffuseTextureUniform = null; +let specularTextureUniform = null; +let normalTextureUniform = null; +let aoTextureUniform = null; +let emissiveTextureUniform = null; +let emissiveMaskTextureUniform = null; +let emissiveMaskIntensityUniform = null; +let alphaTextureUniform = null; +let envTextureUniform = null; +let inEnvMapIntensityUni = null; +let inEnvMapWidthUni = null; +let luminanceTextureUniform = null; +let inLuminanceMaskIntensityUniform = null; + +inColorizeTexture.onChange = function () +{ + shader.toggleDefine("COLORIZE_TEXTURE", inColorizeTexture.get()); +}; + +function updateDiffuseTexture() +{ + if (inDiffuseTexture.get()) + { + if (!shader.hasDefine("HAS_TEXTURE_DIFFUSE")) + { + shader.define("HAS_TEXTURE_DIFFUSE"); + if (!diffuseTextureUniform) diffuseTextureUniform = new CGL.Uniform(shader, "t", "texDiffuse", 0); + } + } + else + { + shader.removeUniform("texDiffuse"); + shader.removeDefine("HAS_TEXTURE_DIFFUSE"); + diffuseTextureUniform = null; + } +} + +function updateSpecularTexture() +{ + if (inSpecularTexture.get()) + { + inSpecularIntensity.setUiAttribs({ "greyout": false }); + if (!shader.hasDefine("HAS_TEXTURE_SPECULAR")) + { + shader.define("HAS_TEXTURE_SPECULAR"); + if (!specularTextureUniform) specularTextureUniform = new CGL.Uniform(shader, "t", "texSpecular", 0); + } + } + else + { + inSpecularIntensity.setUiAttribs({ "greyout": true }); + shader.removeUniform("texSpecular"); + shader.removeDefine("HAS_TEXTURE_SPECULAR"); + specularTextureUniform = null; + } +} + +function updateNormalTexture() +{ + if (inNormalTexture.get()) + { + inNormalIntensity.setUiAttribs({ "greyout": false }); + + if (!shader.hasDefine("HAS_TEXTURE_NORMAL")) + { + shader.define("HAS_TEXTURE_NORMAL"); + if (!normalTextureUniform) normalTextureUniform = new CGL.Uniform(shader, "t", "texNormal", 0); + } + } + else + { + inNormalIntensity.setUiAttribs({ "greyout": true }); + + shader.removeUniform("texNormal"); + shader.removeDefine("HAS_TEXTURE_NORMAL"); + normalTextureUniform = null; + } +} + +aoTextureUniform = new CGL.Uniform(shader, "t", "texAO"); + +function updateAoTexture() +{ + shader.toggleDefine("HAS_TEXTURE_AO", inAoTexture.get()); + + inAoIntensity.setUiAttribs({ "greyout": !inAoTexture.get() }); + + // if (inAoTexture.get()) + // { + // // inAoIntensity.setUiAttribs({ "greyout": false }); + + // // if (!shader.hasDefine("HAS_TEXTURE_AO")) + // // { + // // shader.define("HAS_TEXTURE_AO"); + // // if (!aoTextureUniform) + // aoTextureUniform = new CGL.Uniform(shader, "t", "texAO", 0); + // // } + // } + // else + // { + // // inAoIntensity.setUiAttribs({ "greyout": true }); + + // shader.removeUniform("texAO"); + // // shader.removeDefine("HAS_TEXTURE_AO"); + // aoTextureUniform = null; + // } +} + +function updateEmissiveTexture() +{ + if (inEmissiveTexture.get()) + { + inEmissiveR.setUiAttribs({ "greyout": true }); + inEmissiveG.setUiAttribs({ "greyout": true }); + inEmissiveB.setUiAttribs({ "greyout": true }); + inEmissiveColorIntensity.setUiAttribs({ "greyout": true }); + + if (inEmissiveActive.get()) + { + inEmissiveIntensity.setUiAttribs({ "greyout": false }); + } + + if (!shader.hasDefine("HAS_TEXTURE_EMISSIVE")) + { + shader.define("HAS_TEXTURE_EMISSIVE"); + if (!emissiveTextureUniform) emissiveTextureUniform = new CGL.Uniform(shader, "t", "texEmissive", 0); + } + } + else + { + inEmissiveIntensity.setUiAttribs({ "greyout": true }); + + if (inEmissiveActive.get()) + { + inEmissiveR.setUiAttribs({ "greyout": false }); + inEmissiveG.setUiAttribs({ "greyout": false }); + inEmissiveB.setUiAttribs({ "greyout": false }); + inEmissiveColorIntensity.setUiAttribs({ "greyout": false }); + } + else + { + inEmissiveTexture.setUiAttribs({ "greyout": true }); + } + + shader.removeUniform("texEmissive"); + shader.removeDefine("HAS_TEXTURE_EMISSIVE"); + emissiveTextureUniform = null; + } +} + +function updateEmissiveMaskTexture() +{ + if (inEmissiveMaskTexture.get()) + { // we have a emissive texture + if (inEmissiveActive.get()) + { + inEmissiveMaskIntensity.setUiAttribs({ "greyout": false }); + } + + if (!shader.hasDefine("HAS_TEXTURE_EMISSIVE_MASK")) + { + shader.define("HAS_TEXTURE_EMISSIVE_MASK"); + if (!emissiveMaskTextureUniform) emissiveMaskTextureUniform = new CGL.Uniform(shader, "t", "texMaskEmissive", 0); + if (!emissiveMaskIntensityUniform) emissiveMaskIntensityUniform = new CGL.Uniform(shader, "f", "inEmissiveMaskIntensity", inEmissiveMaskIntensity); + } + } + else + { + if (!inEmissiveActive.get()) + { + inEmissiveMaskTexture.setUiAttribs({ "greyout": true }); + } + inEmissiveMaskIntensity.setUiAttribs({ "greyout": true }); + shader.removeUniform("texMaskEmissive"); + shader.removeUniform("inEmissiveMaskIntensity"); + shader.removeDefine("HAS_TEXTURE_EMISSIVE_MASK"); + emissiveMaskTextureUniform = null; + emissiveMaskIntensityUniform = null; + } +} + +let updateEnvTextureLater = false; +function updateEnvTexture() +{ + shader.toggleDefine("HAS_TEXTURE_ENV", inEnvTexture.get()); + + inEnvMapIntensity.setUiAttribs({ "greyout": !inEnvTexture.get() }); + + if (inEnvTexture.get()) + { + if (!envTextureUniform) envTextureUniform = new CGL.Uniform(shader, "t", "texEnv", 0); + + shader.toggleDefine("TEX_FORMAT_CUBEMAP", inEnvTexture.get().cubemap); + + if (inEnvTexture.get().cubemap) + { + shader.removeDefine("TEX_FORMAT_EQUIRECT"); + shader.removeDefine("ENVMAP_MATCAP"); + if (!inEnvMapIntensityUni)inEnvMapIntensityUni = new CGL.Uniform(shader, "f", "inEnvMapIntensity", inEnvMapIntensity); + if (!inEnvMapWidthUni)inEnvMapWidthUni = new CGL.Uniform(shader, "f", "inEnvMapWidth", inEnvTexture.get().cubemap.width); + } + else + { + const isSquare = inEnvTexture.get().width === inEnvTexture.get().height; + shader.toggleDefine("TEX_FORMAT_EQUIRECT", !isSquare); + shader.toggleDefine("ENVMAP_MATCAP", isSquare); + + if (!inEnvMapIntensityUni)inEnvMapIntensityUni = new CGL.Uniform(shader, "f", "inEnvMapIntensity", inEnvMapIntensity); + if (!inEnvMapWidthUni) inEnvMapWidthUni = new CGL.Uniform(shader, "f", "inEnvMapWidth", inEnvTexture.get().width); + } + } + else + { + shader.removeUniform("inEnvMapIntensity"); + shader.removeUniform("inEnvMapWidth"); + shader.removeUniform("texEnv"); + shader.removeDefine("HAS_TEXTURE_ENV"); + shader.removeDefine("ENVMAP_MATCAP"); + envTextureUniform = null; + inEnvMapIntensityUni = null; + } + + updateEnvTextureLater = false; +} + +function updateLuminanceMaskTexture() +{ + if (inLuminanceMaskTexture.get()) + { + inLuminanceMaskIntensity.setUiAttribs({ "greyout": false }); + if (!luminanceTextureUniform) + { + shader.define("HAS_TEXTURE_LUMINANCE_MASK"); + luminanceTextureUniform = new CGL.Uniform(shader, "t", "texLuminance", 0); + inLuminanceMaskIntensityUniform = new CGL.Uniform(shader, "f", "inLuminanceMaskIntensity", inLuminanceMaskIntensity); + } + } + else + { + inLuminanceMaskIntensity.setUiAttribs({ "greyout": true }); + shader.removeDefine("HAS_TEXTURE_LUMINANCE_MASK"); + shader.removeUniform("inLuminanceMaskIntensity"); + shader.removeUniform("texLuminance"); + luminanceTextureUniform = null; + inLuminanceMaskIntensityUniform = null; + } +} + +// TEX OPACITY + +function updateDefines() +{ + shader.toggleDefine("ENV_BLEND_ADD", inEnvMapBlend.get() == "Add"); + shader.toggleDefine("ENV_BLEND_MUL", inEnvMapBlend.get() == "Multiply"); + shader.toggleDefine("ENV_BLEND_MIX", inEnvMapBlend.get() == "Mix"); + + shader.toggleDefine("ALPHA_MASK_ALPHA", alphaMaskSource.get() == "Alpha Channel"); + shader.toggleDefine("ALPHA_MASK_LUMI", alphaMaskSource.get() == "Luminance"); + shader.toggleDefine("ALPHA_MASK_R", alphaMaskSource.get() == "R"); + shader.toggleDefine("ALPHA_MASK_G", alphaMaskSource.get() == "G"); + shader.toggleDefine("ALPHA_MASK_B", alphaMaskSource.get() == "B"); +} + +inEnvMapBlend.onChange = updateDefines; +alphaMaskSource.onChange = updateDefines; + +function updateAlphaTexture() +{ + if (inAlphaTexture.get()) + { + if (alphaTextureUniform !== null) return; + shader.removeUniform("texAlpha"); + shader.define("HAS_TEXTURE_ALPHA"); + if (!alphaTextureUniform) alphaTextureUniform = new CGL.Uniform(shader, "t", "texAlpha", 0); + + alphaMaskSource.setUiAttribs({ "greyout": false }); + discardTransPxl.setUiAttribs({ "greyout": false }); + } + else + { + shader.removeUniform("texAlpha"); + shader.removeDefine("HAS_TEXTURE_ALPHA"); + alphaTextureUniform = null; + + alphaMaskSource.setUiAttribs({ "greyout": true }); + discardTransPxl.setUiAttribs({ "greyout": true }); + } + updateDefines(); +} + +discardTransPxl.onChange = function () +{ + shader.toggleDefine("DISCARDTRANS", discardTransPxl.get()); +}; + +inDiffuseTexture.onChange = updateDiffuseTexture; +inSpecularTexture.onChange = updateSpecularTexture; +inNormalTexture.onChange = updateNormalTexture; +inAoTexture.onChange = updateAoTexture; +inEmissiveTexture.onChange = updateEmissiveTexture; +inEmissiveMaskTexture.onChange = updateEmissiveMaskTexture; +inAlphaTexture.onChange = updateAlphaTexture; +inEnvTexture.onChange = () => { updateEnvTextureLater = true; }; +inLuminanceMaskTexture.onChange = updateLuminanceMaskTexture; + +const MAX_UNIFORM_FRAGMENTS = cgl.maxUniformsFrag; +const MAX_LIGHTS = MAX_UNIFORM_FRAGMENTS === 64 ? 6 : 16; + +shader.define("MAX_LIGHTS", MAX_LIGHTS.toString()); +shader.define("SPECULAR_PHONG"); + +inSpecularMode.onChange = function () +{ + if (inSpecularMode.get() === "Phong") + { + shader.define("SPECULAR_PHONG"); + shader.removeDefine("SPECULAR_BLINN"); + shader.removeDefine("SPECULAR_GAUSS"); + shader.removeDefine("SPECULAR_SCHLICK"); + } + else if (inSpecularMode.get() === "Blinn") + { + shader.define("SPECULAR_BLINN"); + shader.removeDefine("SPECULAR_PHONG"); + shader.removeDefine("SPECULAR_GAUSS"); + shader.removeDefine("SPECULAR_SCHLICK"); + } + else if (inSpecularMode.get() === "Gauss") + { + shader.define("SPECULAR_GAUSS"); + shader.removeDefine("SPECULAR_BLINN"); + shader.removeDefine("SPECULAR_PHONG"); + shader.removeDefine("SPECULAR_SCHLICK"); + } + else if (inSpecularMode.get() === "Schlick") + { + shader.define("SPECULAR_SCHLICK"); + shader.removeDefine("SPECULAR_BLINN"); + shader.removeDefine("SPECULAR_PHONG"); + shader.removeDefine("SPECULAR_GAUSS"); + } +}; + +inEnergyConservation.onChange = function () +{ + shader.toggleDefine("CONSERVE_ENERGY", inEnergyConservation.get()); +}; + +inToggleDoubleSided.onChange = function () +{ + shader.toggleDefine("DOUBLE_SIDED", inToggleDoubleSided.get()); +}; + +// * INIT UNIFORMS * + +const uniMaterialProps = new CGL.Uniform(shader, "4f", "inMaterialProperties", inAlbedo, inRoughness, inShininess, inSpecularCoefficient); +const uniDiffuseColor = new CGL.Uniform(shader, "4f", "inDiffuseColor", inDiffuseR, inDiffuseG, inDiffuseB, inDiffuseA); +const uniTextureIntensities = new CGL.Uniform(shader, "4f", "inTextureIntensities", inNormalIntensity, inAoIntensity, inSpecularIntensity, inEmissiveIntensity); +const uniTextureRepeatOffset = new CGL.Uniform(shader, "4f", "inTextureRepeatOffset", inDiffuseRepeatX, inDiffuseRepeatY, inTextureOffsetX, inTextureOffsetY); + +shader.uniformColorDiffuse = uniDiffuseColor; + +const lightUniforms = []; +let oldCount = 0; + +function createUniforms(lightsCount) +{ + for (let i = 0; i < lightUniforms.length; i += 1) + { + lightUniforms[i] = null; + } + + for (let i = 0; i < lightsCount; i += 1) + { + lightUniforms[i] = null; + if (!lightUniforms[i]) + { + lightUniforms[i] = { + "color": new CGL.Uniform(shader, "3f", "phongLight" + i + ".color", [1, 1, 1]), + "position": new CGL.Uniform(shader, "3f", "phongLight" + i + ".position", [0, 11, 0]), + "specular": new CGL.Uniform(shader, "3f", "phongLight" + i + ".specular", [1, 1, 1]), + // intensity, attenuation, falloff, radius + "lightProperties": new CGL.Uniform(shader, "4f", "phongLight" + i + ".lightProperties", [1, 1, 1, 1]), + + "conePointAt": new CGL.Uniform(shader, "3f", "phongLight" + i + ".conePointAt", vec3.create()), + "spotProperties": new CGL.Uniform(shader, "3f", "phongLight" + i + ".spotProperties", [0, 0, 0, 0]), + "castLight": new CGL.Uniform(shader, "i", "phongLight" + i + ".castLight", 1), + + }; + } + } +} + +function setDefaultUniform(light) +{ + defaultUniform.position.setValue(light.position); + defaultUniform.color.setValue(light.color); + defaultUniform.specular.setValue(light.specular); + defaultUniform.lightProperties.setValue([ + light.intensity, + light.attenuation, + light.falloff, + light.radius, + ]); + + defaultUniform.conePointAt.setValue(light.conePointAt); + defaultUniform.spotProperties.setValue([ + light.cosConeAngle, + light.cosConeAngleInner, + light.spotExponent, + ]); +} + +function setUniforms(lightStack) +{ + for (let i = 0; i < lightStack.length; i += 1) + { + const light = lightStack[i]; + light.isUsed = true; + + lightUniforms[i].position.setValue(light.position); + lightUniforms[i].color.setValue(light.color); + lightUniforms[i].specular.setValue(light.specular); + + lightUniforms[i].lightProperties.setValue([ + light.intensity, + light.attenuation, + light.falloff, + light.radius, + ]); + + lightUniforms[i].conePointAt.setValue(light.conePointAt); + lightUniforms[i].spotProperties.setValue([ + light.cosConeAngle, + light.cosConeAngleInner, + light.spotExponent, + ]); + + lightUniforms[i].castLight.setValue(light.castLight); + } +} + +function compareLights(lightStack) +{ + if (lightStack.length !== oldCount) + { + createShader(lightStack); + createUniforms(lightStack.length); + oldCount = lightStack.length; + setUniforms(lightStack); + recompileShader = false; + } + else + { + if (recompileShader) + { + createShader(lightStack); + createUniforms(lightStack.length); + recompileShader = false; + } + setUniforms(lightStack); + } +} + +let defaultUniform = null; + +function createDefaultUniform() +{ + defaultUniform = { + "color": new CGL.Uniform(shader, "3f", "phongLight" + 0 + ".color", [1, 1, 1]), + "specular": new CGL.Uniform(shader, "3f", "phongLight" + 0 + ".specular", [1, 1, 1]), + "position": new CGL.Uniform(shader, "3f", "phongLight" + 0 + ".position", [0, 11, 0]), + // intensity, attenuation, falloff, radius + "lightProperties": new CGL.Uniform(shader, "4f", "phongLight" + 0 + ".lightProperties", [1, 1, 1, 1]), + "conePointAt": new CGL.Uniform(shader, "3f", "phongLight" + 0 + ".conePointAt", vec3.create()), + "spotProperties": new CGL.Uniform(shader, "3f", "phongLight" + 0 + ".spotProperties", [0, 0, 0, 0]), + "castLight": new CGL.Uniform(shader, "i", "phongLight" + 0 + ".castLight", 1), + }; +} + +const DEFAULT_LIGHTSTACK = [{ + "type": "point", + "position": [5, 5, 5], + "color": [1, 1, 1], + "specular": [1, 1, 1], + "intensity": 1, + "attenuation": 0, + "falloff": 0.5, + "radius": 80, + "castLight": 1, +}]; + +const iViewMatrix = mat4.create(); + +function updateLights() +{ + if (cgl.frameStore.lightStack) + { + if (cgl.frameStore.lightStack.length === 0) + { + op.setUiError("deflight", "Default light is enabled. Please add lights to your patch to make this warning disappear.", 1); + } + else op.setUiError("deflight", null); + } + + if ((!cgl.frameStore.lightStack || !cgl.frameStore.lightStack.length)) + { + // if no light in light stack, use default light & set count to -1 + // so when a new light gets added, the shader does recompile + if (!defaultUniform) + { + createDefaultShader(); + createDefaultUniform(); + } + + mat4.invert(iViewMatrix, cgl.vMatrix); + // set default light position to camera position + DEFAULT_LIGHTSTACK[0].position = [iViewMatrix[12], iViewMatrix[13], iViewMatrix[14]]; + setDefaultUniform(DEFAULT_LIGHTSTACK[0]); + + oldCount = -1; + } + else + { + if (shader) + { + if (cgl.frameStore.lightStack) + { + if (cgl.frameStore.lightStack.length) + { + defaultUniform = null; + compareLights(cgl.frameStore.lightStack); + } + } + } + } +} + +const render = function () +{ + if (!shader) + { + op.log("NO SHADER"); + return; + } + + cgl.pushShader(shader); + shader.popTextures(); + + outTrigger.trigger(); + cgl.popShader(); +}; + +op.preRender = function () +{ + shader.bind(); + render(); +}; + +/* transform for default light */ +const inverseViewMat = mat4.create(); +const vecTemp = vec3.create(); +const camPos = vec3.create(); + +inTrigger.onTriggered = function () +{ + if (!shader) + { + op.log("phong has no shader..."); + return; + } + + if (updateEnvTextureLater)updateEnvTexture(); + + cgl.pushShader(shader); + + shader.popTextures(); + + if (inDiffuseTexture.get()) shader.pushTexture(diffuseTextureUniform, inDiffuseTexture.get()); + if (inSpecularTexture.get()) shader.pushTexture(specularTextureUniform, inSpecularTexture.get()); + if (inNormalTexture.get()) shader.pushTexture(normalTextureUniform, inNormalTexture.get()); + if (inAoTexture.get()) shader.pushTexture(aoTextureUniform, inAoTexture.get()); + if (inEmissiveTexture.get()) shader.pushTexture(emissiveTextureUniform, inEmissiveTexture.get()); + if (inEmissiveMaskTexture.get()) shader.pushTexture(emissiveMaskTextureUniform, inEmissiveMaskTexture.get()); + if (inAlphaTexture.get()) shader.pushTexture(alphaTextureUniform, inAlphaTexture.get()); + if (inEnvTexture.get()) + { + if (inEnvTexture.get().cubemap) shader.pushTexture(envTextureUniform, inEnvTexture.get().cubemap, cgl.gl.TEXTURE_CUBE_MAP); + else shader.pushTexture(envTextureUniform, inEnvTexture.get()); + } + + if (inLuminanceMaskTexture.get()) + { + shader.pushTexture(luminanceTextureUniform, inLuminanceMaskTexture.get()); + } + + updateLights(); + + outTrigger.trigger(); + + cgl.popShader(); +}; + +if (cgl.glVersion == 1) +{ + if (!cgl.gl.getExtension("EXT_shader_texture_lod")) + { + op.log("no EXT_shader_texture_lod texture extension"); + // throw "no EXT_shader_texture_lod texture extension"; + } + else + { + shader.enableExtension("GL_EXT_shader_texture_lod"); + cgl.gl.getExtension("OES_texture_float"); + cgl.gl.getExtension("OES_texture_float_linear"); + cgl.gl.getExtension("OES_texture_half_float"); + cgl.gl.getExtension("OES_texture_half_float_linear"); + + shader.enableExtension("GL_OES_standard_derivatives"); + shader.enableExtension("GL_OES_texture_float"); + shader.enableExtension("GL_OES_texture_float_linear"); + shader.enableExtension("GL_OES_texture_half_float"); + shader.enableExtension("GL_OES_texture_half_float_linear"); + } +} + +updateDiffuseTexture(); +updateSpecularTexture(); +updateNormalTexture(); +updateAoTexture(); +updateAlphaTexture(); +updateEmissiveTexture(); +updateEmissiveMaskTexture(); +updateEnvTexture(); +updateLuminanceMaskTexture(); + + +}; + +Ops.Gl.Phong.PhongMaterial_v6.prototype = new CABLES.Op(); +CABLES.OPS["0d83ed06-cdbe-4fe0-87bb-0ccece7fb6e1"]={f:Ops.Gl.Phong.PhongMaterial_v6,objName:"Ops.Gl.Phong.PhongMaterial_v6"}; + + + + +// ************************************************************** +// +// Ops.Gl.Phong.PointLight_v5 +// +// ************************************************************** + +Ops.Gl.Phong.PointLight_v5 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; +const gl = cgl.gl; +const mesh = CGL.MESHES.getSimpleRect(cgl, "fullscreenRectangle"); + +function Light(config) +{ + this.type = config.type || "point"; + this.color = config.color || [1, 1, 1]; + this.specular = config.specular || [0, 0, 0]; + this.position = config.position || null; + this.intensity = config.intensity || 1; + this.radius = config.radius || 1; + this.falloff = config.falloff || 1; + this.spotExponent = config.spotExponent || 1; + this.cosConeAngle = Math.cos(CGL.DEG2RAD * this.coneAngle); + this.conePointAt = config.conePointAt || [0, 0, 0]; + this.castShadow = config.castShadow || false; + return this; +} + +// * OP START * +const inTrigger = op.inTrigger("Trigger In"); +const inCastLight = op.inBool("Cast Light", true); +const inIntensity = op.inFloat("Intensity", 2); +const inRadius = op.inFloat("Radius", 15); + +const inPosX = op.inFloat("X", 0); +const inPosY = op.inFloat("Y", 2); +const inPosZ = op.inFloat("Z", 0.75); + +const positionIn = [inPosX, inPosY, inPosZ]; +op.setPortGroup("Position", positionIn); + +const inR = op.inFloat("R", 0.8); +const inG = op.inFloat("G", 0.8); +const inB = op.inFloat("B", 0.8); + +inR.setUiAttribs({ "colorPick": true }); +const colorIn = [inR, inG, inB]; +op.setPortGroup("Color", colorIn); + +const inSpecularR = op.inFloat("Specular R", 1); +const inSpecularG = op.inFloat("Specular G", 1); +const inSpecularB = op.inFloat("Specular B", 1); + +inSpecularR.setUiAttribs({ "colorPick": true }); +const colorSpecularIn = [inSpecularR, inSpecularG, inSpecularB]; +op.setPortGroup("Specular Color", colorSpecularIn); + +const inFalloff = op.inFloatSlider("Falloff", 0.5); + +const attribIns = [inIntensity, inCastLight, inRadius]; +op.setPortGroup("Light Attributes", attribIns); + +const inCastShadow = op.inBool("Cast Shadow", false); +const inRenderMapActive = op.inBool("Rendering Active", true); +const inMapSize = op.inSwitch("Map Size", [256, 512, 1024, 2048], 512); +const inShadowStrength = op.inFloatSlider("Shadow Strength", 1); +const inNear = op.inFloat("Near", 0.1); +const inFar = op.inFloat("Far", 30); +const inBias = op.inFloatSlider("Bias", 0.004); +const inPolygonOffset = op.inInt("Polygon Offset", 0); +op.setPortGroup("", [inCastShadow]); +op.setPortGroup("Shadow Map Settings", [inMapSize, inRenderMapActive, inShadowStrength, inNear, inFar, inBias, inPolygonOffset]); +const shadowProperties = [inNear, inFar]; +inMapSize.setUiAttribs({ "greyout": !inCastShadow.get() }); +inRenderMapActive.setUiAttribs({ "greyout": !inCastShadow.get() }); +inShadowStrength.setUiAttribs({ "greyout": !inCastShadow.get() }); +inNear.setUiAttribs({ "greyout": !inCastShadow.get() }); +inBias.setUiAttribs({ "greyout": !inCastShadow.get() }); +inFar.setUiAttribs({ "greyout": !inCastShadow.get() }); +inPolygonOffset.setUiAttribs({ "greyout": !inCastShadow.get() }); + +let updating = false; + +inCastShadow.onChange = function () +{ + updating = true; + updateLight = true; + + inMapSize.setUiAttribs({ "greyout": !inCastShadow.get() }); + inRenderMapActive.setUiAttribs({ "greyout": !inCastShadow.get() }); + inShadowStrength.setUiAttribs({ "greyout": !inCastShadow.get() }); + inNear.setUiAttribs({ "greyout": !inCastShadow.get() }); + inFar.setUiAttribs({ "greyout": !inCastShadow.get() }); + inBias.setUiAttribs({ "greyout": !inCastShadow.get() }); + inPolygonOffset.setUiAttribs({ "greyout": !inCastShadow.get() }); +}; + +const outTrigger = op.outTrigger("Trigger Out"); +const outCubemap = op.outObject("Cubemap", null, "texture"); +const outWorldPosX = op.outNumber("World Position X"); +const outWorldPosY = op.outNumber("World Position Y"); +const outWorldPosZ = op.outNumber("World Position Z"); + +const newLight = new CGL.Light(cgl, { + "type": "point", + "position": [0, 1, 2].map(function (i) { return positionIn[i].get(); }), + "color": [0, 1, 2].map(function (i) { return colorIn[i].get(); }), + "specular": [0, 1, 2].map(function (i) { return colorSpecularIn[i].get(); }), + "intensity": inIntensity.get(), + "radius": inRadius.get(), + "falloff": inFalloff.get(), + "shadowStrength": inShadowStrength.get(), + "shadowBias": inBias.get() +}); +newLight.castLight = inCastLight.get(); + +let updateLight = false; + +inPosX.onChange = inPosY.onChange = inPosZ.onChange = inR.onChange = inG.onChange = inB.onChange += inSpecularR.onChange = inSpecularG.onChange = inSpecularB.onChange = inIntensity.onChange += inCastLight.onChange = inRadius.onChange = inFalloff.onChange = inNear.onChange = inFar.onChange = inShadowStrength.onChange = function () + { + updateLight = true; + }; + +inMapSize.onChange = function () +{ + // TODO: update this one + updating = true; +}; + +function updateShadowMapFramebuffer() +{ + if (inCastShadow.get()) + { + const size = inMapSize.get(); + newLight.createFramebuffer(size, size, {}); + newLight.createShadowMapShader(); + } + updating = false; +} + +const sc = vec3.create(); +const result = vec3.create(); +const position = vec3.create(); +const transVec = vec3.create(); + +function drawHelpers() +{ + if (cgl.frameStore.shadowPass) return; + if (cgl.shouldDrawHelpers(op)) + { + gui.setTransformGizmo({ + "posX": inPosX, + "posY": inPosY, + "posZ": inPosZ, + }); + + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, transVec); + CABLES.GL_MARKER.drawSphere(op, inRadius.get()); + cgl.popModelMatrix(); + } +} + +let errorActive = false; + +inTrigger.onTriggered = function () +{ + if (updating) + { + if (cgl.frameStore.shadowPass) return; + updateShadowMapFramebuffer(); + } + + if (!cgl.frameStore.shadowPass) + { + if (!newLight.isUsed && !errorActive) + { + op.setUiError("lightUsed", "No operator is using this light. Make sure this op is positioned before an operator that uses lights. Also make sure there is an operator that uses lights after this.", 1); // newLight.isUsed = false; + errorActive = true; + } + else if (!newLight.isUsed && errorActive) {} + else if (newLight.isUsed && errorActive) + { + op.setUiError("lightUsed", null); + errorActive = false; + } + else if (newLight.isUsed && !errorActive) {} + + newLight.isUsed = false; + } + + if (updateLight) + { + newLight.position = [0, 1, 2].map(function (i) { return positionIn[i].get(); }); + newLight.color = [0, 1, 2].map(function (i) { return colorIn[i].get(); }); + newLight.specular = [0, 1, 2].map(function (i) { return colorSpecularIn[i].get(); }); + newLight.intensity = inIntensity.get(); + newLight.radius = inRadius.get(); + newLight.falloff = inFalloff.get(); + newLight.castShadow = inCastShadow.get(); + newLight.castLight = inCastLight.get(); + newLight.updateProjectionMatrix(null, inNear.get(), inFar.get(), null); + updateLight = false; + } + + if (!cgl.frameStore.lightStack) cgl.frameStore.lightStack = []; + + vec3.set(transVec, inPosX.get(), inPosY.get(), inPosZ.get()); + vec3.transformMat4(position, transVec, cgl.mMatrix); + + newLight.position = position; + + outWorldPosX.set(newLight.position[0]); + outWorldPosY.set(newLight.position[1]); + outWorldPosZ.set(newLight.position[2]); + + if (!cgl.frameStore.shadowPass) drawHelpers(); + + cgl.frameStore.lightStack.push(newLight); + + if (inCastShadow.get()) + { + if (inRenderMapActive.get()) newLight.renderPasses(inPolygonOffset.get(), null, function () { outTrigger.trigger(); }); + + if (!cgl.frameStore.shadowPass) + { + cgl.frameStore.lightStack.pop(); + newLight.castShadow = inCastShadow.get(); + newLight.shadowBias = inBias.get(); + newLight.shadowStrength = inShadowStrength.get(); + + if (newLight.shadowCubeMap) + { + if (newLight.shadowCubeMap.cubemap) + { + outCubemap.set(null); + outCubemap.set(newLight.shadowCubeMap); + if (inRenderMapActive.get()) + { + // needs to be "cloned", cannot save reference. + newLight.positionForShadowMap = [newLight.position[0], newLight.position[1], newLight.position[2]]; + } + } + } + cgl.frameStore.lightStack.push(newLight); + } + } + else + { + outCubemap.set(null); + } + + outTrigger.trigger(); + cgl.frameStore.lightStack.pop(); +}; + + +}; + +Ops.Gl.Phong.PointLight_v5.prototype = new CABLES.Op(); +CABLES.OPS["54e5d3f5-e3f4-4381-990d-d5e32b9a2d39"]={f:Ops.Gl.Phong.PointLight_v5,objName:"Ops.Gl.Phong.PointLight_v5"}; + + + + +// ************************************************************** +// +// Ops.Gl.Phong.ResetLights +// +// ************************************************************** + +Ops.Gl.Phong.ResetLights = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; +const inTrigger = op.inTrigger("Trigger In"); +const inBool = op.inBool("Reset Lights", true); +const outTrigger = op.outTrigger("Trigger Out"); + +inTrigger.onTriggered = () => +{ + if (inBool.get()) + { + const oldStack = cgl.frameStore.lightStack; + cgl.frameStore.lightStack = []; + outTrigger.trigger(); + cgl.frameStore.lightStack = oldStack; + return; + } + + outTrigger.trigger(); +}; + + +}; + +Ops.Gl.Phong.ResetLights.prototype = new CABLES.Op(); +CABLES.OPS["3954ca2b-56c8-4501-9b47-feeaba71d75f"]={f:Ops.Gl.Phong.ResetLights,objName:"Ops.Gl.Phong.ResetLights"}; + + + + +// ************************************************************** +// +// Ops.Gl.Phong.SpotLight_v5 +// +// ************************************************************** + +Ops.Gl.Phong.SpotLight_v5 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +// * OP START * +const inTrigger = op.inTrigger("Trigger In"); + +const inCastLight = op.inBool("Cast Light", true); +const inIntensity = op.inFloat("Intensity", 2); +const inRadius = op.inFloat("Radius", 10); + +const inPosX = op.inFloat("X", 1); +const inPosY = op.inFloat("Y", 3); +const inPosZ = op.inFloat("Z", 1); + +const positionIn = [inPosX, inPosY, inPosZ]; +op.setPortGroup("Position", positionIn); + +const inPointAtX = op.inFloat("Point At X", 0); +const inPointAtY = op.inFloat("Point At Y", 0); +const inPointAtZ = op.inFloat("Point At Z", 0); +const pointAtIn = [inPointAtX, inPointAtY, inPointAtZ]; +op.setPortGroup("Point At", pointAtIn); + +const inR = op.inFloatSlider("R", 1); +const inG = op.inFloatSlider("G", 1); +const inB = op.inFloatSlider("B", 1); +inR.setUiAttribs({ "colorPick": true }); +const colorIn = [inR, inG, inB]; +op.setPortGroup("Color", colorIn); + +const inSpecularR = op.inFloatSlider("Specular R", 1); +const inSpecularG = op.inFloatSlider("Specular G", 1); +const inSpecularB = op.inFloatSlider("Specular B", 1); +inSpecularR.setUiAttribs({ "colorPick": true }); +const colorSpecularIn = [inSpecularR, inSpecularG, inSpecularB]; +op.setPortGroup("Specular Color", colorSpecularIn); + +const inConeAngle = op.inFloat("Cone Angle", 120); +const inConeAngleInner = op.inFloat("Inner Cone Angle", 60); +const inSpotExponent = op.inFloat("Spot Exponent", 0.97); +const coneAttribsIn = [inConeAngle, inConeAngleInner, inSpotExponent]; +op.setPortGroup("Cone Attributes", coneAttribsIn); + +const inFalloff = op.inFloatSlider("Falloff", 0.00001); +const lightAttribsIn = [inCastLight, inIntensity, inRadius]; +op.setPortGroup("Light Attributes", lightAttribsIn); + +const inCastShadow = op.inBool("Cast Shadow", false); +const inRenderMapActive = op.inBool("Rendering Active", true); +const inMapSize = op.inSwitch("Map Size", [256, 512, 1024, 2048], 512); +const inShadowStrength = op.inFloatSlider("Shadow Strength", 0.99); +const inNear = op.inFloat("Near", 0.1); +const inFar = op.inFloat("Far", 30); +const inBias = op.inFloatSlider("Bias", 0.0001); +const inPolygonOffset = op.inInt("Polygon Offset", 0); +const inNormalOffset = op.inFloatSlider("Normal Offset", 0); +const inBlur = op.inFloatSlider("Blur Amount", 0); +op.setPortGroup("", [inCastShadow]); +op.setPortGroup("Shadow Map Settings", [ + inMapSize, + inRenderMapActive, + inShadowStrength, + inNear, + inFar, + inBias, + inPolygonOffset, + inNormalOffset, + inBlur +]); + +inMapSize.setUiAttribs({ "greyout": true, "hidePort": true }); +inRenderMapActive.setUiAttribs({ "greyout": true }); +inShadowStrength.setUiAttribs({ "greyout": true }); +inNear.setUiAttribs({ "greyout": true, "hidePort": true }); +inFar.setUiAttribs({ "greyout": true, "hidePort": true }); +inBlur.setUiAttribs({ "greyout": true, "hidePort": true }); +inPolygonOffset.setUiAttribs({ "greyout": true, "hidePort": true }); +inNormalOffset.setUiAttribs({ "greyout": true, "hidePort": true }); +inBias.setUiAttribs({ "greyout": true, "hidePort": true }); + +const inAdvanced = op.inBool("Enable Advanced", false); +const inMSAA = op.inSwitch("MSAA", ["none", "2x", "4x", "8x"], "none"); +const inFilterType = op.inSwitch("Texture Filter", ["Linear", "Nearest", "Mip Map"], "Linear"); +const inAnisotropic = op.inSwitch("Anisotropic", [0, 1, 2, 4, 8, 16], "0"); +inMSAA.setUiAttribs({ "greyout": true, "hidePort": true }); +inFilterType.setUiAttribs({ "greyout": true, "hidePort": true }); +inAnisotropic.setUiAttribs({ "greyout": true, "hidePort": true }); +op.setPortGroup("Advanced Options", [inAdvanced, inMSAA, inFilterType, inAnisotropic]); + +let updating = false; + +inAdvanced.setUiAttribs({ "hidePort": true }); + +inAdvanced.onChange = function () +{ + inMSAA.setUiAttribs({ "greyout": !inAdvanced.get() }); + inFilterType.setUiAttribs({ "greyout": !inAdvanced.get() }); + inAnisotropic.setUiAttribs({ "greyout": !inAdvanced.get() }); +}; + +const outTrigger = op.outTrigger("Trigger Out"); +const outTexture = op.outTexture("Shadow Map"); +const outWorldPosX = op.outNumber("World Position X"); +const outWorldPosY = op.outNumber("World Position Y"); +const outWorldPosZ = op.outNumber("World Position Z"); + +const newLight = new CGL.Light(cgl, { + "type": "spot", + "position": [0, 1, 2].map(function (i) { return positionIn[i].get(); }), + "color": [0, 1, 2].map(function (i) { return colorIn[i].get(); }), + "specular": [0, 1, 2].map(function (i) { return colorSpecularIn[i].get(); }), + "conePointAt": [0, 1, 2].map(function (i) { return pointAtIn[i].get(); }), + "intensity": inIntensity.get(), + "radius": inRadius.get(), + "falloff": inFalloff.get(), + "cosConeAngleInner": Math.cos(CGL.DEG2RAD * inConeAngleInner.get()), + "cosConeAngle": Math.cos(CGL.DEG2RAD * inConeAngle.get()), + "spotExponent": inSpotExponent.get(), + "castShadow": false, + "shadowStrength": inShadowStrength.get(), + "shadowBias": inBias.get(), + "normalOffset": inNormalOffset.get(), +}); +newLight.castLight = inCastLight.get(); + +let updateLight = false; +inR.onChange = inG.onChange = inB.onChange = inSpecularR.onChange = inSpecularG.onChange = inSpecularB.onChange += inPointAtX.onChange = inPointAtY.onChange = inPointAtZ.onChange = inPosX.onChange = inPosY.onChange = inPosZ.onChange; +inCastLight.onChange = inIntensity.onChange = inRadius.onChange = inFalloff.onChange = inConeAngle.onChange = inConeAngleInner.onChange += inSpotExponent.onChange = inShadowStrength.onChange = inNear.onChange = inFar.onChange = updateLightParameters; + +function updateLightParameters() +{ + updateLight = true; +} + +inCastShadow.onChange = function () +{ + updating = true; + const castShadow = inCastShadow.get(); + + inMapSize.setUiAttribs({ "greyout": !castShadow }); + inRenderMapActive.setUiAttribs({ "greyout": !castShadow }); + inShadowStrength.setUiAttribs({ "greyout": !castShadow }); + inNear.setUiAttribs({ "greyout": !castShadow }); + inFar.setUiAttribs({ "greyout": !castShadow }); + inNormalOffset.setUiAttribs({ "greyout": !castShadow }); + inBlur.setUiAttribs({ "greyout": !castShadow }); + inBias.setUiAttribs({ "greyout": !castShadow }); + inPolygonOffset.setUiAttribs({ "greyout": !castShadow }); + + updateLight = true; +}; + +let texelSize = 1 / Number(inMapSize.get()); + +function updateBuffers() +{ + const MSAA = Number(inMSAA.get().charAt(0)); + + let filterType = null; + const anisotropyFactor = Number(inAnisotropic.get()); + + if (inFilterType.get() == "Linear") + { + filterType = CGL.Texture.FILTER_LINEAR; + } + else if (inFilterType.get() == "Nearest") + { + filterType = CGL.Texture.FILTER_NEAREST; + } + else if (inFilterType.get() == "Mip Map") + { + filterType = CGL.Texture.FILTER_MIPMAP; + } + + const mapSize = Number(inMapSize.get()); + const textureOptions = { + "isFloatingPointTexture": true, + "filter": filterType, + }; + + if (MSAA) Object.assign(textureOptions, { "multisampling": true, "multisamplingSamples": MSAA }); + Object.assign(textureOptions, { "anisotropic": anisotropyFactor }); + + newLight.createFramebuffer(mapSize, mapSize, textureOptions); + newLight.createBlurEffect(textureOptions); +} + +inMSAA.onChange = inAnisotropic.onChange = inFilterType.onChange = inMapSize.onChange = function () +{ + updating = true; +}; + +function updateShadowMapFramebuffer() +{ + const size = Number(inMapSize.get()); + texelSize = 1 / size; + + if (inCastShadow.get()) + { + newLight.createFramebuffer(Number(inMapSize.get()), Number(inMapSize.get()), {}); + newLight.createShadowMapShader(); + newLight.createBlurEffect({}); + newLight.createBlurShader(); + newLight.updateProjectionMatrix(null, inNear.get(), inFar.get(), inConeAngle.get()); + } + + if (inAdvanced.get()) updateBuffers(); + + updating = false; +} + +const position = vec3.create(); +const pointAtPos = vec3.create(); +const resultPos = vec3.create(); +const resultPointAt = vec3.create(); + +function drawHelpers() +{ + if (cgl.frameStore.shadowPass) return; + if (cgl.shouldDrawHelpers(op)) + { + gui.setTransformGizmo({ + "posX": inPosX, + "posY": inPosY, + "posZ": inPosZ, + }); + + CABLES.GL_MARKER.drawLineSourceDest( + op, + newLight.position[0], + newLight.position[1], + newLight.position[2], + newLight.conePointAt[0], + newLight.conePointAt[1], + newLight.conePointAt[2], + ); + } +} + +let errorActive = false; +inTrigger.onTriggered = renderLight; + +op.preRender = () => +{ + updateShadowMapFramebuffer(); + renderLight(); +}; + +function renderLight() +{ + if (updating) + { + if (cgl.frameStore.shadowPass) return; + updateShadowMapFramebuffer(); + } + + if (!cgl.frameStore.shadowPass) + { + if (!newLight.isUsed && !errorActive) + { + op.setUiError("lightUsed", "No operator is using this light. Make sure this op is positioned before an operator that uses lights. Also make sure there is an operator that uses lights after this.", 1); // newLight.isUsed = false; + errorActive = true; + } + else if (!newLight.isUsed && errorActive) {} + else if (newLight.isUsed && errorActive) + { + op.setUiError("lightUsed", null); + errorActive = false; + } + else if (newLight.isUsed && !errorActive) {} + newLight.isUsed = false; + } + + if (updateLight) + { + newLight.position = [0, 1, 2].map(function (i) { return positionIn[i].get(); }); + newLight.color = [0, 1, 2].map(function (i) { return colorIn[i].get(); }); + newLight.specular = [0, 1, 2].map(function (i) { return colorSpecularIn[i].get(); }); + newLight.conePointAt = [0, 1, 2].map(function (i) { return pointAtIn[i].get(); }); + newLight.intensity = inIntensity.get(); + newLight.castLight = inCastLight.get(); + newLight.radius = inRadius.get(); + newLight.falloff = inFalloff.get(); + newLight.cosConeAngleInner = Math.cos(CGL.DEG2RAD * inConeAngleInner.get()); + newLight.cosConeAngle = Math.cos(CGL.DEG2RAD * inConeAngle.get()); + newLight.spotExponent = inSpotExponent.get(); + newLight.castShadow = inCastShadow.get(); + newLight.updateProjectionMatrix(null, inNear.get(), inFar.get(), inConeAngle.get()); + } + + if (!cgl.frameStore.lightStack) cgl.frameStore.lightStack = []; + + vec3.set(position, inPosX.get(), inPosY.get(), inPosZ.get()); + vec3.set(pointAtPos, inPointAtX.get(), inPointAtY.get(), inPointAtZ.get()); + + vec3.transformMat4(resultPos, position, cgl.mMatrix); + vec3.transformMat4(resultPointAt, pointAtPos, cgl.mMatrix); + + newLight.position = resultPos; + newLight.conePointAt = resultPointAt; + + outWorldPosX.set(newLight.position[0]); + outWorldPosY.set(newLight.position[1]); + outWorldPosZ.set(newLight.position[2]); + + if (!cgl.frameStore.shadowPass) drawHelpers(); + + cgl.frameStore.lightStack.push(newLight); + + if (inCastShadow.get()) + { + const blurAmount = 1.5 * inBlur.get() * texelSize; + if (inRenderMapActive.get()) newLight.renderPasses(inPolygonOffset.get(), blurAmount, function () { outTrigger.trigger(); }); + outTexture.set(null); + outTexture.set(newLight.getShadowMapDepth()); + + // remove light from stack and readd it with shadow map & mvp matrix + cgl.frameStore.lightStack.pop(); + + newLight.castShadow = inCastShadow.get(); + newLight.blurAmount = inBlur.get(); + newLight.normalOffset = inNormalOffset.get(); + newLight.shadowBias = inBias.get(); + newLight.shadowStrength = inShadowStrength.get(); + cgl.frameStore.lightStack.push(newLight); + } + + outTrigger.trigger(); + + cgl.frameStore.lightStack.pop(); +} + + +}; + +Ops.Gl.Phong.SpotLight_v5.prototype = new CABLES.Op(); +CABLES.OPS["76418c17-abd5-401b-82e2-688db6f966ee"]={f:Ops.Gl.Phong.SpotLight_v5,objName:"Ops.Gl.Phong.SpotLight_v5"}; + + + + +// ************************************************************** +// +// Ops.Gl.PixelProjection +// +// ************************************************************** + +Ops.Gl.PixelProjection = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + useVPSize = op.inBool("use viewport size", true), + width = op.inFloat("Width", 500), + height = op.inFloat("Height", 500), + zNear = op.inFloat("frustum near", -500), + zFar = op.inFloat("frustum far", 500), + fixAxis = op.inSwitch("Axis", ["X", "Y"], "X"), + inAlign = op.inSwitch("Position 0,0", ["Top Left ", "Top Right", "Center", "Bottom Right", "Bottom Left"], "Bottom Left"), + + flipX = op.inBool("Flip X", false), + flipY = op.inBool("Flip Y", false), + zeroY = op.inBool("Zero Y", false), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; + +op.setPortGroup("Canvas size", [useVPSize, width, height]); +op.setPortGroup("Clipping", [zNear, zFar]); +op.setPortGroup("Flip", [flipX, flipY]); +op.toWorkPortsNeedToBeLinked(render); + +render.onTriggered = exec; +useVPSize.onChange = updateSizeUI; +updateSizeUI(); + +function updateSizeUI() +{ + width.setUiAttribs({ "greyout": useVPSize.get() }); + height.setUiAttribs({ "greyout": useVPSize.get() }); +} + +function exec() +{ + let xl = 0; + let yt = 0; + let xr = 0; + let yb = 0; + + let w = width.get(); + let h = height.get(); + + let x0 = 0; + let y0 = 0; + + if (useVPSize.get()) + { + xr = cgl.getViewPort()[2] / cgl.pixelDensity; + yb = cgl.getViewPort()[3] / cgl.pixelDensity; + w = cgl.getViewPort()[2] / cgl.pixelDensity; + h = cgl.getViewPort()[3] / cgl.pixelDensity; + } + else + { + xr = w; + yb = h; + } + + if (flipX.get()) + { + const temp = xr; + xr = x0; + xl = temp; + } + + if (flipY.get()) + { + const temp = yb; + yb = y0; + yt = temp; + } + + if (inAlign.get() === "Center") + { + xl -= w / 2; + xr -= w / 2; + yt -= h / 2; + yb -= h / 2; + } + else + if (inAlign.get() === "Bottom Right") + { + xl -= w; + xr = x0; + yt = y0; + yb = -h; + } + else + if (inAlign.get() === "Top Right") + { + xl -= w; + xr = x0; + yt -= h; + yb = y0; + } + if (inAlign.get() === "Top Left ") + { + xl = x0; + xr = w; + yt = -h; + yb = y0; + } + + cgl.pushPMatrix(); + + mat4.ortho( + cgl.pMatrix, + xl, + xr, + yt, + yb, + parseFloat(zNear.get()), + parseFloat(zFar.get()) + ); + + trigger.trigger(); + cgl.popPMatrix(); +} + + +}; + +Ops.Gl.PixelProjection.prototype = new CABLES.Op(); +CABLES.OPS["949d6daf-d677-4ed6-a921-51a5732b64ac"]={f:Ops.Gl.PixelProjection,objName:"Ops.Gl.PixelProjection"}; + + + + +// ************************************************************** +// +// Ops.Gl.PointCollector +// +// ************************************************************** + +Ops.Gl.PointCollector = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + outPoints = op.outArray("Points", null, 3); + // outPoints=op.addOutPort(new CABLES.Port(op,"Points",CABLES.OP_PORT_TYPE_ARRAY)), + +outPoints.ignoreValueSerialize = true; + +let inAbsolute = op.inValueBool("Absolute", true); + +let points = []; +let cgl = op.patch.cgl; + +let oldSplinePoints = null; + +let pos = vec3.create(); +let empty = vec3.create(); +let m = mat4.create(); + +let mySplinePoints = []; + +render.onTriggered = function () +{ + if (cgl.frameStore.SplinePoints) + { + oldSplinePoints = cgl.frameStore.SplinePoints; + cgl.frameStore.SplinePoints = []; + } + + cgl.frameStore.SplinePointCounter = 0; + + cgl.frameStore.SplinePoints = mySplinePoints;// cgl.frameStore.SplinePoints||[]; + + if (cgl.frameStore.SplinePointCounter != cgl.frameStore.SplinePoints.length) + cgl.frameStore.SplinePoints.length = cgl.frameStore.SplinePointCounter; + + if (!inAbsolute.get()) + { + mat4.invert(m, cgl.mMatrix); + cgl.frameStore.SplinePointsInverseOriginalMatrix = m; + } + else + { + cgl.frameStore.SplinePointsInverseOriginalMatrix = null; + } + + trigger.trigger(); + + outPoints.set(null); + outPoints.set(cgl.frameStore.SplinePoints); + + if (oldSplinePoints) cgl.frameStore.SplinePoints = oldSplinePoints; +}; + + +}; + +Ops.Gl.PointCollector.prototype = new CABLES.Op(); +CABLES.OPS["52f368ee-303b-4d84-919d-450b0c002e39"]={f:Ops.Gl.PointCollector,objName:"Ops.Gl.PointCollector"}; + + + + +// ************************************************************** +// +// Ops.Gl.PointCollectorCollect +// +// ************************************************************** + +Ops.Gl.PointCollectorCollect = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; + +let pos = vec3.create(); +let empty = vec3.create(); +let tempMat = mat4.create(); + +render.onTriggered = function () +{ + if (!cgl.frameStore.SplinePoints) return; + + if (cgl.frameStore.SplinePointsInverseOriginalMatrix) + { + mat4.multiply(tempMat, cgl.frameStore.SplinePointsInverseOriginalMatrix, cgl.mMatrix); + vec3.transformMat4(pos, empty, tempMat); + } + else + { + vec3.transformMat4(pos, empty, cgl.mMatrix); + } + + cgl.frameStore.SplinePoints[cgl.frameStore.SplinePointCounter + 0] = pos[0]; + cgl.frameStore.SplinePoints[cgl.frameStore.SplinePointCounter + 1] = pos[1]; + cgl.frameStore.SplinePoints[cgl.frameStore.SplinePointCounter + 2] = pos[2]; + + cgl.frameStore.SplinePointCounter += 3; + + trigger.trigger(); +}; + + +}; + +Ops.Gl.PointCollectorCollect.prototype = new CABLES.Op(); +CABLES.OPS["766147f4-94c5-4197-8e0b-f41320aac2c6"]={f:Ops.Gl.PointCollectorCollect,objName:"Ops.Gl.PointCollectorCollect"}; + + + + +// ************************************************************** +// +// Ops.Gl.PointCollectorScreenCoords +// +// ************************************************************** + +Ops.Gl.PointCollectorScreenCoords = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let render = op.inTrigger("render"); +let trigger = op.outTrigger("trigger"); + +let cgl = op.patch.cgl; + +let m = mat4.create(); +let pos = [0, 0, 0]; +let trans = vec3.create(); + +render.onTriggered = function () +{ + if (!cgl.frameStore.SplinePoints) return; + + // vec3.transformMat4(pos, [0,0,0], cgl.mMatrix); + + mat4.multiply(m, cgl.vMatrix, cgl.mMatrix); + vec3.transformMat4(pos, [0, 0, 0], m); + + vec3.transformMat4(trans, pos, cgl.pMatrix); + + let vp = cgl.getViewPort(); + + cgl.frameStore.SplinePoints[cgl.frameStore.SplinePointCounter + 0] = vp[2] - (vp[2] * 0.5 - trans[0] * vp[2] * 0.5 / trans[2]); + cgl.frameStore.SplinePoints[cgl.frameStore.SplinePointCounter + 1] = vp[3] - (vp[3] * 0.5 + trans[1] * vp[3] * 0.5 / trans[2]); + + cgl.frameStore.SplinePointCounter += 2; + + trigger.trigger(); +}; + + +}; + +Ops.Gl.PointCollectorScreenCoords.prototype = new CABLES.Op(); +CABLES.OPS["ada03d29-07be-48f1-a428-7d0e8c52a163"]={f:Ops.Gl.PointCollectorScreenCoords,objName:"Ops.Gl.PointCollectorScreenCoords"}; + + + + +// ************************************************************** +// +// Ops.Gl.RandomCluster +// +// ************************************************************** + +Ops.Gl.RandomCluster = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + num = op.inValueInt("num"), + seed = op.inValueFloat("random seed", 1.5), + round = op.inValueBool("round", false), + size = op.inValueFloat("size", 10), + scaleX = op.inValueFloat("scaleX", 1), + scaleY = op.inValueFloat("scaleY", 1), + scaleZ = op.inValueFloat("scaleZ", 1), + trigger = op.outTrigger("trigger"), + idx = op.outNumber("index"), + rnd = op.outNumber("rnd"), + rotX = op.inValueSlider("Rotate X", 1), + rotY = op.inValueSlider("Rotate Y", 1), + rotZ = op.inValueSlider("Rotate Z", 1), + scrollX = op.inValue("Scroll X", 0); + +op.setPortGroup("Area", [size, scaleX, scaleY, scaleZ]); +op.setPortGroup("Rotation", [rotX, rotY, rotZ]); +op.setPortGroup("Parameters", [num, round, seed]); +op.toWorkPortsNeedToBeLinked(exe, trigger); + +const cgl = op.patch.cgl; +const randoms = []; +const origRandoms = []; +const randomsRot = []; +const randomsFloats = []; + +const transVec = vec3.create(); +const mat = mat4.create(); + +function doRender() +{ + if (cgl.shouldDrawHelpers(op)) + { + CABLES.GL_MARKER.drawCube(op, + size.get() / 2 * scaleX.get(), + size.get() / 2 * scaleY.get(), + size.get() / 2 * scaleZ.get()); + } + + if (scrollX.get() != 0) + { + for (let i = 0; i < origRandoms.length; i++) + { + randoms[i][0] = origRandoms[i][0] + scrollX.get(); + randoms[i][0] = (randoms[i][0] % size.get()) - (size.get() / 2); + } + } + + for (let i = 0; i < randoms.length; i++) + { + cgl.pushModelMatrix(); + + mat4.translate(cgl.mMatrix, cgl.mMatrix, randoms[i]); + + if (randomsRot[i][0]) mat4.rotateX(cgl.mMatrix, cgl.mMatrix, randomsRot[i][0]); + if (randomsRot[i][1]) mat4.rotateY(cgl.mMatrix, cgl.mMatrix, randomsRot[i][1]); + if (randomsRot[i][2]) mat4.rotateZ(cgl.mMatrix, cgl.mMatrix, randomsRot[i][2]); + + idx.set(i); + rnd.set(randomsFloats[i]); + + trigger.trigger(); + // op.patch.instancing.increment(); + + cgl.popModelMatrix(); + } + // op.patch.instancing.popLoop(); +} + +exe.onTriggered = doRender; + +function getRandomPos() +{ + return vec3.fromValues( + scaleX.get() * (Math.seededRandom() - 0.5) * size.get(), + scaleY.get() * (Math.seededRandom() - 0.5) * size.get(), + scaleZ.get() * (Math.seededRandom() - 0.5) * size.get() + ); +} + +function reset() +{ + randoms.length = 0; + randomsRot.length = 0; + randomsFloats.length = 0; + origRandoms.length = 0; + + Math.randomSeed = seed.get(); + + const makeRound = round.get(); + + for (let i = 0; i < num.get(); i++) + { + randomsFloats.push(Math.seededRandom()); + + let v = getRandomPos(); + + if (makeRound && size.get() > 0) + while (vec3.len(v) > size.get() / 2) + v = getRandomPos(); + + origRandoms.push([v[0], v[1], v[2]]); + randoms.push(v); + + randomsRot.push(vec3.fromValues( + Math.seededRandom() * 360 * CGL.DEG2RAD * rotX.get(), + Math.seededRandom() * 360 * CGL.DEG2RAD * rotY.get(), + Math.seededRandom() * 360 * CGL.DEG2RAD * rotZ.get() + )); + } +} + +seed.onChange = reset; +num.onChange = reset; +size.onChange = reset; +scaleX.onChange = reset; +scaleZ.onChange = reset; +scaleY.onChange = reset; +round.onChange = reset; +rotX.onChange = reset; +rotY.onChange = reset; +rotZ.onChange = reset; + +num.set(100); + + +}; + +Ops.Gl.RandomCluster.prototype = new CABLES.Op(); +CABLES.OPS["ca3bc984-e596-48fb-b53d-502eb13979b0"]={f:Ops.Gl.RandomCluster,objName:"Ops.Gl.RandomCluster"}; + + + + +// ************************************************************** +// +// Ops.Gl.RandomizeTriangles +// +// ************************************************************** + +Ops.Gl.RandomizeTriangles = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inGeom = op.inObject("Geometry"), + outGeom = op.outObject("Result"), + inSeed = op.inValue("Seed", 1); + +inGeom.ignoreValueSerialize = true; +outGeom.ignoreValueSerialize = true; + +inSeed.onChange = +inGeom.onChange = function () +{ + const geom = inGeom.get(); + if (!geom) return; + if (geom.verticesIndices && geom.verticesIndices.length > 0) + { + op.logError("cannot randomize indexed geom "); + return; + } + + const newGeom = geom.copy(); + let order = []; + let i = 0; + order.length = geom.vertices.length / 9; + for (i = 0; i < order.length; i++)order[i] = i; + Math.randomSeed = inSeed.get(); + order = CABLES.shuffleArray(order); + + const verts = []; + verts.length = geom.vertices.length; + + for (i = 0; i < order.length; i++) + { + const ind = order[i]; + for (let j = 0; j < 9; j++) + verts[i * 9 + j] = geom.vertices[ind * 9 + j]; + } + + newGeom.setVertices(verts); + + outGeom.set(null); + outGeom.set(newGeom); +}; + + +}; + +Ops.Gl.RandomizeTriangles.prototype = new CABLES.Op(); +CABLES.OPS["e8a27786-bd11-4818-88a6-74eeb52018bd"]={f:Ops.Gl.RandomizeTriangles,objName:"Ops.Gl.RandomizeTriangles"}; + + + + +// ************************************************************** +// +// Ops.Gl.Render2Texture +// +// ************************************************************** + +Ops.Gl.Render2Texture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +const + render = op.inTrigger("render"), + useVPSize = op.inValueBool("use viewport size", true), + width = op.inValueInt("texture width", 512), + height = op.inValueInt("texture height", 512), + aspect = op.inBool("Auto Aspect", false), + tfilter = op.inSwitch("filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inSwitch("Wrap", ["Clamp", "Repeat", "Mirror"], "Repeat"), + msaa = op.inSwitch("MSAA", ["none", "2x", "4x", "8x"], "none"), + trigger = op.outTrigger("trigger"), + tex = op.outTexture("texture"), + texDepth = op.outTexture("textureDepth"), + fpTexture = op.inValueBool("HDR"), + depth = op.inValueBool("Depth", true), + clear = op.inValueBool("Clear", true); + +let fb = null; +let reInitFb = true; +tex.set(CGL.Texture.getEmptyTexture(cgl)); + +op.setPortGroup("Size", [useVPSize, width, height, aspect]); + +const prevViewPort = [0, 0, 0, 0]; + +fpTexture.setUiAttribs({ "title": "Pixelformat Float 32bit" }); + +fpTexture.onChange = + depth.onChange = + clear.onChange = + tfilter.onChange = + twrap.onChange = + msaa.onChange = initFbLater; + +useVPSize.onChange = updateVpSize; + +render.onTriggered = + op.preRender = doRender; + +updateVpSize(); + +function updateVpSize() +{ + width.setUiAttribs({ "greyout": useVPSize.get() }); + height.setUiAttribs({ "greyout": useVPSize.get() }); + aspect.setUiAttribs({ "greyout": useVPSize.get() }); +} + +function initFbLater() +{ + reInitFb = true; +} + +function doRender() +{ + const vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + if (!fb || reInitFb) + { + if (fb) fb.delete(); + + let selectedWrap = CGL.Texture.WRAP_REPEAT; + if (twrap.get() == "Clamp") selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + else if (twrap.get() == "Mirror") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + + let selectFilter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "nearest") selectFilter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") selectFilter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") selectFilter = CGL.Texture.FILTER_MIPMAP; + + if (fpTexture.get() && tfilter.get() == "mipmap") op.setUiError("fpmipmap", "Don't use mipmap and HDR at the same time, many systems do not support this."); + else op.setUiError("fpmipmap", null); + + if (cgl.glVersion >= 2) + { + let ms = true; + let msSamples = 4; + + if (msaa.get() == "none") + { + msSamples = 0; + ms = false; + } + if (msaa.get() == "2x") msSamples = 2; + if (msaa.get() == "4x") msSamples = 4; + if (msaa.get() == "8x") msSamples = 8; + + fb = new CGL.Framebuffer2(cgl, 8, 8, + { + "name": "render2texture " + op.id, + "isFloatingPointTexture": fpTexture.get(), + "multisampling": ms, + "wrap": selectedWrap, + "filter": selectFilter, + "depth": depth.get(), + "multisamplingSamples": msSamples, + "clear": clear.get() + }); + } + else + { + fb = new CGL.Framebuffer(cgl, 8, 8, { "isFloatingPointTexture": fpTexture.get(), "clear": clear.get() }); + } + + texDepth.set(fb.getTextureDepth()); + reInitFb = false; + } + + if (useVPSize.get()) + { + width.set(cgl.getViewPort()[2]); + height.set(cgl.getViewPort()[3]); + } + + if (fb.getWidth() != Math.ceil(width.get()) || fb.getHeight() != Math.ceil(height.get())) + { + fb.setSize( + Math.max(1, Math.ceil(width.get())), + Math.max(1, Math.ceil(height.get()))); + } + + fb.renderStart(cgl); + + if (aspect.get()) mat4.perspective(cgl.pMatrix, 45, width.get() / height.get(), 0.1, 1000.0); + + trigger.trigger(); + fb.renderEnd(cgl); + + // cgl.resetViewPort(); + cgl.setViewPort(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); + + tex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + tex.set(fb.getTextureColor()); +} + + +}; + +Ops.Gl.Render2Texture.prototype = new CABLES.Op(); +CABLES.OPS["d01fa820-396c-4cb5-9d78-6b14762852af"]={f:Ops.Gl.Render2Texture,objName:"Ops.Gl.Render2Texture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Render2Textures +// +// ************************************************************** + +Ops.Gl.Render2Textures = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const render = op.inTrigger("Render"); +const trigger = op.outTrigger("Next"); +const msaa = op.inValueSelect("MSAA", ["none", "2x", "4x", "8x"], "none"); +const useVPSize = op.inValueBool("use viewport size"); + +const width = op.inValueInt("texture width"); +const height = op.inValueInt("texture height"); +const tfilter = op.inValueSelect("Filter", ["nearest", "linear", "mipmap"], "none"); + +const tex0 = op.outTexture("texture 0"); +const tex1 = op.outTexture("texture 1"); +const tex2 = op.outTexture("texture 2"); +const tex3 = op.outTexture("texture 3"); + +const texDepth = op.outTexture("textureDepth"); + +const fpTexture = op.inValueBool("HDR"); +const depth = op.inValueBool("Depth", true); +const clear = op.inValueBool("Clear", true); + +const cgl = op.patch.cgl; + +let fb = null; +width.set(512); +height.set(512); +useVPSize.set(true); +tfilter.set("linear"); +let reInitFb = true; + +render.onTriggered = doRender; +useVPSize.onChange = updateVpSize; + +tfilter.onChange = + fpTexture.onChange = + depth.onChange = + clear.onChange = + msaa.onChange = reInitLater; + +updateVpSize(); + +function updateVpSize() +{ + if (useVPSize.get()) + { + width.setUiAttribs({ "hidePort": true, "greyout": true }); + height.setUiAttribs({ "hidePort": true, "greyout": true }); + } + else + { + width.setUiAttribs({ "hidePort": false, "greyout": false }); + height.setUiAttribs({ "hidePort": false, "greyout": false }); + } +} + +function reInitLater() +{ + reInitFb = true; +} + +function doRender() +{ + if (!fb || reInitFb) + { + if (fb) fb.delete(); + if (cgl.glVersion >= 2) + { + let ms = true; + let msSamples = 4; + + if (msaa.get() == "none") + { + msSamples = 0; + ms = false; + } + if (msaa.get() == "2x")msSamples = 2; + if (msaa.get() == "4x")msSamples = 4; + if (msaa.get() == "8x")msSamples = 8; + + fb = new CGL.Framebuffer2(cgl, 8, 8, + { + "numRenderBuffers": 4, + "isFloatingPointTexture": fpTexture.get(), + "multisampling": ms, + "depth": depth.get(), + "multisamplingSamples": msSamples, + "clear": clear.get() + }); + } + else + { + fb = new CGL.Framebuffer(cgl, 8, 8, { "isFloatingPointTexture": fpTexture.get() }); + } + + if (tfilter.get() == "nearest") fb.setFilter(CGL.Texture.FILTER_NEAREST); + else if (tfilter.get() == "linear") fb.setFilter(CGL.Texture.FILTER_LINEAR); + else if (tfilter.get() == "mipmap") fb.setFilter(CGL.Texture.FILTER_MIPMAP); + + tex0.set(fb.getTextureColorNum(0)); + tex1.set(fb.getTextureColorNum(1)); + tex2.set(fb.getTextureColorNum(2)); + tex3.set(fb.getTextureColorNum(3)); + texDepth.set(fb.getTextureDepth()); + reInitFb = false; + } + + if (useVPSize.get()) + { + width.set(cgl.getViewPort()[2]); + height.set(cgl.getViewPort()[3]); + } + + if (fb.getWidth() != Math.ceil(width.get()) || fb.getHeight() != Math.ceil(height.get())) fb.setSize(width.get(), height.get()); + + fb.renderStart(cgl); + trigger.trigger(); + fb.renderEnd(cgl); + + cgl.resetViewPort(); +} + + +}; + +Ops.Gl.Render2Textures.prototype = new CABLES.Op(); +CABLES.OPS["72f95dbd-7b36-4e88-9d59-1bb2d3f2144e"]={f:Ops.Gl.Render2Textures,objName:"Ops.Gl.Render2Textures"}; + + + + +// ************************************************************** +// +// Ops.Gl.RenderAnim_v2 +// +// ************************************************************** + +Ops.Gl.RenderAnim_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Render"), + next = op.outTrigger("Next"), + inType = op.inDropDown("File Type", ["PNG", "JPG", "WebP", "WebM"], "PNG"), + inZip = op.inBool("ZIP multiple files", false), + inDownload = op.inBool("Download Files", true), + inFilePrefix = op.inString("Filename", "cables"), + inQuality = op.inFloatSlider("Quality", 0.8), + inDurType = op.inSwitch("Duration Type", ["Seconds", "Frames"], "Seconds"), + inDuration = op.inInt("Duration", 1), + inFps = op.inInt("FPS", 30), + inTransparency = op.inValueBool("Transparency", false), + useCanvasSize = op.inValueBool("Use Canvas Size", true), + inWidth = op.inValueInt("texture width", 512), + inHeight = op.inValueInt("texture height", 512), + inStart = op.inTriggerButton("Start"), + outProgress = op.outNumber("Progress", 0), + outFrame = op.outNumber("Frame", 0), + outStatus = op.outString("Status", "Waiting"), + outStarted = op.outBool("Started"), + outUrl = op.outString("Data URL"), + outFinished = op.outTrigger("Finished"); + +op.setPortGroup("File", [inType, inZip, inDownload, inFilePrefix, inQuality]); +op.setPortGroup("Size", [useCanvasSize, inWidth, inHeight]); +op.setPortGroup("Timing", [inFps, inDurType, inDuration]); + +outUrl.ignoreValueSerialize = true; + +exec.onTriggered = render; + +let started = false; +let countFrames = 0; +const finished = true; +let fps = 30; +let numFrames = 31; + +const cycle = false; +let shortId = CABLES.shortId(); +let frameStarted = false; +const frames = []; +let lastFrame = -1; +let time = 0; + +let filenamePrefix = ""; + +let zip = null; + +let oldSizeW = op.patch.cgl.canvasWidth; +let oldSizeH = op.patch.cgl.canvasHeight; + +inType.onChange = updateQuality; +useCanvasSize.onChange = updateSize; + +updateQuality(); +updateSize(); + +inZip.onChange = () => +{ + zip = null; +}; + +function updateQuality() +{ + inQuality.setUiAttribs({ "greyout": inType.get() == "PNG" }); +} + +function updateSize() +{ + inWidth.setUiAttribs({ "greyout": useCanvasSize.get() }); + inHeight.setUiAttribs({ "greyout": useCanvasSize.get() }); +} + +inStart.onTriggered = function () +{ + filenamePrefix = inFilePrefix.get(); + op.log("pref", filenamePrefix); + frames.length = 0; + outStatus.set("Starting"); + fps = inFps.get(); + numFrames = inDuration.get() * fps; + if (inDurType.get() == "Frames")numFrames = inDuration.get(); + shortId = CABLES.shortId(); + updateTime(); + + if (inZip.get()) zip = new JSZip(); + + if (!useCanvasSize.get()) + { + oldSizeW = CABLES.patch.cgl.canvasWidth; + oldSizeH = CABLES.patch.cgl.canvasHeight; + op.patch.cgl.setSize(inWidth.get() / CABLES.patch.cgl.pixelDensity, inHeight.get() / CABLES.patch.cgl.pixelDensity); + op.patch.cgl.updateSize(); + } + + if (numFrames == 0) + { + countFrames = 0; + started = true; + } + else + { + countFrames = -20; + started = true; + lastFrame = -9999; + } +}; + +function updateTime() +{ + if (numFrames >= 0) + { + time = Math.max(0, countFrames * (1.0 / fps)); + op.patch.timer.setTime(time); + CABLES.overwriteTime = time;// - 1 / fps; + op.patch.freeTimer.setTime(time); + } +} + +function stopRendering() +{ + started = false; + CABLES.overwriteTime = undefined; + outStatus.set("Finished"); +} + +function render() +{ + outStarted.set(started); + + if (!started) + { + next.trigger(); + return; + } + + const oldInternalNow = CABLES.internalNow; + + if (started) + { + CABLES.internalNow = function () + { + return time * 1000; + }; + + updateTime(); + // CABLES.overwriteTime = time; + // op.patch.timer.setTime(time); + // op.patch.freeTimer.setTime(time); + } + + if (lastFrame == countFrames) + { + next.trigger(); + return; + } + + lastFrame = countFrames; + + let prog = countFrames / numFrames; + if (prog < 0.0) prog = 0.0; + outProgress.set(prog); + outFrame.set(countFrames); + + next.trigger(); + + CABLES.internalNow = oldInternalNow; + + frameStarted = false; + if (countFrames > numFrames) + { + op.log("FINISHED..."); + op.log("ffmpeg -y -framerate 30 -f image2 -i " + filenamePrefix + "_%d.png -b 9999k -vcodec mpeg4 " + shortId + ".mp4"); + + if (!useCanvasSize.get()) + { + op.patch.cgl.setSize(oldSizeW, oldSizeH); + op.patch.cgl.updateSize(); + } + + if (zip) + { + zip.generateAsync({ "type": "blob" }) + .then(function (blob) + { + const anchor = document.createElement("a"); + anchor.download = filenamePrefix + ".zip"; + anchor.href = URL.createObjectURL(blob); + if (inDownload.get()) + { + anchor.click(); + } + stopRendering(); + if (outUrl.isLinked()) + { + blobToDataURL(blob, (dataUrl) => { outUrl.set(dataUrl); outFinished.trigger(); }); + } + else + { + outUrl.set(null); + outFinished.trigger(); + } + }); + } + else + if (inType.get() == "WebM") + { + try + { + outStatus.set("Creating Video File from frames"); + op.log("webm frames", frames.length); + + const video = Whammy.fromImageArray(frames, fps); + const url = window.URL.createObjectURL(video); + const anchor = document.createElement("a"); + + anchor.setAttribute("download", filenamePrefix + ".webm"); + anchor.setAttribute("href", url); + document.body.appendChild(anchor); + if (inDownload.get()) + { + anchor.click(); + } + stopRendering(); + if (outUrl.isLinked()) + { + blobToDataURL(video, (dataUrl) => { outUrl.set(dataUrl); outFinished.trigger(); }); + } + else + { + outUrl.set(null); + outFinished.trigger(); + } + } + catch (e) + { + op.logError(e); + } + + frames.length = 0; + } + else + stopRendering(); + + return; + } + + let mimetype = "image/png"; + let suffix = "png"; + + if (inType.get() == "JPG") + { + mimetype = "image/jpeg"; + suffix = "jpg"; + } + else if (inType.get() == "WebP") + { + mimetype = "image/webp"; + suffix = "webp"; + } + + if (countFrames > 0) + { + outStatus.set("Rendering Frame " + countFrames + " of " + numFrames); + op.log("Rendering Frame " + countFrames + " of " + numFrames, time); + if (inType.get() == "WebM") + { + frames.push(op.patch.cgl.canvas.toDataURL("image/webp", inQuality.get() * 0.999)); + countFrames++; + updateTime(); + } + else + { + op.log("screenshotting frame...", countFrames); + op.patch.cgl.screenShot((blob) => + { + if (blob) + { + if (zip) + { + let filename = filenamePrefix + "_" + countFrames + "." + suffix; + + zip.file(filename, blob, { "base64": false }); + countFrames++; + updateTime(); + } + else + { + let filename = filenamePrefix + "_" + shortId + "_" + countFrames + "." + suffix; + + const anchor = document.createElement("a"); + anchor.download = filename; + anchor.href = URL.createObjectURL(blob); + + setTimeout(() => + { + if (outUrl.isLinked()) + { + blobToDataURL(blob, (dataUrl) => { outUrl.set(dataUrl); }); + } + else + { + outUrl.set(null); + } + if (inDownload.get()) + { + anchor.click(); + } + countFrames++; + updateTime(); + }, 200); + } + } + else + { + op.log("screenshot: no blob"); + } + }, !inTransparency.get(), mimetype, inQuality.get()); + } + } + else + { + outStatus.set("Prerendering..."); + op.log("pre ", countFrames, time); + op.patch.cgl.screenShot((blob) => + { + countFrames++; + updateTime(); + }); + } +} + +function blobToDataURL(blob, callback) +{ + let a = new FileReader(); + a.onload = function (e) { callback(e.target.result); }; + a.readAsDataURL(blob); +} + + +}; + +Ops.Gl.RenderAnim_v2.prototype = new CABLES.Op(); +CABLES.OPS["c05e54a3-3ed5-4941-a412-01134f53f0ac"]={f:Ops.Gl.RenderAnim_v2,objName:"Ops.Gl.RenderAnim_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.RenderGeometry +// +// ************************************************************** + +Ops.Gl.RenderGeometry = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + geometry = op.inObject("Geometry", null, "geometry"), + updateAll = op.inValueBool("Update All", true), + updateFaces = op.inValueBool("Update Face Indices", false), + updateVerts = op.inValueBool("Update Vertices", false), + updateTexcoords = op.inValueBool("Update Texcoords", false), + vertNums = op.inValueBool("Vertex Numbers", true), + trigger = op.outTrigger("trigger"); + +geometry.ignoreValueSerialize = true; + +vertNums.onChange = + geometry.onChange = update; + +let mesh = null; + +op.toWorkPortsNeedToBeLinked(geometry, render); + +render.onTriggered = function () +{ + if (mesh) mesh.render(op.patch.cgl.getShader()); + trigger.trigger(); +}; + +function update() +{ + const geom = geometry.get(); + if (geom && geom instanceof CGL.Geometry) + { + if (mesh) + { + mesh.dispose(); + mesh = null; + } + if (!mesh) + { + mesh = new CGL.Mesh(op.patch.cgl, geom); + mesh.addVertexNumbers = vertNums.get(); + mesh.setGeom(geom); + } + + if (updateFaces.get() || updateAll.get()) + mesh.setVertexIndices(geom.verticesIndices); + + if (updateTexcoords.get() || updateAll.get()) + mesh.updateTexCoords(geom); + + if (updateVerts.get() || updateAll.get()) + mesh.updateVertices(geom); + + mesh.addVertexNumbers = vertNums.get(); + + if (updateAll.get()) + { + if (geom.hasOwnProperty("tangents") && geom.tangents && geom.tangents.length > 0) mesh.setAttribute("attrTangent", geom.tangents, 3); + if (geom.hasOwnProperty("biTangents") && geom.biTangents && geom.biTangents.length > 0) mesh.setAttribute("attrBiTangent", geom.biTangents, 3); + } + } + else + { + mesh = null; + } +} + + +}; + +Ops.Gl.RenderGeometry.prototype = new CABLES.Op(); +CABLES.OPS["40fa6f13-ee0e-4386-a86b-711e1fbcf1bc"]={f:Ops.Gl.RenderGeometry,objName:"Ops.Gl.RenderGeometry"}; + + + + +// ************************************************************** +// +// Ops.Gl.ResetTransform +// +// ************************************************************** + +Ops.Gl.ResetTransform = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + trigger = op.outTrigger("trigger"), + inM = op.inBool("Reset Model Transform", true), + inV = op.inBool("Reset View Transform", true), + inDV = op.inBool("Default View", true), + cgl = op.patch.cgl; + +let doView = false, + doModel = false, + vDefault = false; + +const identView = vec3.create(); +vec3.set(identView, 0, 0, -2); + +exe.onTriggered = ex; + +inM.onChange = + inDV.onChange = + inV.onChange = updateState; +updateState(); + +function updateState() +{ + doView = inV.get(); + doModel = inM.get(); + vDefault = inDV.get(); + inDV.setUiAttribs({ "greyout": !doView }); +} + + +function ex() +{ + if (doView) + { + cgl.pushViewMatrix(); + mat4.identity(cgl.vMatrix); + if (vDefault) + { + mat4.translate(cgl.vMatrix, cgl.vMatrix, identView); + } + } + + if (doModel) + { + cgl.pushModelMatrix(); + mat4.identity(cgl.mMatrix); + } + + trigger.trigger(); + + if (doView) cgl.popViewMatrix(); + if (doModel) cgl.popModelMatrix(); +} + + +}; + +Ops.Gl.ResetTransform.prototype = new CABLES.Op(); +CABLES.OPS["1bf7c63e-e2c2-42e2-abb3-42235e7e24f0"]={f:Ops.Gl.ResetTransform,objName:"Ops.Gl.ResetTransform"}; + + + + +// ************************************************************** +// +// Ops.Gl.SaveScreenShot_v3 +// +// ************************************************************** + +Ops.Gl.SaveScreenShot_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + filename = op.inString("Filename", "cables"), + exe = op.inTriggerButton("Screenshot"), + outNext = op.outTrigger("Finished"); + + +const cgl = op.patch.cgl; + +exe.onTriggered = function () +{ + cgl.saveScreenshot( + filename.get(), + function () + { + outNext.trigger(); + + op.patch.resume(); + } + ); +}; + + + +}; + +Ops.Gl.SaveScreenShot_v3.prototype = new CABLES.Op(); +CABLES.OPS["76843a4d-947f-41ca-9c8f-0faa6ce7380a"]={f:Ops.Gl.SaveScreenShot_v3,objName:"Ops.Gl.SaveScreenShot_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.AttributeAsColorMaterial +// +// ************************************************************** + +Ops.Gl.Shader.AttributeAsColorMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"normalsmaterial_frag":"IN vec3 normal;\nIN vec3 outTangent;\nIN vec3 outBiTangent;\nIN mat4 mMatrix;\nIN vec2 texCoord;\nIN vec3 vert;\nIN mat4 mvMatrix;\n\n\n{{MODULES_HEAD}}\n\nvoid main()\n{\n #ifdef MULMODEL\n vec4 attr;\n attr.w=1.0;\n #endif\n #ifndef MULMODEL\n vec3 attr;\n #endif\n\n #ifdef SHOW_NORMALS\n attr.xyz=normal;\n #endif\n #ifdef SHOW_BITANGENTS\n attr.xyz=outBiTangent;\n #endif\n #ifdef SHOW_TANGENTS\n attr.xyz=outTangent;\n #endif\n #ifdef SHOW_TEXCOORDS\n attr.xy=texCoord;\n #endif\n #ifdef SHOW_POSITION\n attr.xyz=vert;\n #endif\n\n #ifdef MULMODEL\n attr*=mMatrix;\n attr.xyz=normalize(vec3(attr.x,attr.y,attr.z));\n #endif\n\n vec4 col=vec4(attr.x,attr.y,attr.z,1.0);\n\n #ifdef ABS\n col=abs(col);\n #endif\n\n {{MODULE_COLOR}}\n\n outColor=col;\n}","normalsmaterial_vert":"IN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal,attrTangent,attrBiTangent;\nOUT vec2 texCoord;\nOUT vec3 normal;\nOUT vec3 tangent;\nOUT vec3 bitangent;\nOUT vec3 outTangent,outBiTangent;\nOUT mat4 mMatrix;\nOUT vec3 vert;\nOUT mat4 mvMatrix;\nUNI mat4 projMatrix;\n\n// UNI mat4 mvMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\n\n{{MODULES_HEAD}}\n\nvoid main()\n{\n texCoord=attrTexCoord;\n vec3 norm=attrVertNormal;\n tangent=attrTangent;\n bitangent=attrBiTangent;\n\n vec4 pos=vec4(vPosition,1.0);\n mMatrix=modelMatrix;\n\n {{MODULE_VERTEX_POSITION}}\n\n mat4 mvMatrix=viewMatrix*mMatrix;\n\n vert=pos.xyz;\n normal=norm;\n outTangent=tangent;\n outBiTangent=bitangent;\n\n gl_Position = projMatrix * mvMatrix * pos;\n}",}; +const + render = op.inTrigger("render"), + inAttr = op.inSwitch("Attribute", ["TexCoords", "Normals", "Tangents", "BiTangents"], "Normals"), + inAbs = op.inBool("Absolute", false), + inMulModel = op.inBool("World Space", false), + trigger = op.outTrigger("trigger"), + outShader = op.outObject("Shader"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(attachments.normalsmaterial_vert, attachments.normalsmaterial_frag); +outShader.set(shader); +render.onTriggered = doRender; +updateAttr(); +inMulModel.onChange = inAbs.onChange = inAttr.onChange = updateAttr; + +function updateAttr() +{ + shader.toggleDefine("SHOW_NORMALS", inAttr.get() == "Normals"); + shader.toggleDefine("SHOW_TANGENTS", inAttr.get() == "Tangents"); + shader.toggleDefine("SHOW_BITANGENTS", inAttr.get() == "BiTangents"); + shader.toggleDefine("SHOW_TEXCOORDS", inAttr.get() == "TexCoords"); + + shader.toggleDefine("ABS", inAbs.get()); + shader.toggleDefine("MULMODEL", inMulModel.get()); +} + +function doRender() +{ + cgl.pushShader(shader); + trigger.trigger(); + cgl.popShader(); +} + + +}; + +Ops.Gl.Shader.AttributeAsColorMaterial.prototype = new CABLES.Op(); +CABLES.OPS["2963fd23-a860-461a-a3cf-394b8261159f"]={f:Ops.Gl.Shader.AttributeAsColorMaterial,objName:"Ops.Gl.Shader.AttributeAsColorMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.BasicMaterial_v3 +// +// ************************************************************** + +Ops.Gl.Shader.BasicMaterial_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"basicmaterial_frag":"{{MODULES_HEAD}}\n\nIN vec2 texCoord;\n\n#ifdef VERTEX_COLORS\nIN vec4 vertCol;\n#endif\n\n#ifdef HAS_TEXTURES\n IN vec2 texCoordOrig;\n #ifdef HAS_TEXTURE_DIFFUSE\n UNI sampler2D tex;\n #endif\n #ifdef HAS_TEXTURE_OPACITY\n UNI sampler2D texOpacity;\n #endif\n#endif\n\nvoid main()\n{\n {{MODULE_BEGIN_FRAG}}\n vec4 col=color;\n\n\n #ifdef HAS_TEXTURES\n vec2 uv=texCoord;\n\n #ifdef CROP_TEXCOORDS\n if(uv.x<0.0 || uv.x>1.0 || uv.y<0.0 || uv.y>1.0) discard;\n #endif\n\n #ifdef HAS_TEXTURE_DIFFUSE\n col=texture(tex,uv);\n\n #ifdef COLORIZE_TEXTURE\n col.r*=color.r;\n col.g*=color.g;\n col.b*=color.b;\n #endif\n #endif\n col.a*=color.a;\n #ifdef HAS_TEXTURE_OPACITY\n #ifdef TRANSFORMALPHATEXCOORDS\n uv=texCoordOrig;\n #endif\n #ifdef ALPHA_MASK_IALPHA\n col.a*=1.0-texture(texOpacity,uv).a;\n #endif\n #ifdef ALPHA_MASK_ALPHA\n col.a*=texture(texOpacity,uv).a;\n #endif\n #ifdef ALPHA_MASK_LUMI\n col.a*=dot(vec3(0.2126,0.7152,0.0722), texture(texOpacity,uv).rgb);\n #endif\n #ifdef ALPHA_MASK_R\n col.a*=texture(texOpacity,uv).r;\n #endif\n #ifdef ALPHA_MASK_G\n col.a*=texture(texOpacity,uv).g;\n #endif\n #ifdef ALPHA_MASK_B\n col.a*=texture(texOpacity,uv).b;\n #endif\n // #endif\n #endif\n #endif\n\n {{MODULE_COLOR}}\n\n #ifdef DISCARDTRANS\n if(col.a<0.2) discard;\n #endif\n\n #ifdef VERTEX_COLORS\n col*=vertCol;\n #endif\n\n outColor = col;\n}\n","basicmaterial_vert":"\n{{MODULES_HEAD}}\n\n// OUT vec3 norm;\nOUT vec2 texCoord;\nOUT vec2 texCoordOrig;\n\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\n#ifdef HAS_TEXTURES\n UNI float diffuseRepeatX;\n UNI float diffuseRepeatY;\n UNI float texOffsetX;\n UNI float texOffsetY;\n#endif\n\n#ifdef VERTEX_COLORS\n in vec4 attrVertColor;\n out vec4 vertCol;\n\n#endif\n\n\nvoid main()\n{\n mat4 mMatrix=modelMatrix;\n mat4 mvMatrix;\n\n norm=attrVertNormal;\n texCoordOrig=attrTexCoord;\n texCoord=attrTexCoord;\n #ifdef HAS_TEXTURES\n texCoord.x=texCoord.x*diffuseRepeatX+texOffsetX;\n texCoord.y=(1.0-texCoord.y)*diffuseRepeatY+texOffsetY;\n #endif\n\n #ifdef VERTEX_COLORS\n vertCol=attrVertColor;\n #endif\n\n vec4 pos = vec4(vPosition, 1.0);\n\n #ifdef BILLBOARD\n vec3 position=vPosition;\n mvMatrix=viewMatrix*modelMatrix;\n\n gl_Position = projMatrix * mvMatrix * vec4((\n position.x * vec3(\n mvMatrix[0][0],\n mvMatrix[1][0],\n mvMatrix[2][0] ) +\n position.y * vec3(\n mvMatrix[0][1],\n mvMatrix[1][1],\n mvMatrix[2][1]) ), 1.0);\n #endif\n\n {{MODULE_VERTEX_POSITION}}\n\n #ifndef BILLBOARD\n mvMatrix=viewMatrix * mMatrix;\n #endif\n\n\n #ifndef BILLBOARD\n // gl_Position = projMatrix * viewMatrix * modelMatrix * pos;\n gl_Position = projMatrix * mvMatrix * pos;\n #endif\n}\n",}; +const render = op.inTrigger("render"); + +const trigger = op.outTrigger("trigger"); +const shaderOut = op.outObject("shader", null, "shader"); + +shaderOut.ignoreValueSerialize = true; + +op.toWorkPortsNeedToBeLinked(render); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "basicmaterialnew"); +shader.addAttribute({ "type": "vec3", "name": "vPosition" }); +shader.addAttribute({ "type": "vec2", "name": "attrTexCoord" }); +shader.addAttribute({ "type": "vec3", "name": "attrVertNormal", "nameFrag": "norm" }); +shader.addAttribute({ "type": "float", "name": "attrVertIndex" }); + +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); + +shader.setSource(attachments.basicmaterial_vert, attachments.basicmaterial_frag); + +shaderOut.set(shader); + +render.onTriggered = doRender; + +// rgba colors +const r = op.inValueSlider("r", Math.random()); +const g = op.inValueSlider("g", Math.random()); +const b = op.inValueSlider("b", Math.random()); +const a = op.inValueSlider("a", 1); +r.setUiAttribs({ "colorPick": true }); + +// const uniColor=new CGL.Uniform(shader,'4f','color',r,g,b,a); +const colUni = shader.addUniformFrag("4f", "color", r, g, b, a); + +shader.uniformColorDiffuse = colUni; + +// diffuse outTexture + +const diffuseTexture = op.inTexture("texture"); +let diffuseTextureUniform = null; +diffuseTexture.onChange = updateDiffuseTexture; + +const colorizeTexture = op.inValueBool("colorizeTexture", false); +const vertexColors = op.inValueBool("Vertex Colors", false); + +// opacity texture +const textureOpacity = op.inTexture("textureOpacity"); +let textureOpacityUniform = null; + +const alphaMaskSource = op.inSwitch("Alpha Mask Source", ["Luminance", "R", "G", "B", "A", "1-A"], "Luminance"); +alphaMaskSource.setUiAttribs({ "greyout": true }); +textureOpacity.onChange = updateOpacity; + +const texCoordAlpha = op.inValueBool("Opacity TexCoords Transform", false); +const discardTransPxl = op.inValueBool("Discard Transparent Pixels"); + +// texture coords +const + diffuseRepeatX = op.inValue("diffuseRepeatX", 1), + diffuseRepeatY = op.inValue("diffuseRepeatY", 1), + diffuseOffsetX = op.inValue("Tex Offset X", 0), + diffuseOffsetY = op.inValue("Tex Offset Y", 0), + cropRepeat = op.inBool("Crop TexCoords", false); + +shader.addUniformFrag("f", "diffuseRepeatX", diffuseRepeatX); +shader.addUniformFrag("f", "diffuseRepeatY", diffuseRepeatY); +shader.addUniformFrag("f", "texOffsetX", diffuseOffsetX); +shader.addUniformFrag("f", "texOffsetY", diffuseOffsetY); + +const doBillboard = op.inValueBool("billboard", false); + +alphaMaskSource.onChange = + doBillboard.onChange = + discardTransPxl.onChange = + texCoordAlpha.onChange = + cropRepeat.onChange = + vertexColors.onChange = + colorizeTexture.onChange = updateDefines; + +op.setPortGroup("Color", [r, g, b, a]); +op.setPortGroup("Color Texture", [diffuseTexture, vertexColors, colorizeTexture]); +op.setPortGroup("Opacity", [textureOpacity, alphaMaskSource, discardTransPxl, texCoordAlpha]); +op.setPortGroup("Texture Transform", [diffuseRepeatX, diffuseRepeatY, diffuseOffsetX, diffuseOffsetY, cropRepeat]); + +updateOpacity(); +updateDiffuseTexture(); + +op.preRender = function () +{ + shader.bind(); + doRender(); +}; + +function doRender() +{ + if (!shader) return; + + cgl.pushShader(shader); + shader.popTextures(); + + if (diffuseTextureUniform && diffuseTexture.get()) shader.pushTexture(diffuseTextureUniform, diffuseTexture.get()); + if (textureOpacityUniform && textureOpacity.get()) shader.pushTexture(textureOpacityUniform, textureOpacity.get()); + + trigger.trigger(); + + cgl.popShader(); +} + +function updateOpacity() +{ + if (textureOpacity.get()) + { + if (textureOpacityUniform !== null) return; + shader.removeUniform("texOpacity"); + shader.define("HAS_TEXTURE_OPACITY"); + if (!textureOpacityUniform)textureOpacityUniform = new CGL.Uniform(shader, "t", "texOpacity"); + + alphaMaskSource.setUiAttribs({ "greyout": false }); + texCoordAlpha.setUiAttribs({ "greyout": false }); + } + else + { + shader.removeUniform("texOpacity"); + shader.removeDefine("HAS_TEXTURE_OPACITY"); + textureOpacityUniform = null; + + alphaMaskSource.setUiAttribs({ "greyout": true }); + texCoordAlpha.setUiAttribs({ "greyout": true }); + } + + updateDefines(); +} + +function updateDiffuseTexture() +{ + if (diffuseTexture.get()) + { + if (!shader.hasDefine("HAS_TEXTURE_DIFFUSE"))shader.define("HAS_TEXTURE_DIFFUSE"); + if (!diffuseTextureUniform)diffuseTextureUniform = new CGL.Uniform(shader, "t", "texDiffuse"); + + diffuseRepeatX.setUiAttribs({ "greyout": false }); + diffuseRepeatY.setUiAttribs({ "greyout": false }); + diffuseOffsetX.setUiAttribs({ "greyout": false }); + diffuseOffsetY.setUiAttribs({ "greyout": false }); + colorizeTexture.setUiAttribs({ "greyout": false }); + } + else + { + shader.removeUniform("texDiffuse"); + shader.removeDefine("HAS_TEXTURE_DIFFUSE"); + diffuseTextureUniform = null; + + diffuseRepeatX.setUiAttribs({ "greyout": true }); + diffuseRepeatY.setUiAttribs({ "greyout": true }); + diffuseOffsetX.setUiAttribs({ "greyout": true }); + diffuseOffsetY.setUiAttribs({ "greyout": true }); + colorizeTexture.setUiAttribs({ "greyout": true }); + } +} + +function updateDefines() +{ + shader.toggleDefine("VERTEX_COLORS", vertexColors.get()); + shader.toggleDefine("CROP_TEXCOORDS", cropRepeat.get()); + shader.toggleDefine("COLORIZE_TEXTURE", colorizeTexture.get()); + shader.toggleDefine("TRANSFORMALPHATEXCOORDS", texCoordAlpha.get()); + shader.toggleDefine("DISCARDTRANS", discardTransPxl.get()); + shader.toggleDefine("BILLBOARD", doBillboard.get()); + + shader.toggleDefine("ALPHA_MASK_ALPHA", alphaMaskSource.get() == "A"); + shader.toggleDefine("ALPHA_MASK_IALPHA", alphaMaskSource.get() == "1-A"); + shader.toggleDefine("ALPHA_MASK_LUMI", alphaMaskSource.get() == "Luminance"); + shader.toggleDefine("ALPHA_MASK_R", alphaMaskSource.get() == "R"); + shader.toggleDefine("ALPHA_MASK_G", alphaMaskSource.get() == "G"); + shader.toggleDefine("ALPHA_MASK_B", alphaMaskSource.get() == "B"); +} + + +}; + +Ops.Gl.Shader.BasicMaterial_v3.prototype = new CABLES.Op(); +CABLES.OPS["ec55d252-3843-41b1-b731-0482dbd9e72b"]={f:Ops.Gl.Shader.BasicMaterial_v3,objName:"Ops.Gl.Shader.BasicMaterial_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.ChromaKeyMaterial +// +// ************************************************************** + +Ops.Gl.Shader.ChromaKeyMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"chromakeymaterial_frag":"{{MODULE_BEGIN_FRAG}}\n\nIN vec2 texCoord;\n\nUNI sampler2D tex;\nUNI float r;\nUNI float g;\nUNI float b;\nUNI float weightMul;\nUNI float white;\n\nvec3 rgb2hsv(vec4 rgb)\n{\n\tfloat Cmax = max(rgb.r, max(rgb.g, rgb.b));\n\tfloat Cmin = min(rgb.r, min(rgb.g, rgb.b));\n float delta = Cmax - Cmin;\n\n\tvec3 hsv = vec3(0., 0., Cmax);\n\n\tif (Cmax > Cmin)\n\t{\n\t\thsv.y = delta / Cmax;\n\n\t\tif (rgb.r == Cmax)\n\t\t\thsv.x = (rgb.g - rgb.b) / delta;\n\t\telse\n\t\t{\n\t\t\tif (rgb.g == Cmax)\n\t\t\t\thsv.x = 2. + (rgb.b - rgb.r) / delta;\n\t\t\telse\n\t\t\t\thsv.x = 4. + (rgb.r - rgb.g) / delta;\n\t\t}\n\t\thsv.x = fract(hsv.x / 6.);\n\t}\n\treturn hsv;\n}\n\nfloat chromaKey(vec4 color)\n{\n vec4 backgroundColor = vec4(r,g,b,0.0);\n vec3 weights = vec3(4.*weightMul, 1., 2.*weightMul);\n\n vec3 hsv = rgb2hsv(color);\n vec3 target = rgb2hsv(backgroundColor);\n float dist = length(weights * (target - hsv));\n\n return 1. - clamp(3. * dist - 1.5, 0., 1.);\n}\n\nfloat random(vec2 co)\n{\n return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (43758.5453));\n}\n\nvoid main()\n{\n vec4 col=vec4(1.0,1.0,0.0,1.0);\n {{MODULE_COLOR}}\n col=texture(tex,texCoord);\n\n #ifdef MODE_R\n float maxrb = max( col.g, col.b );\n float perc = min(1.0,(col.r*weightMul-maxrb)*2.0);\n #endif\n\n #ifdef MODE_G\n float maxrb = max( col.r, col.b );\n float perc = min(1.0,(col.g*weightMul-maxrb)*2.0);\n col.g=min(maxrb,col.g);\n #endif\n\n #ifdef MODE_COLOR\n float perc=chromaKey(col);\n #endif\n\n float len=length(col);\n col=normalize(col)*len;\n\n col.a=1.0-perc;\n outColor= col;\n}\n","chromakeymaterial_vert":"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\nIN vec2 attrTexCoord;\nOUT vec2 texCoord;\n\nUNI float diffuseRepeatX;\nUNI float diffuseRepeatY;\nUNI float texOffsetX;\nUNI float texOffsetY;\n\nvoid main()\n{\n texCoord=vec2(attrTexCoord.x,1.0-attrTexCoord.y);\n\n texCoord.s=texCoord.s*diffuseRepeatX+texOffsetX;\n texCoord.t=texCoord.t*diffuseRepeatY+texOffsetY;\n\n vec4 pos=vec4(vPosition, 1.0);\n {{MODULE_VERTEX_POSITION}}\n gl_Position = projMatrix * mvMatrix * pos;\n}\n",}; +const render = op.inTrigger("render"); +const texture = op.inTexture("texture"); + +const inMode = op.inValueSelect("Mode", ["G", "R", "COLOR"], "COLOR"); +const trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; + +render.onTriggered = doRender; + +let textureUniform = null; +let shader = new CGL.Shader(cgl, "MinimalMaterial"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +shader.setSource(attachments.chromakeymaterial_vert, attachments.chromakeymaterial_frag); +shader.define("MODE_COLOR"); + +op.toWorkPortsNeedToBeLinked(texture); + +function doRender() +{ + if (shader) + { + cgl.pushShader(shader); + shader.bindTextures(); + trigger.trigger(); + cgl.popShader(); + } +} + +shader.bindTextures = function () +{ + if (texture.get()) cgl.setTexture(0, texture.get().tex); + // else cgl.setTexture(0, CGL.Texture.getTemporaryTexture()); +}; + +inMode.onChange = function () +{ + shader.removeDefine("MODE_G"); + shader.removeDefine("MODE_R"); + shader.removeDefine("MODE_COLOR"); + if (inMode.get() == "R") shader.define("MODE_R"); + else if (inMode.get() == "G") shader.define("MODE_G"); + else if (inMode.get() == "COLOR") shader.define("MODE_COLOR"); +}; + +texture.onChange = function () +{ + if (texture.get()) + { + if (textureUniform !== null) return; + shader.removeUniform("tex"); + shader.define("HAS_TEXTURE_DIFFUSE"); + textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + } + else + { + shader.removeUniform("tex"); + shader.removeDefine("HAS_TEXTURE_DIFFUSE"); + textureUniform = null; + } +}; + +let w = op.inValueSlider("weightMul", 0.6); +w.uniform = new CGL.Uniform(shader, "f", "weightMul", w); + +const r = op.inValueSlider("r", Math.random()); +const g = op.inValueSlider("g", Math.random()); +const b = op.inValueSlider("b", Math.random()); +r.setUiAttribs({ "colorPick": true }); + +r.set(0.467); +r.uniform = new CGL.Uniform(shader, "f", "r", r); + +g.set(0.836); +g.uniform = new CGL.Uniform(shader, "f", "g", g); + +b.set(0.098); +b.uniform = new CGL.Uniform(shader, "f", "b", b); + +let white = op.inValueSlider("white", 0); +white.uniform = new CGL.Uniform(shader, "f", "white", white); + +const diffuseRepeatX = op.inValue("diffuseRepeatX", 1); +const diffuseRepeatY = op.inValue("diffuseRepeatY", 1); + +const diffuseOffsetX = op.inValue("Tex Offset X", 0); +const diffuseOffsetY = op.inValue("Tex Offset Y", 0); + +const diffuseRepeatXUniform = new CGL.Uniform(shader, "f", "diffuseRepeatX", diffuseRepeatX); +const diffuseRepeatYUniform = new CGL.Uniform(shader, "f", "diffuseRepeatY", diffuseRepeatY); +const diffuseOffsetXUniform = new CGL.Uniform(shader, "f", "texOffsetX", diffuseOffsetX); +const diffuseOffsetYUniform = new CGL.Uniform(shader, "f", "texOffsetY", diffuseOffsetY); + + +}; + +Ops.Gl.Shader.ChromaKeyMaterial.prototype = new CABLES.Op(); +CABLES.OPS["20be6064-b332-447c-a220-e70d5bc506e0"]={f:Ops.Gl.Shader.ChromaKeyMaterial,objName:"Ops.Gl.Shader.ChromaKeyMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.CustomShader_v2 +// +// ************************************************************** + +Ops.Gl.Shader.CustomShader_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + fragmentShader = op.inStringEditor("Fragment Code"), + vertexShader = op.inStringEditor("Vertex Code"), + asMaterial = op.inValueBool("Use As Material", true), + trigger = op.outTrigger("trigger"), + outShader = op.outObject("Shader", null, "shader"), + outErrors = op.outBool("Has Errors"); + +const cgl = op.patch.cgl; +const uniformInputs = []; +const uniformTextures = []; +const vectors = []; + +op.toWorkPortsNeedToBeLinked(render); + +fragmentShader.setUiAttribs({ "editorSyntax": "glsl" }); +vertexShader.setUiAttribs({ "editorSyntax": "glsl" }); + +const shader = new CGL.Shader(cgl, op.name); + +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); + +op.setPortGroup("Source Code", [fragmentShader, vertexShader]); +op.setPortGroup("Options", [asMaterial]); + +fragmentShader.set(CGL.Shader.getDefaultFragmentShader()); +vertexShader.set(CGL.Shader.getDefaultVertexShader()); + +fragmentShader.onChange = vertexShader.onChange = function () +{ + if (fragmentShader.isLinked() && !fragmentShader.get()) return; + needsUpdate = true; +}; + +render.onTriggered = doRender; + +let needsUpdate = true; +op.onLoadedValueSet = initDataOnLoad; + +function initDataOnLoad(data) +{ + updateShader(); + + // set uniform values AFTER shader has been compiled and uniforms are extracted and uniform ports are created. + for (let i = 0; i < uniformInputs.length; i++) + for (let j = 0; j < data.portsIn.length; j++) + if (uniformInputs[i] && uniformInputs[i].name == data.portsIn[j].name) + { + uniformInputs[i].set(data.portsIn[j].value); + uniformInputs[i].deSerializeSettings(data.portsIn[j]); + } +} + +op.init = function () +{ + updateShader(); +}; + +function doRender() +{ + setVectorValues(); + if (needsUpdate)updateShader(); + if (asMaterial.get()) cgl.pushShader(shader); + pushTextures(); + trigger.trigger(); + shader.popTextures(); + if (asMaterial.get()) cgl.popShader(); +} + +function pushTextures() +{ + for (let i = 0; i < uniformTextures.length; i++) + if (uniformTextures[i] && uniformTextures[i].get() && uniformTextures[i].get().tex) + shader.pushTexture(uniformTextures[i].uniform, uniformTextures[i].get().tex); + else + shader.pushTexture(uniformTextures[i], CGL.Texture.getEmptyTexture(cgl)); +} + +function bindTextures()// old - should be removed in next version ? +{ + for (let i = 0; i < uniformTextures.length; i++) + if (uniformTextures[i] && uniformTextures[i].get() && uniformTextures[i].get().tex) + cgl.setTexture(0 + i + 3, uniformTextures[i].get().tex); +} + +function hasUniformInput(name) +{ + for (let i = 0; i < uniformInputs.length; i++) if (uniformInputs[i] && uniformInputs[i].name == name) return true; + for (let i = 0; i < uniformTextures.length; i++) if (uniformTextures[i] && uniformTextures[i].name == name) return true; + return false; +} + +const tempMat4 = mat4.create(); +const uniformNameBlacklist = [ + "modelMatrix", + "viewMatrix", + "normalMatrix", + "mvMatrix", + "projMatrix", + "inverseViewMatrix", + "camPos" +]; + +let countTexture = 0; +const foundNames = []; + +function parseUniforms(src) +{ + const lblines = src.split("\n"); + const groupUniforms = []; + + for (let k = 0; k < lblines.length; k++) + { + const lines = lblines[k].split(";"); + + for (let i = 0; i < lines.length; i++) + { + let words = lines[i].split(" "); + + for (let j = 0; j < words.length; j++) words[j] = (words[j] + "").trim(); + + if (words[0] === "UNI" || words[0] === "uniform") + { + let varnames = words[2]; + if (words.length > 4) for (let j = 3; j < words.length; j++)varnames += words[j]; + + words = words.filter(function (el) { return el !== ""; }); + const type = words[1]; + + let names = [varnames]; + if (varnames.indexOf(",") > -1) names = varnames.split(","); + + for (let l = 0; l < names.length; l++) + { + if (uniformNameBlacklist.indexOf(names[l]) > -1) continue; + const uniName = names[l].trim(); + + if (type === "float") + { + foundNames.push(uniName); + if (!hasUniformInput(uniName)) + { + const newInput = op.inFloat(uniName, 0); + newInput.uniform = new CGL.Uniform(shader, "f", uniName, newInput); + uniformInputs.push(newInput); + groupUniforms.push(newInput); + } + } + else if (type === "int") + { + foundNames.push(uniName); + if (!hasUniformInput(uniName)) + { + const newInput = op.inInt(uniName, 0); + newInput.uniform = new CGL.Uniform(shader, "i", uniName, newInput); + uniformInputs.push(newInput); + groupUniforms.push(newInput); + } + } + else if (type === "bool") + { + foundNames.push(uniName); + if (!hasUniformInput(uniName)) + { + const newInput = op.inBool(uniName, false); + newInput.uniform = new CGL.Uniform(shader, "b", uniName, newInput); + uniformInputs.push(newInput); + groupUniforms.push(newInput); + } + } + else if (type === "mat4") + { + foundNames.push(uniName); + if (!hasUniformInput(uniName)) + { + const newInput = op.inArray(uniName, 0); + newInput.uniform = new CGL.Uniform(shader, "m4", uniName, newInput); + uniformInputs.push(newInput); + groupUniforms.push(newInput); + + const vec = { + "name": uniName, + "num": 16, + "port": newInput, + "uni": newInput.uniform, + "changed": false + }; + newInput.onChange = function () { this.changed = true; }.bind(vec); + + vectors.push(vec); + } + } + else if (type === "sampler2D" || type === "samplerCube") + { + foundNames.push(uniName); + if (!hasUniformInput(uniName)) + { + const newInputTex = op.inObject(uniName); + + let uniType = "t"; + if (type === "samplerCube")uniType = "tc"; + + newInputTex.uniform = new CGL.Uniform(shader, uniType, uniName, 3 + uniformTextures.length); + uniformTextures.push(newInputTex); + groupUniforms.push(newInputTex); + newInputTex.set(CGL.Texture.getTempTexture(cgl)); + newInputTex.on("change", (v, p) => + { + if (!v)p.set(CGL.Texture.getTempTexture(cgl)); + }); + countTexture++; + } + } + else if (type === "vec3" || type === "vec2" || type === "vec4") + { + let num = 2; + if (type === "vec4")num = 4; + if (type === "vec3")num = 3; + foundNames.push(uniName + " X"); + foundNames.push(uniName + " Y"); + if (num > 2)foundNames.push(uniName + " Z"); + if (num > 3)foundNames.push(uniName + " W"); + + if (!hasUniformInput(uniName + " X")) + { + const group = []; + const vec = { + "name": uniName, + "num": num, + "changed": false, + }; + vectors.push(vec); + initVectorUniform(vec); + + const newInputX = op.inFloat(uniName + " X", 0); + newInputX.onChange = function () { this.changed = true; }.bind(vec); + uniformInputs.push(newInputX); + group.push(newInputX); + vec.x = newInputX; + + const newInputY = op.inFloat(uniName + " Y", 0); + newInputY.onChange = function () { this.changed = true; }.bind(vec); + uniformInputs.push(newInputY); + group.push(newInputY); + vec.y = newInputY; + + if (num > 2) + { + const newInputZ = op.inFloat(uniName + " Z", 0); + newInputZ.onChange = function () { this.changed = true; }.bind(vec); + uniformInputs.push(newInputZ); + group.push(newInputZ); + vec.z = newInputZ; + } + if (num > 3) + { + const newInputW = op.inFloat(uniName + " W", 0); + newInputW.onChange = function () { this.changed = true; }.bind(vec); + uniformInputs.push(newInputW); + group.push(newInputW); + vec.w = newInputW; + } + + op.setPortGroup(uniName, group); + } + } + } + } + } + } + op.setPortGroup("uniforms", groupUniforms); +} + +function updateShader() +{ + if (!shader) return; + + shader.bindTextures = bindTextures.bind(this); + shader.setSource(vertexShader.get(), fragmentShader.get()); + + if (cgl.glVersion == 1) + { + cgl.gl.getExtension("OES_standard_derivatives"); + // cgl.gl.getExtension('OES_texture_float'); + // cgl.gl.getExtension('OES_texture_float_linear'); + // cgl.gl.getExtension('OES_texture_half_float'); + // cgl.gl.getExtension('OES_texture_half_float_linear'); + + shader.enableExtension("GL_OES_standard_derivatives"); + // shader.enableExtension("GL_OES_texture_float"); + // shader.enableExtension("GL_OES_texture_float_linear"); + // shader.enableExtension("GL_OES_texture_half_float"); + // shader.enableExtension("GL_OES_texture_half_float_linear"); + } + + countTexture = 0; + foundNames.length = 0; + + parseUniforms(vertexShader.get()); + parseUniforms(fragmentShader.get()); + + for (let j = 0; j < uniformTextures.length; j++) + for (let i = 0; i < foundNames.length; i++) + if (uniformTextures[j] && foundNames.indexOf(uniformTextures[j].name) == -1) + { + uniformTextures[j].remove(); + uniformTextures[j] = null; + } + + for (let j = 0; j < uniformInputs.length; j++) + for (let i = 0; i < foundNames.length; i++) + if (uniformInputs[j] && foundNames.indexOf(uniformInputs[j].name) == -1) + { + uniformInputs[j].remove(); + uniformInputs[j] = null; + } + + for (let j = 0; j < vectors.length; j++) + { + initVectorUniform(vectors[j]); + vectors[j].changed = true; + } + + for (let i = 0; i < uniformInputs.length; i++) + if (uniformInputs[i] && uniformInputs[i].uniform)uniformInputs[i].uniform.needsUpdate = true; + + shader.compile(); + + op.refreshParams(); + + outShader.set(null); + outShader.set(shader); + needsUpdate = false; + + if (shader.hasErrors()) op.setUiError("compile", "Shader has errors"); + else op.setUiError("compile", null); + + outErrors.set(shader.hasErrors()); +} + +function initVectorUniform(vec) +{ + if (vec.num == 2) vec.uni = new CGL.Uniform(shader, "2f", vec.name, [0, 0]); + else if (vec.num == 3) vec.uni = new CGL.Uniform(shader, "3f", vec.name, [0, 0, 0]); + else if (vec.num == 4) vec.uni = new CGL.Uniform(shader, "4f", vec.name, [0, 0, 0, 0]); +} + +function setVectorValues() +{ + for (let i = 0; i < vectors.length; i++) + { + const v = vectors[i]; + if (v.changed) + { + if (v.num === 2) v.uni.setValue([v.x.get(), v.y.get()]); + else if (v.num === 3) v.uni.setValue([v.x.get(), v.y.get(), v.z.get()]); + else if (v.num === 4) v.uni.setValue([v.x.get(), v.y.get(), v.z.get(), v.w.get()]); + + else if (v.num > 4) + { + v.uni.setValue(v.port.get()); + } + + v.changed = false; + } + } +} + + +}; + +Ops.Gl.Shader.CustomShader_v2.prototype = new CABLES.Op(); +CABLES.OPS["a165fc89-a35b-4d39-8930-7345b098bd9d"]={f:Ops.Gl.Shader.CustomShader_v2,objName:"Ops.Gl.Shader.CustomShader_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.ErrorMaterial +// +// ************************************************************** + +Ops.Gl.Shader.ErrorMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "errormaterial"); + +shader.setSource(CGL.Shader.getDefaultVertexShader(), CGL.Shader.getErrorFragmentShader()); +render.onTriggered = doRender; + +function doRender() +{ + cgl.pushShader(shader); + trigger.trigger(); + cgl.popShader(); +} + + +}; + +Ops.Gl.Shader.ErrorMaterial.prototype = new CABLES.Op(); +CABLES.OPS["a16c565a-574d-48d2-a95e-1bdce96dc00f"]={f:Ops.Gl.Shader.ErrorMaterial,objName:"Ops.Gl.Shader.ErrorMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.FrontBacksideMaterial +// +// ************************************************************** + +Ops.Gl.Shader.FrontBacksideMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"frontbacksidematerial_frag":"void main()\n{\n if(!gl_FrontFacing) outColor= vec4(1.0,0.0,0.0,1.0);\n else outColor=vec4(0.0,1.0,0.0,1.0);\n}",}; +const render=op.inTrigger("render"); +const next=op.outTrigger("next"); +const cgl=op.patch.cgl; + +const shader=new CGL.Shader(cgl,'showtexcoords material'); +shader.setSource(shader.getDefaultVertexShader(),attachments.frontbacksidematerial_frag); +render.onTriggered=doRender; + +function doRender() +{ + cgl.pushShader(shader); + next.trigger(); + cgl.popShader(); +} + + + +}; + +Ops.Gl.Shader.FrontBacksideMaterial.prototype = new CABLES.Op(); +CABLES.OPS["beb92b81-8fe8-4871-b790-efb71bfeefc4"]={f:Ops.Gl.Shader.FrontBacksideMaterial,objName:"Ops.Gl.Shader.FrontBacksideMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.GetShader +// +// ************************************************************** + +Ops.Gl.Shader.GetShader = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTrigger = op.inTrigger("Update"), + next = op.outTrigger("Next"), + outShader = op.outObject("Shader"); + +inTrigger.onTriggered = () => +{ + outShader.set(op.patch.cgl.getShader()); + next.trigger(); +}; + + +}; + +Ops.Gl.Shader.GetShader.prototype = new CABLES.Op(); +CABLES.OPS["5cca905a-d72b-4a07-9574-d4e4f9a8f300"]={f:Ops.Gl.Shader.GetShader,objName:"Ops.Gl.Shader.GetShader"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.MatCapMaterialNew_v3 +// +// ************************************************************** + +Ops.Gl.Shader.MatCapMaterialNew_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"matcap_frag":"{{MODULES_HEAD}}\n\n#ifdef HAS_TEXTURES\n IN vec2 texCoord;\n#endif\n\nIN vec3 transformedNormal;\nIN vec3 viewSpacePosition;\n\nUNI vec4 inColor;\n\nUNI sampler2D texMatcap;\n\n#ifdef HAS_DIFFUSE_TEXTURE\n UNI sampler2D texDiffuse;\n#endif\n\n#ifdef USE_SPECULAR_TEXTURE\n UNI sampler2D texSpec;\n UNI sampler2D texSpecMatCap;\n#endif\n\n#ifdef HAS_AO_TEXTURE\n UNI sampler2D texAo;\n UNI float aoIntensity;\n#endif\n\n#ifdef HAS_NORMAL_TEXTURE\n IN vec3 vBiTangent;\n IN vec3 vTangent;\n IN mat3 normalMatrix;\n\n UNI sampler2D texNormal;\n UNI float normalMapIntensity;\n#endif\n\n#ifdef HAS_TEXTURE_OPACITY\n UNI sampler2D texOpacity;\n#endif\n\n#ifdef CALC_SSNORMALS\n IN vec3 eye_relative_pos;\n\n // from https://www.enkisoftware.com/devlogpost-20150131-1-Normal_generation_in_the_pixel_shader\n vec3 CalculateScreenSpaceNormals() {\n \tvec3 dFdxPos = dFdx(eye_relative_pos);\n \tvec3 dFdyPos = dFdy(eye_relative_pos);\n \tvec3 screenSpaceNormal = normalize( cross(dFdxPos, dFdyPos));\n return normalize(screenSpaceNormal);\n }\n#endif\n\n// * taken & modified from https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderLib/meshmatcap_frag.glsl.js\nvec2 getMatCapUV(vec3 viewSpacePosition, vec3 normal) {\n vec3 viewDir = normalize(-viewSpacePosition);\n\tvec3 x = normalize(vec3(viewDir.z, 0.0, - viewDir.x));\n\tvec3 y = normalize(cross(viewDir, x));\n\tvec2 uv = vec2(dot(x, normal), dot(y, normal)) * 0.495 + 0.5; // 0.495 to remove artifacts caused by undersized matcap disks\n\treturn uv;\n}\n\nvoid main()\n{\n vec3 viewSpaceNormal = normalize(transformedNormal);\n\n #ifdef HAS_TEXTURES\n vec2 texCoords = texCoord;\n {{MODULE_BEGIN_FRAG}}\n #endif\n\n\n #ifdef CALC_SSNORMALS\n viewSpaceNormal = CalculateScreenSpaceNormals();\n #endif\n\n\n #ifdef HAS_NORMAL_TEXTURE\n vec3 normalFromMap = texture( texNormal, texCoord ).xyz * 2.0 - 1.0;\n normalFromMap = normalize(normalFromMap);\n\n vec3 tangent;\n vec3 binormal;\n\n #ifdef CALC_TANGENT\n vec3 c1 = cross(normalFromMap, vec3(0.0, 0.0, 1.0));\n vec3 c2 = cross(normalFromMap, vec3(0.0, 1.0, 0.0));\n\n tangent = c1;\n tangent = normalize(tangent);\n binormal = cross(viewSpaceNormal, tangent);\n binormal = normalize(binormal);\n #endif\n\n #ifndef CALC_TANGENT\n tangent = normalize(normalMatrix * vTangent);\n vec3 bitangent = normalize(normalMatrix * vBiTangent);\n binormal = normalize(cross(viewSpaceNormal, bitangent));\n #endif\n\n normalFromMap = normalize(\n tangent * normalFromMap.x\n + binormal * normalFromMap.y\n + viewSpaceNormal * normalFromMap.z\n );\n\n vec3 mixedNormal = normalize(viewSpaceNormal + normalFromMap * normalMapIntensity);\n\n viewSpaceNormal = mixedNormal;\n #endif\n\n vec4 col = texture(texMatcap, getMatCapUV(viewSpacePosition, viewSpaceNormal));\n\n #ifdef HAS_DIFFUSE_TEXTURE\n col = col*texture(texDiffuse, texCoords);\n #endif\n\n col.rgb *= inColor.rgb;\n\n\n #ifdef HAS_AO_TEXTURE\n col = col\n * mix(\n vec4(1.0,1.0,1.0,1.0),\n texture(texAo, texCoords),\n aoIntensity\n );\n #endif\n\n #ifdef USE_SPECULAR_TEXTURE\n vec4 spec = texture(texSpecMatCap, getMatCapUV(viewSpacePosition, viewSpaceNormal));\n spec *= texture(texSpec, texCoords);\n col += spec;\n #endif\n\n col.a *= inColor.a;\n\n #ifdef HAS_TEXTURE_OPACITY\n #ifdef TRANSFORMALPHATEXCOORDS\n texCoords=vec2(texCoord.s,1.0-texCoord.t);\n texCoords.y = 1. - texCoords.y;\n #endif\n #ifdef ALPHA_MASK_ALPHA\n col.a*=texture(texOpacity,texCoords).a;\n #endif\n #ifdef ALPHA_MASK_LUMI\n col.a*=dot(vec3(0.2126,0.7152,0.0722), texture(texOpacity,texCoords).rgb);\n #endif\n #ifdef ALPHA_MASK_R\n col.a*=texture(texOpacity,texCoords).r;\n #endif\n #ifdef ALPHA_MASK_G\n col.a*=texture(texOpacity,texCoords).g;\n #endif\n #ifdef ALPHA_MASK_B\n col.a*=texture(texOpacity,texCoords).b;\n #endif\n\n #ifdef DISCARDTRANS\n if(col.a < 0.2) discard;\n #endif\n #endif\n\n {{MODULE_COLOR}}\n\n outColor = col;\n}","matcap_vert":"IN vec3 vPosition;\n\n#ifdef HAS_TEXTURES\n IN vec2 attrTexCoord;\n#endif\n\nIN vec3 attrVertNormal;\nIN float attrVertIndex;\n\n#ifdef HAS_NORMAL_TEXTURE\n IN vec3 attrTangent;\n IN vec3 attrBiTangent;\n OUT vec3 vBiTangent;\n OUT vec3 vTangent;\n#endif\n\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\nUNI vec3 camPos;\n\n#ifdef HAS_TEXTURES\n UNI vec2 texOffset;\n UNI vec2 texRepeat;\n OUT vec2 texCoord;\n#endif\n\nOUT mat3 normalMatrix;\nOUT vec3 viewSpacePosition;\nOUT vec3 transformedNormal;\n\n{{MODULES_HEAD}}\n\n#ifdef CALC_SSNORMALS\n // from https://www.enkisoftware.com/devlogpost-20150131-1-Normal_generation_in_the_pixel_shader\n OUT vec3 eye_relative_pos;\n#endif\n\nmat3 transposeMat3(mat3 m) {\n return mat3(m[0][0], m[1][0], m[2][0],\n m[0][1], m[1][1], m[2][1],\n m[0][2], m[1][2], m[2][2]);\n}\n\n mat3 inverseMat3(mat3 m) {\n float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];\n float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];\n float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];\n\n float b01 = a22 * a11 - a12 * a21;\n float b11 = -a22 * a10 + a12 * a20;\n float b21 = a21 * a10 - a11 * a20;\n\n float det = a00 * b01 + a01 * b11 + a02 * b21;\n\n return mat3(b01, (-a22 * a01 + a02 * a21), (a12 * a01 - a02 * a11),\n b11, (a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10),\n b21, (-a21 * a00 + a01 * a20), (a11 * a00 - a01 * a10)) / det;\n}\n\nvoid main()\n{\n #ifdef HAS_TEXTURES\n texCoord = texRepeat * vec2(attrTexCoord.x, attrTexCoord.y) + texOffset;\n texCoord.y = 1. - texCoord.y;\n #endif\n\n mat4 mMatrix = modelMatrix;\n mat4 mvMatrix;\n\n #ifdef HAS_NORMAL_TEXTURE\n vec3 tangent = attrTangent;\n vec3 bitangent = attrBiTangent;\n vTangent = attrTangent;\n vBiTangent = attrBiTangent;\n #endif\n\n vec4 pos = vec4(vPosition, 1.);\n vec3 norm = attrVertNormal;\n\n {{MODULE_VERTEX_POSITION}}\n\n mvMatrix = viewMatrix * mMatrix;\n vec3 normal = norm;\n\n normalMatrix = transposeMat3(inverseMat3(mat3(mvMatrix)));\n\n vec3 fragPos = vec3((mvMatrix) * pos);\n viewSpacePosition = normalize(fragPos);\n\n #ifdef CALC_SSNORMALS\n eye_relative_pos = -(vec3(viewMatrix * vec4(camPos, 1.)) - fragPos);\n #endif\n\n transformedNormal = normalize(mat3(normalMatrix) * normal);\n\n gl_Position = projMatrix * mvMatrix * pos;\n\n}",}; +const cgl = op.patch.cgl; + +const + render = op.inTrigger("Render"), + textureMatcap = op.inTexture("MatCap"), + textureDiffuse = op.inTexture("Diffuse"), + textureNormal = op.inTexture("Normal"), + textureSpec = op.inTexture("Specular Mask"), + textureSpecMatCap = op.inTexture("Specular MatCap"), + textureAo = op.inTexture("AO Texture"), + textureOpacity = op.inTexture("Opacity Texture"), + r = op.inValueSlider("r", 1), + g = op.inValueSlider("g", 1), + b = op.inValueSlider("b", 1), + pOpacity = op.inValueSlider("Opacity", 1), + aoIntensity = op.inValueSlider("AO Intensity", 1.0), + normalMapIntensity = op.inFloatSlider("Normal Map Intensity", 1), + repeatX = op.inValue("Repeat X", 1), + repeatY = op.inValue("Repeat Y", 1), + offsetX = op.inValue("Offset X", 0), + offsetY = op.inValue("Offset Y", 0), + ssNormals = op.inValueBool("Screen Space Normals"), + calcTangents = op.inValueBool("Calc normal tangents", true), + texCoordAlpha = op.inValueBool("Opacity TexCoords Transform", false), + discardTransPxl = op.inValueBool("Discard Transparent Pixels"), + + next = op.outTrigger("Next"), + shaderOut = op.outObject("Shader"); + +r.setUiAttribs({ "colorPick": true }); + +const alphaMaskSource = op.inSwitch("Alpha Mask Source", ["Luminance", "R", "G", "B", "A"], "Luminance"); +alphaMaskSource.setUiAttribs({ "greyout": true }); + +op.setPortGroup("Texture Opacity", [alphaMaskSource, texCoordAlpha, discardTransPxl]); +op.setPortGroup("Texture Transforms", [aoIntensity, normalMapIntensity, repeatX, repeatY, offsetX, offsetY, calcTangents, ssNormals]); +op.setPortGroup("Texture Maps", [textureDiffuse, textureNormal, textureSpec, textureSpecMatCap, textureAo, textureOpacity]); +op.setPortGroup("Color", [r, g, b, pOpacity]); + +const shader = new CGL.Shader(cgl, "MatCapMaterialNew3"); +const uniOpacity = new CGL.Uniform(shader, "f", "opacity", pOpacity); + +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +shader.setSource(attachments.matcap_vert, attachments.matcap_frag); +shaderOut.set(shader); + +const textureMatcapUniform = new CGL.Uniform(shader, "t", "texMatcap"); +let textureDiffuseUniform = null; +let textureNormalUniform = null; +let normalMapIntensityUniform = null; +let textureSpecUniform = null; +let textureSpecMatCapUniform = null; +let textureAoUniform = null; +const offsetUniform = new CGL.Uniform(shader, "2f", "texOffset", offsetX, offsetY); +const repeatUniform = new CGL.Uniform(shader, "2f", "texRepeat", repeatX, repeatY); + +const aoIntensityUniform = new CGL.Uniform(shader, "f", "aoIntensity", aoIntensity); +const colorUniform = new CGL.Uniform(shader, "4f", "inColor", r, g, b, pOpacity); + +calcTangents.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + if (calcTangents.get()) shader.define("CALC_TANGENT"); + else shader.removeDefine("CALC_TANGENT"); +} + +ssNormals.onChange = function () +{ + if (ssNormals.get()) + { + if (cgl.glVersion < 2) + { + cgl.gl.getExtension("OES_standard_derivatives"); + shader.enableExtension("GL_OES_standard_derivatives"); + } + + shader.define("CALC_SSNORMALS"); + } + else shader.removeDefine("CALC_SSNORMALS"); +}; + +textureMatcap.onChange = updateMatcap; + +function updateMatcap() +{ + if (!cgl.defaultMatcapTex3) + { + const pixels = new Uint8Array(256 * 4); + for (let x = 0; x < 16; x++) + { + for (let y = 0; y < 16; y++) + { + let c = y * 16; + c *= Math.min(1, (x + y / 3) / 8); + pixels[(x + y * 16) * 4 + 0] = pixels[(x + y * 16) * 4 + 1] = pixels[(x + y * 16) * 4 + 2] = c; + pixels[(x + y * 16) * 4 + 3] = 255; + } + } + + cgl.defaultMatcapTex3 = new CGL.Texture(cgl); + cgl.defaultMatcapTex3.initFromData(pixels, 16, 16, CGL.Texture.FILTER_LINEAR, CGL.Texture.WRAP_REPEAT); + } +} + +textureDiffuse.onChange = function () +{ + if (textureDiffuse.get()) + { + if (textureDiffuseUniform !== null) return; + shader.define("HAS_DIFFUSE_TEXTURE"); + shader.removeUniform("texDiffuse"); + textureDiffuseUniform = new CGL.Uniform(shader, "t", "texDiffuse"); + } + else + { + shader.removeDefine("HAS_DIFFUSE_TEXTURE"); + shader.removeUniform("texDiffuse"); + textureDiffuseUniform = null; + } +}; + +textureNormal.onChange = function () +{ + if (textureNormal.get()) + { + if (textureNormalUniform !== null) return; + shader.define("HAS_NORMAL_TEXTURE"); + shader.removeUniform("texNormal"); + textureNormalUniform = new CGL.Uniform(shader, "t", "texNormal"); + if (!normalMapIntensityUniform) normalMapIntensityUniform = new CGL.Uniform(shader, "f", "normalMapIntensity", normalMapIntensity); + } + else + { + shader.removeDefine("HAS_NORMAL_TEXTURE"); + shader.removeUniform("texNormal"); + textureNormalUniform = null; + } +}; + +textureAo.onChange = function () +{ + if (textureAo.get()) + { + if (textureAoUniform !== null) return; + shader.define("HAS_AO_TEXTURE"); + shader.removeUniform("texAo"); + textureAoUniform = new CGL.Uniform(shader, "t", "texAo"); + } + else + { + shader.removeDefine("HAS_AO_TEXTURE"); + shader.removeUniform("texAo"); + textureAoUniform = null; + } +}; + +textureSpec.onChange = textureSpecMatCap.onChange = function () +{ + if (textureSpec.get() && textureSpecMatCap.get()) + { + if (textureSpecUniform !== null) return; + shader.define("USE_SPECULAR_TEXTURE"); + shader.removeUniform("texSpec"); + shader.removeUniform("texSpecMatCap"); + textureSpecUniform = new CGL.Uniform(shader, "t", "texSpec"); + textureSpecMatCapUniform = new CGL.Uniform(shader, "t", "texSpecMatCap"); + } + else + { + shader.removeDefine("USE_SPECULAR_TEXTURE"); + shader.removeUniform("texSpec"); + shader.removeUniform("texSpecMatCap"); + textureSpecUniform = null; + textureSpecMatCapUniform = null; + } +}; + +// TEX OPACITY + +function updateAlphaMaskMethod() +{ + if (alphaMaskSource.get() == "Alpha Channel") shader.define("ALPHA_MASK_ALPHA"); + else shader.removeDefine("ALPHA_MASK_ALPHA"); + + if (alphaMaskSource.get() == "Luminance") shader.define("ALPHA_MASK_LUMI"); + else shader.removeDefine("ALPHA_MASK_LUMI"); + + if (alphaMaskSource.get() == "R") shader.define("ALPHA_MASK_R"); + else shader.removeDefine("ALPHA_MASK_R"); + + if (alphaMaskSource.get() == "G") shader.define("ALPHA_MASK_G"); + else shader.removeDefine("ALPHA_MASK_G"); + + if (alphaMaskSource.get() == "B") shader.define("ALPHA_MASK_B"); + else shader.removeDefine("ALPHA_MASK_B"); +} +alphaMaskSource.onChange = updateAlphaMaskMethod; +textureOpacity.onChange = updateOpacity; + +let textureOpacityUniform = null; + +function updateOpacity() +{ + if (textureOpacity.get()) + { + if (textureOpacityUniform !== null) return; + shader.removeUniform("texOpacity"); + shader.define("HAS_TEXTURE_OPACITY"); + if (!textureOpacityUniform) textureOpacityUniform = new CGL.Uniform(shader, "t", "texOpacity"); + + alphaMaskSource.setUiAttribs({ "greyout": false }); + discardTransPxl.setUiAttribs({ "greyout": false }); + texCoordAlpha.setUiAttribs({ "greyout": false }); + } + else + { + shader.removeUniform("texOpacity"); + shader.removeDefine("HAS_TEXTURE_OPACITY"); + textureOpacityUniform = null; + + alphaMaskSource.setUiAttribs({ "greyout": true }); + discardTransPxl.setUiAttribs({ "greyout": true }); + texCoordAlpha.setUiAttribs({ "greyout": true }); + } + updateAlphaMaskMethod(); +} + +discardTransPxl.onChange = function () +{ + if (discardTransPxl.get()) shader.define("DISCARDTRANS"); + else shader.removeDefine("DISCARDTRANS"); +}; + +texCoordAlpha.onChange = function () +{ + if (texCoordAlpha.get()) shader.define("TRANSFORMALPHATEXCOORDS"); + else shader.removeDefine("TRANSFORMALPHATEXCOORDS"); +}; + +function checkUiErrors() +{ + if (textureSpec.get() && !textureSpecMatCap.get()) + { + op.setUiError("specNoMatCapSpec", "You connected a specular texture but have not connected a specular matcap texture. You need to connect both texture inputs for the specular input to work.", 1); + op.setUiError("noSpecMatCapSpec", null); + } + else if (!textureSpec.get() && textureSpecMatCap.get()) + { + op.setUiError("noSpecMatCapSpec", "You connected a specular matcap texture but have not connected a specular texture. You need to connect both texture inputs for the specular input to work.", 1); + op.setUiError("specNoMatCapSpec", null); + } + else if (textureSpec.get() && textureSpecMatCap.get()) + { + op.setUiError("specNoMatCapSpec", null); + op.setUiError("noSpecMatCapSpec", null); + } + else + { + op.setUiError("specNoMatCapSpec", null); + op.setUiError("noSpecMatCapSpec", null); + } +} + +render.onTriggered = function () +{ + checkUiErrors(); + + if (!cgl.defaultMatcapTex3) updateMatcap(); + shader.popTextures(); + + const tex = textureMatcap.get() || cgl.defaultMatcapTex3; + shader.pushTexture(textureMatcapUniform, tex.tex); + + if (textureDiffuse.get() && textureDiffuseUniform) shader.pushTexture(textureDiffuseUniform, textureDiffuse.get().tex); + if (textureNormal.get() && textureNormalUniform) shader.pushTexture(textureNormalUniform, textureNormal.get().tex); + if (textureSpec.get() && textureSpecUniform) shader.pushTexture(textureSpecUniform, textureSpec.get().tex); + if (textureSpecMatCap.get() && textureSpecMatCapUniform) shader.pushTexture(textureSpecMatCapUniform, textureSpecMatCap.get().tex); + if (textureAo.get() && textureAoUniform) shader.pushTexture(textureAoUniform, textureAo.get().tex); + if (textureOpacity.get() && textureOpacityUniform) shader.pushTexture(textureOpacityUniform, textureOpacity.get().tex); + + cgl.pushShader(shader); + next.trigger(); + cgl.popShader(); +}; + + +}; + +Ops.Gl.Shader.MatCapMaterialNew_v3.prototype = new CABLES.Op(); +CABLES.OPS["c1dd6e76-61b4-471a-b8d1-f550a5a9a4f4"]={f:Ops.Gl.Shader.MatCapMaterialNew_v3,objName:"Ops.Gl.Shader.MatCapMaterialNew_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.Picker +// +// ************************************************************** + +Ops.Gl.Shader.Picker = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +op.render = op.inTrigger("render"); + +const useMouseCoords = op.inBool("Use Mouse Coordinates", true); + +op.x = op.inFloat("x"); +op.y = op.inFloat("y"); +const inEnabled = op.inBool("enabled"); +inEnabled.set(true); + +op.trigger = op.outTrigger("trigger"); +const somethingPicked = op.outBool("Something Picked"); + +const cursor = op.inDropDown("cursor", ["", "pointer", "auto", "default", "crosshair", "move", "n-resize", "ne-resize", "e-resize", "se-resize", "s-resize", "sw-resize", "w-resize", "nw-resize", "text", "wait", "help"]); + +// inValueSelect +cursor.set("default"); + +const pixelRGB = new Uint8Array(4); +let fb = null; +const cgl = op.patch.cgl; +let lastReadPixel = 0; +let canceledTouch = false; +if (cgl.glVersion == 1) fb = new CGL.Framebuffer(cgl, 4, 4); +else +{ + fb = new CGL.Framebuffer2(cgl, 4, 4, { "multisampling": false }); +} + +const tex = op.outTexture("pick texture"); +tex.set(fb.getTextureColor()); +useMouseCoords.onChange = updateListeners; +updateListeners(); + +function renderPickingPass() +{ + cgl.frameStore.renderOffscreen = true; + cgl.frameStore.pickingpass = true; + cgl.frameStore.pickingpassNum = 0; + op.trigger.trigger(); + cgl.frameStore.pickingpass = false; + cgl.frameStore.renderOffscreen = false; +} + +function mouseMove(e) +{ + if (e && e.hasOwnProperty("offsetX") >= 0) + { + op.x.set(e.offsetX * (window.devicePixelRatio || 1)); + op.y.set(e.offsetY * (window.devicePixelRatio || 1)); + } +} + +function updateListeners() +{ + cgl.canvas.removeEventListener("mouseleave", mouseleave); + cgl.canvas.removeEventListener("mousemove", mouseMove); + cgl.canvas.removeEventListener("touchmove", ontouchmove); + cgl.canvas.removeEventListener("touchstart", ontouchstart); + cgl.canvas.removeEventListener("touchend", ontouchend); + cgl.canvas.removeEventListener("touchcancel", ontouchend); + + if (useMouseCoords.get()) + { + cgl.canvas.addEventListener("mouseleave", mouseleave); + cgl.canvas.addEventListener("mousemove", mouseMove); + cgl.canvas.addEventListener("touchmove", ontouchmove); + cgl.canvas.addEventListener("touchstart", ontouchstart); + cgl.canvas.addEventListener("touchend", ontouchend); + cgl.canvas.addEventListener("touchcancel", ontouchend); + } +} + +function fixTouchEvent(touchEvent) +{ + if (touchEvent) + { + touchEvent.offsetX = touchEvent.pageX - touchEvent.target.offsetLeft; + touchEvent.offsetY = touchEvent.pageY - touchEvent.target.offsetTop; + + if (!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) + { + touchEvent.offsetX *= (window.devicePixelRatio || 1); + touchEvent.offsetY *= (window.devicePixelRatio || 1); + } + + return touchEvent; + } +} + +function ontouchstart(event) +{ + canceledTouch = false; + if (event.touches && event.touches.length > 0) + { + ontouchmove(event); + } +} + +function mouseleave(event) +{ + op.x.set(-1000); + op.y.set(-1000); +} + +function ontouchend(event) +{ + canceledTouch = true; + op.x.set(-1000); + op.y.set(-1000); +} + +function ontouchmove(event) +{ + if (event.touches && event.touches.length > 0) + { + mouseMove(fixTouchEvent(event.touches[0])); + } +} + +const doRender = function () +{ + if (cursor.get() != cgl.canvas.style.cursor) + { + cgl.canvas.style.cursor = cursor.get(); + } + + if (inEnabled.get() && op.x.get() >= 0 && !canceledTouch) + { + if (CABLES.now() - lastReadPixel >= 50) + { + const minimizeFB = 2; + cgl.resetViewPort(); + + const vpW = Math.floor(cgl.canvasWidth / minimizeFB); + const vpH = Math.floor(cgl.canvasHeight / minimizeFB); + + if (vpW != fb.getWidth() || vpH != fb.getHeight()) + { + tex.set(null); + fb.setSize(vpW, vpH); + tex.set(fb.getTextureColor()); + } + + cgl.pushModelMatrix(); + fb.renderStart(); + // cgl.gl.clear(cgl.gl.DEPTH_BUFFER_BIT | cgl.gl.COLOR_BUFFER_BIT); + + renderPickingPass(); + + let x = Math.floor(op.x.get() / minimizeFB); + let y = Math.floor(vpH - op.y.get() / minimizeFB); + if (x < 0)x = 0; + if (y < 0)y = 0; + + cgl.gl.readPixels(x, y, 1, 1, cgl.gl.RGBA, cgl.gl.UNSIGNED_BYTE, pixelRGB); + lastReadPixel = CABLES.now(); + + fb.renderEnd(); + cgl.popModelMatrix(); + } + + cgl.frameStore.pickedColor = pixelRGB[0] + pixelRGB[2]; + + if (cgl.frameStore.pickedColor)somethingPicked.set(true); + else somethingPicked.set(false); + + cgl.frameStore.pickingpassNum = 0; + op.trigger.trigger(); + } + else + { + cgl.frameStore.pickedColor = -1000; + op.trigger.trigger(); + somethingPicked.set(false); + } +}; + +function preview() +{ + render(); + tex.get().preview(); +} + +// tex.onPreviewChanged = function () +// { +// if (tex.showPreview) op.render.onTriggered = doRender; +// else op.render.onTriggered = doRender; +// }; + +op.render.onTriggered = doRender; + + +}; + +Ops.Gl.Shader.Picker.prototype = new CABLES.Op(); +CABLES.OPS["09122fbf-3b6b-4a05-ac76-fca031b505b9"]={f:Ops.Gl.Shader.Picker,objName:"Ops.Gl.Shader.Picker"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.PickingMaterial +// +// ************************************************************** + +Ops.Gl.Shader.PickingMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"pick_frag":"UNI float r;\n\nvoid main()\n{\n outColor= vec4(r,1.0,r,1.0);\n}","pick_vert":"IN vec3 vPosition;\nUNI mat4 projMatrix;\n// UNI mat4 mvMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n{{MODULES_HEAD}}\n\nvoid main()\n{\n vec4 pos = vec4( vPosition, 1. );\n mat4 mMatrix=modelMatrix;\n\n #ifdef BILLBOARD\n mat4 mvMatrix=viewMatrix*mMatrix;\n\n vec3 position=vPosition;\n gl_Position = projMatrix * mvMatrix * vec4((\n position.x * vec3(\n mvMatrix[0][0],\n mvMatrix[1][0],\n mvMatrix[2][0] ) +\n position.y * vec3(\n mvMatrix[0][1],\n mvMatrix[1][1],\n mvMatrix[2][1]) ), 1.0);\n #endif\n\n\n {{MODULE_VERTEX_POSITION}}\n\n\n #ifndef BILLBOARD\n\n mat4 mvMatrix=viewMatrix*mMatrix;\n gl_Position = projMatrix * mvMatrix * vec4(vPosition, 1.0);\n #endif\n}",}; +const cgl = op.patch.cgl; + +const render = op.inTrigger("render"); +const next = op.outTrigger("trigger"); + +const isPicked = op.outBool("is picked"); + +const pickedTrigger = op.outTrigger("On Picked"); + +const doBillboard = op.inBool("billboard", false); // op.addInPort(new CABLES.Port(op, "billboard", CABLES.OP_PORT_TYPE_VALUE, { "display": "bool" })); +doBillboard.set(false); + +doBillboard.onChange = function () +{ + if (doBillboard.get()) shader.define("BILLBOARD"); + else shader.removeDefine("BILLBOARD"); +}; + +const cursor = op.inDropDown("cursor", ["pointer", "auto", "default", "crosshair", "move", "n-resize", "ne-resize", "e-resize", "se-resize", "s-resize", "sw-resize", "w-resize", "nw-resize", "text", "wait", "help"]); + +cursor.set("pointer"); + +function doRender() +{ + cgl.frameStore.pickingpassNum += 2; + const currentPickingColor = cgl.frameStore.pickingpassNum; + + if (cgl.frameStore.pickingpass) + { + // isPicked.set(false); + + pickColorUniformR.setValue(currentPickingColor / 511); + cgl.pushShader(shader); + next.trigger(); + cgl.popShader(); + } + else + { + isPicked.set(cgl.frameStore.pickedColor == currentPickingColor); + + if (cgl.frameStore.pickedColor == currentPickingColor) + { + if (cursor.get().length > 0 && cgl.canvas.style.cursor != cursor.get()) + { + cgl.canvas.style.cursor = cursor.get(); + } + pickedTrigger.trigger(); + } + else + { + } + + next.trigger(); + } +} + +const srcVert = attachments.pick_vert; + +const srcFrag = attachments.pick_frag; + +const shader = new CGL.Shader(cgl, "PickingMaterial"); +shader.offScreenPass = true; +shader.setSource(srcVert, srcFrag); + +const pickColorUniformR = new CGL.Uniform(shader, "f", "r", 0); + +render.onTriggered = doRender; +doRender(); + + +}; + +Ops.Gl.Shader.PickingMaterial.prototype = new CABLES.Op(); +CABLES.OPS["2b58daad-4dde-4edb-af22-03ac55ab06ab"]={f:Ops.Gl.Shader.PickingMaterial,objName:"Ops.Gl.Shader.PickingMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.PointMaterial_v4 +// +// ************************************************************** + +Ops.Gl.Shader.PointMaterial_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"pointmat_frag":"\n{{MODULES_HEAD}}\n\nUNI vec4 color;\n// IN vec2 pointCoord;\nIN float ps;\n\n#ifdef HAS_TEXTURE_DIFFUSE\n UNI sampler2D diffTex;\n#endif\n#ifdef HAS_TEXTURE_MASK\n UNI sampler2D texMask;\n#endif\n#ifdef HAS_TEXTURE_COLORIZE\n IN vec4 colorize;\n#endif\n#ifdef HAS_TEXTURE_OPACITY\n IN float opacity;\n#endif\n#ifdef VERTEX_COLORS\n IN vec4 vertexColor;\n#endif\n\nvoid main()\n{\n #ifdef FLIP_TEX\n vec2 pointCoord=vec2(gl_PointCoord.x,(1.0-gl_PointCoord.y));\n #endif\n #ifndef FLIP_TEX\n vec2 pointCoord=gl_PointCoord;\n #endif\n {{MODULE_BEGIN_FRAG}}\n\n if(ps<1.0)discard;\n\n vec4 col=color;\n\n #ifdef HAS_TEXTURE_MASK\n float mask;\n #ifdef TEXTURE_MASK_R\n mask=texture(texMask,pointCoord).r;\n #endif\n #ifdef TEXTURE_MASK_A\n mask=texture(texMask,pointCoord).a;\n #endif\n #ifdef TEXTURE_MASK_LUMI\n \tvec3 lumcoeff = vec3(0.299,0.587,0.114);\n \tmask = dot(texture(texMask,pointCoord).rgb, lumcoeff);\n #endif\n\n #endif\n\n #ifdef HAS_TEXTURE_DIFFUSE\n col=texture(diffTex,pointCoord);\n #ifdef COLORIZE_TEXTURE\n col.rgb*=color.rgb;\n #endif\n #endif\n col.a*=color.a;\n\n {{MODULE_COLOR}}\n\n #ifdef MAKE_ROUND\n\n #ifndef MAKE_ROUNDAA\n if ((gl_PointCoord.x-0.5)*(gl_PointCoord.x-0.5) + (gl_PointCoord.y-0.5)*(gl_PointCoord.y-0.5) > 0.25) discard; //col.a=0.0;\n #endif\n\n #ifdef MAKE_ROUNDAA\n float circ=(gl_PointCoord.x-0.5)*(gl_PointCoord.x-0.5) + (gl_PointCoord.y-0.5)*(gl_PointCoord.y-0.5);\n\n float a=smoothstep(0.25,0.25-fwidth(gl_PointCoord.x),circ);\n if(a==0.0)discard;\n col.a=a*color.a;\n #endif\n #endif\n\n #ifdef HAS_TEXTURE_COLORIZE\n col*=colorize;\n #endif\n\n #ifdef TEXTURE_COLORIZE_MUL\n col*=color;\n #endif\n\n #ifdef HAS_TEXTURE_MASK\n col.a*=mask;\n #endif\n\n #ifdef HAS_TEXTURE_OPACITY\n col.a*=opacity;\n #endif\n\n #ifdef VERTEX_COLORS\n col.rgb = vertexColor.rgb;\n col.a *= vertexColor.a;\n #endif\n\n if (col.a <= 0.0) discard;\n\n #ifdef HAS_TEXTURE_COLORIZE\n col*=colorize;\n #endif\n\n outColor = col;\n}\n","pointmat_vert":"{{MODULES_HEAD}}\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\nIN vec3 attrTangent;\nIN vec3 attrBiTangent;\n\n#ifdef VERTEX_COLORS\n IN vec4 attrVertColor;\n OUT vec4 vertexColor;\n#endif\n\nOUT vec3 norm;\nOUT float ps;\n\nOUT vec2 texCoord;\n\n\n#ifdef HAS_TEXTURES\n#endif\n\n#ifdef HAS_TEXTURE_COLORIZE\n UNI sampler2D texColorize;\n OUT vec4 colorize;\n#endif\n#ifdef HAS_TEXTURE_OPACITY\n UNI sampler2D texOpacity;\n OUT float opacity;\n#endif\n\n#ifdef HAS_TEXTURE_POINTSIZE\n UNI sampler2D texPointSize;\n UNI float texPointSizeMul;\n#endif\n\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\nUNI float pointSize;\nUNI vec3 camPos;\n\nUNI float canvasWidth;\nUNI float canvasHeight;\nUNI float camDistMul;\nUNI float randomSize;\n\nIN float attrVertIndex;\n\n\n\nfloat rand(float n){return fract(sin(n) * 5711.5711123);}\n\n#define POINTMATERIAL\n\nvoid main()\n{\n norm=attrVertNormal;\n #ifdef PIXELSIZE\n float psMul=1.0;\n #endif\n\n #ifndef PIXELSIZE\n float psMul=sqrt(canvasWidth/canvasHeight)+0.00000000001;\n #endif\n\n // float sizeMultiply=1.0;\n\n vec3 tangent=attrTangent;\n vec3 bitangent=attrBiTangent;\n\n\n #ifdef VERTEX_COLORS\n vertexColor=attrVertColor;\n #endif\n\n // #ifdef HAS_TEXTURES\n texCoord=attrTexCoord;\n // #endif\n\n #ifdef HAS_TEXTURE_OPACITY\n // opacity=texture(texOpacity,vec2(rand(attrVertIndex+texCoord.x*texCoord.y+texCoord.y+texCoord.x),rand(texCoord.y*texCoord.x-texCoord.x-texCoord.y-attrVertIndex))).r;\n opacity=texture(texOpacity,texCoord).r;\n #endif\n\n\n #ifdef HAS_TEXTURE_COLORIZE\n #ifdef RANDOM_COLORIZE\n colorize=texture(texColorize,vec2(rand(attrVertIndex+texCoord.x*texCoord.y+texCoord.y+texCoord.x),rand(texCoord.y*texCoord.x-texCoord.x-texCoord.y-attrVertIndex)));\n #endif\n #ifndef RANDOM_COLORIZE\n colorize=texture(texColorize,texCoord);\n #endif\n #endif\n\n\n\n\n\n mat4 mMatrix=modelMatrix;\n vec4 pos = vec4( vPosition, 1. );\n\n gl_PointSize=0.0;\n\n {{MODULE_VERTEX_POSITION}}\n\n vec4 model=mMatrix * pos;\n\n psMul+=rand(texCoord.x*texCoord.y+texCoord.y*3.0+texCoord.x*2.0+attrVertIndex)*randomSize;\n // psMul*=sizeMultiply;\n\n float addPointSize=0.0;\n #ifdef HAS_TEXTURE_POINTSIZE\n\n #ifdef POINTSIZE_CHAN_R\n addPointSize=texture(texPointSize,texCoord).r;\n #endif\n #ifdef POINTSIZE_CHAN_G\n addPointSize=texture(texPointSize,texCoord).g;\n #endif\n #ifdef POINTSIZE_CHAN_B\n addPointSize=texture(texPointSize,texCoord).b;\n #endif\n\n\n #ifdef DOTSIZEREMAPABS\n // addPointSize=(( (texture(texPointSize,texCoord).r) * texPointSizeMul)-0.5)*2.0;\n\n addPointSize=1.0-(distance(addPointSize,0.5)*2.0);\n // addPointSize=abs(1.0-(distance(addPointSize,0.5)*2.0));\n addPointSize=addPointSize*addPointSize*addPointSize*2.0;\n\n // addPointSize=(( (texture(texPointSize,texCoord).r) * texPointSizeMul)-0.5)*2.0;\n #endif\n\n addPointSize*=texPointSizeMul;\n\n #endif\n\n ps=0.0;\n #ifndef SCALE_BY_DISTANCE\n ps = (pointSize+addPointSize) * psMul;\n #endif\n #ifdef SCALE_BY_DISTANCE\n float cameraDist = distance(model.xyz, camPos);\n ps = ( (pointSize+addPointSize) / cameraDist) * psMul;\n #endif\n\n gl_PointSize += ps;\n\n\n gl_Position = projMatrix * viewMatrix * model;\n}\n",}; +const cgl = op.patch.cgl; + +const + render = op.inTrigger("render"), + pointSize = op.inValueFloat("PointSize", 3), + inPixelSize = op.inBool("Size in Pixels", false), + randomSize = op.inValue("Random Size", 0), + makeRound = op.inValueBool("Round", true), + makeRoundAA = op.inValueBool("Round Antialias", false), + doScale = op.inValueBool("Scale by Distance", false), + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + a = op.inValueSlider("a", 1), + vertCols = op.inBool("Vertex Colors", false), + texture = op.inTexture("texture"), + textureMulColor = op.inBool("Colorize Texture"), + textureMask = op.inTexture("Texture Mask"), + texMaskChan = op.inSwitch("Mask Channel", ["R", "A", "Luminance"], "R"), + textureColorize = op.inTexture("Texture Colorize"), + colorizeRandom = op.inValueBool("Colorize Randomize", true), + textureOpacity = op.inTexture("Texture Opacity"), + texturePointSize = op.inTexture("Texture Point Size"), + texturePointSizeChannel = op.inSwitch("Point Size Channel", ["R", "G", "B"], "R"), + texturePointSizeMul = op.inFloat("Texture Point Size Mul", 1), + texturePointSizeMap = op.inSwitch("Map Size 0", ["Black", "Grey"], "Black"), + flipTex = op.inValueBool("Flip Texture", false), + + trigger = op.outTrigger("trigger"), + shaderOut = op.outObject("shader", null, "shader"); + +op.setPortGroup("Texture", [texture, textureMulColor, textureMask, texMaskChan, textureColorize, textureOpacity, colorizeRandom]); +op.setPortGroup("Color", [r, g, b, a, vertCols]); +op.setPortGroup("Size", [pointSize, randomSize, makeRound, makeRoundAA, doScale, inPixelSize, texturePointSize, texturePointSizeMul, texturePointSizeChannel, texturePointSizeMap]); +r.setUiAttribs({ "colorPick": true }); + +const shader = new CGL.Shader(cgl, "PointMaterial"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +shader.define("MAKE_ROUND"); + +const + uniPointSize = new CGL.Uniform(shader, "f", "pointSize", pointSize), + texturePointSizeMulUniform = new CGL.Uniform(shader, "f", "texPointSizeMul", texturePointSizeMul), + uniRandomSize = new CGL.Uniform(shader, "f", "randomSize", randomSize), + uniColor = new CGL.Uniform(shader, "4f", "color", r, g, b, a), + uniWidth = new CGL.Uniform(shader, "f", "canvasWidth", cgl.canvasWidth), + uniHeight = new CGL.Uniform(shader, "f", "canvasHeight", cgl.canvasHeight), + textureUniform = new CGL.Uniform(shader, "t", "diffTex"), + textureColorizeUniform = new CGL.Uniform(shader, "t", "texColorize"), + textureOpacityUniform = new CGL.Uniform(shader, "t", "texOpacity"), + textureColoPointSize = new CGL.Uniform(shader, "t", "texPointSize"), + texturePointSizeUniform = new CGL.Uniform(shader, "t", "texPointSize"), + textureMaskUniform = new CGL.Uniform(shader, "t", "texMask"); + +shader.setSource(attachments.pointmat_vert, attachments.pointmat_frag); +shader.glPrimitive = cgl.gl.POINTS; +shaderOut.set(shader); +shaderOut.ignoreValueSerialize = true; + +render.onTriggered = doRender; +doScale.onChange = + makeRound.onChange = + makeRoundAA.onChange = + texture.onChange = + textureColorize.onChange = + textureMask.onChange = + colorizeRandom.onChange = + flipTex.onChange = + texMaskChan.onChange = + inPixelSize.onChange = + textureOpacity.onChange = + texturePointSize.onChange = + texturePointSizeMap.onChange = + texturePointSizeChannel.onChange = + textureMulColor.onChange = + vertCols.onChange = updateDefines; + +updateUi(); + +op.preRender = function () +{ + if (shader)shader.bind(); + doRender(); +}; + +function doRender() +{ + uniWidth.setValue(cgl.canvasWidth); + uniHeight.setValue(cgl.canvasHeight); + + cgl.pushShader(shader); + shader.popTextures(); + if (texture.get() && !texture.get().deleted) shader.pushTexture(textureUniform, texture.get()); + if (textureMask.get()) shader.pushTexture(textureMaskUniform, textureMask.get()); + if (textureColorize.get()) shader.pushTexture(textureColorizeUniform, textureColorize.get()); + if (textureOpacity.get()) shader.pushTexture(textureOpacityUniform, textureOpacity.get()); + if (texturePointSize.get()) shader.pushTexture(texturePointSizeUniform, texturePointSize.get()); + + trigger.trigger(); + + cgl.popShader(); +} + +function updateUi() +{ + texMaskChan.setUiAttribs({ "greyout": !textureMask.isLinked() }); + + texturePointSizeChannel.setUiAttribs({ "greyout": !texturePointSize.isLinked() }); + texturePointSizeMul.setUiAttribs({ "greyout": !texturePointSize.isLinked() }); + texturePointSizeMap.setUiAttribs({ "greyout": !texturePointSize.isLinked() }); +} + +function updateDefines() +{ + shader.toggleDefine("SCALE_BY_DISTANCE", doScale.get()); + shader.toggleDefine("MAKE_ROUND", makeRound.get()); + shader.toggleDefine("MAKE_ROUNDAA", makeRoundAA.get()); + + shader.toggleDefine("VERTEX_COLORS", vertCols.get()); + shader.toggleDefine("RANDOM_COLORIZE", colorizeRandom.get()); + shader.toggleDefine("HAS_TEXTURE_DIFFUSE", texture.get()); + shader.toggleDefine("HAS_TEXTURE_MASK", textureMask.get()); + shader.toggleDefine("HAS_TEXTURE_COLORIZE", textureColorize.get()); + shader.toggleDefine("HAS_TEXTURE_OPACITY", textureOpacity.get()); + shader.toggleDefine("HAS_TEXTURE_POINTSIZE", texturePointSize.get()); + + shader.toggleDefine("TEXTURE_COLORIZE_MUL", textureMulColor.get()); + + shader.toggleDefine("FLIP_TEX", flipTex.get()); + shader.toggleDefine("TEXTURE_MASK_R", texMaskChan.get() == "R"); + shader.toggleDefine("TEXTURE_MASK_A", texMaskChan.get() == "A"); + shader.toggleDefine("TEXTURE_MASK_LUMI", texMaskChan.get() == "Luminance"); + shader.toggleDefine("PIXELSIZE", inPixelSize.get()); + + shader.toggleDefine("POINTSIZE_CHAN_R", texturePointSizeChannel.get() == "R"); + shader.toggleDefine("POINTSIZE_CHAN_G", texturePointSizeChannel.get() == "G"); + shader.toggleDefine("POINTSIZE_CHAN_B", texturePointSizeChannel.get() == "B"); + + shader.toggleDefine("DOTSIZEREMAPABS", texturePointSizeMap.get() == "Grey"); + updateUi(); +} + + +}; + +Ops.Gl.Shader.PointMaterial_v4.prototype = new CABLES.Op(); +CABLES.OPS["a7cb5d1c-cd4a-4c28-bb13-7bb9bda187ed"]={f:Ops.Gl.Shader.PointMaterial_v4,objName:"Ops.Gl.Shader.PointMaterial_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.PositionAsColorMaterial +// +// ************************************************************** + +Ops.Gl.Shader.PositionAsColorMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"pos_frag":"IN vec3 vert;\n\nvoid main()\n{\n vec4 col=vec4(vert,1.0);\n\n\n outColor= col;\n}","pos_vert":"IN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal,attrTangent,attrBiTangent;\nOUT mat4 mMatrix;\nOUT vec3 vert;\nOUT mat4 mvMatrix;\nUNI mat4 projMatrix;\n\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\n{{MODULES_HEAD}}\n\nvoid main()\n{\n\n vec4 pos=vec4(vPosition,1.0);\n mMatrix=modelMatrix;\n\n\n\n {{MODULE_VERTEX_POSITION}}\n\n mat4 mvMatrix=viewMatrix*mMatrix;\n\n\n vert=(mMatrix*pos).xyz;\n\n\n gl_Position = projMatrix * mvMatrix * pos;\n}",}; +const render = op.inTrigger("render"); +const trigger = op.outTrigger("trigger"); +const cgl = op.patch.cgl; + +function doRender() +{ + cgl.pushShader(shader); + trigger.trigger(); + cgl.popShader(); +} + +const shader = new CGL.Shader(cgl, "showtexcoords material"); + +shader.setSource(attachments.pos_vert, attachments.pos_frag); + +render.onTriggered = doRender; +doRender(); + + +}; + +Ops.Gl.Shader.PositionAsColorMaterial.prototype = new CABLES.Op(); +CABLES.OPS["a654bbb7-bbde-4178-9fef-3209d0c352a4"]={f:Ops.Gl.Shader.PositionAsColorMaterial,objName:"Ops.Gl.Shader.PositionAsColorMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.SetShader +// +// ************************************************************** + +Ops.Gl.Shader.SetShader = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const render = op.inTrigger("render"); +const shader = op.inObject("shader", null, "shader"); +const trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +shader.ignoreValueSerialize = true; +render.onTriggered = doRender; + +function doRender() +{ + if (shader.get()) + { + cgl.pushShader(shader.get()); + if (shader.get().bindTextures) shader.get().bindTextures(); + trigger.trigger(); + cgl.popShader(); + } + else + { + trigger.trigger(); + } +} + + +}; + +Ops.Gl.Shader.SetShader.prototype = new CABLES.Op(); +CABLES.OPS["11d24181-1f1b-4131-bba0-3df410385a66"]={f:Ops.Gl.Shader.SetShader,objName:"Ops.Gl.Shader.SetShader"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.SetUniformFloat_v2 +// +// ************************************************************** + +Ops.Gl.Shader.SetUniformFloat_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// should be called setuniformfloat ? + +const + inRender = op.inTrigger("Render"), + inSelect = op.inValueSelect("Uniform"), + inX = op.inValue("X", 1), + inY = op.inValue("Y", 1), + inZ = op.inValue("Z", 1), + inW = op.inValue("W", 1), + next = op.outTrigger("Next"), + outType = op.outString("Type"); + +let shader = null; +const cgl = op.patch.cgl; +let doSetupUniform = true; +let uniform = null; +let shaderLastCompile = -1; +let unis = []; + +inRender.onTriggered = function () +{ + if (cgl.getShader() && (shader != cgl.getShader() || shader.lastCompile != shaderLastCompile)) + { + shader = cgl.getShader(); + setupShader(); + doSetupUniform = true; + } + + if (doSetupUniform) setupUniform(); + + if (uniform) + { + outType.set(uniform.getType()); + const oldValue = uniform.getValue(); + + uniform.setValue([inX.get(), inY.get(), inZ.get(), inW.get()]); + + next.trigger(); + uniform.setValue(oldValue); + } + else + { + next.trigger(); + } +}; + +inSelect.onChange = function () +{ + doSetupUniform = true; +}; + +function setupUniform() +{ + if (shader) + { + uniform = shader.getUniform((inSelect.get() || "").split(" ")[0]); + + if (uniform) + { + inY.setUiAttribs({ "greyout": uniform.getType() == "f" }); + inZ.setUiAttribs({ "greyout": uniform.getType() == "f" || uniform.getType() == "2f" }); + inW.setUiAttribs({ "greyout": uniform.getType() == "f" || uniform.getType() == "2f" || uniform.getType() == "3f" }); + } + + if (!uniform) op.setUiError("nouni", "uniform unknown", 1);// op.uiAttr({ "error": "uniform unknown. maybe shader changed" }); + else op.setUiError("nouni", null); + + doSetupUniform = false; + } +} + +function setupShader() +{ + unis = shader.getUniforms(); + + shaderLastCompile = shader.lastCompile; + const names = ["..."]; + + for (let i = 0; i < unis.length; i++) + { + if (unis[i].getType() == "f" || unis[i].getType() == "2f" || unis[i].getType() == "3f" || unis[i].getType() == "4f") + names.push(unis[i].getName() + " (" + unis[i].getType() + ")"); + } + + inSelect.setUiAttribs({ "values": names }); +} + + +}; + +Ops.Gl.Shader.SetUniformFloat_v2.prototype = new CABLES.Op(); +CABLES.OPS["9e6328da-457d-4d80-a881-dcab25a0e0f8"]={f:Ops.Gl.Shader.SetUniformFloat_v2,objName:"Ops.Gl.Shader.SetUniformFloat_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.SetUniformTexture +// +// ************************************************************** + +Ops.Gl.Shader.SetUniformTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inRender = op.inTrigger("Render"), + inSelect = op.inValueSelect("Uniform"), + // inValue = op.inValue("Value"), + inValue = op.inTexture("Texture"), + next = op.outTrigger("Next"), + outType = op.outString("Type"); + +let shader = null; +const cgl = op.patch.cgl; +let doSetupUniform = true; +let uniform = null; +let shaderLastCompile = -1; +let unis = []; +let old = null; + +inRender.onTriggered = function () +{ + if (cgl.getShader() && (shader != cgl.getShader() || shader.lastCompile != shaderLastCompile)) + { + shader = cgl.getShader(); + setupShader(); + doSetupUniform = true; + } + + if (doSetupUniform) setupUniform(); + + if (uniform) + { + // outType.set(uniform.getType()); + // const oldValue = uniform.getValue(); + + // shader.pushTexture(uniform, inValue.get()); + + old = shader.setUniformTexture(uniform, inValue.get()); + } + CGL.MESH.lastShader = null; + CGL.MESH.lastMesh = null; + + next.trigger(); + + if (uniform && old) shader.setUniformTexture(uniform, old); + CGL.MESH.lastShader = null; + CGL.MESH.lastMesh = null; +}; + +inSelect.onChange = function () +{ + doSetupUniform = true; +}; + +function setupUniform() +{ + if (shader) + { + uniform = shader.getUniform((inSelect.get() || "").split(" ")[0]); + + if (!uniform) op.setUiError("nouni", "uniform unknown", 1);// op.uiAttr({ "error": "uniform unknown. maybe shader changed" }); + else op.setUiError("nouni", null); + + doSetupUniform = false; + } +} + +function setupShader() +{ + unis = shader.getUniforms(); + + shaderLastCompile = shader.lastCompile; + const names = ["..."]; + + for (let i = 0; i < unis.length; i++) + if (unis[i].getType() == "t") + names.push(unis[i].getName() + " (" + unis[i].getType() + ")"); + + inSelect.setUiAttribs({ "values": names }); +} + + +}; + +Ops.Gl.Shader.SetUniformTexture.prototype = new CABLES.Op(); +CABLES.OPS["e43be3e0-b75b-4881-95c3-2c6ee81bff8c"]={f:Ops.Gl.Shader.SetUniformTexture,objName:"Ops.Gl.Shader.SetUniformTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.Shader2Texture +// +// ************************************************************** + +Ops.Gl.Shader.Shader2Texture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Render"), + inShader = op.inObject("Shader", null, "shader"), + inVPSize = op.inValueBool("Use Viewport Size", true), + inWidth = op.inValueInt("Width", 512), + inHeight = op.inValueInt("Height", 512), + tfilter = op.inValueSelect("filter", ["nearest", "linear", "mipmap"]), + twrap = op.inValueSelect("wrap", ["clamp to edge", "repeat", "mirrored repeat"], "clamp to edge"), + inFloatingPoint = op.inValueBool("Floating Point", false), + inNumTex = op.inSwitch("Num Textures", ["1", "4"], "1"), + next = op.outTrigger("Next"), + outTex = op.outTexture("Texture"), + outTex2 = op.outTexture("Texture 2"), + outTex3 = op.outTexture("Texture 3"), + outTex4 = op.outTexture("Texture 4"); + +op.setPortGroup("Texture Size", [inVPSize, inWidth, inHeight]); +op.setPortGroup("Texture settings", [tfilter, twrap, inFloatingPoint]); + +let numTextures = 1; +const cgl = op.patch.cgl; +const prevViewPort = [0, 0, 0, 0]; +const effect = null; +const drawBuffArr = []; +let lastShader = null; +let shader = null; + +inWidth.onChange = + inHeight.onChange = + inFloatingPoint.onChange = + tfilter.onChange = + inNumTex.onChange = + twrap.onChange = initFbLater; + +inVPSize.onChange = updateUI; + +const showingError = false; + +let fb = null; +const tex = null; +let needInit = true; + +const mesh = CGL.MESHES.getSimpleRect(cgl, "shader2texture rect"); + +op.toWorkPortsNeedToBeLinked(inShader); + +tfilter.set("nearest"); + +updateUI(); + +function warning() +{ + if (tfilter.get() == "mipmap" && inFloatingPoint.get()) + { + op.setUiError("warning", "HDR and mipmap filtering at the same time is not possible"); + } + else + { + op.setUiError("warning", null); + } +} + +function updateUI() +{ + inWidth.setUiAttribs({ "greyout": inVPSize.get() }); + inHeight.setUiAttribs({ "greyout": inVPSize.get() }); + + inWidth.set(cgl.getViewPort()[2]); + inHeight.set(cgl.getViewPort()[3]); +} + +function initFbLater() +{ + needInit = true; + warning(); +} + +function resetShader() +{ + if (shader) shader.dispose(); + lastShader = null; + shader = null; +} + +function initFb() +{ + needInit = false; + if (fb)fb.delete(); + + const oldLen = drawBuffArr.length; + numTextures = parseInt(inNumTex.get()); + drawBuffArr.length = 0; + for (let i = 0; i < numTextures; i++)drawBuffArr[i] = true; + + if (oldLen != drawBuffArr.length) + { + resetShader(); + } + + fb = null; + + let w = inWidth.get(); + let h = inHeight.get(); + + if (inVPSize.get()) + { + w = cgl.getViewPort()[2]; + h = cgl.getViewPort()[3]; + } + + let filter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "linear") filter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") filter = CGL.Texture.FILTER_MIPMAP; + + let selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + if (twrap.get() == "repeat") selectedWrap = CGL.Texture.WRAP_REPEAT; + if (twrap.get() == "mirrored repeat") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + + if (cgl.glVersion >= 2) + { + fb = new CGL.Framebuffer2(cgl, w, h, + { + "isFloatingPointTexture": inFloatingPoint.get(), + "multisampling": false, + "numRenderBuffers": numTextures, + "wrap": selectedWrap, + "filter": filter, + "depth": true, + "multisamplingSamples": 0, + "clear": true + }); + } + else + { + fb = new CGL.Framebuffer(cgl, inWidth.get(), inHeight.get(), + { + "isFloatingPointTexture": inFloatingPoint.get(), + "filter": filter, + "wrap": selectedWrap + }); + } +} + +exec.onTriggered = function () +{ + const vp = cgl.getViewPort(); + + if (!fb || needInit)initFb(); + if (inVPSize.get() && fb && (vp[2] != fb.getTextureColor().width || vp[3] != fb.getTextureColor().height)) initFb(); + + if (!inShader.get() || !inShader.get().setDrawBuffers) return; + + if (inShader.get() != lastShader) + { + lastShader = inShader.get(); + shader = inShader.get().copy(); + + shader.setDrawBuffers(drawBuffArr); + } + + if (!shader) + { + outTex.set(null); + return; + } + + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + fb.renderStart(cgl); + + cgl.pushPMatrix(); + mat4.identity(cgl.pMatrix); + + cgl.pushViewMatrix(); + mat4.identity(cgl.vMatrix); + + cgl.pushModelMatrix(); + mat4.identity(cgl.mMatrix); + + cgl.pushShader(inShader.get()); + if (shader.bindTextures) shader.bindTextures(); + + cgl.pushBlend(false); + + mesh.render(inShader.get()); + + cgl.popBlend(); + + cgl.popPMatrix(); + cgl.popModelMatrix(); + cgl.popViewMatrix(); + fb.renderEnd(cgl); + + if (numTextures >= 2) + { + outTex.set(fb.getTextureColorNum(0)); + outTex2.set(fb.getTextureColorNum(1)); + outTex3.set(fb.getTextureColorNum(2)); + outTex4.set(fb.getTextureColorNum(3)); + } + else outTex.set(fb.getTextureColor()); + + cgl.popShader(); + + cgl.gl.viewport(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); + + next.trigger(); +}; + + +}; + +Ops.Gl.Shader.Shader2Texture.prototype = new CABLES.Op(); +CABLES.OPS["a3debb76-7d84-4548-9e7b-24891423dcce"]={f:Ops.Gl.Shader.Shader2Texture,objName:"Ops.Gl.Shader.Shader2Texture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.ShaderDefine +// +// ************************************************************** + +Ops.Gl.Shader.ShaderDefine = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inShader=op.inObject("Shader"), + inDefineName=op.inString("Name"), + inDefineValue=op.inString("Value"), + inActive=op.inValueBool("Active") + ; + + +inShader.onChange= + inDefineName.onChange= + inDefineValue.onChange= + inActive.onChange=update; +update(); + +function update() +{ + var shader=inShader.get(); + if(!shader) + { + return; + } + + if(!inActive.get()) shader.removeDefine(inDefineName.get()); + else shader.define(inDefineName.get(),inDefineValue.get()); + +} + +}; + +Ops.Gl.Shader.ShaderDefine.prototype = new CABLES.Op(); +CABLES.OPS["1f870586-1ae4-4b7e-9dd7-24c300c989ba"]={f:Ops.Gl.Shader.ShaderDefine,objName:"Ops.Gl.Shader.ShaderDefine"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.ShaderInfo +// +// ************************************************************** + +Ops.Gl.Shader.ShaderInfo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Exec"), + showFrag = op.inTriggerButton("Show Fragment"), + showVert = op.inTriggerButton("Show Vertex"), + showModules = op.inTriggerButton("Show Modules"), + showUniforms = op.inTriggerButton("Show Uniforms"), + showState = op.inTriggerButton("State Info"), + next = op.outTrigger("Next"), + + outSrcFrag = op.outString("Source Frag"), + outSrcVert = op.outString("Source Vert"), + + outName = op.outString("Name"), + outId = op.outString("Id"), + outNeedsBarycentric = op.outBoolNum("needsBarycentric"), + outNumUniforms = op.outNumber("Num Uniforms"), + outNumAttributes = op.outNumber("Num Attributes"), + outAttributeNames = op.outArray("Arributes Names"), + outDefines = op.outArray("Num Defines"); + +const cgl = op.patch.cgl; +let shader = null; + +function showCodeModal(title, code, type) +{ + if (!CABLES.UI || !CABLES.UI.ModalDialog) + { + op.log(title, code); + } + + let html = ""; + html += "

    Code

    "; + html += "" + title + " "; + html += "

    "; + html += "

    "; + + code = code || ""; + code = code.replace(/\/g, ">"); // for > + + html += "
    " + code + "
    "; + + new CABLES.UI.ModalDialog({ + "title": title, + "html": html + }); +} + +showFrag.onTriggered = function () +{ + if (CABLES.UI && shader) showCodeModal("fragment shader", shader.finalShaderFrag, "GLSL"); +}; + +showVert.onTriggered = function () +{ + if (CABLES.UI && shader) showCodeModal("vertex shader", shader.finalShaderVert, "GLSL"); +}; + +let doStateDump = false; +let doUniformDump = false; + +showState.onTriggered = function () +{ + if (!CABLES.UI || !shader) return; + doStateDump = true; +}; + +showUniforms.onTriggered = function () +{ + if (!CABLES.UI || !shader) return; + doUniformDump = true; +}; + +exec.onTriggered = function () +{ + if (cgl.frameStore.shadowPass) return; + shader = cgl.getShader(); + next.trigger(); + + shader.bind(); + + if (!shader.getProgram()) op.setUiError("prognull", "Shader is not compiled"); + else op.setUiError("prognull", null); + + if (!shader) op.setUiError("noshader", "No Shader.."); + else op.setUiError("noshader", null); + + if (shader && shader.getProgram()) + { + const activeUniforms = cgl.gl.getProgramParameter(shader.getProgram(), cgl.gl.ACTIVE_UNIFORMS); + outNumUniforms.set(activeUniforms); + outNumAttributes.set(cgl.gl.getProgramParameter(shader.getProgram(), cgl.gl.ACTIVE_ATTRIBUTES)); + + let i = 0; + const attribNames = []; + for (i = 0; i < cgl.gl.getProgramParameter(shader.getProgram(), cgl.gl.ACTIVE_ATTRIBUTES); i++) + { + const name = cgl.gl.getActiveAttrib(shader.getProgram(), i).name; + attribNames.push(name); + } + outAttributeNames.set(attribNames); + outDefines.set(shader.getDefines()); + outName.set(shader.getName()); + outNeedsBarycentric.set(shader.wireframe); + outId.set(shader.id); + + op.setUiError("prognull", null); + } + else + { + outNumUniforms.set(0); + outNumAttributes.set(0); + outDefines.set(0); + outAttributeNames.set(null); + } + + if (doUniformDump) + { + const json = []; + for (let i = 0; i < shader._uniforms.length; i++) + { + json.push({ + "validLoc": shader._uniforms[i]._isValidLoc(), + "name": shader._uniforms[i]._name, + "type": shader._uniforms[i]._type, + "value": shader._uniforms[i]._value, + "structName": shader._uniforms[i]._structName, + "structUniformName": shader._uniforms[i]._structUniformName + }); + } + showCodeModal("shader uniforms", JSON.stringify(json, false, 2), "json"); + + doUniformDump = false; + } + + if (doStateDump) + { + doStateDump = false; + stateDump(); + } + + outSrcFrag.set(shader.finalShaderFrag); + outSrcVert.set(shader.finalShaderVert); +}; + +function stateDump() +{ + let txt = ""; + txt += ""; + + txt += "defines (" + outDefines.get().length + ")\n\n"; + + for (let i = 0; i < outDefines.get().length; i++) + { + txt += "- "; + txt += outDefines.get()[i][0]; + if (outDefines.get()[i][1]) + { + txt += ": "; + txt += outDefines.get()[i][1]; + } + txt += "\n"; + } + + txt += "\n\n"; + txt += "texturestack (" + shader._textureStackUni.length + ")\n\n"; + + for (let i = 0; i < shader._textureStackUni.length; i++) + { + txt += "- "; + txt += shader._textureStackUni[i]._name; + txt += "(" + shader._textureStackUni[i].shaderType + ")\n"; + if (shader._textureStackTexCgl[i]) txt += JSON.stringify(shader._textureStackTexCgl[i].getInfo()); + txt += "\n"; + } + + txt += "\n\n"; + txt += "uniforms: (" + shader._uniforms.length + ")\n\n"; + + for (let i = 0; i < shader._uniforms.length; i++) + { + txt += "- "; + txt += shader._uniforms[i]._name; + txt += ": "; + txt += shader._uniforms[i].getValue(); + + if (shader._uniforms[i].comment) + { + txt += " // "; + txt += shader._uniforms[i].comment; + } + txt += "\n"; + } + + showCodeModal("state info", txt); +} + +showModules.onTriggered = function () +{ + if (!shader) return; + const mods = shader.getCurrentModules(); + + showCodeModal("vertex shader", JSON.stringify(mods, false, 4), "json"); +}; + +// + + +}; + +Ops.Gl.Shader.ShaderInfo.prototype = new CABLES.Op(); +CABLES.OPS["7afaa77e-a976-4e65-8eb1-a6302b91c0d3"]={f:Ops.Gl.Shader.ShaderInfo,objName:"Ops.Gl.Shader.ShaderInfo"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.SwitchShader +// +// ************************************************************** + +Ops.Gl.Shader.SwitchShader = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + value = op.inValueBool("value"), + shader = op.inObject("shader true"), + shader2 = op.inObject("shader false"), + trigger = op.outTrigger("trigger"), + shaderOut = op.outObject("shaderOut"); + +const cgl = op.patch.cgl; + +shaderOut.ignoreValueSerialize = true; +shader.ignoreValueSerialize = true; +shader2.ignoreValueSerialize = true; + +render.onTriggered = doRender; +doRender(); + +function doRender() +{ + if (value.get()) + { + if (shader.get()) + { + cgl.pushShader(shader.get()); + shaderOut.set(shader.get()); + shader.get().bindTextures(); + trigger.trigger(); + cgl.popShader(); + } + } + else + { + if (shader2.get()) + { + cgl.pushShader(shader2.get()); + shaderOut.set(shader2.get()); + shader2.get().bindTextures(); + trigger.trigger(); + cgl.popShader(); + } + } +} + + +}; + +Ops.Gl.Shader.SwitchShader.prototype = new CABLES.Op(); +CABLES.OPS["34ae93a7-ceb4-4e02-8d6f-a38f5e6c732b"]={f:Ops.Gl.Shader.SwitchShader,objName:"Ops.Gl.Shader.SwitchShader"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.VertexColorMaterial +// +// ************************************************************** + +Ops.Gl.Shader.VertexColorMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"vertexcolor_frag":"{{MODULES_HEAD}}\n\nIN vec4 vertColor;\nUNI float opacity;\n\nvoid main()\n{\n{{MODULE_BEGIN_FRAG}}\n vec4 col=vertColor;\n{{MODULE_COLOR}}\n\n col.a=opacity;\n outColor= col;\n}","vertexcolor_vert":"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nIN vec3 attrVertColor;\nOUT vec4 vertColor;\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\nIN vec3 attrVertNormal;\n\nvoid main()\n{\n mat4 mMatrix=modelMatrix;\n vertColor.rgb=attrVertColor;\n vec3 norm=attrVertNormal;\n vec4 pos = vec4( vPosition, 1. );\n\n {{MODULE_VERTEX_POSITION}}\n\n mat4 mvMatrix=viewMatrix*mMatrix;\n\n gl_Position = projMatrix * mvMatrix * pos;\n}",}; +const cgl = op.patch.cgl, + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + opacity = op.inValueFloat("opacity", 1); + +const shader = new CGL.Shader(cgl, "vertex color material"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +shader.uniOpacity = new CGL.Uniform(shader, "f", "opacity", opacity.get()); +shader.setSource(attachments.vertexcolor_vert, attachments.vertexcolor_frag); + +render.onTriggered = doRender; + +opacity.onChange = function () +{ + shader.uniOpacity.setValue(opacity.get()); +}; + +function doRender() +{ + cgl.pushShader(shader); + trigger.trigger(); + cgl.popShader(); +} + + +}; + +Ops.Gl.Shader.VertexColorMaterial.prototype = new CABLES.Op(); +CABLES.OPS["a01d592b-0e5e-48f3-a9fd-54b87ffd623b"]={f:Ops.Gl.Shader.VertexColorMaterial,objName:"Ops.Gl.Shader.VertexColorMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.VertexNumberMaterial +// +// ************************************************************** + +Ops.Gl.Shader.VertexNumberMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +const render = op.inTrigger("render"); +const trigger = op.outTrigger("trigger"); + +const srcVert = "" + .endl() + "IN float attrVertIndex;" + .endl() + "UNI mat4 projMatrix;" + .endl() + "UNI mat4 mvMatrix;" + .endl() + "IN vec3 vPosition;" + .endl() + "OUT float num;" + + .endl() + "void main()" + .endl() + "{" + .endl() + " num=attrVertIndex;" + .endl() + " gl_Position = projMatrix * mvMatrix * vec4(vPosition, 1.0);" + .endl() + "}"; + +const srcFrag = "" + .endl() + "IN float num;" + .endl() + "UNI float numVertices;" + + .endl() + "void main()" + .endl() + "{" + + .endl() + "float c = num/numVertices/3.0;" + .endl() + "c = mod(c,0.1)*10.0;" + + .endl() + " outColor= vec4(c,c,c,1.0);" + + .endl() + "}"; + +function doRender() +{ + cgl.pushShader(shader); + trigger.trigger(); + cgl.popShader(); +} + +const shader = new CGL.Shader(cgl, "vertexnumber material"); +shader.setSource(srcVert, srcFrag); + +render.onTriggered = doRender; + +doRender(); + + +}; + +Ops.Gl.Shader.VertexNumberMaterial.prototype = new CABLES.Op(); +CABLES.OPS["732ff263-1aeb-4f39-969f-c781d7776cc8"]={f:Ops.Gl.Shader.VertexNumberMaterial,objName:"Ops.Gl.Shader.VertexNumberMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.WireframeMaterial +// +// ************************************************************** + +Ops.Gl.Shader.WireframeMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"wireframe_frag":"{{MODULES_HEAD}}\n\nIN vec3 barycentric;\nUNI float width;\nUNI float opacity;\nUNI float r,g,b;\nUNI float fr,fg,fb;\nIN vec3 norm;\n\nfloat edgeFactor()\n{\n vec3 d = fwidth(barycentric);\n vec3 a3 = smoothstep(vec3(0.0), d*width*4.0, barycentric);\n return min(min(a3.x, a3.y), a3.z);\n}\n\nvoid main()\n{\n {{MODULE_BEGIN_FRAG}}\n\n vec4 col;\n\n #ifdef WIREFRAME_FILL\n float v=opacity*(1.0-edgeFactor())*0.95;\n vec3 wire = vec3(fr, fg, fb);\n col.rgb = vec3(r, g, b);\n col.rgb = mix(wire,col.rgb,v);\n col.a = opacity;\n #endif\n\n #ifndef WIREFRAME_FILL\n col = vec4(r,g,b, opacity*(1.0-edgeFactor())*0.95);\n #endif\n\n {{MODULE_COLOR}}\n\n// col=vec4(1.0);\n outColor=col;\n}","wireframe_vert":"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\nOUT vec3 barycentric;\nIN vec2 attrTexCoord;\nOUT vec2 texCoord;\n\nIN vec3 attrBarycentric;\nIN vec3 attrVertNormal;\nOUT vec3 norm;\n\nvoid main()\n{\n norm=attrVertNormal;\n texCoord=attrTexCoord;\n barycentric=attrBarycentric;\n mat4 mMatrix=modelMatrix;\n vec4 pos=vec4(vPosition, 1.0);\n\n {{MODULE_VERTEX_POSITION}}\n\n gl_Position = projMatrix * viewMatrix * mMatrix * pos;\n}\n",}; +let cgl = op.patch.cgl; + +let render = op.inTrigger("render"); +let trigger = op.outTrigger("trigger"); + +let enableDepth = op.inValueBool("enable depth testing", true); + +let fill = op.inValueBool("fill", true); + +function setDefines() +{ + if (shader) + if (fill.get()) shader.define("WIREFRAME_FILL"); + else shader.removeDefine("WIREFRAME_FILL"); +} + +fill.onChange = function () +{ + setDefines(); +}; + +let w = op.inValueSlider("width", 0.25); +w.onChange = function () { uniformWidth.setValue(w.get()); }; + +let opacity = op.inValueSlider("opacity", 1); +opacity.onChange = function () { uniformOpacity.setValue(opacity.get()); }; + +if (cgl.glVersion == 1 && !cgl.gl.getExtension("OES_standard_derivatives")) +{ + op.uiAttr({ "error": "no oes standart derivatives!" }); +} +else +{ + op.uiAttr({ "error": null }); +} + +let doRender = function () +{ + // if(true!==enableDepth.get()) cgl.gl.disable(cgl.gl.DEPTH_TEST); + // else cgl.gl.enable(cgl.gl.DEPTH_TEST); + cgl.pushDepthTest(enableDepth.get()); + + cgl.pushShader(shader); + trigger.trigger(); + cgl.popShader(); + + // if(true!==enableDepth.get()) cgl.gl.enable(cgl.gl.DEPTH_TEST); + cgl.popDepthTest(); +}; + +const shader = new CGL.Shader(cgl, "Wireframe Material"); + +if (cgl.glVersion > 1)shader.glslVersion = 300; +const uniformWidth = new CGL.Uniform(shader, "f", "width", w.get()); +const uniformOpacity = new CGL.Uniform(shader, "f", "opacity", opacity.get()); + +if (cgl.glVersion == 1)shader.enableExtension("OES_standard_derivatives"); + +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +shader.setSource(attachments.wireframe_vert || "", attachments.wireframe_frag || ""); +shader.wireframe = true; +setDefines(); + +// diffuse color +let r = op.inValueSlider("diffuse r", Math.random()); +let g = op.inValueSlider("diffuse g", Math.random()); +let b = op.inValueSlider("diffuse b", Math.random()); + +r.setUiAttribs({ "colorPick": true }); + +g.uniform = new CGL.Uniform(shader, "f", "g", g); +r.uniform = new CGL.Uniform(shader, "f", "r", r); +b.uniform = new CGL.Uniform(shader, "f", "b", b); + +{ + // diffuse color + + let fr = op.inValueSlider("Fill R", Math.random()); + fr.setUiAttribs({ "colorPick": true }); + fr.uniform = new CGL.Uniform(shader, "f", "fr", fr); + + let fg = op.inValueSlider("Fill G", Math.random()); + fg.uniform = new CGL.Uniform(shader, "f", "fg", fg); + + let fb = op.inValueSlider("Fill B", Math.random()); + fb.uniform = new CGL.Uniform(shader, "f", "fb", fb); +} + +render.onTriggered = doRender; + +doRender(); + + +}; + +Ops.Gl.Shader.WireframeMaterial.prototype = new CABLES.Op(); +CABLES.OPS["e5d64a7d-b161-4f85-907e-e10a34691733"]={f:Ops.Gl.Shader.WireframeMaterial,objName:"Ops.Gl.Shader.WireframeMaterial"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.AreaDiscardPixel_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.AreaDiscardPixel_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"areadiscard_frag":"\n\nfloat MOD_de=0.0;\n\n#ifdef MOD_AREA_SPHERE\n MOD_de=distance(vec3(MOD_x,MOD_y,MOD_z)/MOD_sizeAxis,MOD_areaPos.xyz/MOD_sizeAxis);\n#endif\n\n#ifdef MOD_AREA_BOX\n if( abs(MOD_y/MOD_sizeAxis.y-MOD_areaPos.y/(MOD_sizeAxis.y*MOD_size))>0.5 ||\n abs(MOD_x/MOD_sizeAxis.x-MOD_areaPos.x/(MOD_sizeAxis.x*MOD_size))>0.5 ||\n abs(MOD_z/MOD_sizeAxis.z-MOD_areaPos.z/(MOD_sizeAxis.z*MOD_size))>0.5 ) MOD_de=1.0;\n#endif\n\n#ifdef MOD_AREA_AXIS_X\n MOD_de=abs(MOD_x-MOD_areaPos.x);\n#endif\n#ifdef MOD_AREA_AXIS_XY\n MOD_de=abs(MOD_x-MOD_areaPos.x+MOD_areaPos.y);\n#endif\n#ifdef MOD_AREA_AXIS_XZ\n MOD_de=abs(MOD_x-MOD_areaPos.x+MOD_areaPos.z);\n#endif\n#ifdef MOD_AREA_AXIS_YZ\n MOD_de=abs(MOD_y-MOD_areaPos.y+MOD_areaPos.z);\n#endif\n#ifdef MOD_AREA_AXIS_Y\n MOD_de=abs(MOD_y-MOD_areaPos.y);\n#endif\n#ifdef MOD_AREA_AXIS_Z\n MOD_de=abs(MOD_z-MOD_areaPos.z);\n#endif\n\n#ifdef MOD_AREA_AXIS_X_INFINITE\n MOD_de=MOD_x-MOD_areaPos.x;\n#endif\n#ifdef MOD_AREA_AXIS_Y_INFINITE\n MOD_de=MOD_y-MOD_areaPos.y;\n#endif\n#ifdef MOD_AREA_AXIS_Z_INFINITE\n MOD_de=MOD_z-MOD_areaPos.z;\n#endif\n\n#ifdef MOD_AREA_REPEAT\n MOD_de=mod(MOD_de,MOD_size+MOD_repeat);\n#endif\n\nMOD_de=MOD_de/MOD_size;\n\n#ifdef MOD_AREA_INVERT\n MOD_de=1.0-MOD_de;\n#endif\n\n\nif(MOD_de<=0.5) discard;\n\n","areadiscard_head_frag":"IN vec4 MOD_areaPos;\n",}; +const + render = op.inTrigger("render"), + inInvert = op.inValueBool("Invert"), + inArea = op.inValueSelect("Area", ["Sphere", "Box", "Axis X", "Axis Y", "Axis Z", "Axis XY", "Axis XZ", "Axis YZ", "Axis X Infinite", "Axis Y Infinite", "Axis Z Infinite"], "Sphere"), + inSize = op.inValue("Size", 1), + inSizeX = op.inValueFloat("Size X", 1), + inSizeY = op.inValueFloat("Size Y", 1), + inSizeZ = op.inValueFloat("Size Z", 1), + inRepeat = op.inValueBool("Repeat"), + inRepeatDist = op.inValueFloat("Repeat Distance", 0.0), + x = op.inValue("x"), + y = op.inValue("y"), + z = op.inValue("z"), + inWorldSpace = op.inValueBool("WorldSpace", true), + next = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; + +op.setPortGroup("Size", [inSize, inSizeY, inSizeX, inSizeZ]); +op.setPortGroup("Position", [x, y, z, inWorldSpace]); + + +const srcHeadVert = "" + .endl() + "OUT vec4 MOD_areaPos;" + .endl(); + +const srcBodyVert = "" + .endl() + "#ifndef MOD_WORLDSPACE" + .endl() + " MOD_areaPos=pos;" + .endl() + "#endif" + .endl() + "#ifdef MOD_WORLDSPACE" + .endl() + " MOD_areaPos=mMatrix*pos;" + .endl() + "#endif" + .endl(); + +inWorldSpace.onChange = +inInvert.onChange = +inRepeat.onChange = +inArea.onChange = updateDefines; + +const mod = new CGL.ShaderModifier(cgl, op.name); + +mod.addModule({ + "priority": 2, + "title": op.objName, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert +}); +mod.addModule({ + "title": op.objName, + "name": "MODULE_COLOR", + "srcHeadFrag": attachments.areadiscard_head_frag || "", + "srcBodyFrag": attachments.areadiscard_frag || "" +}); + +mod.addUniformFrag("f", "MOD_size", inSize); +mod.addUniformFrag("f", "MOD_x", x); +mod.addUniformFrag("f", "MOD_y", y); +mod.addUniformFrag("f", "MOD_z", z); +mod.addUniformFrag("3f", "MOD_sizeAxis", inSizeX, inSizeY, inSizeZ); +mod.addUniformFrag("f", "MOD_repeat", inRepeatDist); + +updateDefines(); + +function updateDefines() +{ + mod.toggleDefine("MOD_WORLDSPACE", inWorldSpace.get()); + mod.toggleDefine("MOD_AREA_INVERT", inInvert.get()); + mod.toggleDefine("MOD_AREA_REPEAT", inRepeat.get()); + mod.toggleDefine("MOD_AREA_BOX", inArea.get() == "Box"); + mod.toggleDefine("MOD_AREA_SPHERE", inArea.get() == "Sphere"); + mod.toggleDefine("MOD_AREA_AXIS_X", inArea.get() == "Axis X"); + mod.toggleDefine("MOD_AREA_AXIS_Y", inArea.get() == "Axis Y"); + mod.toggleDefine("MOD_AREA_AXIS_Z", inArea.get() == "Axis Z"); + mod.toggleDefine("MOD_AREA_AXIS_XY", inArea.get() == "Axis XY"); + mod.toggleDefine("MOD_AREA_AXIS_XZ", inArea.get() == "Axis XZ"); + mod.toggleDefine("MOD_AREA_AXIS_YZ", inArea.get() == "Axis YZ"); + mod.toggleDefine("MOD_AREA_AXIS_X_INFINITE", inArea.get() == "Axis X Infinite"); + mod.toggleDefine("MOD_AREA_AXIS_Y_INFINITE", inArea.get() == "Axis Y Infinite"); + mod.toggleDefine("MOD_AREA_AXIS_Z_INFINITE", inArea.get() == "Axis Z Infinite"); +} + +render.onTriggered = function () +{ + if (op.isCurrentUiOp()) + gui.setTransformGizmo( + { + "posX": x, + "posY": y, + "posZ": z + }); + + if (cgl.shouldDrawHelpers(op)) CABLES.GL_MARKER.drawSphere(op, inSize.get()); + + mod.bind(); + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.AreaDiscardPixel_v2.prototype = new CABLES.Op(); +CABLES.OPS["0d39986b-c594-44d5-815d-c46a280fe16c"]={f:Ops.Gl.ShaderEffects.AreaDiscardPixel_v2,objName:"Ops.Gl.ShaderEffects.AreaDiscardPixel_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.AreaRotate_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.AreaRotate_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"area_rotate_vert":"\n\nvec4 MOD_scaler(vec4 pos,mat4 modelMatrix)\n{\n vec3 forcePos=vec3(MOD_x,MOD_y,MOD_z);\n vec3 vecToOrigin=(modelMatrix*pos).xyz-forcePos;\n float dist=abs(length(vecToOrigin));\n float distAlpha = (MOD_size - dist) ;\n\n if(MOD_smooth) distAlpha=smoothstep(0.0,MOD_size,distAlpha);\n\n // pos.xyz*=(1.0+(distAlpha*MOD_strength));\n\n mat3 rotation = mat3(\n vec3( cos(MOD_strength*distAlpha), sin(MOD_strength*distAlpha), 0.0),\n vec3(-sin(MOD_strength*distAlpha), cos(MOD_strength*distAlpha), 0.0),\n vec3( 0.0, 0.0, 1.0)\n );\n pos =vec4(rotation * pos.xyz, 1.0);\n\n\n return pos;\n}\n",}; +const + render=op.inTrigger("render"), + inSize=op.inValue("Size",1), + inStrength=op.inValue("Strength",1), + inSmooth=op.inValueBool("Smooth",true), + x=op.inValue("x"), + y=op.inValue("y"), + z=op.inValue("z"), + next=op.outTrigger("trigger"); + +const cgl=op.patch.cgl; +const srcBodyVert='' + .endl()+'pos=MOD_scaler(pos,mMatrix);' + .endl(); + +const mod = new CGL.ShaderModifier(cgl, op.name); + +mod.addModule({ + "priority": 2, + "title": "vert"+op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.area_rotate_vert, + "srcBodyVert": srcBodyVert +}); + +mod.addUniform("f", "MOD_x", x); +mod.addUniform("f", "MOD_y", y); +mod.addUniform("f", "MOD_z", z); + +mod.addUniform("f", "MOD_size", inSize); +mod.addUniform("f", "MOD_strength", inStrength); +mod.addUniform("b", "MOD_smooth", inSmooth); + + +render.onTriggered=function() +{ + if(!cgl.getShader()) + { + next.trigger(); + return; + } + + if(op.isCurrentUiOp()) gui.setTransformGizmo( {posX:x,posY:y,posZ:z }); + + mod.bind(); + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.AreaRotate_v2.prototype = new CABLES.Op(); +CABLES.OPS["ebfd3f0c-7a58-44d3-aba0-72d91fafd7ae"]={f:Ops.Gl.ShaderEffects.AreaRotate_v2,objName:"Ops.Gl.ShaderEffects.AreaRotate_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.AreaScaler_v3 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.AreaScaler_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"areascale_vert":"\nvec4 MOD_scaler(vec4 pos,vec4 worldPos,vec3 normal,mat4 mMatrix)\n{\n vec3 forcePos = vec3(MOD_x,MOD_y,MOD_z);\n\n #ifdef MOD_OBJECT_POS\n worldPos=mMatrix*vec4(0.0,0.0,0.0,1.0);\n #endif\n\n vec3 vecToOrigin = worldPos.xyz-forcePos;\n\n float dist = abs(length(vecToOrigin)) ;\n\n\n float distAlpha = (MOD_size - dist) ;\n\n if(MOD_smooth) distAlpha = smoothstep(0.0,MOD_size,distAlpha);\n\n float m = distAlpha * MOD_strength;\n\n #ifdef MOD_TO_ZERO\n m+=1.0;\n #endif\n\n if(m<0.0)m=0.0;\n\n #ifdef MOD_CLAMP_SIZE\n pos.xyz*=clamp(m,MOD_clampMin,MOD_clampMax);\n #else\n pos.xyz*=m ;\n #endif\n\n return pos;\n}\n",}; +const + exec = op.inTrigger("render"), + inSize = op.inValue("Area size", 1), + inSrc = op.inSwitch("Source", ["Vertex position", "Object position"], "Vertex position"), + inStrength = op.inValue("Strength", 1), + inSmooth = op.inValueBool("Smoothstep", false), + inToZero = op.inValueBool("Min Size Original", false), + inClampBool = op.inBool("Clamp size", false), + inClampMin = op.inFloat("Clamp min", 0), + inClampMax = op.inFloat("Clamp max", 1.0), + x = op.inValue("Pos X"), + y = op.inValue("Pos Y"), + z = op.inValue("Pos Z"), + next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; +let needsUpdateToZero = true; +const mscaleUni = null; +let shader = null; + +op.setPortGroup("Position", [x, y, z]); +op.setPortGroup("Influence", [inSrc, inStrength, inSmooth, inToZero]); + +const srcBodyVert = "" + .endl() + "pos=MOD_scaler(pos,mMatrix*pos,attrVertNormal,mMatrix);" // modelMatrix* + .endl(); + +let moduleVert = null; + +// op.onDelete = exec.onLinkChanged = removeModule; +inToZero.onChange = inSrc.onChange = inClampBool.onChange = updateToZero; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "priority": 2, + "title": "vert_" + op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.areascale_vert, + "srcBodyVert": srcBodyVert +}); + + +mod.addUniform("f", "MOD_size", inSize); +mod.addUniform("f", "MOD_strength", inStrength); +mod.addUniform("b", "MOD_smooth", inSmooth); + +mod.addUniform("f", "MOD_clampMin", inClampMin); +mod.addUniform("f", "MOD_clampMax", inClampMax); + +mod.addUniform("f", "MOD_x", x); +mod.addUniform("f", "MOD_y", y); +mod.addUniform("f", "MOD_z", z); + + +// function removeModule() +// { +// if (shader && moduleVert) shader.removeModule(moduleVert); +// shader = null; +// } + +function updateToZero() +{ + // if (!shader) + // { + // needsUpdateToZero = true; + // return; + // } + + mod.toggleDefine("MOD_TO_ZERO", inToZero.get()); + mod.toggleDefine("MOD_OBJECT_POS", inSrc.get() == "Object position"); + mod.toggleDefine("MOD_CLAMP_SIZE", inClampBool.get()); + + needsUpdateToZero = false; + + inClampMin.setUiAttribs({ "greyout": !inClampBool.get() }); + inClampMax.setUiAttribs({ "greyout": !inClampBool.get() }); +} + + +exec.onTriggered = function () +{ + if (!cgl.getShader()) + { + next.trigger(); + return; + } + + if (CABLES.UI) + { + if (op.isCurrentUiOp()) gui.setTransformGizmo({ "posX": x, "posY": y, "posZ": z }); + + if (cgl.shouldDrawHelpers(op)) + { + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, [x.get(), y.get(), z.get()]); + CABLES.GL_MARKER.drawSphere(op, inSize.get()); + cgl.popModelMatrix(); + } + } + + // if (cgl.getShader() != shader) + // { + // if (shader) removeModule(); + // shader = cgl.getShader(); + + // moduleVert = shader.addModule( + // { + // "title": op.objName, + // "name": "MODULE_VERTEX_POSITION", + // "srcHeadVert": attachments.areascale_vert, + // "srcBodyVert": srcBodyVert + // }); + + // inSize.uniform = new CGL.Uniform(shader, "f", "MOD_size", inSize); + // inStrength.uniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "strength", inStrength); + // inSmooth.uniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "smooth", inSmooth); + + // x.uniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "x", x); + // y.uniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "y", y); + // z.uniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "z", z); + + // inClampMin.uniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "clampMin", inClampMin); + // inClampMax.uniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "clampMax", inClampMax); + // } + + if (needsUpdateToZero)updateToZero(); + // if (!shader) return; + + + mod.bind(); + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.AreaScaler_v3.prototype = new CABLES.Op(); +CABLES.OPS["3b7c5601-881a-4748-91d1-aea4c7c1bf67"]={f:Ops.Gl.ShaderEffects.AreaScaler_v3,objName:"Ops.Gl.ShaderEffects.AreaScaler_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.AreaTranslateFBMNoise +// +// ************************************************************** + +Ops.Gl.ShaderEffects.AreaTranslateFBMNoise = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"areascale_vert":"UNI bool MOD_smooth;\nUNI float MOD_x,MOD_y,MOD_z;\nUNI float MOD_tx,MOD_ty,MOD_tz;\nUNI float MOD_strength;\nUNI float MOD_size;\nUNI float MOD_noiseScale;\n\n\n\n\nfloat MOD_mod289(float x){return x - floor(x * (1.0 / 289.0)) * 289.0;}\nvec4 MOD_mod289(vec4 x){return x - floor(x * (1.0 / 289.0)) * 289.0;}\nvec4 MOD_perm(vec4 x){return MOD_mod289(((x * 34.0) + 1.0) * x);}\n\nfloat MOD_noise(vec3 p){\n vec3 a = floor(p);\n vec3 d = p - a;\n d = d * d * (3.0 - 2.0 * d);\n\n vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0);\n vec4 k1 = MOD_perm(b.xyxy);\n vec4 k2 = MOD_perm(k1.xyxy + b.zzww);\n\n vec4 c = k2 + a.zzzz;\n vec4 k3 = MOD_perm(c);\n vec4 k4 = MOD_perm(c + 1.0);\n\n vec4 o1 = fract(k3 * (1.0 / 41.0));\n vec4 o2 = fract(k4 * (1.0 / 41.0));\n\n vec4 o3 = o2 * d.z + o1 * (1.0 - d.z);\n vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x);\n\n return o4.y * d.y + o4.x * (1.0 - d.y);\n}\n\n\n\nfloat MOD_fbm(vec3 x)\n{\n const int NUM_OCTAVES=8;\n float time=0.0;\n\n\tfloat v = 0.0;\n\tfloat a = 0.5;\n\tvec3 shift = vec3(100.0+time);\n\tfor (int i = 0; i < NUM_OCTAVES; ++i) {\n\t\tv += a * MOD_noise(x);\n\t\tx = x * 2.0 + shift;\n\t\ta *= 0.5;\n\t}\n\treturn v;\n}\n\n\n\nmat4 MOD_rotation3d(vec3 axis, float angle) {\n axis = normalize(axis);\n float s = sin(angle);\n float c = cos(angle);\n float oc = 1.0 - c;\n\n return mat4(\n\t\toc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,\n oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,\n oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,\n\t\t0.0, 0.0, 0.0, 1.0\n\t);\n}\n\n\nmat4 MOD_translate(mat4 mat)\n{\n vec3 forcePos=vec3(MOD_x,MOD_y,MOD_z);\n vec3 noisePos=vec3(MOD_tx,MOD_ty,MOD_tz);\n vec3 worldPos=vec3(mat[3][0],mat[3][1],mat[3][2]);\n // vec3 worldPosInst=vec3(instMat[3][0],instMat[3][1],instMat[3][2]);\n\n vec3 vecToOrigin=worldPos-forcePos;\n float dist=abs(length(vecToOrigin));\n float distAlpha = (MOD_size - dist) ;\n\n if(MOD_smooth) distAlpha=smoothstep(0.0,MOD_size,distAlpha);\n else\n {\n if(distAlpha>0.0) distAlpha=1.0;\n else distAlpha=0.0;\n }\n\n distAlpha*=MOD_strength;\n\n vec3 tr=vec3(distAlpha);\n\n float nois=(MOD_fbm(MOD_noiseScale*(worldPos+noisePos))-0.5);\n float nois2=(MOD_fbm(MOD_noiseScale*(worldPos+noisePos*5.711))-0.5);\n float nois3=(MOD_fbm(MOD_noiseScale*(worldPos+noisePos*2.0))-0.5);\n // tr=nois*tr;\n\n\n #ifdef MOD_DO_ROTATE\n if(distAlpha>0.0) mat*=MOD_rotation3d( vec3(distAlpha)*vec3(nois,nois2,nois3), MOD_strength/57.297);\n #endif\n\n #ifdef MOD_DO_TRANSLATE\n mat[3][0] += nois*distAlpha;\n mat[3][1] += nois2*distAlpha;\n mat[3][2] += nois3*distAlpha;\n #endif\n\n return mat;\n}\n",}; +const + render = op.inTrigger("render"), + next = op.outTrigger("trigger"), + inMode = op.inSwitch("Mode", ["Rotate", "Translate"], "Translate"), + inSize = op.inValue("Size", 1), + inStrength = op.inValue("Strength", 1), + inSmooth = op.inValueBool("Smooth", true), + x = op.inValue("x"), + y = op.inValue("y"), + z = op.inValue("z"), + inNoiseScale = op.inValue("Noise Scale", 0.5), + tx = op.inValue("Noise X"), + ty = op.inValue("Noise Y"), + tz = op.inValue("Noise Z"); + +const cgl = op.patch.cgl; +op.setPortGroup("Noise", [inNoiseScale, tx, ty, tz]); +op.setPortGroup("Area Position", [x, y, z]); + +let needsUpdateToZero = true; +let mscaleUni = null; +let shader = null; +let uniforms = {}; +let srcHeadVert = attachments.areascale_vert; +let moduleVert = null; +let srcBodyVert = "" + .endl() + "mMatrix=MOD_translate(mMatrix);" // modelMatrix* + .endl(); + +render.onLinkChanged = removeModule; +inMode.onChange = updateMode; + +function removeModule() +{ + if (shader && moduleVert) shader.removeModule(moduleVert); + shader = null; +} + +function updateMode() +{ + if (!shader) return; + shader.toggleDefine(moduleVert.prefix + "DO_ROTATE", inMode.get() == "Rotate"); + shader.toggleDefine(moduleVert.prefix + "DO_TRANSLATE", inMode.get() == "Translate"); +} + +render.onTriggered = function () +{ + if (!cgl.getShader()) + { + next.trigger(); + return; + } + + if (CABLES.UI) + { + if (op.isCurrentUiOp()) gui.setTransformGizmo({ "posX": x, "posY": y, "posZ": z }); + if (cgl.shouldDrawHelpers(op)) + { + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, [x.get(), y.get(), z.get()]); + CABLES.GL_MARKER.drawSphere(op, inSize.get()); + cgl.popModelMatrix(); + } + } + + if (cgl.getShader() != shader) + { + if (shader) removeModule(); + shader = cgl.getShader(); + + moduleVert = shader.addModule( + { + "title": op.objName, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert + }); + + updateMode(); + + uniforms.inSizeUniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "size", inSize); + uniforms.inStrengthUniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "strength", inStrength); + uniforms.inSmoothUniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "smooth", inSmooth); + uniforms.inNoiseScaleUniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "noiseScale", inNoiseScale); + + uniforms.xUniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "x", x); + uniforms.yUniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "y", y); + uniforms.zUniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "z", z); + + uniforms.txUniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "tx", tx); + uniforms.tyUniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "ty", ty); + uniforms.tzUniform = new CGL.Uniform(shader, "f", moduleVert.prefix + "tz", tz); + } + + + if (!shader) return; + + next.trigger(); +}; + + +}; + +Ops.Gl.ShaderEffects.AreaTranslateFBMNoise.prototype = new CABLES.Op(); +CABLES.OPS["e755e88e-a2a7-4db2-84b3-0e76c423bdb6"]={f:Ops.Gl.ShaderEffects.AreaTranslateFBMNoise,objName:"Ops.Gl.ShaderEffects.AreaTranslateFBMNoise"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.AreaTranslateMeshes_v3 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.AreaTranslateMeshes_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"areascale_vert":"\nmat4 MOD_translate(mat4 mat)\n{\n // vec3 forcePos=vec3(MOD_posx,MOD_posy,MOD_posz);\n vec3 worldPos=vec3(mat[3][0],mat[3][1],mat[3][2]);\n vec3 vecToOrigin=worldPos-MOD_pos;\n float dist=abs(length(vecToOrigin));\n float distAlpha = (MOD_size - dist) ;\n\n distAlpha=clamp(distAlpha,0.0,1.0);\n\n distAlpha*=MOD_strength;\n\n vec3 tr=normalize(vecToOrigin)*distAlpha;\n\ntr*=MOD_mul;\n\n mat[3][0] += tr.x;\n mat[3][1] += tr.y;\n mat[3][2] += tr.z;\n\n return mat;\n}\n\n",}; +const + render = op.inTrigger("render"), + next = op.outTrigger("trigger"), + inSize = op.inValue("Size", 1), + inStrength = op.inValue("Strength", 0.5), + x = op.inValue("x"), + y = op.inValue("y"), + z = op.inValue("z"), + mulx = op.inValue("Multiply x", 1), + muly = op.inValue("Multiply y", 1), + mulz = op.inValue("Multiply z", 1); + +const cgl = op.patch.cgl; + +op.setPortGroup("Area Position", [x, y, z]); +op.setPortGroup("Axis Multiply", [mulx, muly, mulz]); + +const needsUpdateToZero = true; +const mscaleUni = null; +let shader = null; +const srcHeadVert = attachments.areascale_vert; +const uniforms = {}; +const srcBodyVert = "" + .endl() + "mMatrix=MOD_translate(mMatrix);" // modelMatrix* + .endl(); + +let moduleVert = null; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert +}); + +mod.addUniformVert("f", "MOD_size", inSize); +mod.addUniformVert("f", "MOD_strength", inStrength); + +mod.addUniformVert("3f", "MOD_pos", x, y, z); +mod.addUniformVert("3f", "MOD_mul", mulx, muly, mulz); + +render.onTriggered = function () +{ + if (!cgl.getShader()) + { + next.trigger(); + return; + } + + if (CABLES.UI) + { + if (op.isCurrentUiOp()) + gui.setTransformGizmo( + { + "posX": x, + "posY": y, + "posZ": z + }); + + if (cgl.shouldDrawHelpers(op)) + { + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, [x.get(), y.get(), z.get()]); + CABLES.GL_MARKER.drawSphere(op, inSize.get()); + cgl.popModelMatrix(); + } + } + + mod.bind(); + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.AreaTranslateMeshes_v3.prototype = new CABLES.Op(); +CABLES.OPS["69f7a5b5-4824-441a-960c-e7b13b17790d"]={f:Ops.Gl.ShaderEffects.AreaTranslateMeshes_v3,objName:"Ops.Gl.ShaderEffects.AreaTranslateMeshes_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.Bend_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.Bend_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"bend_vert":"\n\nvoid MOD_bendDistort(inout vec3 pos, inout vec3 norm)\n{\n pos = (MOD_transMatrix * vec4(pos, 1.0)).xyz;\n norm = (vec4(norm, 0.0) * MOD_invTransMatrix).xyz;\n\n float PI = 3.14159265;\n if (abs(MOD_amount) > 1e-5) {\n float bendAngle = MOD_amount;\n float radius = 1.0 / bendAngle;\n\n // float d = clamp(pos.x, MOD_range.x, MOD_range.y);\n float d = clamp(pos.x, MOD_range.x, MOD_range.y);\n float th = PI * 0.5 + (bendAngle * d);\n float s = sin(th);\n float c = cos(th);\n pos.xy = vec2(s * (pos.x - d) - c * (radius + pos.y),\n c * (pos.x - d) + s * (radius + pos.y) - radius);\n norm.xy = vec2(s * norm.x - c * norm.y,\n c * norm.x + s * norm.y);\n }\n\n pos = (MOD_invTransMatrix * vec4(pos, 1.0)).xyz;\n norm = (vec4(norm, 0.0) * MOD_transMatrix).xyz;\n}\n",}; +const render = op.inTrigger("render"); +const amount = op.inValue("Amount"); +const rotX = op.inValue("RotX"); +const rotY = op.inValue("RotY"); +const rotZ = op.inValue("RotZ"); +const scale = op.inValue("Scale", 2); +const offset = op.inValue("Offset", 0.25); +const limited = op.inValueBool("Limited", true); + +const next = op.outTrigger("trigger"); + +const srcHeadVert = attachments.bend_vert; +const srcBodyVert = "" + .endl() + " MOD_bendDistort(pos.xyz, norm);" + .endl(); + +const uniAmount = null; +const uniRange = null; +const uniTransMatrix = null; +const uniInvTransMatrix = null; +const cgl = op.patch.cgl; + +let matricesValid = false; +const transMatrix = mat4.create(); +const invTransMatrix = mat4.create(); +let amountRadians = 0; + +function invalidateMatrices() +{ + matricesValid = false; +} + +rotX.onChange = invalidateMatrices; +rotY.onChange = invalidateMatrices; +rotZ.onChange = invalidateMatrices; +scale.onChange = invalidateMatrices; +offset.onChange = invalidateMatrices; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.bend_vert, + "srcBodyVert": srcBodyVert +}); + +mod.addUniformVert("f", "MOD_amount", 0); +mod.addUniformVert("2f", "MOD_range", [0, 1]); +mod.addUniformVert("m4", "MOD_transMatrix", transMatrix); +mod.addUniformVert("m4", "MOD_invTransMatrix", invTransMatrix); + +amount.onChange = function () { amountRadians = amount.get() * CGL.DEG2RAD; }; + +mat4.identity(transMatrix); +mat4.identity(invTransMatrix); + +const tvec = vec3.create(); +const svec = vec4.create(); +function updateMatrices() +{ + if (matricesValid) return; + + vec3.set(tvec, offset.get(), 0, 0); + + const s = 1 / scale.get(); + vec3.set(svec, s, s, s); + + mat4.identity(transMatrix); + mat4.translate(transMatrix, transMatrix, tvec); + + mat4.rotateX(transMatrix, transMatrix, rotX.get() * CGL.DEG2RAD); + mat4.rotateY(transMatrix, transMatrix, rotY.get() * CGL.DEG2RAD); + mat4.rotateZ(transMatrix, transMatrix, rotZ.get() * CGL.DEG2RAD); + + mat4.scale(transMatrix, transMatrix, svec); + + mat4.invert(invTransMatrix, transMatrix); + matricesValid = true; +} + +render.onTriggered = function () +{ + mod.setUniformValue("MOD_range", limited.get() ? [0, 1] : [-99999, 99999]); + + updateMatrices(); + + mod.bind(); + mod.setUniformValue("MOD_amount", amountRadians); + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.Bend_v2.prototype = new CABLES.Op(); +CABLES.OPS["7476156e-3ca3-445a-bd30-1b9373a6af4c"]={f:Ops.Gl.ShaderEffects.Bend_v2,objName:"Ops.Gl.ShaderEffects.Bend_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.ClampVertexPosition_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.ClampVertexPosition_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"restrictVertex_vert":"\n\n#ifndef RESTRICT_UPDATENORMALS\n\n #ifdef MOD_RESTRICT_AXIS_X\n pos.x=min(MOD_max,pos.x);\n pos.x=max(MOD_min,pos.x);\n #endif\n #ifdef MOD_RESTRICT_AXIS_Y\n pos.y=min(MOD_max,pos.y);\n pos.y=max(MOD_min,pos.y);\n #endif\n #ifdef MOD_RESTRICT_AXIS_Z\n pos.z=min(MOD_max,pos.z);\n pos.z=max(MOD_min,pos.z);\n #endif\n\n\n#endif\n\n#ifdef RESTRICT_UPDATENORMALS\n #ifdef MOD_RESTRICT_AXIS_X\n if(pos.x>MOD_max)\n {\n pos.x=MOD_max;\n pos.y=0.0;\n pos.z=0.0;\n norm=vec3(0.0,1.0,0.0);\n }\n if(pos.xMOD_max)\n {\n pos.y=MOD_max;\n pos.x=0.0;\n pos.z=0.0;\n norm=vec3(0.0,0.0,1.0);\n }\n if(pos.yMOD_max)\n {\n pos.z=MOD_max;\n pos.y=0.0;\n pos.x=0.0;\n norm=vec3(1.0,0.0,0.0);\n }\n if(pos.z MOD_inSizeAmountFalloffSizeX.x ||\n abs((MOD_vertPos.xyz*MOD_size).y-(MOD_pos.xyz*MOD_size).y) > MOD_inSizeAmountFalloffSizeX.x ||\n abs((MOD_vertPos.xyz*MOD_size).z-(MOD_pos.xyz*MOD_size).z) > MOD_inSizeAmountFalloffSizeX.x ) MOD_de=0.0;\n#endif\n\n#ifdef MOD_AREA_AXIS_X\n float MOD_de=abs(MOD_pos.x-MOD_vertPos.x);\n#endif\n#ifdef MOD_AREA_AXIS_Y\n float MOD_de=abs(MOD_pos.y-MOD_vertPos.y);\n#endif\n#ifdef MOD_AREA_AXIS_Z\n float MOD_de=abs(MOD_pos.z-MOD_vertPos.z);\n#endif\n\n#ifdef MOD_AREA_AXIS_X_INFINITE\n float MOD_de=MOD_pos.x-MOD_vertPos.x;\n#endif\n#ifdef MOD_AREA_AXIS_Y_INFINITE\n float MOD_de=MOD_pos.y-MOD_vertPos.y;\n#endif\n#ifdef MOD_AREA_AXIS_Z_INFINITE\n float MOD_de=MOD_pos.z-MOD_vertPos.z;\n#endif\n\n#ifndef MOD_AREA_BOX\n MOD_de=1.0-smoothstep(MOD_inSizeAmountFalloffSizeX.z*MOD_inSizeAmountFalloffSizeX.x,MOD_inSizeAmountFalloffSizeX.x,MOD_de);\n#endif\n\n#ifdef MOD_AREA_INVERT\n MOD_de=1.0-MOD_de;\n#endif\n\n#ifdef MOD_BLEND_NORMAL\n col.rgb=mix(col.rgb,MOD_col, MOD_de*MOD_inSizeAmountFalloffSizeX.y);\n#endif\n\n\n#ifdef MOD_BLEND_MULTIPLY\n col.rgb=mix(col.rgb,col.rgb*MOD_col,MOD_de*MOD_inSizeAmountFalloffSizeX.y);\n#endif\n\n#ifdef MOD_BLEND_ADD\n col.rgb+=MOD_de*MOD_inSizeAmountFalloffSizeX.y*MOD_col;\n#endif\n\n\n#ifdef MOD_BLEND_OPACITY\n col.a*=(1.0-MOD_de*MOD_inSizeAmountFalloffSizeX.y);\n#endif\n\n#ifdef MOD_BLEND_DISCARD\n if(MOD_de*MOD_inSizeAmountFalloffSizeX.y>=0.999)discard;\n#endif\n\n// col.rgb=vec3(distance(MOD_vertPos.xyz,MOD_pos.xyz))*0.1\n// col.rgb=MOD_pos.xyz;","colorarea_head_frag":"IN vec4 MOD_vertPos;\n",}; +const + render = op.inTrigger("Render"), + inArea = op.inValueSelect("Area", ["Sphere", "Box", "Axis X", "Axis Y", "Axis Z", "Axis X Infinite", "Axis Y Infinite", "Axis Z Infinite"], "Sphere"), + inSize = op.inValue("Size", 1), + inAmount = op.inValueSlider("Amount", 0.5), + inFalloff = op.inValueSlider("Falloff", 0), + inInvert = op.inValueBool("Invert"), + inBlend = op.inSwitch("Blend ", ["Normal", "Multiply", "Opacity", "Add", "Discard"], "Normal"), + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + x = op.inValue("x"), + y = op.inValue("y"), + z = op.inValue("z"), + doScale = op.inBool("Change Size", false), + sizeX = op.inFloat("Size X", 1), + sizeY = op.inFloat("Size Y", 1), + sizeZ = op.inFloat("Size Z", 1), + inTex = op.inTexture("Texture"), + + inWorldSpace = op.inValueBool("WorldSpace", true), + inPrio = op.inBool("Priority", true), + next = op.outTrigger("Next"); + +op.setPortGroup("Scale", [doScale, sizeX, sizeZ, sizeY]); +op.setPortGroup("Position", [x, y, z]); +op.setPortGroup("Color", [inBlend, r, g, b]); +r.setUiAttribs({ "colorPick": true }); + +const cgl = op.patch.cgl; + +const srcHeadVert = "" + .endl() + "OUT vec4 MOD_vertPos;" + .endl(); + +const srcBodyVert = "" + .endl() + "#ifndef MOD_WORLDSPACE" + .endl() + " MOD_vertPos=vec4(vPosition,1.0);" + .endl() + "#endif" + + .endl() + "#ifdef MOD_WORLDSPACE" + .endl() + " MOD_vertPos=mMatrix*pos;" + .endl() + "#endif" + .endl(); + +inWorldSpace.onChange = + inTex.onLinkChanged = + inArea.onChange = + inInvert.onChange = + doScale.onChange = + inBlend.onChange = updateDefines; + +render.onTriggered = doRender; + +const vertModTitle = "vert_" + op.name; +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "priority": 2, + "title": vertModTitle, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert +}); + +mod.addModule({ + "title": op.name, + "name": "MODULE_COLOR", + "srcHeadFrag": attachments.colorarea_head_frag, + "srcBodyFrag": attachments.colorarea_frag +}); + +mod.addUniform("4f", "MOD_inSizeAmountFalloffSizeX", inSize, inAmount, inFalloff, 0); +mod.addUniform("3f", "MOD_color", r, g, b); +mod.addUniform("3f", "MOD_pos", x, y, z); +mod.addUniform("3f", "MOD_scale", sizeX, sizeY, sizeZ); +mod.addUniform("t", "MOD_tex"); + +updateDefines(); + +inPrio.onChange = updatePrio; +updatePrio(); + +function updatePrio() +{ + mod.removeModule(vertModTitle); + + const vmod = { + // "priority": 0, + "title": vertModTitle, + "name": "MODULE_VERTEX_POSITION", + srcHeadVert, + srcBodyVert + }; + + if (inPrio.get()) vmod.priority = 2; + + mod.addModule(vmod); +} + +function updateDefines() +{ + mod.toggleDefine("MOD_BLEND_NORMAL", inBlend.get() == "Normal"); + mod.toggleDefine("MOD_BLEND_OPACITY", inBlend.get() == "Opacity"); + mod.toggleDefine("MOD_BLEND_MULTIPLY", inBlend.get() == "Multiply"); + mod.toggleDefine("MOD_BLEND_DISCARD", inBlend.get() == "Discard"); + mod.toggleDefine("MOD_BLEND_ADD", inBlend.get() == "Add"); + + mod.toggleDefine("MOD_AREA_SIZE", doScale.get()); + + mod.toggleDefine("MOD_AREA_INVERT", inInvert.get()); + mod.toggleDefine("MOD_WORLDSPACE", inWorldSpace.get()); + + mod.toggleDefine("MOD_AREA_AXIS_X", inArea.get() == "Axis X"); + mod.toggleDefine("MOD_AREA_AXIS_Y", inArea.get() == "Axis Y"); + mod.toggleDefine("MOD_AREA_AXIS_Z", inArea.get() == "Axis Z"); + mod.toggleDefine("MOD_AREA_AXIS_X_INFINITE", inArea.get() == "Axis X Infinite"); + mod.toggleDefine("MOD_AREA_AXIS_Y_INFINITE", inArea.get() == "Axis Y Infinite"); + mod.toggleDefine("MOD_AREA_AXIS_Z_INFINITE", inArea.get() == "Axis Z Infinite"); + mod.toggleDefine("MOD_AREA_SPHERE", inArea.get() == "Sphere"); + mod.toggleDefine("MOD_AREA_BOX", inArea.get() == "Box"); + + mod.toggleDefine("MOD_DOSCALE", doScale.get()); + + // mod.removeUniform("3f", "MOD_scale",sizeX,sizeY,sizeZ); + sizeX.setUiAttribs({ "greyout": !doScale.get() }); + sizeY.setUiAttribs({ "greyout": !doScale.get() }); + sizeZ.setUiAttribs({ "greyout": !doScale.get() }); + + mod.toggleDefine("MOD_USE_TEX", inTex.isLinked()); +} + +function drawHelpers() +{ + if (cgl.frameStore.shadowPass) return; + if (cgl.shouldDrawHelpers(op)) gui.setTransformGizmo({ "posX": x, "posY": y, "posZ": z }); +} + +function doRender() +{ + // if(doScale.get()) mod.setUniformValue("MOD_scale",[sizeX.get(),sizeY.get(),sizeZ.get()]); + mod.bind(); + + if (inTex.isLinked()) + { + let tex = inTex.get(); + + if (!tex) tex = CGL.Texture.getEmptyTexture(cgl).tex; + else tex = tex.tex; + + mod.pushTexture("MOD_tex", tex); + } + + drawHelpers(); + next.trigger(); + + mod.unbind(); +} + + +}; + +Ops.Gl.ShaderEffects.ColorArea_v4.prototype = new CABLES.Op(); +CABLES.OPS["83a4b2b8-23ff-4491-b538-e89ac7934d1a"]={f:Ops.Gl.ShaderEffects.ColorArea_v4,objName:"Ops.Gl.ShaderEffects.ColorArea_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.ColorizeInstancedMeshes +// +// ************************************************************** + +Ops.Gl.ShaderEffects.ColorizeInstancedMeshes = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colorize_instances_frag":"\n\n// col.rgb*=MOD_random(vec2(instanceIndexFrag/1000.0,instanceIndexFrag/1000.0));\n\nfloat MOD_rand=fract(sin(instanceIndexFrag));\n\n#ifdef LOOKUPTEX\n // col.rgb*= (texture( MOD_lut, vec2(MOD_rand,0.5) ).rgb-vec3(0.5))*2.0*MOD_strength;\n col.rgb*= ( (texture( MOD_lut, vec2(MOD_rand,0.5) ).rgb+0.5)*MOD_strength);\n // col.rgb*=MOD_strength;\n#endif\n\n#ifdef MULCOLOR\n col.rgb*=(MOD_rand*2.0)*MOD_strength;\n#endif\n\n",}; +const cgl = op.patch.cgl; + +op.render = op.inTrigger("render"); +op.trigger = op.outTrigger("trigger"); + +const inStrength = op.inValueSlider("Amount", 1); +const mulColor = op.inValueBool("Multiply Color"); +const inLookup = op.inTexture("Lookup Texture"); + +let shader = null; + +let srcBodyVert = "" + .endl() + "instanceIndexFrag=instanceIndex;" + .endl(); + +let srcHeadVert = "" + .endl() + "#ifndef ATTRIB_instanceIndex" + .endl() + " #define ATTRIB_instanceIndex" + .endl() + " IN float instanceIndex;" + .endl() + "#endif" + .endl() + "#ifndef ATTRIB_instanceIndexFrag" + .endl() + " #define ATTRIB_instanceIndexFrag" + .endl() + " OUT float instanceIndexFrag;" + .endl() + "#endif" + .endl(); + +let srcHeadFrag = "" + + .endl() + "UNI float MOD_strength;" + .endl() + "#ifdef LOOKUPTEX" + .endl() + " UNI sampler2D MOD_lut;" + .endl() + "#endif" + .endl() + "#ifndef ATTRIB_instanceIndexFrag" + .endl() + " #define ATTRIB_instanceIndexFrag" + .endl() + " IN float instanceIndexFrag;" + .endl() + "#endif" + .endl() + "float MOD_random(vec2 co)" + .endl() + "{" + .endl() + " return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 437511.5453);" + .endl() + "}" + + .endl(); + +let moduleVert = null; +let moduleFrag = null; +function removeModule() +{ + if (shader && moduleVert) shader.removeModule(moduleVert); + if (shader && moduleFrag) shader.removeModule(moduleFrag); + shader = null; +} + +mulColor.onChange = updateMulColor; +function updateMulColor() +{ + if (shader) + if (mulColor.get())shader.define("MULCOLOR"); + else shader.removeDefine("MULCOLOR"); +} + +op.render.onLinkChanged = removeModule; + +inLookup.onChange = updateLookupTexture; +function updateLookupTexture() +{ + if (shader) + { + if (inLookup.get())shader.define("LOOKUPTEX"); + else shader.removeDefine("LOOKUPTEX"); + inLookup.uniform = new CGL.Uniform(shader, "t", moduleFrag.prefix + "lut", 5); + } +} + +op.render.onTriggered = function () +{ + if (!cgl.getShader()) + { + op.trigger.trigger(); + return; + } + + if (cgl.getShader() != shader) + { + if (shader) removeModule(); + shader = cgl.getShader(); + + moduleVert = shader.addModule( + { + "title": op.objName, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert + }); + + moduleFrag = shader.addModule( + { + "title": op.objName, + "name": "MODULE_COLOR", + "srcHeadFrag": srcHeadFrag, + "srcBodyFrag": attachments.colorize_instances_frag + }); + + inStrength.uniform = new CGL.Uniform(shader, "f", moduleFrag.prefix + "strength", inStrength); + + updateMulColor(); + updateLookupTexture(); + } + + if (inLookup.get()) + { + cgl.setTexture(5, inLookup.get().tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, inLookup.get().tex); + } + + if (!shader) return; + + op.trigger.trigger(); +}; + + +}; + +Ops.Gl.ShaderEffects.ColorizeInstancedMeshes.prototype = new CABLES.Op(); +CABLES.OPS["2939f8ec-dd70-40ad-999d-7242e87296b9"]={f:Ops.Gl.ShaderEffects.ColorizeInstancedMeshes,objName:"Ops.Gl.ShaderEffects.ColorizeInstancedMeshes"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.DeformArea +// +// ************************************************************** + +Ops.Gl.ShaderEffects.DeformArea = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"deformarea_vert":"vec4 MOD_deform(vec4 pos,mat4 mMatrix)\n{\n vec4 modelPos=pos;\n\n #ifdef MOD_WORLDSPACE\n modelPos=mMatrix*pos;\n #endif\n\n vec3 forcePos=vec3(MOD_x,MOD_y,MOD_z);\n vec3 vecToOrigin=modelPos.xyz-forcePos;\n float dist=abs(length(vecToOrigin));\n float distAlpha = (MOD_size - dist) / MOD_size;\n\n if(MOD_size > dist)\n {\n vec3 vecNormal=normalize(vecToOrigin);\n\n if(MOD_smooth>0.0) distAlpha=smoothstep(0.0,MOD_size,distAlpha);\n\n vec3 velocity = (vecNormal * distAlpha * MOD_strength );\n\n pos.xyz+=velocity*0.1;\n }\n\n return pos;\n\n}\n",}; +const + render = op.inTrigger("render"), + next = op.outTrigger("trigger"), + inSize = op.inValue("Size", 1), + inStrength = op.inValue("Strength", 0.5), + inSmooth = op.inValueBool("Smooth", true), + inWorldSpace = op.inValueBool("WorldSpace", false), + x = op.inValue("x"), + y = op.inValue("y"), + z = op.inValue("z"); + +const cgl = op.patch.cgl; + +inWorldSpace.onChange = updateWorldspace; + +// let shader = null; +const srcHeadVert = attachments.deformarea_vert; + +const srcBodyVert = "" + .endl() + "pos=MOD_deform(pos,mMatrix);" + .endl(); + +// let moduleVert = null; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.deformarea_vert, + "srcBodyVert": srcBodyVert +}); + +mod.addUniformVert("f", "MOD_size", inSize); +mod.addUniformVert("f", "MOD_strength", inStrength); +mod.addUniformVert("f", "MOD_smooth", inSmooth); + +mod.addUniformVert("f", "MOD_x", x); +mod.addUniformVert("f", "MOD_y", y); +mod.addUniformVert("f", "MOD_z", z); + +render.onTriggered = function () +{ + if (CABLES.UI) + { + if (op.isCurrentUiOp()) gui.setTransformGizmo({ "posX": x, "posY": y, "posZ": z }); + + if (cgl.shouldDrawHelpers(op)) + { + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, [x.get(), y.get(), z.get()]); + CABLES.GL_MARKER.drawSphere(op, inSize.get()); + cgl.popModelMatrix(); + } + } + + mod.bind(); + next.trigger(); + mod.unbind(); +}; + +function updateWorldspace() +{ + mod.toggleDefine("MOD_WORLDSPACE", inWorldSpace.get()); +} + + +}; + +Ops.Gl.ShaderEffects.DeformArea.prototype = new CABLES.Op(); +CABLES.OPS["6c40cfcb-75c8-4c64-bc13-c37faaea73df"]={f:Ops.Gl.ShaderEffects.DeformArea,objName:"Ops.Gl.ShaderEffects.DeformArea"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.DiscardMaterialAlpha +// +// ************************************************************** + +Ops.Gl.ShaderEffects.DiscardMaterialAlpha = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"alpha_frag":"\n\n#ifdef ALPHA_THRESHOLD\n if(col.a +{ + mod.toggleDefine("ENABLE_FRESNEL_MOD", inActive); + inR.setUiAttribs({ "greyout": !inActive.get() }); + inG.setUiAttribs({ "greyout": !inActive.get() }); + inB.setUiAttribs({ "greyout": !inActive.get() }); + inIntensity.setUiAttribs({ "greyout": !inActive.get() }); + inExponent.setUiAttribs({ "greyout": !inActive.get() }); +}; + +const outTrigger = op.outTrigger("Trigger Out"); + + +const mod = new CGL.ShaderModifier(cgl, "fresnelGlow"); +mod.toggleDefine("ENABLE_FRESNEL_MOD", inActive); + +mod.addModule({ + "priority": 2, + "title": "fresnelGlow", + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.fresnel_head_vert, + "srcBodyVert": attachments.fresnel_body_vert +}); + +mod.addModule({ + "title": "fresnelGlow", + "name": "MODULE_COLOR", + "srcHeadFrag": attachments.fresnel_head_frag, + "srcBodyFrag": attachments.fresnel_body_frag +}); + +mod.addUniform("4f", "MOD_inFresnel", inR, inG, inB, inIntensity); +mod.addUniform("f", "MOD_inFresnelExponent", inExponent); + +inTrigger.onTriggered = () => +{ + mod.bind(); + outTrigger.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.FresnelGlow.prototype = new CABLES.Op(); +CABLES.OPS["89979937-68a6-4736-8241-3c6b748103d4"]={f:Ops.Gl.ShaderEffects.FresnelGlow,objName:"Ops.Gl.ShaderEffects.FresnelGlow"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.InstancedDisplacementMap_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.InstancedDisplacementMap_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"displace_vert":"\n#ifdef INSTANCING\n\n #ifdef MOD_SRC_INSTMAT\n vec3 MOD_p=vec3(instMat[3][0],instMat[3][1],instMat[3][2]);\n #endif\n\n #ifdef MOD_SRC_MMAT\n vec3 MOD_p=vec3(mMatrix[3][0],mMatrix[3][1],mMatrix[3][2]);\n #endif\n // vec3 MOD_p=(mMatrix*vec4(0.0,0.0,0.0,1.0)).xyz;//vec3(mMatrix[3][0],mMatrix[3][1],mMatrix[3][2]);\n\n vec2 MOD_coord=MOD_p.xy*(1.0/MOD_scale)+MOD_offset-vec2(0.5,0.5);\n\n // vec3 MOD_dis\n\n #ifdef MOD_CHAN_R\n vec3 MOD_dis=vec3( texture(MOD_texture,MOD_coord).r );\n #endif\n #ifdef MOD_CHAN_G\n vec3 MOD_dis=vec3( texture(MOD_texture,MOD_coord).g );\n #endif\n #ifdef MOD_CHAN_B\n vec3 MOD_dis=vec3( texture(MOD_texture,MOD_coord).b );\n #endif\n\n #ifdef MOD_CHAN_RGB\n vec3 MOD_dis=vec3( texture(MOD_texture,MOD_coord).rgb );\n #endif\n\n #ifdef MOD_COLORIZE\n MOD_dispColor=MOD_dis;\n #endif\n\n #ifdef MOD_DEBUG\n MOD_dispColor.rg=mod(MOD_coord,1.0).rg;\n #endif\n\n\n #ifdef MOD_NORMALIZE\n MOD_dis=(MOD_dis-0.5)*2.0;\n #endif\n\n\n #ifdef MOD_CLAMP\n if(MOD_coord.x>1.0||MOD_coord.x<0.0|| MOD_coord.y>1.0 || MOD_coord.y<0.0)\n {\n MOD_dis=vec3(0.0);\n #ifdef MOD_COLORIZE\n MOD_dispColor=vec3(0.0);\n #endif\n }\n #endif\n\n\n #ifdef MOD_MODE_TRANS\n MOD_dis*=MOD_strength;\n MOD_dis+=MOD_min;\n\n #ifdef MOD_AXIS_X\n // pos.x+=MOD_dis;\n mMatrix[3][0] += MOD_dis.x;\n #endif\n #ifdef MOD_AXIS_Y\n // pos.y+=MOD_dis;\n mMatrix[3][1] += MOD_dis.y;\n #endif\n #ifdef MOD_AXIS_Z\n // pos.z+=MOD_dis;\n mMatrix[3][2] += MOD_dis.z;\n #endif\n #endif\n\n #ifdef MOD_MODE_SCALE\n MOD_dis*=MOD_strength;\n MOD_dis+=MOD_min;\n\n #ifdef MOD_ABS\n MOD_dis=abs(MOD_dis);\n #endif\n\n #ifdef MOD_AXIS_X\n pos.x*=MOD_dis.x;\n #endif\n #ifdef MOD_AXIS_Y\n pos.y*=MOD_dis.y;\n #endif\n #ifdef MOD_AXIS_Z\n pos.z*=MOD_dis.z;\n #endif\n #endif\n\n #ifdef MOD_MODE_ROT\n #define MOD_PI 3.14159265358\n MOD_dis*=MOD_strength;\n MOD_dis+=MOD_min;\n\n #ifdef MOD_AXIS_X\n pos*=MOD_rot(vec3(1.0,0.0,0.0), MOD_dis.x*3.14);\n norm=((vec4(norm,0.0)*MOD_rot(vec3(1.0,0.0,0.0), MOD_dis.x*MOD_PI)).xyz);\n #endif\n #ifdef MOD_AXIS_Y\n pos*=MOD_rot(vec3(0.0,1.0,0.0), MOD_dis.y*MOD_PI);\n norm=((vec4(norm,0.0)*MOD_rot(vec3(0.0,1.0,0.0), MOD_dis.y*MOD_PI)).xyz);\n #endif\n #ifdef MOD_AXIS_Z\n pos*=MOD_rot(vec3(0.0,0.0,1.0), MOD_dis.z*MOD_PI);\n norm=((vec4(norm,0.0)*MOD_rot(vec3(0.0,0.0,1.0), MOD_dis.z*MOD_PI)).xyz);\n #endif\n\n norm=normalize(norm);\n #endif\n\n#endif\n","displace_head_vert":"\n#ifdef MOD_COLORIZE\nOUT vec3 MOD_dispColor;\n#endif\n\n\n\n#ifdef MOD_MODE_ROT\n\n mat4 MOD_rot(vec3 axis, float angle)\n {\n axis = normalize(axis);\n float s = sin(angle);\n float c = cos(angle);\n float oc = 1.0 - c;\n\n return mat4(\n \t\toc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,\n oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,\n oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,\n \t\t0.0, 0.0, 0.0, 1.0\n \t);\n }\n\n#endif",}; +const + inTrigger = op.inTrigger("Trigger"), + inTex = op.inTexture("Texture"), + inSrc = op.inSwitch("Source", ["Model Pos", "Inst Pos"], "Model Pos"), + inMode = op.inSwitch("Mode", ["Translate", "Scale", "Rotate"], "Translate"), + inStrength = op.inFloat("Strength", 1), + inMin = op.inFloat("Min", 0), + inScale = op.inFloat("Scale", 1), + inClamp = op.inBool("Clamp", false), + inColorize = op.inBool("Colorize", false), + inDebug = op.inBool("Debug Bounds", false), + inNormalize = op.inBool("Normalize", false), + inOffsetX = op.inFloat("Offset X", 0), + inOffsetY = op.inFloat("Offset Y", 0), + inAbs = op.inBool("Abs", false), + inChannel = op.inSwitch("Channel", ["R", "G", "B", "RGB"], "R"), + + inAxisX = op.inBool("X", false), + inAxisY = op.inBool("Y", false), + inAxisZ = op.inBool("Z", true), + + next = op.outTrigger("Next"); + +inAxisX.onChange = +inTex.onChange = + inAxisY.onChange = + inAxisZ.onChange = + inClamp.onChange = + inMode.onChange = + inNormalize.onChange = + inDebug.onChange = + inChannel.onChange = + inAbs.onChange = + inColorize.onChange = updateDefines; +inTrigger.onTriggered = render; + +const cgl = op.patch.cgl; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.displace_head_vert || "", + "srcBodyVert": attachments.displace_vert || "" +}); + +mod.addModule({ + "title": op.name, + "name": "MODULE_COLOR", + "srcHeadFrag": "#ifdef MOD_COLORIZE\nIN vec3 MOD_dispColor;\n#endif", + "srcBodyFrag": "#ifdef MOD_COLORIZE\ncol.rgb*=MOD_dispColor;\n#endif" +}); + +mod.addUniformVert("t", "MOD_texture", 0); +mod.addUniformVert("2f", "MOD_offset", inOffsetX, inOffsetY); +mod.addUniformVert("f", "MOD_scale", inScale); +mod.addUniformVert("f", "MOD_strength", inStrength); +mod.addUniformVert("f", "MOD_min", inMin); +updateDefines(); + +function updateDefines() +{ + op.setUiAttrib({ "extendTitle": inMode.get() }); + + mod.toggleDefine("MOD_MODE_TRANS", inMode.get() === "Translate"); + mod.toggleDefine("MOD_MODE_SCALE", inMode.get() === "Scale"); + mod.toggleDefine("MOD_MODE_ROT", inMode.get() === "Rotate"); + + mod.toggleDefine("MOD_CHAN_R", inChannel.get() != "G" && inChannel.get() != "B" && inChannel.get() != "RGB"); + mod.toggleDefine("MOD_CHAN_G", inChannel.get() == "G"); + mod.toggleDefine("MOD_CHAN_B", inChannel.get() == "B"); + mod.toggleDefine("MOD_CHAN_RGB", inChannel.get() == "RGB"); + + mod.toggleDefine("MOD_SRC_INSTMAT", inSrc.get() == "Inst Pos"); + mod.toggleDefine("MOD_SRC_MMAT", inSrc.get() == "Model Pos"); + + mod.toggleDefine("MOD_AXIS_X", inAxisX.get()); + mod.toggleDefine("MOD_AXIS_Y", inAxisY.get()); + mod.toggleDefine("MOD_AXIS_Z", inAxisZ.get()); + + mod.toggleDefine("MOD_ABS", inAbs.get()); + + mod.toggleDefine("MOD_CLAMP", inClamp.get()); + mod.toggleDefine("MOD_COLORIZE", inColorize.get() || inDebug.get()); + mod.toggleDefine("MOD_NORMALIZE", inNormalize.get()); + + mod.toggleDefine("MOD_DEBUG", inDebug.get()); +} + +function render() +{ + mod.bind(); + + if (inTex.get()) mod.pushTexture("MOD_texture", inTex.get().tex); + else mod.pushTexture("MOD_texture", CGL.Texture.getEmptyTexture(cgl).tex); + + next.trigger(); + mod.unbind(); +} + + +}; + +Ops.Gl.ShaderEffects.InstancedDisplacementMap_v2.prototype = new CABLES.Op(); +CABLES.OPS["e6ba9d95-20f3-4c1c-9967-26d48d278f0a"]={f:Ops.Gl.ShaderEffects.InstancedDisplacementMap_v2,objName:"Ops.Gl.ShaderEffects.InstancedDisplacementMap_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.InstancedPerlinPosition_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.InstancedPerlinPosition_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"perlin_instposition_vert":"\nfloat MOD_Interpolation_C2( float x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } // 6x^5-15x^4+10x^3\t( Quintic Curve. As used by Perlin in Improved Noise. http://mrl.nyu.edu/~perlin/paper445.pdf )\nvec2 MOD_Interpolation_C2( vec2 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec3 MOD_Interpolation_C2( vec3 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 MOD_Interpolation_C2( vec4 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 MOD_Interpolation_C2_InterpAndDeriv( vec2 x ) { return x.xyxy * x.xyxy * ( x.xyxy * ( x.xyxy * ( x.xyxy * vec2( 6.0, 0.0 ).xxyy + vec2( -15.0, 30.0 ).xxyy ) + vec2( 10.0, -60.0 ).xxyy ) + vec2( 0.0, 30.0 ).xxyy ); }\nvec3 MOD_Interpolation_C2_Deriv( vec3 x ) { return x * x * (x * (x * 30.0 - 60.0) + 30.0); }\n\n\nvoid MOD_FAST32_hash_3D( \tvec3 gridcell,\n out vec4 lowz_hash_0,\n out vec4 lowz_hash_1,\n out vec4 lowz_hash_2,\n out vec4 highz_hash_0,\n out vec4 highz_hash_1,\n out vec4 highz_hash_2\t)\t\t//\tgenerates 3 random numbers for each of the 8 cell corners\n{\n // gridcell is assumed to be an integer coordinate\n\n //\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const vec3 SOMELARGEFLOATS = vec3( 635.298681, 682.357502, 668.926525 );\n const vec3 ZINC = vec3( 48.500388, 65.294118, 63.934599 );\n\n //\ttruncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n //\tcalculate the noise\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n P = P.xzxz * P.yyww;\n vec3 lowz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) );\n vec3 highz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) );\n lowz_hash_0 = fract( P * lowz_mod.xxxx );\n highz_hash_0 = fract( P * highz_mod.xxxx );\n lowz_hash_1 = fract( P * lowz_mod.yyyy );\n highz_hash_1 = fract( P * highz_mod.yyyy );\n lowz_hash_2 = fract( P * lowz_mod.zzzz );\n highz_hash_2 = fract( P * highz_mod.zzzz );\n}\n\n//\n//\tPerlin Noise 3D ( gradient noise )\n//\tReturn value range of -1.0->1.0\n//\thttp://briansharpe.files.wordpress.com/2011/11/perlinsample.jpg\n//\nfloat MOD_Perlin3D( vec3 P )\n{\n //\testablish our grid cell and unit position\n vec3 Pi = floor(P);\n vec3 Pf = P - Pi;\n vec3 Pf_min1 = Pf - 1.0;\n\n#if 1\n //\n //\tclassic noise.\n //\trequires 3 random values per point. with an efficent hash function will run faster than improved noise\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1;\n MOD_FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n //SGPP_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n\n //\tcalculate the gradients\n vec4 grad_x0 = hashx0 - 0.49999;\n vec4 grad_y0 = hashy0 - 0.49999;\n vec4 grad_z0 = hashz0 - 0.49999;\n vec4 grad_x1 = hashx1 - 0.49999;\n vec4 grad_y1 = hashy1 - 0.49999;\n vec4 grad_z1 = hashz1 - 0.49999;\n vec4 grad_results_0 = inversesqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 );\n vec4 grad_results_1 = inversesqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 );\n\n#if 1\n //\tClassic Perlin Interpolation\n vec3 blend = MOD_Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n float final = dot( res0, blend2.zxzx * blend2.wwyy );\n final *= 1.1547005383792515290182975610039;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.75)\n return final;\n#else\n //\tClassic Perlin Surflet\n //\thttp://briansharpe.wordpress.com/2012/03/09/modifications-to-classic-perlin-noise/\n Pf *= Pf;\n Pf_min1 *= Pf_min1;\n vec4 vecs_len_sq = vec4( Pf.x, Pf_min1.x, Pf.x, Pf_min1.x ) + vec4( Pf.yy, Pf_min1.yy );\n float final = dot( Falloff_Xsq_C2( min( vec4( 1.0 ), vecs_len_sq + Pf.zzzz ) ), grad_results_0 ) + dot( Falloff_Xsq_C2( min( vec4( 1.0 ), vecs_len_sq + Pf_min1.zzzz ) ), grad_results_1 );\n final *= 2.3703703703703703703703703703704;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/cube(0.75)\n return final;\n#endif\n\n#else\n //\n //\timproved noise.\n //\trequires 1 random value per point. Will run faster than classic noise if a slow hashing function is used\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hash_lowz, hash_highz;\n MOD_FAST32_hash_3D( Pi, hash_lowz, hash_highz );\n //BBS_hash_3D( Pi, hash_lowz, hash_highz );\n //SGPP_hash_3D( Pi, hash_lowz, hash_highz );\n\n //\n //\t\"improved\" noise using 8 corner gradients. Faster than the 12 mid-edge point method.\n //\tKen mentions using diagonals like this can cause \"clumping\", but we'll live with that.\n //\t[1,1,1] [-1,1,1] [1,-1,1] [-1,-1,1]\n //\t[1,1,-1] [-1,1,-1] [1,-1,-1] [-1,-1,-1]\n //\n hash_lowz -= 0.5;\n vec4 grad_results_0_0 = vec2( Pf.x, Pf_min1.x ).xyxy * sign( hash_lowz );\n hash_lowz = abs( hash_lowz ) - 0.25;\n vec4 grad_results_0_1 = vec2( Pf.y, Pf_min1.y ).xxyy * sign( hash_lowz );\n vec4 grad_results_0_2 = Pf.zzzz * sign( abs( hash_lowz ) - 0.125 );\n vec4 grad_results_0 = grad_results_0_0 + grad_results_0_1 + grad_results_0_2;\n\n hash_highz -= 0.5;\n vec4 grad_results_1_0 = vec2( Pf.x, Pf_min1.x ).xyxy * sign( hash_highz );\n hash_highz = abs( hash_highz ) - 0.25;\n vec4 grad_results_1_1 = vec2( Pf.y, Pf_min1.y ).xxyy * sign( hash_highz );\n vec4 grad_results_1_2 = Pf_min1.zzzz * sign( abs( hash_highz ) - 0.125 );\n vec4 grad_results_1 = grad_results_1_0 + grad_results_1_1 + grad_results_1_2;\n\n //\tblend the gradients and return\n vec3 blend = MOD_Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n return dot( res0, blend2.zxzx * blend2.wwyy ) * (2.0 / 3.0);\t//\t(optionally) mult by (2.0/3.0) to scale to a strict -1.0->1.0 range\n#endif\n}\n\nvec4 MOD_deform(mat4 mMatrix,mat4 instMat,vec4 pos)\n{\n\n vec3 ppos=vec3(mMatrix[3][0]+instMat[3][0]*MOD_scale+MOD_scrollx,mMatrix[3][1]+instMat[3][1]*MOD_scale+MOD_scrolly,mMatrix[3][2]+instMat[3][2]*MOD_scale+MOD_scrollz);\n float p=MOD_Perlin3D(ppos)*MOD_strength;\n\n #ifdef MOD_METH_TRANSLATE\n pos.x+=p*MOD_mulAxis.x;\n pos.y+=p*MOD_mulAxis.y;\n pos.z+=p*MOD_mulAxis.z;\n #endif\n #ifdef MOD_METH_SCALE\n p=clamp(p,0.0,99999.0);\n pos.x*=p*MOD_mulAxis.x+MOD_minScale;\n pos.y*=p*MOD_mulAxis.y+MOD_minScale;\n pos.z*=p*MOD_mulAxis.z+MOD_minScale;\n #endif\n\n return pos;\n}\n\n",}; +const + render = op.inTrigger("Render"), + next = op.outTrigger("Next"), + inStrength = op.inValue("Strength", 1), + scrollx = op.inValue("Scroll X"), + scrolly = op.inValue("Scroll Y"), + scrollz = op.inValue("Scroll Z"), + scale = op.inValue("Scale", 1), + meth = op.inSwitch("Method", ["Translate", "Scale"], "Translate"), + mulx = op.inValue("Mul X", 1), + muly = op.inValue("Mul Y", 1), + mulz = op.inValue("Mul Z", 1), + minScale = op.inValue("Min Scale", 0), + inWorldSpace = op.inValueBool("WorldSpace"); + +const cgl = op.patch.cgl; + +let srcBodyVert = "" + .endl() + "#ifdef INSTANCING" + .endl() + " pos=MOD_deform(mMatrix,instMat,pos);" + .endl() + "#endif" + .endl() + "#ifndef INSTANCING" + .endl() + " pos=MOD_deform(mMatrix,mMatrix,pos);" + .endl() + "#endif" + + .endl(); + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.perlin_instposition_vert, + "srcBodyVert": srcBodyVert +}); + +mod.addUniform("f", "MOD_strength", inStrength); +mod.addUniform("f", "MOD_scrollx", scrollx); +mod.addUniform("f", "MOD_scrolly", scrolly); +mod.addUniform("f", "MOD_scrollz", scrollz); +mod.addUniform("f", "MOD_scale", scale); +mod.addUniform("f", "MOD_minScale", minScale); +mod.addUniform("3f", "MOD_mulAxis", mulx, muly, mulz); + +meth.onChange = +inWorldSpace.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + mod.toggleDefine("MOD_WORLDSPACE", inWorldSpace.get()); + mod.toggleDefine("MOD_METH_TRANSLATE", meth.get() == "Translate"); + mod.toggleDefine("MOD_METH_SCALE", meth.get() == "Scale"); +} + +render.onTriggered = function () +{ + mod.bind(); + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.InstancedPerlinPosition_v2.prototype = new CABLES.Op(); +CABLES.OPS["716a9004-cbb8-4032-9f29-2b2f8c8973ba"]={f:Ops.Gl.ShaderEffects.InstancedPerlinPosition_v2,objName:"Ops.Gl.ShaderEffects.InstancedPerlinPosition_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.InstancedTextureColorize +// +// ************************************************************** + +Ops.Gl.ShaderEffects.InstancedTextureColorize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colorize_frag":"\n\n\n\nvec4 MOD_col=texture(MOD_texture,MOD_coord);\n\n#ifdef METH_ADD\ncol.rgb+=MOD_col.rgb*MOD_strength;\n#endif\n#ifdef METH_MUL\ncol.rgb*=MOD_col.rgb*MOD_strength;\n#endif\n\n#ifdef MOD_DEBUG\n col.rg=mod(MOD_coord,1.0).rg;\n#endif\n","colorize_head_frag":"\nIN vec2 MOD_coord;\n\nUNI sampler2D MOD_texture;\nUNI float MOD_strength;","displace_vert":"\n#ifdef INSTANCING\n\n vec3 MOD_p=vec3(mMatrix[3][0],mMatrix[3][1],mMatrix[3][2]);\n MOD_coord=MOD_p.xy*(1.0/MOD_scale)+MOD_offset-vec2(0.5,0.5);\n\n#endif\n\n","displace_head_vert":"OUT vec2 MOD_coord;\n\n",}; +const + inTrigger = op.inTrigger("Trigger"), + inTex = op.inTexture("Texture"), + inStrength = op.inFloatSlider("Strength", 1), + inScale = op.inFloat("Scale", 1), + inClamp = op.inBool("Clamp", false), + inDebug = op.inBool("Debug Bounds", false), + inOffsetX = op.inFloat("Offset X", 0), + inOffsetY = op.inFloat("Offset Y", 0), + inMode = op.inSwitch("Method", ["Add", "Mul"], "Add"), + next = op.outTrigger("Next"); + +inTex.onChange = + inClamp.onChange = + inMode.onChange = + inDebug.onChange = updateDefines; +inTrigger.onTriggered = render; + +const cgl = op.patch.cgl; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.displace_head_vert || "", + "srcBodyVert": attachments.displace_vert || "" +}); + +mod.addModule({ + "title": op.name, + "name": "MODULE_COLOR", + "srcHeadFrag": attachments.colorize_head_frag, + "srcBodyFrag": attachments.colorize_frag +}); + +mod.addUniformVert("t", "MOD_texture", 0); +mod.addUniformVert("2f", "MOD_offset", inOffsetX, inOffsetY); +mod.addUniformVert("f", "MOD_scale", inScale); +mod.addUniformVert("f", "MOD_strength", inStrength); + +function updateDefines() +{ + mod.toggleDefine("MOD_CLAMP", inClamp.get()); + mod.toggleDefine("MOD_DEBUG", inDebug.get()); + mod.toggleDefine("METH_ADD", inMode.get() == "Add"); + mod.toggleDefine("METH_MUL", inMode.get() == "Mul"); +} + +function render() +{ + mod.bind(); + + if (inTex.get()) mod.pushTexture("MOD_texture", inTex.get().tex); + else mod.pushTexture("MOD_texture", CGL.Texture.getEmptyTexture(cgl).tex); + + next.trigger(); + mod.unbind(); +} + + +}; + +Ops.Gl.ShaderEffects.InstancedTextureColorize.prototype = new CABLES.Op(); +CABLES.OPS["7a13a699-6a7f-4fd1-84bb-51dd6c13c820"]={f:Ops.Gl.ShaderEffects.InstancedTextureColorize,objName:"Ops.Gl.ShaderEffects.InstancedTextureColorize"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.LimitMeshByTexCoord +// +// ************************************************************** + +Ops.Gl.ShaderEffects.LimitMeshByTexCoord = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"limitByTexCoords_frag":"\nfloat MOD_offset=0.0;\n\n#ifdef MOD_ANIN_SIN\n #ifdef MOD_ANIN_SIN_TCX\n MOD_offset=sin((texCoord.x)*MOD_freq+MOD_time)*(MOD_ampl/1000.0);\n #endif\n #ifdef MOD_ANIN_SIN_TCY\n MOD_offset=sin((texCoord.y)*MOD_freq+MOD_time)*(MOD_ampl/1000.0);\n #endif\n#endif\n\n#ifdef MOD_AXIS_X\n if(texCoord.x+MOD_offset>=MOD_treshhold)discard;\n#endif\n#ifdef MOD_AXIS_Y\n if(texCoord.y+MOD_offset>=MOD_treshhold)discard;\n#endif\n#ifdef MOD_AXIS_XY\n if((texCoord.y+texCoord.x)/2.0+MOD_offset>=MOD_treshhold)discard;\n#endif\n#ifdef MOD_AXIS_X_INV\n if(1.0-texCoord.x+MOD_offset>=MOD_treshhold)discard;\n#endif\n#ifdef MOD_AXIS_Y_INV\n if(1.0-texCoord.y+MOD_offset>=MOD_treshhold)discard;\n#endif\n#ifdef MOD_AXIS_XY_INV\n if(1.0-((texCoord.y+texCoord.x)/2.0)+MOD_offset>=MOD_treshhold)discard;\n#endif\n\n\n",}; +op.render = op.inTrigger("render"); +op.trigger = op.outTrigger("trigger"); + +const axis = op.inValueSelect("Axis", ["X", "Y", "XY", "X Inverted", "Y Inverted", "XY Inverted"], "X"); +const inTreshhold = op.inValueSlider("treshhold", 0.3); + +const inSinAnim = op.inValueBool("Sine Animation", false); +const inTime = op.inValueFloat("Time"); +const inSinAxis = op.inValueSelect("Sine Source", ["Texcoord X", "Texcoord Y"], "Texcoord X"); + +const inFreq = op.inValueFloat("Frequency", 10); +const inAmpl = op.inValueFloat("Amplitude", 10); + +op.setPortGroup("Sine Animation", [inTime, inSinAnim, inFreq, inAmpl, inSinAxis]); + +const cgl = op.patch.cgl; +let shader = null; + +let srcHeadFrag = "" + .endl() + "UNI float MOD_treshhold;" + .endl() + "UNI float MOD_time;" + .endl() + "UNI float MOD_ampl;" + .endl() + "UNI float MOD_freq;" + .endl(); + +let moduleFrag = null; + +inSinAxis.onChange = inSinAnim.onChange = axis.onChange = updateAxis; + +function updateAxis() +{ + if (!shader) return; + shader.toggleDefine(moduleFrag.prefix + "AXIS_X", axis.get() == "X"); + shader.toggleDefine(moduleFrag.prefix + "AXIS_Y", axis.get() == "Y"); + shader.toggleDefine(moduleFrag.prefix + "AXIS_XY", axis.get() == "XY"); + shader.toggleDefine(moduleFrag.prefix + "AXIS_X_INV", axis.get() == "X Inverted"); + shader.toggleDefine(moduleFrag.prefix + "AXIS_Y_INV", axis.get() == "Y Inverted"); + shader.toggleDefine(moduleFrag.prefix + "AXIS_XY_INV", axis.get() == "XY Inverted"); + + shader.toggleDefine(moduleFrag.prefix + "ANIN_SIN", inSinAnim.get()); + + shader.toggleDefine(moduleFrag.prefix + "ANIN_SIN_TCX", inSinAxis.get() == "Texcoord X"); + shader.toggleDefine(moduleFrag.prefix + "ANIN_SIN_TCY", inSinAxis.get() == "Texcoord Y"); +} + +function removeModule() +{ + if (shader && moduleFrag) shader.removeModule(moduleFrag); + shader = null; +} + +op.render.onLinkChanged = removeModule; +let + uniTime = null, + uniFreq = null, + uniAmpl = null, + uniTreshhold = null; + +op.render.onTriggered = function () +{ + if (!cgl.getShader()) + { + op.trigger.trigger(); + return; + } + + if (cgl.getShader() != shader) + { + if (shader) removeModule(); + shader = cgl.getShader(); + + moduleFrag = shader.addModule( + { + "title": op.objName, + "name": "MODULE_COLOR", + "srcHeadFrag": srcHeadFrag, + "srcBodyFrag": attachments.limitByTexCoords_frag || "" + }); + + uniTreshhold = new CGL.Uniform(shader, "f", moduleFrag.prefix + "treshhold", inTreshhold); + uniTime = new CGL.Uniform(shader, "f", moduleFrag.prefix + "time", inTime); + uniFreq = new CGL.Uniform(shader, "f", moduleFrag.prefix + "freq", inFreq); + uniAmpl = new CGL.Uniform(shader, "f", moduleFrag.prefix + "ampl", inAmpl); + + updateAxis(); + } + + if (!shader) return; + + op.trigger.trigger(); +}; + + +}; + +Ops.Gl.ShaderEffects.LimitMeshByTexCoord.prototype = new CABLES.Op(); +CABLES.OPS["b7482d75-913d-4564-92ae-81f8dbead3ce"]={f:Ops.Gl.ShaderEffects.LimitMeshByTexCoord,objName:"Ops.Gl.ShaderEffects.LimitMeshByTexCoord"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.MeshPixelNoise_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.MeshPixelNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"pixelnoise_frag":"IN vec4 MOD_pos;\n\nfloat MOD_mod289(float x){return x - floor(x * (1.0 / 289.0)) * 289.0;}\nvec4 MOD_mod289(vec4 x){return x - floor(x * (1.0 / 289.0)) * 289.0;}\nvec4 MOD_perm(vec4 x){return MOD_mod289(((x * 34.0) + 1.0) * x);}\n\nfloat MOD_meshPixelNoise(vec3 p){\n p+=vec3(MOD_x,MOD_y,MOD_z);\n vec3 a = floor(p);\n vec3 d = p - a;\n d = d * d * (3.0 - 2.0 * d);\n\n vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0);\n vec4 k1 = MOD_perm(b.xyxy);\n vec4 k2 = MOD_perm(k1.xyxy + b.zzww);\n\n vec4 c = k2 + a.zzzz;\n vec4 k3 = MOD_perm(c);\n vec4 k4 = MOD_perm(c + 1.0);\n\n vec4 o1 = fract(k3 * (1.0 / 41.0));\n vec4 o2 = fract(k4 * (1.0 / 41.0));\n\n vec4 o3 = o2 * d.z + o1 * (1.0 - d.z);\n vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x);\n\n return o4.y * d.y + o4.x * (1.0 - d.y);\n}\n",}; +const + render = op.inTrigger("render"), + next = op.outTrigger("trigger"), + inScale = op.inValue("Scale", 10), + inAmount = op.inValueSlider("Amount", 0.3), + inBlend = op.inSwitch("Blendmode", ["Sub", "Add", "Mul"], "Sub"), + inWorldSpace = op.inValueBool("WorldSpace"), + r = op.inValueSlider("r", 0), + g = op.inValueSlider("g", 0), + b = op.inValueSlider("b", 0), + x = op.inFloat("x", 0), + y = op.inFloat("y", 0), + z = op.inFloat("z", 0); + +r.setUiAttribs({ "colorPick": true }); + +op.setPortGroup("Color", [r, g, b]); +op.setPortGroup("Position", [x, y, z]); +const cgl = op.patch.cgl; + +inBlend.onChange = +inWorldSpace.onChange = updateDefines; + +const srcHeadVert = "" + .endl() + "OUT vec4 MOD_pos;" + .endl(); + +const srcBodyVert = "" + .endl() + "#ifndef MOD_WORLDSPACE" + .endl() + " MOD_pos=vec4(pos.xyz,1.0);" + .endl() + "#endif" + .endl() + "#ifdef MOD_WORLDSPACE" + .endl() + " MOD_pos=vec4(pos.xyz,1.0)*mMatrix;" + .endl() + "#endif" + .endl(); + +const srcHeadFrag = attachments.pixelnoise_frag; + +const srcBodyFrag = "" + .endl() + "vec3 MOD_rndVal = vec3(1.-MOD_r,1.-MOD_g,1.-MOD_b)*MOD_meshPixelNoise(MOD_pos.xyz*MOD_scale)*MOD_amount/4.0;" + + .endl() + "#ifdef MOD_BLEND_MUL" + .endl() + " col.rgb *= MOD_rndVal;" + .endl() + "#endif" + .endl() + "#ifdef MOD_BLEND_SUB" + .endl() + " col.rgb -= MOD_rndVal;" + .endl() + "#endif" + .endl() + "#ifdef MOD_BLEND_ADD" + .endl() + " col.rgb += MOD_rndVal;" + .endl() + "#endif" + .endl(); + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert +}); + +mod.addModule({ + "title": op.name, + "name": "MODULE_COLOR", + "srcHeadFrag": attachments.pixelnoise_frag, + "srcBodyFrag": srcBodyFrag +}); + +mod.addUniformFrag("f", "MOD_scale", inScale); +mod.addUniformFrag("f", "MOD_amount", inAmount); +mod.addUniformFrag("f", "MOD_r", r); +mod.addUniformFrag("f", "MOD_g", g); +mod.addUniformFrag("f", "MOD_b", b); +mod.addUniformFrag("f", "MOD_x", x); +mod.addUniformFrag("f", "MOD_y", y); +mod.addUniformFrag("f", "MOD_z", z); +updateDefines(); + +function updateDefines() +{ + mod.toggleDefine("MOD_WORLDSPACE", inWorldSpace.get()); + + mod.toggleDefine("MOD_BLEND_ADD", inBlend.get() == "Add"); + mod.toggleDefine("MOD_BLEND_SUB", inBlend.get() == "Sub"); + mod.toggleDefine("MOD_BLEND_MUL", inBlend.get() == "Mul"); +} + +render.onTriggered = function () +{ + mod.bind(); + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.MeshPixelNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["f6240484-56b5-4bb8-92e6-ead30431771a"]={f:Ops.Gl.ShaderEffects.MeshPixelNoise_v2,objName:"Ops.Gl.ShaderEffects.MeshPixelNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.ModuloVertexPosition +// +// ************************************************************** + +Ops.Gl.ShaderEffects.ModuloVertexPosition = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"trans_vert":"\n#ifdef MOD_AXIS_X\npos.x=mod(pos.x,MOD_modulo);\n#endif\n\n#ifdef MOD_AXIS_Y\npos.y=mod(pos.y,MOD_modulo);\n#endif\n\n#ifdef MOD_AXIS_Z\npos.z=mod(pos.z,MOD_modulo);\n#endif",}; +const + render = op.inTrigger("render"), + axis = op.inValueSelect("Axis", ["X", "Y", "Z"], "X"), + inModu = op.inFloat("Modulo", 1), + trigger = op.outTrigger("Trigger"); + +const cgl = op.patch.cgl; +const mod = new CGL.ShaderModifier(cgl, op.name); + +mod.addModule({ + "name": "MODULE_VERTEX_POSITION", + "srcBodyVert": attachments.trans_vert || "" +}); + +mod.addUniformVert("f", "MOD_modulo", inModu); + +axis.onChange = updateDefines; + +updateDefines(); + +function updateDefines() +{ + mod.toggleDefine("MOD_AXIS_X", axis.get() == "X"); + mod.toggleDefine("MOD_AXIS_Y", axis.get() == "Y"); + mod.toggleDefine("MOD_AXIS_Z", axis.get() == "Z"); +} + +render.onTriggered = function () +{ + mod.bind(); + trigger.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.ModuloVertexPosition.prototype = new CABLES.Op(); +CABLES.OPS["5b657c67-2d43-43b7-855d-76c8aad90a0d"]={f:Ops.Gl.ShaderEffects.ModuloVertexPosition,objName:"Ops.Gl.ShaderEffects.ModuloVertexPosition"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.PerlinAreaDeform_v4 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.PerlinAreaDeform_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"perlindeform_vert":"vec3 MOD_newTangent,MOD_newBiTangent;\n\n#ifndef PERLINDEFORM\n#define PERLINDEFORM\nfloat Interpolation_C2( float x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } // 6x^5-15x^4+10x^3\t( Quintic Curve. As used by Perlin in Improved Noise. http://mrl.nyu.edu/~perlin/paper445.pdf )\nvec2 Interpolation_C2( vec2 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec3 Interpolation_C2( vec3 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2( vec4 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2_InterpAndDeriv( vec2 x ) { return x.xyxy * x.xyxy * ( x.xyxy * ( x.xyxy * ( x.xyxy * vec2( 6.0, 0.0 ).xxyy + vec2( -15.0, 30.0 ).xxyy ) + vec2( 10.0, -60.0 ).xxyy ) + vec2( 0.0, 30.0 ).xxyy ); }\nvec3 Interpolation_C2_Deriv( vec3 x ) { return x * x * (x * (x * 30.0 - 60.0) + 30.0); }\n\n\nvoid FAST32_hash_3D( \tvec3 gridcell,\n out vec4 lowz_hash_0,\n out vec4 lowz_hash_1,\n out vec4 lowz_hash_2,\n out vec4 highz_hash_0,\n out vec4 highz_hash_1,\n out vec4 highz_hash_2\t)\t\t//\tgenerates 3 random numbers for each of the 8 cell corners\n{\n // gridcell is assumed to be an integer coordinate\n\n //\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const vec3 SOMELARGEFLOATS = vec3( 635.298681, 682.357502, 668.926525 );\n const vec3 ZINC = vec3( 48.500388, 65.294118, 63.934599 );\n\n //\ttruncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n //\tcalculate the noise\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n P = P.xzxz * P.yyww;\n vec3 lowz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) );\n vec3 highz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) );\n lowz_hash_0 = fract( P * lowz_mod.xxxx );\n highz_hash_0 = fract( P * highz_mod.xxxx );\n lowz_hash_1 = fract( P * lowz_mod.yyyy );\n highz_hash_1 = fract( P * highz_mod.yyyy );\n lowz_hash_2 = fract( P * lowz_mod.zzzz );\n highz_hash_2 = fract( P * highz_mod.zzzz );\n}\n\n//\n//\tPerlin Noise 3D ( gradient noise )\n//\tReturn value range of -1.0->1.0\n//\thttp://briansharpe.files.wordpress.com/2011/11/perlinsample.jpg\n//\nfloat Perlin3D( vec3 P )\n{\n //\testablish our grid cell and unit position\n vec3 Pi = floor(P);\n vec3 Pf = P - Pi;\n vec3 Pf_min1 = Pf - 1.0;\n\n#if 1\n //\n //\tclassic noise.\n //\trequires 3 random values per point. with an efficent hash function will run faster than improved noise\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1;\n FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n //SGPP_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n\n //\tcalculate the gradients\n vec4 grad_x0 = hashx0 - 0.49999;\n vec4 grad_y0 = hashy0 - 0.49999;\n vec4 grad_z0 = hashz0 - 0.49999;\n vec4 grad_x1 = hashx1 - 0.49999;\n vec4 grad_y1 = hashy1 - 0.49999;\n vec4 grad_z1 = hashz1 - 0.49999;\n vec4 grad_results_0 = inversesqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 );\n vec4 grad_results_1 = inversesqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 );\n\n#if 1\n //\tClassic Perlin Interpolation\n vec3 blend = Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n float final = dot( res0, blend2.zxzx * blend2.wwyy );\n final *= 1.1547005383792515290182975610039;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.75)\n return final;\n#else\n //\tClassic Perlin Surflet\n //\thttp://briansharpe.wordpress.com/2012/03/09/modifications-to-classic-perlin-noise/\n Pf *= Pf;\n Pf_min1 *= Pf_min1;\n vec4 vecs_len_sq = vec4( Pf.x, Pf_min1.x, Pf.x, Pf_min1.x ) + vec4( Pf.yy, Pf_min1.yy );\n float final = dot( Falloff_Xsq_C2( min( vec4( 1.0 ), vecs_len_sq + Pf.zzzz ) ), grad_results_0 ) + dot( Falloff_Xsq_C2( min( vec4( 1.0 ), vecs_len_sq + Pf_min1.zzzz ) ), grad_results_1 );\n final *= 2.3703703703703703703703703703704;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/cube(0.75)\n return final;\n#endif\n\n#else\n //\n //\timproved noise.\n //\trequires 1 random value per point. Will run faster than classic noise if a slow hashing function is used\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hash_lowz, hash_highz;\n FAST32_hash_3D( Pi, hash_lowz, hash_highz );\n //BBS_hash_3D( Pi, hash_lowz, hash_highz );\n //SGPP_hash_3D( Pi, hash_lowz, hash_highz );\n\n //\n //\t\"improved\" noise using 8 corner gradients. Faster than the 12 mid-edge point method.\n //\tKen mentions using diagonals like this can cause \"clumping\", but we'll live with that.\n //\t[1,1,1] [-1,1,1] [1,-1,1] [-1,-1,1]\n //\t[1,1,-1] [-1,1,-1] [1,-1,-1] [-1,-1,-1]\n //\n hash_lowz -= 0.5;\n vec4 grad_results_0_0 = vec2( Pf.x, Pf_min1.x ).xyxy * sign( hash_lowz );\n hash_lowz = abs( hash_lowz ) - 0.25;\n vec4 grad_results_0_1 = vec2( Pf.y, Pf_min1.y ).xxyy * sign( hash_lowz );\n vec4 grad_results_0_2 = Pf.zzzz * sign( abs( hash_lowz ) - 0.125 );\n vec4 grad_results_0 = grad_results_0_0 + grad_results_0_1 + grad_results_0_2;\n\n hash_highz -= 0.5;\n vec4 grad_results_1_0 = vec2( Pf.x, Pf_min1.x ).xyxy * sign( hash_highz );\n hash_highz = abs( hash_highz ) - 0.25;\n vec4 grad_results_1_1 = vec2( Pf.y, Pf_min1.y ).xxyy * sign( hash_highz );\n vec4 grad_results_1_2 = Pf_min1.zzzz * sign( abs( hash_highz ) - 0.125 );\n vec4 grad_results_1 = grad_results_1_0 + grad_results_1_1 + grad_results_1_2;\n\n //\tblend the gradients and return\n vec3 blend = Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n return dot( res0, blend2.zxzx * blend2.wwyy ) * (2.0 / 3.0);\t//\t(optionally) mult by (2.0/3.0) to scale to a strict -1.0->1.0 range\n#endif\n}\n\n#endif\n\nvec3 MOD_deform(vec3 pos,vec3 norm)\n{\n vec3 modelPos=pos;\n vec3 forcePos=vec3(MOD_x,MOD_y,MOD_z);\n\n vec3 vecToOrigin=modelPos-forcePos;\n float dist=abs(length(vecToOrigin));\n // float distAlpha = (MOD_size - dist) / MOD_size;\n\n if(dist*MOD_mScale +{ + inOpacityThreshold.setUiAttribs({ "greyout": !inDiscardTransparent.get() }); + inAlphaMaskSource.setUiAttribs({ "greyout": !inDiscardTransparent.get() }); +}; + +inAlphaMaskSource.onChange = () => +{ + shadowShaderModule.toggleDefine("MOD_ALPHA_MASK_LUMINANCE", inAlphaMaskSource.get() === "Luminance"); + shadowShaderModule.toggleDefine("MOD_ALPHA_MASK_R", inAlphaMaskSource.get() === "R"); + shadowShaderModule.toggleDefine("MOD_ALPHA_MASK_G", inAlphaMaskSource.get() === "G"); + shadowShaderModule.toggleDefine("MOD_ALPHA_MASK_B", inAlphaMaskSource.get() === "B"); + shadowShaderModule.toggleDefine("MOD_ALPHA_MASK_A", inAlphaMaskSource.get() === "A"); +}; + +inReceiveShadow.onChange = () => +{ + inAlgorithm.setUiAttribs({ "greyout": !inReceiveShadow.get() }); + setAlgorithmGreyouts(); +}; + +inAlgorithm.onChange = () => +{ + const current = inAlgorithm.get(); + algorithms.forEach((alg) => shaderModule.toggleDefine("MODE_" + alg.toUpperCase(), alg === current)); + + setAlgorithmGreyouts(); +}; + +function setAlgorithmGreyouts() +{ + if (!inReceiveShadow.get()) + { + inSamples.setUiAttribs({ "greyout": true }); + inSpread.setUiAttribs({ "greyout": true }); + return; + } + + if (inAlgorithm.get() === "PCF" || inAlgorithm.get() === "Poisson") + { + inSamples.setUiAttribs({ "greyout": false }); + inSpread.setUiAttribs({ "greyout": false }); + } + else + { + inSamples.setUiAttribs({ "greyout": true }); + inSpread.setUiAttribs({ "greyout": true }); + } +} + +inSamples.onChange = () => +{ + shaderModule.define("SAMPLE_AMOUNT", "float(" + clamp(Number(inSamples.get()), 1, 16).toString() + ")"); +}; + +const outTrigger = op.outTrigger("Trigger Out"); + +const createVertexHead = (n, type) => +{ + if (type === "ambient") return ""; + if (type === "point") return attachments.shadow_head_point_vert.replace(LIGHT_INDEX_REGEX, n); + if (type === "spot") return attachments.shadow_head_spot_vert.replace(LIGHT_INDEX_REGEX, n); + if (type === "directional") return attachments.shadow_head_directional_vert.replace(LIGHT_INDEX_REGEX, n); +}; + +const createVertexBody = (n, type) => +{ + if (type === "ambient") return ""; + if (type === "point") return attachments.shadow_body_point_vert.replace(LIGHT_INDEX_REGEX, n); + if (type === "spot") return attachments.shadow_body_spot_vert.replace(LIGHT_INDEX_REGEX, n); + if (type === "directional") return attachments.shadow_body_directional_vert.replace(LIGHT_INDEX_REGEX, n); +}; + +const createFragmentHead = (n, type) => +{ + if (type === "ambient") return ""; + if (type === "point") return attachments.shadow_head_point_frag.replace(LIGHT_INDEX_REGEX, n); + if (type === "spot") return attachments.shadow_head_spot_frag.replace(LIGHT_INDEX_REGEX, n); + if (type === "directional") return attachments.shadow_head_directional_frag.replace(LIGHT_INDEX_REGEX, n); +}; + +const createFragmentBody = (n, type) => +{ + if (type === "ambient") return ""; + let fragmentCode = ""; + if (type === "spot") + { + fragmentCode = fragmentCode.concat(attachments.shadow_body_spot_frag.replace(LIGHT_INDEX_REGEX, n)); + } + else if (type === "directional") + { + fragmentCode = fragmentCode.concat(attachments.shadow_body_directional_frag.replace(LIGHT_INDEX_REGEX, n)); + } + else if (type === "point") + { + fragmentCode = fragmentCode.concat(attachments.shadow_body_point_frag.replace(LIGHT_INDEX_REGEX, n)); + } + + return fragmentCode; +}; + +const STATE = { + "lastLength": 0, + "updating": false +}; + +function renderShadowPassWithModule() +{ + if (inDiscardTransparent.get()) + { + if (inOpacityTexture.get()) + { + if (!shadowShaderModule.hasUniform("MOD_texOpacity")) + { + shadowShaderModule.addUniformFrag("t", "MOD_texOpacity", 0); + } + + if (!shadowShaderModule.hasDefine("MOD_HAS_TEXTURE_OPACITY")) shadowShaderModule.define("MOD_HAS_TEXTURE_OPACITY", ""); + shadowShaderModule.pushTexture("MOD_texOpacity", inOpacityTexture.get().tex); + } + else + { + if (shadowShaderModule.hasUniform("MOD_texOpacity")) + shadowShaderModule.removeUniform("MOD_texOpacity"); + + if (shadowShaderModule.hasDefine("MOD_HAS_TEXTURE_OPACITY")) shadowShaderModule.removeDefine("MOD_HAS_TEXTURE_OPACITY"); + } + + shadowShaderModule.bind(); + outTrigger.trigger(); + shadowShaderModule.unbind(); + } + else + { + outTrigger.trigger(); + } +} + +function createModuleShaders() +{ + STATE.updating = true; + removeUniforms(); + + let vertexHead = ""; + let fragmentHead = ""; + let vertexBody = ""; + let fragmentBody = ""; + + for (let i = 0; i < cgl.frameStore.lightStack.length; i += 1) + { + const light = cgl.frameStore.lightStack[i]; + vertexHead = vertexHead.concat(createVertexHead(i, light.type)); + vertexBody = vertexBody.concat(createVertexBody(i, light.type)); + + fragmentHead = fragmentHead.concat(createFragmentHead(i, light.type)); + fragmentBody = fragmentBody.concat(createFragmentBody(i, light.type, light.castShadow)); + } + + srcHeadVert = srcHeadVertBase.concat(vertexHead); + srcBodyVert = srcBodyVertBase.concat(vertexBody); + srcHeadFrag = srcHeadFragBase.concat(fragmentHead); + srcBodyFrag = srcBodyFragBase.concat(fragmentBody); + + shaderModule.removeModule(op.objName); + + shaderModule.addModule({ + "name": "MODULE_VERTEX_POSITION", + "title": op.objName, + // "priority": -2, + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert + }); + + shaderModule.addModule({ + "name": "MODULE_COLOR", + // "priority": -2, + "title": op.objName, + "srcHeadFrag": srcHeadFrag, + "srcBodyFrag": srcBodyFrag, + }); + + createUniforms(); +} + +// * SHADOW PASS MODULE * +const shadowShaderModule = new CGL.ShaderModifier(cgl, "shadowPassModifier_" + op.id); +shadowShaderModule.addModule({ + "name": "MODULE_COLOR", + // "priority": -2, + "title": op.objName + "shadowPass", + "srcHeadFrag": "", + "srcBodyFrag": ` + #ifdef MOD_HAS_TEXTURE_OPACITY + #ifdef MOD_ALPHA_MASK_LUMINANCE + outColor.a *= dot(vec3(0.2126,0.7152,0.0722), texture(MOD_texOpacity, texCoord).rgb); + #endif + #ifdef MOD_ALPHA_MASK_R + outColor.a *= texture(MOD_texOpacity, texCoord).r; + #endif + #ifdef MOD_ALPHA_MASK_G + outColor.a *= texture(MOD_texOpacity, texCoord).g; + #endif + #ifdef MOD_ALPHA_MASK_B + outColor.a *= texture(MOD_texOpacity, texCoord).b; + #endif + #ifdef MOD_ALPHA_MASK_A + outColor.a *= texture(MOD_texOpacity, texCoord).a; + #endif + if (outColor.a < MOD_inOpacityThreshold) discard; + #endif + ` +}); + +shadowShaderModule.addUniformFrag("f", "MOD_inOpacityThreshold", inOpacityThreshold); + +shadowShaderModule.toggleDefine("MOD_ALPHA_MASK_LUMINANCE", inAlphaMaskSource.get() === "Luminance"); +shadowShaderModule.toggleDefine("MOD_ALPHA_MASK_R", inAlphaMaskSource.get() === "R"); +shadowShaderModule.toggleDefine("MOD_ALPHA_MASK_G", inAlphaMaskSource.get() === "G"); +shadowShaderModule.toggleDefine("MOD_ALPHA_MASK_B", inAlphaMaskSource.get() === "B"); +shadowShaderModule.toggleDefine("MOD_ALPHA_MASK_A", inAlphaMaskSource.get() === "A"); + +const srcHeadVertBase = attachments.head_vert; +const srcBodyVertBase = ""; +const srcHeadFragBase = attachments.head_frag; +const srcBodyFragBase = ""; + +let srcHeadVert = srcHeadVertBase; +let srcBodyVert = srcBodyVertBase; +let srcHeadFrag = srcHeadFragBase; +let srcBodyFrag = srcBodyFragBase; + +// * MAIN PASS MODULE * +const shaderModule = new CGL.ShaderModifier(cgl, "shadowModule_" + op.id); +shaderModule.define("SAMPLE_AMOUNT", "float(" + clamp(Number(inSamples.get()), 1, 16).toString() + ")"); +shaderModule.toggleDefine("RECEIVE_SHADOW", inReceiveShadow); + +algorithms.forEach((alg) => shaderModule.toggleDefine("MODE_" + alg.toUpperCase(), alg === inAlgorithm.get())); + +const hasShadowMap = []; +const hasShadowCubemap = []; + +function removeUniforms() +{ + for (let i = 0; i < STATE.lastLength; i += 1) + { + shaderModule.removeUniformStruct("MOD_light" + i); + shaderModule.removeUniform("MOD_shadowMap" + i); + shaderModule.removeUniform("MOD_shadowMapCube" + i); + shaderModule.removeUniform("MOD_normalOffset" + i); + shaderModule.removeUniform("MOD_lightMatrix" + i); + shaderModule.removeDefine("HAS_SHADOW_MAP_" + i); + } + + if (STATE.lastLength > 0) + { + shaderModule.removeUniform("MOD_sampleSpread"); + shaderModule.removeUniform("MOD_camPos"); + } + hasShadowMap.length = 0; + hasShadowCubemap.length = 0; +} + +function createUniforms() +{ + for (let i = 0; i < cgl.frameStore.lightStack.length; i += 1) + { + const light = cgl.frameStore.lightStack[i]; + + shaderModule.addUniformStructFrag("MOD_Light", "MOD_light" + i, [ + { "type": "3f", "name": "position", "v1": null }, + { "type": "2i", "name": "typeCastShadow", "v1": null }, + { "type": "4f", "name": "shadowProperties", "v1": [light.nearFar[0], light.nearFar[1], 512, light.shadowBias] }, + { "type": "f", "name": "shadowStrength", "v1": light.shadowStrength }, + ]); + + hasShadowMap[i] = false; + hasShadowCubemap[i] = false; + + if (light.type !== "point") + { + shaderModule.addUniformVert("m4", "MOD_lightMatrix" + i, mat4.create(), null, null, null); + shaderModule.addUniformVert("f", "MOD_normalOffset" + i, 0, null, null, null, null); + shaderModule.addUniformFrag("t", "MOD_shadowMap" + i, 0, null, null, null); + } + else shaderModule.addUniformFrag("tc", "MOD_shadowMapCube" + i, 0, null, null, null); + } + + if (cgl.frameStore.lightStack.length > 0) + { + shaderModule.addUniformFrag("3f", "MOD_shadowColor", inShadowColorR, inShadowColorG, inShadowColorB, null); + shaderModule.addUniformFrag("f", "MOD_sampleSpread", inSpread, null, null, null); + if (cgl.frameStore.lightStack.map((l) => l.type).indexOf("point") !== -1) shaderModule.addUniformFrag("3f", "MOD_camPos", [0, 0, 0], null, null, null); + } + + STATE.lastLength = cgl.frameStore.lightStack.length; + STATE.updating = false; +} + +function setUniforms() +{ + if (STATE.updating) return; + const receiveShadow = inReceiveShadow.get(); + + for (let i = 0; i < cgl.frameStore.lightStack.length; i += 1) + { + const light = cgl.frameStore.lightStack[i]; + + if (light.type === "ambient") continue; + + if (!light.isUsed) light.isUsed = true; + + if (light.type !== "point") shaderModule.setUniformValue("MOD_light" + i + ".position", light.position); + else + { + shaderModule.setUniformValue("MOD_light" + i + ".position", light.positionForShadowMap); + } + shaderModule.setUniformValue("MOD_light" + i + ".typeCastShadow", [ + LIGHT_TYPES[light.type], + Number(light.castShadow), + ]); + + shaderModule.setUniformValue("MOD_light" + i + ".shadowStrength", light.shadowStrength); + + if (light.shadowMap) + { + if (!hasShadowMap[i]) + { + hasShadowMap[i] = true; + hasShadowCubemap[i] = false; + } + if (!shaderModule.hasDefine("HAS_SHADOW_MAP_" + i)) + { + shaderModule.define("HAS_SHADOW_MAP_" + i, true); + } + + if (light.type !== "point") + { + shaderModule.setUniformValue("MOD_lightMatrix" + i, light.lightMatrix); + shaderModule.setUniformValue("MOD_normalOffset" + i, light.normalOffset); + } + + shaderModule.setUniformValue("MOD_light" + i + ".shadowProperties", [ + light.nearFar[0], + light.nearFar[1], + light.shadowMap.width, + light.shadowBias + ]); + + if (light.type === "point") shaderModule.setUniformValue("MOD_camPos", [_tempCamPosMatrix[12], _tempCamPosMatrix[13], _tempCamPosMatrix[14]]); + + if (hasShadowMap[i]) + { + if (light.shadowMap.tex) shaderModule.pushTexture("MOD_shadowMap" + i, light.shadowMap.tex); + } + continue; + } + + if (light.shadowCubeMap) + { + if (!hasShadowCubemap[i]) + { + hasShadowCubemap[i] = true; + hasShadowMap[i] = false; + } + + if (!shaderModule.hasDefine("HAS_SHADOW_MAP_" + i)) shaderModule.define("HAS_SHADOW_MAP_" + i, ""); + + shaderModule.setUniformValue("MOD_light" + i + ".shadowProperties", [ + light.nearFar[0], + light.nearFar[1], + light.shadowCubeMap.size, + light.shadowBias + ]); + + if (hasShadowCubemap[i]) + { + if (light.shadowCubeMap.cubemap) shaderModule.pushTexture("MOD_shadowMapCube" + i, light.shadowCubeMap.cubemap, cgl.gl.TEXTURE_CUBE_MAP); + } + continue; + } + + else + { + if (hasShadowMap[i]) + { + if (shaderModule.hasDefine("HAS_SHADOW_MAP_" + i)) + { + shaderModule.removeDefine("HAS_SHADOW_MAP_" + i); + } + hasShadowMap[i] = false; + } + else if (hasShadowCubemap[i]) + { + if (shaderModule.hasDefine("HAS_SHADOW_MAP_" + i)) shaderModule.removeDefine("HAS_SHADOW_MAP_" + i); + hasShadowCubemap[i] = false; + } + continue; + } + } +} + +function updateShader() +{ + if (cgl.frameStore.lightStack.length !== STATE.lastLength) + createModuleShaders(); + + setUniforms(); +} + +inTrigger.onLinkChanged = function () +{ + if (!inTrigger.isLinked()) STATE.lastLength = 0; + hasShadowMap.length = 0; + hasShadowCubemap.length = 0; +}; +outTrigger.onLinkChanged = function () +{ + if (!outTrigger.isLinked()) STATE.lastLength = 0; + hasShadowMap.length = 0; + hasShadowCubemap.length = 0; +}; + +const _tempCamPosMatrix = mat4.create(); + +inTrigger.onTriggered = () => +{ + if (STATE.updating) + { + outTrigger.trigger(); + return; + } + + if (cgl.frameStore.shadowPass) + { + if (!inCastShadow.get()) return; + renderShadowPassWithModule(); + return; + } + + if (!inReceiveShadow.get()) + { + outTrigger.trigger(); + return; + } + + checkUiErrors(); + + mat4.invert(_tempCamPosMatrix, cgl.vMatrix); + + if (cgl.frameStore.lightStack) + { + if (cgl.frameStore.lightStack.length) + { + updateShader(); + + shaderModule.bind(); + outTrigger.trigger(); + shaderModule.unbind(); + } + else + { + outTrigger.trigger(); + STATE.lastLength = 0; + hasShadowMap.length = 0; + hasShadowCubemap.length = 0; + } + } + else + { + outTrigger.trigger(); + } +}; + +function checkUiErrors() +{ + if (cgl.frameStore.lightStack) + { + if (cgl.frameStore.lightStack.length === 0) + { + op.setUiError("nolights", "There are no lights in the patch. Please add lights before this op and activate their \"Cast Shadow\" property to be able to use shadows.", 1); + } + else + { + op.setUiError("nolights", null); + + let oneLightCastsShadow = false; + let allLightsBlurAboveZero = true; + + for (let i = 0; i < cgl.frameStore.lightStack.length; i += 1) + { + oneLightCastsShadow = oneLightCastsShadow || cgl.frameStore.lightStack[i].castShadow; + + if (cgl.frameStore.lightStack[i].castShadow && cgl.frameStore.lightStack[i].type !== "point") + allLightsBlurAboveZero = allLightsBlurAboveZero && (cgl.frameStore.lightStack[i].blurAmount > 0); + } + + if (oneLightCastsShadow) + { + op.setUiError("nolights2", null); + if (inReceiveShadow.get()) + { + op.setUiError("inReceiveShadowActive", null); + } + else + { + op.setUiError("inReceiveShadowActive", "Your lights cast shadows but the \"Receive Shadow\" option in this op is not active. Please enable it to use shadows.", 1); + } + } + else + { + op.setUiError("nolights2", "There are lights in the patch but none that cast shadows. Please activate the \"Cast Shadow\" property of one of your lights in the patch to make shadows visible.", 1); + op.setUiError("inReceiveShadowActive", null); + } + + if (!allLightsBlurAboveZero) + { + if (inAlgorithm.get() === "VSM") + { + op.setUiError("vsmBlurZero", "You chose the VSM algorithm but one of your lights still has a blur amount of 0. For VSM to work correctly, consider raising the blur amount in your lights.", 1); + } + else + { + op.setUiError("vsmBlurZero", null); + } + } + else + { + op.setUiError("vsmBlurZero", null); + } + } + } + else + { + op.setUiError("nolights", "There are no lights in the patch. Please add lights before this op and activate their \"Cast Shadow\" property to be able to use shadows.", 1); + } +} + + +}; + +Ops.Gl.ShaderEffects.Shadow_v2.prototype = new CABLES.Op(); +CABLES.OPS["f5214bd2-575d-4c0c-a7a9-4eff76915ac1"]={f:Ops.Gl.ShaderEffects.Shadow_v2,objName:"Ops.Gl.ShaderEffects.Shadow_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.SplineDeform_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.SplineDeform_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"splinedeform_vert":"\n\n\npos.xyz=MOD_splineDeform(pos,0);\n\nnorm=normalize(norm*MOD_splineDeform(pos,1));\n\n","splinedeform_head_vert":"\nUNI vec3 MOD_points[SPLINE_POINTS];\n\nvec3 ip(float v)\n{\n int index0=int(abs(mod(v,max(0.0,float(SPLINE_POINTS)))));\n int index1=int(abs(mod(v+1.0,max(0.0,float(SPLINE_POINTS)))));\n float fr=fract(abs(mod(v,max(0.0,float(SPLINE_POINTS)))));\n return mix( MOD_points[index0] ,MOD_points[index1] ,fr);\n}\n\nvec3 MOD_splineDeform(vec4 pos, int isNormal)\n{\n float off=MOD_offset+( (pos.x)*MOD_size);\n\n vec3 bezierPointPrevious=ip(off-1.0);\n vec3 bezierPoint=ip(off);\n vec3 bezierPointNext=ip(off+1.0);\n\n vec3 _Up=vec3(0.0,1.0,0.0);\n vec3 _Forward=vec3(1.0,0.0,0.0);\n vec3 _Right=vec3(0.0,0.0,1.0);\n\n\tfloat vertexForward = pos.x * _Forward.x + pos.y * _Forward.y + pos.z * _Forward.z;\n\tfloat vertexRight = pos.x * _Right.x + pos.y * _Right.y + pos.z * _Right.z;\n\tfloat vertexUp = pos.x * _Up.x + pos.y * _Up.y + pos.z * _Up.z;\n\n\tfloat angle = atan(vertexUp,vertexRight);\n\tfloat radius = length(vec2(vertexRight, vertexUp));\n\n\tvec3 forward = normalize(bezierPointNext - bezierPoint);\n\tvec3 backward = normalize(bezierPointPrevious - bezierPoint);\n\tvec3 up = normalize(cross(forward, backward));\n\tvec3 right = normalize(cross(forward, up));\n\n if(isNormal==0)\n {\n pos.xyz = bezierPoint + right * cos(angle) * radius + up * sin(angle) * radius;\n }\n else\n {\n pos.xyz = pos.xyz-( (bezierPoint) + right * cos(angle) * radius + up * sin(angle) * radius);\n }\n\n return pos.xyz;\n}",}; +const + render = op.inTrigger("Render"), + next = op.outTrigger("Next"), + inSize = op.inValue("Size", 1), + inOffset = op.inValue("offset"), + inPoints = op.inArray("Points"); + +const cgl = op.patch.cgl; +const srcHeadVert = attachments.splinedeform_head_vert || ""; +const srcBodyVert = attachments.splinedeform_vert || ""; + +let needsUpdate = true; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + srcHeadVert, + srcBodyVert +}); + +mod.addUniform("f", "MOD_size", inSize); +mod.addUniform("f", "MOD_offset", inOffset); +mod.addUniform("3f[]", "MOD_points", new Float32Array([0, 0, 0, 0, 0, 0])); +mod.define("SPLINE_POINTS", 1); + +inPoints.onChange = () => +{ + needsUpdate = true; +}; + +render.onTriggered = function () +{ + mod.bind(); + let pointArray = inPoints.get(); + + if (needsUpdate) + { + if (inPoints.get()) + { + if (pointArray && pointArray.length > 0) + { + mod.define("SPLINE_POINTS", Math.floor(pointArray.length / 3)); + needsUpdate = false; + } + } + } + + if (pointArray && pointArray.length > 0) + mod.setUniformValue("MOD_points", pointArray); + + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.SplineDeform_v2.prototype = new CABLES.Op(); +CABLES.OPS["d0cac165-2146-4a3c-863c-5042149c0e95"]={f:Ops.Gl.ShaderEffects.SplineDeform_v2,objName:"Ops.Gl.ShaderEffects.SplineDeform_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.TextureProjection_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.TextureProjection_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"maptexture_frag":"IN vec2 MOD_tc;\n\n#ifdef MOD_MAP_TRIPLANAR\n IN vec2 MOD_tc1;\n IN vec2 MOD_tc2;\n IN vec3 MOD_blendingTri;\n#endif\n\n\n{{CGL.BLENDMODES3}}","maptexture_vert":"vec3 MOD_pos;\n\n#ifndef MOD_WORLDSPACE\n MOD_pos=(vec4(vPosition,1.0)*1.0/MOD_scale).xyz;\n#endif\n#ifdef MOD_WORLDSPACE\n MOD_pos=(mMatrix*pos).xyz*1.0/MOD_scale;\n#endif\n\nMOD_pos=(vec4(MOD_pos,1.0)*MOD_rotationX(MOD_rotX*MOD_DEG2RAD)).xyz;\nMOD_pos=(vec4(MOD_pos,1.0)*MOD_rotationY(MOD_rotY*MOD_DEG2RAD)).xyz;\nMOD_pos=(vec4(MOD_pos,1.0)*MOD_rotationZ(MOD_rotZ*MOD_DEG2RAD)).xyz;\n\n#ifdef MOD_MAP_XY\n MOD_tc=MOD_pos.xy;\n#endif\n#ifdef MOD_MAP_XZ\n MOD_tc=MOD_pos.xz;\n#endif\n#ifdef MOD_MAP_YZ\n MOD_tc=MOD_pos.yz;\n#endif\n\nMOD_tc.xy+=vec2(0.5,0.5);\nMOD_tc.xy+=MOD_offset;\n\n\n#ifdef MOD_TARGET_POINTSIZE\n\n gl_PointSize+=texture(MOD_tex,MOD_tc).x*MOD_amount;\n\n#endif\n\n\n#ifdef MOD_MAP_TRIPLANAR\n mapTriplanar((mMatrix*vec4(attrVertNormal,1.0)).xyz,MOD_pos);\n#endif\n\n","maptexture_body_frag":"#ifndef MOD_TARGET_POINTSIZE\n\n\n vec4 MOD_color;\n\n #ifdef MOD_MAP_TRIPLANAR\n vec4 xaxis = texture( MOD_tex, MOD_tc);\n vec4 yaxis = texture( MOD_tex, MOD_tc1);\n vec4 zaxis = texture( MOD_tex, MOD_tc2);\n MOD_color = xaxis *MOD_blendingTri.x + yaxis *MOD_blendingTri.y + zaxis *MOD_blendingTri.z;\n MOD_color.a=1.0;\n #endif\n\n\n vec2 MOD_ntc=MOD_tc;\n\n #ifdef MOD_MAP_SCREEN\n MOD_ntc=(vec2(gl_FragCoord.x,gl_FragCoord.y)/vec2(MOD_viewPortW,MOD_viewPortH));\n\n MOD_ntc-=vec2(0.5,0.5);\n MOD_ntc*=1.0/MOD_scale;\n MOD_ntc+=vec2(0.5,0.5);\n MOD_ntc-=MOD_offset;\n #endif\n\n #ifdef MOD_MAP_TEXCOORD\n MOD_ntc=texCoord;\n #endif\n\n #ifdef MOD_MAP_TEXCOORD1\n MOD_ntc=texCoord1;\n #endif\n\n #ifdef MOD_MAP_TEXCOORD2\n MOD_ntc=texCoord2;\n #endif\n\n\n #ifdef MOD_DISCARD\n if(MOD_ntc.x>0.0 && MOD_ntc.x<1.0 && MOD_ntc.y>0.0 && MOD_ntc.y<1.0)\n {\n #endif\n\n #ifndef MOD_MAP_TRIPLANAR\n MOD_color=texture(MOD_tex,MOD_ntc);\n #endif\n\n #ifdef MOD_USE_IMGALPHA\n col.a=MOD_color.a;\n #endif\n\n #ifdef MOD_TARGET_COLOR\n col=cgl_blendPixel(col,MOD_color,MOD_amount*col.a);\n #endif\n #ifdef MOD_TARGET_ALPHA\n col.a=1.0-MOD_color.r*MOD_amount;\n #endif\n\n #ifdef MOD_DISCARD\n }\n\n #endif\n#endif\n","maptexture_body_vert":"OUT vec2 MOD_tc;\n\nconst float MOD_DEG2RAD = 0.017453292519943;\n\n#ifdef MOD_MAP_TRIPLANAR\n\n OUT vec2 MOD_tc1;\n OUT vec2 MOD_tc2;\n OUT vec3 MOD_blendingTri;\n\n void mapTriplanar(vec3 wNorm,vec3 pos)\n {\n vec3 blending = abs( wNorm );\n blending = normalize(max(blending, 0.1));\n float b = (blending.x + blending.y + blending.z);\n blending /= vec3(b);\n MOD_blendingTri=blending;\n\n MOD_tc = pos.yz;\n MOD_tc1 = pos.xz;\n MOD_tc2 = pos.xy;\n }\n\n#endif\n\nmat4 MOD_rotationX( in float angle ) {\n\treturn mat4(\t1.0,\t\t0,\t\t\t0,\t\t\t0,\n\t\t\t \t\t0, \tcos(angle),\t-sin(angle),\t\t0,\n\t\t\t\t\t0, \tsin(angle),\t cos(angle),\t\t0,\n\t\t\t\t\t0, \t\t\t0,\t\t\t 0, \t\t1);\n}\n\nmat4 MOD_rotationY( in float angle ) {\n\treturn mat4(\tcos(angle),\t\t0,\t\tsin(angle),\t0,\n\t\t\t \t\t\t\t0,\t\t1.0,\t\t\t 0,\t0,\n\t\t\t\t\t-sin(angle),\t0,\t\tcos(angle),\t0,\n\t\t\t\t\t\t\t0, \t\t0,\t\t\t\t0,\t1);\n}\n\nmat4 MOD_rotationZ( in float angle ) {\n\treturn mat4(\tcos(angle),\t\t-sin(angle),\t0,\t0,\n\t\t\t \t\tsin(angle),\t\tcos(angle),\t\t0,\t0,\n\t\t\t\t\t\t\t0,\t\t\t\t0,\t\t1,\t0,\n\t\t\t\t\t\t\t0,\t\t\t\t0,\t\t0,\t1);\n}\n",}; +const + render = op.inTrigger("render"), + next = op.outTrigger("trigger"), + inTex = op.inTexture("Texture"), + + inScale = op.inValue("Scale", 10), + + inTarget = op.inSwitch("Target", ["Color", "Pointsize", "Alpha"], "Color"), + + inBlend = CGL.TextureEffect.AddBlendSelect(op, "blendMode"), + inAmount = op.inValueSlider("Amount", 0.3), + + inUseTexAlpha = op.inBool("Use Texture Alpha", false), + + inPosX = op.inFloat("Pos X", 0), + inPosY = op.inFloat("Pos Y", 0), + + inRotX = op.inFloat("Rot X", 0), + inRotY = op.inFloat("Rot Y", 0), + inRotZ = op.inFloat("Rot Z", 0), + + inMethod = op.inValueSelect("Mapping", ["Triplanar", "XY", "XZ", "YZ", "Screen", "TexCoords 1", "TexCoords 2", "TexCoords 3"], "XY"), + inDiscard = op.inValueBool("Discard"), + inWorldSpace = op.inValueBool("WorldSpace"); + +const cgl = op.patch.cgl; + +inUseTexAlpha.onChange = +inTarget.onChange = +inBlend.onChange = inDiscard.onChange = inWorldSpace.onChange = inMethod.onChange = updateDefines; + +op.setPortGroup("Rotation", [inRotX, inRotY, inRotZ]); +op.setPortGroup("Position", [inPosX, inPosY]); + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.maptexture_body_vert, + "srcBodyVert": attachments.maptexture_vert, + "attributes": [ + { "type": "vec2", "name": "attrTexCoord1", "nameFrag": "texCoord1" }, + { "type": "vec2", "name": "attrTexCoord2", "nameFrag": "texCoord2" }] +}); + +let head_frag = attachments.maptexture_frag; +head_frag = head_frag.replace("{{BLENDCODE}}", CGL.TextureEffect.getBlendCode(3)); + +mod.addModule({ + "title": op.name, + "name": "MODULE_COLOR", + "srcHeadFrag": head_frag, + "srcBodyFrag": attachments.maptexture_body_frag +}); + +mod.addUniformBoth("f", "MOD_rotX", inRotX); +mod.addUniformBoth("f", "MOD_rotY", inRotY); +mod.addUniformBoth("f", "MOD_rotZ", inRotZ); + +mod.addUniformBoth("t", "MOD_tex"); +mod.addUniformBoth("f", "MOD_scale", inScale); +mod.addUniformBoth("f", "MOD_amount", inAmount); +mod.addUniformBoth("2f", "MOD_offset", inPosX, inPosY); + +const uniWidth = mod.addUniformFrag("f", "MOD_viewPortW"); +const uniHeight = mod.addUniformFrag("f", "MOD_viewPortH"); + +CGL.TextureEffect.setupBlending(op, mod, inBlend, inAmount); + +updateDefines(); + +function updateDefines() +{ + mod.toggleDefine("MOD_USE_IMGALPHA", inUseTexAlpha.get()); + mod.toggleDefine("MOD_WORLDSPACE", inWorldSpace.get()); + mod.toggleDefine("MOD_MAP_XY", inMethod.get() == "XY"); + mod.toggleDefine("MOD_MAP_XZ", inMethod.get() == "XZ"); + mod.toggleDefine("MOD_MAP_YZ", inMethod.get() == "YZ"); + mod.toggleDefine("MOD_MAP_TEXCOORD", inMethod.get() == "TexCoords 1"); + mod.toggleDefine("MOD_MAP_TEXCOORD1", inMethod.get() == "TexCoords 2"); + mod.toggleDefine("MOD_MAP_TEXCOORD2", inMethod.get() == "TexCoords 3"); + mod.toggleDefine("MOD_MAP_SCREEN", inMethod.get() == "Screen"); + mod.toggleDefine("MOD_MAP_TRIPLANAR", inMethod.get() == "Triplanar"); + mod.toggleDefine("MOD_DISCARD", inDiscard.get()); + + mod.toggleDefine("MOD_BLEND_NORMAL", inBlend.get() == "Normal"); + mod.toggleDefine("MOD_BLEND_ADD", inBlend.get() == "Add"); + mod.toggleDefine("MOD_BLEND_MUL", inBlend.get() == "Mul"); + mod.toggleDefine("MOD_BLEND_MUL", inBlend.get() == "Mul"); + + mod.toggleDefine("MOD_TARGET_ALPHA", inTarget.get() == "Alpha"); + mod.toggleDefine("MOD_TARGET_COLOR", inTarget.get() == "Color"); + mod.toggleDefine("MOD_TARGET_POINTSIZE", inTarget.get() == "Pointsize"); +} + +render.onTriggered = function () +{ + const vp = cgl.getViewPort(); + + mod.setUniformValue("MOD_viewPortW", vp[2]); + mod.setUniformValue("MOD_viewPortH", vp[3]); + + mod.bind(); + let tex = inTex.get(); + if (!tex) tex = CGL.Texture.getEmptyTexture(cgl).tex; + else tex = tex.tex; + + mod.pushTexture("MOD_tex", tex); + + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.TextureProjection_v2.prototype = new CABLES.Op(); +CABLES.OPS["9be647c2-7afd-40ed-b669-9826ea6a50ca"]={f:Ops.Gl.ShaderEffects.TextureProjection_v2,objName:"Ops.Gl.ShaderEffects.TextureProjection_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.TransformTextureCoordinates +// +// ************************************************************** + +Ops.Gl.ShaderEffects.TransformTextureCoordinates = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"trans_vert":"\n#ifdef MOD_FLIPX\n texCoord.x=1.0-texCoord.x;\n#endif\n#ifdef MOD_FLIPY\n texCoord.y=1.0-texCoord.y;\n#endif\n\ntexCoord*=MOD_scale;\ntexCoord+=MOD_trans;",}; +const + render = op.inTrigger("render"), + next = op.outTrigger("Trigger"), + + transX = op.inValue("Translate X", 0), + transY = op.inValue("Translate Y", 0), + + scaleX = op.inValue("Repeat X", 1), + scaleY = op.inValue("Repeat Y", 1), + + inFlipX=op.inBool("Flip X",false), + inFlipY=op.inBool("Flip Y",false) + ; + +const cgl = op.patch.cgl; +const mod = new CGL.ShaderModifier(cgl, op.name); + +mod.addModule({ + "priority": -2, + "name": "MODULE_VERTEX_POSITION", + "srcBodyVert": attachments.trans_vert || "" +}); + +mod.addUniformVert("2f", "MOD_trans", transX, transY); +mod.addUniformVert("2f", "MOD_scale", scaleX, scaleY); + + +inFlipX.onChange= + inFlipY.onChange=updateDefines; + +updateDefines(); + +function updateDefines() +{ + mod.toggleDefine("MOD_FLIPX",inFlipX.get()); + mod.toggleDefine("MOD_FLIPY",inFlipY.get()); +} + +render.onTriggered = function () +{ + mod.bind(); + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.TransformTextureCoordinates.prototype = new CABLES.Op(); +CABLES.OPS["54eeb750-1491-4fdf-bfd5-290e1b29bafd"]={f:Ops.Gl.ShaderEffects.TransformTextureCoordinates,objName:"Ops.Gl.ShaderEffects.TransformTextureCoordinates"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.TransformVertex +// +// ************************************************************** + +Ops.Gl.ShaderEffects.TransformVertex = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"trans_vert":"\n\n\npos.xyz*=vec3(MOD_scale);\npos.xyz+=vec3(MOD_translate);\n\nmat4 MOD_rmat=\n MOD_rotationX(MOD_rot.x*0.0174533)*\n MOD_rotationY(MOD_rot.y*0.0174533)*\n MOD_rotationZ(MOD_rot.z*0.0174533);\n\npos*=MOD_rmat;\n\n#ifdef MOD_TRANS_NORMS\n norm=(vec4(norm,1.0)*MOD_rmat).xyz;\n bitangent=(vec4(bitangent,1.0)*MOD_rmat).xyz;\n tangent=(vec4(tangent,1.0)*MOD_rmat).xyz;\n#endif","trans_head_vert":"\nmat4 MOD_rotationX( in float angle ) {\n\treturn mat4(\t1.0,\t\t0,\t\t\t0,\t\t\t0,\n\t\t\t \t\t0, \tcos(angle),\t-sin(angle),\t\t0,\n\t\t\t\t\t0, \tsin(angle),\t cos(angle),\t\t0,\n\t\t\t\t\t0, \t\t\t0,\t\t\t 0, \t\t1);\n}\n\nmat4 MOD_rotationY( in float angle ) {\n\treturn mat4(\tcos(angle),\t\t0,\t\tsin(angle),\t0,\n\t\t\t \t\t\t\t0,\t\t1.0,\t\t\t 0,\t0,\n\t\t\t\t\t-sin(angle),\t0,\t\tcos(angle),\t0,\n\t\t\t\t\t\t\t0, \t\t0,\t\t\t\t0,\t1);\n}\n\nmat4 MOD_rotationZ( in float angle ) {\n\treturn mat4(\tcos(angle),\t\t-sin(angle),\t0,\t0,\n\t\t\t \t\tsin(angle),\t\tcos(angle),\t\t0,\t0,\n\t\t\t\t\t\t\t0,\t\t\t\t0,\t\t1,\t0,\n\t\t\t\t\t\t\t0,\t\t\t\t0,\t\t0,\t1);\n}\n",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("Trigger"), + transX = op.inValue("Translate X", 0), + transY = op.inValue("Translate Y", 0), + transZ = op.inValue("Translate Z", 0), + + scaleX = op.inValue("Scale X", 1), + scaleY = op.inValue("Scale Y", 1), + scaleZ = op.inValue("Scale Z", 1), + + rotX = op.inValue("Rotation X", 0), + rotY = op.inValue("Rotation Y", 0), + rotZ = op.inValue("Rotation Z", 0), + transNorm = op.inBool("Transform normals", false); + +const cgl = op.patch.cgl; +const mod = new CGL.ShaderModifier(cgl, op.name); + +mod.addModule({ + "priority": -2, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.trans_head_vert || "", + "srcBodyVert": attachments.trans_vert || "" +}); + +mod.addUniformVert("3f", "MOD_translate", transX, transY, transZ); +mod.addUniformVert("3f", "MOD_scale", scaleX, scaleY, scaleZ); +mod.addUniformVert("3f", "MOD_rot", rotX, rotY, rotZ); + +transNorm.onChange = updateDefines; + +updateDefines(); + +function updateDefines() +{ + mod.toggleDefine("MOD_TRANS_NORMS", transNorm.get()); +} + +render.onTriggered = function () +{ + mod.bind(); + trigger.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.TransformVertex.prototype = new CABLES.Op(); +CABLES.OPS["68ca4a91-c3ce-4e81-bb84-3ba5f50dfaa1"]={f:Ops.Gl.ShaderEffects.TransformVertex,objName:"Ops.Gl.ShaderEffects.TransformVertex"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.Twist_v3 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.Twist_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"twist_vert":"\nfloat MOD_angle_rad = MOD_amount * 3.14159 / 180.0;\n\nfloat MOD_axis=pos.y;\n\n#ifdef MOD_AXIS_Z\n MOD_axis=pos.z;\n#endif\n\n#ifdef MOD_AXIS_X\n MOD_axis=pos.x;\n#endif\n\nfloat MOD_ang = (MOD_height*0.5 + MOD_axis)/MOD_height * MOD_angle_rad;\n\npos = MOD_twist(pos, MOD_ang);\n\n\nnorm = normalize(MOD_twist( vec4(norm, 1.0), MOD_ang ).xyz);\n","twist_head_vert":"vec4 MOD_twist(vec4 pos, float t)\n{\n\tfloat st = sin(t);\n\tfloat ct = cos(t);\n\tvec4 new_pos;\n\n\tnew_pos.x = pos.x;\n\tnew_pos.y = pos.y;\n\tnew_pos.z = pos.z;\n\tnew_pos.w = pos.w;\n\n #ifdef MOD_AXIS_Z\n \tnew_pos.x = pos.y*ct - pos.x*st;\n \tnew_pos.y = pos.y*st + pos.x*ct;\n #endif\n\n #ifdef MOD_AXIS_Y\n \tnew_pos.x = pos.x*ct - pos.z*st;\n \tnew_pos.z = pos.x*st + pos.z*ct;\n #endif\n\n #ifdef MOD_AXIS_X\n \tnew_pos.y = pos.y*ct - pos.z*st;\n \tnew_pos.z = pos.y*st + pos.z*ct;\n #endif\n\n\treturn( new_pos );\n}\n",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("Trigger"), + amount = op.inFloat("Degree", 180), + height = op.inFloat("Height", 2), + axis = op.inValueSelect("Axis", ["X", "Y", "Z"], "Y"); + +const cgl = op.patch.cgl; + +axis.onChange = updateAxis; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.twist_head_vert, + "srcBodyVert": attachments.twist_vert +}); + +updateAxis(); + +mod.addUniformVert("f", "MOD_amount", amount); +mod.addUniformVert("f", "MOD_height", height); + +function updateAxis() +{ + mod.toggleDefine("MOD_AXIS_X", axis.get() == "X"); + mod.toggleDefine("MOD_AXIS_Y", axis.get() == "Y"); + mod.toggleDefine("MOD_AXIS_Z", axis.get() == "Z"); +} + +render.onTriggered = function () +{ + if (cgl.shouldDrawHelpers(op)) + { + CABLES.GL_MARKER.drawCube(op, 1, height.get() / 2, 1); + } + + mod.bind(); + trigger.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.Twist_v3.prototype = new CABLES.Op(); +CABLES.OPS["4635abe3-a6b1-413f-9cd1-fbf64f8c4942"]={f:Ops.Gl.ShaderEffects.Twist_v3,objName:"Ops.Gl.ShaderEffects.Twist_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.UseVertexColor +// +// ************************************************************** + +Ops.Gl.ShaderEffects.UseVertexColor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colorarea_frag":"baseColor.rgb=vertColor.rgb;","colorarea_head_frag":"IN vec4 vertColor;\n",}; +const + render = op.inTrigger("Render"), + next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; + +const srcHeadVert = "" + .endl() + "IN vec4 attrVertColor;" + .endl() + "OUT vec4 vertColor;" + .endl(); + +const srcBodyVert = "" + .endl() + " vertColor=attrVertColor;" + .endl(); + + +render.onTriggered = doRender; + +const vertModTitle = "vert_" + op.name; +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": vertModTitle, + "name": "MODULE_VERTEX_POSITION", + srcHeadVert, + srcBodyVert +}); + +mod.addModule({ + "title": op.name, + "name": "MODULE_BASE_COLOR", + "srcHeadFrag": attachments.colorarea_head_frag, + "srcBodyFrag": attachments.colorarea_frag +}); + + +function doRender() +{ + mod.bind(); + next.trigger(); + + mod.unbind(); +} + + +}; + +Ops.Gl.ShaderEffects.UseVertexColor.prototype = new CABLES.Op(); +CABLES.OPS["8c3cc332-3bab-4cb7-ad0a-368814eb8282"]={f:Ops.Gl.ShaderEffects.UseVertexColor,objName:"Ops.Gl.ShaderEffects.UseVertexColor"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.VertexColorAsAlpha +// +// ************************************************************** + +Ops.Gl.ShaderEffects.VertexColorAsAlpha = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colorarea_frag":"\nfloat MOD_vca=1.0;\n\n#ifdef MOD_INPUT_LUMI\n float MOD_lumi = dot(vec3(0.2126,0.7152,0.0722), col.rgb);\n\n MOD_vca=MOD_lumi;\n#endif\n#ifdef MOD_INPUT_R\n MOD_vca=vertColor.r;\n#endif\n#ifdef MOD_INPUT_G\n MOD_vca=vertColor.g;\n#endif\n#ifdef MOD_INPUT_B\n MOD_vca=vertColor.b;\n#endif\n\n#ifdef MOD_INVERT\n col.a=1.0-MOD_vca;\n#endif\n#ifndef MOD_INVERT\n col.a=MOD_vca;\n#endif","colorarea_head_frag":"IN vec4 vertColor;\n",}; +const + render = op.inTrigger("Render"), + inInput=op.inSwitch("Input",['Luminance','R','G','B'],'default'), + inInvert=op.inBool("Invert",false), + next = op.outTrigger("Next"); + + +const cgl = op.patch.cgl; + +const srcHeadVert = "" + .endl() + "IN vec3 attrVertColor;" + .endl() + "OUT vec4 vertColor;" + + .endl(); + +const srcBodyVert = "" + .endl() + "vertColor.rgb=attrVertColor;" + .endl(); + +inInput.onChange = + inInvert.onChange = updateDefines; + +render.onTriggered = doRender; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "priority": 2, + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + srcHeadVert, + srcBodyVert +}); + +mod.addModule({ + "title": op.name, + "name": "MODULE_COLOR", + "srcHeadFrag": attachments.colorarea_head_frag, + "srcBodyFrag": attachments.colorarea_frag +}); + +updateDefines(); + + +function updateDefines() +{ + mod.toggleDefine("MOD_INPUT_R", inInput.get()==="R"); + mod.toggleDefine("MOD_INPUT_G", inInput.get()==="G"); + mod.toggleDefine("MOD_INPUT_B", inInput.get()==="B"); + mod.toggleDefine("MOD_INPUT_LUMI", inInput.get()==="Luminance"); + mod.toggleDefine("MOD_INVERT", inInvert.get()); +} + + + +function doRender() +{ + mod.bind(); + next.trigger(); + + mod.unbind(); +} + + +}; + +Ops.Gl.ShaderEffects.VertexColorAsAlpha.prototype = new CABLES.Op(); +CABLES.OPS["3a3a4c1f-3824-4fa2-b287-1368bde0f0a0"]={f:Ops.Gl.ShaderEffects.VertexColorAsAlpha,objName:"Ops.Gl.ShaderEffects.VertexColorAsAlpha"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.VertexDisplacementMap_v4 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.VertexDisplacementMap_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"vertdisplace_body_vert":"\nvec2 MOD_tc=texCoord;\n\n#ifdef MOD_COORD_MESHXY\n MOD_tc=pos.xy;\n#endif\n#ifdef MOD_COORD_MESHXZ\n MOD_tc=pos.xz;\n#endif\n\n\n#ifdef MOD_FLIP_Y\n MOD_tc.y=1.0-MOD_tc.y;\n#endif\n#ifdef MOD_FLIP_X\n MOD_tc.x=1.0-MOD_tc.x;\n#endif\n#ifdef MOD_FLIP_XY\n MOD_tc=1.0-MOD_tc;\n#endif\n\nMOD_tc*=MOD_scale;\n\nvec4 MOD_sample=texture( MOD_texture, vec2(MOD_tc.x+MOD_offsetX,MOD_tc.y+MOD_offsetY) );\nvec3 MOD_disp;\n\n#ifdef MOD_INPUT_R\n MOD_disp=vec3(MOD_sample.r);\n#endif\n#ifdef MOD_INPUT_G\n MOD_disp=vec3(MOD_sample.g);\n#endif\n#ifdef MOD_INPUT_B\n MOD_disp=vec3(MOD_sample.b);\n#endif\n#ifdef MOD_INPUT_A\n MOD_disp=vec3(MOD_sample.a);\n#endif\n#ifdef MOD_INPUT_RGB\n MOD_disp=MOD_sample.rgb;\n#endif\n#ifdef MOD_INPUT_LUMI\n MOD_disp=vec3(dot(vec3(0.2126,0.7152,0.0722), MOD_sample.rgb));\n#endif\n\n\n\n#ifdef MOD_HEIGHTMAP_INVERT\n MOD_disp=1.0-MOD_disp;\n#endif\n// #ifdef MOD_HEIGHTMAP_NORMALIZE\n// MOD_disp-=0.5;\n// MOD_disp*=2.0;\n// #endif\n\n\n#ifdef MOD_HEIGHTMAP_NORMALIZE\n MOD_disp=(MOD_disp-0.5)*2.0;\n // MOD_disp=(MOD_disp-0.5)*-1.0+0.5;\n#endif\n\n\nfloat MOD_zero=0.0;\n\n#ifdef MOD_MODE_DIV\n MOD_zero=1.0;\n#endif\n#ifdef MOD_MODE_MUL\n MOD_zero=1.0;\n#endif\n\n\n\nvec3 MOD_mask=vec3(1.0);\n\n#ifdef MOD_AXIS_X\n MOD_mask=vec3(1.,0.,0.);\n MOD_disp*=MOD_mask*MOD_extrude;\n#endif\n#ifdef MOD_AXIS_Y\n MOD_mask=vec3(0.,1.,0.);\n MOD_disp*=MOD_mask*MOD_extrude;\n#endif\n#ifdef MOD_AXIS_Z\n MOD_mask=vec3(0.,0.,1.);\n MOD_disp*=MOD_mask*MOD_extrude;\n#endif\n#ifdef MOD_AXIS_XY\n MOD_mask=vec3(1.,1.,0.);\n MOD_disp*=MOD_mask*MOD_extrude;\n#endif\n#ifdef MOD_AXIS_XYZ\n MOD_mask=vec3(1.,1.,1.);\n MOD_disp*=MOD_mask*MOD_extrude;\n#endif\n\n\n// MOD_disp=smoothstep(-1.,1.,MOD_disp*MOD_disp*MOD_disp);\n// MOD_disp=MOD_disp*MOD_disp*MOD_disp;\n\n// #ifdef MOD_FLIP_Y\n// MOD_mask.y=1.0-MOD_mask.y;\n// #endif\n// #ifdef MOD_FLIP_X\n// MOD_mask.x=1.0-MOD_mask.x;\n// #endif\n// #ifdef MOD_FLIP_XY\n// MOD_mask.xy=1.0-MOD_mask.xy;\n// #endif\n\n\n\n#ifdef MOD_MODE_DIV\n pos.xyz/=MOD_disp*MOD_mask;\n#endif\n\n#ifdef MOD_MODE_MUL\n pos.xyz*=MOD_disp*MOD_mask;\n#endif\n\n#ifdef MOD_MODE_ADD\n pos.xyz+=MOD_disp*MOD_mask;\n#endif\n\n#ifdef MOD_MODE_NORMAL\n\n vec3 MOD_t=norm;\n #ifdef MOD_SMOOTHSTEP\n MOD_t=smoothstep(-1.,1.,MOD_t);\n #endif\n\n pos.xyz+=MOD_t*MOD_disp*MOD_mask;\n\n#endif\n\n#ifdef MOD_MODE_TANGENT\n MOD_disp*=-1.0;\n\n vec3 MOD_t=attrTangent;\n #ifdef MOD_SMOOTHSTEP\n MOD_t=smoothstep(-1.,1.,MOD_t);\n #endif\n\n pos.xyz+=MOD_t*MOD_disp*MOD_mask;\n\n#endif\n\n#ifdef MOD_MODE_BITANGENT\n MOD_disp*=-1.0;\n vec3 MOD_t=attrBiTangent;\n\n #ifdef MOD_SMOOTHSTEP\n MOD_t=smoothstep(-1.,1.,MOD_t);\n #endif\n\n pos.xyz+=MOD_t*MOD_disp*MOD_mask;\n\n#endif\n\n#ifdef MOD_MODE_VERTCOL\n vec3 MOD_t=attrVertColor.rgb*vec3(2.0)-vec3(1.0);\n\n #ifdef MOD_SMOOTHSTEP\n MOD_t=smoothstep(-1.,1.,MOD_t);\n #endif\n\n pos.xyz+=MOD_t*MOD_disp*MOD_mask;\n\n#endif\n\n\n// pos.y*=-1.0;\n // pos.xy+=vec2(MOD_texVal*MOD_extrude)*normalize(pos.xy);\n\n\nMOD_displHeightMapColor=MOD_disp;\n\n\n#ifdef CALC_NORMALS\n norm+=MOD_calcNormal(MOD_texture,MOD_tc);\n#endif","vertdisplace_head_vert":"OUT vec3 MOD_displHeightMapColor;\n\n#ifdef MOD_MODE_VERTCOL\n#ifndef VERTEX_COLORS\nIN vec4 attrVertColor;\n#endif\n#endif\n\n// mat4 rotationX( in float angle ) {\n// \treturn mat4(\t1.0,\t\t0,\t\t\t0,\t\t\t0,\n// \t\t\t \t\t0, \tcos(angle),\t-sin(angle),\t\t0,\n// \t\t\t\t\t0, \tsin(angle),\t cos(angle),\t\t0,\n// \t\t\t\t\t0, \t\t\t0,\t\t\t 0, \t\t1);\n// }\n\n// mat4 rotationY( in float angle ) {\n// \treturn mat4(\tcos(angle),\t\t0,\t\tsin(angle),\t0,\n// \t\t\t \t\t\t\t0,\t\t1.0,\t\t\t 0,\t0,\n// \t\t\t\t\t-sin(angle),\t0,\t\tcos(angle),\t0,\n// \t\t\t\t\t\t\t0, \t\t0,\t\t\t\t0,\t1);\n// }\n\n// mat4 rotationZ( in float angle ) {\n// \treturn mat4(\tcos(angle),\t\t-sin(angle),\t0,\t0,\n// \t\t\t \t\tsin(angle),\t\tcos(angle),\t\t0,\t0,\n// \t\t\t\t\t\t\t0,\t\t\t\t0,\t\t1,\t0,\n// \t\t\t\t\t\t\t0,\t\t\t\t0,\t\t0,\t1);\n// }\n\n\nvec3 MOD_calcNormal(sampler2D tex,vec2 uv)\n{\n float strength=13.0;\n float texelSize=1.0/512.0;\n\n float tl = abs(texture(tex, uv + texelSize * vec2(-1.0, -1.0)).x); // top left\n float l = abs(texture(tex, uv + texelSize * vec2(-1.0, 0.0)).x); // left\n float bl = abs(texture(tex, uv + texelSize * vec2(-1.0, 1.0)).x); // bottom left\n float t = abs(texture(tex, uv + texelSize * vec2( 0.0, -1.0)).x); // top\n float b = abs(texture(tex, uv + texelSize * vec2( 0.0, 1.0)).x); // bottom\n float tr = abs(texture(tex, uv + texelSize * vec2( 1.0, -1.0)).x); // top right\n float r = abs(texture(tex, uv + texelSize * vec2( 1.0, 0.0)).x); // right\n float br = abs(texture(tex, uv + texelSize * vec2( 1.0, 1.0)).x); // bottom right\n\n // // Compute dx using Sobel:\n // // -1 0 1\n // // -2 0 2\n // // -1 0 1\n float dX = tr + 2.0*r + br -tl - 2.0*l - bl;\n\n // // Compute dy using Sobel:\n // // -1 -2 -1\n // // 0 0 0\n // // 1 2 1\n float dY = bl + 2.0*b + br -tl - 2.0*t - tr;\n\n // // Build the normalized normal\n\n vec3 N = normalize(vec3(dX,dY, 1.0 / strength));\n\n // //convert (-1.0 , 1.0) to (0.0 , 1.0), if needed\n N= N * 0.5 + 0.5;\n\n return N;\n}\n",}; +const + render = op.inTrigger("Render"), + + // meth = op.inValueSelect("Mode", ["normal", "normal xy", "mul xyz", "mul xy", "sub x", "add x", "add xy", "add y", "add z", "mul y", "mul z", "sub z", "normal2", "normal RGB", "m14"], "normal"), + extrude = op.inValue("Extrude", 0.5), + meth = op.inSwitch("Mode", ["Norm", "Tang", "BiTang", "VertCol", "*", "+", "/"], "Norm"), + axis = op.inSwitch("Axis", ["XYZ", "XY", "X", "Y", "Z"], "XYZ"), + src = op.inSwitch("Coordinates", ["Tex Coords", "Mesh XY", "Mesh XZ"], "Tex Coords"), + + texture = op.inTexture("Texture", null, "texture"), + channel = op.inSwitch("Channel", ["Luminance", "R", "G", "B", "A", "RGB"], "Luminance"), + flip = op.inSwitch("Flip", ["None", "X", "Y", "XY"], "None"), + range = op.inSwitch("Range", ["0-1", "1-0", "Normalized"], "0-1"), + offsetX = op.inValueFloat("Offset X"), + offsetY = op.inValueFloat("Offset Y"), + scale = op.inValueFloat("Scale", 1), + + calcNormals = op.inValueBool("Calc Normals", false), + removeZero = op.inValueBool("Discard Zero Values"), + colorize = op.inValueBool("colorize", false), + colorizeMin = op.inValueSlider("Colorize Min", 0), + colorizeMax = op.inValueSlider("Colorize Max", 1), + next = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; + +op.setPortGroup("Input", [texture, flip, channel, range, offsetX, offsetY, scale]); +op.setPortGroup("Colorize", [colorize, colorizeMin, colorizeMax]); + +op.toWorkPortsNeedToBeLinked(texture, next, render); + +render.onTriggered = dorender; + +channel.onChange = +colorize.onChange = +axis.onChange = + range.onChange = + removeZero.onChange = + flip.onChange = + calcNormals.onChange = + src.onChange = + meth.onChange = updateDefines; + +const srcHeadVert = attachments.vertdisplace_head_vert; +const srcBodyVert = attachments.vertdisplace_body_vert; + +const srcHeadFrag = "" + .endl() + "IN vec3 MOD_displHeightMapColor;" + .endl() + "vec3 MOD_map(vec3 value, float inMin, float inMax, float outMin, float outMax) { return outMin + (outMax - outMin) * (value - inMin) / (inMax - inMin);}" + + .endl(); + +const srcBodyFrag = "" + .endl() + "#ifdef MOD_HEIGHTMAP_COLORIZE" + .endl() + " col.rgb*=MOD_map( MOD_displHeightMapColor, 0.0,1.0 , MOD_colorizeMin,MOD_colorizeMax);" + .endl() + "#endif" + .endl() + "#ifdef MOD_DISPLACE_REMOVE_ZERO" + .endl() + " if(MOD_displHeightMapColor.r==0.0)discard;" + .endl() + "#endif" + .endl(); + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert +}); + +mod.addModule({ + "title": op.name, + "name": "MODULE_COLOR", + "srcHeadFrag": srcHeadFrag, + "srcBodyFrag": srcBodyFrag +}); + +mod.addUniformVert("t", "MOD_texture", 0); +mod.addUniformVert("f", "MOD_extrude", extrude); +mod.addUniformVert("f", "MOD_offsetX", offsetX); +mod.addUniformVert("f", "MOD_offsetY", offsetY); +mod.addUniformVert("f", "MOD_scale", scale); + +mod.addUniformFrag("f", "MOD_colorizeMin", colorizeMin); +mod.addUniformFrag("f", "MOD_colorizeMax", colorizeMax); + +updateDefines(); + +function updateDefines() +{ + mod.toggleDefine("MOD_HEIGHTMAP_COLORIZE", colorize.get()); + + mod.toggleDefine("MOD_HEIGHTMAP_INVERT", range.get() == "1-0"); + mod.toggleDefine("MOD_HEIGHTMAP_NORMALIZE", range.get() == "Normalized"); + + mod.toggleDefine("MOD_DISPLACE_REMOVE_ZERO", removeZero.get()); + + mod.toggleDefine("MOD_INPUT_R", channel.get() == "R"); + mod.toggleDefine("MOD_INPUT_G", channel.get() == "G"); + mod.toggleDefine("MOD_INPUT_B", channel.get() == "B"); + mod.toggleDefine("MOD_INPUT_A", channel.get() == "A"); + mod.toggleDefine("MOD_INPUT_RGB", channel.get() == "RGB"); + mod.toggleDefine("MOD_INPUT_LUMI", channel.get() == "Luminance"); + + mod.toggleDefine("MOD_FLIP_X", flip.get() == "X"); + mod.toggleDefine("MOD_FLIP_Y", flip.get() == "Y"); + mod.toggleDefine("MOD_FLIP_XY", flip.get() == "XY"); + + mod.toggleDefine("MOD_AXIS_X", axis.get() == "X"); + mod.toggleDefine("MOD_AXIS_Y", axis.get() == "Y"); + mod.toggleDefine("MOD_AXIS_Z", axis.get() == "Z"); + mod.toggleDefine("MOD_AXIS_XYZ", axis.get() == "XYZ"); + mod.toggleDefine("MOD_AXIS_XY", axis.get() == "XY"); + + mod.toggleDefine("MOD_MODE_BITANGENT", meth.get() == "BiTang"); + mod.toggleDefine("MOD_MODE_TANGENT", meth.get() == "Tang"); + mod.toggleDefine("MOD_MODE_NORMAL", meth.get() == "Norm"); + mod.toggleDefine("MOD_MODE_VERTCOL", meth.get() == "VertCol"); + mod.toggleDefine("MOD_MODE_MUL", meth.get() == "*"); + mod.toggleDefine("MOD_MODE_ADD", meth.get() == "+"); + mod.toggleDefine("MOD_MODE_DIV", meth.get() == "/"); + mod.toggleDefine("MOD_SMOOTHSTEP", 0); + + mod.toggleDefine("MOD_COORD_TC", src.get() == "Tex Coords"); + mod.toggleDefine("MOD_COORD_MESHXY", src.get() == "Mesh XY"); + mod.toggleDefine("MOD_COORD_MESHXZ", src.get() == "Mesh XZ"); + + mod.toggleDefine("CALC_NORMALS", calcNormals.get()); +} + +function dorender() +{ + mod.bind(); + + if (texture.get() && !texture.get().deleted) mod.pushTexture("MOD_texture", texture.get()); + else mod.pushTexture("MOD_texture", CGL.Texture.getEmptyTexture(cgl)); + + next.trigger(); + + mod.unbind(); +} + + +}; + +Ops.Gl.ShaderEffects.VertexDisplacementMap_v4.prototype = new CABLES.Op(); +CABLES.OPS["ed36e5ad-457b-4ac6-a929-11b66951cb6c"]={f:Ops.Gl.ShaderEffects.VertexDisplacementMap_v4,objName:"Ops.Gl.ShaderEffects.VertexDisplacementMap_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.VertexNumberLimit_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.VertexNumberLimit_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + limitMin = op.inValueInt("Min", 0), + limitMax = op.inValueInt("Max", 1000), + inv=op.inBool("Invert",false); + +const trigger = op.outTrigger("Next"); +const cgl = op.patch.cgl; + +const srcHeadVert = "" + .endl() + "OUT float MOD_discard;" + .endl(); + +const srcBodyVert = "" + .endl() + "MOD_discard=1.0; " + + .endl() + "#ifndef MOD_INVERT" + .endl() + " if(attrVertIndex >= MOD_vertLimit.x && attrVertIndex <= MOD_vertLimit.y) MOD_discard=0.0; " + .endl() + "#endif" + + .endl() + "#ifdef MOD_INVERT" + .endl() + " if(attrVertIndex < MOD_vertLimit.x || attrVertIndex > MOD_vertLimit.y) MOD_discard=0.0; " + .endl() + "#endif" + .endl(); + +const srcHeadFrag = "" + .endl() + "IN float MOD_discard;" + .endl(); + +const srcBodyFrag = "" + .endl() + "if(MOD_discard>0.0) discard;" + .endl(); + + +const mod = new CGL.ShaderModifier(cgl, op.name); + +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + srcHeadVert, + srcBodyVert +}); + +mod.addModule({ + "title": op.name, + "name": "MODULE_COLOR", + "srcHeadFrag": srcHeadFrag, + "srcBodyFrag": srcBodyFrag +}); + +mod.addUniform("2f", "MOD_vertLimit", limitMin,limitMax); +inv.onChange=updateDefines; + + +function updateDefines() +{ + mod.toggleDefine("MOD_INVERT",inv.get()); +} + +render.onTriggered = function () +{ + mod.bind(); + trigger.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.VertexNumberLimit_v2.prototype = new CABLES.Op(); +CABLES.OPS["3c1043bd-e65d-42ea-a154-456f235b197c"]={f:Ops.Gl.ShaderEffects.VertexNumberLimit_v2,objName:"Ops.Gl.ShaderEffects.VertexNumberLimit_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.VertexPositionFromTexture_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.VertexPositionFromTexture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"vertposbody_vert":"vec4 col=texture(MOD_tex,texCoord);\n\nvec3 MOD_pos=col.xyz;\n\n#ifdef MOD_ADD\npos.xyz+=MOD_pos.xyz;\n#endif\n\n#ifdef MOD_ABS\npos.xyz=MOD_pos.xyz;\n#endif\n\n",}; +const + render = op.inTrigger("render"), + inTex = op.inTexture("Texture"), + inMode = op.inSwitch("Mode", ["Absolute", "Add"], "Absolute"), + trigger = op.outTrigger("Trigger"); + +const cgl = op.patch.cgl; + +const mod = new CGL.ShaderModifier(cgl, op.name); +mod.addModule({ + "priority": 2, + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": "", + "srcBodyVert": attachments.vertposbody_vert +}); + +mod.addUniformVert("t", "MOD_tex"); +inMode.onChange = updateDefines; +render.onTriggered = doRender; +updateDefines(); + +function updateDefines() +{ + mod.toggleDefine("MOD_ADD", inMode.get() == "Add"); + mod.toggleDefine("MOD_ABS", inMode.get() == "Absolute"); +} + +function doRender() +{ + mod.bind(); + if (inTex.get())mod.pushTexture("MOD_tex", inTex.get().tex); + + trigger.trigger(); + mod.unbind(); +} + + +}; + +Ops.Gl.ShaderEffects.VertexPositionFromTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["30eea555-afda-456e-9c50-9a40b370fa1f"]={f:Ops.Gl.ShaderEffects.VertexPositionFromTexture_v2,objName:"Ops.Gl.ShaderEffects.VertexPositionFromTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShaderEffects.VertexWobble_v2 +// +// ************************************************************** + +Ops.Gl.ShaderEffects.VertexWobble_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"sinewobble_vert":"\n#ifndef MOD_WORLDSPACE\n vec4 MOD_vertPos=vec4(vPosition,1.0);\n#endif\n#ifdef MOD_WORLDSPACE\n vec4 MOD_vertPos=mMatrix*pos;\n#endif\n\n#ifdef MOD_AREA_SPHERE\n float MOD_de=distance(\n MOD_posSize.xyz,\n vec3(MOD_vertPos.x,MOD_vertPos.y,MOD_vertPos.z)\n );\n#endif\n\n#ifdef MOD_AREA_BOX\n float MOD_de=0.0;\n if(abs(MOD_vertPos.y-MOD_posSize.y)>MOD_posSize.w ||\n abs(MOD_vertPos.x-MOD_posSize.x)>MOD_posSize.w ||\n abs(MOD_vertPos.z-MOD_posSize.z)>MOD_posSize.w ) MOD_de=1.0;\n#endif\n\n#ifdef MOD_AREA_AXIS_X\n float MOD_de=abs(MOD_posSize.x-MOD_vertPos.x);\n#endif\n#ifdef MOD_AREA_AXIS_Y\n float MOD_de=abs(MOD_posSize.y-MOD_vertPos.y);\n#endif\n#ifdef MOD_AREA_AXIS_Z\n float MOD_de=abs(MOD_posSize.z-MOD_vertPos.z);\n#endif\n\n#ifdef MOD_AREA_AXIS_X_INFINITE\n float MOD_de=MOD_posSize.x-MOD_vertPos.x;\n#endif\n#ifdef MOD_AREA_AXIS_Y_INFINITE\n float MOD_de=MOD_posSize.y-MOD_vertPos.y;\n#endif\n#ifdef MOD_AREA_AXIS_Z_INFINITE\n float MOD_de=MOD_posSize.z-MOD_vertPos.z;\n#endif\n\n#ifndef MOD_AREA_BOX\n MOD_de=1.0-smoothstep(MOD_falloff,MOD_posSize.w,MOD_de);\n#endif\n\n#ifdef MOD_AREA_INVERT\n MOD_de=1.0-MOD_de;\n#endif\n\nfloat MOD_v=0.0;\n\n#ifdef MOD_SRC_XZ\n MOD_v=(MOD_vertPos.x+MOD_vertPos.z);\n#endif\n#ifdef MOD_SRC_XY\n MOD_v=(MOD_vertPos.x+MOD_vertPos.y);\n#endif\n#ifdef MOD_SRC_X\n MOD_v=MOD_vertPos.x;\n#endif\n#ifdef MOD_SRC_Y\n MOD_v=MOD_vertPos.y;\n#endif\n#ifdef MOD_SRC_Z\n MOD_v=MOD_vertPos.z;\n#endif\n\n\nfloat MOD_amnt=MOD_amount*MOD_de;\n\nMOD_v=sin( (MOD_time)+( MOD_v*MOD_scale ) ) ;\n#ifdef MOD_POSITIVE\n MOD_v=(MOD_v+1.0)/2.0;\n#endif\nMOD_v*=MOD_amnt;\n\n\n\n\n#ifdef MOD_TO_AXIS_X\n pos.x+=MOD_v;\n// norm.x+=MOD_v;\n#endif\n\n#ifdef MOD_TO_AXIS_Y\n pos.y+=MOD_v;\n// norm.y+=MOD_v;\n#endif\n\n#ifdef MOD_TO_AXIS_Z\n pos.z+=MOD_v;\n// norm.z+=MOD_v;\n#endif\n\n// norm=normalize(norm);\n\n\n\n\n\n",}; +let self = this; +const cgl = op.patch.cgl; + +const render = op.inTrigger("render"); +let src = op.inValueSelect("Source", [ + "X * Z + Time", + "X * Y + Time", + "X + Time", + "Y + Time", + "Z + Time"], "X * Z + Time"); + +const + amount = op.inValueSlider("amount", 0.1), + inTime=op.inFloat("Time",0), + mul = op.inValueFloat("Scale", 3), + toAxisX = op.inValueBool("axisX", true), + toAxisY = op.inValueBool("axisY", true), + toAxisZ = op.inValueBool("axisZ", true), + positive = op.inSwitch("Range",['-1 to 1','0 to 1'],'-1 to 1'), + + inArea = op.inValueSelect("Area", ["Sphere", "Box", "Axis X", "Axis Y", "Axis Z", "Axis X Infinite", "Axis Y Infinite", "Axis Z Infinite"], "Sphere"), + inSize = op.inValue("Size", 1), + inFalloff = op.inValueSlider("Falloff", 0), + + x = op.inValue("x"), + y = op.inValue("y"), + z = op.inValue("z"), + inWorldSpace = op.inValueBool("WorldSpace", true), + inInvert = op.inValueBool("Invert"), + + next = this.outTrigger("trigger"); + +op.setPortGroup("Area",[inArea,inSize,x,y,z,inFalloff,inWorldSpace,inInvert]); + +positive.onChange= +inArea.onChange= + inWorldSpace.onChange= + inSize.onChange= + src.onChange = + toAxisZ.onChange = + toAxisX.onChange = + toAxisY.onChange = setDefines; + +const srcHeadVert = ""; +// let startTime = CABLES.now() / 1000.0; +const mod = new CGL.ShaderModifier(cgl, op.name); + +mod.addModule({ + "title": op.name, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": attachments.sinewobble_vert +}); + +mod.addUniform("4f", "MOD_posSize", x, y, z,inSize); +mod.addUniformVert("f", "MOD_time", inTime); +mod.addUniformVert("f", "MOD_amount", amount); +mod.addUniformVert("f", "MOD_scale", mul); +mod.addUniformVert("f", "MOD_falloff", inFalloff); + +setDefines(); + +function setDefines() +{ + mod.toggleDefine("MOD_AREA_INVERT", inInvert.get()); + mod.toggleDefine("MOD_POSITIVE", positive.get()=="0 to 1"); + + mod.toggleDefine("MOD_WORLDSPACE", inWorldSpace.get() ); + mod.toggleDefine("MOD_AREA_AXIS_X", inArea.get() == "Axis X"); + mod.toggleDefine("MOD_AREA_AXIS_Y", inArea.get() == "Axis Y"); + mod.toggleDefine("MOD_AREA_AXIS_Z", inArea.get() == "Axis Z"); + mod.toggleDefine("MOD_AREA_AXIS_X_INFINITE", inArea.get() == "Axis X Infinite"); + mod.toggleDefine("MOD_AREA_AXIS_Y_INFINITE", inArea.get() == "Axis Y Infinite"); + mod.toggleDefine("MOD_AREA_AXIS_Z_INFINITE", inArea.get() == "Axis Z Infinite"); + mod.toggleDefine("MOD_AREA_SPHERE", inArea.get() == "Sphere"); + mod.toggleDefine("MOD_AREA_BOX", inArea.get() == "Box"); + + mod.toggleDefine("MOD_TO_AXIS_X", toAxisX.get()); + mod.toggleDefine("MOD_TO_AXIS_Y", toAxisY.get()); + mod.toggleDefine("MOD_TO_AXIS_Z", toAxisZ.get()); + mod.toggleDefine("MOD_SRC_XZ", !src.get() || src.get() == "X * Z + Time" || src.get() === ""); + mod.toggleDefine("MOD_SRC_XY", src.get() == "X * Y + Time"); + mod.toggleDefine("MOD_SRC_X", src.get() == "X + Time"); + mod.toggleDefine("MOD_SRC_Y", src.get() == "Y + Time"); + mod.toggleDefine("MOD_SRC_Z", src.get() == "Z + Time"); +} + +render.onTriggered = function () +{ + mod.bind(); + next.trigger(); + mod.unbind(); +}; + + +}; + +Ops.Gl.ShaderEffects.VertexWobble_v2.prototype = new CABLES.Op(); +CABLES.OPS["76983d1c-be6d-453d-b34f-472efe8221e7"]={f:Ops.Gl.ShaderEffects.VertexWobble_v2,objName:"Ops.Gl.ShaderEffects.VertexWobble_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.ShowNormals_v2 +// +// ************************************************************** + +Ops.Gl.ShowNormals_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colorize_normals_frag":"UNI vec4 inColor;\n\nvoid main() {\n {{MODULE_BEGIN_FRAG}}\n vec4 col = inColor;\n\n {{MODULE_COLOR}}\n\n outColor = col;\n}",}; +const + render = op.inTrigger("render"), + inDoRender = op.inBool("Draw", true), + geometry = op.inObject("geometry", null, "geometry"), + dropdown = op.inSwitch("Vectors", ["Normals", "Tangents", "Bitangents"], "Normals"), + mul = op.inValueFloat("Length", 0.1), + inColorize = op.inBool("Colorize", true), + inR = op.inFloat("R", 0), + inG = op.inFloat("G", 0.8), + inB = op.inFloat("B", 0), + inA = op.inFloatSlider("A", 1), + trigger = op.outTrigger("trigger"), + outGeom = op.outObject("Line Geom", null, "geometry"); + +inR.setUiAttribs({ "colorPick": true, "greyout": true }); +inG.setUiAttribs({ "greyout": true }); +inB.setUiAttribs({ "greyout": true }); +inA.setUiAttribs({ "greyout": true }); + +geometry.ignoreValueSerialize = true; + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "colorizeNormals"); +shader.setSource(shader.getDefaultVertexShader(), attachments.colorize_normals_frag); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +shader.glPrimitive = cgl.gl.LINES; + +const inColorUniform = new CGL.Uniform(shader, "4f", "inColor", inR, inG, inB, inA); + +geometry.onChange = mul.onChange = dropdown.onChange = buildMesh; +inColorize.onChange = handleColorizeChange; +handleColorizeChange(); + +let mesh = null; +const position = vec3.create(); + +buildMesh(); + +function handleColorizeChange() +{ + inR.setUiAttribs({ "greyout": !inColorize.get() }); + inG.setUiAttribs({ "greyout": !inColorize.get() }); + inB.setUiAttribs({ "greyout": !inColorize.get() }); + inA.setUiAttribs({ "greyout": !inColorize.get() }); + + if (inColorize.get()) + { + shader.setSource(shader.getDefaultVertexShader(), attachments.colorize_normals_frag); + } + else + { + shader.setSource(shader.getDefaultVertexShader(), shader.getDefaultFragmentShader()); + } +} + +function buildMesh() +{ + const geom = new CGL.Geometry("shownormals"); + + const points = []; + const tc = []; + const geometryInput = geometry.get(); + + if (geometryInput && geometryInput.vertices) + { + op.setUiError("noVertices", null); + for (let i = 0; i < geometryInput.vertices.length; i += 3) + { + points.push(geometryInput.vertices[i + 0]); + points.push(geometryInput.vertices[i + 1]); + points.push(geometryInput.vertices[i + 2]); + + tc.push(0, 1); + tc.push(0, 1); + if (dropdown.get() === "Normals") + { + if (!geometryInput.vertexNormals || !geometryInput.vertexNormals.length) + { + op.setUiError("noNormals", "Input geometry has no normals!", 1); + } + else + { + op.setUiError("noNormals", null); + points.push(geometryInput.vertices[i + 0] + geometryInput.vertexNormals[i + 0] * mul.get()); + points.push(geometryInput.vertices[i + 1] + geometryInput.vertexNormals[i + 1] * mul.get()); + points.push(geometryInput.vertices[i + 2] + geometryInput.vertexNormals[i + 2] * mul.get()); + } + } + + if (dropdown.get() === "Tangents") + { + if (!geometryInput.tangents || !geometryInput.tangents.length) + { + op.setUiError("noTangents", "Input geometry has no tangents!", 1); + } + else + { + op.setUiError("noTangents", null); + points.push(geometryInput.vertices[i + 0] + geometryInput.tangents[i + 0] * mul.get()); + points.push(geometryInput.vertices[i + 1] + geometryInput.tangents[i + 1] * mul.get()); + points.push(geometryInput.vertices[i + 2] + geometryInput.tangents[i + 2] * mul.get()); + } + } + if (dropdown.get() === "Bitangents") + { + if (!geometryInput.biTangents || !geometryInput.biTangents.length) + { + op.setUiError("noBitangents", "Input geometry has no bitangents!", 1); + } + else + { + op.setUiError("noBitangents", null); + points.push(geometryInput.vertices[i + 0] + geometryInput.biTangents[i + 0] * mul.get()); + points.push(geometryInput.vertices[i + 1] + geometryInput.biTangents[i + 1] * mul.get()); + points.push(geometryInput.vertices[i + 2] + geometryInput.biTangents[i + 2] * mul.get()); + } + } + } + + geom.vertices = points; + geom.texCoords = tc; + geom.glPrimitive = cgl.gl.LINES; + + if (mesh) mesh.dispose(); + mesh = new CGL.Mesh(cgl, geom); + + outGeom.set(null); + outGeom.set(geom); + } + else + { + outGeom.set(null); + if (mesh) mesh.dispose(); + mesh = null; + op.setUiError("noVertices", "There is no input geometry or input geometry has no vertices!", 0); + } +} + +render.onTriggered = function () +{ + if (geometry.get() && inDoRender.get()) + { + if (!shader) return; + if (mesh) mesh.render(shader); + } + + trigger.trigger(); +}; + + +}; + +Ops.Gl.ShowNormals_v2.prototype = new CABLES.Op(); +CABLES.OPS["3d68b4d4-2945-48bf-8fc9-30567946deda"]={f:Ops.Gl.ShowNormals_v2,objName:"Ops.Gl.ShowNormals_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.SurfaceScatter_v2 +// +// ************************************************************** + +Ops.Gl.SurfaceScatter_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Render"), + inDraw = op.inValueBool("draw", true), + inNum = op.inValueInt("Num", 100), + inGeomSurface = op.inObject("Geom Surface"), + // inGeom = op.inObject("Geometry"), + inDistribution = op.inValueSelect("Distribution", ["Vertex", "Triangle Center", "Triangle Side", "Random Triangle Point"], "Vertex"), + inVariety = op.inValueSelect("Selection", ["Random", "Sequential"], "Random"), + seed = op.inValueFloat("Random Seed"), + inSizeMin = op.inValueSlider("Size min", 1.0), + inSizeMax = op.inValueSlider("Size max", 1.0), + inDoLimit = op.inValueBool("Limit", false), + inLimit = op.inValueInt("Limit Num", 0), + inRotateRandom = op.inValueBool("Random Rotate", true), + outNext = op.outTrigger("Next"), + outArrPositions = op.outArray("Positions", 3), + outArrScale = op.outArray("Scale", 3), + outArrRotations = op.outArray("Quaternions", 4); +const cgl = op.patch.cgl; +const mod = null; +let recalc = true; + +let matrixArray = new Float32Array(1); +const m = mat4.create(); +const qAxis = vec3.create(); + +op.setPortGroup("Size", [inSizeMin, inSizeMax]); +op.setPortGroup("Distribution", [inDistribution, inVariety, seed]); + +inDistribution.onChange = + seed.onChange = + inNum.onChange = + inRotateRandom.onChange = + inSizeMin.onChange = + inSizeMax.onChange = + inVariety.onChange = + inGeomSurface.onChange = reset; +render.onTriggered = doRender; + +const arrPositions = []; +const arrRotations = []; +const arrScale = []; + +function uniqueIndices(oldCount, newCount, randomize) +{ + function fisherYatesShuffle(array) + { + Math.randomSeed = seed.get(); + let i = 0; + let j = 0; + let temp = null; + + for (i = array.length - 1; i > 0; i -= 1) + { + j = Math.floor(Math.seededRandom() * (i + 1)); + temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + } + + const arr = []; + arr.length = newCount; + + if (newCount > oldCount) + { + arr.length = newCount; + for (let i = 0; i < newCount; i++) arr[i] = i % (oldCount); + } + else + { + arr.length = oldCount; + for (let i = 0; i < oldCount; i++) arr[i] = i; + } + + if (randomize)fisherYatesShuffle(arr); + return arr; +} + +function getEuler(out, quat) +{ + const x = quat[0], + y = quat[1], + z = quat[2], + w = quat[3], + x2 = x * x, + y2 = y * y, + z2 = z * z, + w2 = w * w; + const unit = x2 + y2 + z2 + w2; + const test = x * w - y * z; + if (test > 0.499995 * unit) + { // TODO: Use glmatrix.EPSILON + // singularity at the north pole + out[0] = Math.PI / 2; + out[1] = 2 * Math.atan2(y, x); + out[2] = 0; + } + else if (test < -0.499995 * unit) + { // TODO: Use glmatrix.EPSILON + // singularity at the south pole + out[0] = -Math.PI / 2; + out[1] = 2 * Math.atan2(y, x); + out[2] = 0; + } + else + { + out[0] = Math.asin(2 * (x * z - w * y)); + out[1] = Math.atan2(2 * (x * w + y * z), 1 - 2 * (z2 + w2)); + out[2] = Math.atan2(2 * (x * y + z * w), 1 - 2 * (y2 + z2)); + } + // TODO: Return them as degrees and not as radians + return out; +} + +function setup() +{ + recalc = false; + + op.toWorkPortsNeedToBeLinkedReset(); + + // if (inDraw.get()) + // { + // op.toWorkPortsNeedToBeLinked(inGeom); + // } + const geom = inGeomSurface.get(); + const num = Math.abs(Math.floor(inNum.get())); + const m = mat4.create(); + const q = quat.create(); + const vm2 = vec3.create(); + const qMat = mat4.create(); + const norm = vec3.create(); + + if (!geom) return; + + Math.randomSeed = seed.get(); + + const DISTMODE_VERTEX = 0; + const DISTMODE_TRIANGLE_CENTER = 1; + const DISTMODE_TRIANGLE_SIDE = 2; + const DISTMODE_TRIANGLE_RANDOM = 3; + + let distMode = 0; + if (inDistribution.get() == "Triangle Center")distMode = DISTMODE_TRIANGLE_CENTER; + else if (inDistribution.get() == "Triangle Side")distMode = DISTMODE_TRIANGLE_SIDE; + else if (inDistribution.get() == "Random Triangle Point")distMode = DISTMODE_TRIANGLE_RANDOM; + + if (matrixArray.length != num * 16) matrixArray = new Float32Array(num * 16); + + let faces = geom.verticesIndices; + if (!geom.isIndexed()) + { + faces = []; + for (let i = 0; i < geom.vertices.length / 3; i++) faces[i] = i; + } + + const indices = uniqueIndices(faces.length / 3, num, inVariety.get() == "Random"); + + arrScale.length = arrPositions.length = num * 3; + arrRotations.length = num * 4; + + for (let i = 0; i < num; i++) + { + const index = indices[i]; + const index3 = index * 3; + + let px = 0; + let py = 0; + let pz = 0; + + mat4.identity(m); + + let nx = geom.vertexNormals[faces[index3] * 3 + 0]; + let ny = geom.vertexNormals[faces[index3] * 3 + 1]; + let nz = geom.vertexNormals[faces[index3] * 3 + 2]; + + if (distMode == DISTMODE_VERTEX) + { + px = geom.vertices[faces[index3] * 3 + 0]; + py = geom.vertices[faces[index3] * 3 + 1]; + pz = geom.vertices[faces[index3] * 3 + 2]; + } + else if (distMode == DISTMODE_TRIANGLE_CENTER) + { + px = (geom.vertices[faces[index3] * 3 + 0] + geom.vertices[faces[index3 + 1] * 3 + 0] + geom.vertices[faces[index3 + 2] * 3 + 0]) / 3; + py = (geom.vertices[faces[index3] * 3 + 1] + geom.vertices[faces[index3 + 1] * 3 + 1] + geom.vertices[faces[index3 + 2] * 3 + 1]) / 3; + pz = (geom.vertices[faces[index3] * 3 + 2] + geom.vertices[faces[index3 + 1] * 3 + 2] + geom.vertices[faces[index3 + 2] * 3 + 2]) / 3; + + nx = (geom.vertexNormals[faces[index3] * 3 + 0] + geom.vertexNormals[faces[index3 + 1] * 3 + 0] + geom.vertexNormals[faces[index3 + 2] * 3 + 0]) / 3; + ny = (geom.vertexNormals[faces[index3] * 3 + 1] + geom.vertexNormals[faces[index3 + 1] * 3 + 1] + geom.vertexNormals[faces[index3 + 2] * 3 + 1]) / 3; + nz = (geom.vertexNormals[faces[index3] * 3 + 2] + geom.vertexNormals[faces[index3 + 1] * 3 + 2] + geom.vertexNormals[faces[index3 + 2] * 3 + 2]) / 3; + } + else if (distMode == DISTMODE_TRIANGLE_SIDE) + { + const which = Math.round(Math.seededRandom() * 3.0); + const whichA = which; + let whichB = which + 1; + if (whichB > 2)whichB = 0; + + px = (geom.vertices[faces[index3 + whichA] * 3 + 0] + geom.vertices[faces[index3 + whichB] * 3 + 0]) / 2; + py = (geom.vertices[faces[index3 + whichA] * 3 + 1] + geom.vertices[faces[index3 + whichB] * 3 + 1]) / 2; + pz = (geom.vertices[faces[index3 + whichA] * 3 + 2] + geom.vertices[faces[index3 + whichB] * 3 + 2]) / 2; + } + else if (distMode == DISTMODE_TRIANGLE_RANDOM) + { + let r = Math.seededRandom(); + const p1x = CABLES.map(r, 0, 1, geom.vertices[(faces[index3 + 0]) * 3 + 0], geom.vertices[(faces[index3 + 1]) * 3 + 0]); + const p1y = CABLES.map(r, 0, 1, geom.vertices[(faces[index3 + 0]) * 3 + 1], geom.vertices[(faces[index3 + 1]) * 3 + 1]); + const p1z = CABLES.map(r, 0, 1, geom.vertices[(faces[index3 + 0]) * 3 + 2], geom.vertices[(faces[index3 + 1]) * 3 + 2]); + + const n1x = CABLES.map(r, 0, 1, geom.vertexNormals[(faces[index3 + 0]) * 3 + 0], geom.vertexNormals[(faces[index3 + 1]) * 3 + 0]); + const n1y = CABLES.map(r, 0, 1, geom.vertexNormals[(faces[index3 + 0]) * 3 + 1], geom.vertexNormals[(faces[index3 + 1]) * 3 + 1]); + const n1z = CABLES.map(r, 0, 1, geom.vertexNormals[(faces[index3 + 0]) * 3 + 2], geom.vertexNormals[(faces[index3 + 1]) * 3 + 2]); + + r = Math.seededRandom(); + const p2x = CABLES.map(r, 0, 1, geom.vertices[(faces[index3 + 1]) * 3 + 0], geom.vertices[(faces[index3 + 2]) * 3 + 0]); + const p2y = CABLES.map(r, 0, 1, geom.vertices[(faces[index3 + 1]) * 3 + 1], geom.vertices[(faces[index3 + 2]) * 3 + 1]); + const p2z = CABLES.map(r, 0, 1, geom.vertices[(faces[index3 + 1]) * 3 + 2], geom.vertices[(faces[index3 + 2]) * 3 + 2]); + + const n2x = CABLES.map(r, 0, 1, geom.vertexNormals[(faces[index3 + 1]) * 3 + 0], geom.vertexNormals[(faces[index3 + 2]) * 3 + 0]); + const n2y = CABLES.map(r, 0, 1, geom.vertexNormals[(faces[index3 + 1]) * 3 + 1], geom.vertexNormals[(faces[index3 + 2]) * 3 + 1]); + const n2z = CABLES.map(r, 0, 1, geom.vertexNormals[(faces[index3 + 1]) * 3 + 2], geom.vertexNormals[(faces[index3 + 2]) * 3 + 2]); + + r = Math.seededRandom(); + const p3x = CABLES.map(r, 0, 1, geom.vertices[(faces[index3 + 2]) * 3 + 0], geom.vertices[(faces[index3 + 0]) * 3 + 0]); + const p3y = CABLES.map(r, 0, 1, geom.vertices[(faces[index3 + 2]) * 3 + 1], geom.vertices[(faces[index3 + 0]) * 3 + 1]); + const p3z = CABLES.map(r, 0, 1, geom.vertices[(faces[index3 + 2]) * 3 + 2], geom.vertices[(faces[index3 + 0]) * 3 + 2]); + + const n3x = CABLES.map(r, 0, 1, geom.vertexNormals[(faces[index3 + 2]) * 3 + 0], geom.vertexNormals[(faces[index3 + 0]) * 3 + 0]); + const n3y = CABLES.map(r, 0, 1, geom.vertexNormals[(faces[index3 + 2]) * 3 + 1], geom.vertexNormals[(faces[index3 + 0]) * 3 + 1]); + const n3z = CABLES.map(r, 0, 1, geom.vertexNormals[(faces[index3 + 2]) * 3 + 2], geom.vertexNormals[(faces[index3 + 0]) * 3 + 2]); + + px = (p1x + p2x + p3x) / 3; + py = (p1y + p2y + p3y) / 3; + pz = (p1z + p2z + p3z) / 3; + + nx = (n1x + n2x + n3x) / 3; + ny = (n1y + n2y + n3y) / 3; + nz = (n1z + n2z + n3z) / 3; + } + + arrPositions[i * 3 + 0] = px; + arrPositions[i * 3 + 1] = py; + arrPositions[i * 3 + 2] = pz; + mat4.translate(m, m, [px, py, pz]); + + // rotate to normal direction + vec3.set(norm, nx, ny, nz); + vec3.set(vm2, 1, 0, 0); + quat.rotationTo(q, vm2, norm); + + // mat4.fromQuat(qMat, q); + // mat4.mul(m,m,qMat); + + // random rotate around up axis + if (inRotateRandom.get()) + { + const mr = mat4.create(); + // let qbase=quat.create(); + quat.rotateX(q, q, Math.seededRandom() * 360 * CGL.DEG2RAD); + // mat4.fromQuat(mr,qbase); + // mat4.mul(m,m,mr); + } + + // rotate -90 degree + const mr2 = mat4.create(); + // let qbase2=quat.create(); + quat.rotateZ(q, q, -90 * CGL.DEG2RAD); + mat4.fromQuat(mr2, q); + mat4.mul(m, m, mr2); + + // scale + if (inSizeMin.get() != 1.0 || inSizeMax != 1.0) + { + const sc = inSizeMin.get() + (Math.seededRandom() * (inSizeMax.get() - inSizeMin.get())); + mat4.scale(m, m, [sc, sc, sc]); + + arrScale[i * 3 + 0] = + arrScale[i * 3 + 1] = + arrScale[i * 3 + 2] = sc; + } + + // //quaternion to euler, KINDA works, but not really :/ + // let finalq=q;// + // let finalq=quat.create(); + // mat4.getRotation(finalq,m); + + // function clamp(v) + // { + // return Math.min(1,Math.max(-1,v) ) ; + // } + + // let yaw = Math.atan2(2.0*(finalq[1]*finalq[2] + finalq[3]*finalq[0]), finalq[3]*finalq[3] - finalq[0]*finalq[0] - finalq[1]*finalq[1] + finalq[2]*finalq[2]); + // let pitch = Math.asin( clamp( -2.0*(finalq[0]*finalq[2] - finalq[3]*finalq[1]))); + // let roll = Math.atan2(2.0*(finalq[0]*finalq[1] + finalq[3]*finalq[2]), finalq[3]*finalq[3] + finalq[0]*finalq[0] - finalq[1]*finalq[1] - finalq[2]*finalq[2]); + + // arrRotations[i*3+0]=360-(pitch*CGL.RAD2DEG); + // arrRotations[i*3+1]=360-(yaw*CGL.RAD2DEG); + // arrRotations[i*3+2]=(roll*CGL.RAD2DEG); + + const rotDeg = vec3.create(); + // quat.getAxisAngle(rotDeg,finalq); + getEuler(rotDeg, q); + + arrRotations[i * 4 + 0] = q[0]; + arrRotations[i * 4 + 1] = q[1]; + arrRotations[i * 4 + 2] = q[2]; + arrRotations[i * 4 + 3] = q[3]; + + // save + for (let a = 0; a < 16; a++) + { + matrixArray[i * 16 + a] = m[a]; + } + } + + outArrScale.set(null); + outArrScale.set(arrScale); + + outArrRotations.set(null); + outArrRotations.set(arrRotations); + + outArrPositions.set(null); + outArrPositions.set(arrPositions); +} + +function reset() +{ + recalc = true; +} + +function doRender() +{ + if (recalc)setup(); + recalc = false; + + outNext.trigger(); +} + + +}; + +Ops.Gl.SurfaceScatter_v2.prototype = new CABLES.Op(); +CABLES.OPS["18eb1e62-80fe-41ce-a57b-15653d00925b"]={f:Ops.Gl.SurfaceScatter_v2,objName:"Ops.Gl.SurfaceScatter_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TesselateGeometry +// +// ************************************************************** + +Ops.Gl.TesselateGeometry = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inGeom = op.inObject("Geometry"); +const inTimes = op.inValueInt("Iterations", 1); +const outGeom = op.outObject("Result"); +const outVertices = op.outNumber("Num Vertices"); + +inGeom.onChange = update; +inTimes.onChange = update; + +function tesselateTC(tc, x1, y1, x2, y2, x3, y3) +{ + tc.push(x1); + tc.push(y1); + + tc.push((x1 + x2) / 2); + tc.push((y1 + y2) / 2); + + tc.push((x1 + x3) / 2); + tc.push((y1 + y3) / 2); + + // -- + + tc.push((x1 + x2) / 2); + tc.push((y1 + y2) / 2); + + tc.push(x2); + tc.push(y2); + + tc.push((x2 + x3) / 2); + tc.push((y2 + y3) / 2); + + // -- + + tc.push((x2 + x3) / 2); + tc.push((y2 + y3) / 2); + + tc.push(x3); + tc.push(y3); + + tc.push((x1 + x3) / 2); + tc.push((y1 + y3) / 2); + + // -- + + tc.push((x1 + x2) / 2); + tc.push((y1 + y2) / 2); + + tc.push((x2 + x3) / 2); + tc.push((y2 + y3) / 2); + + tc.push((x1 + x3) / 2); + tc.push((y1 + y3) / 2); +} + +function tesselate(vertices, x1, y1, z1, x2, y2, z2, x3, y3, z3) +{ + vertices.push(x1); + vertices.push(y1); + vertices.push(z1); + + vertices.push((x1 + x2) / 2); + vertices.push((y1 + y2) / 2); + vertices.push((z1 + z2) / 2); + + vertices.push((x1 + x3) / 2); + vertices.push((y1 + y3) / 2); + vertices.push((z1 + z3) / 2); + + // -- + + vertices.push((x1 + x2) / 2); + vertices.push((y1 + y2) / 2); + vertices.push((z1 + z2) / 2); + + vertices.push(x2); + vertices.push(y2); + vertices.push(z2); + + vertices.push((x2 + x3) / 2); + vertices.push((y2 + y3) / 2); + vertices.push((z2 + z3) / 2); + + // -- + + vertices.push((x2 + x3) / 2); + vertices.push((y2 + y3) / 2); + vertices.push((z2 + z3) / 2); + + vertices.push(x3); + vertices.push(y3); + vertices.push(z3); + + vertices.push((x1 + x3) / 2); + vertices.push((y1 + y3) / 2); + vertices.push((z1 + z3) / 2); + + // -- + + vertices.push((x1 + x2) / 2); + vertices.push((y1 + y2) / 2); + vertices.push((z1 + z2) / 2); + + vertices.push((x2 + x3) / 2); + vertices.push((y2 + y3) / 2); + vertices.push((z2 + z3) / 2); + + vertices.push((x1 + x3) / 2); + vertices.push((y1 + y3) / 2); + vertices.push((z1 + z3) / 2); +} + +function tesselateGeom(oldGeom) +{ + const geom = new CGL.Geometry(op.name); + const vertices = []; + const norms = []; + const biTangents = []; + const tangents = []; + const tc = []; + + let i, j, k; + + if (oldGeom.verticesIndices.length > 0) + { + for (i = 0; i < oldGeom.verticesIndices.length; i += 3) + { + for (j = 0; j < 4; j++) + for (k = 0; k < 3; k++) + { + norms.push( + oldGeom.vertexNormals[oldGeom.verticesIndices[i + k] * 3 + 0], + oldGeom.vertexNormals[oldGeom.verticesIndices[i + k] * 3 + 1], + oldGeom.vertexNormals[oldGeom.verticesIndices[i + k] * 3 + 2] + ); + + if (oldGeom.tangents) + tangents.push( + oldGeom.tangents[oldGeom.verticesIndices[i + k] * 3 + 0], + oldGeom.tangents[oldGeom.verticesIndices[i + k] * 3 + 1], + oldGeom.tangents[oldGeom.verticesIndices[i + k] * 3 + 2] + ); + + if (oldGeom.biTangents) + biTangents.push( + oldGeom.biTangents[oldGeom.verticesIndices[i + k] * 3 + 0], + oldGeom.biTangents[oldGeom.verticesIndices[i + k] * 3 + 1], + oldGeom.biTangents[oldGeom.verticesIndices[i + k] * 3 + 2] + ); + } + + tesselate(vertices, + oldGeom.vertices[oldGeom.verticesIndices[i + 0] * 3 + 0], + oldGeom.vertices[oldGeom.verticesIndices[i + 0] * 3 + 1], + oldGeom.vertices[oldGeom.verticesIndices[i + 0] * 3 + 2], + + oldGeom.vertices[oldGeom.verticesIndices[i + 1] * 3 + 0], + oldGeom.vertices[oldGeom.verticesIndices[i + 1] * 3 + 1], + oldGeom.vertices[oldGeom.verticesIndices[i + 1] * 3 + 2], + + oldGeom.vertices[oldGeom.verticesIndices[i + 2] * 3 + 0], + oldGeom.vertices[oldGeom.verticesIndices[i + 2] * 3 + 1], + oldGeom.vertices[oldGeom.verticesIndices[i + 2] * 3 + 2] + ); + + tesselateTC(tc, + oldGeom.texCoords[oldGeom.verticesIndices[i + 0] * 2 + 0], + oldGeom.texCoords[oldGeom.verticesIndices[i + 0] * 2 + 1], + + oldGeom.texCoords[oldGeom.verticesIndices[i + 1] * 2 + 0], + oldGeom.texCoords[oldGeom.verticesIndices[i + 1] * 2 + 1], + + oldGeom.texCoords[oldGeom.verticesIndices[i + 2] * 2 + 0], + oldGeom.texCoords[oldGeom.verticesIndices[i + 2] * 2 + 1] + ); + } + } + else + { + if (oldGeom.vertices.length > 0) + { + for (i = 0; i < oldGeom.vertices.length; i += 9) + { + for (j = 0; j < 4; j++) + { + for (k = 0; k < 9; k++) + norms.push(oldGeom.vertexNormals[i + k]); + + if (oldGeom.tangents) + for (k = 0; k < 9; k++) + tangents.push(oldGeom.tangents[i + k]); + + if (oldGeom.biTangents) + for (k = 0; k < 9; k++) + biTangents.push(oldGeom.biTangents[i + k]); + } + + tesselate(vertices, + oldGeom.vertices[i + 0], + oldGeom.vertices[i + 1], + oldGeom.vertices[i + 2], + + oldGeom.vertices[i + 3], + oldGeom.vertices[i + 4], + oldGeom.vertices[i + 5], + + oldGeom.vertices[i + 6], + oldGeom.vertices[i + 7], + oldGeom.vertices[i + 8] + ); + + tesselateTC(tc, + oldGeom.texCoords[i / 9 * 6 + 0], + oldGeom.texCoords[i / 9 * 6 + 1], + + oldGeom.texCoords[i / 9 * 6 + 2], + oldGeom.texCoords[i / 9 * 6 + 3], + + oldGeom.texCoords[i / 9 * 6 + 4], + oldGeom.texCoords[i / 9 * 6 + 5] + + ); + } + } + } + + geom.vertexNormals = norms; + geom.setVertices(vertices); + geom.setTexCoords(tc); + geom.tangents = tangents; + geom.biTangents = biTangents; + return geom; +} + +function update() +{ + let geom = inGeom.get(); + if (!geom) return; + const startTime = CABLES.now(); + + for (let i = 0; i < inTimes.get(); i++) + { + geom = tesselateGeom(geom); + } + + outVertices.set(geom.vertices.length / 3); + + outGeom.set(null); + outGeom.set(geom); +} + + +}; + +Ops.Gl.TesselateGeometry.prototype = new CABLES.Op(); +CABLES.OPS["eb3f0bd8-211c-4336-a3ad-fa31c50d705d"]={f:Ops.Gl.TesselateGeometry,objName:"Ops.Gl.TesselateGeometry"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextMeshMSDF_v2 +// +// ************************************************************** + +Ops.Gl.TextMeshMSDF_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"textmeshsdf_frag":"\nUNI sampler2D tex0;\nUNI sampler2D tex1;\nUNI sampler2D tex2;\nUNI sampler2D tex3;\nIN vec2 texCoord;\nUNI vec4 color;\nUNI vec2 texSize;\n\n#ifdef BORDER\n UNI float borderWidth;\n UNI float borderSmooth;\n UNI vec3 colorBorder;\n#endif\n\n#ifdef TEXTURE_COLOR\nUNI sampler2D texMulColor;\n#endif\n#ifdef TEXTURE_MASK\nUNI sampler2D texMulMask;\n#endif\n\nIN float texIndex;\n\n#ifdef SHADOW\n UNI float shadowWidth;\n#endif\n\n\nfloat median(float r, float g, float b)\n{\n return max(min(r, g), min(max(r, g), b));\n}\n\nvoid main()\n{\n vec4 bgColor=vec4(0.0,0.0,0.0,0.0);\n vec4 fgColor=color;\n float opacity=1.0;\n\n #ifndef SDF\n if(int(texIndex)==0) outColor = texture(tex0, texCoord);\n if(int(texIndex)==1) outColor = texture(tex1, texCoord);\n if(int(texIndex)==2) outColor = texture(tex2, texCoord);\n if(int(texIndex)==3) outColor = texture(tex3, texCoord);\n\n return;\n #endif\n\n\n #ifdef TEXTURE_COLOR\n fgColor.rgb *= texture(texMulColor, vec2(0.0,0.0)).rgb; //todo texcoords from char positioning\n #endif\n #ifdef TEXTURE_MASK\n opacity *= texture(texMulMask, vec2(0.0,0.0)).r; //todo texcoords from char positioning\n #endif\n\n\n #ifdef SHADOW\n vec2 msdfUnit1 = texSize;\n vec2 tcv=vec2(texCoord.x-0.002,texCoord.y-0.002);\n vec3 smpl1;\n if(int(texIndex)==0) smpl1 = texture(tex0, tcv).rgb;\n if(int(texIndex)==1) smpl1 = texture(tex1, tcv).rgb;\n if(int(texIndex)==2) smpl1 = texture(tex2, tcv).rgb;\n if(int(texIndex)==3) smpl1 = texture(tex3, tcv).rgb;\n\n float sigDist1 = median(smpl1.r, smpl1.g, smpl1.b) - 0.001;\n float opacity1 = smoothstep(0.0,0.9,sigDist1*sigDist1);\n outColor = mix(bgColor, vec4(0.0,0.0,0.0,1.0), opacity1);\n #endif\n\n vec2 msdfUnit = 8.0/texSize;\n vec3 smpl;\n\n if(int(texIndex)==0) smpl = texture(tex0, texCoord).rgb;\n if(int(texIndex)==1) smpl = texture(tex1, texCoord).rgb;\n if(int(texIndex)==2) smpl = texture(tex2, texCoord).rgb;\n if(int(texIndex)==3) smpl = texture(tex3, texCoord).rgb;\n\n\n float sigDist = median(smpl.r, smpl.g, smpl.b) - 0.5;\n sigDist *= dot(msdfUnit, 0.5/fwidth(texCoord));\n opacity *= clamp(sigDist + 0.5, 0.0, 1.0);\n\n #ifdef BORDER\n float sigDist2 = median(smpl.r, smpl.g, smpl.b) - 0.01;\n float bw=borderWidth*0.6+0.24;\n float opacity2 = smoothstep(bw-borderSmooth,bw+borderSmooth,sigDist2*sigDist2);\n fgColor=mix(fgColor,vec4(colorBorder,1.0),1.0-opacity2);\n #endif\n\n if(color.a==0.0)discard;\n outColor = mix(outColor, fgColor, opacity*color.a);\n\n}\n\n","textmeshsdf_vert":"UNI sampler2D tex1;\nUNI sampler2D tex2;\nUNI sampler2D tex3;\nUNI sampler2D tex4;\n\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN mat4 instMat;\nIN vec2 attrTexOffsets;\nIN vec2 attrSize;\nIN vec2 attrTcSize;\nIN float attrPage;\n\nOUT vec2 texCoord;\nOUT float texIndex;\n\nconst float mulSize=0.01;\n\nvoid main()\n{\n texCoord=(attrTexOffsets+attrTexCoord*attrTcSize);\n texCoord.y=1.0-texCoord.y;\n\n mat4 instMVMat=instMat;\n vec4 vert=vec4( vPosition, 1. );\n vert.x*=attrSize.x*mulSize;\n vert.y*=attrSize.y*mulSize;\n\n texIndex=attrPage+0.4; // strange ios rounding errors?!\n\n mat4 mvMatrix=viewMatrix * modelMatrix * instMVMat;\n\n gl_Position = projMatrix * mvMatrix * vert;\n}\n",}; +// https://soimy.github.io/msdf-bmfont-xml/ + +// antialiasing: +// https://github.com/Chlumsky/msdfgen/issues/22 + +const + render = op.inTrigger("Render"), + str = op.inString("Text", "cables"), + inFont = op.inDropDown("Font", [], "", true), + scale = op.inFloat("Scale", 0.25), + + letterSpace = op.inFloat("Letter Spacing", 0), + lineHeight = op.inFloat("Line Height", 1), + + align = op.inSwitch("Align", ["Left", "Center", "Right"], "Center"), + valign = op.inSwitch("Vertical Align", ["Zero", "Top", "Middle", "Bottom"], "Middle"), + + r = op.inValueSlider("r", 1), + g = op.inValueSlider("g", 1), + b = op.inValueSlider("b", 1), + a = op.inValueSlider("a", 1), + doSDF = op.inBool("SDF", true), + + inBorder = op.inBool("Border", false), + inBorderWidth = op.inFloatSlider("Border Width", 0.5), + inBorderSmooth = op.inFloatSlider("Smoothness", 0.25), + br = op.inValueSlider("Border r", 1), + bg = op.inValueSlider("Border g", 1), + bb = op.inValueSlider("Border b", 1), + + inShadow = op.inBool("Shadow", false), + + inTexColor = op.inTexture("Texture Color"), + inTexMask = op.inTexture("Texture Mask"), + + inPosArr = op.inArray("Positions"), + inScaleArr = op.inArray("Scalings"), + inRotArr = op.inArray("Rotations"), + + next = op.outTrigger("Next"), + outArr = op.outArray("Positions Original", null, 3), + outLines = op.outNumber("Num Lines"), + + outWidth = op.outNumber("Width"), + outHeight = op.outNumber("Height"), + outStartY = op.outNumber("Start Y"), + outNumChars = op.outNumber("Num Chars"); + +op.setPortGroup("Size", [letterSpace, lineHeight, scale]); +op.setPortGroup("Character Transformations", [inScaleArr, inRotArr, inPosArr]); +op.setPortGroup("Alignment", [align, valign]); +op.setPortGroup("Color", [r, g, b, a, doSDF]); +op.setPortGroup("Border", [br, bg, bb, inBorderSmooth, inBorderWidth, inBorder]); + +r.setUiAttribs({ "colorPick": true }); +br.setUiAttribs({ "colorPick": true }); + +const cgl = op.patch.cgl; +const fontDataVarPrefix = "font_data_"; +const substrLength = fontDataVarPrefix.length; +const alignVec = vec3.create(); +const vScale = vec3.create(); +const shader = new CGL.Shader(cgl, "TextMeshSDF"); + +let fontTexs = null; +let fontData = null; +let fontChars = null; +let needUpdate = true; +let geom = null; +let mesh = null; +let disabled = false; +let valignMode = 1; +let heightAll = 0, widthAll = 0; +let avgHeight = 0; +let minY, maxY, minX, maxX; +let needsUpdateTransmats = true; +let transMats = null; +let offY = 0; + +if (cgl.glVersion == 1) +{ + cgl.gl.getExtension("OES_standard_derivatives"); + shader.enableExtension("GL_OES_standard_derivatives"); +} + +shader.setSource(attachments.textmeshsdf_vert, attachments.textmeshsdf_frag); + +const + uniTex = new CGL.Uniform(shader, "t", "tex0", 0), + uniTex1 = new CGL.Uniform(shader, "t", "tex1", 1), + uniTex2 = new CGL.Uniform(shader, "t", "tex2", 2), + uniTex3 = new CGL.Uniform(shader, "t", "tex3", 3), + uniTexMul = new CGL.Uniform(shader, "t", "texMulColor", 4), + uniTexMulMask = new CGL.Uniform(shader, "t", "texMulMask", 5), + uniColor = new CGL.Uniform(shader, "4f", "color", r, g, b, a), + uniColorBorder = new CGL.Uniform(shader, "3f", "colorBorder", br, bg, bb), + + uniTexSize = new CGL.Uniform(shader, "2f", "texSize", 0, 0), + uniborderSmooth = new CGL.Uniform(shader, "f", "borderSmooth", inBorderSmooth), + uniborderWidth = new CGL.Uniform(shader, "f", "borderWidth", inBorderWidth); + +scale.onChange = updateScale; + +inRotArr.onChange = + inPosArr.onChange = + inScaleArr.onChange = function () { needsUpdateTransmats = true; }; + +inTexColor.onChange = +inTexMask.onChange = +inShadow.onChange = +inBorder.onChange = +doSDF.onChange = + updateDefines; + +updateDefines(); +updateScale(); + +align.onChange = + str.onChange = + letterSpace.onChange = + lineHeight.onChange = + function () + { + needUpdate = true; + }; + +valign.onChange = updateAlign; + +op.patch.addEventListener("variablesChanged", updateFontList); +op.patch.addEventListener("FontLoadedMSDF", updateFontList); + +inFont.onChange = updateFontData; + +updateFontList(); + +function updateDefines() +{ + shader.toggleDefine("SDF", doSDF.get()); + shader.toggleDefine("SHADOW", inShadow.get()); + shader.toggleDefine("BORDER", inBorder.get()); + shader.toggleDefine("TEXTURE_COLOR", inTexColor.isLinked()); + shader.toggleDefine("TEXTURE_MASK", inTexMask.isLinked()); + + br.setUiAttribs({ "greyout": !inBorder.get() }); + bg.setUiAttribs({ "greyout": !inBorder.get() }); + bb.setUiAttribs({ "greyout": !inBorder.get() }); + inBorderSmooth.setUiAttribs({ "greyout": !inBorder.get() }); + inBorderWidth.setUiAttribs({ "greyout": !inBorder.get() }); +} + +function updateFontData() +{ + updateFontList(); + const varname = fontDataVarPrefix + inFont.get(); + + fontData = null; + fontTexs = null; + fontChars = {}; + + const dataVar = op.patch.getVar(varname); + + if (!dataVar || !dataVar.getValue()) + { + fontData = null; + + op.warn("no varname", varname); + return; + } + + fontData = dataVar.getValue().data; + + if (!fontData) + { + return; + } + + const basename = dataVar.getValue().basename; + + const textVar = op.patch.getVar("font_tex_" + basename); + if (!textVar) + { + fontTexs = null; + fontData = null; + return; + } + + fontTexs = textVar.getValue(); + + for (let i = 0; i < fontData.chars.length; i++) fontChars[fontData.chars[i].char] = fontData.chars[i]; + needUpdate = true; +} + +function updateFontList() +{ + const vars = op.patch.getVars(); + const names = ["..."]; + + for (const i in vars) + if (vars[i].type == "fontData") + names.push(i.substr(substrLength)); + + inFont.uiAttribs.values = names; +} + +function updateScale() +{ + const s = scale.get(); + vec3.set(vScale, s, s, s); + + vec3.set(alignVec, 0, offY * s, 0); + + outWidth.set(widthAll * s); + outHeight.set(heightAll * s); + + outStartY.set((maxY + offY) * s); +} + +function updateAlign() +{ + if (minX == undefined) return; + if (valign.get() == "Top") valignMode = 0; + else if (valign.get() == "Middle") valignMode = 1; + else if (valign.get() == "Bottom") valignMode = 2; + else if (valign.get() == "Zero") valignMode = 3; + + offY = 0; + widthAll = (Math.abs(minX - maxX)); + heightAll = (Math.abs(minY - maxY)); + + if (valignMode === 1) offY = heightAll / 2; + else if (valignMode === 2) offY = heightAll; + + if (valignMode != 0)offY -= avgHeight; + + updateScale(); +} + +function buildTransMats() +{ + needsUpdateTransmats = false; + + // if(!( inPosArr.get() || inScaleArr.get() || inRotArr.get())) + // { + // transMats=null; + // return; + // } + + const transformations = []; + const translates = inPosArr.get() || outArr.get(); + const scales = inScaleArr.get(); + const rots = inRotArr.get(); + + for (let i = 0; i < mesh.numInstances; i++) + { + const m = mat4.create(); + mat4.translate(m, m, [translates[i * 3 + 0], translates[i * 3 + 1], translates[i * 3 + 2]]); + + if (scales) mat4.scale(m, m, [scales[i * 3 + 0], scales[i * 3 + 1], scales[i * 3 + 2]]); + + if (rots) + { + mat4.rotateX(m, m, rots[i * 3 + 0] * CGL.DEG2RAD); + mat4.rotateY(m, m, rots[i * 3 + 1] * CGL.DEG2RAD); + mat4.rotateZ(m, m, rots[i * 3 + 2] * CGL.DEG2RAD); + } + + transformations.push(Array.prototype.slice.call(m)); + } + + transMats = [].concat.apply([], transformations); +} + +render.onTriggered = function () +{ + if (!fontData) + { + op.setUiError("nodata", "No font data!"); + op.setUiError("msdfhint", "Use the FontMSDF op to create font and texture.", 0); + updateFontData(); + next.trigger(); + return; + } + if (!fontTexs) + { + op.setUiError("nodata", "No font texture"); + op.setUiError("msdfhint", "Use the FontMSDF op to create font and texture.", 0); + updateFontData(); + next.trigger(); + return; + } + + op.setUiError("nodata", null); + op.setUiError("msdfhint", null); + + if (needUpdate) + { + generateMesh(); + needUpdate = false; + } + + if (mesh && mesh.numInstances > 0) + { + // cgl.pushBlendMode(CGL.BLEND_NORMAL, true); + cgl.pushShader(shader); + + if (fontTexs[0]) uniTexSize.setValue([fontTexs[0].width, fontTexs[0].height]); + + if (fontTexs[0]) cgl.setTexture(0, fontTexs[0].tex); + else cgl.setTexture(0, CGL.Texture.getEmptyTexture(cgl).tex); + + if (fontTexs[1])cgl.setTexture(1, fontTexs[1].tex); + else cgl.setTexture(1, CGL.Texture.getEmptyTexture(cgl).tex); + + if (fontTexs[2])cgl.setTexture(2, fontTexs[2].tex); + else cgl.setTexture(2, CGL.Texture.getEmptyTexture(cgl).tex); + + if (fontTexs[3])cgl.setTexture(3, fontTexs[3].tex); + else cgl.setTexture(3, CGL.Texture.getEmptyTexture(cgl).tex); + + if (inTexColor.get()) cgl.setTexture(4, inTexColor.get().tex); + if (inTexMask.get()) cgl.setTexture(5, inTexMask.get().tex); + + cgl.pushModelMatrix(); + mat4.translate(cgl.mMatrix, cgl.mMatrix, alignVec); + + if (needsUpdateTransmats) buildTransMats(); + if (transMats) mesh.setAttribute("instMat", new Float32Array(transMats), 16, { "instanced": true }); + if (cgl.getShader())cgl.getShader().define("INSTANCING"); + + if (!disabled) + { + mat4.scale(cgl.mMatrix, cgl.mMatrix, vScale); + + mesh.render(cgl.getShader()); + } + + cgl.popModelMatrix(); + + cgl.setTexture(0, null); + cgl.popShader(); + // cgl.popBlendMode(); + } + + next.trigger(); +}; + +function getChar(chStr) +{ + return fontChars[String(chStr)] || fontChars["?"] || fontChars._ || fontChars.X; +} + +function generateMesh() +{ + outArr.set(null); + + if (!fontData || !fontChars) + { + outNumChars.set(0); + return; + } + + const theString = String(str.get() + ""); + + if (!geom) + { + geom = new CGL.Geometry("textmesh"); + + geom.vertices = [ + 0.5, 0.5, 0.0, + -0.5, 0.5, 0.0, + 0.5, -0.5, 0.0, + -0.5, -0.5, 0.0 + ]; + + geom.normals = [ + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0 + ]; + + geom.texCoords = new Float32Array([ + 1.0, 0.0, + 0.0, 0.0, + 1.0, 1.0, + 0.0, 1.0 + ]); + + geom.verticesIndices = [ + 0, 1, 2, + 3, 1, 2 + ]; + } + + if (mesh)mesh.dispose(); + mesh = new CGL.Mesh(cgl, geom); + + const strings = (theString).split("\n"); + const transformations = []; + const tcOffsets = []; + const sizes = []; + const texPos = []; + const tcSizes = []; + const pages = []; + let charCounter = 0; + const arrPositions = []; + + const mulSize = 0.01; + + outLines.set(strings.length); + minY = 99999; + maxY = -99999; + minX = 99999; + maxX = -99999; + + avgHeight = 0; + + for (let i = 0; i < fontData.chars.length; i++) + { + if (fontData.chars[i].height) avgHeight += fontData.chars[i].height; + } + avgHeight /= fontData.chars.length; + avgHeight *= mulSize; + + for (let s = 0; s < strings.length; s++) + { + const txt = strings[s]; + const numChars = txt.length; + let lineWidth = 0; + + for (let i = 0; i < numChars; i++) + { + const chStr = txt.substring(i, i + 1); + const char = getChar(chStr); + if (char) lineWidth += char.xadvance * mulSize + letterSpace.get(); + } + + let pos = 0; + if (align.get() == "Right") pos -= lineWidth; + else if (align.get() == "Center") pos -= lineWidth / 2; + + for (let i = 0; i < numChars; i++) + { + const m = mat4.create(); + + const chStr = txt.substring(i, i + 1); + const char = getChar(chStr); + + if (!char) continue; + + pages.push(char.page || 0); + sizes.push(char.width, char.height); + + tcOffsets.push(char.x / fontData.common.scaleW, (char.y / fontData.common.scaleH)); + + const charWidth = char.width / fontData.common.scaleW; + const charHeight = char.height / fontData.common.scaleH; + const charOffsetY = (char.yoffset / fontData.common.scaleH); + const charOffsetX = char.xoffset / fontData.common.scaleW; + + if (chStr == " ") tcSizes.push(0, 0); + else tcSizes.push(charWidth, charHeight); + + mat4.identity(m); + + let adv = (char.xadvance / 2) * mulSize; + pos += adv; + + const x = pos + (char.xoffset / 2) * mulSize; + const y = (s * -lineHeight.get()) + (avgHeight) - (mulSize * (char.yoffset + char.height / 2)); + + minX = Math.min(x - charWidth, minX); + maxX = Math.max(x + charWidth, maxX); + minY = Math.min(y - charHeight - avgHeight / 2, minY); + maxY = Math.max(y + charHeight + avgHeight / 2, maxY); + + mat4.translate(m, m, [x, y, 0]); + arrPositions.push(x, y, 0); + + adv = (char.xadvance / 2) * mulSize + letterSpace.get(); + + pos += adv; + + minX = Math.min(pos - charWidth, minX); + maxX = Math.max(pos + charWidth, maxX); + + transformations.push(Array.prototype.slice.call(m)); + + charCounter++; + } + } + + transMats = [].concat.apply([], transformations); + + disabled = false; + if (transMats.length == 0) disabled = true; + + mesh.numInstances = transMats.length / 16; + outNumChars.set(mesh.numInstances); + + if (mesh.numInstances == 0) + { + disabled = true; + return; + } + + mesh.setAttribute("instMat", new Float32Array(transMats), 16, { "instanced": true }); + mesh.setAttribute("attrTexOffsets", new Float32Array(tcOffsets), 2, { "instanced": true }); + mesh.setAttribute("attrTcSize", new Float32Array(tcSizes), 2, { "instanced": true }); + mesh.setAttribute("attrSize", new Float32Array(sizes), 2, { "instanced": true }); + mesh.setAttribute("attrPage", new Float32Array(pages), 1, { "instanced": true }); + + // updateAlign(); + updateAlign(); + needsUpdateTransmats = true; + outArr.set(arrPositions); +} + + +}; + +Ops.Gl.TextMeshMSDF_v2.prototype = new CABLES.Op(); +CABLES.OPS["b5c99363-a749-4040-884b-66f91294bcad"]={f:Ops.Gl.TextMeshMSDF_v2,objName:"Ops.Gl.TextMeshMSDF_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Texture2ColorArray +// +// ************************************************************** + +Ops.Gl.Texture2ColorArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + cgl = op.patch.cgl, + pUpdate = op.inTrigger("update"), + tex = op.inObject("texture"), + inNormalize = op.inSwitch("Normalize", ["0-255", "0-1", "-1-1"], "0-255"), + outTrigger = op.outTrigger("trigger"), + outColors = op.outArray("Colors"), + outIsFloatingPoint = op.outBoolNum("Floating Point"); + +let + fb = null, + pixelData = null, + texChanged = false; +tex.onChange = function () { texChanged = true; }; + +op.toWorkPortsNeedToBeLinked(tex, outColors); + +let isFloatingPoint = false; +let channelType = op.patch.cgl.gl.UNSIGNED_BYTE; + +let convertedPixelData = null; + +let lastFloatingPoint = false; +let lastWidth = 0; +let lastHeight = 0; + +pUpdate.onTriggered = function () +{ + const realTexture = tex.get(), gl = cgl.gl; + + if (!realTexture) return; + if (!fb) fb = gl.createFramebuffer(); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + let channels = gl.RGBA; + // channels = gl.R; + + let numChannels = 4; + // numChannels = 1; + + if (texChanged) + { + gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, realTexture.tex, 0 + ); + + isFloatingPoint = realTexture.textureType == CGL.Texture.TYPE_FLOAT; + + if (isFloatingPoint) channelType = gl.FLOAT; + else channelType = gl.UNSIGNED_BYTE; + + outIsFloatingPoint.set(isFloatingPoint); + + if ( + lastFloatingPoint != isFloatingPoint || + lastWidth != realTexture.width || + lastHeight != realTexture.height) + { + const size = realTexture.width * realTexture.height * numChannels; + if (isFloatingPoint) pixelData = new Float32Array(size); + else pixelData = new Uint8Array(size); + + lastFloatingPoint = isFloatingPoint; + lastWidth = realTexture.width; + lastHeight = realTexture.height; + } + + texChanged = false; + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + gl.readPixels( + 0, 0, + realTexture.width, + realTexture.height, + channels, + channelType, + pixelData + ); + + // if (!convertedPixelData || convertedPixelData.length != pixelData.length) convertedPixelData = new Float32Array(pixelData.length); + // for (let i = 0; i < pixelData.length; i++) + // { + // convertedPixelData[i] = pixelData[i]; + // } + if (inNormalize.get() == "0-1") + { + if (!convertedPixelData || convertedPixelData.length != pixelData.length) convertedPixelData = new Float32Array(pixelData.length); + for (let i = 0; i < pixelData.length; i++) + { + convertedPixelData[i] = pixelData[i] / 255; + } + } + if (inNormalize.get() == "-1-1") + { + if (!convertedPixelData || convertedPixelData.length != pixelData.length) convertedPixelData = new Float32Array(pixelData.length); + + for (let i = 0; i < pixelData.length; i++) + { + convertedPixelData[i] = ((pixelData[i] - 128) * 2.0) / 255; + } + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + outColors.set(null); + outColors.set(convertedPixelData || pixelData); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + outTrigger.trigger(); +}; + + +}; + +Ops.Gl.Texture2ColorArray.prototype = new CABLES.Op(); +CABLES.OPS["51dbc04e-f970-44ca-9cb0-39596ac4d212"]={f:Ops.Gl.Texture2ColorArray,objName:"Ops.Gl.Texture2ColorArray"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureArray +// +// ************************************************************** + +Ops.Gl.TextureArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + outArr = op.outArray("Array"), + outCount = op.outNumber("Count"); + +outArr.ignoreValueSerialize = true; + +const num = 15; +let texturePorts = []; +let arr = []; + +function rebuild() +{ + let i = 0; + let count = 0; + for (i = 0; i < texturePorts.length; i++) if (texturePorts[i].isLinked()) count++; + + arr.length = count; + + count = 0; + for (i = 0; i < texturePorts.length; i++) + { + if (texturePorts[i].isLinked()) + { + arr[count] = texturePorts[i].get(); + count++; + } + } + + outArr.set(null); + outArr.set(arr); + outCount.set(count); +} + +for (let i = 0; i < num; i++) +{ + let p = op.inTexture("Texture " + i); + p.onLinkChanged = rebuild; + p.onChange = rebuild; + texturePorts.push(p); +} + + +}; + +Ops.Gl.TextureArray.prototype = new CABLES.Op(); +CABLES.OPS["fac012e2-43c8-4e21-85ac-189fe85d5723"]={f:Ops.Gl.TextureArray,objName:"Ops.Gl.TextureArray"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureArrayLoaderFromArray +// +// ************************************************************** + +Ops.Gl.TextureArrayLoaderFromArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + filenames = op.inArray("urls"), + tfilter = op.inValueSelect("filter", ["nearest", "linear", "mipmap"]), + wrap = op.inValueSelect("wrap", ["repeat", "mirrored repeat", "clamp to edge"], "clamp to edge"), + flip = op.addInPort(new CABLES.Port(op, "flip", CABLES.OP_PORT_TYPE_VALUE, { "display": "bool" })), + unpackAlpha = op.addInPort(new CABLES.Port(op, "unpackPreMultipliedAlpha", CABLES.OP_PORT_TYPE_VALUE, { "display": "bool" })), + inCaching = op.inBool("Caching", false), + inPatchAsset = op.inBool("Asset in patch", false), + arrOut = op.outArray("TextureArray"), + width = op.outNumber("width"), + height = op.outNumber("height"), + loading = op.outBoolNum("loading"), + ratio = op.outNumber("Aspect Ratio"); + +flip.set(false); +unpackAlpha.set(false); + +const cgl = op.patch.cgl; +let cgl_filter = 0; +let cgl_wrap = 0; +let loadingId = null; +const arr = []; +arrOut.set(arr); + +inPatchAsset.onChange = +flip.onChange = function () { reload(); }; +filenames.onChange = reload; + +tfilter.onChange = onFilterChange; +wrap.onChange = onWrapChange; +unpackAlpha.onChange = function () { reload(); }; + +let timedLoader = 0; + +const setTempTexture = function () +{ + const t = CGL.Texture.getTempTexture(cgl); + // textureOut.set(t); +}; + +function reload(nocache) +{ + clearTimeout(timedLoader); + timedLoader = setTimeout(function () + { + realReload(nocache); + }, 30); +} + +function loadImage(_i, _url, nocache, cb) +{ + let url = _url; + const i = _i; + if (!url) return; + // url=url.replace("XXX",i); + + if (inPatchAsset.get()) + { + let patchId = null; + if (op.storage && op.storage.blueprint && op.storage.blueprint.patchId) + { + patchId = op.storage.blueprint.patchId; + } + url = op.patch.getAssetPath(patchId) + url; + } + + url = op.patch.getFilePath(url); + if (!inCaching.get()) + if (nocache)url += "?rnd=" + CABLES.generateUUID(); + + // if((filename.get() && filename.get().length>1)) + { + loading.set(true); + + let tex = CGL.Texture.load(cgl, url, + function (err) + { + if (err) + { + setTempTexture(); + const errMsg = "could not load texture \"" + url + "\""; + op.uiAttr({ "error": errMsg }); + op.warn("[TextureArrayLoader] " + errMsg); + if (cb)cb(); + return; + } + else op.uiAttr({ "error": null }); + // textureOut.set(tex); + width.set(tex.width); + height.set(tex.height); + ratio.set(tex.width / tex.height); + + arr[i] = tex; + + arrOut.set(null); + arrOut.set(arr); + if (cb)cb(); + }, { + "wrap": cgl_wrap, + "flip": flip.get(), + "unpackAlpha": unpackAlpha.get(), + "filter": cgl_filter + }); + + loading.set(false); + } +} + +function realReload(nocache) +{ + const files = filenames.get(); + + if (!files || files.length == 0) return; + + if (loadingId)cgl.patch.loading.finished(loadingId); + loadingId = cgl.patch.loading.start("texturearray", CABLES.uuid()); + + for (let i = 0; i < files.length; i++) + { + arr[i] = CGL.Texture.getEmptyTexture(cgl); + let cb = null; + if (i == files.length - 1)cb = function () + { + cgl.patch.loading.finished(loadingId); + }; + + if (!files[i]) { if (cb) cb(); } + else loadImage(i, files[i], nocache, cb); + } +} + +function onFilterChange() +{ + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + + reload(); +} + +function onWrapChange() +{ + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + reload(); +} + +op.onFileChanged = function (fn) +{ + +}; + +tfilter.set("linear"); +wrap.set("repeat"); + + +}; + +Ops.Gl.TextureArrayLoaderFromArray.prototype = new CABLES.Op(); +CABLES.OPS["febd6cdf-e049-4aed-be34-d7e370b90e03"]={f:Ops.Gl.TextureArrayLoaderFromArray,objName:"Ops.Gl.TextureArrayLoaderFromArray"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureArrayLoader_v2 +// +// ************************************************************** + +Ops.Gl.TextureArrayLoader_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const USE_LEFT_PAD_DEFAULT = false; + +const filename = op.inString("Url"); +const leftPadFilename = op.inBool("Left Pad", USE_LEFT_PAD_DEFAULT); +const numberLengthPort = op.inInt("Num Digits", 3); +numberLengthPort.setUiAttribs({ "hidePort": !USE_LEFT_PAD_DEFAULT, "greyout": !USE_LEFT_PAD_DEFAULT }); + +const indexStart = op.inInt("Index Start"); +const indexEnd = op.inInt("Index End"); + +const tfilter = op.inValueSelect("filter", ["nearest", "linear", "mipmap"]); +const wrap = op.inValueSelect("wrap", ["repeat", "mirrored repeat", "clamp to edge"], "clamp to edge"); +const flip = op.inBool("Flip", false); +const unpackAlpha = op.inBool("unpackPreMultipliedAlpha", false); + +const arrOut = op.outArray("TextureArray"); + +const width = op.outNumber("Width"); +const height = op.outNumber("Height"); +const loading = op.outBool("Loading"); +const ratio = op.outNumber("Aspect Ratio"); + +indexEnd.set(10); +flip.set(false); +unpackAlpha.set(false); + +const cgl = op.patch.cgl; +let cgl_filter = 0; +let cgl_wrap = 0; + +const arr = []; +arrOut.set(arr); + +flip.onChange = function () { reload(); }; +filename.onChange = reload; + +tfilter.onChange = onFilterChange; +wrap.onChange = onWrapChange; +unpackAlpha.onChange = function () { reload(); }; + +leftPadFilename.onChange = setNumberLengthPortVisibility; + +let timedLoader = 0; + +function setNumberLengthPortVisibility() +{ + const doLeftPad = leftPadFilename.get(); + numberLengthPort.setUiAttribs({ "hidePort": !doLeftPad, "greyout": !doLeftPad }); +} + +const setTempTexture = function () +{ + const t = CGL.Texture.getTempTexture(cgl); + // textureOut.set(t); +}; + +function reload(nocache) +{ + clearTimeout(timedLoader); + timedLoader = setTimeout(function () + { + realReload(nocache); + }, 30); +} + +const REPLACE_CHARACTER = "X"; + +function pad(value, length) +{ + return (value.toString().length < length) ? pad("0" + value, length) : value; +} + +function loadImage(i, nocache) +{ + let url = filename.get(); + if (!url) return; + const firstXIndex = url.indexOf(REPLACE_CHARACTER); + const lastXIndex = url.lastIndexOf(REPLACE_CHARACTER); + if (firstXIndex === -1) { return; } + const replaceString = url.substring(firstXIndex, lastXIndex + 1); + let numberString = i; + if (leftPadFilename.get()) + { + numberString = pad(i, numberLengthPort.get()); + } + url = url.replace(replaceString, numberString); + url = op.patch.getFilePath(url); + + if (nocache)url += "?rnd=" + CABLES.generateUUID(); + + if ((filename.get() && filename.get().length > 1)) + { + loading.set(1); + + var tex = CGL.Texture.load(cgl, url, + function (err) + { + if (err) + { + setTempTexture(); + op.uiAttr({ "error": "could not load texture \"" + filename.get() + "\"" }); + return; + } + else op.uiAttr({ "error": null }); + // textureOut.set(tex); + width.set(tex.width); + height.set(tex.height); + ratio.set(tex.width / tex.height); + + arr[i - parseInt(indexStart.get())] = tex; + if (!tex.isPowerOfTwo()) op.uiAttr( + { + "hint": "texture dimensions not power of two! - texture filtering will not work.", + "warning": null + }); + else op.uiAttr( + { + "hint": null, + "warning": null + }); + + // textureOut.set(null); + // textureOut.set(tex); + + // tex.printInfo(); + arrOut.set(null); + arrOut.set(arr); + }, { + "wrap": cgl_wrap, + "flip": flip.get(), + "unpackAlpha": unpackAlpha.get(), + "filter": cgl_filter + }); + + // textureOut.set(null); + // textureOut.set(tex); + + // if(!textureOut.get() && nocache) + // { + // } + loading.set(0); + } + else + { + setTempTexture(); + } +} + +function realReload(nocache) +{ + for (var i = 0; i < arr.length; i++) + { + if (arr[i]) + { + arr[i].delete(); + } + } + arr.length = 0; + for (var i = Math.floor(indexStart.get()); i <= Math.floor(indexEnd.get()); i++) + { + loadImage(i, nocache); + } +} + +function onFilterChange() +{ + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + + reload(); +} + +function onWrapChange() +{ + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + reload(); +} + +op.onFileChanged = function (fn) +{ + // if(filename.get() && filename.get().indexOf(fn)>-1) + // { + // textureOut.set(null); + // textureOut.set(CGL.Texture.getTempTexture(cgl)); + + // realReload(true); + // } +}; + +tfilter.set("linear"); +wrap.set("repeat"); + + +}; + +Ops.Gl.TextureArrayLoader_v2.prototype = new CABLES.Op(); +CABLES.OPS["4bbb57ce-3c7b-49c1-84fd-2b6a27ff3afa"]={f:Ops.Gl.TextureArrayLoader_v2,objName:"Ops.Gl.TextureArrayLoader_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureColorPick +// +// ************************************************************** + +Ops.Gl.TextureColorPick = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + cgl = op.patch.cgl, + pUpdate = op.inTrigger("update"), + // inNormalize = op.inSwitch("Coordinate", ["Pixel", "Normalized"], "Pixel"), + inX = op.inInt("X", 0), + inY = op.inInt("Y", 0), + tex = op.inObject("texture"), + outTrigger = op.outTrigger("trigger"), + outR = op.outNumber("Red"), + outG = op.outNumber("Green"), + outB = op.outNumber("Blue"), + outA = op.outNumber("Alpha"); + +let + fb = null, + pixelData = null, + texChanged = false; + +tex.onChange = function () { texChanged = true; }; + +let isFloatingPoint = false; +let channelType = op.patch.cgl.gl.UNSIGNED_BYTE; + +pUpdate.onTriggered = function () +{ + const realTexture = tex.get(), gl = cgl.gl; + + if (!realTexture) return; + if (!fb) fb = gl.createFramebuffer(); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + if (texChanged) + { + gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, realTexture.tex, 0 + ); + + isFloatingPoint = realTexture.textureType == CGL.Texture.TYPE_FLOAT; + + if (isFloatingPoint) channelType = gl.FLOAT; + else channelType = gl.UNSIGNED_BYTE; + + const size = 4 * 4; + if (isFloatingPoint) pixelData = new Float32Array(size); + else pixelData = new Uint8Array(size); + + texChanged = false; + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + gl.readPixels( + inX.get(), inY.get(), + 1, 1, + gl.RGBA, + channelType, + pixelData + ); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + if (isFloatingPoint) + { + outR.set(pixelData[0]); + outG.set(pixelData[1]); + outB.set(pixelData[2]); + outA.set(pixelData[3]); + } + else + { + outR.set(pixelData[0] / 255); + outG.set(pixelData[1] / 255); + outB.set(pixelData[2] / 255); + outA.set(pixelData[3] / 255); + } + + outTrigger.trigger(); +}; + + +}; + +Ops.Gl.TextureColorPick.prototype = new CABLES.Op(); +CABLES.OPS["060e740a-2432-4685-a081-5df6ab7418e4"]={f:Ops.Gl.TextureColorPick,objName:"Ops.Gl.TextureColorPick"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffect.RgbeToFloat32Texture +// +// ************************************************************** + +Ops.Gl.TextureEffect.RgbeToFloat32Texture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rgbe2fp_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\n\n\nvec3 decodeRGBE8(vec4 rgbe)\n{\n vec3 vDecoded = rgbe.rgb * pow(2.0, rgbe.a * 255.0-128.0);\n return vDecoded;\n}\n\nvoid main()\n{\n highp vec4 col=vec4(\n decodeRGBE8(\n texture(tex,texCoord)\n ), 1.0);\n\n\n outColor= col;\n}",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.rgbe2fp_frag); + +// const highEdgeUniform = new CGL.Uniform(shader, "f", "highEdge", inHighEdge); + +// CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + +// const +// exec = op.inTrigger("Execute"), +// inTex = op.inTexture("RGBE Texture"), +// next = op.outTrigger("Next"), +// outFpTex = op.outTexture("HDR Texture"), +// tfilter = op.inSwitch("Filter", ["nearest", "linear"], "linear"), +// twrap = op.inValueSelect("Wrap", ["clamp to edge", "repeat", "mirrored repeat"], "repeat"); + +// let tc = null; + +// twrap.onChange = +// tfilter.onChange = init; +// init(); + +// function init() +// { +// let wrap = CGL.Texture.WRAP_REPEAT; +// if (twrap.get() == "mirrored repeat") wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; +// if (twrap.get() == "clamp to edge") wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + +// let filter = CGL.Texture.FILTER_NEAREST; +// if (tfilter.get() == "linear") filter = CGL.Texture.FILTER_LINEAR; + +// if (tc)tc.dispose(); +// tc = new CGL.CopyTexture(op.patch.cgl, "rgbe2hdr", +// { +// "shader": attachments.rgbe2fp_frag, +// "isFloatingPointTexture": true, +// "filter": filter, +// "wrap": wrap +// }); +// } + +// outFpTex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + +// exec.onTriggered = () => +// { +// if (!inTex.get()) return; + +// outFpTex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); +// outFpTex.set(tc.copy(inTex.get())); + +// next.trigger(); +// }; + + +}; + +Ops.Gl.TextureEffect.RgbeToFloat32Texture.prototype = new CABLES.Op(); +CABLES.OPS["35cc1b97-3deb-4cbb-91b5-1658697d4460"]={f:Ops.Gl.TextureEffect.RgbeToFloat32Texture,objName:"Ops.Gl.TextureEffect.RgbeToFloat32Texture"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Alpha +// +// ************************************************************** + +Ops.Gl.TextureEffects.Alpha = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"clearAlpha_frag":"\n// void main()\n// {\n// outColor.a=0.0;\n// }\n\nIN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\n\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n\n outColor=base;\n\n #ifdef METH_NORM\n outColor.a=amount;\n #endif\n #ifdef METH_ADD\n outColor.a+=amount;\n #endif\n #ifdef METH_SUB\n outColor.a-=amount;\n #endif\n #ifdef METH_MUL\n outColor.a*=amount;\n #endif\n\n #ifdef DO_CLAMP\n outColor.a=clamp(0.0,1.0,outColor.a);\n #endif\n\n}\n",}; +const + render = op.inTrigger("Render"), + amount = op.inValueSlider("Amount", 1), + meth = op.inSwitch("Method", ["Set", "Add", "Sub", "Mul"], "Set"), + clamp = op.inBool("Clamp", true), + trigger = op.outTrigger("Next"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "Alpha"); +const TEX_SLOT = 0; + +shader.setSource(shader.getDefaultVertexShader(), attachments.clearAlpha_frag || ""); + +const uniformAmount = new CGL.Uniform(shader, "f", "amount", amount); +const textureUniform = new CGL.Uniform(shader, "t", "tex", TEX_SLOT); + +clamp.onChange = + meth.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("METH_NORM", meth.get() == "Set"); + shader.toggleDefine("METH_ADD", meth.get() == "Add"); + shader.toggleDefine("METH_SUB", meth.get() == "Sub"); + shader.toggleDefine("METH_MUL", meth.get() == "Mul"); + shader.toggleDefine("DO_CLAMP", clamp.get()); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(TEX_SLOT, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Alpha.prototype = new CABLES.Op(); +CABLES.OPS["131687e0-77f5-4fd7-be57-864aa6559418"]={f:Ops.Gl.TextureEffects.Alpha,objName:"Ops.Gl.TextureEffects.Alpha"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.AlphaMask_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.AlphaMask_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"alphamask_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D image;\nUNI float amount;\n\nvoid main()\n{\n vec4 col=vec4(0.0,0.0,0.0,1.0);\n col=texture(tex,texCoord);\n\n#ifdef USE_TEXTURE\n float newAlpha=0.0;\n\n #ifdef FROM_RED\n newAlpha=texture(image,texCoord).r;\n #endif\n\n #ifdef FROM_GREEN\n newAlpha=texture(image,texCoord).g;\n #endif\n\n #ifdef FROM_BLUE\n newAlpha=texture(image,texCoord).b;\n #endif\n\n #ifdef FROM_ALPHA\n newAlpha=texture(image,texCoord).a;\n #endif\n\n #ifdef FROM_LUMINANCE\n float gray = dot(vec3(0.2126,0.7152,0.0722), texture(image,texCoord).rgb );\n newAlpha=gray;\n #endif\n\n\n newAlpha*=amount;\n\n #ifdef INVERT\n newAlpha=1.0-newAlpha;\n #endif\n\n #ifdef METH_OVERRIDE\n col.a=newAlpha;\n #endif\n #ifdef METH_ADD\n col.a+=newAlpha;\n #endif\n #ifdef METH_MUL\n col.a*=newAlpha;\n #endif\n\n#endif\n#ifndef USE_TEXTURE\n col.a*=amount;\n // col.g=1.0;\n#endif\n\n outColor= col;\n}",}; +const + render = op.inTrigger("render"), + inAmount = op.inFloatSlider("Amount", 1), + inMethod = op.inSwitch("Method", ["Override", "Mul", "Add"], "Override"), + inInvert = op.inBool("Invert", false), + image = op.inTexture("image"), + next = op.outTrigger("trigger"); + +const cgl = this.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); +shader.setSource(shader.getDefaultVertexShader(), attachments.alphamask_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const textureDisplaceUniform = new CGL.Uniform(shader, "t", "image", 1); +const amountUniform = new CGL.Uniform(shader, "f", "amount", inAmount); + +// const method = this.addInPort(new CABLES.Port(this, "method", CABLES.OP_PORT_TYPE_VALUE, { "display": "dropdown", "values": ["luminance", "image alpha", "red", "green", "blue"] })); +const method = op.inDropDown("method", ["luminance", "image alpha", "red", "green", "blue"], "luminance"); + +image.onChange = +inMethod.onChange = +inInvert.onChange = +method.onChange = function () +{ + shader.toggleDefine("FROM_LUMINANCE", method.get() == "luminance"); + shader.toggleDefine("FROM_ALPHA", method.get() == "image alpha"); + shader.toggleDefine("FROM_RED", method.get() == "red"); + shader.toggleDefine("FROM_GREEN", method.get() == "green"); + shader.toggleDefine("FROM_BLUR", method.get() == "blue"); + shader.toggleDefine("USE_TEXTURE", image.get()); + + shader.toggleDefine("INVERT", inInvert.get()); + shader.toggleDefine("METH_MUL", inMethod.get() == "Mul"); + shader.toggleDefine("METH_ADD", inMethod.get() == "Add"); + shader.toggleDefine("METH_OVERRIDE", inMethod.get() == "Override"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + // if (!image.get()) return next.trigger(); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (image.get() && image.get().tex) cgl.setTexture(1, image.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + next.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.AlphaMask_v2.prototype = new CABLES.Op(); +CABLES.OPS["a971aaa4-565f-47cf-8a4f-61a3a4378183"]={f:Ops.Gl.TextureEffects.AlphaMask_v2,objName:"Ops.Gl.TextureEffects.AlphaMask_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.BarrelDistortion_v3 +// +// ************************************************************** + +Ops.Gl.TextureEffects.BarrelDistortion_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"barreldistort_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float intensity;\n\n{{CGL.BLENDMODES3}}\n\n// adapted from https://www.shadertoy.com/view/MlSXR3\n\nvec2 brownConradyDistortion(vec2 uv)\n{\n// positive values of K1 give barrel distortion, negative give pincushion\n float barrelDistortion1 = intensity*10.; // K1 in text books\n float barrelDistortion2 = 0.; // K2 in text books\n float r2 = uv.x*uv.x + uv.y*uv.y;\n uv *= 1.0 + barrelDistortion1 * r2 + barrelDistortion2 * r2 * r2;\n\n // tangential distortion (due to off center lens elements)\n // is not modeled in this function, but if it was, the terms would go here\n return uv;\n}\n\nvoid main()\n{\n vec2 tc=brownConradyDistortion(texCoord-0.5)+0.5;\n vec4 col=texture(tex,texCoord);\n vec4 base=texture(tex,tc);\n\n col.a=0.0;\n outColor=cgl_blendPixel(col,base,amount);\n}",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1.0), + intensity = op.inValue("Intensity", 10.0), + trigger = op.outTrigger("Trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.barreldistort_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniintensity = new CGL.Uniform(shader, "f", "intensity", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + let texture = cgl.currentTextureEffect.getCurrentSourceTexture(); + + uniintensity.setValue(intensity.get() * (1 / texture.width)); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, texture.tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.BarrelDistortion_v3.prototype = new CABLES.Op(); +CABLES.OPS["d5efa9e4-d552-42f8-a345-d49e5e861602"]={f:Ops.Gl.TextureEffects.BarrelDistortion_v3,objName:"Ops.Gl.TextureEffects.BarrelDistortion_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Blur +// +// ************************************************************** + +Ops.Gl.TextureEffects.Blur = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"blur_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float dirX;\nUNI float dirY;\nUNI float amount;\n\n#ifdef HAS_MASK\n UNI sampler2D imageMask;\n#endif\n\nfloat random(vec3 scale, float seed)\n{\n return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\n}\n\nvoid main()\n{\n vec4 color = vec4(0.0);\n float total = 0.0;\n\n float am=amount;\n #ifdef HAS_MASK\n am=amount*texture(imageMask,texCoord).r;\n if(am<=0.02)\n {\n outColor=texture(tex, texCoord);\n return;\n }\n #endif\n\n vec2 delta=vec2(dirX*am*0.01,dirY*am*0.01);\n\n\n float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);\n\n #ifdef MOBILE\n offset = 0.1;\n #endif\n\n #if defined(FASTBLUR) && !defined(MOBILE)\n const float range=5.0;\n #else\n const float range=20.0;\n #endif\n\n for (float t = -range; t <= range; t+=1.0)\n {\n float percent = (t + offset - 0.5) / range;\n float weight = 1.0 - abs(percent);\n vec4 smpl = texture(tex, texCoord + delta * percent);\n\n smpl.rgb *= smpl.a;\n\n color += smpl * weight;\n total += weight;\n }\n\n outColor= color / total;\n\n outColor.rgb /= outColor.a + 0.00001;\n\n\n\n}\n",}; +const render = op.inTrigger("render"); +const trigger = op.outTrigger("trigger"); +const amount = op.inValueFloat("amount"); +const direction = op.inSwitch("direction", ["both", "vertical", "horizontal"], "both"); +const fast = op.inValueBool("Fast", true); +const cgl = op.patch.cgl; + +amount.set(10); + +let shader = new CGL.Shader(cgl, "blur"); + +shader.define("FASTBLUR"); + +fast.onChange = function () +{ + if (fast.get()) shader.define("FASTBLUR"); + else shader.removeDefine("FASTBLUR"); +}; + +shader.setSource(shader.getDefaultVertexShader(), attachments.blur_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +let uniDirX = new CGL.Uniform(shader, "f", "dirX", 0); +let uniDirY = new CGL.Uniform(shader, "f", "dirY", 0); + +let uniWidth = new CGL.Uniform(shader, "f", "width", 0); +let uniHeight = new CGL.Uniform(shader, "f", "height", 0); + +let uniAmount = new CGL.Uniform(shader, "f", "amount", amount.get()); +amount.onChange = function () { uniAmount.setValue(amount.get()); }; + +let textureAlpha = new CGL.Uniform(shader, "t", "imageMask", 1); + +let showingError = false; + +function fullScreenBlurWarning() +{ + if (cgl.currentTextureEffect.getCurrentSourceTexture().width == cgl.canvasWidth && + cgl.currentTextureEffect.getCurrentSourceTexture().height == cgl.canvasHeight) + { + op.setUiError("warning", "Full screen blurs are slow! Try reducing the resolution to 1/2 or a 1/4", 0); + } + else + { + op.setUiError("warning", null); + } +} + +let dir = 0; +direction.onChange = function () +{ + if (direction.get() == "both")dir = 0; + if (direction.get() == "horizontal")dir = 1; + if (direction.get() == "vertical")dir = 2; +}; + +let mask = op.inTexture("mask"); + +mask.onChange = function () +{ + if (mask.get() && mask.get().tex) shader.define("HAS_MASK"); + else shader.removeDefine("HAS_MASK"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + + uniWidth.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().width); + uniHeight.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().height); + + fullScreenBlurWarning(); + + // first pass + if (dir === 0 || dir == 2) + { + cgl.currentTextureEffect.bind(); + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (mask.get() && mask.get().tex) + { + cgl.setTexture(1, mask.get().tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, mask.get().tex ); + } + + uniDirX.setValue(0.0); + uniDirY.setValue(1.0); + + cgl.currentTextureEffect.finish(); + } + + // second pass + if (dir === 0 || dir == 1) + { + cgl.currentTextureEffect.bind(); + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (mask.get() && mask.get().tex) + { + cgl.setTexture(1, mask.get().tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, mask.get().tex ); + } + + uniDirX.setValue(1.0); + uniDirY.setValue(0.0); + + cgl.currentTextureEffect.finish(); + } + + cgl.popShader(); + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Blur.prototype = new CABLES.Op(); +CABLES.OPS["54f26f53-f637-44c1-9bfb-a2f2b722e998"]={f:Ops.Gl.TextureEffects.Blur,objName:"Ops.Gl.TextureEffects.Blur"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Border_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Border_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"border_frag":"IN vec2 texCoord;\nUNI float width;\nUNI sampler2D tex;\nUNI float amount;\nUNI float r;\nUNI float g;\nUNI float b;\nUNI float aspect;\nUNI vec4 mulSides;\nUNI bool smoothed;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec4 col= texture(tex,texCoord);\n\n if(!smoothed)\n {\n float border=0.0;\n if(border==0.0 && texCoord.y < width/aspect/3.0) border=mulSides.x;\n if(border==0.0 && texCoord.y > 1.0-width/aspect/3.0) border=mulSides.z;\n if(border==0.0 && texCoord.x > 1.0-width/3.0) border=mulSides.a;\n if(border==0.0 && texCoord.x < width/3.0 ) border=mulSides.y;\n\n col = vec4(r,g,b, border);\n }\n else\n {\n float f=smoothstep(0.0,width,texCoord.x)-smoothstep(1.0-width,1.0,texCoord.x);\n f*=smoothstep(0.0,width/aspect,texCoord.y);\n f*=smoothstep(1.0,1.0-width/aspect,texCoord.y);\n col= mix(col,vec4(r,g,b, 1.0),1.0-f);\n }\n\n vec4 base=texture(tex,texCoord);\n outColor=cgl_blendPixel(base,col,amount*col.a);\n}",}; +const + render = op.inTrigger("render"), + width = op.inValue("width", 0.1), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + + amount = op.inValueSlider("Amount", 1), + trigger = op.outTrigger("trigger"), + smooth = op.inValueBool("Smooth", false), + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + sideA = op.inFloat("Side A", 1), + sideB = op.inFloat("Side B", 1), + sideC = op.inFloat("Side C", 1), + sideD = op.inFloat("Side D", 1); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "border"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.border_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + aspectUniform = new CGL.Uniform(shader, "f", "aspect", 0), + uniSmooth = new CGL.Uniform(shader, "b", "smoothed", smooth), + uniWidth = new CGL.Uniform(shader, "f", "width", width.get()), + unir = new CGL.Uniform(shader, "f", "r", r), + unig = new CGL.Uniform(shader, "f", "g", g), + unib = new CGL.Uniform(shader, "f", "b", b); + +width.onChange = function () +{ + uniWidth.setValue(width.get() / 2); +}; + +r.setUiAttribs({ "colorPick": true }); +shader.addUniformFrag("4f", "mulSides", sideA, sideB, sideC, sideD); + +op.setPortGroup("Sides", [sideA, sideB, sideC, sideD]); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + let texture = cgl.currentTextureEffect.getCurrentSourceTexture(); + aspectUniform.set(texture.height / texture.width); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, texture.tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Border_v2.prototype = new CABLES.Op(); +CABLES.OPS["ee773dc0-d00c-4d6a-86b7-8fd32f9d72e1"]={f:Ops.Gl.TextureEffects.Border_v2,objName:"Ops.Gl.TextureEffects.Border_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.BrightnessContrast +// +// ************************************************************** + +Ops.Gl.TextureEffects.BrightnessContrast = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"brightness_contrast_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float amountbright;\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n col=texture(tex,texCoord);\n\n // apply contrast\n col.rgb = ((col.rgb - 0.5) * max(amount*2.0, 0.0))+0.5;\n\n // apply brightness\n col.rgb *= amountbright*2.0;\n\n outColor = col;\n}",}; +const + render = op.inTrigger("render"), + amount = op.inValueSlider("contrast", 0.5), + amountBright = op.inValueSlider("brightness", 0.5), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; + +const shader = new CGL.Shader(cgl, "brightnesscontrast"); +shader.setSource(shader.getDefaultVertexShader(), attachments.brightness_contrast_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); +const amountBrightUniform = new CGL.Uniform(shader, "f", "amountbright", amountBright); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + if (!cgl.currentTextureEffect.getCurrentSourceTexture()) return; + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.BrightnessContrast.prototype = new CABLES.Op(); +CABLES.OPS["54b89199-c594-4dff-bc48-82d6c7a55e8a"]={f:Ops.Gl.TextureEffects.BrightnessContrast,objName:"Ops.Gl.TextureEffects.BrightnessContrast"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.BulgePinch +// +// ************************************************************** + +Ops.Gl.TextureEffects.BulgePinch = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"bulgepinch_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\n\nUNI float radius;\nUNI float strength;\nUNI float centerX;\nUNI float centerY;\n\nvoid main()\n{\n vec2 center=vec2(centerX,centerY);\n vec2 coord=texCoord;\n coord -= center;\n float distance = length(coord);\n float percent = distance / radius;\n if (strength > 0.0) coord *= mix(1.0, smoothstep(0.0, radius / distance, percent), strength * 0.75);\n else coord *= mix(1.0, pow(percent, 1.0 + strength * 0.75) * radius / distance, 1.0 - percent);\n coord += center;\n vec4 col=texture(tex,coord);\n outColor= col;\n}",}; +const + render=op.inTrigger('render'), + radius=op.inValueFloat("Radius",0.5), + strength=op.inValueFloat("Strength",1), + centerX=op.inValueFloat("Center X",0.5), + centerY=op.inValueFloat("Center Y",0.5), + trigger=op.outTrigger('trigger'); + +const cgl=op.patch.cgl; +const shader=new CGL.Shader(cgl,'bulgepinch'); +shader.setSource(shader.getDefaultVertexShader(),attachments.bulgepinch_frag); + +const + uniRadius=new CGL.Uniform(shader,'f','radius',radius), + uniStrength=new CGL.Uniform(shader,'f','strength',strength), + uniCenterX=new CGL.Uniform(shader,'f','centerX',centerX), + uniCenterY=new CGL.Uniform(shader,'f','centerY',centerY), + textureUniform=new CGL.Uniform(shader,'t','tex',0); + +render.onTriggered=function() +{ + if(!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex ); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.BulgePinch.prototype = new CABLES.Op(); +CABLES.OPS["25696840-bd64-463e-9301-964a81385bfb"]={f:Ops.Gl.TextureEffects.BulgePinch,objName:"Ops.Gl.TextureEffects.BulgePinch"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.CheckerBoard_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.CheckerBoard_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"checkerboard_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float numX;\nUNI float numY;\nUNI float amount;\nUNI float rotate;\nUNI float aspect;\n\n{{CGL.BLENDMODES3}}\n\n#define PI 3.14159265\n#define TAU (2.0*PI)\n\nvoid pR(inout vec2 p, float a)\n{\n\tp = cos(a)*p + sin(a)*vec2(p.y, -p.x);\n}\n\nvoid main()\n{\n vec2 uv=texCoord-0.5;\n pR(uv.xy,rotate * (TAU));\n // uv = vec2(texCoord.x,texCoord.y*aspect)-0.5;\n\n #ifdef CENTER\n uv+=vec2(0.5,0.5);\n #endif\n\n float asp=1.0;\n float nY=numY;\n #ifdef SQUARE\n asp=aspect;\n nY=numX/aspect;\n\n #endif\n\n float total = floor(uv.x*numX-numX/2.0) +floor(uv.y/asp*nY-nY/2.0);\n float r = mod(total,2.0);\n\n vec4 col=vec4(r,r,r,1.0);\n vec4 base=texture(tex,texCoord);\n outColor=cgl_blendPixel(base,col,amount);\n}",}; +const render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + amount = op.inValueSlider("Amount", 1), + inSquare = op.inBool("Square", true), + numX = op.inValue("Num X", 10), + numY = op.inValue("Num Y", 10), + inRotate = op.inValueSlider("Rotate", 0.0), + inCentered = op.inBool("Centered", true), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "checkerboard"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.checkerboard_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniNumX = new CGL.Uniform(shader, "f", "numX", numX), + uniNumY = new CGL.Uniform(shader, "f", "numY", numY), + uniAspect = new CGL.Uniform(shader, "f", "aspect", 1), + rotateUniform = new CGL.Uniform(shader, "f", "rotate", inRotate); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +inSquare.onChange = +inCentered.onChange = updateDefines; + +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("CENTER", inCentered.get()); + shader.toggleDefine("SQUARE", inSquare.get()); + + numY.setUiAttribs({ "greyout": inSquare.get() }); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + uniAspect.set(cgl.currentTextureEffect.aspectRatio); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.CheckerBoard_v2.prototype = new CABLES.Op(); +CABLES.OPS["7edfae81-f092-413f-a2a0-b109fdffa61d"]={f:Ops.Gl.TextureEffects.CheckerBoard_v2,objName:"Ops.Gl.TextureEffects.CheckerBoard_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ChromaticAberration_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.ChromaticAberration_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"chromatic_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float pixel;\nUNI float onePixel;\nUNI float amount;\nUNI float lensDistort;\n\n#ifdef MASK\nUNI sampler2D texMask;\n#endif\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n vec4 col=texture(tex,texCoord);\n\n vec2 tc=texCoord;;\n float pix = pixel;\n if(lensDistort>0.0)\n {\n float dist = distance(texCoord, vec2(0.5,0.5));\n tc-=0.5;\n tc *=smoothstep(-0.9,1.0*lensDistort,1.0-dist);\n tc+=0.5;\n }\n\n #ifdef MASK\n vec4 m=texture(texMask,texCoord);\n pix*=m.r*m.a;\n #endif\n\n #ifdef SMOOTH\n #ifdef WEBGL2\n float numSamples=round(pix/onePixel/4.0+1.0);\n col.r=0.0;\n col.b=0.0;\n\n for(float off=0.0;offinner*sz) v=(smoothstep(0.0,1.0,(dist-(inner*sz))/(fade)));\n }\n\n if(fade>=0.0)\n {\n\n #ifdef FALLOFF_SMOOTHSTEP\n if(dist>inner*sz && distinner*sz && dist(1.0-width) || texCoord.y>(1.0-width) || texCoord.y0.001*amount)outColor= vec4(1.0,0.0,0.0, 1.0);\n #endif\n}\n",}; +const + render = op.inTrigger("Render"), + amount = op.inValueSlider("Amount", 1), + blendMode = CGL.TextureEffect.AddBlendSelect(op), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + inSize = op.inValueSlider("Size", 0.25), + inInner = op.inValueSlider("Inner"), + inStretchX = op.inFloat("Stretch X"), + inStretchY = op.inFloat("Stretch Y"), + inX = op.inValue("Pos X", 0), + inY = op.inValue("Pos Y", 0), + fallOff = op.inValueSelect("fallOff", ["Linear", "SmoothStep"], "Linear"), + inFadeOut = op.inValueSlider("fade Out"), + warnOverflow = op.inValueBool("warn overflow", false), + r = op.inValueSlider("r", 1), + g = op.inValueSlider("g", 1), + b = op.inValueSlider("b", 1), + a = op.inValueSlider("a", 1), + trigger = op.outTrigger("Next"); + +r.setUiAttribs({ "colorPick": true }); + +op.setPortGroup("Size", [inSize, inInner, inStretchX, inStretchY]); +op.setPortGroup("Position", [inX, inY]); +op.setPortGroup("Style", [warnOverflow, fallOff, inFadeOut]); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, "textureeffect stripes"); +shader.setSource(shader.getDefaultVertexShader(), attachments.circle_frag); + +updateDefines(); + +let + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniStretch = new CGL.Uniform(shader, "2f", "stretch", inStretchX, inStretchY), + uniSize = new CGL.Uniform(shader, "f", "size", inSize), + uniFadeOut = new CGL.Uniform(shader, "f", "fadeOut", inFadeOut), + uniInner = new CGL.Uniform(shader, "f", "inner", inInner), + aspect = new CGL.Uniform(shader, "f", "aspect", 1), + uniformR = new CGL.Uniform(shader, "f", "r", r), + uniformG = new CGL.Uniform(shader, "f", "g", g), + uniformB = new CGL.Uniform(shader, "f", "b", b), + uniformA = new CGL.Uniform(shader, "f", "a", a), + uniformX = new CGL.Uniform(shader, "f", "x", inX), + uniformY = new CGL.Uniform(shader, "f", "y", inY); + +fallOff.onChange = + warnOverflow.onChange = updateDefines; + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +function updateDefines() +{ + shader.toggleDefine("FALLOFF_LINEAR", fallOff.get() == "Linear"); + shader.toggleDefine("FALLOFF_SMOOTHSTEP", fallOff.get() == "SmoothStep"); + shader.toggleDefine("WARN_OVERFLOW", warnOverflow.get()); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + aspect.set(cgl.currentTextureEffect.aspectRatio); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.CircleTexture_v4.prototype = new CABLES.Op(); +CABLES.OPS["9738c421-1ee0-42bf-be0b-f4a481385fbf"]={f:Ops.Gl.TextureEffects.CircleTexture_v4,objName:"Ops.Gl.TextureEffects.CircleTexture_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ClampTexture +// +// ************************************************************** + +Ops.Gl.TextureEffects.ClampTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"clampShader_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float lowEdge;\nUNI float highEdge;\n\n{{CGL.BLENDMODES}}\n\nvoid main()\n{\n vec3 result = vec3(0.);\n vec3 color = texture(tex,texCoord).rgb;\n\n #ifdef CLAMP\n result = clamp(color,vec3(lowEdge),vec3(highEdge));\n #endif\n\n #ifdef REMAP\n result = mix(color*vec3(lowEdge),color*vec3(highEdge),color);\n #endif\n\n #ifdef REMAP_SMOOTH\n result = smoothstep(vec3(lowEdge),vec3(highEdge),color);\n #endif\n\n outColor= mix(vec4(color,1.0),\n vec4(result,1.0),\n amount);\n}",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + modeSelect = op.inValueSelect("Mode", ["Clamp", "Remap", "Remap smooth"], "Clamp"), + inLowEdge = op.inValue("Min", 0.0), + inHighEdge = op.inValue("Max", 1.0), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.clampShader_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); +const lowEdgeUniform = new CGL.Uniform(shader, "f", "lowEdge", inLowEdge); +const highEdgeUniform = new CGL.Uniform(shader, "f", "highEdge", inHighEdge); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +op.init = modeSelect.onChange = function () +{ + shader.toggleDefine("CLAMP", modeSelect.get() === "Clamp"); + shader.toggleDefine("REMAP", modeSelect.get() === "Remap"); + shader.toggleDefine("REMAP_SMOOTH", modeSelect.get() === "Remap smooth"); +}; +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.ClampTexture.prototype = new CABLES.Op(); +CABLES.OPS["086ca023-af3c-4e3a-9be7-1972adcf63b5"]={f:Ops.Gl.TextureEffects.ClampTexture,objName:"Ops.Gl.TextureEffects.ClampTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Clarity +// +// ************************************************************** + +Ops.Gl.TextureEffects.Clarity = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"clarity_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float pX,pY;\n\nvec3 desaturate(vec4 color)\n{\n vec3 c= vec3(dot(vec3(0.2126,0.7152,0.0722), color.rgb));\n return c;\n}\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n\n vec3 gray=desaturate(col);\n vec3 m=smoothstep(0.2,0.5,gray)*smoothstep(0.75,0.5,gray);\n vec4 col2=vec4(1.0);\n\n col2.rgb = ((col.rgb - 0.5) * max(( vec3(amount)*m+0.5)*2.0, 0.0))+0.5;\n\n outColor= col2;\n}\n\n\n",}; +const render = op.inTrigger("Render"); +const trigger = op.outTrigger("Trigger"); +const amount = op.inFloat("amount", 0.5); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.clarity_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Clarity.prototype = new CABLES.Op(); +CABLES.OPS["37d66c32-5594-4509-bba0-0ba2cbb706d8"]={f:Ops.Gl.TextureEffects.Clarity,objName:"Ops.Gl.TextureEffects.Clarity"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ColorBalance_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.ColorBalance_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colorbalance_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float r;\nUNI float g;\nUNI float b;\n\nfloat lumi(vec3 color)\n{\n return vec3(dot(vec3(0.2126,0.7152,0.0722), color)).r;\n}\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n float l=lumi(base.rgb);\n\n #ifdef TONE_MID\n l=smoothstep(0.33,0.66,l);\n #endif\n\n #ifdef TONE_LOW\n l=1.0-l;\n #endif\n\n l=l*l;\n vec3 color=base.rgb+vec3(l*r*0.1,l*g*0.1,l*b*0.1);\n outColor= vec4(color,base.a);\n}\n",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + tone = op.inValueSelect("Tone", ["Highlights", "Midtones", "Shadows"], "Highlights"), + r = op.inValue("r"), + g = op.inValue("g"), + b = op.inValue("b"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.colorbalance_frag); +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniR = new CGL.Uniform(shader, "f", "r", r), + uniG = new CGL.Uniform(shader, "f", "g", g), + uniB = new CGL.Uniform(shader, "f", "b", b); + +tone.onChange = function () +{ + shader.toggleDefine("TONE_HIGH", tone.get() == "Highlights"); + shader.toggleDefine("TONE_MID", tone.get() == "Midtones"); + shader.toggleDefine("TONE_LOW", tone.get() == "Shadows"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.ColorBalance_v2.prototype = new CABLES.Op(); +CABLES.OPS["5af81475-2aa6-451b-a1f3-0980f641a72e"]={f:Ops.Gl.TextureEffects.ColorBalance_v2,objName:"Ops.Gl.TextureEffects.ColorBalance_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ColorChannel_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.ColorChannel_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colorchannel_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\n\nvoid main()\n{\n vec4 color=texture(tex,texCoord);\n vec4 col=vec4(0.0,0.0,0.0,color.a);\n\n #ifdef CHANNEL_R\n col.r=color.r;\n #ifdef MONO\n col.g=col.b=col.r;\n #endif\n #endif\n\n #ifdef CHANNEL_G\n col.g=color.g;\n #ifdef MONO\n col.r=col.b=col.g;\n #endif\n #endif\n\n #ifdef CHANNEL_B\n col.b=color.b;\n #ifdef MONO\n col.g=col.r=col.b;\n #endif\n #endif\n\n #ifdef CHANNEL_A\n col.r=col.g=col.b=color.a;\n #endif\n\n outColor = col;\n}",}; +const + render = op.inTrigger("render"), + channelR = op.inValueBool("channelR", true), + channelG = op.inValueBool("channelG", false), + channelB = op.inValueBool("channelB", false), + channelA = op.inValueBool("channelA", false), + mono = op.inValueBool("mono", false), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.colorchannel_frag || ""); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +mono.onChange = + channelA.onChange = + channelR.onChange = + channelG.onChange = + channelB.onChange = updateChannels; +updateChannels(); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + +function updateChannels() +{ + shader.toggleDefine("CHANNEL_R",channelR.get()); + shader.toggleDefine("CHANNEL_G",channelG.get()); + shader.toggleDefine("CHANNEL_B",channelB.get()); + shader.toggleDefine("CHANNEL_A",channelA.get()); + shader.toggleDefine("MONO",mono.get()); +} + + +}; + +Ops.Gl.TextureEffects.ColorChannel_v2.prototype = new CABLES.Op(); +CABLES.OPS["0a512974-0920-4aa8-89dc-4dda184c0d46"]={f:Ops.Gl.TextureEffects.ColorChannel_v2,objName:"Ops.Gl.TextureEffects.ColorChannel_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ColorMapRange +// +// ************************************************************** + +Ops.Gl.TextureEffects.ColorMapRange = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"maprange_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\n\nUNI float min1,min2,max1,max2;\n\nfloat map(float value)\n{\n return min2 + (value - min1) * (max2 - min2) / (max1 - min1);\n}\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n\n #ifdef CH_R\n col.r=map(col.r);\n #endif\n #ifdef CH_G\n col.g=map(col.g);\n #endif\n #ifdef CH_B\n col.b=map(col.b);\n #endif\n #ifdef CH_A\n col.a=map(col.a);\n #endif\n\n outColor = col;\n}",}; +const + render = op.inTrigger("render"), + min1 = op.inValueSlider("Old Min", 0), + max1 = op.inValueSlider("Old Max", 1), + min2 = op.inValueSlider("New Min", 0), + max2 = op.inValueSlider("New Max", 1), + + inR = op.inBool("R", true), + inG = op.inBool("G", true), + inB = op.inBool("B", true), + inA = op.inBool("A", false), + + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Input Range", [min1, max1]); +op.setPortGroup("Output Range", [min2, max2]); + +const cgl = op.patch.cgl; + +const shader = new CGL.Shader(cgl, "colorMaprange"); +shader.setSource(shader.getDefaultVertexShader(), attachments.maprange_frag); +toggleChannels(shader); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniMin1 = new CGL.Uniform(shader, "f", "min1", min1), + uniMin2 = new CGL.Uniform(shader, "f", "min2", min2), + unimax1 = new CGL.Uniform(shader, "f", "max1", max1), + unimax2 = new CGL.Uniform(shader, "f", "max2", max2); + +inR.onChange = + inG.onChange = + inB.onChange = + inA.onChange = () => + { + toggleChannels(shader); + }; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + if (!cgl.currentTextureEffect.getCurrentSourceTexture()) return; + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + +function toggleChannels(shader) +{ + shader.toggleDefine("CH_R", inR.get()); + shader.toggleDefine("CH_G", inG.get()); + shader.toggleDefine("CH_B", inB.get()); + shader.toggleDefine("CH_A", inA.get()); +} + + +}; + +Ops.Gl.TextureEffects.ColorMapRange.prototype = new CABLES.Op(); +CABLES.OPS["a1452720-dc08-4195-983b-7949aac33055"]={f:Ops.Gl.TextureEffects.ColorMapRange,objName:"Ops.Gl.TextureEffects.ColorMapRange"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ColorMap_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.ColorMap_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colormap_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D gradient;\nUNI float pos;\nUNI float amount;\nUNI float vmin;\nUNI float vmax;\n\n{{CGL.BLENDMODES3}}\n\n\nfloat lumi(vec3 color)\n{\n return vec3(dot(vec3(0.2126,0.7152,0.0722), color)).r;\n}\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n float a=base.a;\n\n base=clamp(base,vmin,vmax);\n\n #ifdef METH_LUMI\n vec4 color=texture(gradient,vec2(lumi(base.rgb),pos));\n #endif\n\n #ifdef METH_CHANNELS\n vec4 color=vec4(1.0);\n color.r=texture(gradient,vec2(base.r,pos)).r;\n color.g=texture(gradient,vec2(base.g,pos)).g;\n color.b=texture(gradient,vec2(base.b,pos)).b;\n #endif\n\n base.a=color.a=a;\n\n\n outColor=cgl_blendPixel(base,color,amount);\n\n}\n",}; +let render = op.inTrigger("render"); +let trigger = op.outTrigger("trigger"); + +const blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"); +const amount = op.inValueSlider("Amount", 1); + +let inGradient = op.inTexture("Gradient"); +let inMethod = op.inSwitch("Method", ["Luminance", "Channels"], "Luminance"); + +let inMin = op.inFloatSlider("Min", 0); +let inMax = op.inFloatSlider("Max", 1); + +let inPos = op.inValueSlider("Position", 0.5); + +op.setPortGroup("Vertical Position", [inMin, inMax, inPos]); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); +shader.define("METH_LUMI"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.colormap_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +let textureUniform2 = new CGL.Uniform(shader, "t", "gradient", 1); +let uniPos = new CGL.Uniform(shader, "f", "pos", inPos); +let uniMin = new CGL.Uniform(shader, "f", "vmin", inMin); +let uniMax = new CGL.Uniform(shader, "f", "vmax", inMax); +let uniAmount = new CGL.Uniform(shader, "f", "amount", amount); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +inMethod.onChange = () => +{ + shader.toggleDefine("METH_LUMI", inMethod.get() == "Luminance"); + shader.toggleDefine("METH_CHANNELS", inMethod.get() == "Channels"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + if (!inGradient.get()) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.setTexture(1, inGradient.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.ColorMap_v2.prototype = new CABLES.Op(); +CABLES.OPS["440c1675-122d-411f-b848-16c60b677120"]={f:Ops.Gl.TextureEffects.ColorMap_v2,objName:"Ops.Gl.TextureEffects.ColorMap_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Color_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Color_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"color_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float r;\nUNI float g;\nUNI float b;\nUNI float amount;\n\n#ifdef MASK\n UNI sampler2D mask;\n#endif\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec4 col=vec4(r,g,b,1.0);\n vec4 base=texture(tex,texCoord);\n\n float am=amount;\n #ifdef MASK\n float msk=texture(mask,texCoord).r;\n #ifdef INVERTMASK\n msk=1.0-msk;\n #endif\n am*=1.0-msk;\n #endif\n\n outColor=cgl_blendPixel(base,col,am);\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op), + amount = op.inValueSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + + inMask = op.inTexture("Mask"), + inMaskInvert = op.inValueBool("Mask Invert"), + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + trigger = op.outTrigger("trigger"); + +r.setUiAttribs({ "colorPick": true }); +op.setPortGroup("Color", [r, g, b]); + +const TEX_SLOT = 0; +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "textureeffect color"); +const srcFrag = attachments.color_frag || ""; +shader.setSource(shader.getDefaultVertexShader(), srcFrag); +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", TEX_SLOT), + makstextureUniform = new CGL.Uniform(shader, "t", "mask", 1), + uniformR = new CGL.Uniform(shader, "f", "r", r), + uniformG = new CGL.Uniform(shader, "f", "g", g), + uniformB = new CGL.Uniform(shader, "f", "b", b), + uniformAmount = new CGL.Uniform(shader, "f", "amount", amount); + +inMask.onChange = function () +{ + if (inMask.isLinked())shader.define("MASK"); + else shader.removeDefine("MASK"); +}; + +inMaskInvert.onChange = function () +{ + if (inMaskInvert.get())shader.define("INVERTMASK"); + else shader.removeDefine("INVERTMASK"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(TEX_SLOT, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (inMask.get()) cgl.setTexture(1, inMask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Color_v2.prototype = new CABLES.Op(); +CABLES.OPS["6dada2b7-da7c-47ee-87a9-a12e87055208"]={f:Ops.Gl.TextureEffects.Color_v2,objName:"Ops.Gl.TextureEffects.Color_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Denoise +// +// ************************************************************** + +Ops.Gl.TextureEffects.Denoise = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"denoise_frag":"UNI sampler2D tex;\nUNI float exponent;\nUNI float strength;\nUNI vec2 texSize;\nIN vec2 texCoord;\n\nvoid main()\n{\n vec4 center = texture(tex, texCoord);\n vec4 color = vec4(0.0);\n float total = 0.0;\n const float pixels=4.0;\n for (float x = -pixels; x <= pixels; x += 1.0) {\n for (float y = -pixels; y <= pixels; y += 1.0) {\n vec4 smpl = texture(tex, texCoord + vec2(x, y) / texSize);\n float weight = 1.0 - abs(dot(smpl.rgb - center.rgb, vec3(0.25)));\n weight = pow(weight, (1.0-exponent)*50.0);\n color += smpl * weight;\n total += weight;\n }\n }\n outColor = color / total;\n}\n",}; +let render = op.inTrigger("render"); +let strength = op.inValueSlider("Exponent", 0.6); + +let trigger = op.outTrigger("trigger"); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); +let tsize = [128, 128]; +let srcFrag = attachments.denoise_frag; + +shader.setSource(shader.getDefaultVertexShader(), srcFrag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +let strengthUniform = new CGL.Uniform(shader, "f", "exponent", strength); +let texSizeUniform = new CGL.Uniform(shader, "2f", "texSize", tsize); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + tsize[0] = cgl.currentTextureEffect.getCurrentSourceTexture().width; + tsize[1] = cgl.currentTextureEffect.getCurrentSourceTexture().height; + texSizeUniform.setValue(tsize); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Denoise.prototype = new CABLES.Op(); +CABLES.OPS["0abfea0f-1aa9-47bf-b540-f54f89a60a6c"]={f:Ops.Gl.TextureEffects.Denoise,objName:"Ops.Gl.TextureEffects.Denoise"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.DepthTextureFocus_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.DepthTextureFocus_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"depth_focus_frag":"IN vec2 texCoord;\nUNI sampler2D image;\nUNI float nearPlane;\nUNI float farPlane;\nUNI float focus; // center\nUNI float width;\n\n\nvoid main()\n{\n float depthFromTexture = texture(image,texCoord).r;\n\n float z_viewSpace = (nearPlane * farPlane) / (farPlane - depthFromTexture * (farPlane - nearPlane));\n z_viewSpace = abs(z_viewSpace - (focus));\n z_viewSpace = smoothstep(0.0, width, z_viewSpace);\n\n #ifndef INVERT\n z_viewSpace = 1. - z_viewSpace;\n #endif\n\n outColor = vec4(vec3(z_viewSpace), 1.);\n}",}; +const render = op.inTrigger("render"); +const depthTexture = op.inTexture("Depth Texture"); + +const inFocus = op.inFloat("Focus", 0.5); +const inWidth = op.inFloat("Width", 0.2); +const inInv = op.inBool("Invert", false); +const nearPlane = op.inFloat("nearplane", 0.1); +const farPlane = op.inFloat("farplane", 100); + +const trigger = op.outTrigger("trigger"); + +op.setPortGroup("Frustum", [farPlane, nearPlane]); +op.setPortGroup("Focus Settings", [inInv, inFocus, inWidth]); + + +const cgl = op.patch.cgl; + +const shader = new CGL.Shader(cgl, op.name); +const srcFrag = attachments.depth_focus_frag || ""; +shader.setSource(shader.getDefaultVertexShader(), srcFrag); + +const textureUniform = new CGL.Uniform(shader, "t", "depthTexture", 0); +const uniFarplane = new CGL.Uniform(shader, "f", "farPlane", farPlane); +const uniNearplane = new CGL.Uniform(shader, "f", "nearPlane", nearPlane); +const uniFocus = new CGL.Uniform(shader, "f", "focus", inFocus); +const uniwidth = new CGL.Uniform(shader, "f", "width", inWidth); +const uniAspect = new CGL.Uniform(shader, "f", "aspectRatio", 0); + +inInv.onChange = function () +{ + if (inInv.get())shader.define("INVERT"); + else shader.removeDefine("INVERT"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + if (depthTexture.get() && depthTexture.get().tex) + { + const a = + cgl.currentTextureEffect.getCurrentSourceTexture().height + / cgl.currentTextureEffect.getCurrentSourceTexture().width; + + uniAspect.set(a); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + + cgl.setTexture(0, depthTexture.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + } + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.DepthTextureFocus_v2.prototype = new CABLES.Op(); +CABLES.OPS["f84ac22e-664e-4ff1-b23f-539ac1f5c67e"]={f:Ops.Gl.TextureEffects.DepthTextureFocus_v2,objName:"Ops.Gl.TextureEffects.DepthTextureFocus_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.DepthTexture_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.DepthTexture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"depthtexture_frag":"IN vec2 texCoord;\nUNI float amount;\nUNI sampler2D texDepth;\nUNI sampler2D texBase;\nUNI float n;\nUNI float f;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec4 col=texture(texDepth,texCoord);\n float z=col.r;\n float c=(2.0*n)/(f+n-z*(f-n));\n\n #ifdef INVERT\n c=1.0-c;\n #endif\n\n col=vec4(c,c,c,1.0);\n vec4 base=texture(texBase,texCoord);\n\n outColor=cgl_blendPixel(base,col,amount);\n\n}",}; +const + render = op.inTrigger("render"), + inDepthTex = op.inTexture("image"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + farPlane = op.inValue("farplane", 50.0), + nearPlane = op.inValue("nearplane", 0.1), + inInv = op.inValueBool("Invert", false), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Frustum", [farPlane, nearPlane]); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.depthtexture_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "texDepth", 0), + textureBaseUniform = new CGL.Uniform(shader, "t", "texBase", 1), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniFarplane = new CGL.Uniform(shader, "f", "f", farPlane), + uniNearplane = new CGL.Uniform(shader, "f", "n", nearPlane); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +inInv.onChange = function () +{ + if (inInv.get())shader.define("INVERT"); + else shader.removeDefine("INVERT"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + if (inDepthTex.get() && inDepthTex.get().tex) + { + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, inDepthTex.get().tex); + cgl.setTexture(1, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + cgl.currentTextureEffect.finish(); + cgl.popShader(); + } + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.DepthTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["5958fbb8-fb78-4de2-85dc-5eb289f652cb"]={f:Ops.Gl.TextureEffects.DepthTexture_v2,objName:"Ops.Gl.TextureEffects.DepthTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Desaturate +// +// ************************************************************** + +Ops.Gl.TextureEffects.Desaturate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"desaturate_frag":"\nIN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\n\n#ifdef MASK\n UNI sampler2D mask;\n#endif\n\nvec3 desaturate(vec3 color, float amount)\n{\n vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), color));\n return vec3(mix(color, gray, amount));\n}\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n\n float am=amount;\n #ifdef MASK\n am*=1.0-texture(mask,texCoord).r;\n #ifdef INVERTMASK\n am=1.0-am;\n #endif\n #endif\n\n col.rgb=desaturate(col.rgb,am);\n outColor= col;\n}",}; +const render = op.inTrigger("render"); +const trigger = op.outTrigger("trigger"); +const amount = op.inValueSlider("amount", 1); +const inMask = op.inTexture("Mask"); +const invertMask = op.inValueBool("Invert Mask"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.desaturate_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +let masktextureUniform = new CGL.Uniform(shader, "t", "mask", 1); +let amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + + +invertMask.onChange = function () +{ + if (invertMask.get())shader.define("INVERTMASK"); + else shader.removeDefine("INVERTMASK"); +}; + +inMask.onChange = function () +{ + if (inMask.get())shader.define("MASK"); + else shader.removeDefine("MASK"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (inMask.get()) cgl.setTexture(1, inMask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Desaturate.prototype = new CABLES.Op(); +CABLES.OPS["340efbd5-be53-4bd5-92ad-8f38d8eeecf1"]={f:Ops.Gl.TextureEffects.Desaturate,objName:"Ops.Gl.TextureEffects.Desaturate"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Dither_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Dither_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"dither_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float strength;\nUNI float amount;\nUNI float width;\nUNI float height;\nUNI float threshold;\n\nfloat lumi( vec4 col ) {\n return (0.2126*col.r + 0.7152*col.g + 0.0722*col.b);\n}\n\n{{CGL.BLENDMODES3}}\n\nfloat adjustFrag( mat4 adjustments,float val, vec2 coord )\n{\n vec2 coordMod = mod(vec2(coord.x*width,coord.y*height), 4.0);\n int xMod = int(coordMod.x);\n int yMod = int(coordMod.y);\n\n vec4 col;\n if (xMod == 0) col = adjustments[0];\n else if (xMod == 1) col = adjustments[1];\n else if (xMod == 2) col = adjustments[2];\n else if (xMod == 3) col = adjustments[3];\n\n float adjustment;\n if (yMod == 0) adjustment = col.x;\n else if (yMod == 1) adjustment = col.y;\n else if (yMod == 2) adjustment = col.z;\n else if (yMod == 3) adjustment = col.w;\n\n return val + (val * adjustment);\n}\n\nvoid main()\n{\n mat4 adjustments = ((mat4(\n 1, 13, 4, 16,\n 9, 5, 12, 8,\n 3, 15, 2, 14,\n 11, 7, 10, 6\n ) - 8.) * 1.0 / strength);\n\n vec4 base=texture(tex,texCoord);\n vec4 color;\n\n float lum = lumi(base);\n lum = adjustFrag(adjustments,lum, texCoord.xy);\n\n if (lum > threshold) color = vec4(1, 1, 1, base.a);\n else color = vec4(0, 0, 0, base.a);\n\n outColor=cgl_blendPixel(base,color,amount);\n}",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + trigger = op.outTrigger("Trigger"), + strength = op.inValue("strength", 2), + threshold = op.inValueSlider("threshold", 0.35); + +const + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.dither_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + strengthUniform = new CGL.Uniform(shader, "f", "strength", strength), + uniWidth = new CGL.Uniform(shader, "f", "width", 0), + uniHeight = new CGL.Uniform(shader, "f", "height", 0), + unithreshold = new CGL.Uniform(shader, "f", "threshold", threshold); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + uniWidth.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().width); + uniHeight.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().height); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Dither_v2.prototype = new CABLES.Op(); +CABLES.OPS["686ae373-2d2d-44cc-b45f-2ccb782dea26"]={f:Ops.Gl.TextureEffects.Dither_v2,objName:"Ops.Gl.TextureEffects.Dither_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.DrawImage_v3 +// +// ************************************************************** + +Ops.Gl.TextureEffects.DrawImage_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"drawimage_frag":"#ifdef HAS_TEXTURES\n IN vec2 texCoord;\n UNI sampler2D tex;\n UNI sampler2D image;\n#endif\n\n#ifdef TEX_TRANSFORM\n IN mat3 transform;\n#endif\n// UNI float rotate;\n\n{{CGL.BLENDMODES}}\n\n#ifdef HAS_TEXTUREALPHA\n UNI sampler2D imageAlpha;\n#endif\n\nUNI float amount;\n\n#ifdef ASPECT_RATIO\n UNI float aspectTex;\n UNI float aspectPos;\n#endif\n\nvoid main()\n{\n vec4 blendRGBA=vec4(0.0,0.0,0.0,1.0);\n\n #ifdef HAS_TEXTURES\n vec2 tc=texCoord;\n\n #ifdef TEX_FLIP_X\n tc.x=1.0-tc.x;\n #endif\n #ifdef TEX_FLIP_Y\n tc.y=1.0-tc.y;\n #endif\n\n #ifdef ASPECT_RATIO\n #ifdef ASPECT_AXIS_X\n tc.y=(1.0-aspectPos)-(((1.0-aspectPos)-tc.y)*aspectTex);\n #endif\n #ifdef ASPECT_AXIS_Y\n tc.x=(1.0-aspectPos)-(((1.0-aspectPos)-tc.x)/aspectTex);\n #endif\n #endif\n\n #ifdef TEX_TRANSFORM\n vec3 coordinates=vec3(tc.x, tc.y,1.0);\n tc=(transform * coordinates ).xy;\n #endif\n\n blendRGBA=texture(image,tc);\n\n vec3 blend=blendRGBA.rgb;\n vec4 baseRGBA=texture(tex,texCoord);\n vec3 base=baseRGBA.rgb;\n\n\n #ifdef PREMUL\n blend.rgb = (blend.rgb) + (base.rgb * (1.0 - blendRGBA.a));\n #endif\n\n vec3 colNew=_blend(base,blend);\n\n\n\n\n #ifdef REMOVE_ALPHA_SRC\n blendRGBA.a=1.0;\n #endif\n\n #ifdef HAS_TEXTUREALPHA\n vec4 colImgAlpha=texture(imageAlpha,tc);\n float colImgAlphaAlpha=colImgAlpha.a;\n\n #ifdef ALPHA_FROM_LUMINANCE\n vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), colImgAlpha.rgb ));\n colImgAlphaAlpha=(gray.r+gray.g+gray.b)/3.0;\n #endif\n\n #ifdef ALPHA_FROM_INV_UMINANCE\n vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), colImgAlpha.rgb ));\n colImgAlphaAlpha=1.0-(gray.r+gray.g+gray.b)/3.0;\n #endif\n\n #ifdef INVERT_ALPHA\n colImgAlphaAlpha=clamp(colImgAlphaAlpha,0.0,1.0);\n colImgAlphaAlpha=1.0-colImgAlphaAlpha;\n #endif\n\n blendRGBA.a=colImgAlphaAlpha*blendRGBA.a;\n #endif\n #endif\n\n float am=amount;\n\n #ifdef CLIP_REPEAT\n if(tc.y>1.0 || tc.y<0.0 || tc.x>1.0 || tc.x<0.0)\n {\n // colNew.rgb=vec3(0.0);\n am=0.0;\n }\n #endif\n\n #ifdef ASPECT_RATIO\n #ifdef ASPECT_CROP\n if(tc.y>1.0 || tc.y<0.0 || tc.x>1.0 || tc.x<0.0)\n {\n colNew.rgb=base.rgb;\n am=0.0;\n }\n\n #endif\n #endif\n\n\n\n #ifndef PREMUL\n blendRGBA.rgb=mix(colNew,base,1.0-(am*blendRGBA.a));\n blendRGBA.a=clamp(baseRGBA.a+(blendRGBA.a*am),0.,1.);\n #endif\n\n #ifdef PREMUL\n // premultiply\n // blendRGBA.rgb = (blendRGBA.rgb) + (baseRGBA.rgb * (1.0 - blendRGBA.a));\n blendRGBA=vec4(\n mix(colNew.rgb,base,1.0-(am*blendRGBA.a)),\n blendRGBA.a*am+baseRGBA.a\n );\n #endif\n\n #ifdef ALPHA_MASK\n blendRGBA.a=baseRGBA.a;\n #endif\n\n outColor=blendRGBA;\n}\n\n\n\n\n\n\n\n","drawimage_vert":"IN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\n\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\n\nOUT vec2 texCoord;\n// OUT vec3 norm;\n\n#ifdef TEX_TRANSFORM\n UNI float posX;\n UNI float posY;\n UNI float scaleX;\n UNI float scaleY;\n UNI float rotate;\n OUT mat3 transform;\n#endif\n\nvoid main()\n{\n texCoord=attrTexCoord;\n// norm=attrVertNormal;\n\n #ifdef TEX_TRANSFORM\n vec3 coordinates=vec3(attrTexCoord.x, attrTexCoord.y,1.0);\n float angle = radians( rotate );\n vec2 scale= vec2(scaleX,scaleY);\n vec2 translate= vec2(posX,posY);\n\n transform = mat3( scale.x * cos( angle ), scale.x * sin( angle ), 0.0,\n - scale.y * sin( angle ), scale.y * cos( angle ), 0.0,\n - 0.5 * scale.x * cos( angle ) + 0.5 * scale.y * sin( angle ) - 0.5 * translate.x*2.0 + 0.5, - 0.5 * scale.x * sin( angle ) - 0.5 * scale.y * cos( angle ) - 0.5 * translate.y*2.0 + 0.5, 1.0);\n #endif\n\n gl_Position = projMatrix * mvMatrix * vec4(vPosition, 1.0);\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "blendMode"), + amount = op.inValueSlider("amount", 1), + + image = op.inTexture("Image"), + inAlphaPremul = op.inValueBool("Premultiplied", false), + inAlphaMask = op.inValueBool("Alpha Mask", false), + removeAlphaSrc = op.inValueBool("removeAlphaSrc", false), + + imageAlpha = op.inTexture("Mask"), + alphaSrc = op.inValueSelect("Mask Src", ["alpha channel", "luminance", "luminance inv"], "luminance"), + invAlphaChannel = op.inValueBool("Invert alpha channel"), + + inAspect = op.inValueBool("Aspect Ratio", false), + inAspectAxis = op.inValueSelect("Stretch Axis", ["X", "Y"], "X"), + inAspectPos = op.inValueSlider("Position", 0.0), + inAspectCrop = op.inValueBool("Crop", false), + + trigger = op.outTrigger("trigger"); + +blendMode.set("normal"); +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "drawimage"); + +imageAlpha.onLinkChanged = updateAlphaPorts; + +op.setPortGroup("Mask", [imageAlpha, alphaSrc, invAlphaChannel]); +op.setPortGroup("Aspect Ratio", [inAspect, inAspectPos, inAspectCrop, inAspectAxis]); + +function updateAlphaPorts() +{ + if (imageAlpha.isLinked()) + { + removeAlphaSrc.setUiAttribs({ "greyout": true }); + alphaSrc.setUiAttribs({ "greyout": false }); + invAlphaChannel.setUiAttribs({ "greyout": false }); + } + else + { + removeAlphaSrc.setUiAttribs({ "greyout": false }); + alphaSrc.setUiAttribs({ "greyout": true }); + invAlphaChannel.setUiAttribs({ "greyout": true }); + } +} + +op.toWorkPortsNeedToBeLinked(image); + +shader.setSource(attachments.drawimage_vert, attachments.drawimage_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureImaghe = new CGL.Uniform(shader, "t", "image", 1), + textureAlpha = new CGL.Uniform(shader, "t", "imageAlpha", 2), + uniTexAspect = new CGL.Uniform(shader, "f", "aspectTex", 1), + uniAspectPos = new CGL.Uniform(shader, "f", "aspectPos", inAspectPos); + +inAspect.onChange = + inAspectCrop.onChange = + inAspectAxis.onChange = updateAspectRatio; + +function updateAspectRatio() +{ + shader.removeDefine("ASPECT_AXIS_X"); + shader.removeDefine("ASPECT_AXIS_Y"); + shader.removeDefine("ASPECT_CROP"); + + inAspectPos.setUiAttribs({ "greyout": !inAspect.get() }); + inAspectCrop.setUiAttribs({ "greyout": !inAspect.get() }); + inAspectAxis.setUiAttribs({ "greyout": !inAspect.get() }); + + if (inAspect.get()) + { + shader.define("ASPECT_RATIO"); + + if (inAspectCrop.get()) shader.define("ASPECT_CROP"); + + if (inAspectAxis.get() == "X") shader.define("ASPECT_AXIS_X"); + if (inAspectAxis.get() == "Y") shader.define("ASPECT_AXIS_Y"); + } + else + { + shader.removeDefine("ASPECT_RATIO"); + if (inAspectCrop.get()) shader.define("ASPECT_CROP"); + + if (inAspectAxis.get() == "X") shader.define("ASPECT_AXIS_X"); + if (inAspectAxis.get() == "Y") shader.define("ASPECT_AXIS_Y"); + } +} + +alphaSrc.set("alpha channel"); + +// +// texture flip +// +const flipX = op.inValueBool("flip x"); +const flipY = op.inValueBool("flip y"); + +// +// texture transform +// + +let doTransform = op.inValueBool("Transform"); + +let scaleX = op.inValueSlider("Scale X", 1); +let scaleY = op.inValueSlider("Scale Y", 1); + +let posX = op.inValue("Position X", 0); +let posY = op.inValue("Position Y", 0); + +let rotate = op.inValue("Rotation", 0); + +const inClipRepeat = op.inValueBool("Clip Repeat", false); + +const uniScaleX = new CGL.Uniform(shader, "f", "scaleX", scaleX); +const uniScaleY = new CGL.Uniform(shader, "f", "scaleY", scaleY); +const uniPosX = new CGL.Uniform(shader, "f", "posX", posX); +const uniPosY = new CGL.Uniform(shader, "f", "posY", posY); +const uniRotate = new CGL.Uniform(shader, "f", "rotate", rotate); + +doTransform.onChange = updateTransformPorts; + +function updateTransformPorts() +{ + shader.toggleDefine("TEX_TRANSFORM", doTransform.get()); + + scaleX.setUiAttribs({ "greyout": !doTransform.get() }); + scaleY.setUiAttribs({ "greyout": !doTransform.get() }); + posX.setUiAttribs({ "greyout": !doTransform.get() }); + posY.setUiAttribs({ "greyout": !doTransform.get() }); + rotate.setUiAttribs({ "greyout": !doTransform.get() }); +} + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +render.onTriggered = doRender; + +inClipRepeat.onChange = + imageAlpha.onChange = + inAlphaPremul.onChange = + inAlphaMask.onChange = + invAlphaChannel.onChange = + flipY.onChange = + flipX.onChange = + removeAlphaSrc.onChange = + alphaSrc.onChange = updateDefines; + +updateTransformPorts(); +updateAlphaPorts(); +updateAspectRatio(); +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("REMOVE_ALPHA_SRC", removeAlphaSrc.get()); + shader.toggleDefine("ALPHA_MASK", inAlphaMask.get()); + + shader.toggleDefine("CLIP_REPEAT", inClipRepeat.get()); + + shader.toggleDefine("HAS_TEXTUREALPHA", imageAlpha.get() && imageAlpha.get().tex); + + shader.toggleDefine("TEX_FLIP_X", flipX.get()); + shader.toggleDefine("TEX_FLIP_Y", flipY.get()); + + shader.toggleDefine("INVERT_ALPHA", invAlphaChannel.get()); + + shader.toggleDefine("ALPHA_FROM_LUMINANCE", alphaSrc.get() == "luminance"); + shader.toggleDefine("ALPHA_FROM_INV_UMINANCE", alphaSrc.get() == "luminance_inv"); + shader.toggleDefine("PREMUL", inAlphaPremul.get()); +} + +function doRender() +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + const tex = image.get(); + if (tex && tex.tex && amount.get() > 0.0) + { + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + const imgTex = cgl.currentTextureEffect.getCurrentSourceTexture(); + cgl.setTexture(0, imgTex.tex); + + if (imgTex && tex) + { + if (tex.textureType != imgTex.textureType && (tex.textureType != CGL.Texture.TYPE_FLOAT || imgTex.textureType != CGL.Texture.TYPE_FLOAT)) + op.setUiError("textypediff", "Drawing 32bit texture into an 8 bit can result in data/precision loss", 1); + else + op.setUiError("textypediff", null); + } + + const asp = 1 / (cgl.currentTextureEffect.getWidth() / cgl.currentTextureEffect.getHeight()) * (tex.width / tex.height); + // uniTexAspect.setValue(1 / (tex.height / tex.width * imgTex.width / imgTex.height)); + + uniTexAspect.setValue(asp); + + cgl.setTexture(1, tex.tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, image.get().tex ); + + if (imageAlpha.get() && imageAlpha.get().tex) + { + cgl.setTexture(2, imageAlpha.get().tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, imageAlpha.get().tex ); + } + + // cgl.pushBlend(false); + + cgl.pushBlendMode(CGL.BLEND_NONE, true); + + cgl.currentTextureEffect.finish(); + cgl.popBlendMode(); + + // cgl.popBlend(); + + cgl.popShader(); + } + + trigger.trigger(); +} + + +}; + +Ops.Gl.TextureEffects.DrawImage_v3.prototype = new CABLES.Op(); +CABLES.OPS["8f6b2f15-fcb0-4597-90c0-e5173f2969fe"]={f:Ops.Gl.TextureEffects.DrawImage_v3,objName:"Ops.Gl.TextureEffects.DrawImage_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.EdgeDetection_v4 +// +// ************************************************************** + +Ops.Gl.TextureEffects.EdgeDetection_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"edgedetect_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float width;\nUNI float strength;\nUNI float texWidth;\nUNI float texHeight;\nUNI float mulColor;\n\nconst vec4 lumcoeff = vec4(0.299,0.587,0.114, 0.);\n\nvec3 desaturate(vec3 color)\n{\n return vec3(dot(vec3(0.2126,0.7152,0.0722), color));\n}\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n // vec4 col=vec4(1.0,0.0,0.0,1.0);\n\n // float pixelX=0.27/texWidth;\n // float pixelY=0.27/texHeight;\n float pixelX=(width+0.01)*4.0/texWidth;\n float pixelY=(width+0.01)*4.0/texHeight;\n\nvec2 tc=texCoord;\n// #ifdef OFFSETPIXEL\n tc.x+=1.0/texWidth*0.5;\n tc.y+=1.0/texHeight*0.5;\n// #endif\n // col=texture(tex,texCoord);\n\n float count=1.0;\n vec4 base=texture(tex,texCoord);\n\n\tvec4 horizEdge = vec4( 0.0 );\n\thorizEdge -= texture( tex, vec2( tc.x - pixelX, tc.y - pixelY ) ) * 1.0;\n\thorizEdge -= texture( tex, vec2( tc.x - pixelX, tc.y ) ) * 2.0;\n\thorizEdge -= texture( tex, vec2( tc.x - pixelX, tc.y + pixelY ) ) * 1.0;\n\thorizEdge += texture( tex, vec2( tc.x + pixelX, tc.y - pixelY ) ) * 1.0;\n\thorizEdge += texture( tex, vec2( tc.x + pixelX, tc.y ) ) * 2.0;\n\thorizEdge += texture( tex, vec2( tc.x + pixelX, tc.y + pixelY ) ) * 1.0;\n\tvec4 vertEdge = vec4( 0.0 );\n\tvertEdge -= texture( tex, vec2( tc.x - pixelX, tc.y - pixelY ) ) * 1.0;\n\tvertEdge -= texture( tex, vec2( tc.x , tc.y - pixelY ) ) * 2.0;\n\tvertEdge -= texture( tex, vec2( tc.x + pixelX, tc.y - pixelY ) ) * 1.0;\n\tvertEdge += texture( tex, vec2( tc.x - pixelX, tc.y + pixelY ) ) * 1.0;\n\tvertEdge += texture( tex, vec2( tc.x , tc.y + pixelY ) ) * 2.0;\n\tvertEdge += texture( tex, vec2( tc.x + pixelX, tc.y + pixelY ) ) * 1.0;\n\n\thorizEdge*=base.a;\n\tvertEdge*=base.a;\n\n\n\tvec3 edge = sqrt((horizEdge.rgb/count * horizEdge.rgb/count) + (vertEdge.rgb/count * vertEdge.rgb/count));\n\n edge=desaturate(edge);\n edge*=strength;\n\n if(mulColor>0.0) edge*=texture( tex, texCoord ).rgb*mulColor*4.0;\n edge=max(min(edge,1.0),0.0);\n\n //blend section\n vec4 col=vec4(edge,base.a);\n\n outColor=cgl_blendPixel(base,col,amount*base.a);\n}\n\n",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + strength = op.inFloat("Strength", 4.0), + width = op.inValueSlider("Width", 0.1), + mulColor = op.inValueSlider("Mul Color", 0), + trigger = op.outTrigger("Trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.edgedetect_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + strengthUniform = new CGL.Uniform(shader, "f", "strength", strength), + widthUniform = new CGL.Uniform(shader, "f", "width", width), + uniWidth = new CGL.Uniform(shader, "f", "texWidth", 128), + uniHeight = new CGL.Uniform(shader, "f", "texHeight", 128), + uniMulColor = new CGL.Uniform(shader, "f", "mulColor", mulColor); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + uniWidth.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().width); + uniHeight.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().height); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.EdgeDetection_v4.prototype = new CABLES.Op(); +CABLES.OPS["0240e26e-b86d-43b2-8c72-6795bb86dc76"]={f:Ops.Gl.TextureEffects.EdgeDetection_v4,objName:"Ops.Gl.TextureEffects.EdgeDetection_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Emboss +// +// ************************************************************** + +Ops.Gl.TextureEffects.Emboss = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"emboss_frag":"#ifdef HAS_TEXTURES\n IN vec2 texCoord;\n UNI sampler2D tex;\n UNI float texSizeX;\n UNI float texSizeY;\n#endif\n\nUNI float strength;\n\nvoid main()\n{\n vec4 orig=texture(tex, texCoord);\n vec2 texelSize=vec2(texSizeX,texSizeY);\n\n float tl = abs(texture(tex, texCoord + texelSize * vec2(-1.0, -1.0)).x); // top left\n float l = abs(texture(tex, texCoord + texelSize * vec2(-1.0, 0.0)).x); // left\n float bl = abs(texture(tex, texCoord + texelSize * vec2(-1.0, 1.0)).x); // bottom left\n float t = abs(texture(tex, texCoord + texelSize * vec2( 0.0, -1.0)).x); // top\n float b = abs(texture(tex, texCoord + texelSize * vec2( 0.0, 1.0)).x); // bottom\n float tr = abs(texture(tex, texCoord + texelSize * vec2( 1.0, -1.0)).x); // top right\n float r = abs(texture(tex, texCoord + texelSize * vec2( 1.0, 0.0)).x); // right\n float br = abs(texture(tex, texCoord + texelSize * vec2( 1.0, 1.0)).x); // bottom right\n\n float dX = tr + 2.0*r + br -tl - 2.0*l - bl;\n float dY = bl + 2.0*b + br -tl - 2.0*t - tr;\n\n vec4 N=vec4(1.0);\n\n N.rgb=orig.rgb;\n\n #ifdef CLEAR\n N.rgb=vec3(0.5,0.5,0.5);\n #endif\n\n N.rgb-=vec3(dY)*strength;\n N.rgb+=vec3(dX)*strength;\n\n outColor= N;\n}",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + strength = op.inValue("Strength", 4), + clear = op.inValueBool("Clear", true), + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.emboss_frag || ""); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +let uniStrength = new CGL.Uniform(shader, "f", "strength", strength); +let unitexSizeX = new CGL.Uniform(shader, "f", "texSizeX", 1024); +let unitexSizeY = new CGL.Uniform(shader, "f", "texSizeY", 1024); + +clear.onChange = updateClear; +updateClear(); + +function updateClear() +{ + if (clear.get())shader.define("CLEAR"); + else shader.removeDefine("CLEAR"); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + unitexSizeX.set(1.0 / cgl.currentTextureEffect.getCurrentSourceTexture().width); + unitexSizeY.set(1.0 / cgl.currentTextureEffect.getCurrentSourceTexture().height); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Emboss.prototype = new CABLES.Op(); +CABLES.OPS["e973e104-392a-44f0-bbc5-c382a08c15df"]={f:Ops.Gl.TextureEffects.Emboss,objName:"Ops.Gl.TextureEffects.Emboss"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.FXAA +// +// ************************************************************** + +Ops.Gl.TextureEffects.FXAA = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"fxaa_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float FXAA_SPAN_MAX;\nUNI float FXAA_REDUCE_MUL;\nUNI float FXAA_REDUCE_MIN;\nUNI float width;\nUNI float height;\n\nvec4 getColorFXAA(vec2 coord)\n{\n vec2 invtexsize=vec2(1.0/width,1.0/height);\n\n float step=1.0;\n\n vec3 rgbNW = texture(tex, coord.xy + (vec2(-step, -step)*invtexsize )).xyz;\n vec3 rgbNE = texture(tex, coord.xy + (vec2(+step, -step)*invtexsize )).xyz;\n vec3 rgbSW = texture(tex, coord.xy + (vec2(-step, +step)*invtexsize )).xyz;\n vec3 rgbSE = texture(tex, coord.xy + (vec2(+step, +step)*invtexsize )).xyz;\n vec3 rgbM = texture(tex, coord.xy).xyz;\n\n vec3 luma = vec3(0.299, 0.587, 0.114);\n float lumaNW = dot(rgbNW, luma);\n float lumaNE = dot(rgbNE, luma);\n float lumaSW = dot(rgbSW, luma);\n float lumaSE = dot(rgbSE, luma);\n float lumaM = dot( rgbM, luma);\n\n float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\n vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\n float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\n float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);\n\n dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),\n max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin))*invtexsize ;\n\n vec3 rgbA = (1.0/2.0) * (\n texture(tex, coord.xy + dir * (1.0/3.0 - 0.5)).xyz +\n texture(tex, coord.xy + dir * (2.0/3.0 - 0.5)).xyz);\n vec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (\n texture(tex, coord.xy + dir * (0.0/3.0 - 0.5)).xyz +\n texture(tex, coord.xy + dir * (3.0/3.0 - 0.5)).xyz);\n float lumaB = dot(rgbB, luma);\n\n vec4 color=texture(tex,coord).rgba;\n\n if((lumaB < lumaMin) || (lumaB > lumaMax)){\n color.xyz=rgbA;\n } else {\n color.xyz=rgbB;\n }\n return color;\n}\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n outColor= getColorFXAA(texCoord);\n}",}; +// shader from: https://github.com/mattdesl/glsl-fxaa + +let render = op.inTrigger("render"); +let trigger = op.outTrigger("trigger"); +let fxaa_span = op.inValueSelect("span", [0, 2, 4, 8, 16, 32, 64]); +let fxaa_reduceMin = op.inValueFloat("reduceMin"); +let fxaa_reduceMul = op.inValueFloat("reduceMul"); +let useVPSize = op.inValueBool("use viewport size", true); +let texWidth = op.inValueInt("width"); +let texHeight = op.inValueInt("height"); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.fxaa_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + cgl.pushShader(shader); + + if (cgl.getViewPort()[2] != texWidth.get() || cgl.getViewPort()[3] != texHeight.get()) + { + changeRes(); + } + + cgl.currentTextureEffect.bind(); + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + + cgl.currentTextureEffect.finish(); + + cgl.popShader(); + + trigger.trigger(); +}; + + +let uniformSpan = new CGL.Uniform(shader, "f", "FXAA_SPAN_MAX", 0); +let uniformMul = new CGL.Uniform(shader, "f", "FXAA_REDUCE_MUL", 0); +let uniformMin = new CGL.Uniform(shader, "f", "FXAA_REDUCE_MIN", 0); + +fxaa_span.onChange = function () +{ + uniformSpan.setValue(parseInt(fxaa_span.get(), 10)); +}; + +let uWidth = new CGL.Uniform(shader, "f", "width", 0); +let uHeight = new CGL.Uniform(shader, "f", "height", 0); + +function changeRes() +{ + if (useVPSize.get()) + { + let w = cgl.getViewPort()[2]; + let h = cgl.getViewPort()[3]; + uWidth.setValue(w); + uHeight.setValue(h); + // texWidth.set(w); + // texHeight.set(h); + } + else + { + uWidth.setValue(texWidth.get()); + uHeight.setValue(texHeight.get()); + } +} + +texWidth.onChange = changeRes; +texHeight.onChange = changeRes; +useVPSize.onChange = changeRes; +op.onResize = changeRes; + +fxaa_span.set(8); +// texWidth.set(1920); +// texHeight.set(1080); + +fxaa_reduceMul.onChange = function () +{ + uniformMul.setValue(1.0 / fxaa_reduceMul.get()); +}; + +fxaa_reduceMin.onChange = function () +{ + uniformMin.setValue(1.0 / fxaa_reduceMin.get()); +}; + +fxaa_reduceMul.set(8); +fxaa_reduceMin.set(128); + + +}; + +Ops.Gl.TextureEffects.FXAA.prototype = new CABLES.Op(); +CABLES.OPS["3e679c17-f050-4bc8-bfe5-5b9190e7ce40"]={f:Ops.Gl.TextureEffects.FXAA,objName:"Ops.Gl.TextureEffects.FXAA"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.FastBlur_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.FastBlur_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"blur_frag":"\nUNI sampler2D tex;\n#ifdef USE_MASK\n UNI sampler2D texMask;\n#endif\nUNI float amount;\nUNI float pass;\n\nIN vec2 texCoord;\n\nUNI float dirX;\nUNI float dirY;\nUNI float width;\nUNI float height;\n\nIN vec2 coord0;\nIN vec2 coord1;\nIN vec2 coord2;\nIN vec2 coord3;\nIN vec2 coord4;\nIN vec2 coord5;\nIN vec2 coord6;\n\n#ifdef HAS_MASK\n UNI sampler2D imageMask;\n#endif\n\nvoid main()\n{\n vec4 color = vec4(0.0);\n\n #ifdef USE_MASK\n #ifdef MASK_INVERT\n if(texture(texMask,texCoord).r<0.5)\n {\n outColor= texture(tex, texCoord);\n return;\n }\n #endif\n\n #ifndef MASK_INVERT\n if(texture(texMask,texCoord).r>0.5)\n {\n outColor= texture(tex, texCoord);\n return;\n }\n #endif\n #endif\n\n color += texture(tex, coord0) * 0.06927096443792478;\n color += texture(tex, coord1) * 0.1383328848652136;\n color += texture(tex, coord2) * 0.21920904690397863;\n color += texture(tex, coord3) * 0.14637421;\n color += texture(tex, coord4) * 0.21920904690397863;\n color += texture(tex, coord5) * 0.1383328848652136;\n color += texture(tex, coord6) * 0.06927096443795711;\n\n outColor= color;\n}","blur_vert":"\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\nOUT vec2 texCoord;\nOUT vec3 norm;\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\nUNI mat4 modelMatrix;\n\nUNI float pass;\nUNI float dirX;\nUNI float dirY;\nUNI float width;\nUNI float height;\n\nOUT vec2 coord0;\nOUT vec2 coord1;\nOUT vec2 coord2;\nOUT vec2 coord3;\nOUT vec2 coord4;\nOUT vec2 coord5;\nOUT vec2 coord6;\n\nvoid main()\n{\n texCoord=attrTexCoord;\n norm=attrVertNormal;\n vec4 pos=vec4(vPosition, 1.0);\n {{MODULE_VERTEX_POSITION}}\n\n vec2 dir=vec2(dirX,dirY);\n vec2 res=vec2( (1.) / width , (1.) / height )*dir;\n\n coord3= attrTexCoord;\n\n coord0= attrTexCoord + (-3.0368997744118595 * res);\n coord1= attrTexCoord + (-2.089778445362373 * res);\n coord2= attrTexCoord + (-1.2004366090034069 * res);\n coord4= attrTexCoord + (1.2004366090034069 * res);\n coord5= attrTexCoord + (2.089778445362373* res);\n coord6= attrTexCoord + (3.0368997744118595 * res);\n\n #ifdef CLAMP\n coord0=clamp(coord0,0.0,1.0);\n coord1=clamp(coord1,0.0,1.0);\n coord2=clamp(coord2,0.0,1.0);\n coord3=clamp(coord3,0.0,1.0);\n coord4=clamp(coord4,0.0,1.0);\n coord5=clamp(coord5,0.0,1.0);\n coord6=clamp(coord6,0.0,1.0);\n #endif\n\n gl_Position = projMatrix * mvMatrix * pos;\n}\n",}; +// http://dev.theomader.com/gaussian-kernel-calculator/ +// http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ + +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + inPasses = op.inFloat("Passes", 3), + clamp = op.inBool("Clamp", false), + direction = op.inDropDown("direction", ["both", "vertical", "horizontal"], "both"), + mask = op.inTexture("Mask"), + maskInvert = op.inBool("Mask Invert", false); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "fastblur"); + +op.setPortGroup("Mask", [mask, maskInvert]); + +shader.setSource(attachments.blur_vert, attachments.blur_frag); +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniDirX = new CGL.Uniform(shader, "f", "dirX", 0), + uniDirY = new CGL.Uniform(shader, "f", "dirY", 0), + uniWidth = new CGL.Uniform(shader, "f", "width", 0), + uniHeight = new CGL.Uniform(shader, "f", "height", 0), + uniPass = new CGL.Uniform(shader, "f", "pass", 0), + uniAmount = new CGL.Uniform(shader, "f", "amount", inPasses.get()), + textureAlpha = new CGL.Uniform(shader, "t", "texMask", 1); + +inPasses.onChange = () => { uniAmount.setValue(inPasses.get()); }; + +let dir = 0; +direction.onChange = () => +{ + if (direction.get() == "both") dir = 0; + if (direction.get() == "horizontal") dir = 1; + if (direction.get() == "vertical") dir = 2; +}; + +clamp.onChange = () => { shader.toggleDefine("CLAMP", clamp.get()); }; + +maskInvert.onChange = + mask.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("USE_MASK", mask.isLinked()); + shader.toggleDefine("MASK_INVERT", maskInvert.get()); + + maskInvert.setUiAttribs({ "greyout": !mask.isLinked() }); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + uniWidth.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().width); + uniHeight.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().height); + const numPasses = inPasses.get(); + + if (mask.get())cgl.setTexture(1, mask.get().tex); + + for (let i = 0; i < numPasses; i++) + { + cgl.pushShader(shader); + + uniPass.setValue(i / numPasses); + + // first pass + if (dir === 0 || dir == 2) + { + cgl.currentTextureEffect.bind(); + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + uniDirX.setValue(0.0); + uniDirY.setValue(1.0 + (i * i)); + + cgl.currentTextureEffect.finish(); + } + + // second pass + if (dir === 0 || dir == 1) + { + cgl.currentTextureEffect.bind(); + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + uniDirX.setValue(1.0 + (i * i)); + uniDirY.setValue(0.0); + + cgl.currentTextureEffect.finish(); + } + + cgl.popShader(); + } + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.FastBlur_v2.prototype = new CABLES.Op(); +CABLES.OPS["61ed277f-d096-43b2-9de8-dc87fb3a9169"]={f:Ops.Gl.TextureEffects.FastBlur_v2,objName:"Ops.Gl.TextureEffects.FastBlur_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Flip +// +// ************************************************************** + +Ops.Gl.TextureEffects.Flip = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"flip_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float x;\nUNI float y;\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n col=texture(tex,vec2(abs(x-texCoord.x),abs(y-texCoord.y)));\n outColor= col;\n}",}; +const render = op.inTrigger("render"); +const x = op.inValueBool("X"); +const y = op.inValueBool("Y"); +const trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.flip_frag); + +const uniTexture = new CGL.Uniform(shader, "t", "tex", 0); +const uniX = new CGL.Uniform(shader, "f", "x", x); +const uniY = new CGL.Uniform(shader, "f", "y", y); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Flip.prototype = new CABLES.Op(); +CABLES.OPS["ce36ad01-411a-412f-affa-1959aa23f93b"]={f:Ops.Gl.TextureEffects.Flip,objName:"Ops.Gl.TextureEffects.Flip"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Float32ToRgbeTexture +// +// ************************************************************** + +Ops.Gl.TextureEffects.Float32ToRgbeTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rgbe2fp_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\n\n\n// highp vec3 decodeRGBE8(highp vec4 rgbe)\n// {\n// highp vec3 vDecoded = rgbe.rgb * pow(2.0, rgbe.a * 255.0-128.0);\n// return vDecoded;\n// }\nvec4 encodeRGBE8( vec3 rgb )\n{\n vec4 vEncoded;\n float maxComponent = max(max(rgb.r, rgb.g), rgb.b );\n float fExp = ceil( log2(maxComponent) );\n vEncoded.rgb = rgb / exp2(fExp);\n vEncoded.a = (fExp + 128.0) / 255.0;\n return vEncoded;\n}\n\n\nvoid main()\n{\n vec4 col=vec4( encodeRGBE8(texture(tex,texCoord).rgb));\n\n outColor= col;\n}",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.rgbe2fp_frag); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Float32ToRgbeTexture.prototype = new CABLES.Op(); +CABLES.OPS["ae3585c4-b49a-4dd5-b7be-1f53a90bb060"]={f:Ops.Gl.TextureEffects.Float32ToRgbeTexture,objName:"Ops.Gl.TextureEffects.Float32ToRgbeTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Fog_v4 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Fog_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"fog_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D texDepth;\n\n#ifdef HAS_GRADIENT_TEX\n UNI sampler2D texGradient;\n#endif\n\n#ifdef HAS_BG_TEX\n UNI sampler2D texBg;\n#endif\n\nUNI float nearPlane;\nUNI float farPlane;\nUNI float inAmount;\nUNI vec4 inFogColor;\nUNI float inFogDensity;\nUNI float inFogStart;\nUNI float inFogEnd;\n\n{{CGL.BLENDMODES3}}\n\nfloat map(float value, float min1, float max1, float min2, float max2) {\n return min2 + (value - min1) * (max2 - min2) / (max1 - min1);\n}\n\nfloat CalcFogDensity(float depth) {\n float newDepth = map(depth, nearPlane, farPlane, 0., 1.);\n float fogAmount = 1.0 - clamp((inFogEnd - depth) / (inFogEnd - inFogStart), 0.0, 1.0);\n\n // EXPONENTIAL: fogAmount = 1. - exp(MAGIC_NUMBER * -inFogDensity * newDepth); // smoothstep(fogStart, fogEnd, newDepth));\n // EXP2: fogAmount = 1. - exp(-pow(MAGIC_NUMBER * inFogDensity * smoothstep(fogStart, fogEnd, newDepth), 2.0));\n\n fogAmount *= inFogDensity;\n\n return fogAmount;\n}\n\nvoid main()\n{\n vec4 color = texture(tex, texCoord);\n\n float depthFromTexture = texture(texDepth,texCoord).r;\n\n float distanceToCamera_viewSpace = (nearPlane * farPlane) / (farPlane - depthFromTexture * (farPlane - nearPlane));\n\n float fogAmount = CalcFogDensity(distanceToCamera_viewSpace);\n\n vec4 fogColor = inFogColor;\n\n #ifdef HAS_GRADIENT_TEX\n vec4 fogColTex = texture(texGradient, vec2(clamp(distanceToCamera_viewSpace / (farPlane - nearPlane), 0.0, 0.9999),0.5));\n fogColor *= fogColTex;\n #endif\n\n #ifdef HAS_BG_TEX\n vec4 fogColTexBg = texture(texBg, texCoord);\n fogColor *= fogColTexBg;\n\n #endif\n\n fogColor = color * (1.0 - fogAmount) + fogColor * fogAmount;\n // fogColor = mix(color, fogColor, fogAmount);\n\n\n outColor = cgl_blendPixel(color, fogColor, inAmount);\n\n}\n","fog_vert":"IN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\n\nOUT vec2 texCoord;\nOUT vec3 norm;\n\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\nUNI mat4 modelMatrix;\n\nvoid main()\n{\n texCoord = attrTexCoord;\n norm = attrVertNormal;\n vec4 pos = vec4(vPosition, 1.0);\n\n {{MODULE_VERTEX_POSITION}}\n\n gl_Position = projMatrix * mvMatrix * pos;\n\n}\n",}; +const cgl = op.patch.cgl; + +const render = op.inTrigger("render"); +const blendMode = CGL.TextureEffect.AddBlendSelect(op, "blendMode"); +const inAmount = op.inFloatSlider("Amount", 1); +const image = op.inTexture("Depth Texture"); +const inGradientTexture = op.inTexture("Gradient Texture"); +const inBgTex = op.inTexture("Background Texture"); +const inFogStart = op.inFloat("Fog Start", 1); +const inFogEnd = op.inFloat("Fog End", 8); +const inFogDensity = op.inFloatSlider("Fog Density", 1); +const nearPlane = op.inFloat("nearplane", 0.1); +const farPlane = op.inFloat("farplane", 20); +const inFogR = op.inFloatSlider("Fog R", 0.6); +const inFogG = op.inFloatSlider("Fog G", 0.6); +const inFogB = op.inFloatSlider("Fog B", 0.6); +const inFogA = op.inFloatSlider("Fog A", 1); +inFogR.setUiAttribs({ "colorPick": true }); + +const trigger = op.outTrigger("trigger"); + +op.setPortGroup("Textures", [image, inGradientTexture, inBgTex]); +op.setPortGroup("Frustum", [farPlane, nearPlane]); +op.setPortGroup("Fog Options", [inFogStart, inFogEnd, inFogDensity]); +op.setPortGroup("Fog Color", [inFogR, inFogG, inFogB, inFogA]); + +const shader = new CGL.Shader(cgl, "Fog"); +const srcFrag = attachments.fog_frag; +const srcVert = attachments.fog_vert; +shader.setSource(srcVert, srcFrag); + +const uniAmount = new CGL.Uniform(shader, "f", "inAmount", inAmount); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const depthTextureUniform = new CGL.Uniform(shader, "t", "texDepth", 1); +const uniGradientTexture = new CGL.Uniform(shader, "t", "texGradient", 2); +const uniBgTexture = new CGL.Uniform(shader, "t", "texBg", 3); + +const uniFarplane = new CGL.Uniform(shader, "f", "farPlane", farPlane); +const uniNearplane = new CGL.Uniform(shader, "f", "nearPlane", nearPlane); + +const uniAspect = new CGL.Uniform(shader, "f", "aspectRatio", 0); +const uniFogColor = new CGL.Uniform(shader, "4f", "inFogColor", inFogR, inFogG, inFogB, inFogA); +const uniFogDensity = new CGL.Uniform(shader, "f", "inFogDensity", inFogDensity); +const uniFogStart = new CGL.Uniform(shader, "f", "inFogStart", inFogStart); +const uniFogEnd = new CGL.Uniform(shader, "f", "inFogEnd", inFogEnd); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, inAmount); + +let texturesChanged = false; +inGradientTexture.onChange = +inBgTex.onChange = () => +{ + texturesChanged = true; +}; + +function updateDefines() +{ + shader.toggleDefine("HAS_BG_TEX", inBgTex.get() && inBgTex.get().tex); + + if (inGradientTexture.get() && inGradientTexture.get().tex) shader.define("HAS_GRADIENT_TEX"); + else shader.removeDefine("HAS_GRADIENT_TEX"); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + if (!image.get()) + { + op.setUiError("noDepthTex", "This op needs a depth texture to work properly!", 0); + } + else + { + op.setUiError("noDepthTex", null); + } + + if (texturesChanged)updateDefines(); + + if (image.get() && image.get().tex) + { + const a = + cgl.currentTextureEffect.getCurrentSourceTexture().height + / cgl.currentTextureEffect.getCurrentSourceTexture().width; + + uniAspect.set(a); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (image.get()) cgl.setTexture(1, image.get().tex); + if (inGradientTexture.get()) cgl.setTexture(2, inGradientTexture.get().tex); + if (inBgTex.get()) cgl.setTexture(3, inBgTex.get().tex); + cgl.currentTextureEffect.finish(); + cgl.popShader(); + } + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Fog_v4.prototype = new CABLES.Op(); +CABLES.OPS["9d52bb1d-cf80-4f75-a80b-bdca16421d65"]={f:Ops.Gl.TextureEffects.Fog_v4,objName:"Ops.Gl.TextureEffects.Fog_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.GammaCorrection_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.GammaCorrection_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"gamma_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float multiplyAmount;\nUNI float gammaAmount;\n\n{{CGL.BLENDMODES}}\n\nvoid main()\n{\n vec4 base4=texture(tex,texCoord);\n vec3 color = base4.rgb * multiplyAmount;\n\n outColor= vec4(\n mix(\n color,\n vec3(pow(color,vec3(1.0 / gammaAmount))\n ),amount),\n base4.a);\n}",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + inMultiply = op.inValue("Multiply texture", 1.0), + inGamma = op.inValue("Gamma correction", 2.2), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.gamma_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); +const multiplyUniform = new CGL.Uniform(shader, "f", "multiplyAmount", inMultiply); +const gammaUniform = new CGL.Uniform(shader, "f", "gammaAmount", inGamma); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.GammaCorrection_v2.prototype = new CABLES.Op(); +CABLES.OPS["18da81c9-bcae-4446-ac58-d3ba31808013"]={f:Ops.Gl.TextureEffects.GammaCorrection_v2,objName:"Ops.Gl.TextureEffects.GammaCorrection_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Gradient_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Gradient_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"gradient_frag":"IN vec2 texCoord;\nUNI float amount;\nUNI float pos;\nUNI float width;\n\nUNI vec3 colA;\nUNI vec3 colB;\nUNI vec3 colC;\nUNI sampler2D tex;\n\n{{CGL.BLENDMODES3}}\n\n\n\n\nvec3 lin2srgb( vec3 cl )\n{\n\tcl = clamp( cl, 0.0, 1.0 );\n\tvec3 c_lo = 12.92 * cl;\n\tvec3 c_hi = 1.055 * pow(cl,vec3(0.41666,0.41666,0.41666)) - 0.055;\n\treturn vec3( (cl.r<0.0031308) ? c_lo.r : c_hi.r,\n (cl.g<0.0031308) ? c_lo.g : c_hi.g,\n (cl.b<0.0031308) ? c_lo.b : c_hi.b );\n}\n\nvec3 oklab_mix( vec3 colA, vec3 colB, float h )\n{\n // https://www.shadertoy.com/view/ttcyRS\n // https://bottosson.github.io/posts/oklab\n const mat3 kCONEtoLMS = mat3(\n 0.4121656120, 0.2118591070, 0.0883097947,\n 0.5362752080, 0.6807189584, 0.2818474174,\n 0.0514575653, 0.1074065790, 0.6302613616);\n const mat3 kLMStoCONE = mat3(\n 4.0767245293, -1.2681437731, -0.0041119885,\n -3.3072168827, 2.6093323231, -0.7034763098,\n 0.2307590544, -0.3411344290, 1.7068625689);\n\n // rgb to cone (arg of pow can't be negative)\n vec3 lmsA = pow( kCONEtoLMS*colA, vec3(1.0/3.0) );\n vec3 lmsB = pow( kCONEtoLMS*colB, vec3(1.0/3.0) );\n // lerp\n vec3 lms = mix( lmsA, lmsB, h );\n // gain in the middle (no oaklab anymore, but looks better?)\n #ifdef OKLABGAIN\n lms *= 1.0+0.2*h*(1.0-h);\n #endif\n // cone to rgb\n return kLMStoCONE*(lms*lms*lms);\n}\n\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n vec4 col;\n float ax=texCoord.x;\n\n #ifdef GRAD_Y\n ax=texCoord.y;\n #endif\n #ifdef GRAD_XY\n ax=(texCoord.x+texCoord.y)/2.0;\n #endif\n #ifdef GRAD_RADIAL\n ax=distance(texCoord,vec2(0.5,0.5))*2.0;\n #endif\n\n ax=((ax-0.5)*width)+0.5;\nax=clamp(ax,0.0,1.0);\n\n #ifndef GRAD_SMOOTHSTEP\n if(ax<=pos) col = vec4(MIXER(colA, colB, ax*1.0/pos),1.0);\n else col = vec4(MIXER(colB, colC, min(1.0,(ax-pos)*1.0/(1.0-pos))),1.0);\n #endif\n\n #ifdef GRAD_SMOOTHSTEP\n if(ax<=pos) col = vec4(MIXER(colA, colB, smoothstep(0.0,1.0,ax*1.0/pos)),1.0);\n else col = vec4(MIXER(colB, colC, smoothstep(0.0,1.0,min(1.0,(ax-pos)*1.0/(1.0-pos)))),1.0);\n #endif\n\n #ifdef SRGB\n col.rgb=lin2srgb(col.rgb);\n #endif\n\n outColor=cgl_blendPixel(base,col,amount);\n}",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + amount = op.inValueSlider("Amount", 1), + width = op.inValue("Width", 1), + gType = op.inSwitch("Type", ["X", "Y", "XY", "Radial"], "X"), + pos1 = op.inValueSlider("Pos", 0.5), + smoothStep = op.inValueBool("Smoothstep", true), + inSrgb = op.inValueBool("sRGB", false), + inColSpace = op.inSwitch("color space", ["RGB", "Oklab", "OklabG"], "RGB"), + + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + + r2 = op.inValueSlider("r2", Math.random()), + g2 = op.inValueSlider("g2", Math.random()), + b2 = op.inValueSlider("b2", Math.random()), + + r3 = op.inValueSlider("r3", Math.random()), + g3 = op.inValueSlider("g3", Math.random()), + b3 = op.inValueSlider("b3", Math.random()), + + randomize = op.inTriggerButton("Randomize"), + next = op.outTrigger("Next"); + +r.setUiAttribs({ "colorPick": true }); +r2.setUiAttribs({ "colorPick": true }); +r3.setUiAttribs({ "colorPick": true }); + +op.setPortGroup("Blending", [blendMode, amount]); +op.setPortGroup("Color A", [r, g, b]); +op.setPortGroup("Color B", [r2, g2, b2]); +op.setPortGroup("Color C", [r3, g3, b3]); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "gradient"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.gradient_frag); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); +const uniPos = new CGL.Uniform(shader, "f", "pos", pos1); +const uniWidth = new CGL.Uniform(shader, "f", "width", width); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +let r3uniform, r2uniform, runiform; + +r2.onChange = g2.onChange = b2.onChange = updateCol2; +r3.onChange = g3.onChange = b3.onChange = updateCol3; +r.onChange = g.onChange = b.onChange = updateCol; + +r2.onLinkChanged = g2.onLinkChanged = b2.onLinkChanged = +r3.onLinkChanged = g3.onLinkChanged = b3.onLinkChanged = +r.onLinkChanged = g.onLinkChanged = b.onLinkChanged = updateUi; + +updateCol(); +updateCol2(); +updateCol3(); +updateDefines(); + +inSrgb.onChange = +inColSpace.onChange = +smoothStep.onChange = + gType.onChange = updateDefines; + +function updateUi() +{ + randomize.setUiAttribs({ "greyout": r2.isLinked() || g2.isLinked() || b2.isLinked() || r3.isLinked() || g3.isLinked() || b3.isLinked() || r.isLinked() || g.isLinked() || b.isLinked() }); +} + +function updateDefines() +{ + // shader.toggleDefine("OKLABGAIN", inoklabGain.get()); + shader.toggleDefine("SRGB", inSrgb.get()); + + shader.define("MIXER", (inColSpace.get() + "").indexOf("Oklab") > -1 ? "oklab_mix" : "mix"); + shader.toggleDefine("OKLABGAIN", (inColSpace.get() + "").indexOf("OklabG") > -1); + + shader.toggleDefine("GRAD_SMOOTHSTEP", smoothStep.get()); + shader.toggleDefine("GRAD_X", gType.get() == "X"); + shader.toggleDefine("GRAD_XY", gType.get() == "XY"); + shader.toggleDefine("GRAD_Y", gType.get() == "Y"); + shader.toggleDefine("GRAD_RADIAL", gType.get() == "Radial"); +} + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +randomize.onTriggered = function () +{ + r.set(Math.random()); + g.set(Math.random()); + b.set(Math.random()); + + r2.set(Math.random()); + g2.set(Math.random()); + b2.set(Math.random()); + + r3.set(Math.random()); + g3.set(Math.random()); + b3.set(Math.random()); + + op.refreshParams(); +}; + +function updateCol() +{ + const colA = [r.get(), g.get(), b.get()]; + if (!runiform) runiform = new CGL.Uniform(shader, "3f", "colA", colA); + else runiform.setValue(colA); +} + +function updateCol2() +{ + const colB = [r2.get(), g2.get(), b2.get()]; + if (!r2uniform) r2uniform = new CGL.Uniform(shader, "3f", "colB", colB); + else r2uniform.setValue(colB); +} + +function updateCol3() +{ + const colC = [r3.get(), g3.get(), b3.get()]; + if (!r3uniform) r3uniform = new CGL.Uniform(shader, "3f", "colC", colC); + else r3uniform.setValue(colC); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + next.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Gradient_v2.prototype = new CABLES.Op(); +CABLES.OPS["c8a9408a-75e5-481f-99a7-6aa7ca88bebc"]={f:Ops.Gl.TextureEffects.Gradient_v2,objName:"Ops.Gl.TextureEffects.Gradient_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.GridTexture_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.GridTexture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"grid_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI bool invertColor;\nUNI float lineThicknessX;\nUNI float lineThicknessY;\nUNI float cellsX;\nUNI float cellsY;\nUNI float offsetX;\nUNI float offsetY;\nUNI float rotate;\n\nUNI float lineR;\nUNI float lineG;\nUNI float lineB;\n\n{{CGL.BLENDMODES3}}\n\n#define PI 3.14159265\n#define TAU (2.0*PI)\n\nvoid pR(inout vec2 p, float a)\n{\n\tp = cos(a)*p + sin(a)*vec2(p.y, -p.x);\n}\n\nfloat vmax(vec2 v)\n{\n\treturn max(v.x, v.y);\n}\nfloat fBox2(vec2 p, vec2 b)\n{\n\tvec2 d = abs(p) - b;\n\treturn length(max(d, vec2(0.0))) + vmax(min(d, vec2(0.0)));\n}\n\nvec2 pMod2(inout vec2 p, vec2 size)\n{\n\tvec2 c = floor((p + size*0.5)/size);\n\tp = mod(p + size*0.5,size) - size*0.5;\n\treturn c;\n}\nvoid main()\n{\n vec2 uv = texCoord;\n uv -= 0.5;\n pR(uv.xy,rotate * (TAU));\n uv += 0.5;\n uv *= vec2(cellsX,cellsY);\n uv -= 0.5;\n uv -= vec2(offsetX,offsetY);\n pMod2(uv,vec2(1.));\n\n float box = 0.0;\n\n if(invertColor) box = 1.0 - sign(fBox2(uv,vec2(lineThicknessX,lineThicknessY)));\n else box = sign(fBox2(uv,vec2(lineThicknessX,lineThicknessY)));\n vec4 color = vec4(vec3(box) * vec3(lineR,lineG,lineB),1.0);\n\n vec4 col=vec4(color);\n vec4 base=texture(tex,texCoord);\n\n outColor=cgl_blendPixel(base,col,amount);\n}",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + + amount = op.inValueSlider("Amount", 1), + + lineThicknessX = op.inValueSlider("Line thickness X", 0.4), + lineThicknessY = op.inValueSlider("Line thickness Y", 0.4), + cellsX = op.inValueFloat("Cells X", 10), + cellsY = op.inValueFloat("Cells Y", 10), + inRotate = op.inValueSlider("Rotate", 0.0), + offsetX = op.inValue("Offset X", 0.0), + offsetY = op.inValue("Offset Y", 0.0), + + invertColor = op.inValueBool("Invert color", false), + r = op.inValueSlider("Line red", Math.random()), + g = op.inValueSlider("Line green", Math.random()), + b = op.inValueSlider("Line Blue", Math.random()); + +r.setUiAttribs({ "colorPick": true }); + +op.setPortGroup("LineThickness", [lineThicknessX, lineThicknessY]); +op.setPortGroup("Cells", [cellsX, cellsY]); +op.setPortGroup("Position", [inRotate, offsetX, offsetY]); + +const trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.grid_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniInvertColor = new CGL.Uniform(shader, "b", "invertColor", invertColor), + unilineThicknessX = new CGL.Uniform(shader, "f", "lineThicknessX", lineThicknessX), + unilineThicknessY = new CGL.Uniform(shader, "f", "lineThicknessY", lineThicknessY), + unicellsX = new CGL.Uniform(shader, "f", "cellsX", cellsX), + unicellsY = new CGL.Uniform(shader, "f", "cellsY", cellsY), + rotateUniform = new CGL.Uniform(shader, "f", "rotate", inRotate), + offsetXUniform = new CGL.Uniform(shader, "f", "offsetX", offsetX), + offsetYUniform = new CGL.Uniform(shader, "f", "offsetY", offsetY), + uniformLineR = new CGL.Uniform(shader, "f", "lineR", r), + uniformLineG = new CGL.Uniform(shader, "f", "lineG", g), + uniformLineB = new CGL.Uniform(shader, "f", "lineB", b); + + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount,maskAlpha); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.GridTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["4e3c7990-9434-4f41-ad79-dfed8395c460"]={f:Ops.Gl.TextureEffects.GridTexture_v2,objName:"Ops.Gl.TextureEffects.GridTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.GrowPixels_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.GrowPixels_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"outline_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float strength;\nUNI float texWidth,texHeight;\nUNI float r,g,b;\nconst vec4 lumcoeff = vec4(0.299,0.587,0.114, 0.);\n\n{{CGL.BLENDMODES3}}\n\nvec3 desaturate(vec3 color)\n{\n return vec3(dot(vec3(0.2126,0.7152,0.0722), color));\n}\n\nvoid main()\n{\n float pixelX=1.0/texWidth*0.5;\n float pixelY=1.0/texHeight*0.5;\n\n vec4 co = texture(tex, vec2(texCoord.x, texCoord.y - pixelY ));\n float n=co.r*co.a;\n co = texture(tex, vec2(texCoord.x, texCoord.y + pixelY ));\n float s=co.r*co.a;\n\n co = texture(tex, vec2(texCoord.x+pixelX, texCoord.y ));\n float e=co.r*co.a;\n co = texture(tex, vec2(texCoord.x-pixelX, texCoord.y ));\n float w=co.r*co.a;\n\n float c=0.0;\n if(n+s+e+w/4.0>((1.0-strength)*0.4)) c=1.0;\n\n vec4 base=texture(tex,texCoord);\n vec4 col=vec4(r*c,g*c,b*c,base.a+c);\n\n outColor=cgl_blendPixel(base,col,amount);\n}\n\n",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + strength = op.inValueSlider("strength", 1), + iter = op.inInt("iterations", 1), + r = op.inValueSlider("r", 1), + g = op.inValueSlider("g", 1), + b = op.inValueSlider("b", 1), + trigger = op.outTrigger("Trigger"); + +op.setPortGroup("Look", strength); +r.setUiAttribs({ "colorPick": true }); +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.outline_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + strengthUniform = new CGL.Uniform(shader, "f", "strength", strength), + uniWidth = new CGL.Uniform(shader, "f", "texWidth", 128), + uniHeight = new CGL.Uniform(shader, "f", "texHeight", 128), + unir = new CGL.Uniform(shader, "f", "r", r), + unig = new CGL.Uniform(shader, "f", "g", g), + unib = new CGL.Uniform(shader, "f", "b", b); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + + for (let i = 0; i < Math.floor(iter.get()); i++) + if (strength.get() > 0.0) + { + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + uniWidth.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().width); + uniHeight.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().height); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + } + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.GrowPixels_v2.prototype = new CABLES.Op(); +CABLES.OPS["f81a7074-7a97-4d1e-bcb8-98f5874a64fc"]={f:Ops.Gl.TextureEffects.GrowPixels_v2,objName:"Ops.Gl.TextureEffects.GrowPixels_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Hue +// +// ************************************************************** + +Ops.Gl.TextureEffects.Hue = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"hue_frag":"UNI float hue;\n\n#ifdef HAS_TEXTURES\n IN vec2 texCoord;\n UNI sampler2D tex;\n#endif\n\n#ifdef TEX_MASK\n UNI sampler2D texMask;\n#endif\n#ifdef TEX_OFFSET\n UNI sampler2D texOffset;\n#endif\n\nvec3 rgb2hsv(vec3 c)\n{\n vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n\n float d = q.x - min(q.w, q.y);\n float e = 1.0e-10;\n return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n}\n\nvec3 hsv2rgb(vec3 c)\n{\n vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n}\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n #ifdef HAS_TEXTURES\n col=texture(tex,texCoord);\n float h=hue;\n\n #ifdef TEX_OFFSET\n h += texture(texOffset,texCoord).r;\n #endif\n\n\n vec3 hsv = rgb2hsv(col.rgb);\n hsv.x=hsv.x+h;\n\n #ifndef TEX_MASK\n col.rgb = hsv2rgb(hsv);\n #endif\n\n #ifdef TEX_MASK\n col.rgb = mix(col.rgb,hsv2rgb(hsv),texture(texMask,texCoord).r);\n #endif\n\n #endif\n outColor= col;\n}",}; +const + render = op.inTrigger("render"), + hue = op.inValueSlider("hue", 1), + texMask = op.inTexture("Mask"), + texOffset = op.inTexture("Offset"), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.hue_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +const textureMaskUniform = new CGL.Uniform(shader, "t", "texMask", 1); +const textureOffsetUniform = new CGL.Uniform(shader, "t", "texOffset", 2); + +const uniformHue = new CGL.Uniform(shader, "f", "hue", 1.0); + +hue.onChange = function () { uniformHue.setValue(hue.get()); }; + +texMask.onChange = +texOffset.onChange = () => +{ + shader.toggleDefine("TEX_MASK", texMask.get()); + shader.toggleDefine("TEX_OFFSET", texOffset.get()); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (texMask.get()) cgl.setTexture(1, texMask.get().tex); + if (texOffset.get()) cgl.setTexture(2, texOffset.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Hue.prototype = new CABLES.Op(); +CABLES.OPS["94ef0da0-c920-415c-81b0-fecbd437991d"]={f:Ops.Gl.TextureEffects.Hue,objName:"Ops.Gl.TextureEffects.Hue"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ImageComposeAspectRatio +// +// ************************************************************** + +Ops.Gl.TextureEffects.ImageComposeAspectRatio = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUpdate=op.inTrigger("Update"), + inAspect=op.inFloat("Aspect",1), + next=op.outTrigger("Next"); + +const cgl=op.patch.cgl; +let prev=1; + +inUpdate.onTriggered=()=> +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + if(cgl.currentTextureEffect) + { + prev=cgl.currentTextureEffect.aspectRatio; + cgl.currentTextureEffect.aspectRatio=inAspect.get(); + + next.trigger(); + + cgl.currentTextureEffect.aspectRatio=prev; + } + +}; + + +}; + +Ops.Gl.TextureEffects.ImageComposeAspectRatio.prototype = new CABLES.Op(); +CABLES.OPS["b346b8cf-b63d-4e1d-b95f-19edf04ae07d"]={f:Ops.Gl.TextureEffects.ImageComposeAspectRatio,objName:"Ops.Gl.TextureEffects.ImageComposeAspectRatio"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ImageComposeSnapshot +// +// ************************************************************** + +Ops.Gl.TextureEffects.ImageComposeSnapshot = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("Update"), + trigger = op.outTrigger("trigger"), + outTex = op.outTexture("Texture"); + +const cgl = op.patch.cgl; +let tc = new CGL.CopyTexture(cgl, "textureThief", {}); +let fp = false; + +render.onTriggered = () => +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + const effect = cgl.currentTextureEffect; + effect.endEffect(); + + const shouldFp = cgl.currentTextureEffect.getCurrentSourceTexture().isFloatingPoint(); + + if (fp != shouldFp) + { + tc = new CGL.CopyTexture(cgl, "textureThief", + { + "isFloatingPointTexture": shouldFp, + }); + fp = shouldFp; + } + + const vp = cgl.getViewPort(); + outTex.set(CGL.Texture.getEmptyTexture(cgl)); + + const tx = cgl.currentTextureEffect.getCurrentSourceTexture(); + outTex.set(tc.copy(tx)); + + effect.continueEffect(tx); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.ImageComposeSnapshot.prototype = new CABLES.Op(); +CABLES.OPS["e15c0803-02bb-4783-9d75-e75abd70d910"]={f:Ops.Gl.TextureEffects.ImageComposeSnapshot,objName:"Ops.Gl.TextureEffects.ImageComposeSnapshot"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ImageCompose_v3 +// +// ************************************************************** + +Ops.Gl.TextureEffects.ImageCompose_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"imgcomp_frag":"IN vec2 texCoord;\nUNI vec4 bgColor;\nUNI sampler2D tex;\n\nvoid main()\n{\n\n #ifndef USE_TEX\n outColor=bgColor;\n #endif\n #ifdef USE_TEX\n outColor=texture(tex,texCoord);\n #endif\n\n\n\n}\n",}; +const + cgl = op.patch.cgl, + render = op.inTrigger("Render"), + inTex = op.inTexture("Base Texture"), + inSize = op.inSwitch("Size", ["Auto", "Manual"], "Auto"), + width = op.inValueInt("Width", 640), + height = op.inValueInt("Height", 480), + inFilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"], "linear"), + inWrap = op.inValueSelect("Wrap", ["clamp to edge", "repeat", "mirrored repeat"], "repeat"), + inPixel = op.inDropDown("Pixel Format", CGL.Texture.PIXELFORMATS, CGL.Texture.PFORMATSTR_RGBA8UB), + + r = op.inValueSlider("R", 0), + g = op.inValueSlider("G", 0), + b = op.inValueSlider("B", 0), + a = op.inValueSlider("A", 0), + + trigger = op.outTrigger("Next"), + texOut = op.outTexture("texture_out", CGL.Texture.getEmptyTexture(cgl)), + outRatio = op.outNumber("Aspect Ratio"), + outWidth = op.outNumber("Texture Width"), + outHeight = op.outNumber("Texture Height"); + +op.setPortGroup("Texture Size", [inSize, width, height]); +op.setPortGroup("Texture Parameters", [inWrap, inFilter, inPixel]); + +r.setUiAttribs({ "colorPick": true }); +op.setPortGroup("Color", [r, g, b, a]); + +const prevViewPort = [0, 0, 0, 0]; +let effect = null; +let tex = null; +let reInitEffect = true; +let isFloatTex = false; +let copyShader = null; +let copyShaderTexUni = null; +let copyShaderRGBAUni = null; + +inWrap.onChange = + inFilter.onChange = + inPixel.onChange = reInitLater; + +inTex.onLinkChanged = +inSize.onChange = updateUi; + +render.onTriggered = + op.preRender = doRender; + +updateUi(); + +function initEffect() +{ + if (effect)effect.delete(); + if (tex)tex.delete(); + + effect = new CGL.TextureEffect(cgl, { "isFloatingPointTexture": getFloatingPoint() }); + + tex = new CGL.Texture(cgl, + { + "name": "image_compose_v2_" + op.id, + "isFloatingPointTexture": getFloatingPoint(), + "filter": getFilter(), + "wrap": getWrap(), + "width": getWidth(), + "height": getHeight() + }); + + effect.setSourceTexture(tex); + + outWidth.set(getWidth()); + outHeight.set(getHeight()); + outRatio.set(getWidth() / getHeight()); + + texOut.set(CGL.Texture.getEmptyTexture(cgl)); + + reInitEffect = false; + updateUi(); +} + +function getFilter() +{ + if (inFilter.get() == "nearest") return CGL.Texture.FILTER_NEAREST; + else if (inFilter.get() == "linear") return CGL.Texture.FILTER_LINEAR; + else if (inFilter.get() == "mipmap") return CGL.Texture.FILTER_MIPMAP; +} + +function getWrap() +{ + if (inWrap.get() == "repeat") return CGL.Texture.WRAP_REPEAT; + else if (inWrap.get() == "mirrored repeat") return CGL.Texture.WRAP_MIRRORED_REPEAT; + else if (inWrap.get() == "clamp to edge") return CGL.Texture.WRAP_CLAMP_TO_EDGE; +} + +function getFloatingPoint() +{ + isFloatTex = inPixel.get() == CGL.Texture.PFORMATSTR_RGBA32F; + return isFloatTex; +} + +function getWidth() +{ + if (inTex.get() && inSize.get() == "Auto") return inTex.get().width; + if (inSize.get() == "Auto") return cgl.getViewPort()[2]; + return Math.ceil(width.get()); +} + +function getHeight() +{ + if (inTex.get() && inSize.get() == "Auto") return inTex.get().height; + else if (inSize.get() == "Auto") return cgl.getViewPort()[3]; + else return Math.ceil(height.get()); +} + +function reInitLater() +{ + reInitEffect = true; +} + +function updateResolution() +{ + if (( + getWidth() != tex.width || + getHeight() != tex.height || + tex.isFloatingPoint() != getFloatingPoint() || + tex.filter != getFilter() || + tex.wrap != getWrap() + ) && (getWidth() !== 0 && getHeight() !== 0)) + { + initEffect(); + effect.setSourceTexture(tex); + texOut.set(CGL.Texture.getEmptyTexture(cgl)); + texOut.set(tex); + updateResolutionInfo(); + } +} + +function updateResolutionInfo() +{ + let info = null; + + if (inSize.get() == "Manual") + { + info = null; + } + else if (inSize.get() == "Auto") + { + if (inTex.get()) info = "Input Texture"; + else info = "Canvas Size"; + + info += ": " + getWidth() + " x " + getHeight(); + } + + let changed = false; + changed = inSize.uiAttribs.info != info; + inSize.setUiAttribs({ "info": info }); + if (changed)op.refreshParams(); +} + +function updateDefines() +{ + if (copyShader)copyShader.toggleDefine("USE_TEX", inTex.isLinked()); +} + +function updateUi() +{ + r.setUiAttribs({ "greyout": inTex.isLinked() }); + b.setUiAttribs({ "greyout": inTex.isLinked() }); + g.setUiAttribs({ "greyout": inTex.isLinked() }); + a.setUiAttribs({ "greyout": inTex.isLinked() }); + + width.setUiAttribs({ "greyout": inSize.get() == "Auto" }); + height.setUiAttribs({ "greyout": inSize.get() == "Auto" }); + + width.setUiAttribs({ "hideParam": inSize.get() != "Manual" }); + height.setUiAttribs({ "hideParam": inSize.get() != "Manual" }); + + if (tex) + if (getFloatingPoint() && getFilter() == CGL.Texture.FILTER_MIPMAP) op.setUiError("fpmipmap", "Don't use mipmap and 32bit at the same time, many systems do not support this."); + else op.setUiError("fpmipmap", null); + + updateResolutionInfo(); + updateDefines(); +} + +op.preRender = () => +{ + doRender(); +}; + +function copyTexture() +{ + if (!copyShader) + { + copyShader = new CGL.Shader(cgl, "copytextureshader"); + copyShader.setSource(copyShader.getDefaultVertexShader(), attachments.imgcomp_frag); + copyShaderTexUni = new CGL.Uniform(copyShader, "t", "tex", 0); + copyShaderRGBAUni = new CGL.Uniform(copyShader, "4f", "bgColor", r, g, b, a); + updateDefines(); + } + + cgl.pushShader(copyShader); + cgl.currentTextureEffect.bind(); + + if (inTex.get()) cgl.setTexture(0, inTex.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); +} + +function doRender() +{ + if (!effect || reInitEffect) initEffect(); + + const vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + cgl.pushBlend(false); + + updateResolution(); + + const oldEffect = cgl.currentTextureEffect; + cgl.currentTextureEffect = effect; + cgl.currentTextureEffect.imgCompVer = 3; + cgl.currentTextureEffect.width = width.get(); + cgl.currentTextureEffect.height = height.get(); + effect.setSourceTexture(tex); + + effect.startEffect(inTex.get() || CGL.Texture.getEmptyTexture(cgl, isFloatTex), true); + copyTexture(); + + trigger.trigger(); + + texOut.set(CGL.Texture.getEmptyTexture(cgl)); + + texOut.set(effect.getCurrentSourceTexture()); + + effect.endEffect(); + + cgl.setViewPort(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); + + cgl.popBlend(false); + cgl.currentTextureEffect = oldEffect; +} + + +}; + +Ops.Gl.TextureEffects.ImageCompose_v3.prototype = new CABLES.Op(); +CABLES.OPS["e890a050-11b7-456e-b09b-d08cd9c1ee41"]={f:Ops.Gl.TextureEffects.ImageCompose_v3,objName:"Ops.Gl.TextureEffects.ImageCompose_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Interlace +// +// ************************************************************** + +Ops.Gl.TextureEffects.Interlace = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"interlace_frag":"\n#ifdef HAS_TEXTURES\n IN vec2 texCoord;\n UNI sampler2D tex;\n#endif\nUNI float amount;\nUNI float lum;\nUNI float add;\nUNI float lineSize;\nUNI float scroll;\nUNI float displace;\n\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n\n col=texture(tex,texCoord);\n // .endl()+' col=clamp(col,0.0,1.0);'\n\n float dir;\n #ifdef DIRECTION\n dir = gl_FragCoord.x;\n #endif\n\n #ifndef DIRECTION\n dir = gl_FragCoord.y;\n #endif\n\n if( mod(dir+scroll,lineSize)>=lineSize*0.5)\n {\n col=texture(tex,vec2(texCoord.x+displace*0.05,texCoord.y));\n float gray = vec3(dot(vec3(0.2126,0.7152,0.0722), col.rgb)).r;\n col.rgb=col.rgb*(1.0-amount) + (col.rgb*gray*gray*lum)*amount;\n }\n else col+=add;\n\n\n outColor= col;\n}",}; +let render = op.inTrigger("render"); +let amount = op.inValueSlider("amount", 0.5); +let lum = op.inValueSlider("Lumi Scale", 0.9); +let direction = op.inBool("X or Y", false); +let lineSize = op.inValue("Line Size", 4); +let displace = op.inValueSlider("Displacement", 0); + +let add = op.inValue("Add", 0.02); +let inScroll = op.inValue("scroll", 0); + +let trigger = op.outTrigger("trigger"); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.interlace_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +let uniAmount = new CGL.Uniform(shader, "f", "amount", amount); + +let uniLum = new CGL.Uniform(shader, "f", "lum", lum); +let uniLineSize = new CGL.Uniform(shader, "f", "lineSize", lineSize); +let uniAdd = new CGL.Uniform(shader, "f", "add", add); +let uniDisplace = new CGL.Uniform(shader, "f", "displace", displace); +let uniScroll = new CGL.Uniform(shader, "f", "scroll", inScroll); + +direction.onChange = function () +{ + shader.toggleDefine("DIRECTION", direction.get()); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Interlace.prototype = new CABLES.Op(); +CABLES.OPS["3cd69d5b-6c05-4522-9551-52458f99421a"]={f:Ops.Gl.TextureEffects.Interlace,objName:"Ops.Gl.TextureEffects.Interlace"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Invert_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Invert_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"invert_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D texMask;\nUNI float amount;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n\n #ifdef USE_MASK\n #ifdef MASK_INVERT\n if(texture(texMask,texCoord).r>0.5)\n {\n outColor= col;\n return;\n }\n #endif\n\n #ifndef MASK_INVERT\n if(texture(texMask,texCoord).r<0.5)\n {\n outColor= col;\n return;\n }\n #endif\n #endif\n\n\n vec3 m=vec3( INVR , INVG , INVB );\n vec4 invert = vec4(clamp(m-col.rgb,0.0,1.0),col.a);\n\n outColor=cgl_blendPixel(col,invert,amount);\n\n // outColor.rgb=m;\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + maskInvert = op.inBool("Mask Invert", false), + mask = op.inTexture("Mask"), + invertR = op.inBool("Invert R", true), + invertG = op.inBool("Invert G", true), + invertB = op.inBool("Invert B", true), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.invert_frag); +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + textureMaskUniform = new CGL.Uniform(shader, "t", "texMask", 1); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +maskInvert.onChange = + invertR.onChange = + invertG.onChange = + invertB.onChange = + mask.onLinkChanged = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("USE_MASK", mask.isLinked()); + shader.toggleDefine("MASK_INVERT", maskInvert.get()); + + shader.define("INVR", invertR.get() ? "1.0" : "0.0"); + shader.define("INVG", invertG.get() ? "1.0" : "0.0"); + shader.define("INVB", invertB.get() ? "1.0" : "0.0"); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (mask.get())cgl.setTexture(1, mask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Invert_v2.prototype = new CABLES.Op(); +CABLES.OPS["56e8c95b-da89-423b-9d31-23351c263bb6"]={f:Ops.Gl.TextureEffects.Invert_v2,objName:"Ops.Gl.TextureEffects.Invert_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Kaleidoscope_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Kaleidoscope_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"kaleidoscope_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\n\nUNI float sides;\nUNI float angle;\nUNI float amount;\n\nUNI float slidex;\nUNI float slidey;\nUNI float centerX;\nUNI float centerY;\nUNI float aspect;\n\nconst float tau = 6.28318530718;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec2 center=vec2(centerX,centerY/aspect);\n\n\tvec2 loc = texCoord;\n\tloc.y/=aspect;\n\n\tfloat r = distance(center, loc);\n\tfloat a = atan ((loc.y-center.y),(loc.x-center.x));\n\n\t// kaleidoscope\n\ta = mod(a, tau/sides);\n\ta = abs(a - tau/sides/2.);\n\n\tloc.x = r * cos(a + tau * angle);\n\tloc.y = r * sin(a + tau * angle);\n\n\tloc = (center + loc) *2.1;\n\n\tloc.x = mod(loc.x + slidex, 1.0);\n\tloc.y = mod(loc.y + slidey, 1.0);\n\n\tif(loc.x < 0.0)loc.x = mod(abs(loc.x),1.0);\n\tif(loc.y < 0.0)loc.y = mod(abs(loc.y),1.0);\n\n\tif(loc.x > 1.0) loc.x = mod(abs(1.0-loc.x),1.0);\n\tif(loc.y > 1.0) loc.y = mod(abs(1.0-loc.y),1.0);\n\n\tvec4 col=texture(tex,loc);\n\tvec4 base=texture(tex,texCoord);\n\tbase.a=0.0;\n outColor= cgl_blendPixel(base,col,amount);\n\n}\n",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + sides = op.inValue("Sides", 10), + angle = op.inValueSlider("Angle", 0), + slidex = op.inValueSlider("Slide X", 0), + slidey = op.inValueSlider("Slide Y", 0), + centerX = op.inValueSlider("Center X", 0.5), + centerY = op.inValueSlider("Center Y", 0.5), + inAspect=op.inBool("Aspect Ratio",true), + trigger = op.outTrigger("Next"); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); + +const + unisides = new CGL.Uniform(shader, "f", "sides", sides), + uniangle = new CGL.Uniform(shader, "f", "angle", angle), + unislidex = new CGL.Uniform(shader, "f", "slidex", slidex), + unislidey = new CGL.Uniform(shader, "f", "slidey", slidey), + uniCenterX = new CGL.Uniform(shader, "f", "centerX", centerX), + uniCenterY = new CGL.Uniform(shader, "f", "centerY", centerY), + uniAmount = new CGL.Uniform(shader, "f", "amount", amount), + uniAspect = new CGL.Uniform(shader, "f", "aspect", 1); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +shader.setSource(shader.getDefaultVertexShader(), attachments.kaleidoscope_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if(inAspect.get()) uniAspect.setValue(cgl.currentTextureEffect.aspectRatio); + else uniAspect.setValue(1); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Kaleidoscope_v2.prototype = new CABLES.Op(); +CABLES.OPS["3c900fc2-8508-4531-a365-43224f7751c8"]={f:Ops.Gl.TextureEffects.Kaleidoscope_v2,objName:"Ops.Gl.TextureEffects.Kaleidoscope_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.LUTMap +// +// ************************************************************** + +Ops.Gl.TextureEffects.LUTMap = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"lut_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D texLut;\nUNI float amount;\n\nvoid main()\n{\n vec4 textureColor = texture(tex, texCoord);\n float blueColor = textureColor.b * 63.0;\n \n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n \n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n \n highp vec2 texPos1;\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);\n \n highp vec2 texPos2;\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);\n \n vec4 newColor1 = texture(texLut, texPos1);\n vec4 newColor2 = texture(texLut, texPos2);\n \n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n outColor= mix(textureColor,vec4(newColor.rgb, textureColor.w),amount);\n}\n",}; +let render = op.inTrigger("render"); +let trigger = op.outTrigger("trigger"); +let inLut = op.inTexture("LUT Image"); +let inAmount = op.inValueSlider("Amount", 1.0); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.lut_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +let textureUniform2 = new CGL.Uniform(shader, "t", "texLut", 1); +let uniPos = new CGL.Uniform(shader, "f", "amount", inAmount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + if (!inLut.get()) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.setTexture(1, inLut.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.LUTMap.prototype = new CABLES.Op(); +CABLES.OPS["baba13d7-1cc2-4ba8-b66d-05425b0e5a75"]={f:Ops.Gl.TextureEffects.LUTMap,objName:"Ops.Gl.TextureEffects.LUTMap"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.LensDirt_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.LensDirt_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"lensDirt_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float uOffsetX;\nUNI float uOffsetY;\nUNI float uZoom;\n// UNI int uIterations;\nUNI int uRandomSeed;\nUNI float uSpotEdge;\nUNI float uGamma;\nUNI float uAspect;\nUNI float amount;\n\n{{CGL.BLENDMODES3}}\n\n// https://www.shadertoy.com/view/MdfBRX\nfloat Bokeh(vec2 p, vec2 sp, float size, float mi, float blur)\n{\n float d = length(p - sp);\n float c = smoothstep(size, size*(1.-blur), d);\n c *= mix(mi, 1., smoothstep(size*.8, size, d));\n return c;\n}\n/// 2 out, 2 in... from https://www.shadertoy.com/view/4djSRW\n//stable\nvec2 hash22(vec2 p)\n{\n\tvec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));\n p3 += dot(p3, p3.yzx+33.33);\n return fract((p3.xx+p3.yz)*p3.zy)*2.0-1.0;\n\n}\nvec3 dirt(vec2 uv, float n,float density,float hardness)\n{\n n *= density;\n vec2 p = fract(uv * n);\n vec2 st = (floor(uv * n) + 0.5) / n;\n vec2 rnd = hash22(st);\n float c = Bokeh(p, vec2(0.5, 0.5) + vec2(0.3) * rnd, 0.2,\n abs(rnd.y * 0.35) + 0.3, 0.25 + rnd.x * rnd.y * hardness);\n\n return vec3(c) * exp(rnd.x * 4.0);\n}\n\nvoid main()\n{\n\tvec3 di = vec3(1.0);\n float edgeHardness = 0.1;\n edgeHardness = clamp(uSpotEdge*0.25,0.0,0.2);\n\n vec2 uv = (texCoord-0.5);\n\n uv.y/=uAspect;\n uv *= uZoom;\n uv -= vec2(uOffsetX,uOffsetY);\n\n float q = 0.0;\n for (int i = 1; i < ITERATIONS ; i++)\n {\n q = float(i);\n vec2 h=hash22(vec2(q)+vec2(uRandomSeed));\n di += dirt(uv-h, h.x, 1.0, edgeHardness);\n }\n\n di = pow(di* 0.01,vec3(uGamma));\n vec4 col = vec4(di,1.0);\n vec4 base = texture(tex,texCoord);\n\n outColor = cgl_blendPixel(base,col,amount);\n}",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + offsetX = op.inFloat("Offset X", 0), + offsetY = op.inFloat("Offset Y", 0), + zoom = op.inFloat("Zoom", 5), + iterations = op.inInt("Iterations", 50), + randomSeed = op.inInt("Seed", 1), + spotEdge = op.inFloatSlider("Spot edge", 0.5), + gamma = op.inFloat("Gamma", 0.75), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.lensDirt_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + offsetXUniformX = new CGL.Uniform(shader, "f", "uOffsetX", offsetX), + offsetXUniformY = new CGL.Uniform(shader, "f", "uOffsetY", offsetY), + zoomUniform = new CGL.Uniform(shader, "f", "uZoom", zoom), + // iterationsUniform=new CGL.Uniform(shader,'i','uIterations',iterations), + randomSeedUniform = new CGL.Uniform(shader, "i", "uRandomSeed", randomSeed), + spotEdgeUniform = new CGL.Uniform(shader, "f", "uSpotEdge", spotEdge), + gammaUniform = new CGL.Uniform(shader, "f", "uGamma", gamma), + uniformAspect = new CGL.Uniform(shader, "f", "uAspect", 1.0), + + amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +iterations.onChange = updateIterations; +updateIterations(); + +function updateIterations() +{ + let its = iterations.get(); + its = Math.min(300, Math.max(1, its)); + shader.define("ITERATIONS", its); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + uniformAspect.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().width / cgl.currentTextureEffect.getCurrentSourceTexture().height); + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.LensDirt_v2.prototype = new CABLES.Op(); +CABLES.OPS["a04156f6-f636-430f-810e-9cb27199ceeb"]={f:Ops.Gl.TextureEffects.LensDirt_v2,objName:"Ops.Gl.TextureEffects.LensDirt_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.LensScratches_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.LensScratches_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"lensScratches_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float uOffsetX;\nUNI float uOffsetY;\nUNI float uWavyness;\nUNI float uScale;\nUNI int uLayers;\nUNI float uAntiAliasIterations;\nUNI float uFrequency;\nUNI float uFrequencyStep;\nUNI float uResWidth;\nUNI float uResHeight;\nUNI float uAspect;\nUNI float amount;\n\n{{CGL.BLENDMODES3}}\n//Shader kindly used with the permission of Tropical trevor\n//shadertoy user Daedelus\n//https://www.shadertoy.com/view/4syXRD\n\nvoid pR(inout vec2 p, float a)\n{\n float sa = sin(a);\n float ca = cos(a);\n p *= mat2(ca, sa, -sa, ca);\n}\n\nfloat scratch(vec2 uv, vec2 seed)\n{\n seed.x = floor(sin(seed.x * 51024.0) * 3104.0);\n seed.y = floor(sin(seed.y * 1324.0) * 554.0);\n\n uv = uv * 2.0 - 1.0;\n pR(uv, seed.x + seed.y);\n uv += sin(seed.x - seed.y);\n uv = clamp(uv * 0.5 + 0.5, 0.0, 1.0);\n\n float s1 = sin(seed.x + uv.y * 3.1415) * uWavyness;\n float s2 = sin(seed.y + uv.y * 3.1415) * uWavyness;\n\n float x = sign(0.01 - abs(uv.x - 0.5 + s2 + s1));\n return clamp(((1.0 - pow(uv.y, 2.0)) * uv.y) * 2.5 * x, 0.0, 1.0);\n}\n\nfloat layer(vec2 uv, vec2 frequency, vec2 offset, float angle)\n{\n pR(uv, angle);\n uv = uv * frequency + offset;\n return scratch(fract(uv), floor(uv));\n}\n\nfloat scratches(vec2 uv)\n{\n uv *= uScale;\n uv -= vec2(uOffsetX,uOffsetY);\n vec2 frequency = vec2(uFrequency);\n float scratches = 0.0;\n int iterations = clamp(uLayers,1,20);\n for(int i = 0; i < iterations; ++i)\n {\n float fi = float(i);\n \tscratches += layer(uv, frequency, vec2(fi, fi), fi * 3145.0);\n frequency += uFrequencyStep;\n }\n return scratches;\n}\nvoid main()\n{\n vec2 uv = texCoord.xy-0.5;\n uv.y/=uAspect;\n\n // using AA by Shane:\n // https://www.shadertoy.com/view/4d3SWf\n // Antialias level. Set to 1 for a standard, aliased scene\n float AA = clamp(uAntiAliasIterations,1.,4.);\n int AA2 = int(AA*AA);\n float color = 0.0;\n vec2 pix = 2.0/vec2(uResWidth,uResHeight)/AA; // or iResolution.xy\n for (int i=0; i threshholdHigh) col.r=col.g=col.b=col.a=0.0;\n #ifdef BLACKWHITE\n else col.r=col.g=col.b=col.a=1.0;\n #endif\n #endif\n\n #ifdef INVERT\n if(gray > threshholdLow && gray < threshholdHigh) col.r=col.g=col.b=col.a=0.0;\n #ifdef BLACKWHITE\n else col.r=col.g=col.b=col.a=1.0;\n #endif\n #endif\n\n outColor= col;\n}",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + inInvert = op.inValueBool("Invert"), + inBlackWhite = op.inValueBool("Black White"), + thresholdLow = op.inValueSlider("Threshold low ", 0.5), + thresholdHigh = op.inValueSlider("Threshold high", 1.0); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "lumakey"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.lumakeyV2_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const uniThresholdLow = new CGL.Uniform(shader, "f", "threshholdLow", thresholdLow); +const uniThresholdHigh = new CGL.Uniform(shader, "f", "threshholdHigh", thresholdHigh); + +inBlackWhite.onChange = function () +{ + if (inBlackWhite.get()) shader.define("BLACKWHITE"); + else shader.removeDefine("BLACKWHITE"); +}; + +inInvert.onChange = function () +{ + if (inInvert.get()) shader.define("INVERT"); + else shader.removeDefine("INVERT"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + + cgl.currentTextureEffect.bind(); + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + + cgl.popShader(); + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.LumaKey_v2.prototype = new CABLES.Op(); +CABLES.OPS["1699f3fc-355b-4497-a23c-1620a04a9cd4"]={f:Ops.Gl.TextureEffects.LumaKey_v2,objName:"Ops.Gl.TextureEffects.LumaKey_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Math.RgbMath +// +// ************************************************************** + +Ops.Gl.TextureEffects.Math.RgbMath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rgbmul_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\n#ifdef MOD_MASK\n UNI sampler2D texMask;\n#endif\nUNI float r;\nUNI float g;\nUNI float b;\nUNI float a;\n\n\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n vec4 v=vec4(r,g,b,a);\n\n #ifdef MOD_MASK\n v*=texture(texMask,texCoord);\n #endif\n\n #ifdef MOD_OP_SUB_CX\n #ifdef MOD_CHAN_R\n col.r=col.r-v.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g=col.g-v.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b=col.b-v.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a=col.a-v.a;\n #endif\n #endif\n\n #ifdef MOD_OP_SUB_XC\n #ifdef MOD_CHAN_R\n col.r=v.r-col.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g=v.g-col.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b=v.b-col.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a=v.a-col.a;\n #endif\n #endif\n\n #ifdef MOD_OP_ADD\n #ifdef MOD_CHAN_R\n col.r+=v.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g+=v.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b+=v.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a+=v.a;\n #endif\n #endif\n\n #ifdef MOD_OP_MUL\n #ifdef MOD_CHAN_R\n col.r*=v.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g*=v.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b*=v.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a*=v.a;\n #endif\n #endif\n\n #ifdef MOD_OP_DIV_XC\n #ifdef MOD_CHAN_R\n col.r=v.r/col.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g=v.g/col.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b=v.b/col.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a=v.a/col.a;\n #endif\n #endif\n\n #ifdef MOD_OP_DIV_CX\n #ifdef MOD_CHAN_R\n col.r=col.r/v.r;\n #endif\n #ifdef MOD_CHAN_G\n col.g=col.g/v.g;\n #endif\n #ifdef MOD_CHAN_B\n col.b=col.b/v.b;\n #endif\n #ifdef MOD_CHAN_A\n col.a=col.a/v.a;\n #endif\n #endif\n\n #ifdef MOD_OP_MODULO\n col=mod(col,v);\n #endif\n\n// csdcsd\n outColor= col;\n}\n",}; +const + render = op.inTrigger("Render"), + inOp = op.inSwitch("Operation", ["c-x", "x-c", "c+x", "c*x", "x/c", "c/x", "c%x"], "c*x"), + chanR = op.inBool("R Active", true), + chanG = op.inBool("G Active", true), + chanB = op.inBool("B Active", true), + chanA = op.inBool("A Active", false), + r = op.inValue("r", 1), + g = op.inValue("g", 1), + b = op.inValue("b", 1), + a = op.inValue("a", 1), + inTexMask = op.inTexture("Mask"), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.rgbmul_frag); +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureMaskUniform = new CGL.Uniform(shader, "t", "texMask", 1), + uniformR = new CGL.Uniform(shader, "f", "r", r), + uniformG = new CGL.Uniform(shader, "f", "g", g), + uniformB = new CGL.Uniform(shader, "f", "b", b), + uniformA = new CGL.Uniform(shader, "f", "a", a); + +chanR.onChange = + inTexMask.onChange = + chanG.onChange = + chanB.onChange = + chanA.onChange = + inOp.onChange = updateDefines; + +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("MOD_MASK", inTexMask.get()); + + shader.toggleDefine("MOD_OP_SUB_CX", inOp.get() === "c-x"); + shader.toggleDefine("MOD_OP_SUB_XC", inOp.get() === "x-c"); + + shader.toggleDefine("MOD_OP_ADD", inOp.get() === "c+x"); + shader.toggleDefine("MOD_OP_MUL", inOp.get() === "c*x"); + + shader.toggleDefine("MOD_OP_DIV_XC", inOp.get() === "x/c"); + shader.toggleDefine("MOD_OP_DIV_CX", inOp.get() === "c/x"); + + shader.toggleDefine("MOD_OP_MODULO", inOp.get() === "c%x"); + + shader.toggleDefine("MOD_CHAN_R", chanR.get()); + r.setUiAttribs({ "greyout": !chanR.get() }); + + shader.toggleDefine("MOD_CHAN_G", chanG.get()); + g.setUiAttribs({ "greyout": !chanG.get() }); + + shader.toggleDefine("MOD_CHAN_B", chanB.get()); + b.setUiAttribs({ "greyout": !chanB.get() }); + + shader.toggleDefine("MOD_CHAN_A", chanA.get()); + a.setUiAttribs({ "greyout": !chanA.get() }); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (inTexMask.get())cgl.setTexture(1, inTexMask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Math.RgbMath.prototype = new CABLES.Op(); +CABLES.OPS["dc858e71-1f12-4de5-89f5-67fb41ebfa39"]={f:Ops.Gl.TextureEffects.Math.RgbMath,objName:"Ops.Gl.TextureEffects.Math.RgbMath"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Math.RgbTransform +// +// ************************************************************** + +Ops.Gl.TextureEffects.Math.RgbTransform = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rgbmul_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\n#ifdef MOD_MASK\n UNI sampler2D texMask;\n#endif\n\nUNI vec3 translate;\nUNI vec3 scale;\nUNI vec3 rot;\n\n\n\nmat4 rotationMatrix(vec3 axis, float angle)\n{\n axis = normalize(axis);\n float s = sin(angle);\n float c = cos(angle);\n float oc = 1.0 - c;\n\n return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,\n oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,\n oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,\n 0.0, 0.0, 0.0, 1.0);\n}\n\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n\n float mul=1.0;\n\n\n #ifdef MOD_MASK\n mul=texture(texMask,texCoord).r;\n #endif\n\n #ifdef DO_ROT\n col*=rotationMatrix(vec3(1.0,0.0,0.0), mul*rot.x/57.29577951308232);\n col*=rotationMatrix(vec3(0.0,1.0,0.0), mul*rot.y/57.29577951308232);\n col*=rotationMatrix(vec3(0.0,0.0,1.0), mul*rot.z/57.29577951308232);\n #endif\n\n #ifdef DO_SCALE\n col.xyz*=scale*mul;\n #endif\n\n #ifdef DO_TRANS\n col.xyz+=translate*mul;\n #endif\n\n outColor=col;\n}\n",}; +const + render = op.inTrigger("Render"), + + inDoTrans = op.inBool("Translate", true), + posx = op.inValue("Pos X", 0), + posy = op.inValue("Pos Y", 0), + posz = op.inValue("Pos Z", 0), + + inDoScale = op.inBool("Scale", true), + scalex = op.inValue("Scale X", 1), + scaley = op.inValue("Scale Y", 1), + scalez = op.inValue("Scale Z", 1), + + inDoRot = op.inBool("Rotate", true), + rotx = op.inValue("Rotation X", 1), + roty = op.inValue("Rotation Y", 1), + rotz = op.inValue("Rotation Z", 1), + + inTexMask = op.inTexture("Mask"), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Rotation", [inDoRot, rotx, roty, rotz]); +op.setPortGroup("Position", [inDoTrans, posx, posy, posz]); +op.setPortGroup("Scale", [inDoScale, scalex, scaley, scalez]); +op.setUiAxisPorts(posx, posz, posy); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.rgbmul_frag); +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureMaskUniform = new CGL.Uniform(shader, "t", "texMask", 1), + uniformTransl = new CGL.Uniform(shader, "3f", "translate", posx, posy, posz), + uniformScale = new CGL.Uniform(shader, "3f", "scale", scalex, scaley, scalez), + uniformRot = new CGL.Uniform(shader, "3f", "rot", rotx, roty, rotz); + +inTexMask.onChange = + inDoTrans.onChange = + inDoRot.onChange = + inDoScale.onChange = updateDefines; + +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("MOD_MASK", inTexMask.get()); + + shader.toggleDefine("DO_TRANS", inDoTrans.get()); + shader.toggleDefine("DO_ROT", inDoRot.get()); + shader.toggleDefine("DO_SCALE", inDoScale.get()); + + posx.setUiAttribs({ "greyout": !inDoTrans.get() }); + posy.setUiAttribs({ "greyout": !inDoTrans.get() }); + posz.setUiAttribs({ "greyout": !inDoTrans.get() }); + + rotx.setUiAttribs({ "greyout": !inDoRot.get() }); + roty.setUiAttribs({ "greyout": !inDoRot.get() }); + rotz.setUiAttribs({ "greyout": !inDoRot.get() }); + + scalex.setUiAttribs({ "greyout": !inDoScale.get() }); + scaley.setUiAttribs({ "greyout": !inDoScale.get() }); + scalez.setUiAttribs({ "greyout": !inDoScale.get() }); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (inTexMask.get())cgl.setTexture(1, inTexMask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Math.RgbTransform.prototype = new CABLES.Op(); +CABLES.OPS["0a069281-e7a1-43a4-a74a-101026df6a29"]={f:Ops.Gl.TextureEffects.Math.RgbTransform,objName:"Ops.Gl.TextureEffects.Math.RgbTransform"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Mirror +// +// ************************************************************** + +Ops.Gl.TextureEffects.Mirror = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"mirror_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float axis;\nUNI float width;\nUNI float flip;\nUNI float offset;\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n\n float tc=texCoord.x;\n if(axis==1.0) tc=(texCoord.y);\n\n float x=(tc);\n if(tc>=0.5)x=1.0-tc;\n\n x*=width*2.0;\n if(flip==1.0)x=1.0-x;\n x*=1.0-offset;\n\n if(axis==1.0) col=texture(tex,vec2(texCoord.x,x) );\n else col=texture(tex,vec2(x,texCoord.y) );\n\n outColor= col;\n}",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + axis = op.inSwitch("axis", ["X", "Y"], "X"), + width = op.inValueFloat("width", 0.5), + offset = op.inValueFloat("offset"), + flip = op.inValueBool("flip"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.mirror_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniAxis = new CGL.Uniform(shader, "f", "axis", 0), + uniWidth = new CGL.Uniform(shader, "f", "width", width), + uniOffset = new CGL.Uniform(shader, "f", "offset", offset), + uniFlip = new CGL.Uniform(shader, "f", "flip", 0); + +flip.onChange = function () +{ + if (flip.get())uniFlip.setValue(1); + else uniFlip.setValue(0); +}; + +axis.onChange = function () +{ + if (axis.get() == "X")uniAxis.setValue(0); + else if (axis.get() == "Y")uniAxis.setValue(1); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Mirror.prototype = new CABLES.Op(); +CABLES.OPS["10d3c769-9a7f-4bd3-a849-7354d3e5f7f0"]={f:Ops.Gl.TextureEffects.Mirror,objName:"Ops.Gl.TextureEffects.Mirror"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.MultiDrawImage +// +// ************************************************************** + +Ops.Gl.TextureEffects.MultiDrawImage = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"invert_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D tex1;\nUNI sampler2D tex2;\nUNI sampler2D tex3;\nUNI sampler2D tex4;\nUNI sampler2D tex5;\nUNI sampler2D tex6;\nUNI sampler2D tex7;\nUNI sampler2D tex8;\n\n\nUNI sampler2D texMask1;\nUNI sampler2D texMask2;\nUNI sampler2D texMask3;\nUNI sampler2D texMask4;\nUNI sampler2D texMask5;\nUNI sampler2D texMask6;\nUNI sampler2D texMask7;\nUNI sampler2D texMask8;\n\nUNI float amount1;\nUNI float amount2;\nUNI float amount3;\nUNI float amount4;\nUNI float amount5;\nUNI float amount6;\nUNI float amount7;\nUNI float amount8;\n\n{{BLENDCODE}}\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n\n\n #ifdef USE_TEX_1\n vec4 col1=texture(tex1,texCoord);\n col=cgl_blend1(col,col1,amount1);\n #endif\n #ifdef USE_TEX_2\n vec4 col2=texture(tex2,texCoord);\n col=cgl_blend2(col,col2,amount2);\n #endif\n #ifdef USE_TEX_3\n vec4 col3=texture(tex3,texCoord);\n col=cgl_blend3(col,col3,amount3);\n #endif\n #ifdef USE_TEX_4\n vec4 col4=texture(tex4,texCoord);\n col=cgl_blend4(col,col4,amount4);\n #endif\n #ifdef USE_TEX_5\n vec4 col5=texture(tex5,texCoord);\n col=cgl_blend5(col,col5,amount5);\n #endif\n #ifdef USE_TEX_6\n vec4 col6=texture(tex6,texCoord);\n col=cgl_blend6(col,col6,amount6);\n #endif\n #ifdef USE_TEX_7\n vec4 col7=texture(tex7,texCoord);\n col=cgl_blend7(col,col7,amount7);\n #endif\n #ifdef USE_TEX_8\n vec4 col8=texture(tex8,texCoord);\n col=cgl_blend8(col,col8,amount8);\n #endif\n\n outColor=col;\n\n}\n",}; +const blendmodes = ["normal", "lighten", "darken", "multiply", "multiply invert", "average", "add", "substract", "difference", "negation", "exclusion", "overlay", "screen", "color dodge", "color burn", "softlight", "hardlight"]; +const + render = op.inTrigger("render"), + maskInvert = op.inBool("Mask Invert", false), + trigger = op.outTrigger("trigger"); +const + NUM = 8, + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name), + amounts = [], + blends = [], + alphas = [], + texMasks = [], + maskModes = [], + texs = []; + +let needsUpdate = true; + +for (let i = 0; i < NUM; i++) +{ + const tex = op.inTexture("Texture " + (i + 1)); + const blend = op.inDropDown("Blendmode " + (i + 1), blendmodes, "normal"); + const texMask = op.inTexture("Mask " + (i + 1)); + const maskMode = op.inSwitch("Mask Source " + (i + 1), ["R", "R inv", "A", "A inv"], "R"); + const alpha = op.inSwitch("Opacity " + (i + 1), ["Normal", "Prev A", "Prev R"], "Normal"); + const amount = op.inValueSlider("Amount " + (i + 1), 1); + + blend.onChange = + alpha.onChange = + maskMode.onChange = + texMask.onLinkChanged = + tex.onLinkChanged = () => { needsUpdate = true; }; + + texs.push(tex); + texMasks.push(texMask); + alphas.push(alpha); + blends.push(blend); + amounts.push(amount); + maskModes.push(maskMode); + + op.setPortGroup("Image " + (i + 1), [tex, blend, amount, alpha, texMask, maskMode]); + + new CGL.Uniform(shader, "t", "tex" + (i + 1), i * 2 + 1); + new CGL.Uniform(shader, "t", "texMask" + (i + 1), i * 2 + 2); + new CGL.Uniform(shader, "f", "amount" + (i + 1), amount); +} + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +function setBlendCode() +{ + let defines = ""; + let blendcode = ""; + for (let i = 0; i < NUM; i++) + { + let active = texs[i].isLinked(); + amounts[i].setUiAttribs({ "greyout": !active }); + blends[i].setUiAttribs({ "greyout": !active }); + alphas[i].setUiAttribs({ "greyout": !active }); + + if (active) + { + defines += "#define USE_TEX_" + (i + 1) + "".endl(); + blendcode += getBlendCode(i + 1, + blends[i].get(), + alphas[i].get(), + texMasks[i].get(), + maskModes[i].get()); + } + } + + let src = defines.endl() + attachments.invert_frag; + src = src.replace("{{BLENDCODE}}", blendcode); + + shader.setSource(shader.getDefaultVertexShader(), src); + + needsUpdate = false; +} + +function getBlendCode(idx, name, alpha, hasMask, maskMode) +{ + let src = "" + + "vec3 _blend" + idx + "(vec3 base,vec3 blend)".endl() + + "{".endl() + + " vec3 colNew=blend;".endl(); + + if (name == "multiply") src += " colNew=base*blend;".endl(); + else if (name == "multiply invert") src += " colNew=base* vec3(1.0)-blend;".endl(); + else if (name == "average") src += " colNew=((base + blend) / 2.0);".endl(); + else if (name == "add") src += " colNew=min(base + blend, vec3(1.0));".endl(); + else if (name == "substract") src += " colNew=max(base + blend - vec3(1.0), vec3(0.0));".endl(); + else if (name == "difference") src += " colNew=abs(base - blend);".endl(); + else if (name == "negation") src += " colNew=(vec3(1.0) - abs(vec3(1.0) - base - blend));".endl(); + else if (name == "exclusion") src += " colNew=(base + blend - 2.0 * base * blend);".endl(); + else if (name == "lighten") src += " colNew=max(blend, base);".endl(); + else if (name == "darken") src += " colNew=min(blend, base);".endl(); + else if (name == "overlay") + { + src += "" + + " #define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))".endl() + + " colNew=vec3(BlendOverlayf(base.r, blend.r),BlendOverlayf(base.g, blend.g),BlendOverlayf(base.b, blend.b));".endl(); + } + else if (name == "screen") + { + src += "" + + " #define BlendScreenf(base, blend) (1.0 - ((1.0 - base) * (1.0 - blend)))".endl() + + " colNew=vec3(BlendScreenf(base.r, blend.r),BlendScreenf(base.g, blend.g),BlendScreenf(base.b, blend.b));".endl(); + } + else if (name == "softlight") + { + src += "" + + " #define BlendSoftLightf(base, blend) ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend)))".endl() + + " colNew=vec3(BlendSoftLightf(base.r, blend.r),BlendSoftLightf(base.g, blend.g),BlendSoftLightf(base.b, blend.b));".endl(); + } + else if (name == "hardlight") + { + src += "" + + " #define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))".endl() + + " colNew=vec3(BlendOverlayf(base.r, blend.r),BlendOverlayf(base.g, blend.g),BlendOverlayf(base.b, blend.b));".endl(); + } + else if (name == "color dodge") + { + src += "" + + " #define BlendColorDodgef(base, blend) ((blend == 1.0) ? blend : min(base / (1.0 - blend), 1.0))".endl() + + " colNew=vec3(BlendColorDodgef(base.r, blend.r),BlendColorDodgef(base.g, blend.g),BlendColorDodgef(base.b, blend.b));".endl(); + } + else if (name == "color burn") + { + src += "" + + " #define BlendColorBurnf(base, blend) ((blend == 0.0) ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0))".endl() + + " colNew=vec3(BlendColorBurnf(base.r, blend.r),BlendColorBurnf(base.g, blend.g),BlendColorBurnf(base.b, blend.b));".endl(); + } + + src += "" + + " return colNew;".endl() + + "}".endl() + + + "vec4 cgl_blend" + idx + "(vec4 oldColor,vec4 newColor,float amount)".endl() + + "{".endl() + // +"vec4 col=vec4(0.0,0.0,0.0,1.0);" + + // +"if(newColor.a==0.0)return vec4(0.0);".endl() + + "float a=(amount*newColor.a);".endl(); + + if (alpha === "Prev A") src += "a*=oldColor.a;"; + if (alpha === "Prev R") src += "a*=oldColor.r;"; + + src = src + + + "vec4 col = vec4( _blend" + idx + "(oldColor.rgb, newColor.rgb), 1.0);".endl() + + "col = vec4( mix( col.rgb,oldColor.rgb, col.a * 1.0-amount), 1.0);".endl(); + + if (hasMask) + if (maskMode === "R")src = src + "newColor.a *= texture(texMask" + idx + ",texCoord).r;".endl(); + else if (maskMode === "R inv")src = src + "newColor.a *= 1.0-texture(texMask" + idx + ",texCoord).r;".endl(); + else if (maskMode === "A")src = src + "newColor.a *= texture(texMask" + idx + ",texCoord).a;".endl(); + else if (maskMode === "A inv")src = src + "newColor.a *= 1.0-texture(texMask" + idx + ",texCoord).a;".endl(); + + src = src + "col = vec4( mix( oldColor,col, newColor.a));".endl() + + // + "vec3 blendedCol=_blend"+idx+"(newColor.rgb,newColor.rgb);".endl() + // + "col.rgb=mix(oldColor.rgb,newColor.rgb,a);".endl() + + // + "col.a=clamp(oldColor.a+a,0.0,1.0);".endl() + + + "return col;".endl() + + "}".endl().endl(); + return src; +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + if (needsUpdate)setBlendCode(); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + let count = 1; + for (let i = 0; i < texs.length; i++) + { + if (texs[i].get()) + { + cgl.setTexture(i * 2 + 1, texs[i].get().tex); + count++; + } + if (texMasks[i].get()) + { + cgl.setTexture(i * 2 + 2, texMasks[i].get().tex); + count++; + } + } + if (count > cgl.maxTextureUnits) op.setUiError("manytex", "Too many textures bound"); + else op.setUiError("manytex", null); + + cgl.pushBlendMode(CGL.BLEND_NONE, true); + cgl.currentTextureEffect.finish(); + cgl.popBlendMode(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.MultiDrawImage.prototype = new CABLES.Op(); +CABLES.OPS["4df51ead-c46c-471d-ac5e-1872219a3174"]={f:Ops.Gl.TextureEffects.MultiDrawImage,objName:"Ops.Gl.TextureEffects.MultiDrawImage"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.CellularNoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.CellularNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"cellularnoise3d_frag":"UNI float z;\nUNI float x;\nUNI float y;\nUNI float scale;\nIN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float harmonics;\nUNI float aspect;\n\n#ifdef HAS_TEX_OFFSETMAP\n UNI sampler2D texOffsetZ;\n UNI float offMul;\n#endif\n\n#ifdef HAS_TEX_MASK\n UNI sampler2D texMask;\n#endif\n\n{{CGL.BLENDMODES3}}\n\nvoid FAST32_hash_3D( \tvec3 gridcell,\n out vec4 lowz_hash_0,\n out vec4 lowz_hash_1,\n out vec4 lowz_hash_2,\n out vec4 highz_hash_0,\n out vec4 highz_hash_1,\n out vec4 highz_hash_2\t)\t\t//\tgenerates 3 random numbers for each of the 8 cell corners\n{\n // gridcell is assumed to be an integer coordinate\n\n //\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const vec3 SOMELARGEFLOATS = vec3( 635.298681, 682.357502, 668.926525 );\n const vec3 ZINC = vec3( 48.500388, 65.294118, 63.934599 );\n\n //\ttruncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n //\tcalculate the noise\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n P = P.xzxz * P.yyww;\n vec3 lowz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) );\n vec3 highz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) );\n lowz_hash_0 = fract( P * lowz_mod.xxxx );\n highz_hash_0 = fract( P * highz_mod.xxxx );\n lowz_hash_1 = fract( P * lowz_mod.yyyy );\n highz_hash_1 = fract( P * highz_mod.yyyy );\n lowz_hash_2 = fract( P * lowz_mod.zzzz );\n highz_hash_2 = fract( P * highz_mod.zzzz );\n}\n\n\n\n\nvec4 Cellular_weight_samples( vec4 samples )\n{\n samples = samples * 2.0 - 1.0;\n //return (1.0 - samples * samples) * sign(samples);\t// square\n return (samples * samples * samples) - sign(samples);\t// cubic (even more variance)\n}\n\n\n//\n//\tCellular Noise 3D\n//\tBased off Stefan Gustavson's work at http://www.itn.liu.se/~stegu/GLSL-cellular\n//\thttp://briansharpe.files.wordpress.com/2011/12/cellularsample.jpg\n//\n//\tSpeed up by using 2x2x2 search window instead of 3x3x3\n//\tproduces range of 0.0->1.0\n//\nfloat Cellular3D(vec3 P)\n{\n //\testablish our grid cell and unit position\n vec3 Pi = floor(P);\n vec3 Pf = P - Pi;\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hash_x0, hash_y0, hash_z0, hash_x1, hash_y1, hash_z1;\n FAST32_hash_3D( Pi, hash_x0, hash_y0, hash_z0, hash_x1, hash_y1, hash_z1 );\n //SGPP_hash_3D( Pi, hash_x0, hash_y0, hash_z0, hash_x1, hash_y1, hash_z1 );\n\n //\tgenerate the 8 random points\n#if 1\n //\trestrict the random point offset to eliminate artifacts\n //\twe'll improve the variance of the noise by pushing the points to the extremes of the jitter window\n const float JITTER_WINDOW = 0.166666666;\t// 0.166666666 will guarentee no artifacts. It is the intersection on x of graphs f(x)=( (0.5 + (0.5-x))^2 + 2*((0.5-x)^2) ) and f(x)=( 2 * (( 0.5 + x )^2) + x * x )\n hash_x0 = Cellular_weight_samples( hash_x0 ) * JITTER_WINDOW + vec4(0.0, 1.0, 0.0, 1.0);\n hash_y0 = Cellular_weight_samples( hash_y0 ) * JITTER_WINDOW + vec4(0.0, 0.0, 1.0, 1.0);\n hash_x1 = Cellular_weight_samples( hash_x1 ) * JITTER_WINDOW + vec4(0.0, 1.0, 0.0, 1.0);\n hash_y1 = Cellular_weight_samples( hash_y1 ) * JITTER_WINDOW + vec4(0.0, 0.0, 1.0, 1.0);\n hash_z0 = Cellular_weight_samples( hash_z0 ) * JITTER_WINDOW + vec4(0.0, 0.0, 0.0, 0.0);\n hash_z1 = Cellular_weight_samples( hash_z1 ) * JITTER_WINDOW + vec4(1.0, 1.0, 1.0, 1.0);\n#else\n //\tnon-weighted jitter window. jitter window of 0.4 will give results similar to Stefans original implementation\n //\tnicer looking, faster, but has minor artifacts. ( discontinuities in signal )\n const float JITTER_WINDOW = 0.4;\n hash_x0 = hash_x0 * JITTER_WINDOW * 2.0 + vec4(-JITTER_WINDOW, 1.0-JITTER_WINDOW, -JITTER_WINDOW, 1.0-JITTER_WINDOW);\n hash_y0 = hash_y0 * JITTER_WINDOW * 2.0 + vec4(-JITTER_WINDOW, -JITTER_WINDOW, 1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW);\n hash_x1 = hash_x1 * JITTER_WINDOW * 2.0 + vec4(-JITTER_WINDOW, 1.0-JITTER_WINDOW, -JITTER_WINDOW, 1.0-JITTER_WINDOW);\n hash_y1 = hash_y1 * JITTER_WINDOW * 2.0 + vec4(-JITTER_WINDOW, -JITTER_WINDOW, 1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW);\n hash_z0 = hash_z0 * JITTER_WINDOW * 2.0 + vec4(-JITTER_WINDOW, -JITTER_WINDOW, -JITTER_WINDOW, -JITTER_WINDOW);\n hash_z1 = hash_z1 * JITTER_WINDOW * 2.0 + vec4(1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW);\n#endif\n\n //\treturn the closest squared distance\n vec4 dx1 = Pf.xxxx - hash_x0;\n vec4 dy1 = Pf.yyyy - hash_y0;\n vec4 dz1 = Pf.zzzz - hash_z0;\n vec4 dx2 = Pf.xxxx - hash_x1;\n vec4 dy2 = Pf.yyyy - hash_y1;\n vec4 dz2 = Pf.zzzz - hash_z1;\n vec4 d1 = dx1 * dx1 + dy1 * dy1 + dz1 * dz1;\n vec4 d2 = dx2 * dx2 + dy2 * dy2 + dz2 * dz2;\n d1 = min(d1, d2);\n d1.xy = min(d1.xy, d1.wz);\n return min(d1.x, d1.y) * ( 9.0 / 12.0 );\t//\tscale return value from 0.0->1.333333 to 0.0->1.0 \t(2/3)^2 * 3 == (12/9) == 1.333333\n}\n\n\nvoid main()\n{\n\n\n vec2 p=vec2(texCoord.x-0.5,texCoord.y-0.5);\n\n #ifdef DO_TILEABLE\n p=abs(texCoord-0.5);\n #endif\n\n p.x*=aspect;\n\n p=p*scale;\n\n p=vec2(p.x+0.5-x,p.y+0.5-y);\n\n vec3 offset;\n #ifdef HAS_TEX_OFFSETMAP\n vec4 offMap=texture(texOffsetZ,texCoord);\n\n #ifdef OFFSET_X_R\n offset.x=offMap.r;\n #endif\n #ifdef OFFSET_X_G\n offset.x=offMap.g;\n #endif\n #ifdef OFFSET_X_B\n offset.x=offMap.b;\n #endif\n\n #ifdef OFFSET_Y_R\n offset.y=offMap.r;\n #endif\n #ifdef OFFSET_Y_G\n offset.y=offMap.g;\n #endif\n #ifdef OFFSET_Y_B\n offset.y=offMap.b;\n #endif\n\n #ifdef OFFSET_Z_R\n offset.z=offMap.r;\n #endif\n #ifdef OFFSET_Z_G\n offset.z=offMap.g;\n #endif\n #ifdef OFFSET_Z_B\n offset.z=offMap.b;\n #endif\n\n offset*=offMul;\n\n #endif\n\n float v=Cellular3D(vec3(p.x,p.y,z)+offset);\n\n\n if (harmonics >= 2.0) v += Cellular3D(vec3(p.x,p.y,z)*2.3+offset) * 0.5;\n if (harmonics >= 3.0) v += Cellular3D(vec3(p.x,p.y,z)*4.2+offset) * 0.25;\n if (harmonics >= 4.0) v += Cellular3D(vec3(p.x,p.y,z)*8.1+offset) * 0.125;\n if (harmonics >= 5.0) v += Cellular3D(vec3(p.x,p.y,z)*16.7+offset) * 0.0625;\n\n\n vec4 col=vec4(v,v,v,1.0);\n\n vec4 base=texture(tex,texCoord);\n\n\n float str=1.0;\n #ifdef HAS_TEX_MASK\n str=texture(texMask,texCoord).r;\n #endif\n\n outColor=cgl_blendPixel(base,col,amount*str);\n\n}\n",}; +const + render = op.inTrigger("render"), + inTexMask = op.inTexture("Mask"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + + amount = op.inValueSlider("Amount", 1), + x = op.inValue("X", 0), + y = op.inValue("Y", 0), + z = op.inValue("Z", 0), + scale = op.inValue("Scale", 5), + + inHarmonics = op.inSwitch("Harmonics", ["1", "2", "3", "4", "5"], "1"), + tile = op.inValueBool("Tileable", false), + + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.cellularnoise3d_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniOffset = new CGL.Uniform(shader, "t", "texOffsetZ", 1), + uniMask = new CGL.Uniform(shader, "t", "texMask", 2), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniZ = new CGL.Uniform(shader, "f", "z", z), + uniX = new CGL.Uniform(shader, "f", "x", x), + uniY = new CGL.Uniform(shader, "f", "y", y), + uniScale = new CGL.Uniform(shader, "f", "scale", scale); + +tile.onChange = updateTileable; +function updateTileable() +{ + if (tile.get())shader.define("DO_TILEABLE"); + else shader.removeDefine("DO_TILEABLE"); +} + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount,maskAlpha); + +// offsetMap + +const + inTexOffsetTex = op.inTexture("Offset"), + inOffsetMul = op.inFloat("Offset Multiply", 1), + + offsetX = op.inSwitch("Offset X", ["None", "R", "G", "B"], "None"), + offsetY = op.inSwitch("Offset Y", ["None", "R", "G", "B"], "None"), + offsetZ = op.inSwitch("Offset Z", ["None", "R", "G", "B"], "R"); + +const uniOffMul = new CGL.Uniform(shader, "f", "offMul", inOffsetMul); + +op.setPortGroup("Offset Map", [inTexOffsetTex, offsetZ, offsetY, offsetX, inOffsetMul]); + +const uniAspect = new CGL.Uniform(shader, "f", "aspect", 1); +const uniHarmonics = new CGL.Uniform(shader, "f", "harmonics", 0); +inHarmonics.onChange = () => +{ + uniHarmonics.setValue(parseFloat(inHarmonics.get())); +}; + +offsetX.onChange = +offsetY.onChange = +offsetZ.onChange = +inTexMask.onChange = +inTexOffsetTex.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("HAS_TEX_OFFSETMAP", inTexOffsetTex.get()); + shader.toggleDefine("HAS_TEX_MASK", inTexMask.get()); + + shader.toggleDefine("OFFSET_X_R", offsetX.get() == "R"); + shader.toggleDefine("OFFSET_X_G", offsetX.get() == "G"); + shader.toggleDefine("OFFSET_X_B", offsetX.get() == "B"); + + shader.toggleDefine("OFFSET_Y_R", offsetY.get() == "R"); + shader.toggleDefine("OFFSET_Y_G", offsetY.get() == "G"); + shader.toggleDefine("OFFSET_Y_B", offsetY.get() == "B"); + + shader.toggleDefine("OFFSET_Z_R", offsetZ.get() == "R"); + shader.toggleDefine("OFFSET_Z_G", offsetZ.get() == "G"); + shader.toggleDefine("OFFSET_Z_B", offsetZ.get() == "B"); + + offsetX.setUiAttribs({ "greyout": !inTexOffsetTex.isLinked() }); + offsetY.setUiAttribs({ "greyout": !inTexOffsetTex.isLinked() }); + offsetZ.setUiAttribs({ "greyout": !inTexOffsetTex.isLinked() }); +} + +// end offsetmap + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + uniAspect.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (inTexOffsetTex.get()) cgl.setTexture(1, inTexOffsetTex.get().tex); + if (inTexMask.get()) cgl.setTexture(2, inTexMask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.CellularNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["e0ce24fe-4ddb-4df4-a087-623cf4c711a7"]={f:Ops.Gl.TextureEffects.Noise.CellularNoise_v2,objName:"Ops.Gl.TextureEffects.Noise.CellularNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.FBMNoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.FBMNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"fbmnoise_frag":"UNI sampler2D tex;\nUNI float anim;\n\nUNI float scale;\nUNI float repeat;\n\nUNI float scrollX;\nUNI float scrollY;\n\nUNI float amount;\n\nUNI bool layer1;\nUNI bool layer2;\nUNI bool layer3;\nUNI bool layer4;\nUNI vec3 color;\nUNI float aspect;\n\nIN vec2 texCoord;\n\n\n{{CGL.BLENDMODES3}}\n\n// csdcsdcds\n// adapted from warp shader by inigo quilez/iq\n// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.\n\n// See here for a tutorial on how to make this: http://www.iquilezles.org/www/articles/warp/warp.htm\n\nconst mat2 m = mat2( 0.80, 0.60, -0.60, 0.80 );\n\nfloat noise( in vec2 x )\n{\n\treturn sin(1.5*x.x)*sin(1.5*x.y);\n}\n\nfloat fbm4( vec2 p )\n{\n float f = 0.0;\n f += 0.5000*noise( p ); p = m*p*2.02;\n f += 0.2500*noise( p ); p = m*p*2.03;\n f += 0.1250*noise( p ); p = m*p*2.01;\n f += 0.0625*noise( p );\n return f/0.9375;\n}\n\nfloat fbm6( vec2 p )\n{\n float f = 0.0;\n f += 0.500000*(0.5+0.5*noise( p )); p = m*p*2.02;\n f += 0.250000*(0.5+0.5*noise( p )); p = m*p*2.03;\n f += 0.125000*(0.5+0.5*noise( p )); p = m*p*2.01;\n f += 0.062500*(0.5+0.5*noise( p )); p = m*p*2.04;\n f += 0.031250*(0.5+0.5*noise( p )); p = m*p*2.01;\n f += 0.015625*(0.5+0.5*noise( p ));\n return f/0.96875;\n}\n\nvoid main()\n{\n vec2 tc=texCoord;\n\t#ifdef DO_TILEABLE\n\t tc=abs(texCoord-0.5);\n\t#endif\n\n vec2 p=(tc-0.5)*scale;\n\n p.y/=aspect;\n vec2 q = vec2( fbm4( p + vec2(0.3+scrollX,0.20+scrollY) ),\n fbm4( p + vec2(3.1+scrollX,1.3+scrollY) ) );\n\n vec2 q2 = vec2( fbm4( p + vec2(2.0+scrollX,1.0+scrollY) ),\n fbm4( p + vec2(3.1+scrollX,1.3+scrollY) ) );\n\n vec2 q3 = vec2( fbm4( p + vec2(9.0+scrollX,4.0+scrollY) ),\n fbm4( p + vec2(3.1+scrollX,4.3+scrollY) ) );\n\n float v= fbm4( ( p + 4.0*q +anim*0.1)*repeat);\n float v2= fbm4( (p + 4.0*q2 +anim*0.1)*repeat );\n\n float v3= fbm6( (p + 4.0*q3 +anim*0.1)*repeat );\n float v4= fbm6( (p + 4.0*q2 +anim*0.1)*repeat );\n\n vec4 base=texture(tex,texCoord);\n\n vec4 finalColor;\n float colVal=0.0;\n float numLayers=0.0;\n\n if(layer1)\n {\n colVal+=v;\n numLayers++;\n }\n\n if(layer2)\n {\n colVal+=v2;\n numLayers++;\n }\n\n if(layer3)\n {\n colVal+=v3;\n numLayers++;\n }\n\n if(layer4)\n {\n colVal+=v4;\n numLayers++;\n }\n\n finalColor=vec4( color*vec3(colVal/numLayers),1.0);\n\n outColor = cgl_blendPixel(base,finalColor,amount);\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + r = op.inValueSlider("r", 1.0), + g = op.inValueSlider("g", 1.0), + b = op.inValueSlider("b", 1.0), + trigger = op.outTrigger("trigger"); + +r.setUiAttribs({ "colorPick": true }); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "fbmnoise"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.fbmnoise_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniScale = new CGL.Uniform(shader, "f", "scale", op.inValue("scale", 2)), + uniAnim = new CGL.Uniform(shader, "f", "anim", op.inValue("anim", 0)), + uniScrollX = new CGL.Uniform(shader, "f", "scrollX", op.inValue("scrollX", 9)), + uniScrollY = new CGL.Uniform(shader, "f", "scrollY", op.inValue("scrollY", 0)), + uniRepeat = new CGL.Uniform(shader, "f", "repeat", op.inValue("repeat", 1)), + uniAspect = new CGL.Uniform(shader, "f", "aspect", op.inValue("aspect", 1)), + uniLayer1 = new CGL.Uniform(shader, "b", "layer1", op.inValueBool("Layer 1", true)), + uniLayer2 = new CGL.Uniform(shader, "b", "layer2", op.inValueBool("Layer 2", true)), + uniLayer3 = new CGL.Uniform(shader, "b", "layer3", op.inValueBool("Layer 3", true)), + uniLayer4 = new CGL.Uniform(shader, "b", "layer4", op.inValueBool("Layer 4", true)), + uniColor = new CGL.Uniform(shader, "3f", "color", r, g, b), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +const tile = op.inValueBool("Tileable", false); +tile.onChange = updateTileable; + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +function updateTileable() +{ + shader.toggleDefine("DO_TILEABLE", tile.get()); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + uniAspect.set(cgl.currentTextureEffect.getCurrentSourceTexture().width / cgl.currentTextureEffect.getCurrentSourceTexture().height); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.FBMNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["9422eeab-b6c5-47d1-8737-d5c43dc137a3"]={f:Ops.Gl.TextureEffects.Noise.FBMNoise_v2,objName:"Ops.Gl.TextureEffects.Noise.FBMNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.GlitchNoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.GlitchNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"glitchnoise_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\n\n{{CGL.BLENDMODES3}}\n\nUNI float amount;\nUNI float time;\nUNI float frequency;\nUNI float strength;\nUNI float blockSizeA;\nUNI float blockSizeB;\nUNI float blockSizeC;\nUNI float blockSizeD;\nUNI float scrollX;\nUNI float scrollY;\n\nfloat rng2(vec2 seed)\n{\n return fract(sin(dot(seed * floor(time * (frequency * 10.0)), vec2(127.1,311.7))) * 43758.5453123);//43758.5453123\n}\n\nfloat rng(float seed)\n{\n return rng2(vec2(seed, 1.0));\n}\n\nvoid main( )\n{\n //add scroll for x and y\n vec2 scrollXY = vec2(scrollX,scrollY);\n vec2 blockS = floor((texCoord + scrollXY ) * vec2(blockSizeA,blockSizeB));\n vec2 blockL = floor((texCoord ) * vec2(blockSizeC,blockSizeD));\n\n float r = rng2(texCoord);\n vec3 noise = (vec3(r, 1. - r, r / 2. + 0.5) * 1.0 - 2.0) * 0.08;\n\n float lineNoise = pow(rng2(blockS), 8.0) * pow(rng2(blockL), 3.0) - pow(rng(7.2341), 17.0) * 2.;\n\n vec4 col1 = texture(tex, texCoord);\n vec4 col2 = texture(tex, texCoord + vec2(lineNoise * (0.05 * strength) * rng(5.0), 1));\n vec4 col3 = texture(tex, texCoord - vec2(lineNoise * (0.05 * strength) * rng(31.0), 1));\n\n float glitch = (lineNoise * strength * rng(5.0)) + (lineNoise * strength * rng(31.));\n float glitch2 = lineNoise * strength * rng(31.);\n\n //blend section\n vec4 col=vec4(vec3(glitch),1.0);\n //original texture\n vec4 base=texture(tex,texCoord);\n\n outColor=cgl_blendPixel(base,col,amount);\n\n}",}; +const + render = op.inTrigger("render"), + amount = op.inValueSlider("Amount", 1), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + + time = op.inValue("Seed", 0), + inFrequency = op.inValue("frequency", 1), + inStrength = op.inValue("strength", 8.0), + inBlockSizeA = op.inValue("Block size small x", 24.0), + inBlockSizeB = op.inValue("Block size small y", 9.0), + inBlockSizeC = op.inValue("Block size large x", 8.0), + inBlockSizeD = op.inValue("Block size large y", 4.0), + inScrollX = op.inValue("Scroll X", 0.0), + inScrollY = op.inValue("Scroll Y", 0.0), + trigger = op.outTrigger("trigger"); + +const TEX_SLOT = 0; +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); +shader.setSource(shader.getDefaultVertexShader(), attachments.glitchnoise_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", TEX_SLOT), + uniformAmount = new CGL.Uniform(shader, "f", "amount", amount), + timeUniform = new CGL.Uniform(shader, "f", "time", time), + frequencyUniform = new CGL.Uniform(shader, "f", "frequency", inFrequency), + strengthUniform = new CGL.Uniform(shader, "f", "strength", inStrength), + sizeAUniform = new CGL.Uniform(shader, "f", "blockSizeA", inBlockSizeA), + sizeBUniform = new CGL.Uniform(shader, "f", "blockSizeB", inBlockSizeB), + sizeCUniform = new CGL.Uniform(shader, "f", "blockSizeC", inBlockSizeC), + sizeDUniform = new CGL.Uniform(shader, "f", "blockSizeD", inBlockSizeD), + scrollXUniform = new CGL.Uniform(shader, "f", "scrollX", inScrollX), + scrollYUniform = new CGL.Uniform(shader, "f", "scrollY", inScrollY); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(TEX_SLOT, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.GlitchNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["87de572e-644a-4613-b0f3-68b9ec74f489"]={f:Ops.Gl.TextureEffects.Noise.GlitchNoise_v2,objName:"Ops.Gl.TextureEffects.Noise.GlitchNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.HexagonNoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.HexagonNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"hexnoise_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float scale;\nUNI float addX;\nUNI float addY;\nUNI float addZ;\nUNI float seed2;\nUNI float minIn;\nUNI float maxIn;\n\nUNI float aspect;\n{{CGL.BLENDMODES3}}\n\n\n#define PI 3.14159265\n#define TAU (2.0*PI)\n\nvoid pR(inout vec2 p, float a)\n{\n\tp = cos(a)*p + sin(a)*vec2(p.y, -p.x);\n}\n\n//cell code from https://www.shadertoy.com/view/lldfWH\nvec4 hex(vec2 uv, out vec2 id)\n{\n // #ifdef ASPECT\n uv.x*=aspect;\n // #endif\n\n uv *= mat2(1.1547,0.0,-0.5773503,1.0);\n vec2 f = fract(uv);\n float triid = 1.0;\n\tif((f.x+f.y) > 1.0) { f = 1.0 - f; triid = -1.0; }\n\n vec2 co = step(f.yx,f) * step(1.0-f.x-f.y,max(f.x,f.y));\n id = floor(uv) + (triid < 0.0 ? 1.0 - co : co);\n co = (f - co) * triid * mat2(0.866026,0.0,0.5,1.0);\n\n uv = abs(co);\n return vec4(0.5-max(uv.y,abs(dot(vec2(0.866026,0.5),uv))),length(co),co);\n}\n\nfloat random(vec2 co)\n{\n float r=fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (5.711+(mod(addZ+5711.210,57.0))));\n\n #ifdef LOOP\n r=abs(r-0.5)*2.0;\n #endif\n\n return r;\n}\n\n\nvoid main()\n{\n vec2 uv = texCoord;\n uv -= 0.5;\n uv -= vec2(addX,addY);\n uv*=scale;\n\n #ifdef FLIP\n pR(uv.xy,0.25*TAU);\n #endif\n\n float r,g,b;\n vec4 rnd = vec4(0.0);\n\n // get hexagon cell id\n vec2 id0;\n vec4 h = hex(uv*4.0, id0);\n\n #ifndef RGB\n r=g=b= random(id0.xy*id0.xy+seed2);\n #endif\n\n #ifdef RGB\n r = random(((id0.xy*id0.xy)*0.234)+seed2);\n g = random(((id0.xy*id0.xy)*0.234)+seed2+0.9812);\n b = random(((id0.xy*id0.xy)*0.234)+seed2+57.101);\n #endif\n\n rnd = clamp( vec4( r,g,b,1.0 ),vec4(minIn), vec4(maxIn) );\n vec4 base=texture(tex,texCoord);\n\n outColor=cgl_blendPixel(base,rnd,amount);\n}\n\n//",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + inLoop = op.inValueBool("Loop", false), + inRGB = op.inValueBool("RGB", false), + minValue = op.inValue("Minimum value", 0), + maxValue = op.inValue("Maximum value", 1), + scale = op.inValue("Scale", 3), + orientation = op.inBool("Orientation", false), + addX = op.inValue("X", 0), + addY = op.inValue("Y", 0), + addZ = op.inValue("Z", 0), + seed2 = op.inValue("Seed", 0), + trigger = op.outTrigger("Next"); + +op.setPortGroup("Look", [inRGB, inLoop, minValue, maxValue]); +op.setPortGroup("Position", [addX, addY, addZ]); +op.setPortGroup("Position", [addX, addY, addZ]); +op.setPortGroup("", [scale, orientation]); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); +shader.setSource(shader.getDefaultVertexShader(), attachments.hexnoise_frag); + +const + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + timeUniform = new CGL.Uniform(shader, "f", "time", 1.0), + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uni_scale = new CGL.Uniform(shader, "f", "scale", scale), + uni_addX = new CGL.Uniform(shader, "f", "addX", addX), + uni_addY = new CGL.Uniform(shader, "f", "addY", addY), + uni_addZ = new CGL.Uniform(shader, "f", "addZ", addZ), + uni_seed = new CGL.Uniform(shader, "f", "seed2", seed2), + uni_minValue = new CGL.Uniform(shader, "f", "minIn", minValue), + uni_maxValue = new CGL.Uniform(shader, "f", "maxIn", maxValue), + uni_asp = new CGL.Uniform(shader, "f", "aspect", 1); + +inLoop.onChange = +inRGB.onChange = +orientation.onChange = updateDefines; + +function updateDefines() +{ + shader.toggleDefine("FLIP", orientation.get()); + shader.toggleDefine("RGB", inRGB.get()); + shader.toggleDefine("LOOP", inLoop.get()); +} + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + uni_asp.setValue(cgl.currentTextureEffect.aspectRatio); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.HexagonNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["1c4def10-3574-4ef7-8abc-de28d2293095"]={f:Ops.Gl.TextureEffects.Noise.HexagonNoise_v2,objName:"Ops.Gl.TextureEffects.Noise.HexagonNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.LayerNoise_v3 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.LayerNoise_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"layernoise_frag":"// @author Jan Scheurer - Xe-Development UG\n// @copyright undefined development UG\n\n{{MODULES_HEAD}}\n\nUNI int mode;\nUNI vec4 attribs;\nUNI vec3 scroll;\nUNI bool rgba;\nUNI float amount;\nIN vec2 texCoord;\nUNI sampler2D tex;\nUNI float aspect;\n\n{{CGL.BLENDMODES3}}\n\n#define LINEAR 0\n#define EXPONENTIAL 1\n#define LOGARITHMIC 2\n\nfloat rand (vec3 p) {\n return fract(sin(dot(p,vec3(12.4085,48.512313,32.6143)))*42754.71415);\n}\n\nconst vec2 O = vec2(0,1);\n\nfloat noise (vec3 p) {\n vec3 b=floor(p),f=fract(p);\n return mix(\n mix(mix(rand(b+O.xxx),rand(b+O.yxx),f.x),mix(rand(b+O.xyx),rand(b+O.yyx),f.x),f.y),\n mix(mix(rand(b+O.xxy),rand(b+O.yxy),f.x),mix(rand(b+O.xyy),rand(b+O.yyy),f.x),f.y),\n f.z\n );\n}\n\nfloat gn(vec3 p){\n float n = 0., fi;\n int numLayers = int(attribs.g);\n for (int i = 1; i < 100; i++) {\n if (i > numLayers) break;\n if (mode == LINEAR)\n fi = float(i),p+=attribs.r;\n else if (mode == EXPONENTIAL)\n fi = float(pow(float(i),attribs.a));\n else if (mode == LOGARITHMIC)\n fi = log(float(i+1)),p+=attribs.r;\n n += noise(p*fi) / fi;\n }\n return n*attribs.b;\n}\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n vec2 tc=texCoord;\n tc.y/=aspect;\n\n\t#ifdef DO_TILEABLE\n\t tc=abs(texCoord-0.5);\n\t#endif\n vec3 p = vec3(tc * 2. - 1.,0) + scroll;\n vec4 col;\n if (rgba)\n {\n for(int i = 0; i < 4; i++)\n {\n col[i] = gn(p*attribs.r);\n p += attribs.r;\n }\n } else col = vec4(vec3(gn(p*attribs.r)),1);\n\n outColor=cgl_blendPixel(base,col,amount);\n}",}; +const cgl = op.patch.cgl; + +// inputs +const + inTrigger = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inFloatSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + inLayerMode = op.inValueSelect("mode", [ + "exponential", + "logarithmic", + "linear" + ], "exponential"), + inRGBA = op.inValueBool("RGBA"), + inScale = op.inValue("scale", 7), + inNumLayers = op.inValueInt("layers", 3), + inFactor = op.inFloat("factor", 1), + inExponent = op.inFloat("exponent", 2), + inScrollX = op.inFloat("scrollX"), + inScrollY = op.inFloat("scrollY"), + inScrollZ = op.inFloat("scrollZ"), + tile = op.inValueBool("Tileable", false), + outTrigger = op.outTrigger("trigger"); + +// locals +const TEX_SLOT = 0; +const shader = new CGL.Shader(cgl, "layernoise"); +const attribs = [inScale.get(), inNumLayers.get(), inFactor.get(), 0]; +shader.setSource(shader.getDefaultVertexShader(), attachments.layernoise_frag); +const attributes = new CGL.Uniform(shader, "4f", "attribs", attribs); + +const uniMode = new CGL.Uniform(shader, "i", "mode", 2); +shader._addUniform(uniMode); +const uniRGBA = new CGL.Uniform(shader, "b", "rgba", false); +const scrollArr = [inScrollX.get(), inScrollY.get(), inScrollZ.get()]; +const uniScroll = new CGL.Uniform(shader, "3f", "scroll", scrollArr); +const uniformAmount = new CGL.Uniform(shader, "f", "amount", amount); +const uniAspect = new CGL.Uniform(shader, "f", "aspect", 1); +const textureUniform = new CGL.Uniform(shader, "t", "tex", TEX_SLOT); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +let needsUpdate = false; +// events + +tile.onChange = updateTileable; + +inTrigger.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + if (needsUpdate) + { + attribs[0] = inScale.get(); + attribs[1] = inNumLayers.get(); + + const layerMode = inLayerMode.get(); + if (layerMode == "linear") + uniMode.set(0); + else if (layerMode == "exponential") + uniMode.set(1); + else + uniMode.set(2); + + attribs[2] = inFactor.get(); + attribs[3] = inExponent.get(); + attributes.set(attribs); + + uniRGBA.set(inRGBA.get()); + scroll[0] = inScrollX.get(); + scroll[1] = inScrollY.get(); + scroll[2] = inScrollZ.get(); + uniScroll.set(scroll); + + needsUpdate = false; + } + + uniAspect.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.setTexture(TEX_SLOT, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + cgl.currentTextureEffect.finish(); + cgl.popShader(); + outTrigger.trigger(); +}; + +function updateTileable() +{ + shader.toggleDefine("DO_TILEABLE", tile.get()); +} + +inScale.onChange = + inNumLayers.onChange = + inLayerMode.onChange = + inExponent.onChange = + inFactor.onChange = + inRGBA.onChange = + inScrollX.onChange = + inScrollY.onChange = + inScrollZ.onChange = update; +function update() +{ + needsUpdate = true; +} +update(); + + +}; + +Ops.Gl.TextureEffects.Noise.LayerNoise_v3.prototype = new CABLES.Op(); +CABLES.OPS["b918b3f1-53ee-4d32-92b7-73a1d00e0d69"]={f:Ops.Gl.TextureEffects.Noise.LayerNoise_v3,objName:"Ops.Gl.TextureEffects.Noise.LayerNoise_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.Noise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.Noise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"noise_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float time;\n\n#ifdef HAS_MULMASK\n UNI sampler2D texMul;\n#endif\n\n{{CGL.BLENDMODES3}}\n{{MODULES_HEAD}}\n\n{{CGL.RANDOM_TEX}}\n\nvoid main()\n{\n vec4 rnd;\n\n #ifdef RGB\n rnd=vec4(cgl_random3(texCoord.xy+vec2(time)),1.0);\n #else\n float r=cgl_random(texCoord.xy+vec2(time));\n rnd=vec4( r,r,r,1.0 );\n #endif\n\n vec4 base=texture(tex,texCoord);\n vec4 col=rnd;//( _blend(base.rgb,rnd.rgb) ,1.0);\n\n #ifdef NORMALIZE\n col.rgb=(col.rgb-0.5)*2.0;\n #endif\n\n #ifdef HAS_MULMASK\n col.rgb*=texture(texMul,texCoord).rgb;\n #endif\n\n outColor=cgl_blendPixel(base,col,amount);\n // outColor=vec4( mix( col.rgb, base.rgb ,1.0-base.a*amount),1.0);\n}",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + amount = op.inValueSlider("Amount", 1), + animated = op.inValueBool("Animated", true), + inRGB = op.inValueBool("RGB", false), + normalize = op.inValueBool("Normalize", false), + inTexMul = op.inTexture("Multiply"), + trigger = op.outTrigger("Next"); + +const + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + timeUniform = new CGL.Uniform(shader, "f", "time", 1.0), + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + mulUniform = new CGL.Uniform(shader, "t", "texMul", 1); + +shader.setSource(shader.getDefaultVertexShader(), attachments.noise_frag); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount,maskAlpha); + +op.toWorkPortsNeedToBeLinked(render); + +inTexMul.onChange = +normalize.onChange = +inRGB.onChange = function () +{ + shader.toggleDefine("HAS_MULMASK", inTexMul.get()); + shader.toggleDefine("RGB", inRGB.get()); + shader.toggleDefine("NORMALIZE", normalize.get()); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + if (animated.get()) timeUniform.setValue(op.patch.freeTimer.get() / 1000 % 100); + else timeUniform.setValue(0); + + cgl.pushShader(shader); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (inTexMul.get())cgl.setTexture(1, inTexMul.get().tex); + + cgl.currentTextureEffect.bind(); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.Noise_v2.prototype = new CABLES.Op(); +CABLES.OPS["b1d9aacc-dc52-43a6-a00f-414f08768800"]={f:Ops.Gl.TextureEffects.Noise.Noise_v2,objName:"Ops.Gl.TextureEffects.Noise.Noise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.PerlinNoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.PerlinNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"perlinnoise3d_frag":"UNI float z;\nUNI float x;\nUNI float y;\nUNI float scale;\nUNI float rangeMul;\nUNI float harmonics;\nUNI float aspect;\n\nIN vec2 texCoord;\nUNI sampler2D tex;\n\n#ifdef HAS_TEX_OFFSETMAP\n UNI sampler2D texOffsetZ;\n UNI float offMul;\n#endif\n\n#ifdef HAS_TEX_MASK\n UNI sampler2D texMask;\n#endif\n\nUNI float amount;\n\n{{CGL.BLENDMODES3}}\n\n\nfloat Interpolation_C2( float x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } // 6x^5-15x^4+10x^3\t( Quintic Curve. As used by Perlin in Improved Noise. http://mrl.nyu.edu/~perlin/paper445.pdf )\nvec2 Interpolation_C2( vec2 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec3 Interpolation_C2( vec3 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2( vec4 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2_InterpAndDeriv( vec2 x ) { return x.xyxy * x.xyxy * ( x.xyxy * ( x.xyxy * ( x.xyxy * vec2( 6.0, 0.0 ).xxyy + vec2( -15.0, 30.0 ).xxyy ) + vec2( 10.0, -60.0 ).xxyy ) + vec2( 0.0, 30.0 ).xxyy ); }\nvec3 Interpolation_C2_Deriv( vec3 x ) { return x * x * (x * (x * 30.0 - 60.0) + 30.0); }\n\n\nvoid FAST32_hash_3D( vec3 gridcell, out vec4 lowz_hash, out vec4 highz_hash )\t//\tgenerates a random number for each of the 8 cell corners\n{\n // gridcell is assumed to be an integer coordinate\n\n //\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const float SOMELARGEFLOAT = 635.298681;\n const float ZINC = 48.500388;\n\n //\ttruncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n //\tcalculate the noise\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n P = P.xzxz * P.yyww;\n highz_hash.xy = vec2( 1.0 / ( SOMELARGEFLOAT + vec2( gridcell.z, gridcell_inc1.z ) * ZINC ) );\n lowz_hash = fract( P * highz_hash.xxxx );\n highz_hash = fract( P * highz_hash.yyyy );\n}\n\n\n\n\nvoid FAST32_hash_3D( \tvec3 gridcell,\n out vec4 lowz_hash_0,\n out vec4 lowz_hash_1,\n out vec4 lowz_hash_2,\n out vec4 highz_hash_0,\n out vec4 highz_hash_1,\n out vec4 highz_hash_2\t)\t\t//\tgenerates 3 random numbers for each of the 8 cell corners\n{\n // gridcell is assumed to be an integer coordinate\n\n //\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const vec3 SOMELARGEFLOATS = vec3( 635.298681, 682.357502, 668.926525 );\n const vec3 ZINC = vec3( 48.500388, 65.294118, 63.934599 );\n\n //\ttruncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n //\tcalculate the noise\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n P = P.xzxz * P.yyww;\n vec3 lowz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) );\n vec3 highz_mod = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) );\n lowz_hash_0 = fract( P * lowz_mod.xxxx );\n highz_hash_0 = fract( P * highz_mod.xxxx );\n lowz_hash_1 = fract( P * lowz_mod.yyyy );\n highz_hash_1 = fract( P * highz_mod.yyyy );\n lowz_hash_2 = fract( P * lowz_mod.zzzz );\n highz_hash_2 = fract( P * highz_mod.zzzz );\n}\nfloat Falloff_Xsq_C1( float xsq ) { xsq = 1.0 - xsq; return xsq*xsq; }\t// ( 1.0 - x*x )^2 ( Used by Humus for lighting falloff in Just Cause 2. GPUPro 1 )\nfloat Falloff_Xsq_C2( float xsq ) { xsq = 1.0 - xsq; return xsq*xsq*xsq; }\t// ( 1.0 - x*x )^3. NOTE: 2nd derivative is 0.0 at x=1.0, but non-zero at x=0.0\nvec4 Falloff_Xsq_C2( vec4 xsq ) { xsq = 1.0 - xsq; return xsq*xsq*xsq; }\n\n\n//\n//\tPerlin Noise 3D ( gradient noise )\n//\tReturn value range of -1.0->1.0\n//\thttp://briansharpe.files.wordpress.com/2011/11/perlinsample.jpg\n//\nfloat Perlin3D( vec3 P )\n{\n //\testablish our grid cell and unit position\n vec3 Pi = floor(P);\n vec3 Pf = P - Pi;\n vec3 Pf_min1 = Pf - 1.0;\n\n#if 1\n //\n //\tclassic noise.\n //\trequires 3 random values per point. with an efficent hash function will run faster than improved noise\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1;\n FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n //SGPP_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 );\n\n //\tcalculate the gradients\n vec4 grad_x0 = hashx0 - 0.49999;\n vec4 grad_y0 = hashy0 - 0.49999;\n vec4 grad_z0 = hashz0 - 0.49999;\n vec4 grad_x1 = hashx1 - 0.49999;\n vec4 grad_y1 = hashy1 - 0.49999;\n vec4 grad_z1 = hashz1 - 0.49999;\n vec4 grad_results_0 = inversesqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 );\n vec4 grad_results_1 = inversesqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( vec2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + vec2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 );\n\n#if 1\n //\tClassic Perlin Interpolation\n vec3 blend = Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n float final = dot( res0, blend2.zxzx * blend2.wwyy );\n final *= 1.1547005383792515290182975610039;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.75)\n return final;\n#else\n //\tClassic Perlin Surflet\n //\thttp://briansharpe.wordpress.com/2012/03/09/modifications-to-classic-perlin-noise/\n Pf *= Pf;\n Pf_min1 *= Pf_min1;\n vec4 vecs_len_sq = vec4( Pf.x, Pf_min1.x, Pf.x, Pf_min1.x ) + vec4( Pf.yy, Pf_min1.yy );\n float final = dot( Falloff_Xsq_C2( min( vec4( 1.0 ), vecs_len_sq + Pf.zzzz ) ), grad_results_0 ) + dot( Falloff_Xsq_C2( min( vec4( 1.0 ), vecs_len_sq + Pf_min1.zzzz ) ), grad_results_1 );\n final *= 2.3703703703703703703703703703704;\t\t//\t(optionally) scale things to a strict -1.0->1.0 range *= 1.0/cube(0.75)\n return final;\n#endif\n\n#else\n //\n //\timproved noise.\n //\trequires 1 random value per point. Will run faster than classic noise if a slow hashing function is used\n //\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hash_lowz, hash_highz;\n FAST32_hash_3D( Pi, hash_lowz, hash_highz );\n //BBS_hash_3D( Pi, hash_lowz, hash_highz );\n //SGPP_hash_3D( Pi, hash_lowz, hash_highz );\n\n //\n //\t\"improved\" noise using 8 corner gradients. Faster than the 12 mid-edge point method.\n //\tKen mentions using diagonals like this can cause \"clumping\", but we'll live with that.\n //\t[1,1,1] [-1,1,1] [1,-1,1] [-1,-1,1]\n //\t[1,1,-1] [-1,1,-1] [1,-1,-1] [-1,-1,-1]\n //\n hash_lowz -= 0.5;\n vec4 grad_results_0_0 = vec2( Pf.x, Pf_min1.x ).xyxy * sign( hash_lowz );\n hash_lowz = abs( hash_lowz ) - 0.25;\n vec4 grad_results_0_1 = vec2( Pf.y, Pf_min1.y ).xxyy * sign( hash_lowz );\n vec4 grad_results_0_2 = Pf.zzzz * sign( abs( hash_lowz ) - 0.125 );\n vec4 grad_results_0 = grad_results_0_0 + grad_results_0_1 + grad_results_0_2;\n\n hash_highz -= 0.5;\n vec4 grad_results_1_0 = vec2( Pf.x, Pf_min1.x ).xyxy * sign( hash_highz );\n hash_highz = abs( hash_highz ) - 0.25;\n vec4 grad_results_1_1 = vec2( Pf.y, Pf_min1.y ).xxyy * sign( hash_highz );\n vec4 grad_results_1_2 = Pf_min1.zzzz * sign( abs( hash_highz ) - 0.125 );\n vec4 grad_results_1 = grad_results_1_0 + grad_results_1_1 + grad_results_1_2;\n\n //\tblend the gradients and return\n vec3 blend = Interpolation_C2( Pf );\n vec4 res0 = mix( grad_results_0, grad_results_1, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n return dot( res0, blend2.zxzx * blend2.wwyy ) * (2.0 / 3.0);\t//\t(optionally) mult by (2.0/3.0) to scale to a strict -1.0->1.0 range\n#endif\n}\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n vec2 p=vec2(texCoord.x-0.5,texCoord.y-0.5);\n\n p=p*scale;\n p=vec2(p.x+0.5-x,p.y+0.5-y);\n\n\n\n vec3 offset;\n #ifdef HAS_TEX_OFFSETMAP\n vec4 offMap=texture(texOffsetZ,texCoord);\n\n #ifdef OFFSET_X_R\n offset.x=offMap.r;\n #endif\n #ifdef OFFSET_X_G\n offset.x=offMap.g;\n #endif\n #ifdef OFFSET_X_B\n offset.x=offMap.b;\n #endif\n\n #ifdef OFFSET_Y_R\n offset.y=offMap.r;\n #endif\n #ifdef OFFSET_Y_G\n offset.y=offMap.g;\n #endif\n #ifdef OFFSET_Y_B\n offset.y=offMap.b;\n #endif\n\n #ifdef OFFSET_Z_R\n offset.z=offMap.r;\n #endif\n #ifdef OFFSET_Z_G\n offset.z=offMap.g;\n #endif\n #ifdef OFFSET_Z_B\n offset.z=offMap.b;\n #endif\n offset*=offMul;\n #endif\n\n float aa=texture(tex,texCoord).r;\n\n float v = 0.0;\n p.x*=aspect;\n\n v+=Perlin3D(vec3(p.x,p.y,z)+offset);\n\n #ifdef HARMONICS\n if (harmonics >= 2.0) v += Perlin3D(vec3(p.x,p.y,z)*2.2+offset) * 0.5;\n if (harmonics >= 3.0) v += Perlin3D(vec3(p.x,p.y,z)*4.3+offset) * 0.25;\n if (harmonics >= 4.0) v += Perlin3D(vec3(p.x,p.y,z)*8.4+offset) * 0.125;\n if (harmonics >= 5.0) v += Perlin3D(vec3(p.x,p.y,z)*16.5+offset) * 0.0625;\n #endif\n\n\n v*=rangeMul;\n v=v*0.5+0.5;\n float v2=v;\n float v3=v;\n\n #ifdef RGB\n v2=Perlin3D(vec3(p.x+2.0,p.y+2.0,z))*0.5+0.5;\n\n #ifdef HARMONICS\n if (harmonics >= 2.0) v2 += Perlin3D(vec3(p.x,p.y,z)*2.2+offset) * 0.5;\n if (harmonics >= 3.0) v2 += Perlin3D(vec3(p.x,p.y,z)*4.3+offset) * 0.25;\n if (harmonics >= 4.0) v2 += Perlin3D(vec3(p.x,p.y,z)*8.4+offset) * 0.125;\n if (harmonics >= 5.0) v2 += Perlin3D(vec3(p.x,p.y,z)*16.5+offset) * 0.0625;\n #endif\n\n v3=Perlin3D(vec3(p.x+3.0,p.y+3.0,z))*0.5+0.5;\n\n #ifdef HARMONICS\n if (harmonics >= 2.0) v3 += Perlin3D(vec3(p.x,p.y,z)*2.2+offset) * 0.5;\n if (harmonics >= 3.0) v3 += Perlin3D(vec3(p.x,p.y,z)*4.3+offset) * 0.25;\n if (harmonics >= 4.0) v3 += Perlin3D(vec3(p.x,p.y,z)*8.4+offset) * 0.125;\n if (harmonics >= 5.0) v3 += Perlin3D(vec3(p.x,p.y,z)*16.5+offset) * 0.0625;\n #endif\n\n #endif\n\n vec4 col=vec4(v,v2,v3,1.0);\n\n float str=1.0;\n #ifdef HAS_TEX_MASK\n str=texture(texMask,texCoord).r;\n #endif\n\n col=cgl_blendPixel(base,col,amount*str);\n\n\n #ifdef NO_CHANNEL_R\n col.r=base.r;\n #endif\n #ifdef NO_CHANNEL_G\n col.g=base.g;\n #endif\n #ifdef NO_CHANNEL_B\n col.b=base.b;\n #endif\n\n\n\n outColor=col;\n}\n",}; +const + render = op.inTrigger("render"), + inTexMask = op.inTexture("Mask"), + blendMode = CGL.TextureEffect.AddBlendSelect(op), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + amount = op.inValueSlider("Amount", 1), + inMode = op.inSwitch("Color", ["Mono", "RGB", "R", "G", "B"], "Mono"), + scale = op.inValue("Scale", 8), + rangeMul = op.inValue("Multiply", 1), + inHarmonics = op.inSwitch("Harmonics", ["1", "2", "3", "4", "5"], "1"), + x = op.inValue("X", 0), + y = op.inValue("Y", 0), + z = op.inValue("Z", 0), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "perlinnoise"); + +op.setPortGroup("Position", [x, y, z]); + +shader.setSource(shader.getDefaultVertexShader(), attachments.perlinnoise3d_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureUniformOffZ = new CGL.Uniform(shader, "t", "texOffsetZ", 1), + textureUniformMask = new CGL.Uniform(shader, "t", "texMask", 2), + + uniZ = new CGL.Uniform(shader, "f", "z", z), + uniX = new CGL.Uniform(shader, "f", "x", x), + uniY = new CGL.Uniform(shader, "f", "y", y), + uniScale = new CGL.Uniform(shader, "f", "scale", scale), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + rangeMulUniform = new CGL.Uniform(shader, "f", "rangeMul", rangeMul); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +// offsetMap + +const + inTexOffsetZ = op.inTexture("Offset"), + inOffsetMul = op.inFloat("Offset Multiply", 1), + offsetX = op.inSwitch("Offset X", ["None", "R", "G", "B"], "None"), + offsetY = op.inSwitch("Offset Y", ["None", "R", "G", "B"], "None"), + offsetZ = op.inSwitch("Offset Z", ["None", "R", "G", "B"], "R"); + +op.setPortGroup("Offset Map", [inTexOffsetZ, offsetZ, offsetY, offsetX, inOffsetMul]); + +const uniOffMul = new CGL.Uniform(shader, "f", "offMul", inOffsetMul); + +const uniAspect = new CGL.Uniform(shader, "f", "aspect", 1); +const uniHarmonics = new CGL.Uniform(shader, "f", "harmonics", 0); + +inHarmonics.onChange = () => +{ + uniHarmonics.setValue(parseFloat(inHarmonics.get())); + shader.toggleDefine("HARMONICS", inHarmonics.get() > 1); +}; + +offsetX.onChange = +offsetY.onChange = +offsetZ.onChange = +inTexMask.onChange = +inMode.onChange = +inTexOffsetZ.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("NO_CHANNEL_R", inMode.get() == "G" || inMode.get() == "B"); + shader.toggleDefine("NO_CHANNEL_G", inMode.get() == "R" || inMode.get() == "B"); + shader.toggleDefine("NO_CHANNEL_B", inMode.get() == "R" || inMode.get() == "G"); + + shader.toggleDefine("HAS_TEX_OFFSETMAP", inTexOffsetZ.get()); + shader.toggleDefine("HAS_TEX_MASK", inTexMask.get()); + + shader.toggleDefine("OFFSET_X_R", offsetX.get() == "R"); + shader.toggleDefine("OFFSET_X_G", offsetX.get() == "G"); + shader.toggleDefine("OFFSET_X_B", offsetX.get() == "B"); + + shader.toggleDefine("OFFSET_Y_R", offsetY.get() == "R"); + shader.toggleDefine("OFFSET_Y_G", offsetY.get() == "G"); + shader.toggleDefine("OFFSET_Y_B", offsetY.get() == "B"); + + shader.toggleDefine("OFFSET_Z_R", offsetZ.get() == "R"); + shader.toggleDefine("OFFSET_Z_G", offsetZ.get() == "G"); + shader.toggleDefine("OFFSET_Z_B", offsetZ.get() == "B"); + + offsetX.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + offsetY.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + offsetZ.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + inOffsetMul.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + + shader.toggleDefine("RGB", inMode.get() == "RGB"); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + uniAspect.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (inTexOffsetZ.get()) cgl.setTexture(1, inTexOffsetZ.get().tex); + if (inTexMask.get()) cgl.setTexture(2, inTexMask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.PerlinNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["b4b238d3-db68-4206-8dc7-4b52433fc932"]={f:Ops.Gl.TextureEffects.Noise.PerlinNoise_v2,objName:"Ops.Gl.TextureEffects.Noise.PerlinNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.PixelNoise_v3 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.PixelNoise_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"pixelnoise2_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float numX;\nUNI float numY;\nUNI float addX;\nUNI float addY;\nUNI float addZ;\nUNI float seed2;\nUNI float minIn;\nUNI float maxIn;\n\nfloat random(vec2 co)\n{\n float r=fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (5.711+(mod(addZ+5711.210,57111.0))));\n\n #ifdef LOOP\n r=abs(r-0.5)*2.0;\n #endif\n\n return r;\n}\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec2 seed=vec2(0.0);\n\n #ifndef CENTER\n seed=vec2(floor( texCoord.x*numX+addX),floor( texCoord.y*numY+addY));\n #endif\n #ifdef CENTER\n seed=vec2(floor( (texCoord.x-0.5)*numX+addX),floor( (texCoord.y-0.5)*numY+addY));\n #endif\n\n float r,g,b;\n\n #ifndef RGB\n r=g=b=random( seed + 0.5711 + seed2 );\n #endif\n\n #ifdef RGB\n r=random( seed+0.5711 + seed2);\n g=random( seed+0.5712 + seed2);\n b=random( seed+0.5713 + seed2);\n #endif\n\n vec4 rnd = clamp( vec4( r,g,b,1.0 ),vec4(minIn), vec4(maxIn) );\n\n vec4 base=texture(tex,texCoord);\n\n outColor=cgl_blendPixel(base,rnd,amount);\n\n}",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + amount = op.inValueSlider("Amount", 1), + inLoop = op.inValueBool("Loop", false), + inRGB = op.inValueBool("RGB", false), + minValue = op.inValue("Minimum value", 0), + maxValue = op.inValue("Maximum value", 1), + numX = op.inValue("Num X", 10), + numY = op.inValue("Num Y", 10), + addX = op.inValue("X", 0), + addY = op.inValue("Y", 0), + addZ = op.inValue("Z", 0), + seed2 = op.inValue("Seed", 0), + inCentered = op.inBool("Centered", false), + trigger = op.outTrigger("Next"); + +op.setPortGroup("Look", [inRGB, inLoop, minValue, maxValue]); +op.setPortGroup("Position", [addX, addY, addZ]); +op.setPortGroup("Scaling", [numX, numY]); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); +shader.setSource(shader.getDefaultVertexShader(), attachments.pixelnoise2_frag); + +const + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + timeUniform = new CGL.Uniform(shader, "f", "time", 1.0), + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uni_numX = new CGL.Uniform(shader, "f", "numX", numX), + uni_numY = new CGL.Uniform(shader, "f", "numY", numY), + uni_addX = new CGL.Uniform(shader, "f", "addX", addX), + uni_addY = new CGL.Uniform(shader, "f", "addY", addY), + uni_addZ = new CGL.Uniform(shader, "f", "addZ", addZ), + uni_seed = new CGL.Uniform(shader, "f", "seed2", seed2), + uni_minValue = new CGL.Uniform(shader, "f", "minIn", minValue), + uni_maxValue = new CGL.Uniform(shader, "f", "maxIn", maxValue); + +inLoop.onChange = function () +{ + if (inLoop.get())shader.define("LOOP"); + else shader.removeDefine("LOOP"); +}; + +inRGB.onChange = function () +{ + if (inRGB.get())shader.define("RGB"); + else shader.removeDefine("RGB"); +}; + +inCentered.onChange = function () +{ + shader.toggleDefine("CENTER", inCentered.get()); +}; + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.PixelNoise_v3.prototype = new CABLES.Op(); +CABLES.OPS["01ad08b6-dea4-4765-984c-3885c9c6520f"]={f:Ops.Gl.TextureEffects.Noise.PixelNoise_v3,objName:"Ops.Gl.TextureEffects.Noise.PixelNoise_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.PolkaDotNoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.PolkaDotNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"polkadotnoise_frag":"#ifdef HAS_TEXTURES\n IN vec2 texCoord;\n UNI sampler2D tex;\n#endif\n\nUNI float amount;\nUNI float radius_low;\nUNI float radius_high;\nUNI float X,Y,Z;\nUNI float scale;\nUNI float threshhold;\nUNI float aspect;\n\n{{CGL.BLENDMODES3}}\n\nfloat random(vec2 co)\n{\n return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (437511.5453));\n}\n\n\n//\n//\tFalloff defined in XSquared\n//\t( smoothly decrease from 1.0 to 0.0 as xsq increases from 0.0 to 1.0 )\n//\thttp://briansharpe.wordpress.com/2011/11/14/two-useful-interpolation-functions-for-noise-development/\n//\nfloat Falloff_Xsq_C1( float xsq ) { xsq = 1.0 - xsq; return xsq*xsq; }\t// ( 1.0 - x*x )^2 ( Used by Humus for lighting falloff in Just Cause 2. GPUPro 1 )\nfloat Falloff_Xsq_C2( float xsq ) { xsq = 1.0 - xsq; return xsq*xsq*xsq; }\t// ( 1.0 - x*x )^3. NOTE: 2nd derivative is 0.0 at x=1.0, but non-zero at x=0.0\nvec4 Falloff_Xsq_C2( vec4 xsq ) { xsq = 1.0 - xsq; return xsq*xsq*xsq; }\n\n\nvec4 FAST32_hash_3D_Cell( vec3 gridcell )\t//\tgenerates 4 different random numbers for the single given cell point\n{\n // gridcell is assumed to be an integer coordinate\n\n //\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const vec4 SOMELARGEFLOATS = vec4( 635.298681, 682.357502, 668.926525, 588.255119 );\n const vec4 ZINC = vec4( 48.500388, 65.294118, 63.934599, 63.279683 );\n\n //\ttruncate the domain\n gridcell.xyz = gridcell - floor(gridcell * ( 1.0 / DOMAIN )) * DOMAIN;\n gridcell.xy += OFFSET.xy;\n gridcell.xy *= gridcell.xy;\n return fract( ( gridcell.x * gridcell.y ) * ( 1.0 / ( SOMELARGEFLOATS + gridcell.zzzz * ZINC ) ) );\n}\n\n\n//\tPolkaDot Noise 3D\n//\thttp://briansharpe.files.wordpress.com/2011/12/polkadotsample.jpg\n//\thttp://briansharpe.files.wordpress.com/2012/01/polkaboxsample.jpg\n//\tTODO, these images have random intensity and random radius. This noise now has intensity as proportion to radius. Images need updated. TODO\n//\n//\tGenerates a noise of smooth falloff polka dots.\n//\tAllow for control on radius. Intensity is proportional to radius\n//\tReturn value range of 0.0->1.0\n//\nfloat PolkaDot3D( \tvec3 P,\n float radius_low,\t\t//\tradius range is 0.0->1.0\n float radius_high\t)\n{\n //\testablish our grid cell and unit position\n vec3 Pi = floor(P);\n vec3 Pf = P - Pi;\n\n //\tcalculate the hash.\n vec4 hash = FAST32_hash_3D_Cell( Pi );\n\n hash.w=step(hash.w,threshhold);\n\n //\tuser variables\n float RADIUS = max( 0.0, radius_low + hash.w * ( radius_high - radius_low ) );\n float VALUE = RADIUS / max( radius_high, radius_low );\t//\tnew keep value in proportion to radius. Behaves better when used for bumpmapping, distortion and displacement\n\n //\tcalc the noise and return\n RADIUS = 2.0/RADIUS;\n Pf *= RADIUS;\n Pf -= ( RADIUS - 1.0 );\n Pf += hash.xyz * ( RADIUS - 2.0 );\n #ifdef BOX\n Pf *= Pf;\t\t//\tthis gives us a cool box looking effect\n #endif\n return Falloff_Xsq_C2( min( dot( Pf, Pf ), 1.0 ) ) * VALUE;\n}\n\n\n\nvoid main()\n{\n vec3 pos=vec3(texCoord.x,texCoord.y,0);\n\n pos.xy-=0.5;\n pos.y/=aspect;\n pos.xy*=scale;\n pos.xy-=vec2(X,Y);\n\n vec3 Pi = floor(pos);\n vec4 hash = FAST32_hash_3D_Cell( Pi );\n pos.z=Z+random(hash.zz);\n\n vec4 rnd=vec4(PolkaDot3D(pos,radius_low,radius_high));\n rnd.a=1.0;\n\n vec4 base=texture(tex,texCoord);\n\n outColor=cgl_blendPixel(base,rnd,amount);\n\n}",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + inBox = op.inValueBool("Square Look", false), + threshhold = op.inValueSlider("Threshold", 0.25), + radius_low = op.inValueSlider("Radius Low", 0), + radius_high = op.inValueSlider("Radius High", 1), + scale = op.inValue("Scale", 14), + X = op.inValue("X", 0), + Y = op.inValue("Y", 0), + Z = op.inValue("Z", 0), + trigger = op.outTrigger("Next"); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); + +let amountUniform = new CGL.Uniform(shader, "f", "amount", amount); +let timeUniform = new CGL.Uniform(shader, "f", "time", 1.0); + +shader.setSource(CGL.Shader.getDefaultVertexShader(), attachments.polkadotnoise_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +radius_low.uniform = new CGL.Uniform(shader, "f", "radius_low", radius_low); +radius_high.uniform = new CGL.Uniform(shader, "f", "radius_high", radius_high); +X.uniform = new CGL.Uniform(shader, "f", "X", X); +Y.uniform = new CGL.Uniform(shader, "f", "Y", Y); +Z.uniform = new CGL.Uniform(shader, "f", "Z", Z); +scale.uniform = new CGL.Uniform(shader, "f", "scale", scale); +const uniaspect = new CGL.Uniform(shader, "f", "aspect", 1); +const uniThreshhold = new CGL.Uniform(shader, "f", "threshhold", threshhold); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +inBox.onChange = function () +{ + if (inBox.get())shader.define("BOX"); + else shader.removeDefine("BOX"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + uniaspect.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.PolkaDotNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["2f128bcc-0846-4565-89ad-eda22e41ca4f"]={f:Ops.Gl.TextureEffects.Noise.PolkaDotNoise_v2,objName:"Ops.Gl.TextureEffects.Noise.PolkaDotNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.SimplexNoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.SimplexNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"simplexnoise_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float smoothness;\nUNI float scale;\nUNI float seed;\nUNI float x;\nUNI float y;\nUNI float time;\nUNI float aspect;\nUNI float harmonics;\n\n#ifdef HAS_TEX_OFFSETMAP\n UNI sampler2D texOffsetZ;\n UNI float offMul;\n#endif\n\n\n#ifdef HAS_TEX_MASK\n UNI sampler2D texMask;\n#endif\n\n\n\n{{CGL.BLENDMODES3}}\n\nvoid FAST32_hash_3D( vec3 gridcell,\n vec3 v1_mask, // user definable v1 and v2. ( 0s and 1s )\n vec3 v2_mask,\n out vec4 hash_0,\n out vec4 hash_1,\n out vec4 hash_2 ) // generates 3 random numbers for each of the 4 3D cell corners. cell corners: v0=0,0,0 v3=1,1,1 the other two are user definable\n{\n // gridcell is assumed to be an integer coordinate\n\n // TODO: these constants need tweaked to find the best possible noise.\n // probably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const vec3 SOMELARGEFLOATS = vec3( 635.298681, 682.357502, 668.926525 );\n const vec3 ZINC = vec3( 48.500388, 65.294118, 63.934599 );\n\n // truncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n // compute x*x*y*y for the 4 corners\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n vec4 V1xy_V2xy = mix( P.xyxy, P.zwzw, vec4( v1_mask.xy, v2_mask.xy ) ); // appl y mask for v1 and v2\n P = vec4( P.x, V1xy_V2xy.xz, P.z ) * vec4( P.y, V1xy_V2xy.yw, P.w );\n\n // get the lowz and highz mods\n vec3 lowz_mods = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) );\n vec3 highz_mods = vec3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) );\n\n // appl mask for v1 and v2 mod values\n v1_mask = ( v1_mask.z < 0.5 ) ? lowz_mods : highz_mods;\n v2_mask = ( v2_mask.z < 0.5 ) ? lowz_mods : highz_mods;\n\n // compute the final hash\n hash_0 = fract( P * vec4( lowz_mods.x, v1_mask.x, v2_mask.x, highz_mods.x ) );\n hash_1 = fract( P * vec4( lowz_mods.y, v1_mask.y, v2_mask.y, highz_mods.y ) );\n hash_2 = fract( P * vec4( lowz_mods.z, v1_mask.z, v2_mask.z, highz_mods.z ) );\n}\n\n\n//\n// Given an arbitrary 3D point this calculates the 4 vectors from the corners of the simplex pyramid to the point\n// It also returns the integer grid index information for the corners\n//\nvoid Simplex3D_GetCornerVectors( vec3 P, // input point\n out vec3 Pi, // integer grid index for the origin\n out vec3 Pi_1, // offsets for the 2nd and 3rd corners. ( the 4th = Pi + 1.0 )\n out vec3 Pi_2,\n out vec4 v1234_x, // vectors from the 4 corners to the intput point\n out vec4 v1234_y,\n out vec4 v1234_z )\n{\n //\n // Simplex math from Stefan Gustavsons and Ian McEwans work at...\n // http://github.com/ashima/webgl-noise\n //\n\n // simplex math constants\n const float SKEWFACTOR = 1.0/3.0;\n const float UNSKEWFACTOR = 1.0/6.0;\n const float SIMPLEX_CORNER_POS = 0.5;\n const float SIMPLEX_PYRAMID_HEIGHT = 0.70710678118654752440084436210485; // sqrt( 0.5 ) height of simplex pyramid.\n\n P *= SIMPLEX_PYRAMID_HEIGHT; // scale space so we can have an approx feature size of 1.0 ( optional )\n\n // Find the vectors to the corners of our simplex pyramid\n Pi = floor( P + dot( P, vec3( SKEWFACTOR) ) );\n vec3 x0 = P - Pi + dot(Pi, vec3( UNSKEWFACTOR ) );\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n Pi_1 = min( g.xyz, l.zxy );\n Pi_2 = max( g.xyz, l.zxy );\n vec3 x1 = x0 - Pi_1 + UNSKEWFACTOR;\n vec3 x2 = x0 - Pi_2 + SKEWFACTOR;\n vec3 x3 = x0 - SIMPLEX_CORNER_POS;\n\n // pack them into a parallel-friendly arrangement\n v1234_x = vec4( x0.x, x1.x, x2.x, x3.x );\n v1234_y = vec4( x0.y, x1.y, x2.y, x3.y );\n v1234_z = vec4( x0.z, x1.z, x2.z, x3.z );\n}\n\n//\n// Calculate the weights for the 3D simplex surflet\n//\nvec4 Simplex3D_GetSurfletWeights( vec4 v1234_x,\n vec4 v1234_y,\n vec4 v1234_z )\n{\n // perlins original implementation uses the surlet falloff formula of (0.6-x*x)^4.\n // This is buggy as it can cause discontinuities along simplex faces. (0.5-x*x)^3 solves this and gives an almost identical curve\n\n // evaluate surflet. f(x)=(0.5-x*x)^3\n vec4 surflet_weights = v1234_x * v1234_x + v1234_y * v1234_y + v1234_z * v1234_z;\n surflet_weights = max(0.5 - surflet_weights, 0.0); // 0.5 here represents the closest distance (squared) of any simplex pyramid corner to any of its planes. ie, SIMPLEX_PYRAMID_HEIGHT^2\n return surflet_weights*surflet_weights*surflet_weights;\n}\n\n\n\n//\n// SimplexPerlin3D ( simplex gradient noise )\n// Perlin noise over a simplex (tetrahedron) grid\n// Return value range of -1.0->1.0\n// http://briansharpe.files.wordpress.com/2012/01/simplexperlinsample.jpg\n//\n// Implementation originally based off Stefan Gustavsons and Ian McEwans work at...\n// http://github.com/ashima/webgl-noise\n//\nfloat SimplexPerlin3D(vec3 P)\n{\n // calculate the simplex vector and index math\n vec3 Pi;\n vec3 Pi_1;\n vec3 Pi_2;\n vec4 v1234_x;\n vec4 v1234_y;\n vec4 v1234_z;\n Simplex3D_GetCornerVectors( P, Pi, Pi_1, Pi_2, v1234_x, v1234_y, v1234_z );\n\n // generate the random vectors\n // ( various hashing methods listed in order of speed )\n vec4 hash_0;\n vec4 hash_1;\n vec4 hash_2;\n FAST32_hash_3D( Pi, Pi_1, Pi_2, hash_0, hash_1, hash_2 );\n //SGPP_hash_3D( Pi, Pi_1, Pi_2, hash_0, hash_1, hash_2 );\n hash_0 -= 0.49999;\n hash_1 -= 0.49999;\n hash_2 -= 0.49999;\n\n // evaluate gradients\n vec4 grad_results = inversesqrt( hash_0 * hash_0 + hash_1 * hash_1 + hash_2 * hash_2 ) * ( hash_0 * v1234_x + hash_1 * v1234_y + hash_2 * v1234_z );\n\n // Normalization factor to scale the final result to a strict 1.0->-1.0 range\n // x = sqrt( 0.75 ) * 0.5\n // NF = 1.0 / ( x * ( ( 0.5 * x*x ) ^ 3 ) * 2.0 )\n // http://briansharpe.wordpress.com/2012/01/13/simplex-noise/#comment-36\n float FINAL_NORMALIZATION = 37.837227241611314102871574478976*smoothness;\n\n // sum with the surflet and return\n return dot( Simplex3D_GetSurfletWeights( v1234_x, v1234_y, v1234_z ), grad_results ) * FINAL_NORMALIZATION;\n}\n\nvoid main()\n{\n vec2 p=vec2(texCoord.x-0.5,texCoord.y-0.5);\n\n p.x*=aspect;\n p=p*scale;\n\n p=vec2(p.x+0.5-x,p.y+0.5-y);\n\n vec3 offset;\n #ifdef HAS_TEX_OFFSETMAP\n vec4 offMap=texture(texOffsetZ,texCoord);\n\n #ifdef OFFSET_X_R\n offset.x=offMap.r;\n #endif\n #ifdef OFFSET_X_G\n offset.x=offMap.g;\n #endif\n #ifdef OFFSET_X_B\n offset.x=offMap.b;\n #endif\n\n #ifdef OFFSET_Y_R\n offset.y=offMap.r;\n #endif\n #ifdef OFFSET_Y_G\n offset.y=offMap.g;\n #endif\n #ifdef OFFSET_Y_B\n offset.y=offMap.b;\n #endif\n\n #ifdef OFFSET_Z_R\n offset.z=offMap.r;\n #endif\n #ifdef OFFSET_Z_G\n offset.z=offMap.g;\n #endif\n #ifdef OFFSET_Z_B\n offset.z=offMap.b;\n #endif\n\n offset*=offMul;\n #endif\n\n float v=SimplexPerlin3D(vec3(p.x,p.y,time)+offset)*0.5+0.5;\n\n\n if (harmonics >= 2.0) v += SimplexPerlin3D(vec3(p.x,p.y,time)*2.3+offset) * 0.5;\n if (harmonics >= 3.0) v += SimplexPerlin3D(vec3(p.x,p.y,time)*4.2+offset) * 0.25;\n if (harmonics >= 4.0) v += SimplexPerlin3D(vec3(p.x,p.y,time)*8.1+offset) * 0.125;\n if (harmonics >= 5.0) v += SimplexPerlin3D(vec3(p.x,p.y,time)*16.7+offset) * 0.0625;\n\n\n\n //blend section\n vec4 col=vec4(v,v,v,1.0);\n\n vec4 base=texture(tex,texCoord);\n\n // outColor=cgl_blend(base,col,amount);\n\n\n float str=1.0;\n #ifdef HAS_TEX_MASK\n str=texture(texMask,texCoord).r;\n #endif\n\n outColor=cgl_blendPixel(base,col,amount*str);\n\n}",}; +const + render = op.inTrigger("render"), + inTexMask = op.inTexture("Mask"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + smoothness = op.inValue("smoothness", 1.0), + inHarmonics = op.inSwitch("Harmonics", ["1", "2", "3", "4", "5"], "1"), + scale = op.inValue("scale", 7), + trigger = op.outTrigger("trigger"), + x = op.inValue("x"), + y = op.inValue("y"), + time = op.inValue("time", 0); + +const + inTexOffsetZ = op.inTexture("Offset"), + inOffsetMul = op.inFloat("Offset Multiply", 1), + offsetX = op.inSwitch("Offset X", ["None", "R", "G", "B"], "None"), + offsetY = op.inSwitch("Offset Y", ["None", "R", "G", "B"], "None"), + offsetZ = op.inSwitch("Offset Z", ["None", "R", "G", "B"], "R"); + +op.setPortGroup("Offset Map", [inTexOffsetZ, offsetZ, offsetY, offsetX, inOffsetMul]); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.simplexnoise_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureUniformOffZ = new CGL.Uniform(shader, "t", "texOffsetZ", 1), + uniTexMask = new CGL.Uniform(shader, "t", "texMask", 2), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniSmoothness = new CGL.Uniform(shader, "f", "smoothness", smoothness), + uniScale = new CGL.Uniform(shader, "f", "scale", scale), + uniX = new CGL.Uniform(shader, "f", "x", x), + uniY = new CGL.Uniform(shader, "f", "y", y), + uniAspect = new CGL.Uniform(shader, "f", "aspect", 1), + uniTime = new CGL.Uniform(shader, "f", "time", time), + uniOffMul = new CGL.Uniform(shader, "f", "offMul", inOffsetMul), + uniHarmonics = new CGL.Uniform(shader, "f", "harmonics", 0); + +inHarmonics.onChange = () => +{ + uniHarmonics.setValue(parseFloat(inHarmonics.get())); +}; + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount,maskAlpha); + +offsetX.onChange = +offsetY.onChange = +offsetZ.onChange = +inTexMask.onChange = +inTexOffsetZ.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("HAS_TEX_OFFSETMAP", inTexOffsetZ.get()); + shader.toggleDefine("HAS_TEX_MASK", inTexMask.get()); + + shader.toggleDefine("OFFSET_X_R", offsetX.get() == "R"); + shader.toggleDefine("OFFSET_X_G", offsetX.get() == "G"); + shader.toggleDefine("OFFSET_X_B", offsetX.get() == "B"); + + shader.toggleDefine("OFFSET_Y_R", offsetY.get() == "R"); + shader.toggleDefine("OFFSET_Y_G", offsetY.get() == "G"); + shader.toggleDefine("OFFSET_Y_B", offsetY.get() == "B"); + + shader.toggleDefine("OFFSET_Z_R", offsetZ.get() == "R"); + shader.toggleDefine("OFFSET_Z_G", offsetZ.get() == "G"); + shader.toggleDefine("OFFSET_Z_B", offsetZ.get() == "B"); + + offsetX.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + offsetY.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + offsetZ.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + inOffsetMul.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + uniAspect.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (inTexOffsetZ.get()) cgl.setTexture(1, inTexOffsetZ.get().tex); + if (inTexMask.get()) cgl.setTexture(2, inTexMask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.SimplexNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["0f4a8d04-89b0-437a-9da8-b4098772d250"]={f:Ops.Gl.TextureEffects.Noise.SimplexNoise_v2,objName:"Ops.Gl.TextureEffects.Noise.SimplexNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.TriangleNoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.TriangleNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"trianglenoise_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float scale;\nUNI float angle;\nUNI float ratio;\nUNI float add;\nUNI float amount;\n\n{{CGL.BLENDMODES3}}\n\nfloat rand(vec2 co)\n{\n return fract(sin(dot(co.xy ,vec2(13.98987,28.533))) * 4758.6453+add*0.1);\n}\n\nfloat GetLocation(vec2 s, float d)\n{\n vec2 f = s*d;\n f = f + vec2(0,0.5)*floor(f).x;\n s = fract(f);\n f = floor(f);\n\n d = s.y - 0.5;\n float l = abs(d) + 0.5 * s.x;\n float ff = f.x+f.y;\n f = mix(f, f+sign(d)*vec2(0,0.5), step(0.5, l));\n l = mix(ff, ff+sign(d)*0.5, step(0.5, l));\n\n float r=mod(rand(f)*2.0,2.0);\n if(r>1.0)r=2.0-r;\n\n return r;\n}\n\nvoid main()\n{\n vec2 coord=texCoord;\n coord-=0.5;\n coord.y/=ratio;\n coord*=scale;\n coord+=0.5;\n\n float sin_factor = sin(angle*0.01745329251);\n float cos_factor = cos(angle*0.01745329251);\n coord = vec2((coord.x - 0.5), coord.y - ratio/2.0) * mat2(cos_factor, sin_factor, -sin_factor, cos_factor);\n\n float a=GetLocation(coord,1.0);\n\n vec4 base=texture(tex,texCoord);\n vec4 col=vec4(a,a,a,1.0);\n\n outColor=cgl_blendPixel(base,col,amount);\n}",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + scale = op.inValue("scale", 10), + angle = op.inValue("angle", 0), + add = op.inValue("Add", 0), + trigger = op.outTrigger("Next"); + +const + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.trianglenoise_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + addUniform = new CGL.Uniform(shader, "f", "add", add), + scaleUniform = new CGL.Uniform(shader, "f", "scale", scale), + angleUniform = new CGL.Uniform(shader, "f", "angle", angle), + ratioUniform = new CGL.Uniform(shader, "f", "ratio", 1); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + ratioUniform.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.TriangleNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["9e634336-cb44-40ca-b06b-90063e4324a2"]={f:Ops.Gl.TextureEffects.Noise.TriangleNoise_v2,objName:"Ops.Gl.TextureEffects.Noise.TriangleNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.ValueNoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.ValueNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"valuenoise3d_frag":"UNI float z;\nUNI float x;\nUNI float y;\nUNI float scale;\nUNI float amount;\nIN vec2 texCoord;\nUNI sampler2D tex;\nUNI float aspect;\n\n{{CGL.BLENDMODES3}}\n\n//\n//\tValue Noise 3D\n//\tReturn value range of 0.0->1.0\n//\thttp://briansharpe.files.wordpress.com/2011/11/valuesample1.jpg\n//\n\nfloat Interpolation_C2( float x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } // 6x^5-15x^4+10x^3\t( Quintic Curve. As used by Perlin in Improved Noise. http://mrl.nyu.edu/~perlin/paper445.pdf )\nvec2 Interpolation_C2( vec2 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec3 Interpolation_C2( vec3 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2( vec4 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); }\nvec4 Interpolation_C2_InterpAndDeriv( vec2 x ) { return x.xyxy * x.xyxy * ( x.xyxy * ( x.xyxy * ( x.xyxy * vec2( 6.0, 0.0 ).xxyy + vec2( -15.0, 30.0 ).xxyy ) + vec2( 10.0, -60.0 ).xxyy ) + vec2( 0.0, 30.0 ).xxyy ); }\nvec3 Interpolation_C2_Deriv( vec3 x ) { return x * x * (x * (x * 30.0 - 60.0) + 30.0); }\n\nvoid FAST32_hash_3D( vec3 gridcell, out vec4 lowz_hash, out vec4 highz_hash )\t//\tgenerates a random number for each of the 8 cell corners\n{\n // gridcell is assumed to be an integer coordinate\n\n //\tTODO: \tthese constants need tweaked to find the best possible noise.\n //\t\t\tprobably requires some kind of brute force computational searching or something....\n const vec2 OFFSET = vec2( 50.0, 161.0 );\n const float DOMAIN = 69.0;\n const float SOMELARGEFLOAT = 635.298681;\n const float ZINC = 48.500388;\n\n //\ttruncate the domain\n gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN;\n vec3 gridcell_inc1 = step( gridcell, vec3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 );\n\n //\tcalculate the noise\n vec4 P = vec4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy;\n P *= P;\n P = P.xzxz * P.yyww;\n highz_hash.xy = vec2( 1.0 / ( SOMELARGEFLOAT + vec2( gridcell.z, gridcell_inc1.z ) * ZINC ) );\n lowz_hash = fract( P * highz_hash.xxxx );\n highz_hash = fract( P * highz_hash.yyyy );\n}\n\n\nfloat Value3D( vec3 P )\n{\n //\testablish our grid cell and unit position\n vec3 Pi = floor(P);\n vec3 Pf = P - Pi;\n\n //\tcalculate the hash.\n //\t( various hashing methods listed in order of speed )\n vec4 hash_lowz, hash_highz;\n FAST32_hash_3D( Pi, hash_lowz, hash_highz );\n //FAST32_2_hash_3D( Pi, hash_lowz, hash_highz );\n //BBS_hash_3D( Pi, hash_lowz, hash_highz );\n //SGPP_hash_3D( Pi, hash_lowz, hash_highz );\n\n //\tblend the results and return\n vec3 blend = Interpolation_C2( Pf );\n vec4 res0 = mix( hash_lowz, hash_highz, blend.z );\n vec4 blend2 = vec4( blend.xy, vec2( 1.0 - blend.xy ) );\n return dot( res0, blend2.zxzx * blend2.wwyy );\n}\n\nvoid main()\n{\n vec2 tc=texCoord;\n tc.y/=aspect;\n vec4 base=texture(tex,tc);\n\n vec2 p=vec2(tc.x-0.5,tc.y-0.5);\n p=p*scale;\n\n p=vec2(p.x+0.5-x,p.y+0.5-y);\n\n float v=Value3D(vec3(p.x,p.y,z));\n vec4 col=vec4(v,v,v,1.0);\n\n outColor=cgl_blendPixel(base,col,amount);\n\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + scale = op.inValue("Scale", 25), + x = op.inValue("X", 0), + y = op.inValue("Y", 0), + z = op.inValue("Z", 0), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Position", [x, y, z]); +op.setPortGroup("Look", [scale]); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(CGL.Shader.getDefaultVertexShader(), attachments.valuenoise3d_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniZ = new CGL.Uniform(shader, "f", "z", z), + uniX = new CGL.Uniform(shader, "f", "x", x), + uniY = new CGL.Uniform(shader, "f", "y", y), + uniScale = new CGL.Uniform(shader, "f", "scale", scale), + uniAspect = new CGL.Uniform(shader, "f", "aspect", 1), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + uniAspect.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.ValueNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["d2748c53-9ac1-4cd7-8176-66d3eb5d1bb6"]={f:Ops.Gl.TextureEffects.Noise.ValueNoise_v2,objName:"Ops.Gl.TextureEffects.Noise.ValueNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.Voronoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.Voronoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"voronoise_frag":"// adapted from https://thebookofshaders.com/12/\n\nIN vec2 texCoord;\n\nUNI bool drawIsoLines;\nUNI bool drawDistance;\nUNI int fill;\nUNI float seed;\n\nUNI float time;\nUNI float movement;\nUNI float amount;\nUNI float centerSize;\nUNI sampler2D tex;\nUNI float aspect;\n\n{{CGL.BLENDMODES3}}\n{{CGL.RANDOM_OLD}}\n\nfloat rand(float n){return fract(sin(n) * 43.5453123);}\nvec2 random2( vec2 p )\n{\n return vec2(rand(p.x),rand(p.x+p.y));\n}\n\nvoid main() {\n\n vec3 color = vec3(.0);\n float m_dist = 1.; // minimun distance\n vec2 m_point; // minimum position\n float indexColor=0.0;\n\n vec2 coord=texCoord;\n\n coord.y/=aspect;\n\n // const float NUM=21.0;\n\n // Iterate through the points positions\n for (float i = 0.0; i < NUM; i++)\n {\n vec2 pos= random2(vec2(i+seed,i+seed));\n\n pos.x+=sin(time+i)*movement;\n pos.y+=cos(time+i)*movement;\n\n if(i==0.0)\n {\n // pos=vec2(mouseX,mouseY);\n }\n\n float dist = distance(coord, pos);\n if( dist < m_dist )\n {\n // Keep the closer distance\n m_dist = dist;\n\n // Kepp the position of the closer point\n m_point = pos;\n indexColor=(i)/NUM;\n }\n }\n\n // tint acording the closest point position\n if(fill==1) color.rgb = vec3( indexColor );\n if(fill==2) color.rgb = vec3( m_point.x );\n if(fill==3) color.rgb = vec3( 0.5 );\n\n // Add distance field to closest point center\n if(drawDistance) color += m_dist*2.;\n\n // Show isolines\n if(drawIsoLines) color -= abs(sin(120.0*m_dist))*0.07;\n\n // Draw point center\n if(centerSize>0.0)\n color += 1.-step(centerSize/30.0, m_dist);\n\n vec4 base=texture(tex,coord);\n\n outColor= cgl_blendPixel(base,vec4(color,1.0),amount);\n}",}; +const + render = op.inTrigger("Render"), + trigger = op.outTrigger("Trigger"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + time = op.inValue("Time", 0), + movement = op.inValueSlider("Movement", 0), + num = op.inValue("Num", 20), + seed = op.inValue("seed", 0), + fill = op.inValueSelect("Fill", ["None", "Random", "Gradient", "Gray"], "Random"), + drawIsoLines = op.inValueBool("Draw Isolines", false), + drawDistance = op.inValueBool("Draw Distance", false), + centerSize = op.inValueSlider("Draw Center", 0); + +const cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.voronoise_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniPx = new CGL.Uniform(shader, "f", "pX", 1 / 1024), + uniPy = new CGL.Uniform(shader, "f", "pY", 1 / 1024), + uniFill = new CGL.Uniform(shader, "i", "fill", 1), + uniSeed = new CGL.Uniform(shader, "f", "seed", seed), + uniTime = new CGL.Uniform(shader, "f", "time", time), + uniMovement = new CGL.Uniform(shader, "f", "movement", movement), + uniAspect = new CGL.Uniform(shader, "f", "aspect", 1), + uniIsoLines = new CGL.Uniform(shader, "b", "drawIsoLines", drawIsoLines), + uniDrawDistance = new CGL.Uniform(shader, "b", "drawDistance", drawDistance), + uniCenterSize = new CGL.Uniform(shader, "f", "centerSize", centerSize); + +shader.define("NUM", 20.001); + +num.onChange = function () +{ + shader.define("NUM", num.get() + 0.001); +}; + +fill.onChange = function () +{ + if (fill.get() == "Random") uniFill.setValue(1); + else if (fill.get() == "Gradient") uniFill.setValue(2); + else if (fill.get() == "Gray") uniFill.setValue(3); + else uniFill.setValue(0); +}; + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + uniPx.setValue(1 / cgl.currentTextureEffect.getCurrentSourceTexture().width); + uniPy.setValue(1 / cgl.currentTextureEffect.getCurrentSourceTexture().height); + + uniAspect.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.Voronoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["0e68dbf4-dc3e-448d-b1e1-256dde99802a"]={f:Ops.Gl.TextureEffects.Noise.Voronoise_v2,objName:"Ops.Gl.TextureEffects.Noise.Voronoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.WorleyNoise_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.WorleyNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"worleynoise_frag":"// Author: Stefan Gustavson\n// Title: Worley noise 2x2x2\n\nIN vec2 texCoord;\n\nUNI float amount;\nUNI float x;\nUNI float y;\nUNI float z;\nUNI float scale;\nUNI sampler2D tex;\nUNI float rangeA;\nUNI float rangeB;\nUNI float aspect;\nUNI float harmonics;\n\n#ifdef HAS_MASK\n UNI sampler2D texMask;\n#endif\n\n#ifdef HAS_TEX_OFFSETMAP\n UNI sampler2D texOffsetZ;\n UNI float offMul;\n#endif\n\n{{CGL.BLENDMODES3}}\n{{CGL.LUMINANCE}}\n\n// Cellular noise (\"Worley noise\") in 3D in GLSL.\n// Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved.\n// This code is released under the conditions of the MIT license.\n// See LICENSE file for details.\n\n// Permutation polynomial: (34x^2 + x) mod 289\nvec4 permute(vec4 x) {\n return mod((34.0 * x + 1.0) * x, 289.0);\n}\nvec3 permute(vec3 x) {\n return mod((34.0 * x + 1.0) * x, 289.0);\n}\n\n// Cellular noise, returning F1 and F2 in a vec2.\n// Speeded up by using 2x2x2 search window instead of 3x3x3,\n// at the expense of some pattern artifacts.\n// F2 is often wrong and has sharp discontinuities.\n// If you need a good F2, use the slower 3x3x3 version.\nvec2 cellular2x2x2(vec3 P) {\n\t#define K 0.142857142857 // 1/7\n\t#define Ko 0.428571428571 // 1/2-K/2\n\t#define K2 0.020408163265306 // 1/(7*7)\n\t#define Kz 0.166666666667 // 1/6\n\t#define Kzo 0.416666666667 // 1/2-1/6*2\n\t#define jitter 0.8 // smaller jitter gives less errors in F2\n\tvec3 Pi = mod(floor(P), 289.0);\n \tvec3 Pf = fract(P);\n\tvec4 Pfx = Pf.x + vec4(0.0, -1.0, 0.0, -1.0);\n\tvec4 Pfy = Pf.y + vec4(0.0, 0.0, -1.0, -1.0);\n\tvec4 p = permute(Pi.x + vec4(0.0, 1.0, 0.0, 1.0));\n\tp = permute(p + Pi.y + vec4(0.0, 0.0, 1.0, 1.0));\n\tvec4 p1 = permute(p + Pi.z); // z+0\n\tvec4 p2 = permute(p + Pi.z + vec4(1.0)); // z+1\n\tvec4 ox1 = fract(p1*K) - Ko;\n\tvec4 oy1 = mod(floor(p1*K), 7.0)*K - Ko;\n\tvec4 oz1 = floor(p1*K2)*Kz - Kzo; // p1 < 289 guaranteed\n\tvec4 ox2 = fract(p2*K) - Ko;\n\tvec4 oy2 = mod(floor(p2*K), 7.0)*K - Ko;\n\tvec4 oz2 = floor(p2*K2)*Kz - Kzo;\n\tvec4 dx1 = Pfx + jitter*ox1;\n\tvec4 dy1 = Pfy + jitter*oy1;\n\tvec4 dz1 = Pf.z + jitter*oz1;\n\tvec4 dx2 = Pfx + jitter*ox2;\n\tvec4 dy2 = Pfy + jitter*oy2;\n\tvec4 dz2 = Pf.z - 1.0 + jitter*oz2;\n\tvec4 d1 = dx1 * dx1 + dy1 * dy1 + dz1 * dz1; // z+0\n\tvec4 d2 = dx2 * dx2 + dy2 * dy2 + dz2 * dz2; // z+1\n\n\t// Sort out the two smallest distances (F1, F2)\n#if 0\n\t// Cheat and sort out only F1\n\td1 = min(d1, d2);\n\td1.xy = min(d1.xy, d1.wz);\n\td1.x = min(d1.x, d1.y);\n\treturn sqrt(d1.xx);\n#else\n\t// Do it right and sort out both F1 and F2\n\tvec4 d = min(d1,d2); // F1 is now in d\n\td2 = max(d1,d2); // Make sure we keep all candidates for F2\n\td.xy = (d.x < d.y) ? d.xy : d.yx; // Swap smallest to d.x\n\td.xz = (d.x < d.z) ? d.xz : d.zx;\n\td.xw = (d.x < d.w) ? d.xw : d.wx; // F1 is now in d.x\n\td.yzw = min(d.yzw, d2.yzw); // F2 now not in d2.yzw\n\td.y = min(d.y, d.z); // nor in d.z\n\td.y = min(d.y, d.w); // nor in d.w\n\td.y = min(d.y, d2.x); // F2 is now in d.y\n\treturn sqrt(d.xy); // F1 and F2\n#endif\n}\n\nvoid main(void) {\n\tvec2 st = texCoord;//gl_FragCoord.xy/u_resolution.xy;\n\n\t#ifdef DO_TILEABLE\n\t st=abs(texCoord-0.5);\n\t#endif\n\n vec3 offset;\n #ifdef HAS_TEX_OFFSETMAP\n vec4 offMap=texture(texOffsetZ,texCoord);\n\n #ifdef OFFSET_X_R\n offset.x=offMap.r;\n #endif\n #ifdef OFFSET_X_G\n offset.x=offMap.g;\n #endif\n #ifdef OFFSET_X_B\n offset.x=offMap.b;\n #endif\n\n #ifdef OFFSET_Y_R\n offset.y=offMap.r;\n #endif\n #ifdef OFFSET_Y_G\n offset.y=offMap.g;\n #endif\n #ifdef OFFSET_Y_B\n offset.y=offMap.b;\n #endif\n\n #ifdef OFFSET_Z_R\n offset.z=offMap.r;\n #endif\n #ifdef OFFSET_Z_G\n offset.z=offMap.g;\n #endif\n #ifdef OFFSET_Z_B\n offset.z=offMap.b;\n #endif\n offset*=offMul;\n #endif\n\n st.x-=0.5;\n st.y-=0.5;\n\tst *= scale;\n st.x+=0.5;\n st.y+=0.5;\n\n st.y/=aspect;\n\n\tst.x+=x;\n\tst.y+=y;\n\n\tvec2 F = cellular2x2x2(vec3(st,z)+offset);\n\n if (harmonics >= 2.0) F.x += cellular2x2x2(vec3(st,z)*2.2+offset).x * 0.5;\n if (harmonics >= 3.0) F.x += cellular2x2x2(vec3(st,z)*4.3+offset).x * 0.25;\n if (harmonics >= 4.0) F.x += cellular2x2x2(vec3(st,z)*8.4+offset).x * 0.125;\n if (harmonics >= 5.0) F.x += cellular2x2x2(vec3(st,z)*16.5+offset).x * 0.0625;\n\n\tfloat n = smoothstep(rangeA,rangeB, F.x);\n\n #ifdef DO_INVERT\n n=1.0-n;\n #endif\n\n vec4 col=vec4(n,n,n,1.0);\n vec4 base=texture(tex,texCoord);\n\n float am=amount;\n #ifdef HAS_MASK\n #ifdef MASK_SRC_R\n float mul=texture(texMask,texCoord).r;\n #endif\n #ifdef MASK_SRC_G\n float mul=texture(texMask,texCoord).g;\n #endif\n #ifdef MASK_SRC_B\n float mul=texture(texMask,texCoord).b;\n #endif\n #ifdef MASK_SRC_A\n float mul=texture(texMask,texCoord).a;\n #endif\n #ifdef MASK_SRC_LUM\n float mul=cgl_luminance(texture(texMask,texCoord).rgb);\n #endif\n #ifdef MASK_INV\n mul=1.0-mul;\n #endif\n am*=mul;\n #endif\n\n outColor=cgl_blendPixel(base,col,am);\n\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + amount = op.inValueSlider("Amount", 1), + x = op.inValue("X", 0), + y = op.inValue("Y", 0), + z = op.inValue("Z", 0), + scale = op.inValue("Scale", 6), + inHarmonics = op.inSwitch("Harmonics", ["1", "2", "3", "4", "5"], "1"), + inv = op.inValueBool("Invert", true), + rangeA = op.inValueSlider("RangeA", 0.4), + rangeB = op.inValueSlider("RangeB", 0.5), + tile = op.inValueBool("Tileable", false), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.worleynoise_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureUniformOffZ = new CGL.Uniform(shader, "t", "texOffsetZ", 1), + textureUniformMask = new CGL.Uniform(shader, "t", "texMask", 2), + uniZ = new CGL.Uniform(shader, "f", "z", z), + uniX = new CGL.Uniform(shader, "f", "x", x), + uniY = new CGL.Uniform(shader, "f", "y", y), + uniScale = new CGL.Uniform(shader, "f", "scale", scale), + uniAspect = new CGL.Uniform(shader, "f", "aspect", 1), + uniharmonics = new CGL.Uniform(shader, "f", "harmonics", inHarmonics), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + rangeAUniform = new CGL.Uniform(shader, "f", "rangeA", rangeA), + rangeBUniform = new CGL.Uniform(shader, "f", "rangeB", rangeB); + +// amount Map + +const + inMaskTex = op.inTexture("Amount Map"), + inMaskSource = op.inSwitch("Source Strength Map", ["R", "G", "B", "A", "Lum"], "R"), + inMaskInv = op.inBool("Invert Strength Map", false); + +inMaskSource.setUiAttribs({ "title": "Source Amount Map" }); +inMaskInv.setUiAttribs({ "title": "Invert Amount Map" }); + +op.setPortGroup("Amount Map", [inMaskTex, inMaskSource, inMaskInv]); + +// offsetMap + +const + inTexOffsetZ = op.inTexture("Offset"), + inOffsetMul = op.inFloat("Offset Multiply", 1), + offsetX = op.inSwitch("Offset X", ["None", "R", "G", "B"], "None"), + offsetY = op.inSwitch("Offset Y", ["None", "R", "G", "B"], "None"), + offsetZ = op.inSwitch("Offset Z", ["None", "R", "G", "B"], "R"); + +op.setPortGroup("Offset Map", [inTexOffsetZ, offsetZ, offsetY, offsetX, inOffsetMul]); + +const uniOffMul = new CGL.Uniform(shader, "f", "offMul", inOffsetMul); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +inMaskTex.onChange = + inMaskSource.onChange = + inMaskInv.onChange = + inv.onChange = + offsetX.onChange = + offsetY.onChange = + offsetZ.onChange = + inMaskTex.onLinkChanged = + inTexOffsetZ.onLinkChanged = + tile.onChange = updateDefines; + +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("DO_INVERT", inv.get()); + shader.toggleDefine("DO_TILEABLE", tile.get()); + + shader.toggleDefine("HAS_TEX_OFFSETMAP", inTexOffsetZ.isLinked()); + shader.toggleDefine("HAS_TEX_MASK", inMaskTex.isLinked()); + + shader.toggleDefine("OFFSET_X_R", offsetX.get() == "R"); + shader.toggleDefine("OFFSET_X_G", offsetX.get() == "G"); + shader.toggleDefine("OFFSET_X_B", offsetX.get() == "B"); + + shader.toggleDefine("OFFSET_Y_R", offsetY.get() == "R"); + shader.toggleDefine("OFFSET_Y_G", offsetY.get() == "G"); + shader.toggleDefine("OFFSET_Y_B", offsetY.get() == "B"); + + shader.toggleDefine("OFFSET_Z_R", offsetZ.get() == "R"); + shader.toggleDefine("OFFSET_Z_G", offsetZ.get() == "G"); + shader.toggleDefine("OFFSET_Z_B", offsetZ.get() == "B"); + + offsetX.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + offsetY.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + offsetZ.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + inOffsetMul.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + + shader.toggleDefine("HAS_MASK", inMaskTex.isLinked()); + shader.toggleDefine("MASK_SRC_R", inMaskSource.get() == "R"); + shader.toggleDefine("MASK_SRC_G", inMaskSource.get() == "G"); + shader.toggleDefine("MASK_SRC_B", inMaskSource.get() == "B"); + shader.toggleDefine("MASK_SRC_A", inMaskSource.get() == "A"); + shader.toggleDefine("MASK_SRC_LUM", inMaskSource.get() == "Lum"); + shader.toggleDefine("MASK_INV", inMaskInv.get()); + inMaskSource.setUiAttribs({ "greyout": !inMaskTex.isLinked() }); + inMaskInv.setUiAttribs({ "greyout": !inMaskTex.isLinked() }); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (inTexOffsetZ.get()) cgl.setTexture(1, inTexOffsetZ.get().tex); + if (inMaskTex.get()) cgl.setTexture(2, inMaskTex.get().tex); + + uniAspect.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.WorleyNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["fc6fbe7c-4bf8-4fdc-bd37-95b9f50a5bf2"]={f:Ops.Gl.TextureEffects.Noise.WorleyNoise_v2,objName:"Ops.Gl.TextureEffects.Noise.WorleyNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.PatternLookup +// +// ************************************************************** + +Ops.Gl.TextureEffects.PatternLookup = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"patternlookup_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D patternTex;\nUNI float amount;\nUNI float patternWidth;\nUNI float patternHeight;\n\n{{CGL.BLENDMODES}}\n\nfloat desaturate(vec3 color)\n{\n vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), color));\n return gray.g;\n}\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n vec4 base=texture(tex,texCoord);\n\n vec2 nc=vec2(0.5,desaturate(base.rgb));\n nc.x=mod(texCoord.x,patternWidth*0.1);\n nc.y=nc.y+mod(texCoord.y,patternHeight*0.1);\n\n // maybe should have a ping pong modulo option...\n\n nc.x=clamp(nc.x,0.0,1.0);\n nc.y=clamp(nc.y,0.0,1.0);\n\n vec4 newCol=texture(patternTex,nc);\n\n outColor=cgl_blend(base,newCol,amount);\n}",}; +const render = op.inTrigger("render"); +const multiplierTex = op.inTexture("Multiplier"); +const blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"); +const amount = op.inValueSlider("Amount", 1); +const trigger = op.outTrigger("trigger"); + +const patternWidth = op.inValueSlider("Width", 0.1); +const patternHeight = op.inValueSlider("Height", 0.1); + + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +op.toWorkPortsNeedToBeLinked(multiplierTex); + + +shader.setSource(shader.getDefaultVertexShader(), attachments.patternlookup_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const textureMultiplierUniform = new CGL.Uniform(shader, "t", "patternTex", 1); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); +const ptrnWidthUniform = new CGL.Uniform(shader, "f", "patternWidth", patternWidth); +const patternHeightUniform = new CGL.Uniform(shader, "f", "patternHeight", patternHeight); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + let multex = multiplierTex.get(); + if (multex) + { + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (multex) cgl.setTexture(1, multex.tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + } + + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.PatternLookup.prototype = new CABLES.Op(); +CABLES.OPS["f05f45fe-f3d2-4d99-b1b7-dfae7005382a"]={f:Ops.Gl.TextureEffects.PatternLookup,objName:"Ops.Gl.TextureEffects.PatternLookup"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.PixelColor +// +// ************************************************************** + +Ops.Gl.TextureEffects.PixelColor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"pixelate_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D srcTex;\nUNI float amount;\n\nUNI vec2 pixelPos;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n\n vec4 base=texture(tex,texCoord);\n vec4 col=texture(srcTex,pixelPos);\n\n outColor=cgl_blendPixel(base,col,amount);\n outColor=col;\n}",}; +const render = op.inTrigger("render"), + srcTex = op.inTexture("Source Texture"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + posX = op.inFloatSlider("Pos X", 0), + posY = op.inFloatSlider("Pos Y", 0), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.pixelate_frag); + +const textureMultiplierUniform = new CGL.Uniform(shader, "t", "srcTex", 1); +const unipos = new CGL.Uniform(shader, "2f", "pixelPos", posX, posY); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +// srcTex.onChange = function () +// { +// shader.toggleDefine("PI XELATE_TEXTURE", srcTex.isLinked()); +// }; + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (srcTex.get()) cgl.setTexture(1, srcTex.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.PixelColor.prototype = new CABLES.Op(); +CABLES.OPS["114e250c-f569-4963-a3b3-fcdce816406f"]={f:Ops.Gl.TextureEffects.PixelColor,objName:"Ops.Gl.TextureEffects.PixelColor"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.PixelDifference +// +// ************************************************************** + +Ops.Gl.TextureEffects.PixelDifference = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"slope_frag":"\nIN vec2 texCoord;\nUNI sampler2D tex;\nUNI vec2 pixel;\nUNI float strength;\nUNI float sstep;\n\nfloat lum(vec3 c)\n{\n return dot(vec3(0.2126,0.7152,0.0722),c);\n}\n\nvoid main()\n{\n vec4 a,b;\n // vec4 px=texture(tex, texCoord);\n vec4 pxl=texture(tex, texCoord + vec2(-sstep*pixel.x, 0.0));\n vec4 pxr=texture(tex, texCoord + vec2( sstep*pixel.x, 0.0));\n vec4 pxt=texture(tex, texCoord + vec2(0.0, -sstep*pixel.y));\n vec4 pxb=texture(tex, texCoord + vec2(0.0, sstep*pixel.y));\n\n vec4 col=vec4(0.5);\n\n #ifdef DIR_R_HORIZONTAL\n a=pxl;\n b=pxr;\n #endif\n #ifndef DIR_R_HORIZONTAL // VERTICAL\n a=pxt;\n b=pxb;\n #endif\n #ifdef FLIP_R\n vec4 c=a;\n a=b;\n b=c;\n #endif\n #ifdef METH_R_DIFF\n #ifdef METH_R_R\n col.r+=(b.r-a.r)*strength;\n #endif\n #ifdef METH_R_G\n col.r+=(b.g-a.g)*strength;\n #endif\n #ifdef METH_R_B\n col.r+=(b.b-a.b)*strength;\n #endif\n #endif\n #ifdef METH_R_AVG\n #ifdef METH_R_R\n col.r+=(((b.r+a.r-0.5)/2.0))*strength;\n #endif\n #ifdef METH_R_G\n col.r+=(((b.g+a.g-0.5)/2.0))*strength;\n #endif\n #ifdef METH_R_B\n col.r+=(((b.b+a.b-0.5)/2.0))*strength;\n #endif\n #endif\n #ifdef METH_R_LUMI\n col.r+=(lum(b.rgb)-lum(a.rgb))*strength;\n #endif\n #ifdef METH_R_ONE\n col.r=1.0;\n #endif\n #ifdef METH_R_ZERO\n col.r=0.0;\n #endif\n\n\n ////////////////////////\n\n #ifdef DIR_G_HORIZONTAL\n a=pxl;\n b=pxr;\n #endif\n #ifndef DIR_G_HORIZONTAL // VERTICAL\n a=pxt;\n b=pxb;\n #endif\n #ifdef FLIP_G\n vec4 c3=a;\n a=b;\n b=c3;\n #endif\n #ifdef METH_G_DIFF\n #ifdef METH_G_R\n col.g+=(b.r-a.r)*strength;\n #endif\n #ifdef METH_G_G\n col.g+=(b.g-a.g)*strength;\n #endif\n #ifdef METH_G_B\n col.g+=(b.b-a.b)*strength;\n #endif\n #endif\n #ifdef METH_G_AVG\n #ifdef METH_G_R\n col.g+=(((b.r+a.r-0.5)/2.0))*strength;\n #endif\n #ifdef METH_G_G\n col.g+=(((b.g+a.g-0.5)/2.0))*strength;\n #endif\n #ifdef METH_G_B\n col.g+=(((b.b+a.b-0.5)/2.0))*strength;\n #endif\n #endif\n #ifdef METH_G_LUMI\n col.g+=(lum(b.rgb)-lum(a.rgb))*strength;\n #endif\n #ifdef METH_G_ONE\n col.g=1.0;\n #endif\n #ifdef METH_G_ZERO\n col.g=0.0;\n #endif\n\n\n ////////////////////////\n\n #ifdef DIR_B_HORIZONTAL\n a=pxl;\n b=pxr;\n #endif\n #ifndef DIR_B_HORIZONTAL // VERTICAL\n a=pxt;\n b=pxb;\n #endif\n #ifdef FLIP_B\n vec4 c2=a;\n a=b;\n b=c2;\n #endif\n #ifdef METH_B_DIFF\n #ifdef METH_B_R\n col.b+=(b.r-a.r)*strength;\n #endif\n #ifdef METH_B_G\n col.b+=(b.g-a.g)*strength;\n #endif\n #ifdef METH_B_B\n col.b+=(b.b-a.b)*strength;\n #endif\n #endif\n #ifdef METH_B_AVG\n #ifdef METH_B_R\n col.b+=(((b.r+a.r-0.5)/2.0))*strength;\n #endif\n #ifdef METH_B_G\n col.b+=(((b.g+a.g-0.5)/2.0))*strength;\n #endif\n #ifdef METH_B_B\n col.b+=(((b.b+a.b-0.5)/2.0))*strength;\n #endif\n #endif\n #ifdef METH_B_LUMI\n col.b+=(lum(b.rgb)-lum(a.rgb))*strength;\n #endif\n #ifdef METH_B_ONE\n col.b=1.0;\n #endif\n #ifdef METH_B_ZERO\n col.b=0.0;\n #endif\n\n\n\n col.a=1.0;\n\n outColor= col;\n\n}\n",}; +const options = [ + "Vertical Difference Red", "Vertical Difference Green", "Vertical Difference Blue", + "Vertical Average Red", "Vertical Average Green", "Vertical Average Blue", + "Vertical Luminance", + "Horizontal Difference Red", "Horizontal Difference Green", "Horizontal Difference Blue", + "Horizontal Average Red", "Horizontal Average Green", "Horizontal Average Blue", + "Horizontal Luminance", + "Midpoint", "Zero", "One" +]; + +const + render = op.inTrigger("render"), + strength = op.inValue("Strength", 4), + step = op.inValue("Step", 1), + rMeth = op.inDropDown("Red", options, "Horizontal Difference Red"), + rFlip = op.inBool("Red Flip", false), + gMeth = op.inDropDown("Green", options, "Vertical Difference Green"), + gFlip = op.inBool("Green Flip", false), + bMeth = op.inDropDown("Blue", options, "Midpoint"), + bFlip = op.inBool("Blue Flip", false), + trigger = op.outTrigger("trigger"); +op.setPortGroup("Method", [rFlip, gFlip, bFlip, rMeth, gMeth, bMeth]); + +rFlip.onChange = +gFlip.onChange = +bFlip.onChange = +rMeth.onChange = + gMeth.onChange = + bMeth.onChange = updateDefines; + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.slope_frag); +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniStrength = new CGL.Uniform(shader, "f", "strength", strength), + uniStep = new CGL.Uniform(shader, "f", "sstep", step), + uniRes = new CGL.Uniform(shader, "2f", "pixel"); + +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("DIR_R_HORIZONTAL", (rMeth.get() + "").indexOf("Horizontal") != -1); + shader.toggleDefine("DIR_G_HORIZONTAL", (gMeth.get() + "").indexOf("Horizontal") != -1); + shader.toggleDefine("DIR_B_HORIZONTAL", (bMeth.get() + "").indexOf("Horizontal") != -1); + + shader.toggleDefine("METH_R_DIFF", (rMeth.get() + "").indexOf("Difference") != -1); + shader.toggleDefine("METH_G_DIFF", (gMeth.get() + "").indexOf("Difference") != -1); + shader.toggleDefine("METH_B_DIFF", (bMeth.get() + "").indexOf("Difference") != -1); + + shader.toggleDefine("METH_R_AVG", (rMeth.get() + "").indexOf("Average") != -1); + shader.toggleDefine("METH_G_AVG", (gMeth.get() + "").indexOf("Average") != -1); + shader.toggleDefine("METH_B_AVG", (bMeth.get() + "").indexOf("Average") != -1); + + shader.toggleDefine("METH_R_R", (rMeth.get() + "").indexOf("Red") != -1); + shader.toggleDefine("METH_G_R", (gMeth.get() + "").indexOf("Red") != -1); + shader.toggleDefine("METH_B_R", (bMeth.get() + "").indexOf("Red") != -1); + + shader.toggleDefine("METH_R_G", (rMeth.get() + "").indexOf("Green") != -1); + shader.toggleDefine("METH_G_G", (gMeth.get() + "").indexOf("Green") != -1); + shader.toggleDefine("METH_B_G", (bMeth.get() + "").indexOf("Green") != -1); + + shader.toggleDefine("METH_R_B", (rMeth.get() + "").indexOf("Blue") != -1); + shader.toggleDefine("METH_G_B", (gMeth.get() + "").indexOf("Blue") != -1); + shader.toggleDefine("METH_B_B", (bMeth.get() + "").indexOf("Blue") != -1); + + shader.toggleDefine("METH_R_LUMI", (rMeth.get() + "").indexOf("Luminance") != -1); + shader.toggleDefine("METH_G_LUMI", (gMeth.get() + "").indexOf("Luminance") != -1); + shader.toggleDefine("METH_B_LUMI", (bMeth.get() + "").indexOf("Luminance") != -1); + + shader.toggleDefine("METH_R_MID", (rMeth.get() + "").indexOf("Midpoint") != -1); + shader.toggleDefine("METH_G_MID", (gMeth.get() + "").indexOf("Midpoint") != -1); + shader.toggleDefine("METH_B_MID", (bMeth.get() + "").indexOf("Midpoint") != -1); + + shader.toggleDefine("METH_R_ZERO", (rMeth.get() + "").indexOf("Zero") != -1); + shader.toggleDefine("METH_G_ZERO", (gMeth.get() + "").indexOf("Zero") != -1); + shader.toggleDefine("METH_B_ZERO", (bMeth.get() + "").indexOf("Zero") != -1); + + shader.toggleDefine("METH_R_ONE", (rMeth.get() + "").indexOf("One") != -1); + shader.toggleDefine("METH_G_ONE", (gMeth.get() + "").indexOf("One") != -1); + shader.toggleDefine("METH_B_ONE", (bMeth.get() + "").indexOf("One") != -1); + + shader.toggleDefine("FLIP_R", rFlip.get()); + shader.toggleDefine("FLIP_G", gFlip.get()); + shader.toggleDefine("FLIP_B", bFlip.get()); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + const tex = cgl.currentTextureEffect.getCurrentSourceTexture(); + uniRes.set([1 / tex.width, 1 / tex.height]); + + cgl.setTexture(0, tex.tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.PixelDifference.prototype = new CABLES.Op(); +CABLES.OPS["f4e53fd9-d4c3-4d0e-839a-78543c38b8db"]={f:Ops.Gl.TextureEffects.PixelDifference,objName:"Ops.Gl.TextureEffects.PixelDifference"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.PixelDisplacement_v4 +// +// ************************************************************** + +Ops.Gl.TextureEffects.PixelDisplacement_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"pixeldisplace3_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D displaceTex;\nUNI float amountX;\nUNI float amountY;\nUNI float amount;\n\n{{CGL.BLENDMODES3}}\n\nvec3 getOffset(vec3 offset)\n{\n #ifdef ZERO_BLACK\n return offset;\n #endif\n\n #ifdef ZERO_GREY\n return offset*2.0-1.0;\n #endif\n}\n\nfloat getOffset(float offset)\n{\n #ifdef ZERO_BLACK\n return offset;\n #endif\n\n #ifdef ZERO_GREY\n return offset*2.0-1.0;\n #endif\n}\n\nvoid main()\n{\n vec4 rgba=texture(displaceTex,texCoord);\n vec3 offset=rgba.rgb*rgba.a;\n float x,y;\n\n #ifdef INPUT_REDGREEN\n offset=getOffset(offset);\n x=offset.r*amountX+texCoord.x;\n y=offset.g*amountY+texCoord.y;\n #endif\n #ifdef INPUT_RED\n offset=getOffset(offset);\n x=offset.r*amountX+texCoord.x;\n y=offset.r*amountY+texCoord.y;\n #endif\n #ifdef INPUT_GREEN\n offset=getOffset(offset);\n x=offset.g*amountX+texCoord.x;\n y=offset.g*amountY+texCoord.y;\n #endif\n #ifdef INPUT_BLUE\n offset=getOffset(offset);\n x=offset.b*amountX+texCoord.x;\n y=offset.b*amountY+texCoord.y;\n #endif\n #ifdef INPUT_LUMINANCE\n float o=dot(vec3(0.2126,0.7152,0.0722), offset);\n o=getOffset(o);\n x=o*amountX+texCoord.x;\n y=o*amountY+texCoord.y;\n #endif\n #ifdef WRAP_CLAMP\n x=clamp(x,0.0,1.0);\n y=clamp(y,0.0,1.0);\n #endif\n #ifdef WRAP_REPEAT\n x=mod(x,1.0);\n y=mod(y,1.0);\n #endif\n #ifdef WRAP_MIRROR\n float mx=mod(x,2.0);\n float my=mod(y,2.0);\n x=abs((floor(mx)-fract(mx)));\n y=abs((floor(my)-fract(my)));\n #endif\n\n\n\n vec4 col=texture(tex,vec2(x,y));\n vec4 base=texture(tex,texCoord);\n\n base.a=0.0;\n\n outColor=cgl_blendPixel(base,col,amount);\n}\n",}; +const + render = op.inTrigger("render"), + displaceTex = op.inTexture("displaceTex"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + amountX = op.inValueSlider("amount X", 0.2), + amountY = op.inValueSlider("amount Y", 0.2), + inWrap = op.inSwitch("Wrap", ["Mirror", "Clamp", "Repeat"], "Mirror"), + inInput = op.inValueSelect("Input", ["Luminance", "RedGreen", "Red", "Green", "Blue"], "Luminance"), + inZero = op.inSwitch("Zero Displace", ["Grey", "Black"], "Grey"), + // displaceTex=op.inTexture("displaceTex"), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Axis Displacement Strength", [amountX, amountY]); +op.setPortGroup("Modes", [inWrap, inInput]); +op.toWorkPortsNeedToBeLinked(displaceTex); + +const + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.pixeldisplace3_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureDisplaceUniform = new CGL.Uniform(shader, "t", "displaceTex", 1), + amountXUniform = new CGL.Uniform(shader, "f", "amountX", amountX), + amountYUniform = new CGL.Uniform(shader, "f", "amountY", amountY), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +inZero.onChange = updateZero; +inWrap.onChange = updateWrap; +inInput.onChange = updateInput; + +updateWrap(); +updateInput(); +updateZero(); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +function updateZero() +{ + shader.removeDefine("ZERO_BLACK"); + shader.removeDefine("ZERO_GREY"); + shader.define("ZERO_" + (inZero.get() + "").toUpperCase()); +} + +function updateWrap() +{ + shader.removeDefine("WRAP_CLAMP"); + shader.removeDefine("WRAP_REPEAT"); + shader.removeDefine("WRAP_MIRROR"); + shader.define("WRAP_" + (inWrap.get() + "").toUpperCase()); +} + +function updateInput() +{ + shader.removeDefine("INPUT_LUMINANCE"); + shader.removeDefine("INPUT_REDGREEN"); + shader.removeDefine("INPUT_RED"); + shader.define("INPUT_" + (inInput.get() + "").toUpperCase()); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + if(displaceTex.get()) + { + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (displaceTex.get()) cgl.setTexture(1, displaceTex.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + } + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.PixelDisplacement_v4.prototype = new CABLES.Op(); +CABLES.OPS["c00f79f2-0505-4b4f-b0bf-10ef7875dd87"]={f:Ops.Gl.TextureEffects.PixelDisplacement_v4,objName:"Ops.Gl.TextureEffects.PixelDisplacement_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Pixelate_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Pixelate_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"pixelate_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D multiplierTex;\nUNI float amount;\nUNI float amountX;\nUNI float amountY;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n\n float x=1.0/amountX;\n float y=1.0/amountY;\n\n #ifdef PIXELATE_TEXTURE\n x += texture(multiplierTex,texCoord).r*0.1;//*0.1\n y += texture(multiplierTex,texCoord).r*0.1;//*0.1\n #endif\n\n vec2 coord = vec2(x*floor(texCoord.x/x), y*floor(texCoord.y/y));\n\n col=texture(tex,coord);\n vec4 base=texture(tex,texCoord);\n\n outColor=cgl_blendPixel(base,col,amount);\n}",}; +const render = op.inTrigger("render"), + multiplierTex = op.inTexture("Multiplier"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + amountX = op.inValue("width", 100), + amountY = op.inValue("height", 100), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.pixelate_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const textureMultiplierUniform = new CGL.Uniform(shader, "t", "multiplierTex", 1); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); +const amountXUniform = new CGL.Uniform(shader, "f", "amountX", amountX); +const amountYUniform = new CGL.Uniform(shader, "f", "amountY", amountY); + +multiplierTex.onChange = function () +{ + shader.toggleDefine("PIXELATE_TEXTURE", multiplierTex.isLinked()); +}; + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (multiplierTex.get()) cgl.setTexture(1, multiplierTex.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Pixelate_v2.prototype = new CABLES.Op(); +CABLES.OPS["c06ae168-ff4f-4ae6-b4d0-06c3b24203bc"]={f:Ops.Gl.TextureEffects.Pixelate_v2,objName:"Ops.Gl.TextureEffects.Pixelate_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Plasma_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Plasma_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"plasma_frag":"#define PI 3.1415926535897932384626433832795\n\nIN vec2 texCoord;\n\nUNI sampler2D tex;\nUNI vec2 size;\nUNI vec2 pos;\nUNI float mul;\nUNI float amount;\nUNI float time;\nUNI float aspect;\n\n#ifdef HAS_TEX_OFFSETMAP\n UNI sampler2D texOffsetZ;\n UNI float offMul;\n#endif\n\n#ifdef HAS_TEX_MASK\n UNI sampler2D texMask;\n#endif\n\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n float v = 0.0;\n\n vec2 s=size;\n #ifdef FIXASPECT\n s.y=size.x/aspect;\n #endif\n\n vec2 c = texCoord * s - s/2.0;\n\n c+=pos;\n\n vec3 offset;\n #ifdef HAS_TEX_OFFSETMAP\n vec4 offMap=texture(texOffsetZ,texCoord);\n\n #ifdef OFFSET_X_R\n offset.x=offMap.r;\n #endif\n #ifdef OFFSET_X_G\n offset.x=offMap.g;\n #endif\n #ifdef OFFSET_X_B\n offset.x=offMap.b;\n #endif\n\n #ifdef OFFSET_Y_R\n offset.y=offMap.r;\n #endif\n #ifdef OFFSET_Y_G\n offset.y=offMap.g;\n #endif\n #ifdef OFFSET_Y_B\n offset.y=offMap.b;\n #endif\n\n #ifdef OFFSET_Z_R\n offset.z=offMap.r;\n #endif\n #ifdef OFFSET_Z_G\n offset.z=offMap.g;\n #endif\n #ifdef OFFSET_Z_B\n offset.z=offMap.b;\n #endif\n offset*=offMul;\n #endif\n\n c+=offset.xy;\n float t=time+offset.z;\n\n v += sin((c.x+t));\n v += sin((c.y+t)/2.0);\n v += sin((c.x+c.y+t)/2.0);\n c += size/2.0 * vec2(sin(t/3.0), cos(t/2.0));\n\n v += sin(sqrt(c.x*c.x+c.y*c.y+1.0)+t);\n v = v/2.0;\n\n vec3 newColor = vec3(sin(PI*v*mul/4.0), sin(PI*v*mul), cos(PI*v*mul))*.5 + .5;\n vec4 base=texture(tex,texCoord);\n\n #ifndef GREY\n vec4 col=vec4( _blend(base.rgb,newColor) ,1.0);\n #endif\n #ifdef GREY\n vec4 col=vec4( _blend(base.rgb,vec3(newColor.g)) ,1.0);\n #endif\n\n float str=1.0;\n #ifdef HAS_TEX_MASK\n str=texture(texMask,texCoord).r;\n #endif\n\n outColor=cgl_blendPixel(base,col,amount*str);\n}\n\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + amount = op.inValueSlider("Amount", 1), + w = op.inValue("Width", 20), + h = op.inValue("Height", 20), + inAspect = op.inBool("Aspect", true), + mul = op.inValue("Mul", 1), + x = op.inValue("X", 0), + y = op.inValue("Y", 0), + time = op.inValue("Time", 1), + greyscale = op.inValueBool("Greyscale", true), + + inTexOffsetZ = op.inTexture("Offset"), + inOffsetMul = op.inFloat("Offset Multiply", 1), + offsetX = op.inSwitch("Offset X", ["None", "R", "G", "B"], "None"), + offsetY = op.inSwitch("Offset Y", ["None", "R", "G", "B"], "None"), + offsetZ = op.inSwitch("Offset Time", ["None", "R", "G", "B"], "R"), + + inTexMask = op.inTexture("Mask"), + + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Offset Map", [inTexOffsetZ, offsetZ, offsetY, offsetX, inOffsetMul]); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.plasma_frag); +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +const + uniPos = new CGL.Uniform(shader, "2f", "pos", x, y), + uniSize = new CGL.Uniform(shader, "2f", "size", w, h), + uniTime = new CGL.Uniform(shader, "f", "time", time), + uniMul = new CGL.Uniform(shader, "f", "mul", mul), + uniAspect = new CGL.Uniform(shader, "f", "aspect", 1), + uniOffMul = new CGL.Uniform(shader, "f", "offMul", inOffsetMul), + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureUniformOffZ = new CGL.Uniform(shader, "t", "texOffsetZ", 1), + textureUniformMask = new CGL.Uniform(shader, "t", "texMask", 2), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +offsetX.onChange = + inAspect.onChange = + offsetY.onChange = + offsetZ.onChange = + inTexMask.onChange = + greyscale.onChange = + inTexOffsetZ.onLinkChanged = updateDefines; + +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("GREY", greyscale.get()); + + shader.toggleDefine("HAS_TEX_OFFSETMAP", inTexOffsetZ.isLinked()); + shader.toggleDefine("HAS_TEX_MASK", inTexMask.isLinked()); + + shader.toggleDefine("OFFSET_X_R", offsetX.get() == "R"); + shader.toggleDefine("OFFSET_X_G", offsetX.get() == "G"); + shader.toggleDefine("OFFSET_X_B", offsetX.get() == "B"); + + shader.toggleDefine("OFFSET_Y_R", offsetY.get() == "R"); + shader.toggleDefine("OFFSET_Y_G", offsetY.get() == "G"); + shader.toggleDefine("OFFSET_Y_B", offsetY.get() == "B"); + + shader.toggleDefine("OFFSET_Z_R", offsetZ.get() == "R"); + shader.toggleDefine("OFFSET_Z_G", offsetZ.get() == "G"); + shader.toggleDefine("OFFSET_Z_B", offsetZ.get() == "B"); + + offsetX.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + offsetY.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + offsetZ.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + inOffsetMul.setUiAttribs({ "greyout": !inTexOffsetZ.isLinked() }); + + h.setUiAttribs({ "greyout": inAspect.get() }); + shader.toggleDefine("FIXASPECT", inAspect.get()); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + if (inAspect.get()) uniAspect.setValue(cgl.currentTextureEffect.aspectRatio); + else uniAspect.setValue(1); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (inTexOffsetZ.get()) cgl.setTexture(1, inTexOffsetZ.get().tex); + if (inTexMask.get()) cgl.setTexture(2, inTexMask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Plasma_v2.prototype = new CABLES.Op(); +CABLES.OPS["89b3bf34-a9a8-4dcf-a304-9dee597fcf48"]={f:Ops.Gl.TextureEffects.Plasma_v2,objName:"Ops.Gl.TextureEffects.Plasma_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.PolarCoords +// +// ************************************************************** + +Ops.Gl.TextureEffects.PolarCoords = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"polarcoords_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float inner;\nUNI float outer;\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n vec2 x = texCoord - vec2(0.5);\n float radius = length(x);\n float angle = atan(x.y, x.x);\n\n vec2 tc;\n tc.s = ( radius - inner) / (outer - inner);\n tc.t = angle * 0.5 / 3.141592653589793 + 0.5;\n\n #ifdef CROP_IMAGE\n if(tc.s<0.0 || tc.t<0.0 || tc.s>1.0 || tc.t>1.0) discard;\n #endif\n\n col=texture(tex,tc);\n outColor= col;\n}\n",}; +const + render = op.inTrigger("render"), + inner = op.inValueSlider("Radius Inner", 0), + outer = op.inValueSlider("Radius Outer", 1), + crop = op.inValueBool("Crop", false), + trigger = op.outTrigger("trigger"), + cgl = op.patch.cgl; + +const shader = new CGL.Shader(cgl, op.name); +shader.setSource(shader.getDefaultVertexShader(), attachments.polarcoords_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniinner = new CGL.Uniform(shader, "f", "inner", inner), + uniouter = new CGL.Uniform(shader, "f", "outer", outer); + +crop.onChange = updateCrop; + +function updateCrop() +{ + if (crop.get()) shader.define("CROP_IMAGE"); + else shader.removeDefine("CROP_IMAGE"); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.PolarCoords.prototype = new CABLES.Op(); +CABLES.OPS["1555fefe-4eb4-4aed-84c0-ad54e0dae1c1"]={f:Ops.Gl.TextureEffects.PolarCoords,objName:"Ops.Gl.TextureEffects.PolarCoords"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Posterize_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Posterize_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"posterize_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\nUNI float levels;\nUNI float amount;\n\n{{CGL.BLENDMODES3}}\n\nvoid main(void)\n{\n vec3 srcPixel = texture(tex, texCoord ).rgb;\n vec3 amountPerLevel = vec3(1.0/levels);\n vec3 numOfLevels = floor(srcPixel/amountPerLevel);\n vec3 col = numOfLevels * (vec3(1.0) / (vec3(levels) - vec3(1.0)));\n\n vec4 base=texture(tex,texCoord);\n outColor= cgl_blendPixel(base,vec4(col,base.a),amount);\n}\n\n",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + levels = op.inValue("levels", 2), + trigger = op.outTrigger("Trigger"); + +const + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.posterize_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + levelsUniform = new CGL.Uniform(shader, "f", "levels", levels), + uniWidth = new CGL.Uniform(shader, "f", "texWidth", 128), + uniHeight = new CGL.Uniform(shader, "f", "texHeight", 128), + uniAmount = new CGL.Uniform(shader, "f", "amount", amount); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + uniWidth.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().width); + uniHeight.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().height); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Posterize_v2.prototype = new CABLES.Op(); +CABLES.OPS["19703953-7984-4334-af72-0991425b4850"]={f:Ops.Gl.TextureEffects.Posterize_v2,objName:"Ops.Gl.TextureEffects.Posterize_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.PseudoLensFlares +// +// ************************************************************** + +Ops.Gl.TextureEffects.PseudoLensFlares = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"lensflares_frag":"UNI sampler2D tex;\n// UNI sampler2D texInput;\nUNI float haloWidth;\nUNI int numGhosts;\nUNI float dispersal;\nUNI float amountGhosts;\nUNI float amountHalo;\nUNI sampler2D texLookup;\n\nIN vec2 texCoord;\n\nvec3 lumi(vec3 c)\n{\n return vec3(sqrt(c.r*c.r*0.241+c.g*c.g*0.691+c.b*c.b*0.068));\n}\n\nvec4 myTexture(sampler2D tex,vec2 coords)\n{\n vec4 c=texture(tex, coords);\n c.rgb=lumi(c.rgb);\n return c;\n}\n\nvoid main()\n{\n vec2 texcoord = -texCoord + vec2(1.0);\n // vec2 texelSize = 1.0 / vec2(textureSize(texInput, 0));\n vec2 ghostVec = (vec2(0.5) - texcoord) * (0.5*dispersal);\n vec4 result = vec4(0.0,0.0,0.0,1.0);//texture(tex,texCoord);\n\n\n\n // ghosts\n for (int i = 0; i < numGhosts; ++i)\n {\n vec2 offset = fract(texcoord + ghostVec * float(i));\n float weightA = length(vec2(0.5) - offset) / length(vec2(0.5));\n weightA = pow(1. - weightA, 10.0);\n result += myTexture(tex, offset)*weightA*amountGhosts;\n }\n\n // halo\n vec2 haloVec = normalize(ghostVec) * haloWidth;\n float weight = length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5));\n\n weight = pow(1.0 - weight, 5.0);\n result += myTexture(tex, texcoord + haloVec) * weight * amountHalo;\n\n #ifdef TEX_LOOPUP\n result *= texture(texLookup, vec2(length(vec2(0.5) - texcoord) / length(vec2(0.5)),0.5));\n #endif\n\n outColor=result;\n}\n\n",}; +// http://john-chapman-graphics.blogspot.com/2013/02/pseudo-lens-flare.html + +const render = op.inTrigger("render"), + inAmountGhosts = op.inValueSlider("Ghosts", 1.0), + inNumGhosts = op.inValueInt("Num Ghosts", 3), + inDispersal = op.inValueSlider("Dispersal", 0.5), + inAmountHalo = op.inValueSlider("Halo", 1.0), + inHaloWidth = op.inValueSlider("Halo Width", 0.5), + textureLookup = op.inTexture("Color Lookup"), + trigger = op.outTrigger("trigger"); + +const + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.lensflares_frag || ""); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureLookupUni = new CGL.Uniform(shader, "t", "texLookup", 1), + uniHaloWidth = new CGL.Uniform(shader, "f", "haloWidth", inHaloWidth), + uniNumGhosts = new CGL.Uniform(shader, "i", "numGhosts", inNumGhosts), + uniDispersal = new CGL.Uniform(shader, "f", "dispersal", inDispersal), + uniAmountGhosts = new CGL.Uniform(shader, "f", "amountGhosts", inAmountGhosts), + uniAmounthalo = new CGL.Uniform(shader, "f", "amountHalo", inAmountHalo); + +textureLookup.onChange = function () +{ + if (textureLookup.get())shader.define("TEX_LOOPUP"); + else shader.removeDefine("TEX_LOOPUP"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + let texture = cgl.currentTextureEffect.getCurrentSourceTexture(); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, texture.tex); + if (textureLookup.get()) cgl.setTexture(1, textureLookup.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.PseudoLensFlares.prototype = new CABLES.Op(); +CABLES.OPS["9e4a5694-ed59-4401-9f7b-123a627924c5"]={f:Ops.Gl.TextureEffects.PseudoLensFlares,objName:"Ops.Gl.TextureEffects.PseudoLensFlares"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.RGBOffset_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.RGBOffset_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"offsetrgb_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float offsetRedX,offsetRedY,offsetGreenX,offsetGreenY,offsetBlueX,offsetBlueY;\nUNI float redAmount,greenAmount,blueAmount;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec3 rgb = vec3(texture(tex,texCoord+vec2(offsetRedX,offsetRedY)).r,\n texture(tex,texCoord+vec2(offsetGreenX,offsetGreenY)).g,\n texture(tex,texCoord+vec2(offsetBlueX,offsetBlueY)).b);\n\n vec4 base = texture(tex,texCoord);\n vec4 col = vec4(rgb*vec3(redAmount,greenAmount,blueAmount),base.a);\n\n outColor=cgl_blendPixel(base,col,amount);\n}\n",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + offsetRedX = op.inFloat("Red offset X", 0.05), + offsetRedY = op.inFloat("Red offset Y", 0.1), + redAmount = op.inFloat("Red amount", 1.0), + offsetGreenX = op.inFloat("Green offset X", 0.0), + offsetGreenY = op.inFloat("Green offset Y", 0.0), + greenAmount = op.inFloat("Green amount", 1.0), + offsetBlueX = op.inFloat("Blue offset X", 0), + offsetBlueY = op.inFloat("Blue offset Y", 0), + blueAmount = op.inFloat("Blue amount", 1.0), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Red", [offsetRedX, offsetRedY, redAmount]); +op.setPortGroup("Green", [offsetGreenX, offsetGreenY, greenAmount]); +op.setPortGroup("Blue", [offsetBlueX, offsetBlueY, blueAmount]); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "RGB offset"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.offsetrgb_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + offsetRedUniX = new CGL.Uniform(shader, "f", "offsetRedX", offsetRedX), + offsetRedUniY = new CGL.Uniform(shader, "f", "offsetRedY", offsetRedY), + redUniAmount = new CGL.Uniform(shader, "f", "redAmount", redAmount), + + offsetGreenUniX = new CGL.Uniform(shader, "f", "offsetGreenX", offsetGreenX), + offsetGreenUniY = new CGL.Uniform(shader, "f", "offsetGreenY", offsetGreenY), + greenUniAmount = new CGL.Uniform(shader, "f", "greenAmount", greenAmount), + + offsetBlueUniX = new CGL.Uniform(shader, "f", "offsetBlueX", offsetBlueX), + offsetBlueUniY = new CGL.Uniform(shader, "f", "offsetBlueY", offsetBlueY), + blueUniAmount = new CGL.Uniform(shader, "f", "blueAmount", blueAmount); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.RGBOffset_v2.prototype = new CABLES.Op(); +CABLES.OPS["41a9655a-6e05-47ee-82b9-f19a33a5cfd4"]={f:Ops.Gl.TextureEffects.RGBOffset_v2,objName:"Ops.Gl.TextureEffects.RGBOffset_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.RectangleTexture_v4 +// +// ************************************************************** + +Ops.Gl.TextureEffects.RectangleTexture_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rectangle_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\n\nUNI float width;\nUNI float height;\nUNI float x;\nUNI float y;\n\nUNI float r;\nUNI float g;\nUNI float b;\nUNI float a;\n\nUNI float aspect;\nUNI float amount;\nUNI float rotate;\nUNI float roundness;\n\n#define DEG2RAD 0.785398163397\n\n{{CGL.BLENDMODES3}}\n\nmat2 rot(float angle)\n{\n float s=sin(angle);\n float c=cos(angle);\n\n return mat2(c,-s,s,c);\n}\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n vec4 col=vec4(r,g,b,a);\n vec2 p=texCoord;\n\n // p.y*=aspect;\n float d=1.0;\n\n vec2 pos=vec2(x,y);\n pos=pos/2.0+0.5;\n\n\n vec2 pp=p-pos;\n #ifndef CENTER\n pp-=vec2(width,height*aspect);\n #endif\n\n pp=pp*rot(rotate*DEG2RAD/45.0);\n\n float roundn=roundness*min(width,height);\n\n vec2 size=max(vec2(width,height*aspect)-roundn,0.0);\n vec2 absPos=abs(pp)-size;\n\n d=max(absPos.x,absPos.y);\n d=min(d,length(max(absPos,0.0))-roundn);\n d=step(0.0,d);\n\n\n // col=vec4( _blend(base.rgb,vec3(r,g,b)) ,1.0);\n // col=vec4( mix( col.rgb, base.rgb ,1.0-base.a*a*(1.0-d)*amount),1.0);\n // outColor=col;\n\n outColor=cgl_blendPixel(base,col,amount*(1.0-d));\n // outColor=vec4(1.0,1.0,1.0,1.0-d);\n\n\n}\n\n\n\n",}; +const render = op.inTrigger("render"), + amount = op.inValueSlider("Amount", 1), + blendMode = CGL.TextureEffect.AddBlendSelect(op), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + inCenterMode = op.inBool("Center", true), + inWidth = op.inValueSlider("Width", 0.25), + inHeight = op.inValueSlider("Height", 0.25), + inPosX = op.inValueSlider("X", 0), + inPosY = op.inValueSlider("Y", 0), + inRot = op.inValue("Rotate", 0), + inRoundness = op.inValueSlider("roundness", 0), + r = op.inValueSlider("r", 1.0), + g = op.inValueSlider("g", 1.0), + b = op.inValueSlider("b", 1.0), + a = op.inValueSlider("a", 1.0), + trigger = op.outTrigger("trigger"); + +r.setUiAttribs({ "colorPick": true }); + +op.setPortGroup("Size", [inWidth, inHeight]); +op.setPortGroup("Position", [inPosX, inPosY]); +op.setPortGroup("Color", [r, g, b, a]); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, "textureeffect rectangle"); +shader.setSource(shader.getDefaultVertexShader(), attachments.rectangle_frag || ""); + +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniHeight = new CGL.Uniform(shader, "f", "height", inHeight), + unWidth = new CGL.Uniform(shader, "f", "width", inWidth), + uniX = new CGL.Uniform(shader, "f", "x", inPosX), + uniY = new CGL.Uniform(shader, "f", "y", inPosY), + uniRot = new CGL.Uniform(shader, "f", "rotate", inRot), + uniRoundness = new CGL.Uniform(shader, "f", "roundness", inRoundness), + uniformR = new CGL.Uniform(shader, "f", "r", r), + uniformG = new CGL.Uniform(shader, "f", "g", g), + uniformB = new CGL.Uniform(shader, "f", "b", b), + uniformA = new CGL.Uniform(shader, "f", "a", a), + uniformAmount = new CGL.Uniform(shader, "f", "amount", amount), + uniformAspect = new CGL.Uniform(shader, "f", "aspect", 1); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +inCenterMode.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("CENTER", inCenterMode.get()); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + const texture = cgl.currentTextureEffect.getCurrentSourceTexture(); + uniformAspect.set(cgl.currentTextureEffect.aspectRatio); + + cgl.setTexture(0, texture.tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.RectangleTexture_v4.prototype = new CABLES.Op(); +CABLES.OPS["268ad86e-1f6d-4099-b5c0-e1e0150adbfb"]={f:Ops.Gl.TextureEffects.RectangleTexture_v4,objName:"Ops.Gl.TextureEffects.RectangleTexture_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.RemoveAlpha +// +// ************************************************************** + +Ops.Gl.TextureEffects.RemoveAlpha = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const render = op.inTrigger("render"); +const trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +const srcFrag = "" + + .endl() + "#ifdef HAS_TEXTURES" + .endl() + " IN vec2 texCoord;" + .endl() + " uniform sampler2D tex;" + .endl() + "#endif" + .endl() + "" + .endl() + "" + .endl() + "void main()" + .endl() + "{" + .endl() + " vec4 col=vec4(1.0,0.0,0.0,1.0);" + .endl() + " #ifdef HAS_TEXTURES" + .endl() + " col=texture2D(tex,texCoord);" + .endl() + " col.a=1.0;" + .endl() + " #endif" + .endl() + " outColor= col;" + .endl() + "}"; + +shader.setSource(shader.getDefaultVertexShader(), srcFrag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.RemoveAlpha.prototype = new CABLES.Op(); +CABLES.OPS["98875f07-5dfa-4dfa-921c-de317cdc357e"]={f:Ops.Gl.TextureEffects.RemoveAlpha,objName:"Ops.Gl.TextureEffects.RemoveAlpha"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.RepeatTexture_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.RepeatTexture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"repeat_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D mulTex;\nUNI float amount;\nUNI float amountX;\nUNI float amountY;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n float am=amount;\n\n float mul=1.0;\n\n #ifdef HAS_MASK\n mul=texture(mulTex,texCoord).r;\n #endif\n\n vec2 coord = vec2(\n mod(texCoord.x*amountX*mul,1.0),\n mod(texCoord.y*amountY*mul,1.0));\n\n vec4 col=texture(tex,coord);\n vec4 base=texture(tex,texCoord);\n\n\n #ifdef CLEAR\n base.a=0.0;\n #endif\n\n outColor=cgl_blendPixel(base,col,am);\n}",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + amountX = op.inValue("x", 3), + amountY = op.inValue("y", 3), + trigger = op.outTrigger("trigger"), + inClear=op.inBool("Clear",true), + mulTex = op.inTexture("Multiply"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.repeat_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureMulUniform = new CGL.Uniform(shader, "t", "mulTex", 2), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + amountXUniform = new CGL.Uniform(shader, "f", "amountX", amountX), + amountYUniform = new CGL.Uniform(shader, "f", "amountY", amountY); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +inClear.onChange = +mulTex.onChange =updateDefines; + +function updateDefines() +{ + + shader.toggleDefine("CLEAR", inClear.get()); + shader.toggleDefine("HAS_MASK", mulTex.get()); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (mulTex.get())cgl.setTexture(2, mulTex.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.RepeatTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["ff9aa796-d781-444c-a9d4-a62157f82dd5"]={f:Ops.Gl.TextureEffects.RepeatTexture_v2,objName:"Ops.Gl.TextureEffects.RepeatTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.RgbMultiply +// +// ************************************************************** + +Ops.Gl.TextureEffects.RgbMultiply = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rgbmul_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float r;\nUNI float g;\nUNI float b;\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n col=texture(tex,texCoord);\n col.r*=r;\n col.g*=g;\n col.b*=b;\n outColor= col;\n}\n",}; +const render = op.inTrigger("render"); +const r = op.inValue("r", 1); +const g = op.inValue("g", 1); +const b = op.inValue("b", 1); +const trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.rgbmul_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const uniformR = new CGL.Uniform(shader, "f", "r", r); +const uniformG = new CGL.Uniform(shader, "f", "g", g); +const uniformB = new CGL.Uniform(shader, "f", "b", b); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.RgbMultiply.prototype = new CABLES.Op(); +CABLES.OPS["00f30153-f656-4701-9b93-244b033b2eaa"]={f:Ops.Gl.TextureEffects.RgbMultiply,objName:"Ops.Gl.TextureEffects.RgbMultiply"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.RgbToHsvTexture +// +// ************************************************************** + +Ops.Gl.TextureEffects.RgbToHsvTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"tonormal_frag":"#ifdef HAS_TEXTURES\n IN vec2 texCoord;\n UNI sampler2D tex;\n#endif\n\nUNI float strength;\n\n\nvec3 rgb2hsv(vec3 c)\n{\n vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n\n float d = q.x - min(q.w, q.y);\n float e = 1.0e-10;\n return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n}\n\nvec3 hsv2rgb(vec3 c)\n{\n vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n}\n\nvoid main()\n{\n vec4 col=texture(tex, texCoord);\n\n\n\n #ifdef OUT_HSB\n col.rgb=rgb2hsv(col.rgb).rgb;\n #endif\n #ifdef OUT_H\n col.rgb=rgb2hsv(col.rgb).rrr;\n #endif\n #ifdef OUT_S\n col.rgb=rgb2hsv(col.rgb).ggg;\n #endif\n #ifdef OUT_B\n col.rgb=rgb2hsv(col.rgb).bbb;\n #endif\n #ifdef OUT_SB\n col.rgb=rgb2hsv(col.rgb).ggg*rgb2hsv(col.rgb).bbb;\n #endif\n\n\n outColor=col;\n}",}; +const + render = op.inTrigger("render"), + inMeth = op.inSwitch("Output RGB", ["HSB", "Hue", "Sat", "Bright", "Sat*Bright"], "HSB"), + trigger = op.outTrigger("trigger"), + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.tonormal_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +inMeth.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("OUT_HSB", inMeth.get() == "HSB"); + shader.toggleDefine("OUT_H", inMeth.get() == "Hue"); + shader.toggleDefine("OUT_S", inMeth.get() == "Sat"); + shader.toggleDefine("OUT_B", inMeth.get() == "Bright"); + shader.toggleDefine("OUT_SB", inMeth.get() == "Sat*Bright"); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.RgbToHsvTexture.prototype = new CABLES.Op(); +CABLES.OPS["147ed29e-f04e-4f7c-b9d7-133d7c6cbef9"]={f:Ops.Gl.TextureEffects.RgbToHsvTexture,objName:"Ops.Gl.TextureEffects.RgbToHsvTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.RotateTexture_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.RotateTexture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rotate_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D multiplierTex;\nUNI float amount;\nUNI float resX;\nUNI float resY;\nUNI float rotate;\n\n{{CGL.BLENDMODES3}}\n\n#define PI 3.14159265\n#define TAU (2.0*PI)\n\nvoid pR(inout vec2 p, float a)\n{\n\tp = cos(a)*p + sin(a)*vec2(p.y, -p.x);\n}\n\nvoid main()\n{\n float multiplier = 0.0;\n\n #ifdef ROTATE_TEXTURE\n multiplier = dot(vec3(0.2126,0.7152,0.0722), texture(multiplierTex,texCoord).rgb);\n #endif\n\n vec2 uv = texCoord;\n vec2 res = vec2(resX,resY);\n uv -= 0.5;\n pR(uv.xy,(rotate + multiplier) * (TAU) );\n uv += 0.5;\n\n\n\n vec4 col=texture(tex,uv);\n vec4 base=texture(tex,texCoord);\n\n #ifdef CLEAR\n base.a=0.0;\n #endif\n\n\n #ifdef CROP_IMAGE\n if(uv.x>1.0 ||uv.x<0.0 || uv.y>1.0 ||uv.y<0.0 )\n {\n base.a=0.0;\n col.a=0.0;\n // discard;\n // return;\n }\n #endif\n\n outColor=cgl_blendPixel(base,col,amount);\n}",}; +const render = op.inTrigger("render"), + multiplierTex = op.inTexture("Multiplier"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + inRotate = op.inValueSlider("Rotate", 0.125), + crop = op.inValueBool("Crop", true), + inClear=op.inBool("Clear",true), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "rotatetexture"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.rotate_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureMultiplierUniform = new CGL.Uniform(shader, "t", "multiplierTex", 1), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + rotateUniform = new CGL.Uniform(shader, "f", "rotate", inRotate); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +crop.onChange = + multiplierTex.onChange = updateDefines; + +updateDefines(); + + +function updateDefines() +{ + shader.toggleDefine("CLEAR", inClear.get()); + shader.toggleDefine("CROP_IMAGE", crop.get()); + shader.toggleDefine("ROTATE_TEXTURE", multiplierTex.isLinked()); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (multiplierTex.get()) cgl.setTexture(1, multiplierTex.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.RotateTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["20b8a2e6-2419-474b-98a4-71a5e3178631"]={f:Ops.Gl.TextureEffects.RotateTexture_v2,objName:"Ops.Gl.TextureEffects.RotateTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.RoundCorners +// +// ************************************************************** + +Ops.Gl.TextureEffects.RoundCorners = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"roundcorners_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float radius;\nUNI float r;\nUNI float g;\nUNI float b;\nUNI float a;\n\nUNI float width;\nUNI float height;\n\nfloat roundRect(in vec2 distFromCenter)\n{\n float r=radius*0.25*width+0.01;\n float t = length(max(abs(distFromCenter) - (vec2(0.5*width,0.5*height) - vec2(r,r)), vec2(0.0))) - r;\n return smoothstep(-0.001, 0.9,t);\n}\n\nvoid main()\n{\n vec2 tc=texCoord;\n vec4 col=texture(tex,tc);\n float c=0.0;\n\n c=roundRect(vec2(0.5*width,0.5*height)-vec2(gl_FragCoord));\n outColor=mix(col,vec4(r,g,b,a),c);\n\n}",}; +const + render = op.inTrigger("render"), + radius = op.inValueSlider("radius", 0.25), + r = op.inValueSlider("r"), + g = op.inValueSlider("g"), + b = op.inValueSlider("b"), + a = op.inValueSlider("a", 1), + next = op.outTrigger("next"); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); +shader.setSource(shader.getDefaultVertexShader(), attachments.roundcorners_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +radius.uniform = new CGL.Uniform(shader, "f", "radius", radius); +r.uniform = new CGL.Uniform(shader, "f", "r", r); +g.uniform = new CGL.Uniform(shader, "f", "g", g); +b.uniform = new CGL.Uniform(shader, "f", "b", b); +a.uniform = new CGL.Uniform(shader, "f", "a", a); +r.setUiAttribs({ "colorPick": true }); + +let uniWidth = new CGL.Uniform(shader, "f", "width", 512); +let uniHeight = new CGL.Uniform(shader, "f", "height", 512); +let uniAspect = new CGL.Uniform(shader, "f", "aspect", 1); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + uniWidth.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().width); + uniHeight.setValue(cgl.currentTextureEffect.getCurrentSourceTexture().height); + + let texture = cgl.currentTextureEffect.getCurrentSourceTexture(); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, texture.tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + next.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.RoundCorners.prototype = new CABLES.Op(); +CABLES.OPS["f7e5d8c3-6f49-4769-a863-fbff2c466ce8"]={f:Ops.Gl.TextureEffects.RoundCorners,objName:"Ops.Gl.TextureEffects.RoundCorners"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.SSAO_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.SSAO_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"ssao_frag":"/*\nSSAO GLSL shader v1.2\nassembled by Martins Upitis (martinsh) (devlog-martinsh.blogspot.com)\noriginal technique is made by Arkano22 (www.gamedev.net/topic/550699-ssao-no-halo-artifacts/)\n\nchangelog:\n1.2 - added fog calculation to mask AO. Minor fixes.\n1.1 - added spiral sampling method from here:\n(http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere)\n\n*/\n\n{{CGL.BLENDMODES}}\n\nIN vec2 texCoord;\n\nUNI float amount;\n\nUNI sampler2D texDepth;\nUNI sampler2D tex;\n// const float bgl_RenderedTextureWidth=800.0;\n// const float bgl_RenderedTextureHeight=600.0;\n\n#define PI 3.14159265\n\nUNI float width;\nUNI float height;\n// float width = 800.0; //texture width\n// float height = 600.0; //texture height\n\n// vec2 texCoord = texCoord.st;\n\n//------------------------------------------\n//general stuff\n\n//make sure that these two values are the same for your camera, otherwise distances will be wrong.\n\n// const float znear = 0.01; //Z-near\n// const float zfar = 20.0; //Z-far\nUNI float znear;\nUNI float zfar;\n\n//user variables\n// const int SAMPLES = 16; //ao sample count\n// UNI int SAMPLES;\n\n// const float radius = 3.0; //ao radius\n// const float aoclamp = 0.25; //depth clamp - reduces haloing at screen edges\n\nUNI float radius;\nUNI float aoclamp;\nUNI bool noise ; //use noise instead of pattern for sample dithering//*****\nUNI float noiseamount; //dithering amount//********\n\nconst float diffarea = 0.4; //self-shadowing reduction\nconst float gdisplace = 0.4; //gauss bell center\n\n// const bool mist = false; //use mist?\n// const float miststart = 0.0; //mist start\n// const float mistend = 16.0; //mist end\n\n// const bool onlyAO = false; //use only ambient occlusion pass?\n// const float lumInfluence = 0.7; //how much luminance affects occlusion\nUNI float lumInfluence;\n\n\n\n\n//--------------------------------------------------------\n\nvec2 rand(vec2 coord) //generating noise/pattern texture for dithering\n{\n\n #ifndef NOISE\n \treturn vec2(0.0,0.0);\n\t#endif\n\n #ifdef NOISE\n\t\tfloat noiseX = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233))) * 43758.5453),0.0,1.0)*2.0-1.0;\n\t\tfloat noiseY = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233)*2.0)) * 43758.5453),0.0,1.0)*2.0-1.0;\n \treturn vec2(noiseX,noiseY)*noiseamount/100.0;\n #endif\n}\n\n// float doMist()\n// {\n// \tfloat zdepth = texture(texDepth,texCoord.xy).x;\n// \tfloat depth = -zfar * znear / (zdepth * (zfar - znear) - zfar);\n// \treturn clamp((depth-miststart)/mistend,0.0,1.0);\n// }\n\nfloat readDepth(in vec2 coord)\n{\n\tif (texCoord.x<0.0||texCoord.y<0.0) return 1.0;\n\treturn (2.0 * znear) / (zfar + znear - texture(texDepth, coord ).x * (zfar-znear));\n}\n\nfloat compareDepths(in float depth1, in float depth2,inout int far)\n{\n\tfloat garea = 2.0; //gauss bell width\n\tfloat diff = (depth1 - depth2)*100.0; //depth difference (0-100)\n\t//reduce left bell width to avoid self-shadowing\n\tif (diff 0)\n\t{\n\t\ttemp2 = compareDepths(readDepth(coord2),depth,far);\n\t\ttemp += (1.0-temp)*temp2;\n\t}\n\n\treturn temp;\n}\n\nvoid main(void)\n{\n\tvec2 noise = rand(texCoord);\n\tfloat depth = readDepth(texCoord);\n\n\tfloat w = (1.0 / width)/clamp(depth,aoclamp,1.0)+(noise.x*(1.0-noise.x));\n\tfloat h = (1.0 / height)/clamp(depth,aoclamp,1.0)+(noise.y*(1.0-noise.y));\n\n\tfloat pw;\n\tfloat ph;\n\n\tfloat ao;\n\n\tfloat dl = PI*(3.0-sqrt(5.0));\n\tfloat dz = 1.0/float(SAMPLES);\n\tfloat l = 0.0;\n\tfloat z = 1.0 - dz/2.0;\n\n\tfor (int i = 0; i <= SAMPLES; i ++)\n\t{\n\t\tfloat r = sqrt(1.0-z);\n\n\t\tpw = cos(l)*r;\n\t\tph = sin(l)*r;\n\t\tao += calAO(depth,pw*w,ph*h);\n\t\tz = z - dz;\n\t\tl = l + dl;\n\t}\n\n\tao /= float(SAMPLES);\n\tao = 1.0-ao;\n\tvec4 color = texture(tex,texCoord);\n\n\tvec3 lumcoeff = vec3(0.299,0.587,0.114);\n\tfloat lum = dot(color.rgb, lumcoeff);\n\tvec3 luminance = vec3(lum, lum, lum);\n\tvec3 final = vec3(mix( clamp(vec3(ao),0.0,1.0),vec3(1.0),luminance*(1.0-amount)));//mix(color*ao, white, luminance)\n\n vec4 col=vec4( _blend(color.rgb,final) ,1.0);\n\n outColor=vec4( mix( col.rgb, color.rgb ,1.0-color.a*amount),1.0);\n}",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "multiply"), + amount = op.inValueSlider("Amount", 1), + + depth = op.inTexture("depth texture"), + zNear = op.inValue("Frustum Near", 0.1), + zFar = op.inValue("Frustum Far", 20), + samples = op.inValueInt("Samples", 4), + aoRadius = op.inValue("Ao Radius", 3), + aoClamp = op.inValueSlider("Ao Clamp", 0.25), + lumInfluence = op.inValueSlider("Luminance Influence", 0.7), + noise = op.inValueBool("Enable noise", false), + noiseamount = op.inValueFloat("Noise amount", 0.0008); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +op.toWorkPortsNeedToBeLinked(depth, render); +op.setPortGroup("Noise", [noise, noiseamount]); + +shader.setSource(shader.getDefaultVertexShader(), attachments.ssao_frag); +let textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +let textureAlpha = new CGL.Uniform(shader, "t", "texDepth", 1); +aoRadius.uniform = new CGL.Uniform(shader, "f", "radius", aoRadius); +aoClamp.uniform = new CGL.Uniform(shader, "f", "aoclamp", aoClamp); +lumInfluence.uniform = new CGL.Uniform(shader, "f", "lumInfluence", lumInfluence); +zNear.uniform = new CGL.Uniform(shader, "f", "znear", zNear); +zFar.uniform = new CGL.Uniform(shader, "f", "zfar", zFar); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); +noiseamount.uniform = new CGL.Uniform(shader, "f", "noiseamount", noiseamount); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +noise.onChange = function () +{ + shader.toggleDefine("NOISE", noise.get()); +}; + +samples.onChange = function () +{ + shader.define("SAMPLES", samples.get()); +}; + +let uniWidth = new CGL.Uniform(shader, "f", "width", 1024), + uniHeight = new CGL.Uniform(shader, "f", "height", 512); + +shader.define("SAMPLES", samples.get()); +aoClamp.uniform = new CGL.Uniform(shader, "f", "aoclamp", aoClamp); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + if (!depth.get()) return; + + uniWidth.setValue(depth.get().width); + uniHeight.setValue(depth.get().height); + + cgl.pushShader(shader); + + cgl.currentTextureEffect.bind(); + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (depth.get() && depth.get().tex) + { + cgl.setTexture(1, depth.get().tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, depth.get().tex ); + } + + cgl.currentTextureEffect.finish(); + + cgl.popShader(); + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.SSAO_v2.prototype = new CABLES.Op(); +CABLES.OPS["049ad504-09a2-4bc0-bbf1-dad67608a3d2"]={f:Ops.Gl.TextureEffects.SSAO_v2,objName:"Ops.Gl.TextureEffects.SSAO_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ScaleTexture_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.ScaleTexture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"scale_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D multiplierTex;\nUNI float amount;\nUNI float uScaleX,uScaleY;\nUNI float offsetX,offsetY;\nUNI float centerX,centerY;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n float multiplier = 1.0;\n vec2 uv = texCoord;\n\n #ifdef MASK_SCALE\n multiplier = dot(vec3(0.2126,0.7152,0.0722), texture(multiplierTex,texCoord).rgb);\n #endif\n\n uv.x = (uv.x - centerX) / (uScaleX * multiplier) + centerX+offsetX ;\n uv.y = (uv.y - centerY) / (uScaleY * multiplier) + centerY+offsetY ;\n\n vec4 col = texture(tex,uv);\n vec4 base = texture(tex,texCoord);\n float a=amount;\n\n #ifdef CLEAR\n base=vec4(0.0,0.0,0.0,0.0);\n #endif\n\n outColor=cgl_blendPixel(base,col,a);\n\n if(uv.x>1.0||uv.y>1.0||uv.x<0.0||uv.y<0.0)\n outColor.a=0.0;\n\n}\n",}; +const + render = op.inTrigger("render"), + multiplierTex = op.inTexture("Multiplier"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + scaleX = op.inValue("Scale X", 1.5), + scaleY = op.inValue("Scale Y", 1.5), + offsetX = op.inFloat("offset X", 0), + offsetY = op.inFloat("offset Y", 0), + centerX = op.inFloat("center X", 0.5), + centerY = op.inFloat("center Y", 0.5), + inClear = op.inBool("Clear", true), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.scale_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureMultiplierUniform = new CGL.Uniform(shader, "t", "multiplierTex", 1), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + scaleXUniform = new CGL.Uniform(shader, "f", "uScaleX", scaleX), + scaleYUniform = new CGL.Uniform(shader, "f", "uScaleY", scaleY), + centerXUniform = new CGL.Uniform(shader, "f", "centerX", centerX), + centerYUniform = new CGL.Uniform(shader, "f", "centerY", centerY), + offsetXUniform = new CGL.Uniform(shader, "f", "offsetX", offsetX), + offsetYUniform = new CGL.Uniform(shader, "f", "offsetY", offsetY); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +inClear.onChange = +multiplierTex.onChange = function () +{ + shader.toggleDefine("MASK_SCALE", multiplierTex.isLinked()); + shader.toggleDefine("CLEAR", inClear.get()); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (multiplierTex.get()) cgl.setTexture(1, multiplierTex.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.ScaleTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["942ef040-9be9-4848-9122-61cb28cb7789"]={f:Ops.Gl.TextureEffects.ScaleTexture_v2,objName:"Ops.Gl.TextureEffects.ScaleTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ScrollTexture +// +// ************************************************************** + +Ops.Gl.TextureEffects.ScrollTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"scroll_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amountX;\nUNI float amountY;\n\n#ifdef HAS_MASK\n UNI sampler2D texMask;\n#endif\n\nvoid main()\n{\n float amX=amountX;\n float amY=amountY;\n\n #ifdef HAS_MASK\n vec4 m=texture(texMask,texCoord);\n\n\n amX*=(m.r-0.5)*2.0;\n amY*=(m.g-0.5)*2.0;\n #endif\n\n vec4 col=vec4(0.0,0.0,0.0,1.0);\n float x=mod(texCoord.x+amX,1.0);\n float y=mod(texCoord.y+amY,1.0);\n\n\n #ifdef NO_REPEAT\n x=texCoord.x+amX*0.1;\n y=texCoord.y+amY*0.1;\n #endif\n\n col=texture(tex,vec2(x,y));\n\n #ifdef NO_REPEAT\n if(x>1.0 || x<0.0 || y>1.0 || y<0.0) col=vec4(0.0,0.0,0.0,0.0);\n #endif\n outColor= col;\n}",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + amountX = op.inValue("amountX"), + amountY = op.inValue("amountY"), + textureMask = op.inTexture("Mask"), + repeat = op.inValueBool("Repeat", true); + +repeat.onChange = updateRepeat; + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); +shader.setSource(shader.getDefaultVertexShader(), attachments.scroll_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountXUniform = new CGL.Uniform(shader, "f", "amountX", amountX), + amountYUniform = new CGL.Uniform(shader, "f", "amountY", amountY), + unitexMask = new CGL.Uniform(shader, "t", "texMask", 1); + +updateRepeat(); + +textureMask.onChange = function () +{ + if (textureMask.get())shader.define("MASK"); + else shader.removeDefine("MASK"); +}; + +function updateRepeat() +{ + if (!repeat.get())shader.define("NO_REPEAT"); + else shader.removeDefine("NO_REPEAT"); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (textureMask.get()) cgl.setTexture(1, textureMask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.ScrollTexture.prototype = new CABLES.Op(); +CABLES.OPS["9b151d99-7888-4948-81c7-cd23b334e8d4"]={f:Ops.Gl.TextureEffects.ScrollTexture,objName:"Ops.Gl.TextureEffects.ScrollTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Shapes2d_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Shapes2d_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"shapes_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float aspect;\n\nUNI bool mirrorX;\nUNI bool mirrorY;\n\nUNI float xPos;\nUNI float yPos;\n\nUNI bool invertColor;\nUNI bool fillShape;\n\nUNI float width;\nUNI float height;\nUNI float lineThickness;\n\nUNI float rotate;\n\nUNI float r;\nUNI float g;\nUNI float b;\nUNI float a;\n\n{{CGL.BLENDMODES3}}\n\n#define PI 3.14159265\n#define TAU (2.0*PI)\n\nvoid pR(inout vec2 p, float a)\n{\n\tp = cos(a)*p + sin(a)*vec2(p.y, -p.x);\n}\n\nfloat sdCircle( vec2 p, float r )\n{\n return length(p) - r;\n}\n\nfloat sdBox( in vec2 p, in vec2 b )\n{\n vec2 d = abs(p)-b;\n return length(max(d,vec2(0))) + min(max(d.x,d.y),0.0);\n}\n\nfloat sdEquilateralTriangle( in vec2 p , in float size )\n{\n const float k = sqrt(3.0);\n p/= vec2(size);\n p.x = abs(p.x) - 1.0;\n p.y = -p.y + 1.0/k;\n if( p.x + k*p.y > 0.0 ) p = vec2( p.x - k*p.y, -k*p.x - p.y )/2.0;\n p.x -= clamp( p.x, -2.0, 0.0 );\n return (-length(p)*sign(p.y))*size;\n}\n\nfloat sdTriangleIsosceles( in vec2 p, in vec2 q )\n{\n\n p.y +=0.5;\n p.x = abs(p.x);\n\n vec2 a = p - q*clamp( dot(p,q)/dot(q,q), 0.0, 1.0 );\n vec2 b = p - q*vec2( clamp( p.x/q.x, 0.0, 1.0 ), 1.0 );\n float s = -sign( q.y );\n vec2 d = min( vec2( dot(a,a), s*(p.x*q.y-p.y*q.x) ),\n vec2( dot(b,b), s*(p.y-q.y) ));\n\n return -sqrt(d.x)*sign(d.y);\n}\n\nfloat ndot(vec2 a, vec2 b ) { return a.x*b.x - a.y*b.y; }\n\nfloat sdRhombus( in vec2 p, in vec2 b )\n{\n vec2 q = abs(p);\n float h = clamp((-2.0*ndot(q,b)+ndot(b,b))/dot(b,b),-1.0,1.0);\n float d = length( q - 0.5*b*vec2(1.0-h,1.0+h) );\n return d * sign( q.x*b.y + q.y*b.x - b.x*b.y );\n}\n\nfloat sdPentagon( in vec2 p, in float r )\n{\n const vec3 k = vec3(0.809016994,0.587785252,0.726542528);\n p.x = abs(p.x);\n p -= 2.0*min(dot(vec2(-k.x,k.y),p),0.0)*vec2(-k.x,k.y);\n p -= 2.0*min(dot(vec2( k.x,k.y),p),0.0)*vec2( k.x,k.y);\n p -= vec2(clamp(p.x,-r*k.z,r*k.z),r);\n return length(p)*sign(p.y);\n}\n\nfloat sdHexagon( in vec2 p, in float r )\n{\n const vec3 k = vec3(-0.866025404,0.5,0.577350269);\n p = abs(p);\n p -= 2.0*min(dot(k.xy,p),0.0)*k.xy;\n p -= vec2(clamp(p.x, -k.z*r, k.z*r), r);\n return length(p)*sign(p.y);\n}\n\nfloat sdOctogon( in vec2 p, in float r )\n{\n const vec3 k = vec3(-0.9238795325, 0.3826834323, 0.4142135623 );\n p = abs(p);\n p -= 2.0*min(dot(vec2( k.x,k.y),p),0.0)*vec2( k.x,k.y);\n p -= 2.0*min(dot(vec2(-k.x,k.y),p),0.0)*vec2(-k.x,k.y);\n p -= vec2(clamp(p.x, -k.z*r, k.z*r), r);\n return length(p)*sign(p.y);\n}\n\nfloat sdHexagram( in vec2 p, in float r )\n{\n const vec4 k=vec4(-0.5,0.8660254038,0.5773502692,1.7320508076);\n p = abs(p);\n p -= 2.0*min(dot(k.xy,p),0.0)*k.xy;\n p -= 2.0*min(dot(k.yx,p),0.0)*k.yx;\n p -= vec2(clamp(p.x,r*k.z,r*k.w),r);\n return length(p)*sign(p.y);\n}\n\nvoid main()\n{\n vec2 p = texCoord-0.5;\n p.y/=aspect;\n\n p *= 2.0;\n\n float d =0.0;\n\n if(mirrorX)p.x = abs(p.x);\n if(mirrorY)p.y = abs(p.y);\n\n p -= vec2(xPos,yPos/aspect);\n\n pR(p,rotate * (TAU) + PI);\n\n #ifdef IS_CIRCLE\n\n d = sdCircle(p,width);\n #endif\n\n #ifdef IS_EQUI_TRIANGLE\n d = sdEquilateralTriangle(p,width);\n #endif\n\n #ifdef IS_ISO_TRIANGLE\n d = sdTriangleIsosceles(p,vec2(width,height));\n #endif\n\n #ifdef IS_BOX\n d = sdBox(p,vec2(width,height));\n #endif\n\n #ifdef IS_RHOMBUS\n d = sdRhombus(p,vec2(width,height));\n #endif\n\n #ifdef IS_PENTAGON\n d = sdPentagon(p,width);\n #endif\n\n #ifdef IS_HEXAGON\n d = sdHexagon(p,width);\n #endif\n\n #ifdef IS_OCTOGON\n d = sdOctogon(p,width);\n #endif\n\n #ifdef IS_HEXAGRAM\n d = sdHexagram(p*2.0,width);\n #endif\n\n if (fillShape == false)\n {\n d = abs(d)-abs(lineThickness*0.01);\n }\n if(invertColor)\n {\n d = sign(d);\n }\n else\n {\n d = 1.0 - sign(d);\n }\n\n d = clamp(d,0.0,1.0);\n\n vec4 col = vec4(vec4(r,g,b,a)) ;\n vec4 base = texture(tex,texCoord);\n outColor = cgl_blendPixel(base,col,d*amount);\n}\n\n\n",}; +/* +Shaders are from Iq's webapge +https://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm +*/ + +const render = op.inTrigger("render"); +const blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"); +const maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op); + +const amount = op.inValueSlider("Amount", 1); + +const shapeSelect = op.inValueSelect("Shape", ["circle", "eqi triangle", "iso triangle", "box", "rhombus", "pentagon", + "hexagon", "octogon", "hexagram"], "circle"); +const mirrorX = op.inValueBool("Mirror X", false); +const mirrorY = op.inValueBool("Mirror Y", false); + +const xPos = op.inValueFloat("Offset X", 0.0); +const yPos = op.inValueFloat("Offset Y", 0.0); + +const fillShape = op.inValueBool("fillShape", true); +const lineThickness = op.inValue("Line thickness", 1.0); +const invertColor = op.inValueBool("Invert color", false); + +const width = op.inValue("width", 0.5); +const height = op.inValue("height", 0.5); + +const inRotate = op.inValueSlider("Rotate", 0.0); + +const r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + a = op.inValueSlider("a", 1.0); +r.setUiAttribs({ "colorPick": true }); + +const trigger = op.outTrigger("trigger"); + +let selectIndex = 0; + +function onFilterChange() +{ + let selectedMode = shapeSelect.get(); + + if ((selectedMode === "circle") || (selectedMode === "eqi triangle") || (selectedMode === "pentagon") + || (selectedMode === "hexagon") || (selectedMode === "octogon") || (selectedMode === "hexagram")) + selectIndex = 0; + + else if ((selectedMode === "box") || (selectedMode === "iso triangle") || (selectedMode === "rhombus")) + selectIndex = 1; + + if (selectIndex === 0) + { + height.setUiAttribs({ "greyout": true }); + width.setUiAttribs({ "title": "Size" }); + } + else if (selectIndex === 1) + { + height.setUiAttribs({ "greyout": false }); + width.setUiAttribs({ "title": "Width" }); + } +} + +fillShape.onChange = function () +{ + lineThickness.setUiAttribs({ "greyout": fillShape.get() }); +}; + +op.init = shapeSelect.onChange = function () +{ + onFilterChange(); + // choose shape + shader.toggleDefine("IS_CIRCLE", shapeSelect.get()); + shader.toggleDefine("IS_EQUI_TRIANGLE", shapeSelect.get() === "eqi triangle"); + shader.toggleDefine("IS_ISO_TRIANGLE", shapeSelect.get() === "iso triangle"); + shader.toggleDefine("IS_BOX", shapeSelect.get() === "box"); + shader.toggleDefine("IS_RHOMBUS", shapeSelect.get() === "rhombus"); + shader.toggleDefine("IS_PENTAGON", shapeSelect.get() === "pentagon"); + shader.toggleDefine("IS_HEXAGON", shapeSelect.get() === "hexagon"); + shader.toggleDefine("IS_OCTOGON", shapeSelect.get() === "octogon"); + shader.toggleDefine("IS_HEXAGRAM", shapeSelect.get() === "hexagram"); +}; + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.shapes_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); +const mirrorXUniform = new CGL.Uniform(shader, "b", "mirrorX", mirrorX); +const mirrorYUniform = new CGL.Uniform(shader, "b", "mirrorY", mirrorY); + +const xPosUniform = new CGL.Uniform(shader, "f", "xPos", xPos); +const yPosUniform = new CGL.Uniform(shader, "f", "yPos", yPos); +const invertColorUniform = new CGL.Uniform(shader, "b", "invertColor", invertColor); +const fillShapeUniform = new CGL.Uniform(shader, "b", "fillShape", fillShape); + +const uniWidth = new CGL.Uniform(shader, "f", "width", width); +const uniHeight = new CGL.Uniform(shader, "f", "height", height); +const uniModifier = new CGL.Uniform(shader, "f", "lineThickness", lineThickness); +const rotateUniform = new CGL.Uniform(shader, "f", "rotate", inRotate); + +let uniformR = new CGL.Uniform(shader, "f", "r", r); +let uniformG = new CGL.Uniform(shader, "f", "g", g); +let uniformB = new CGL.Uniform(shader, "f", "b", b); +let uniformA = new CGL.Uniform(shader, "f", "a", a); +let uniformAspect = new CGL.Uniform(shader, "f", "aspect", 1); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +render.onTriggered = update; +function update() +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + fillShapeUniform.setValue(fillShape.get()); + uniformAspect.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +} + + +}; + +Ops.Gl.TextureEffects.Shapes2d_v2.prototype = new CABLES.Op(); +CABLES.OPS["1b81100b-7c09-4171-ae82-8865b905720e"]={f:Ops.Gl.TextureEffects.Shapes2d_v2,objName:"Ops.Gl.TextureEffects.Shapes2d_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Sharpen +// +// ************************************************************** + +Ops.Gl.TextureEffects.Sharpen = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"sharpen_frag":"\nIN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\n\nUNI float pX,pY;\n\nconst vec4 lumcoeff = vec4(0.299,0.587,0.114, 0.);\n\nfloat desaturate(vec4 color)\n{\n vec3 c= vec3(dot(vec3(0.2126,0.7152,0.0722), color.rgb));\n return (c.r+c.g+c.b)/3.0;\n}\n\n\n\nvoid main()\n{\n \n vec4 col=vec4(1.0,0.0,0.0,1.0);\n col=texture(tex,texCoord);\n \n \n float colorL = desaturate(texture(tex, texCoord+vec2(-pX,0) ));\n float colorR = desaturate(texture(tex, texCoord+vec2( pX,0) ));\n float colorA = desaturate(texture(tex, texCoord+vec2( 0,-pY) ));\n float colorB = desaturate(texture(tex, texCoord+vec2( 0, pY) ));\n \n float colorLA = desaturate(texture(tex, texCoord+vec2(-pX,pY)));\n float colorRA = desaturate(texture(tex, texCoord+vec2( pX,pY)));\n float colorLB = desaturate(texture(tex, texCoord+vec2(-pX,-pY)));\n float colorRB = desaturate(texture(tex, texCoord+vec2( pX,-pY)));\n \n vec4 final = col + col * amount * (8.0*desaturate(col) - colorL - colorR - colorA - colorB - colorLA - colorRA - colorLB - colorRB);\n\n outColor= final;\n\n}",}; +const render = op.inTrigger("Render"); +const trigger = op.outTrigger("Trigger"); +const amount = op.inValueSlider("amount", 0.5); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.sharpen_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +const uniPx = new CGL.Uniform(shader, "f", "pX", 1 / 1024); +const uniPy = new CGL.Uniform(shader, "f", "pY", 1 / 1024); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + uniPx.setValue(1 / cgl.currentTextureEffect.getCurrentSourceTexture().width); + uniPy.setValue(1 / cgl.currentTextureEffect.getCurrentSourceTexture().height); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Sharpen.prototype = new CABLES.Op(); +CABLES.OPS["55647083-131d-4c70-b667-21fecf311ea5"]={f:Ops.Gl.TextureEffects.Sharpen,objName:"Ops.Gl.TextureEffects.Sharpen"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.SkewStretchImage_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.SkewStretchImage_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"invert_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float stretchTopX;\nUNI float stretchBotX;\nUNI float stretchLeft;\nUNI float stretchRight;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n\n vec2 tc=texCoord;\n\n #ifdef SMOOTHSTEP\n tc.y=smoothstep(0.,1.,tc.y);\n tc.x=smoothstep(0.,1.,tc.x);\n #endif\n\n vec2 tcnorm=texCoord;\n\n tcnorm-=0.5;\n tcnorm*=2.0;\n\n tcnorm.x=mix(tcnorm.x*stretchBotX,tcnorm.x,tc.y);\n tcnorm.x=mix(tcnorm.x*stretchTopX,tcnorm.x,1.0-tc.y);\n\n tcnorm.y=mix(tcnorm.y*stretchLeft,tcnorm.y,tc.x);\n tcnorm.y=mix(tcnorm.y*stretchRight,tcnorm.y,1.0-tc.x);\n\n tc=tcnorm/2.0+0.5;\n\n col=texture(tex,tc);\n\n #ifdef CLAMP\n if(tc.x<0.0 || tc.x>1.0 || tc.y<0.0 || tc.y>1.0) col=vec4(0.0,0.0,0.0,0.0);\n #endif\n\n vec4 base=texture(tex,texCoord);\n base.a=0.0;\n\n outColor=cgl_blendPixel(base,col,amount);\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + inClamp = op.inBool("Clamp", true), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "skewstrechimage"); +shader.setSource(shader.getDefaultVertexShader(), attachments.invert_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +shader.addUniformFrag("f", "stretchTopX", op.inFloat("Stretch Top", 1)); +shader.addUniformFrag("f", "stretchBotX", op.inFloat("Stretch Bottom", 1)); +shader.addUniformFrag("f", "stretchLeft", op.inFloat("Stretch Left", 1)); +shader.addUniformFrag("f", "stretchRight", op.inFloat("Stretch Right", 1)); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +inClamp.onChange = updateDefines; + +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("CLAMP", inClamp.get()); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.SkewStretchImage_v2.prototype = new CABLES.Op(); +CABLES.OPS["665bbc0c-2fd7-4683-a1df-e57d8f17da93"]={f:Ops.Gl.TextureEffects.SkewStretchImage_v2,objName:"Ops.Gl.TextureEffects.SkewStretchImage_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Stripes_v4 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Stripes_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"stripes_v3_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float num;\nUNI float width;\nUNI float axis;\nUNI float offset;\nUNI float rotate;\n\nUNI float r;\nUNI float g;\nUNI float b;\n\n\n{{CGL.BLENDMODES3}}\n\n#define PI 3.14159265\n#define TAU (2.0*PI)\n\nvoid pR(inout vec2 p, float a)\n{\n\tp = cos(a)*p + sin(a)*vec2(p.y, -p.x);\n}\nvoid main()\n{\n vec2 uv = texCoord-0.5;\n pR(uv.xy,rotate*TAU);\n float stripe=0.0;\n\n float v=0.0;\n float c=1.0;\n v=uv.y;\n v+=offset;\n\n float m=mod(v,1.0/num);\n\n #ifdef CIRCULAR\n m=mod((length(uv)+offset)*1.5,1.0/num);\n #endif\n\n float rm=width*2.0*1.0/num/2.0;\n\n if(m>rm)\n stripe=mix(stripe,1.,1.0);\n\n #ifdef STRIPES_SMOOTHED\n m*=2.0;\n stripe= r * smoothstep(0.,1., abs((((m-rm) )/(rm))));\n #endif\n\n #ifdef INVERT\n stripe=1.0-stripe;\n #endif\n\n //blend section\n vec4 col=vec4(vec3(r,g,b),1.0);\n vec4 base=texture(tex,texCoord);\n\n outColor=cgl_blendPixel(base,col,amount*stripe);\n}\n",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + num = op.inValue("Num", 5), + width = op.inValue("Width", 0.5), + rotate = op.inValueSlider("Rotate", 0), + offset = op.inValue("Offset", 0), + smoothed = op.inValueBool("Gradients"), + circular = op.inValueBool("Circular"), + invert = op.inValueBool("Invert"), + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + trigger = op.outTrigger("trigger"); + +r.setUiAttribs({ "colorPick": true }); + +smoothed.onChange = + circular.onChange = + invert.onChange = updateDefines; + +function updateDefines() +{ + shader.toggleDefine("STRIPES_SMOOTHED", smoothed.get()); + shader.toggleDefine("CIRCULAR", circular.get()); + shader.toggleDefine("INVERT", invert.get()); +} + +const + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, "textureeffect stripes"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.stripes_v3_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + rotateUniform = new CGL.Uniform(shader, "f", "rotate", rotate), + numUniform = new CGL.Uniform(shader, "f", "num", num), + uniWidth = new CGL.Uniform(shader, "f", "width", width), + uniOffset = new CGL.Uniform(shader, "f", "offset", offset), + uniformR = new CGL.Uniform(shader, "f", "r", r), + uniformG = new CGL.Uniform(shader, "f", "g", g), + uniformB = new CGL.Uniform(shader, "f", "b", b); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Stripes_v4.prototype = new CABLES.Op(); +CABLES.OPS["a1d57718-20a0-4d18-89d3-69947b0d1acf"]={f:Ops.Gl.TextureEffects.Stripes_v4,objName:"Ops.Gl.TextureEffects.Stripes_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.TexMathModulo +// +// ************************************************************** + +Ops.Gl.TextureEffects.TexMathModulo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"invert_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D texMask;\nUNI float amount;\nUNI float modulo;\n\n{{CGL.BLENDMODES}}\n\nvoid main()\n{\n vec4 col=vec4(1.0);\n col=texture(tex,texCoord);\n\n #ifdef USE_MASK\n #ifdef MASK_INVERT\n if(texture(texMask,texCoord).r>0.5)\n {\n outColor= col;\n return;\n }\n #endif\n\n #ifndef MASK_INVERT\n if(texture(texMask,texCoord).r<0.5)\n {\n outColor= col;\n return;\n }\n #endif\n #endif\n\n vec4 invert = vec4(mod(col.rgb,modulo),1.0);\n\n outColor=cgl_blend(col,invert,amount);\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + + maskInvert = op.inBool("Mask Invert", false), + mask = op.inTexture("Mask"), + + amount = op.inValueSlider("Amount", 1), + modulo = op.inValueSlider("modulo", 1), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "texmodulo"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.invert_frag); +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + moduloUniform = new CGL.Uniform(shader, "f", "modulo", modulo), + textureMaskUniform = new CGL.Uniform(shader, "t", "texMask", 1); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +maskInvert.onChange = +mask.onChange = () => +{ + shader.toggleDefine("USE_MASK", mask.isLinked()); + shader.toggleDefine("MASK_INVERT", maskInvert.get()); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (mask.get())cgl.setTexture(1, mask.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.TexMathModulo.prototype = new CABLES.Op(); +CABLES.OPS["9105dc83-9cb9-4a5e-9ec0-db641436dbe2"]={f:Ops.Gl.TextureEffects.TexMathModulo,objName:"Ops.Gl.TextureEffects.TexMathModulo"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.TextureDifference +// +// ************************************************************** + +Ops.Gl.TextureEffects.TextureDifference = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"tex_difference_frag":"\nIN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D tex1;\nUNI sampler2D tex2;\n\nvoid main()\n{\n\n vec4 col=texture(tex,texCoord);\n \n vec4 col1=texture(tex1,texCoord);\n vec4 col2=texture(tex2,texCoord);\n\n\n outColor = col1-col2;\n outColor.a=1.0;\n\n}\n",}; +const render = op.inTrigger("render"); +const texture1 = op.inTexture("Texture 1"); +const texture2 = op.inTexture("Texture 2"); + +const trigger = op.outTrigger("Next"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.tex_difference_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const unitex1 = new CGL.Uniform(shader, "t", "tex1", 1); +const unitex2 = new CGL.Uniform(shader, "t", "tex2", 2); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + if (texture1.get() && texture2.get()) + { + let texture = cgl.currentTextureEffect.getCurrentSourceTexture(); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, texture.tex); + cgl.setTexture(1, texture1.get().tex); + cgl.setTexture(2, texture2.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + } + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.TextureDifference.prototype = new CABLES.Op(); +CABLES.OPS["e8f01999-ad25-4a75-9cac-46468551ba67"]={f:Ops.Gl.TextureEffects.TextureDifference,objName:"Ops.Gl.TextureEffects.TextureDifference"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ToNormalMap_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.ToNormalMap_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"tonormal_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI vec2 size;\nUNI float strength;\nUNI float sizeMul;\n\nvoid main()\n{\n float tl = abs(texture(tex, texCoord + (size*sizeMul) * vec2(-1.0, -1.0)).x); // top left\n float l = abs(texture(tex, texCoord + (size*sizeMul) * vec2(-1.0, 0.0)).x); // left\n float bl = abs(texture(tex, texCoord + (size*sizeMul) * vec2(-1.0, 1.0)).x); // bottom left\n float t = abs(texture(tex, texCoord + (size*sizeMul) * vec2( 0.0, -1.0)).x); // top\n float b = abs(texture(tex, texCoord + (size*sizeMul) * vec2( 0.0, 1.0)).x); // bottom\n float tr = abs(texture(tex, texCoord + (size*sizeMul) * vec2( 1.0, -1.0)).x); // top right\n float r = abs(texture(tex, texCoord + (size*sizeMul) * vec2( 1.0, 0.0)).x); // right\n float br = abs(texture(tex, texCoord + (size*sizeMul) * vec2( 1.0, 1.0)).x); // bottom right\n\n // Compute dx using Sobel:\n // -1 0 1\n // -2 0 2\n // -1 0 1\n float dX = tr + 2.0*r + br -tl - 2.0*l - bl;\n\n // Compute dy using Sobel:\n // -1 -2 -1\n // 0 0 0\n // 1 2 1\n float dY = bl + 2.0*b + br -tl - 2.0*t - tr;\n\n // Build the normalized normal\n vec4 N = vec4(normalize(vec3(dX,dY, 1.0 / strength)), 1.0);\n N= N * 0.5 + 0.5;\n\n outColor= N;\n}",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + strength = op.inValue("Strength", 4), + sizeMul = op.inValue("Step Multiplier", 1); + +const + cgl = op.patch.cgl, + shader = new CGL.Shader(cgl, op.name); + +// from: https://forum.openframeworks.cc/t/compute-normal-map-from-image/1400/11 +shader.setSource(shader.getDefaultVertexShader(), attachments.tonormal_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + uniStrength = new CGL.Uniform(shader, "f", "strength", strength), + unisizeMul = new CGL.Uniform(shader, "f", "sizeMul", sizeMul), + uniSize = new CGL.Uniform(shader, "2f", "size", 0,0); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + const effectTex=cgl.currentTextureEffect.getCurrentSourceTexture(); + + cgl.setTexture(0, effectTex.tex); + + uniSize.setValue([1/effectTex.width,1/effectTex.height]); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.ToNormalMap_v2.prototype = new CABLES.Op(); +CABLES.OPS["5dfb2856-b589-4bd9-8f4e-b518da115d11"]={f:Ops.Gl.TextureEffects.ToNormalMap_v2,objName:"Ops.Gl.TextureEffects.ToNormalMap_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Twirl_v4 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Twirl_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"twirl_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float twistAmount;\nUNI float times;\nUNI float radius;\nUNI float centerX;\nUNI float centerY;\nUNI float aspect;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec2 center=vec2(centerX,centerY);\n center =((center+1.0)/2.0);\n vec2 tc = texCoord;\n tc -= center;\n\n float dist = length(vec2(tc.x,tc.y/aspect));\n if (dist < radius)\n {\n float percent = (radius - dist) / radius;\n float theta = percent * percent * twistAmount * 8.0;\n float s = sin(theta);\n float c = cos(theta);\n tc = vec2(dot(tc, vec2(c, -s)), dot(tc, vec2(s, c)));\n }\n tc += center;\n\n vec4 col = texture(tex, tc);\n vec4 base=texture(tex,texCoord);\n outColor=cgl_blendPixel(base,col,amount);\n}\n",}; +const render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + twistAmount = op.inValue("Twist amount", 500), + radius = op.inValue("Radius", 0.5), + centerX = op.inValue("Center X", 0), + centerY = op.inValue("Center Y", 0), + trigger = op.outTrigger("Next"); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.twirl_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniTwistAmount = new CGL.Uniform(shader, "f", "twistAmount", 1), + uniRadius = new CGL.Uniform(shader, "f", "radius", radius), + uniAspect = new CGL.Uniform(shader, "f", "aspect", 1), + unicenterX = new CGL.Uniform(shader, "f", "centerX", centerX), + unicenterY = new CGL.Uniform(shader, "f", "centerY", centerY); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + let texture = cgl.currentTextureEffect.getCurrentSourceTexture(); + + uniTwistAmount.setValue(twistAmount.get() * (1 / texture.width)); + uniAspect.setValue(cgl.currentTextureEffect.aspectRatio); + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Twirl_v4.prototype = new CABLES.Op(); +CABLES.OPS["6691bf7f-602d-4a24-b648-cab4b2a2c15e"]={f:Ops.Gl.TextureEffects.Twirl_v4,objName:"Ops.Gl.TextureEffects.Twirl_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Vibrance +// +// ************************************************************** + +Ops.Gl.TextureEffects.Vibrance = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"vibrance_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\n\nconst vec4 lumcoeff = vec4(0.299,0.587,0.114, 0.);\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n col=texture(tex,texCoord);\n\n float luma = dot(col, lumcoeff);\n vec4 mask = (col - vec4(luma));\n mask = clamp(mask, 0.0, 1.0);\n float lumaMask = dot(lumcoeff, mask);\n lumaMask = 1.0 - lumaMask;\n vec4 vibrance = mix(vec4(luma), col, 1.0 + amount * lumaMask);\n outColor= vibrance;\n}",}; +const render = op.inTrigger("Render"); +const trigger = op.outTrigger("Trigger"); +const amount = op.inValue("amount", 2); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.vibrance_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Vibrance.prototype = new CABLES.Op(); +CABLES.OPS["9c71c980-e439-4397-9c2b-c2ae085eaed9"]={f:Ops.Gl.TextureEffects.Vibrance,objName:"Ops.Gl.TextureEffects.Vibrance"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Vignette_v3 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Vignette_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"vignette_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float lensRadius1;\nUNI float aspect;\nUNI float amount;\nUNI float strength;\nUNI float sharp;\n\nUNI vec3 vcol;\n\n{{CGL.BLENDMODES3}}\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n vec4 vvcol=vec4(vcol,1.0);\n vec4 col=texture(tex,texCoord);\n vec2 tcPos=vec2(texCoord.x,(texCoord.y-0.5)*aspect+0.5);\n float dist = distance(tcPos, vec2(0.5,0.5));\n float am = (1.0-smoothstep( (lensRadius1+0.5), (lensRadius1*0.99+0.5)*sharp, dist));\n\n col=mix(col,vvcol,am*strength);\n\n #ifndef ALPHA\n outColor=cgl_blendPixel(base,col,amount);\n #endif\n\n #ifdef ALPHA\n outColor=vec4(base.rgb,base.a*(1.0-am*strength));\n #endif\n}\n",}; +const + render = op.inTrigger("Render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + amount = op.inValueSlider("Amount", 1), + trigger = op.outTrigger("Trigger"), + strength = op.inValueSlider("Strength", 1), + lensRadius1 = op.inValueSlider("Radius", 0.3), + sharp = op.inValueSlider("Sharp", 0.25), + aspect = op.inValue("Aspect", 1), + r = op.inValueSlider("r", 0), + g = op.inValueSlider("g", 0), + b = op.inValueSlider("b", 0), + alpha = op.inBool("Alpha", false); + +r.setUiAttribs({ "colorPick": true }); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "vignette"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.vignette_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniLensRadius1 = new CGL.Uniform(shader, "f", "lensRadius1", lensRadius1), + uniaspect = new CGL.Uniform(shader, "f", "aspect", aspect), + unistrength = new CGL.Uniform(shader, "f", "strength", strength), + unisharp = new CGL.Uniform(shader, "f", "sharp", sharp), + unir = new CGL.Uniform(shader, "3f", "vcol", r, g, b); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +alpha.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("ALPHA", alpha.get()); + + r.setUiAttribs({ "greyout": alpha.get() }); + g.setUiAttribs({ "greyout": alpha.get() }); + b.setUiAttribs({ "greyout": alpha.get() }); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Vignette_v3.prototype = new CABLES.Op(); +CABLES.OPS["588302cb-f5a7-4129-90d2-ba66212d69e5"]={f:Ops.Gl.TextureEffects.Vignette_v3,objName:"Ops.Gl.TextureEffects.Vignette_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.WaveformGradient_v4 +// +// ************************************************************** + +Ops.Gl.TextureEffects.WaveformGradient_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"waveform_v2_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float uFreq;\nUNI float uOffset;\nUNI float uPow;\nUNI float uRotate;\nUNI float amount;\n\nUNI float r;\nUNI float g;\nUNI float b;\n\n{{CGL.BLENDMODES3}}\n\n#define PI 3.14159265359\n#define TAU (2.0 * PI)\n\nvoid pR(inout vec2 p, float a)\n{\n float s = sin(a),c=cos(a); p *= mat2(c,s,-s,c);\n}\n\nfloat pModMirror1(inout float p, float size) {\n\tfloat halfsize = size * 0.5;\n\tfloat c = floor((p + halfsize)/size);\n\tp = mod(p + halfsize,size) - halfsize;\n\tp *= mod(c, 2.0) * 2.0 - 1.0;\n\treturn c;\n}\n\nvoid main()\n{\n vec2 uv = texCoord;\n float v = 0.0;\n\n uv -= 0.5;\n pR(uv,TAU * uRotate);\n uv += 0.5 + uOffset;\n\n uv.x *= uFreq;\n\n #ifdef MODE_SINE\n uv.x += 0.5;\n pModMirror1(uv.x,1.0);\n v = pow(cos(PI * uv.x / 2.0),uPow);\n #endif\n\n #ifdef MODE_SAW\n uv.x = mod(uv.x,1.0);\n v = pow(min(cos(PI * uv.x /2.0),1.0 - abs(uv.x)),uPow);\n #endif\n\n #ifdef MODE_TRI\n uv.x += 0.5;\n pModMirror1(uv.x,1.0);\n uv.x = -abs(uv.x);\n uv.x = fract(uv.x);\n v = pow(uv.x,uPow);\n #endif\n\n #ifdef MODE_SQR\n pModMirror1(uv.x,1.0);\n uv.x = -abs(uv.x);\n uv.x = fract(uv.x);\n v = step(uv.x,uPow);\n #endif\n\n vec4 col = vec4(vec3(v*r,v*g,v*b),1.0);\n vec4 base = texture(tex,texCoord);\n\n outColor = cgl_blendPixel(base,col,amount);\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + amount = op.inValueSlider("Amount", 1), + mode = op.inValueSelect("Mode", ["Sine", "Sawtooth", "Triangle", "Square"], "Sine"), + freq = op.inValue("Frequency", 4), + pow = op.inValue("Pow factor", 6), + offset = op.inValue("Offset", 0), + rotate = op.inFloatSlider("Rotate", 0), + r = op.inValueSlider("r", 1.0), + g = op.inValueSlider("g", 1.0), + b = op.inValueSlider("b", 1.0), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Waveform", [mode, freq, pow, offset, rotate]); +op.setPortGroup("Color", [r, g, b]); +r.setUiAttribs({ "colorPick": true }); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.waveform_v2_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + freqUniform = new CGL.Uniform(shader, "f", "uFreq", freq), + offsetUniform = new CGL.Uniform(shader, "f", "uOffset", offset), + powUniform = new CGL.Uniform(shader, "f", "uPow", pow), + rotateUniform = new CGL.Uniform(shader, "f", "uRotate", rotate), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniformR = new CGL.Uniform(shader, "f", "r", r), + uniformG = new CGL.Uniform(shader, "f", "g", g), + uniformB = new CGL.Uniform(shader, "f", "b", b); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount,maskAlpha); +mode.onChange = updateMode; +updateMode(); + + +function updateMode() +{ + shader.toggleDefine("MODE_SAW",mode.get() == "Sawtooth"); + shader.toggleDefine("MODE_SINE",mode.get() == "Sine"); + shader.toggleDefine("MODE_TRI",mode.get() == "Triangle"); + shader.toggleDefine("MODE_SQR",mode.get() == "Square"); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op,3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.WaveformGradient_v4.prototype = new CABLES.Op(); +CABLES.OPS["0f9561ce-ea3c-4630-9990-9dae77893517"]={f:Ops.Gl.TextureEffects.WaveformGradient_v4,objName:"Ops.Gl.TextureEffects.WaveformGradient_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Waveform_v3 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Waveform_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"wave_v2_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D tex1;\nUNI float amount;\n\nUNI float r;\nUNI float g;\nUNI float b;\n\nUNI float uAmp;\nUNI float uFreq;\nUNI float uWidth;\nUNI float uGlow;\nUNI float uWaveSelect;\nUNI bool uInvert;\nUNI bool uSolid;\n\nUNI float uOffSetX;\nUNI float uOffSetY;\nUNI float uRotate;\n\n{{CGL.BLENDMODES3}}\n\n#define PI 3.14159265359\n#define TAU (2.0*PI)\n\nfloat vmax(vec2 v)\n{\n\treturn max(v.x, v.y);\n}\n\nvoid pR(inout vec2 p, float a)\n{\n float s = sin(a),c=cos(a); p *= mat2(c,s,-s,c);\n}\n\nfloat pMod1(inout float p, float size)\n{\n\tfloat halfsize = size * 0.5;\n\tfloat c = floor((p + halfsize) / size);\n\tp = mod(p + halfsize, size) - halfsize;\n\treturn c;\n}\n\nfloat pModMirror1(inout float p, float size)\n{\n\tfloat halfsize = size * 0.5;\n\tfloat c = floor((p + halfsize) / size);\n\tp = mod(p + halfsize,size) - halfsize;\n\tp *= mod(c, 2.0) * 2.0 - 1.0;\n\treturn c;\n}\n\nfloat fCapsule2D(vec2 p, float r, float c)\n{\n\treturn mix(abs(p.x) - r, length(vec2(p.x, abs(p.y) - c)) - r, step(c, abs(p.y)));\n}\n\nfloat SineWave(vec2 p, float amplitude, float frequency, float line_width, float line_glow, bool solid)\n{\n float v = sin(p.x * frequency * PI);\n v *= amplitude;\n\n float d = 0.0;\n\n if (solid == false)\n {\n d = abs(v * amplitude - p.y * 0.5);\n d -= line_width;\n return smoothstep(0.0, line_glow, d);\n }\n else\n {\n d = v * amplitude - p.y * 0.5;\n d -= -line_width;\n return smoothstep(0.0, line_glow, -d);\n }\n}\n\nfloat SawWave(vec2 p, float amplitude, float frequency, float line_width, float line_glow, bool solid)\n{\n float inverse_frequency = 2.0 / frequency;\n vec2 p1 = p;\n pMod1(p1.x, inverse_frequency);\n\n float d1 = fCapsule2D(p1, 0.0, amplitude);\n p.x += inverse_frequency * 0.5;\n pMod1(p.x, inverse_frequency);\n pR(p, atan(inverse_frequency, amplitude * 2.0));\n\n float d = fCapsule2D(p, 0.0, 0.5 * length(vec2(inverse_frequency, 2.0 * amplitude)));\n\td = min(d, d1);\n d -= line_width;\n\n if(solid == false)\n {\n return smoothstep(0.0, line_glow, d);\n }\n else\n return smoothstep(0.0, line_glow, min(d,p.x));\n}\n\nfloat TriangleWave(vec2 p, float amplitude, float frequency, float line_width, float line_glow, bool solid)\n{\n float inverse_frequency = 1.0 / frequency;\n p.x -= inverse_frequency;\n pModMirror1(p.x, inverse_frequency);\n pR(p, atan(inverse_frequency, amplitude * 2.0));\n\n float d = fCapsule2D(p, 0.0, 0.5 * length(vec2(inverse_frequency, 2.0 * amplitude)));\n d -= line_width;\n\n if (solid == false)\n {\n return smoothstep(0.0, line_glow, d);\n }\n else\n return smoothstep(0.0, line_glow, min(d,p.x));\n}\n\nfloat SquareWave(vec2 p, float amplitude, float frequency, float line_width, float line_glow, bool solid)\n{\n float inverse_frequency = 0.5 / frequency;\n vec2 p1 = p;\n pMod1(p1.x, 2.0 * inverse_frequency);\n\n float d1 = fCapsule2D(p1, 0.0, abs(amplitude));\n p.x -= inverse_frequency * 0.5;\n float cell = pMod1(p.x, inverse_frequency);\n\n if(cell < 0.0) cell = -cell + 1.0;\n if(int(cell * 0.5) % 2 == 1) p.y -= amplitude;\n else p.y += amplitude;\n\n float d = fCapsule2D(p.yx, 0.0, abs(inverse_frequency));\n d = min(d, d1);\n d -= line_width;\n\n if (solid == false)\n {\n return smoothstep(0.0, line_glow, d);\n }\n else\n return smoothstep(0.0, line_glow, p.y);\n}\n\nvoid main()\n{\n vec4 rgb = vec4(r,g,b,1.0);\n\tvec2 uv = texCoord;\n\n uv -= 0.5;\n pR(uv.xy,uRotate * TAU);\n uv += 0.5;\n\n float wave = 0.0;\n #ifdef SINE\n wave = 1.0 - SineWave (uv - vec2(uOffSetX,uOffSetY), uAmp, uFreq , uWidth, uGlow, uSolid);\n #endif\n #ifdef SAWTOOTH\n wave = 1.0 - SawWave (uv - vec2(uOffSetX,uOffSetY), uAmp, uFreq, uWidth, uGlow, uSolid);\n #endif\n #ifdef TRIANGLE\n wave = 1.0 - TriangleWave (uv - vec2(uOffSetX,uOffSetY), uAmp, uFreq, uWidth, uGlow, uSolid);\n #endif\n #ifdef SQUARE\n wave = 1.0 - SquareWave (uv - vec2(uOffSetX,uOffSetY), uAmp, uFreq, uWidth, uGlow, uSolid);\n #endif\n\n #ifdef INVERT\n wave = 1.0-wave;\n #endif\n\n vec4 col = vec4(r,g,b,1.0);\n\n vec4 base=texture(tex,texCoord);\n\n outColor=cgl_blendPixel(base,col,wave*amount);\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + maskAlpha = CGL.TextureEffect.AddBlendAlphaMask(op), + waveSelect = op.inValueSelect("Waveform", ["Sine", "Sawtooth", "Triangle", "Square"], "Sine"), + amplitude = op.inValueSlider("Amplitude", 0.5), + frequency = op.inFloat("Frequency", 2.0), + lineWidth = op.inValueSlider("Line Width", 0.1), + lineGlow = op.inValueSlider("Line Glow", 0.1), + invertCol = op.inValueBool("invert color", false), + solidFill = op.inValueBool("Solid fill", false), + offsetX = op.inValueSlider("Offset X", 0.0), + offsetY = op.inValueSlider("Offset Y", 0.5), + rotate = op.inValueSlider("Rotate", 0.0), + r = op.inValueSlider("r", 1.0), + g = op.inValueSlider("g", 1.0), + b = op.inValueSlider("b", 1.0); + +const trigger = op.outTrigger("trigger"); + +r.setUiAttribs({ "colorPick": true }); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.wave_v2_frag); + +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + amountUniform = new CGL.Uniform(shader, "f", "amount", amount), + uniformR = new CGL.Uniform(shader, "f", "r", r), + uniformG = new CGL.Uniform(shader, "f", "g", g), + uniformB = new CGL.Uniform(shader, "f", "b", b), + amplitudeUniform = new CGL.Uniform(shader, "f", "uAmp", amplitude), + frequencyUniform = new CGL.Uniform(shader, "f", "uFreq", frequency), + lineWidthUniform = new CGL.Uniform(shader, "f", "uWidth", lineWidth), + lineGlowUniform = new CGL.Uniform(shader, "f", "uGlow", lineGlow), + waveSelectUniform = new CGL.Uniform(shader, "f", "uWaveSelect", 1), + invertUniform = new CGL.Uniform(shader, "b", "uInvert", invertCol), + solidFillUniform = new CGL.Uniform(shader, "b", "uSolid", solidFill), + offSetXUniform = new CGL.Uniform(shader, "f", "uOffSetX", offsetX), + offSetYUniform = new CGL.Uniform(shader, "f", "uOffSetY", offsetY), + rotateUniform = new CGL.Uniform(shader, "f", "uRotate", rotate); + +waveSelect.onChange = invertCol.onChange = updateDefines; +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("SINE", waveSelect.get() == "Sine"); + shader.toggleDefine("SAWTOOTH", waveSelect.get() == "Sawtooth"); + shader.toggleDefine("TRIANGLE", waveSelect.get() == "Triangle"); + shader.toggleDefine("SQUARE", waveSelect.get() == "Square"); + shader.toggleDefine("INVERT", invertCol.get()); +} + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount, maskAlpha); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Waveform_v3.prototype = new CABLES.Op(); +CABLES.OPS["fce7d8bc-5a2e-49b8-8f55-8ab8784493d5"]={f:Ops.Gl.TextureEffects.Waveform_v3,objName:"Ops.Gl.TextureEffects.Waveform_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Wobble_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.Wobble_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"wobble_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D texMask;\nUNI float time;\nUNI float speedX;\nUNI float speedY;\nUNI float repeatX;\nUNI float repeatY;\nUNI float mul;\n\n{{CGL.LUMINANCE}}\n\nvoid main()\n{\n float mult=1.0;\n #ifdef HAS_MASK\n #ifdef MASK_SRC_R\n mult*=texture(texMask,texCoord).r;\n #endif\n #ifdef MASK_SRC_G\n mult*=texture(texMask,texCoord).g;\n #endif\n #ifdef MASK_SRC_B\n mult*=texture(texMask,texCoord).b;\n #endif\n #ifdef MASK_SRC_A\n mult*=texture(texMask,texCoord).a;\n #endif\n #ifdef MASK_SRC_LUM\n mult*=cgl_luminance(texture(texMask,texCoord).rgb);\n #endif\n #ifdef MASK_INV\n mult=1.0-mult;\n #endif\n #endif\n\n mult*=mul;\n\n vec2 tc = texCoord + cos( (time*vec2(speedX, speedY) + vec2(texCoord.s*repeatX,texCoord.t*repeatY)))*mult;\n vec4 col=texture(tex,tc);\n\n outColor= col;\n}",}; +const + render = op.inTrigger("Render"), + time = op.inValue("time", 0), + speedX = op.inValue("SpeedX", 4), + speedY = op.inValue("SpeedY", 8), + + repeatX = op.inValue("RepeatX", 11), + repeatY = op.inValue("RepeatY", 11), + mul = op.inValue("Multiply", 0.01), + + inMaskTex = op.inTexture("Amount Map"), + inMaskSource = op.inSwitch("Source Amount Map", ["R", "G", "B", "A", "Lum"], "R"), + inMaskInv = op.inBool("Invert Amount Map", false), + + trigger = op.outTrigger("Trigger"); + +op.setPortGroup("Amount Map", [inMaskTex, inMaskSource, inMaskInv]); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.wobble_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + timeUniform = new CGL.Uniform(shader, "f", "time", time), + speedXUniform = new CGL.Uniform(shader, "f", "speedX", speedX), + speedYUniform = new CGL.Uniform(shader, "f", "speedY", speedY), + repeatXUniform = new CGL.Uniform(shader, "f", "repeatX", repeatX), + repeatYUniform = new CGL.Uniform(shader, "f", "repeatY", repeatY), + mulUniform = new CGL.Uniform(shader, "f", "mul", mul), + maskUniform = new CGL.Uniform(shader, "t", "texMask", 1); + +inMaskTex.onChange = +inMaskSource.onChange = +inMaskInv.onChange = () => +{ + shader.toggleDefine("HAS_MASK", inMaskTex.isLinked()); + shader.toggleDefine("MASK_SRC_R", inMaskSource.get() == "R"); + shader.toggleDefine("MASK_SRC_G", inMaskSource.get() == "G"); + shader.toggleDefine("MASK_SRC_B", inMaskSource.get() == "B"); + shader.toggleDefine("MASK_SRC_A", inMaskSource.get() == "A"); + shader.toggleDefine("MASK_SRC_LUM", inMaskSource.get() == "Lum"); + shader.toggleDefine("MASK_INV", inMaskInv.get()); + inMaskSource.setUiAttribs({ "greyout": !inMaskTex.isLinked() }); + inMaskInv.setUiAttribs({ "greyout": !inMaskTex.isLinked() }); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + if (inMaskTex.get()) cgl.setTexture(1, inMaskTex.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Wobble_v2.prototype = new CABLES.Op(); +CABLES.OPS["04a8a8bc-feb9-4791-8724-ee24bbb150db"]={f:Ops.Gl.TextureEffects.Wobble_v2,objName:"Ops.Gl.TextureEffects.Wobble_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ZoomBlur_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.ZoomBlur_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"zoomblur_frag":"UNI sampler2D tex;\nUNI float x;\nUNI float y;\nUNI float strength;\nIN vec2 texCoord;\n\n#ifdef HAS_MASK\n UNI sampler2D texMask;\n#endif\n\nfloat random(vec3 scale, float seed)\n{\n return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\n}\n\n#ifdef MASK_SRC_LUM\n {{CGL.LUMINANCE}}\n#endif\n\nvoid main()\n{\n float total = 0.0;\n vec4 color = vec4(0.0);\n vec2 center=vec2(x,y);\n center=(center/2.0)+0.5;\n\n vec2 texSize=vec2(1.0,1.0);\n vec2 toCenter = center - texCoord * texSize;\n\n /* randomize the lookup values to hide the fixed number of samples */\n float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);\n float am = strength;\n\n #ifdef HAS_MASK\n\n float mul=1.0;\n #ifdef MASK_SRC_R\n mul=texture(texMask,texCoord).r;\n #endif\n #ifdef MASK_SRC_G\n mul=texture(texMask,texCoord).g;\n #endif\n #ifdef MASK_SRC_B\n mul=texture(texMask,texCoord).b;\n #endif\n #ifdef MASK_SRC_A\n mul=texture(texMask,texCoord).a;\n #endif\n #ifdef MASK_SRC_LUM\n mul=cgl_luminance(texture(texMask,texCoord).rgb);\n #endif\n\n #ifdef MASK_INV\n mul=1.0-mul;\n #endif\n\n am=am*mul;\n\n if(am<=0.02)\n {\n outColor=texture(tex, texCoord);\n return;\n }\n #endif\n\n for (float t = 0.0; t <= 40.0; t++)\n {\n float percent = (t + offset) / 40.0;\n float weight = 4.0 * (percent - percent * percent);\n vec4 smpl = texture(tex, texCoord + toCenter * percent * am / texSize);\n\n smpl.rgb *= smpl.a;\n\n color += smpl * weight;\n total += weight;\n }\n\n outColor = color / total;\n}",}; +const + render = op.inTrigger("render"), + strength = op.inValueSlider("Strength", 0.5), + x = op.inValue("X", 0), + y = op.inValue("Y", 0), + inMaskTex = op.inTexture("Strength Map"), + inMaskSource = op.inSwitch("Source Strength Map", ["R", "G", "B", "A", "Lum"], "R"), + inMaskInv = op.inBool("Invert Strength Map", false), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Strengh Map", [inMaskTex, inMaskSource, inMaskInv]); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "zoomblur"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.zoomblur_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureMask = new CGL.Uniform(shader, "t", "texMask", 1), + uniX = new CGL.Uniform(shader, "f", "x", x), + uniY = new CGL.Uniform(shader, "f", "y", y), + strengthUniform = new CGL.Uniform(shader, "f", "strength", strength); + +inMaskSource.onChange = + inMaskInv.onChange = + inMaskTex.onChange = updateDefines; + +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("HAS_MASK", inMaskTex.isLinked()); + + shader.toggleDefine("MASK_SRC_R", inMaskSource.get() == "R"); + shader.toggleDefine("MASK_SRC_G", inMaskSource.get() == "G"); + shader.toggleDefine("MASK_SRC_B", inMaskSource.get() == "B"); + shader.toggleDefine("MASK_SRC_A", inMaskSource.get() == "A"); + shader.toggleDefine("MASK_SRC_LUM", inMaskSource.get() == "Lum"); + + shader.toggleDefine("MASK_INV", inMaskInv.get()); + + inMaskSource.setUiAttribs({ "greyout": !inMaskTex.isLinked() }); + inMaskInv.setUiAttribs({ "greyout": !inMaskTex.isLinked() }); +} + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op, 3)) return; + + if (strength.get() > 0) + { + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + if (inMaskTex.get() && inMaskTex.get().tex) cgl.setTexture(1, inMaskTex.get().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + } + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.ZoomBlur_v2.prototype = new CABLES.Op(); +CABLES.OPS["b720a2f5-5501-48ef-90de-94a280ba6fbd"]={f:Ops.Gl.TextureEffects.ZoomBlur_v2,objName:"Ops.Gl.TextureEffects.ZoomBlur_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureFromColorArray +// +// ************************************************************** + +Ops.Gl.TextureFromColorArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExe = op.inTrigger("Update"), + inArr = op.inArray("array"), + inWidth = op.inValueInt("width", 32), + inHeight = op.inValueInt("height", 32), + tfilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"], "nearest"), + wrap = op.inValueSelect("Wrap", ["repeat", "mirrored repeat", "clamp to edge"], "repeat"), + outNext = op.outTrigger("Next"), + outTex = op.outTexture("Texture out"); + +inExe.onTriggered = update; + +const cgl = op.patch.cgl; +let tex = new CGL.Texture(cgl); + +let arrayResized = true; +let pixels = new Uint8Array(8); + +let cgl_filter = CGL.Texture.FILTER_NEAREST; +let cgl_wrap = CGL.Texture.WRAP_REPEAT; + +tfilter.onChange = + wrap.onChange = + inWidth.onChange = + inHeight.onChange = function () + { + if (tex)tex.delete(); + tex = null; + arrayResized = true; + + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + else if (tfilter.get() == "Anisotropic") cgl_filter = CGL.Texture.FILTER_ANISOTROPIC; + + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + else if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + else if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + }; + +const emptyTex = CGL.Texture.getEmptyTexture(cgl); + +function update() +{ + let error = false; + let w = inWidth.get(); + let h = inHeight.get(); + let data = inArr.get(); + + if (w <= 0 || h <= 0 || !data) error = true; + + if (error) + { + outTex.set(emptyTex); + return; + } + + if (arrayResized) + { + pixels = new Uint8Array(w * h * 4); + arrayResized = false; + } + let i = 0; + + for (i = 0; i < data.length; i++) + { + pixels[i] = data[i] * 255; + } + for (i = data.length; i < w * h * 4; i++) + { + pixels[i] = 255; + } + + if (!tex)tex = new CGL.Texture(cgl); + + tex.initFromData(pixels, w, h, cgl_filter, cgl_wrap); + + outTex.set(tex); + + outNext.trigger(); +} + + +}; + +Ops.Gl.TextureFromColorArray.prototype = new CABLES.Op(); +CABLES.OPS["7984c105-5b33-471a-833c-0fb310916e30"]={f:Ops.Gl.TextureFromColorArray,objName:"Ops.Gl.TextureFromColorArray"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureToPointArray3 +// +// ************************************************************** + +Ops.Gl.TextureToPointArray3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + cgl = op.patch.cgl, + pUpdate = op.inTrigger("update"), + inCenter = op.inBool("Center", true), + inThresh = op.inFloatSlider("Threshold Remove", 0), + inMulZ = op.inFloat("Z Multiply", 1), + tex = op.inObject("texture"), + inWidth = op.inFloat("Width", 2), + inHeight = op.inFloat("Height", 2), + outTrigger = op.outTrigger("trigger"), + outCoords = op.outArray("Points"), + outNum = op.outNumber("Total Points"), + outMinZ = op.outNumber("Min Z"), + outMaxZ = op.outNumber("Max Z"); + +op.setPortGroup("Size", [inWidth, inHeight]); + +let + fb = null, + pixelData = null, + coords = null, + texChanged = false; +tex.onChange = function () { texChanged = true; }; + +op.toWorkPortsNeedToBeLinked(tex, outCoords); + +let isFloatingPoint = false; +let channelType = op.patch.cgl.gl.UNSIGNED_BYTE; + +pUpdate.onTriggered = function () +{ + let realTexture = tex.get(), gl = cgl.gl; + + if (!realTexture) + { + outCoords.set(null); + outMaxZ.set(0); + outNum.set(0); + return; + } + if (!fb) fb = gl.createFramebuffer(); + if (!coords)coords = []; + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + if (texChanged) + { + gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, realTexture.tex, 0 + ); + + channelType = gl.UNSIGNED_BYTE; + + const size = realTexture.width * realTexture.height * 4; + + pixelData = new Uint8Array(size); + + texChanged = false; + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + gl.readPixels( + 0, 0, + realTexture.width, + realTexture.height, + gl.RGBA, + channelType, + pixelData + ); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + coords.length = 0; + + let offX = 0; + let offY = 0; + + let w = inWidth.get(); + let h = inHeight.get(); + const thresh = inThresh.get() * 256; + const mulz = inMulZ.get() / 256; + let maxZ = 0; + let minZ = 99999999; + + if (inCenter.get()) + { + offX = realTexture.width / 2; + offY = realTexture.height / 2; + } + + for (let x = 0; x < realTexture.width; x++) + { + for (let y = 0; y < realTexture.height; y++) + { + if ( + pixelData[(x + (y * realTexture.width)) * 4 + 0] >= thresh || + pixelData[(x + (y * realTexture.width)) * 4 + 1] >= thresh || + pixelData[(x + (y * realTexture.width)) * 4 + 2] >= thresh) + { + const zz = pixelData[(x + (y * realTexture.width)) * 4 + 0] * mulz; + coords.push((x - offX) / realTexture.width * w, (y - offY) / realTexture.height * h, + zz); + maxZ = Math.max(zz, maxZ); + minZ = Math.min(zz, minZ); + } + } + } + + outMinZ.set(minZ); + outMaxZ.set(maxZ); + outCoords.set(null); + outNum.set(coords.length / 3); + outCoords.set(coords); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + outTrigger.trigger(); +}; + + +}; + +Ops.Gl.TextureToPointArray3.prototype = new CABLES.Op(); +CABLES.OPS["93f03721-4b1e-4dee-aef9-7ef17c9cd6c1"]={f:Ops.Gl.TextureToPointArray3,objName:"Ops.Gl.TextureToPointArray3"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureToPoints +// +// ************************************************************** + +Ops.Gl.TextureToPoints = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + cgl = op.patch.cgl, + pUpdate = op.inTriggerButton("update"), + inNum = op.inValueInt("Num Points", 2000), + inSeed = op.inValueFloat("Seed", 1), + zPos = op.inSwitch("Z Position", ["None", "Red", "Green", "Blue", "Alpha"], "Red"), + zMultiply = op.inValueFloat("Z Multiply", 1.0), + tex = op.inObject("texture"), + outTrigger = op.outTrigger("trigger"), + outPoints = op.outArray("Points"), + outPointsNum = op.outValue("NumPoints"); + +let fb = null, + pixelData = null, + texChanged = false; + +op.toWorkPortsNeedToBeLinked(tex, outPoints); + +tex.onChange = function () { texChanged = true; }; + +let channelType = op.patch.cgl.gl.UNSIGNED_BYTE; +let points = []; + +pUpdate.onTriggered = updatePixels; + +const NUM_COL_CHANNELS = 4; + +function updatePixels() +{ + let realTexture = tex.get(), gl = cgl.gl; + + if (!realTexture) return; + if (!fb) fb = gl.createFramebuffer(); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + if (texChanged) + { + gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, realTexture.tex, 0 + ); + + pixelData = new Uint8Array(realTexture.width * realTexture.height * NUM_COL_CHANNELS); + texChanged = false; + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + gl.readPixels( + 0, 0, + realTexture.width, + realTexture.height, + gl.RGBA, + channelType, + pixelData + ); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + let num = inNum.get(); + let numPixels = pixelData.length; + + if (num * 3 != points.length)points.length = num * 3; + + Math.randomSeed = inSeed.get(); + + let pixelStepX = 1 / realTexture.width; + let pixelStepY = 1 / realTexture.height; + + let offsetX = pixelStepX * realTexture.width / 2; + let offsetY = pixelStepY * realTexture.height / 2; + + let ind = 0; + let count = 0; + + let colChanOffset = 0; + if (zPos.get() == "Green")colChanOffset = 1; + else if (zPos.get() == "Blue")colChanOffset = 2; + else if (zPos.get() == "Alpha")colChanOffset = 3; + else if (zPos.get() == "None")colChanOffset = 4; + + while (ind < num * 3) + { + count++; + if (count > num * 3 * 100) return; + let x = Math.floor(Math.seededRandom() * realTexture.width); + let y = Math.floor(Math.seededRandom() * realTexture.height); + let intens = pixelData[(x + (y * realTexture.width)) * NUM_COL_CHANNELS + colChanOffset]; + + if (intens > 10) + { + points[ind++] = ((x * pixelStepX) - (offsetX)); + points[ind++] = ((y * pixelStepY) - (offsetY)); + + if (colChanOffset < 4) points[ind++] = (intens / 255) * zMultiply.get(); + else points[ind++] = 0; + } + } + + outPointsNum.set(ind / 3); + outPoints.set(null); + outPoints.set(points); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + outTrigger.trigger(); +} + + +}; + +Ops.Gl.TextureToPoints.prototype = new CABLES.Op(); +CABLES.OPS["ff757f51-fbb0-4728-b158-644094cd160e"]={f:Ops.Gl.TextureToPoints,objName:"Ops.Gl.TextureToPoints"}; + + + + +// ************************************************************** +// +// Ops.Gl.Texture_v2 +// +// ************************************************************** + +Ops.Gl.Texture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + filename = op.inUrl("File", [".jpg", ".png", ".webp", ".jpeg", ".avif"]), + tfilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"]), + wrap = op.inValueSelect("Wrap", ["repeat", "mirrored repeat", "clamp to edge"], "clamp to edge"), + aniso = op.inSwitch("Anisotropic", [0, 1, 2, 4, 8, 16], 0), + flip = op.inValueBool("Flip", false), + unpackAlpha = op.inValueBool("Pre Multiplied Alpha", false), + active = op.inValueBool("Active", true), + inFreeMemory = op.inBool("Save Memory", true), + textureOut = op.outTexture("Texture"), + width = op.outNumber("Width"), + height = op.outNumber("Height"), + ratio = op.outNumber("Aspect Ratio"), + loaded = op.outNumber("Loaded", false), + loading = op.outNumber("Loading", false); + +op.setPortGroup("Size", [width, height]); + +unpackAlpha.setUiAttribs({ "hidePort": true }); + +op.toWorkPortsNeedToBeLinked(textureOut); + +const cgl = op.patch.cgl; + +let loadedFilename = null; +let loadingId = null; +let tex = null; +let cgl_filter = CGL.Texture.FILTER_MIPMAP; +let cgl_wrap = CGL.Texture.WRAP_REPEAT; +let cgl_aniso = 0; +let timedLoader = 0; + +filename.onChange = flip.onChange = function () { reloadSoon(); }; +aniso.onChange = tfilter.onChange = onFilterChange; +wrap.onChange = onWrapChange; +unpackAlpha.onChange = function () { reloadSoon(); }; + +tfilter.set("mipmap"); +wrap.set("repeat"); + +textureOut.set(CGL.Texture.getEmptyTexture(cgl)); + +active.onChange = function () +{ + if (active.get()) + { + if (loadedFilename != filename.get() || !tex) reloadSoon(); + else textureOut.set(tex); + } + else + { + textureOut.set(CGL.Texture.getEmptyTexture(cgl)); + width.set(CGL.Texture.getEmptyTexture(cgl).width); + height.set(CGL.Texture.getEmptyTexture(cgl).height); + if (tex)tex.delete(); + tex = null; + } +}; + +const setTempTexture = function () +{ + const t = CGL.Texture.getTempTexture(cgl); + textureOut.set(t); +}; + +function reloadSoon(nocache) +{ + clearTimeout(timedLoader); + timedLoader = setTimeout(function () + { + realReload(nocache); + }, 30); +} + +function realReload(nocache) +{ + if (!active.get()) return; + // if (filename.get() === null) return; + if (!loadingId)loadingId = cgl.patch.loading.start("textureOp", filename.get()); + + let url = op.patch.getFilePath(String(filename.get())); + + if (nocache)url += "?rnd=" + CABLES.uuid(); + + if (String(filename.get()).indexOf("data:") == 0) url = filename.get(); + + let needsRefresh = false; + if (loadedFilename != filename.get()) needsRefresh = true; + loadedFilename = filename.get(); + + if ((filename.get() && filename.get().length > 1)) + { + loaded.set(false); + loading.set(true); + + const fileToLoad = filename.get(); + + op.setUiAttrib({ "extendTitle": CABLES.basename(url) }); + if (needsRefresh) op.refreshParams(); + + cgl.patch.loading.addAssetLoadingTask(() => + { + op.setUiError("urlerror", null); + + CGL.Texture.load(cgl, url, + function (err, newTex) + { + if (filename.get() != fileToLoad) + { + cgl.patch.loading.finished(loadingId); + loadingId = null; + return; + } + + if (err) + { + setTempTexture(); + op.setUiError("urlerror", "could not load texture: \"" + filename.get() + "\"", 2); + cgl.patch.loading.finished(loadingId); + return; + } + + textureOut.set(newTex); + + width.set(newTex.width); + height.set(newTex.height); + ratio.set(newTex.width / newTex.height); + + // if (!newTex.isPowerOfTwo()) op.setUiError("npot", "Texture dimensions not power of two! - Texture filtering will not work in WebGL 1.", 0); + // else op.setUiError("npot", null); + + if (tex)tex.delete(); + tex = newTex; + textureOut.set(null); + textureOut.set(tex); + + loading.set(false); + loaded.set(true); + + if (inFreeMemory.get()) tex.image = null; + + cgl.patch.loading.finished(loadingId); + }, { + "anisotropic": cgl_aniso, + "wrap": cgl_wrap, + "flip": flip.get(), + "unpackAlpha": unpackAlpha.get(), + "filter": cgl_filter + }); + + // textureOut.set(null); + // textureOut.set(tex); + }); + } + else + { + cgl.patch.loading.finished(loadingId); + setTempTexture(); + } +} + +function onFilterChange() +{ + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + else if (tfilter.get() == "Anisotropic") cgl_filter = CGL.Texture.FILTER_ANISOTROPIC; + + cgl_aniso = parseFloat(aniso.get()); + + reloadSoon(); +} + +function onWrapChange() +{ + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + reloadSoon(); +} + +op.onFileChanged = function (fn) +{ + if (filename.get() && filename.get().indexOf(fn) > -1) + { + textureOut.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + textureOut.set(CGL.Texture.getTempTexture(cgl)); + realReload(true); + } +}; + + +}; + +Ops.Gl.Texture_v2.prototype = new CABLES.Op(); +CABLES.OPS["790f3702-9833-464e-8e37-6f0f813f7e16"]={f:Ops.Gl.Texture_v2,objName:"Ops.Gl.Texture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.Base64ToTexture +// +// ************************************************************** + +Ops.Gl.Textures.Base64ToTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + dataIn = op.inStringEditor("Base64 / Data URI", ""), + tfilter = op.inSwitch("filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inValueSelect("wrap", ["clamp to edge", "repeat", "mirrored repeat"], "clamp to edge"), + textureOut = op.outTexture("Texture"), + loadingOut = op.outBool("Loading"); + +const image = new Image(); + +let doUpdateTex = false; +let selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; +let selectedFilter = CGL.Texture.FILTER_LINEAR; + +function createTex() +{ + const tex = CGL.Texture.createFromImage(op.patch.cgl, image, + { + "filter": selectedFilter, + "wrap": selectedWrap + }); + textureOut.set(tex); + loadingOut.set(false); +} + +image.onload = function (e) +{ + op.patch.cgl.addNextFrameOnceCallback(createTex.bind(this)); +}; + +dataIn.onChange = () => +{ + updateTex(); +}; + +twrap.onChange = + tfilter.onChange = () => + { + if (tfilter.get() == "nearest") selectedFilter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") selectedFilter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") selectedFilter = CGL.Texture.FILTER_MIPMAP; + + if (twrap.get() == "repeat") selectedWrap = CGL.Texture.WRAP_REPEAT; + else if (twrap.get() == "mirrored repeat") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + else if (twrap.get() == "clamp to edge") selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + updateTex(); + }; + +function updateTex() +{ + loadingOut.set(true); + let data = dataIn.get(); + if (data && !data.startsWith("data:")) + { + data = "data:;base64," + data; + } + image.src = data; +} + + +}; + +Ops.Gl.Textures.Base64ToTexture.prototype = new CABLES.Op(); +CABLES.OPS["cd07e587-432a-4a81-a2b7-51273cf32171"]={f:Ops.Gl.Textures.Base64ToTexture,objName:"Ops.Gl.Textures.Base64ToTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.ColorTexture +// +// ************************************************************** + +Ops.Gl.Textures.ColorTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + a = op.inValueSlider("a", 1.0), + texOut = op.outTexture("texture_out"); + +r.setUiAttribs({ "colorPick": true }); +const cgl = op.patch.cgl; +let fb = null; + +r.onChange = + g.onChange = + b.onChange = + a.onChange = () => { cgl.addNextFrameOnceCallback(render); }; + +cgl.addNextFrameOnceCallback(render); + +function render() +{ + if (!fb) + { + if (cgl.glVersion == 1) fb = new CGL.Framebuffer(cgl, 8, 8, { "name": "colorTexture" }); + else fb = new CGL.Framebuffer2(cgl, 8, 8, { "name": "colorTexture", "depth": false }); + fb.setFilter(CGL.Texture.FILTER_MIPMAP); + } + + fb.renderStart(); + cgl.gl.clearColor(r.get(), g.get(), b.get(), a.get()); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT); + fb.renderEnd(); + texOut.set(fb.getTextureColor()); +} + + +}; + +Ops.Gl.Textures.ColorTexture.prototype = new CABLES.Op(); +CABLES.OPS["59b94270-0364-4c0f-a9fc-ba2561696a23"]={f:Ops.Gl.Textures.ColorTexture,objName:"Ops.Gl.Textures.ColorTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.CombineTextures +// +// ************************************************************** + +Ops.Gl.Textures.CombineTextures = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rgbe2fp_frag":"UNI sampler2D texR;\nUNI sampler2D texG;\nUNI sampler2D texB;\nUNI sampler2D texA;\nIN vec2 texCoord;\n\nUNI float defaultR;\nUNI float defaultG;\nUNI float defaultB;\nUNI float defaultA;\n\nvoid main()\n{\n float r=defaultR, g=defaultG, b=defaultB, a=defaultA;\n\n #ifdef HAS_R\n #ifdef R_SRC_R\n r=texture(texR,texCoord).r;\n #endif\n #ifdef R_SRC_G\n r=texture(texR,texCoord).g;\n #endif\n #ifdef R_SRC_B\n r=texture(texR,texCoord).b;\n #endif\n #ifdef R_SRC_A\n r=texture(texR,texCoord).a;\n #endif\n #endif\n\n #ifdef HAS_G\n #ifdef G_SRC_R\n g=texture(texG,texCoord).r;\n #endif\n #ifdef G_SRC_G\n g=texture(texG,texCoord).g;\n #endif\n #ifdef G_SRC_B\n g=texture(texG,texCoord).b;\n #endif\n #ifdef G_SRC_A\n g=texture(texG,texCoord).a;\n #endif\n #endif\n\n #ifdef HAS_B\n #ifdef B_SRC_R\n b=texture(texB,texCoord).r;\n #endif\n #ifdef B_SRC_G\n b=texture(texB,texCoord).g;\n #endif\n #ifdef B_SRC_B\n b=texture(texB,texCoord).b;\n #endif\n #ifdef B_SRC_A\n b=texture(texB,texCoord).a;\n #endif\n #endif\n\n #ifdef HAS_A\n #ifdef A_SRC_R\n a=texture(texA,texCoord).r;\n #endif\n #ifdef A_SRC_G\n a=texture(texA,texCoord).g;\n #endif\n #ifdef A_SRC_B\n a=texture(texA,texCoord).b;\n #endif\n #ifdef A_SRC_A\n a=texture(texA,texCoord).a;\n #endif\n #endif\n\n #ifdef INV_R\n r=1.0-r;\n #endif\n #ifdef INV_G\n g=1.0-g;\n #endif\n #ifdef INV_B\n b=1.0-b;\n #endif\n #ifdef INV_A\n a=1.0-a;\n #endif\n\n\n outColor = vec4(r,g,b,a);\n}\n\n\n",}; +const + exec = op.inTrigger("Execute"), + tfilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inValueSelect("Wrap", ["clamp to edge", "repeat", "mirrored repeat"], "repeat"), + inPixel = op.inDropDown("Pixel Format", CGL.Texture.PIXELFORMATS, CGL.Texture.PFORMATSTR_RGBA8UB), + + inTexR = op.inTexture("R"), + inSrcR = op.inSwitch("R Source", ["R", "G", "B", "A"], "R"), + inSrcRVal = op.inSwitch("R Value", ["Source", "Invert"], "Source"), + inSrcRDefault = op.inFloatSlider("R Default", 1), + inTexG = op.inTexture("G"), + inSrcG = op.inSwitch("G Source", ["R", "G", "B", "A"], "G"), + inSrcGVal = op.inSwitch("G Value", ["Source", "Invert"], "Source"), + inSrcGDefault = op.inFloatSlider("G Default", 1), + inTexB = op.inTexture("B"), + inSrcB = op.inSwitch("B Source", ["R", "G", "B", "A"], "B"), + inSrcBVal = op.inSwitch("B Value", ["Source", "Invert"], "Source"), + inSrcBDefault = op.inFloatSlider("B Default", 1), + inTexA = op.inTexture("A"), + inSrcA = op.inSwitch("A Source", ["R", "G", "B", "A"], "R"), + inSrcAVal = op.inSwitch("A Value", ["Source", "Invert"], "Source"), + inSrcADefault = op.inFloatSlider("A Default", 1), + + next = op.outTrigger("Next"), + outTex = op.outTexture("Texture"); + +op.setPortGroup("Red", [inSrcRDefault, inTexR, inSrcR, inSrcRVal]); +op.setPortGroup("Green", [inSrcGDefault, inTexG, inSrcG, inSrcGVal]); +op.setPortGroup("Blue", [inSrcBDefault, inTexB, inSrcB, inSrcBVal]); +op.setPortGroup("Alpha", [inSrcADefault, inTexA, inSrcA, inSrcAVal]); + +const cgl = op.patch.cgl; +let needsUpdate = true; +let tc = null; +let unitexR, unitexG, unitexB, unitexA, uniFloatR, uniFloatG, uniFloatB, uniFloatA; + +inSrcRDefault.onChange = + inSrcGDefault.onChange = + inSrcBDefault.onChange = + inSrcADefault.onChange = + inTexR.onChange = + inTexG.onChange = + inTexB.onChange = + inPixel.onChange = + inTexA.onChange = () => + { + needsUpdate = true; + }; + +inTexR.onLinkChanged = + inTexG.onLinkChanged = + inTexB.onLinkChanged = + inTexA.onLinkChanged = + inSrcR.onChange = + inSrcG.onChange = + inSrcB.onChange = + inSrcA.onChange = + inSrcRVal.onChange = + inSrcGVal.onChange = + inSrcBVal.onChange = + inSrcAVal.onChange = updateDefines; + +tfilter.onChange = + twrap.onChange = initShader; + +function initShader() +{ + let wrap = CGL.Texture.WRAP_REPEAT; + if (twrap.get() == "mirrored repeat") wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (twrap.get() == "clamp to edge") wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + let filter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "linear") filter = CGL.Texture.FILTER_LINEAR; + if (tfilter.get() == "mipmap") filter = CGL.Texture.FILTER_MIPMAP; + + if (tc)tc.dispose(); + tc = new CGL.CopyTexture(cgl, "combinetextures", + { + "shader": attachments.rgbe2fp_frag, + "isFloatingPointTexture": inPixel.get() == CGL.Texture.PFORMATSTR_RGBA32F, + "filter": filter, + "wrap": wrap + }); + + unitexR = new CGL.Uniform(tc.bgShader, "t", "texR", 0); + unitexG = new CGL.Uniform(tc.bgShader, "t", "texG", 1); + unitexB = new CGL.Uniform(tc.bgShader, "t", "texB", 2); + unitexA = new CGL.Uniform(tc.bgShader, "t", "texA", 3); + + uniFloatR = new CGL.Uniform(tc.bgShader, "f", "defaultR", inSrcRDefault); + uniFloatG = new CGL.Uniform(tc.bgShader, "f", "defaultG", inSrcGDefault); + uniFloatB = new CGL.Uniform(tc.bgShader, "f", "defaultB", inSrcBDefault); + uniFloatA = new CGL.Uniform(tc.bgShader, "f", "defaultA", inSrcADefault); + + updateDefines(); + needsUpdate = true; +} + +function updateDefines() +{ + if (!tc) return; + + inSrcR.setUiAttribs({ "greyout": !inTexR.isLinked() }); + inSrcG.setUiAttribs({ "greyout": !inTexG.isLinked() }); + inSrcB.setUiAttribs({ "greyout": !inTexB.isLinked() }); + inSrcA.setUiAttribs({ "greyout": !inTexA.isLinked() }); + + inSrcRVal.setUiAttribs({ "greyout": !inTexR.isLinked() }); + inSrcGVal.setUiAttribs({ "greyout": !inTexG.isLinked() }); + inSrcBVal.setUiAttribs({ "greyout": !inTexB.isLinked() }); + inSrcAVal.setUiAttribs({ "greyout": !inTexA.isLinked() }); + + inSrcRDefault.setUiAttribs({ "greyout": inTexR.isLinked() }); + inSrcGDefault.setUiAttribs({ "greyout": inTexG.isLinked() }); + inSrcBDefault.setUiAttribs({ "greyout": inTexB.isLinked() }); + inSrcADefault.setUiAttribs({ "greyout": inTexA.isLinked() }); + + tc.bgShader.toggleDefine("R_SRC_R", inSrcR.get() == "R"); + tc.bgShader.toggleDefine("R_SRC_G", inSrcR.get() == "G"); + tc.bgShader.toggleDefine("R_SRC_B", inSrcR.get() == "B"); + tc.bgShader.toggleDefine("R_SRC_A", inSrcR.get() == "A"); + + tc.bgShader.toggleDefine("G_SRC_R", inSrcG.get() == "R"); + tc.bgShader.toggleDefine("G_SRC_G", inSrcG.get() == "G"); + tc.bgShader.toggleDefine("G_SRC_B", inSrcG.get() == "B"); + tc.bgShader.toggleDefine("G_SRC_A", inSrcG.get() == "A"); + + tc.bgShader.toggleDefine("B_SRC_R", inSrcB.get() == "R"); + tc.bgShader.toggleDefine("B_SRC_G", inSrcB.get() == "G"); + tc.bgShader.toggleDefine("B_SRC_B", inSrcB.get() == "B"); + tc.bgShader.toggleDefine("B_SRC_A", inSrcB.get() == "A"); + + tc.bgShader.toggleDefine("A_SRC_R", inSrcA.get() == "R"); + tc.bgShader.toggleDefine("A_SRC_G", inSrcA.get() == "G"); + tc.bgShader.toggleDefine("A_SRC_B", inSrcA.get() == "B"); + tc.bgShader.toggleDefine("A_SRC_A", inSrcA.get() == "A"); + + tc.bgShader.toggleDefine("INV_R", inSrcRVal.get() == "Invert"); + tc.bgShader.toggleDefine("INV_G", inSrcGVal.get() == "Invert"); + tc.bgShader.toggleDefine("INV_B", inSrcBVal.get() == "Invert"); + tc.bgShader.toggleDefine("INV_A", inSrcAVal.get() == "Invert"); + + tc.bgShader.toggleDefine("HAS_R", inTexR.isLinked()); + tc.bgShader.toggleDefine("HAS_G", inTexG.isLinked()); + tc.bgShader.toggleDefine("HAS_B", inTexB.isLinked()); + tc.bgShader.toggleDefine("HAS_A", inTexA.isLinked()); + + needsUpdate = true; +} + +exec.onTriggered = () => +{ + if (needsUpdate && !op.patch.cgl.frameStore.shadowPass) + { + if (!tc)initShader(); + tc.bgShader.popTextures(); + + if (inTexR.get()) tc.bgShader.pushTexture(unitexR, inTexR.get().tex); + else tc.bgShader.pushTexture(unitexR, CGL.Texture.getEmptyTexture(cgl).tex); + if (inTexG.get()) tc.bgShader.pushTexture(unitexG, inTexG.get().tex); + else tc.bgShader.pushTexture(unitexG, CGL.Texture.getEmptyTexture(cgl).tex); + if (inTexB.get()) tc.bgShader.pushTexture(unitexB, inTexB.get().tex); + else tc.bgShader.pushTexture(unitexB, CGL.Texture.getEmptyTexture(cgl).tex); + if (inTexA.get()) tc.bgShader.pushTexture(unitexA, inTexA.get().tex); + else tc.bgShader.pushTexture(unitexA, CGL.Texture.getEmptyTexture(cgl).tex); + + uniFloatR.setValue(inSrcRDefault.get()); + uniFloatG.setValue(inSrcGDefault.get()); + uniFloatB.setValue(inSrcBDefault.get()); + uniFloatA.setValue(inSrcADefault.get()); + + outTex.set(CGL.Texture.getEmptyTexture(cgl)); + outTex.set(tc.copy(inTexR.get() || inTexG.get() || inTexB.get() || inTexA.get() || CGL.Texture.getEmptyTexture(cgl))); + + needsUpdate = false; + } + + next.trigger(); +}; + + +}; + +Ops.Gl.Textures.CombineTextures.prototype = new CABLES.Op(); +CABLES.OPS["5f33dd4a-a553-4f0f-b3b1-66a80cd240a7"]={f:Ops.Gl.Textures.CombineTextures,objName:"Ops.Gl.Textures.CombineTextures"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.CopyTexture +// +// ************************************************************** + +Ops.Gl.Textures.CopyTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"copytexture_frag":"UNI float a;\nUNI sampler2D tex;\n\n#ifdef TEX_MASK\nUNI sampler2D texMask;\n#endif\n\nIN vec2 texCoord;\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n\n #ifdef TEX_MASK\n col.a=texture(texMask,texCoord).r;\n #endif\n\n\n #ifdef GREY_R\n col.rgb=vec3(col.r);\n #endif\n\n #ifdef GREY_G\n col.rgb=vec3(col.g);\n #endif\n\n #ifdef GREY_B\n col.rgb=vec3(col.b);\n #endif\n\n #ifdef GREY_A\n col.rgb=vec3(col.a);\n #endif\n\n #ifdef GREY_LUMI\n col.rgb=vec3( dot(vec3(0.2126,0.7152,0.0722), col.rgb) );\n #endif\n\n\n #ifdef INVERT_A\n col.a=1.0-col.a;\n #endif\n\n #ifdef INVERT_R\n col.r=1.0-col.r;\n #endif\n\n #ifdef INVERT_G\n col.g=1.0-col.g;\n #endif\n\n #ifdef INVERT_B\n col.b=1.0-col.b;\n #endif\n\n #ifdef ALPHA_1\n col.a=1.0;\n #endif\n\n\n\n\n outColor= col;\n}",}; +const + render = op.inTriggerButton("render"), + inTexture = op.inTexture("Texture"), + inTextureMask = op.inTexture("Alpha Mask"), + useVPSize = op.inValueBool("use original size", true), + width = op.inValueInt("width", 640), + height = op.inValueInt("height", 360), + tfilter = op.inSwitch("filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inValueSelect("wrap", ["clamp to edge", "repeat", "mirrored repeat"], "clamp to edge"), + fpTexture = op.inValueBool("HDR"), + alphaMaskMethod = op.inSwitch("Alpha Mask Source", ["A", "1"], "A"), + greyscale = op.inSwitch("Convert Greyscale", ["Off", "R", "G", "B", "A", "Luminance"], "Off"), + invertR = op.inBool("Invert R", false), + invertG = op.inBool("Invert G", false), + invertB = op.inBool("Invert B", false), + invertA = op.inBool("Invert A", false), + + trigger = op.outTrigger("trigger"), + texOut = op.outTexture("texture_out", null), + outRatio = op.outNumber("Aspect Ratio"); + +alphaMaskMethod.setUiAttribs({ "hidePort": true }); +greyscale.setUiAttribs({ "hidePort": true }); +invertR.setUiAttribs({ "hidePort": true }); +invertG.setUiAttribs({ "hidePort": true }); +invertB.setUiAttribs({ "hidePort": true }); + +let autoRefreshTimeout = null; +const cgl = op.patch.cgl; +let lastTex = null; +let effect = null; +let tex = null; +let needsResUpdate = true; + +let w = 2, h = 2; +const prevViewPort = [0, 0, 0, 0]; +let reInitEffect = true; + +op.setPortGroup("Size", [useVPSize, width, height]); + +const bgShader = new CGL.Shader(cgl, "copytexture"); +bgShader.setSource(bgShader.getDefaultVertexShader(), attachments.copytexture_frag); +const textureUniform = new CGL.Uniform(bgShader, "t", "tex", 0); +let textureMaskUniform = new CGL.Uniform(bgShader, "t", "texMask", 1); + +let selectedFilter = CGL.Texture.FILTER_LINEAR; +let selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + +alphaMaskMethod.onChange = + greyscale.onChange = + invertR.onChange = + invertG.onChange = + invertB.onChange = + twrap.onChange = + tfilter.onChange = + fpTexture.onChange = + inTextureMask.onChange = updateSoon; + +render.onLinkChanged = +inTexture.onLinkChanged = +inTexture.onChange = () => +{ + // if (!inTexture.get() || inTexture.get() == CGL.Texture.getEmptyTexture(cgl)) texOut.set(CGL.Texture.getEmptyTexture(cgl)); + updateSoon(); +}; + +render.onTriggered = doRender; +updateSizePorts(); + +function initEffect() +{ + if (effect)effect.delete(); + if (tex) + { + tex.delete(); + tex = null; + } + + effect = new CGL.TextureEffect(cgl, { "isFloatingPointTexture": fpTexture.get(), "clear": false }); + + if (!tex || + tex.width != Math.floor(width.get()) || + tex.height != Math.floor(height.get()) || + tex.wrap != selectedWrap || + tex.isFloatingPoint() != fpTexture.get() + ) + { + if (tex) tex.delete(); + tex = new CGL.Texture(cgl, + { + "name": "copytexture_" + op.id, + "isFloatingPointTexture": fpTexture.get(), + "filter": selectedFilter, + "wrap": selectedWrap, + "width": Math.floor(width.get()), + "height": Math.floor(height.get()), + }); + } + + effect.setSourceTexture(tex); + texOut.set(null); + reInitEffect = false; +} + +function updateSoon() +{ + updateParams(); + if (render.links.length === 0) + { + reInitEffect = true; + + // clearTimeout(autoRefreshTimeout); + // autoRefreshTimeout = setTimeout(() => { doRender(); }, 100); + op.patch.cgl.off(autoRefreshTimeout); + autoRefreshTimeout = op.patch.cgl.on("beginFrame", () => + { + op.patch.cgl.off(autoRefreshTimeout); + + if (needsResUpdate)updateResolution(); + if (!effect)op.log("has no effect"); + if (!inTexture.get()) op.log("has no intexture"); + + doRender(); + }); + } +} + +function updateResolution() +{ + if (!inTexture.get() || inTexture.get() == CGL.Texture.getEmptyTexture(cgl)) return; + if (!effect)initEffect(); + + if (useVPSize.get()) + { + w = inTexture.get().width; + h = inTexture.get().height; + } + else + { + w = Math.floor(width.get()); + h = Math.floor(height.get()); + } + + if ((w != tex.width || h != tex.height) && (w !== 0 && h !== 0)) + { + height.set(h); + width.set(w); + tex.filter = selectedFilter; + tex.setSize(w, h); + outRatio.set(w / h); + effect.setSourceTexture(tex); + } + + if (texOut.get() && selectedFilter != CGL.Texture.FILTER_NEAREST) + { + if (!texOut.get().isPowerOfTwo()) op.setUiError("hintnpot", "texture dimensions not power of two! - texture filtering when scaling will not work on ios devices.", 0); + else op.setUiError("hintnpot", null, 0); + } + else op.setUiError("hintnpot", null, 0); + + needsResUpdate = false; +} + +function updateSizePorts() +{ + width.setUiAttribs({ "greyout": useVPSize.get() }); + height.setUiAttribs({ "greyout": useVPSize.get() }); +} + +function updateResolutionLater() +{ + needsResUpdate = true; + updateSoon(); +} + +useVPSize.onChange = function () +{ + updateSizePorts(); + if (useVPSize.get()) + { + width.onChange = null; + height.onChange = null; + } + else + { + width.onChange = updateResolutionLater; + height.onChange = updateResolutionLater; + } + updateResolution(); +}; + +function doRender() +{ + // op.patch.removeOnAnimCallback(doRender); + // if (!inTexture.get()) + + if (!inTexture.get() || inTexture.get() == CGL.Texture.getEmptyTexture(cgl)) texOut.set(CGL.Texture.getEmptyTexture(cgl)); + + if (!inTexture.get() || inTexture.get() == CGL.Texture.getEmptyTexture(cgl)) + { + lastTex = null;// CGL.Texture.getEmptyTexture(cgl); + trigger.trigger(); + return; + } + else + if (!effect || reInitEffect || lastTex != inTexture.get()) + { + initEffect(); + } + const vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + updateResolution(); + + lastTex = inTexture.get(); + const oldEffect = cgl.currentTextureEffect; + cgl.currentTextureEffect = effect; + effect.setSourceTexture(tex); + + effect.startEffect(); + + // render background color... + cgl.pushShader(bgShader); + cgl.currentTextureEffect.bind(); + cgl.setTexture(0, inTexture.get().tex); + if (inTextureMask.get())cgl.setTexture(1, inTextureMask.get().tex); + + cgl.pushBlend(false); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + cgl.popBlend(); + + texOut.set(effect.getCurrentSourceTexture()); + + effect.endEffect(); + + cgl.setViewPort(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); + + cgl.currentTextureEffect = oldEffect; + + cgl.setTexture(0, CGL.Texture.getEmptyTexture(cgl).tex); + + trigger.trigger(); +} + +function updateParams() +{ + bgShader.toggleDefine("TEX_MASK", inTextureMask.get()); + + bgShader.toggleDefine("GREY_R", greyscale.get() === "R"); + bgShader.toggleDefine("GREY_G", greyscale.get() === "G"); + bgShader.toggleDefine("GREY_B", greyscale.get() === "B"); + bgShader.toggleDefine("GREY_A", greyscale.get() === "A"); + bgShader.toggleDefine("GREY_LUMI", greyscale.get() === "Luminance"); + + bgShader.toggleDefine("ALPHA_1", alphaMaskMethod.get() === "1"); + bgShader.toggleDefine("ALPHA_A", alphaMaskMethod.get() === "A"); + + bgShader.toggleDefine("INVERT_R", invertR.get()); + bgShader.toggleDefine("INVERT_G", invertG.get()); + bgShader.toggleDefine("INVERT_B", invertB.get()); + bgShader.toggleDefine("INVERT_A", invertA.get()); + + if (twrap.get() == "repeat") selectedWrap = CGL.Texture.WRAP_REPEAT; + else if (twrap.get() == "mirrored repeat") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + else if (twrap.get() == "clamp to edge") selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + if (tfilter.get() == "nearest") selectedFilter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") selectedFilter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") selectedFilter = CGL.Texture.FILTER_MIPMAP; + + if (bgShader.needsRecompile()) + { + reInitEffect = true; + } + if (tex && ( + tex.width != Math.floor(width.get()) || + tex.height != Math.floor(height.get()) || + tex.wrap != selectedWrap || + tex.isFloatingPoint() != fpTexture.get() + )) + { + reInitEffect = true; + } +} + + +}; + +Ops.Gl.Textures.CopyTexture.prototype = new CABLES.Op(); +CABLES.OPS["18a6d1f4-a7f8-4a3e-ab1d-0c2d2efe3861"]={f:Ops.Gl.Textures.CopyTexture,objName:"Ops.Gl.Textures.CopyTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.EmptyTexture +// +// ************************************************************** + +Ops.Gl.Textures.EmptyTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const width = op.inValue("width", 8); +const height = op.inValue("height", 8); +const textureOut = op.outTexture("texture"); + +const cgl = op.patch.cgl; +const tex = new CGL.Texture(cgl); + +width.onChange = sizeChanged; +height.onChange = sizeChanged; + +sizeChanged(); + +function sizeChanged() +{ + tex.setSize(width.get(), height.get()); + textureOut.set(tex); +} + + +}; + +Ops.Gl.Textures.EmptyTexture.prototype = new CABLES.Op(); +CABLES.OPS["fc124913-0916-4f5c-83e0-702ddf66420c"]={f:Ops.Gl.Textures.EmptyTexture,objName:"Ops.Gl.Textures.EmptyTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.ExrTexture +// +// ************************************************************** + +Ops.Gl.Textures.ExrTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inFile = op.inUrl("EXR File", [".exr"]), + inAlpha = op.inBool("Remove Alpha", false), + inFilter = op.inSwitch("Filter", ["Nearest", "Linear"], "Nearest"), + outTex = op.outTexture("Texture"), + outWidth = op.outNumber("Width"), + outHeight = op.outNumber("Height"), + outChannels = op.outString("Channels"), + outLoading = op.outBool("Loading"); + +let + loadingId = null, + timedLoader = null, + finishedLoading = false; + +const cgl = op.patch.cgl; + +inFilter.onChange = +inAlpha.onChange = +inFile.onChange = reloadSoon; + +function reloadSoon(nocache) +{ + clearTimeout(timedLoader); + timedLoader = setTimeout(function () { loadBin(nocache); }, 30); +} + +function loadBin(addCacheBuster) +{ + // if (!inActive.get()) return; + + if (!loadingId)loadingId = op.patch.loading.start("gltf" + inFile.get(), inFile.get()); + + let url = op.patch.getFilePath(String(inFile.get())); + if (addCacheBuster)url += "?rnd=" + CABLES.generateUUID(); + finishedLoading = false; + outLoading.set(true); + const oReq = new XMLHttpRequest(); + oReq.open("GET", url, true); + oReq.responseType = "arraybuffer"; + + op.patch.loading.addAssetLoadingTask(() => + { + oReq.onload = (oEvent) => + { + const arrayBuffer = oReq.response; + const l = new CABLES.EXRLoader(); + + try + { + const p = l.parse(arrayBuffer); + outTex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + + if (p) + { + const arr = new Float32Array(p.data.length); + for (let i = 0; i < p.data.length; i++) + arr[i] = p.data[i]; + + if (inAlpha.get()) + for (let i = 3; i < arr.length; i += 4)arr[i] = 1; + + let channels = ""; + for (let i = 0; i < p.header.channels.length; i++) + channels += p.header.channels[i].name; + + outChannels.set(channels); + + let filter = CGL.Texture.FILTER_NEAREST; + if (inFilter.get() === "Linear")filter = CGL.Texture.FILTER_LINEAR; + const tex = new CGL.Texture(cgl, { + "filter": filter, + "wrap": filter, + "isFloatingPointTexture": true }); + + tex.initFromData(arr, p.width, p.height, filter, filter); + outTex.set(tex); + outWidth.set(p.width); + outHeight.set(p.height); + } + else + { + outWidth.set(0); + outHeight.set(0); + } + } + catch (e) + { + op.logError(e); + } + + cgl.patch.loading.finished(loadingId); + finishedLoading = true; + outLoading.set(false); + }; + + oReq.send(null); + }); +} + +op.onFileChanged = function (fn) +{ + if (inFile.get() && inFile.get().indexOf(fn) > -1) + { + outTex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + reloadSoon(true); + } +}; + + +}; + +Ops.Gl.Textures.ExrTexture.prototype = new CABLES.Op(); +CABLES.OPS["81a807c3-f814-4815-a43c-5914c223faf4"]={f:Ops.Gl.Textures.ExrTexture,objName:"Ops.Gl.Textures.ExrTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.GraphTexture +// +// ************************************************************** + +Ops.Gl.Textures.GraphTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + trigger = this.inTrigger("trigger"), + value = this.inValueFloat("value"), + index = this.inValueInt("index"), + inReset = this.inTriggerButton("reset"), + inShowMinMax = op.inValueBool("Show Min/Max"), + inSeed = op.inValueFloat("Color Random Seed", 23), + inWidth = op.inValueInt("Texture Width", 512), + inHeight = op.inValueInt("Texture Height", 512), + inLineWidth = op.inFloat("Line Width", 2), + + texOut = op.outTexture("Texture"), + outCanvas = op.outObject("Canvas Element"); + +const cgl = op.patch.cgl; + +let canvas = document.createElement("canvas"); +canvas.id = "graph_" + Math.random(); +canvas.width = 512; +canvas.height = 512; +// canvas.style.display = "none"; +// var body = document.getElementsByTagName("body")[0]; +// body.appendChild(canvas); +outCanvas.set(canvas); + +// let canvImage = document.getElementById(canvas.id); +let ctx = canvas.getContext("2d"); + +inWidth.onChange = inHeight.onChange = function () +{ + canvas.width = inWidth.get(); + canvas.height = inHeight.get(); +}; + +let buff = []; + +let maxValue = -Number.MAX_VALUE; +let minValue = Number.MAX_VALUE; +let colors = []; +let lastTime = Date.now(); + +value.onLinkChanged = reset; +index.onLinkChanged = reset; +inReset.onTriggered = reset; + +value.onChange = function () +{ + addValue(value.get(), Math.round(index.get())); +}; + +trigger.onTriggered = function () +{ + for (let i = 0; i < buff.length; i++) + if (buff[i]) addValue(buff[i][buff[i].length - 1], i); + + updateGraph(); +}; + +function reset() +{ + buff.length = 0; + maxValue = -999999; + minValue = 999999; +} + +function addValue(val, currentIndex) +{ + maxValue = Math.max(maxValue, parseFloat(val)); + minValue = Math.min(minValue, parseFloat(val)); + + if (!buff[currentIndex]) + { + buff[currentIndex] = []; + Math.randomSeed = inSeed.get() + currentIndex; + + colors[currentIndex] = "rgba(" + Math.round(Math.seededRandom() * 255) + "," + Math.round(Math.seededRandom() * 255) + "," + Math.round(Math.seededRandom() * 255) + ",1)"; + } + + let buf = buff[currentIndex]; + buf.push(val); + + if (!trigger.isLinked()) if (Date.now() - lastTime > 30)updateGraph(); +} + +function updateGraph() +{ + function getPos(v) + { + return canvas.height - ((v / h * canvas.height / 2 * 0.9) + canvas.height / 2); + } + + ctx.fillStyle = "#000"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + ctx.fillStyle = "#444"; + ctx.fillRect(0, getPos(0), canvas.width, 1); + + for (let b = 0; b < buff.length; b++) + { + let buf = buff[b]; + if (!buf) continue; + + ctx.lineWidth = inLineWidth.get(); + + var h = Math.max(Math.abs(maxValue), Math.abs(minValue)); + let heightmul = canvas.height / h; + let start = Math.max(0, buf.length - canvas.width); + + ctx.beginPath(); + ctx.strokeStyle = colors[b]; + + ctx.moveTo(0, getPos(buf[start])); + + for (let i = start; i < buf.length; i++) + { + ctx.lineTo( + 1 + i - start, + getPos(buf[i])); + } + ctx.stroke(); + } + + ctx.font = "22px monospace"; + + if (inShowMinMax.get()) + { + ctx.fillStyle = "#fff"; + ctx.fillText("max:" + (Math.round(maxValue * 100) / 100), 10, canvas.height - 10); + ctx.fillText("min:" + (Math.round(minValue * 100) / 100), 10, canvas.height - 30); + } + + if (texOut.get()) texOut.get().initTexture(canvas); + else texOut.set(new CGL.Texture.createFromImage(cgl, canvas, + { + "filter": CGL.Texture.FILTER_MIPMAP + + })); + + lastTime = Date.now(); +} + + +}; + +Ops.Gl.Textures.GraphTexture.prototype = new CABLES.Op(); +CABLES.OPS["98d1c79e-71ea-468b-81fc-17da2cd3da89"]={f:Ops.Gl.Textures.GraphTexture,objName:"Ops.Gl.Textures.GraphTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.Histogram +// +// ************************************************************** + +Ops.Gl.Textures.Histogram = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"histogram_frag":"void main()\n{\n float p=1.0/256.0;\n outColor = vec4(1.0,1.0,1.0,p);\n}\n","histogram_vert":"IN vec3 vPosition;\nIN vec2 attrTexCoord;\nUNI sampler2D tex;\n\nfloat lumi(vec3 color)\n{\n return dot(vec3(0.2126,0.7152,0.0722), color);\n}\n\nvoid main()\n{\n vec2 tc=attrTexCoord;\n\n float strength;\n highp float pos=0.0;\n #ifdef HISTOGRAM_R\n strength=texture(tex,tc).r;\n pos=1.0;\n #endif\n\n #ifdef HISTOGRAM_G\n strength=texture(tex,tc).g;\n pos=0.75;\n #endif\n\n #ifdef HISTOGRAM_B\n strength=texture(tex,tc).b;\n pos=0.5;\n #endif\n\n #ifdef HISTOGRAM_LUMI\n strength=lumi(texture(tex,tc).rgb);\n pos=0.25;\n #endif\n\n vec4 model= vec4(strength*2.0-1.0, pos , 0.0,1.0);\n\n gl_PointSize=1.0;\n gl_Position= model;\n}\n","histogram_wave_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\n\nvoid main()\n{\n vec4 col=vec4(0.0,0.0,0.0,1.0);\n float strengthR=texture(tex,vec2(texCoord.x,1.0-0.0)).r;\n float strengthG=texture(tex,vec2(texCoord.x,1.0-0.2)).r;\n float strengthB=texture(tex,vec2(texCoord.x,1.0-0.3)).r;\n float strengthL=texture(tex,vec2(texCoord.x,1.0-0.4)).r;\n\n strengthR*=strengthR;\n strengthG*=strengthG;\n strengthB*=strengthB;\n strengthL*=strengthL;\n\n\n\n if(strengthR*0.5>texCoord.y) col.r=1.0;\n if(strengthG*0.5>texCoord.y) col.g=1.0;\n if(strengthB*0.5>texCoord.y) col.b=1.0;\n\n if(strengthL > texCoord.y*2.0-1.0 && texCoord.y>0.5) col.rgb=vec3(1.0);\n\n outColor= col;\n}\n",}; +const + exe = op.inTrigger("Trigger"), + inTex = op.inTexture("Texture"), + outTex = op.outTexture("Histogram Texture"), + outTexData = op.outTexture("Histogram Data"); + +const cgl = op.patch.cgl; +let meshPoints = null; +let fb = new CGL.Framebuffer2(cgl, 256, 8, { "isFloatingPointTexture": true, + "filter": CGL.Texture.FILTER_NEAREST, + "wrap": CGL.Texture.WRAP_CLAMP_TO_EDGE }); + +// fb.setFilter(CGL.Texture.FILTER_NEAREST); +let effect = null; + +function initEffect() +{ + if (effect)effect.delete(); + effect = new CGL.TextureEffect(cgl, { "isFloatingPointTexture": false }); + + let tex = new CGL.Texture(cgl, + { + "isFloatingPointTexture": false, + "filter": CGL.Texture.FILTER_NEAREST, + "wrap": CGL.Texture.WRAP_CLAMP_TO_EDGE, + "width": 256, + "height": 256, + }); + + effect.setSourceTexture(tex); + outTex.set(null); +} + +function setUpPointVerts() +{ + const geom = new CGL.Geometry(op.name); + let res = 256; + let verts = []; + let texCoords = []; + let i = 0; + verts.length = res * res * 3; + texCoords.length = res * res * 2; + for (let x = 0; x < res; x++) + { + for (let y = 0; y < res; y++) + { + i++; + verts[i * 3 + 2] = verts[i * 3 + 1] = verts[i * 3 + 0] = 0; + texCoords[i * 2] = x / res; + texCoords[i * 2 + 1] = y / res; + } + } + geom.setPointVertices(verts); + geom.texCoords = texCoords; + + meshPoints = new CGL.Mesh(cgl, geom, cgl.gl.POINTS); + meshPoints.setGeom(geom); +} + +let shaderWave = new CGL.Shader(cgl, "imgcompose bg"); +shaderWave.setSource(shaderWave.getDefaultVertexShader(), attachments.histogram_wave_frag); +shaderWave.textureUniform = new CGL.Uniform(shaderWave, "t", "tex", 2); + +let shaderPointsR = new CGL.Shader(cgl, "histogram r"); +shaderPointsR.setSource(attachments.histogram_vert, attachments.histogram_frag); +shaderPointsR.textureUniform = new CGL.Uniform(shaderPointsR, "t", "tex", 0); +shaderPointsR.define("HISTOGRAM_R"); + +let shaderPointsG = new CGL.Shader(cgl, "histogram g"); +shaderPointsG.setSource(attachments.histogram_vert, attachments.histogram_frag); +shaderPointsG.textureUniform = new CGL.Uniform(shaderPointsG, "t", "tex", 0); +shaderPointsG.define("HISTOGRAM_G"); + +let shaderPointsB = new CGL.Shader(cgl, "histogram b"); +shaderPointsB.setSource(attachments.histogram_vert, attachments.histogram_frag); +shaderPointsB.textureUniform = new CGL.Uniform(shaderPointsB, "t", "tex", 0); +shaderPointsB.define("HISTOGRAM_B"); + +let shaderPointsLumi = new CGL.Shader(cgl, "histogram lumi"); +shaderPointsLumi.setSource(attachments.histogram_vert, attachments.histogram_frag); +shaderPointsLumi.textureUniform = new CGL.Uniform(shaderPointsLumi, "t", "tex", 0); +shaderPointsLumi.define("HISTOGRAM_LUMI"); + +setUpPointVerts(); +initEffect(); +let prevViewPort = [0, 0, 0, 0]; + +exe.onTriggered = function () +{ + if (meshPoints && inTex.get()) + { + cgl.pushBlendMode(CGL.BLEND_NORMAL, false); + cgl.pushBlend(true); + + let vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + // setup data + fb.renderStart(cgl); + + cgl.setTexture(0, inTex.get().tex); + meshPoints.render(shaderPointsR); + meshPoints.render(shaderPointsG); + meshPoints.render(shaderPointsB); + meshPoints.render(shaderPointsLumi); + + fb.renderEnd(cgl); + outTexData.set(fb.getTextureColor()); + + // render wave + + cgl.currentTextureEffect = effect; + + effect.startEffect(); + + cgl.pushShader(shaderWave); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(2, fb.getTextureColor().tex); + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + outTex.set(effect.getCurrentSourceTexture()); + + effect.endEffect(); + + cgl.setViewPort(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); + + cgl.popBlend(); + cgl.popBlendMode(); + + cgl.currentTextureEffect = null; + } +}; + + +}; + +Ops.Gl.Textures.Histogram.prototype = new CABLES.Op(); +CABLES.OPS["966cefee-8349-4ef3-b64e-69afca0395f5"]={f:Ops.Gl.Textures.Histogram,objName:"Ops.Gl.Textures.Histogram"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.NoiseTexture +// +// ************************************************************** + +Ops.Gl.Textures.NoiseTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + + inWidth = op.inValueInt("Width", 256), + inHeight = op.inValueInt("Height", 256), + tfilter = op.inSwitch("Filter", ["nearest", "linear"], "nearest"), + wrap = op.inValueSelect("Wrap", ["repeat", "mirrored repeat", "clamp to edge"], "repeat"), + inColor = op.inValueBool("Color", false), + inPixel = op.inDropDown("Pixel Format", CGL.Texture.PIXELFORMATS, CGL.Texture.PFORMATSTR_RGBA8UB), + + inSeed = op.inFloat("Seed", 0), + inOutR = op.inBool("Channel R", true), + inMinR = op.inFloat("Min R", 0), + inMaxR = op.inFloat("Max R", 1), + inOutG = op.inBool("Channel G", true), + inMinG = op.inFloat("Min G", 0), + inMaxG = op.inFloat("Max G", 1), + inOutB = op.inBool("Channel B", true), + inMinB = op.inFloat("Min B", 0), + inMaxB = op.inFloat("Max B", 1), + outTex = op.outTexture("Texture"); + +const cgl = op.patch.cgl; + +inSeed.onChange = + inWidth.onChange = + inHeight.onChange = + inPixel.onChange = + inMinR.onChange = + inMaxR.onChange = + inMinG.onChange = + inMaxG.onChange = + inMinB.onChange = + inMaxB.onChange = + inOutR.onChange = + inOutB.onChange = + inOutG.onChange = + tfilter.onChange = + wrap.onChange = + inColor.onChange = update; + +update(); + +function update() +{ + const isFp = inPixel.get() == CGL.Texture.PFORMATSTR_RGBA32F; + if (!isFp) + { + if ( + inMinR.get() < 0.0 || inMinR.get() > 1.0 || + inMinG.get() < 0.0 || inMinG.get() > 1.0 || + inMinB.get() < 0.0 || inMinB.get() > 1.0 || + inMaxR.get() < 0.0 || inMaxR.get() > 1.0 || + inMaxG.get() < 0.0 || inMaxG.get() > 1.0 || + inMaxB.get() < 0.0 || inMaxB.get() > 1.0) op.setUiError("nonfprange", "Non floating point textures have to be between 0 and 1"); + else op.setUiError("nonfprange", null); + } + else op.setUiError("nonfprange", null); + + inMinG.setUiAttribs({ "greyout": !inColor.get() }); + inMaxG.setUiAttribs({ "greyout": !inColor.get() }); + inMinB.setUiAttribs({ "greyout": !inColor.get() }); + inMaxB.setUiAttribs({ "greyout": !inColor.get() }); + + let width = Math.ceil(inWidth.get()); + let height = Math.ceil(inHeight.get()); + + if (width < 1)width = 1; + if (height < 1)height = 1; + + let pixels; + const num = width * 4 * height; + + const minR = inMinR.get(); + const diffR = inMaxR.get() - minR; + + const minG = inMinG.get(); + const diffG = inMaxG.get() - minG; + + const minB = inMinB.get(); + const diffB = inMaxB.get() - minB; + + Math.randomSeed = inSeed.get(); + + if (isFp) + { + pixels = new Float32Array(num); + + if (inColor.get()) + { + for (let i = 0; i < num; i += 4) + { + pixels[i + 0] = minR + Math.seededRandom() * diffR; + pixels[i + 1] = minG + Math.seededRandom() * diffG; + pixels[i + 2] = minB + Math.seededRandom() * diffB; + pixels[i + 3] = 1; + } + } + else + { + for (let i = 0; i < num; i += 4) + { + let c = minR + Math.seededRandom() * diffR; + pixels[i + 0] = pixels[i + 1] = pixels[i + 2] = c; + pixels[i + 3] = 1; + } + } + } + else + { + pixels = new Uint8Array(num); + + if (inColor.get()) + { + for (let i = 0; i < num; i += 4) + { + pixels[i + 0] = (minR + Math.seededRandom() * diffR) * 255; + pixels[i + 1] = (minG + Math.seededRandom() * diffG) * 255; + pixels[i + 2] = (minB + Math.seededRandom() * diffB) * 255; + pixels[i + 3] = 255; + } + } + else + { + for (let i = 0; i < num; i += 4) + { + pixels[i + 0] = + pixels[i + 1] = + pixels[i + 2] = (minR + Math.seededRandom() * diffR) * 255; + pixels[i + 3] = 255; + } + } + } + + if (!inOutR.get()) for (let i = 0; i < num; i += 4)pixels[i + 0] = 0.0; + if (!inOutG.get()) for (let i = 0; i < num; i += 4)pixels[i + 1] = 0.0; + if (!inOutB.get()) for (let i = 0; i < num; i += 4)pixels[i + 2] = 0.0; + + let cgl_filter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + // else if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + // else if (tfilter.get() == "Anisotropic") cgl_filter = CGL.Texture.FILTER_ANISOTROPIC; + + let cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + let tex = new CGL.Texture(cgl, { "isFloatingPointTexture": isFp }); + + tex.initFromData(pixels, width, height, cgl_filter, cgl_wrap); + + outTex.set(CGL.Texture.getEmptyTexture(op.patch.cgl, isFp)); + outTex.set(tex); +} + + +}; + +Ops.Gl.Textures.NoiseTexture.prototype = new CABLES.Op(); +CABLES.OPS["b781bc6b-b2cf-44fe-80eb-a840e430d27d"]={f:Ops.Gl.Textures.NoiseTexture,objName:"Ops.Gl.Textures.NoiseTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.PaletteTexture +// +// ************************************************************** + +Ops.Gl.Textures.PaletteTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + arrayInPalette = op.inArray("Palette array"), + inLinear = op.inValueBool("Smooth"), + arrOut = op.outArray("Color Array"), + textureOut = op.outTexture("Texture"); + +const cgl = op.patch.cgl; + +let canvas = document.createElement("canvas"); +canvas.id = "canvas_" + CABLES.generateUUID(); +canvas.width = 5; +canvas.height = 8; +canvas.style.display = "none"; + +let body = document.getElementsByTagName("body")[0]; +body.appendChild(canvas); +let ctx = canvas.getContext("2d"); + +textureOut.set(CGL.Texture.getEmptyTexture(cgl)); + +let arr = []; +let lastFilter = null; + +let r = 0; +let g = 0; +let b = 0; + +inLinear.onChange = arrayInPalette.onChange = buildTexture; + +function hexToR(h) +{ + return parseInt((cutHex(h)).substring(0, 2), 16); +} + +function hexToG(h) +{ + return parseInt((cutHex(h)).substring(2, 4), 16); +} + +function hexToB(h) +{ + return parseInt((cutHex(h)).substring(4, 6), 16); +} + +function cutHex(h) +{ + return (h.charAt(0) == "#") ? h.substring(1, 7) : h; +} + +function buildTexture() +{ + let colors = arrayInPalette.get(); + let isHexCode; + if (!colors) + { + return; + } + + let stringTest = colors[0]; + let paletteSize = 0; + + if (typeof stringTest === "string") + { + isHexCode = true; + paletteSize = colors.length; + canvas.width = paletteSize; + arr.length = colors.length; + } + else + { + isHexCode = false; + paletteSize = Math.floor(colors.length / 3); + canvas.width = paletteSize; + arr.length = paletteSize; + } + + for (let i = 0; i < paletteSize; i++) + { + if (isHexCode) + { + r = hexToR(colors[i]); + g = hexToG(colors[i]); + b = hexToB(colors[i]); + + arr[i * 3 + 0] = r / 255; + arr[i * 3 + 1] = g / 255; + arr[i * 3 + 2] = b / 255; + } + else + { + r = Math.floor(colors[i * 3 + 0] * 255); + g = Math.floor(colors[i * 3 + 1] * 255); + b = Math.floor(colors[i * 3 + 2] * 255); + + arr[i * 3 + 0] = colors[i * 3 + 0]; + arr[i * 3 + 1] = colors[i * 3 + 1]; + arr[i * 3 + 2] = colors[i * 3 + 2]; + } + + ctx.fillStyle = "rgb(" + r + "," + g + "," + b + ")"; + ctx.fillRect( + canvas.width / paletteSize * i, + 0, + canvas.width / paletteSize, + canvas.height + ); + } + + let filter = CGL.Texture.FILTER_NEAREST; + if (inLinear.get())filter = CGL.Texture.FILTER_LINEAR; + + if (lastFilter == filter && textureOut.get()) textureOut.get().initTexture(canvas, filter); + else textureOut.set(new CGL.Texture.createFromImage(op.patch.cgl, canvas, { "filter": filter })); + + arrOut.set(null); + arrOut.set(arr); + textureOut.get().unpackAlpha = false; + lastFilter = filter; +} + +arrayInPalette.onLinkChanged = function () +{ + if (!arrayInPalette.isLinked()) + { + arrOut.set(null); + textureOut.set(CGL.Texture.getEmptyTexture(cgl)); + lastFilter = null; + } +}; + +op.onDelete = function () +{ + canvas.remove(); +}; + +buildTexture(); + + +}; + +Ops.Gl.Textures.PaletteTexture.prototype = new CABLES.Op(); +CABLES.OPS["8117ed3b-a264-4a46-83a4-3cdb3f743d10"]={f:Ops.Gl.Textures.PaletteTexture,objName:"Ops.Gl.Textures.PaletteTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.SwitchTextures_v2 +// +// ************************************************************** + +Ops.Gl.Textures.SwitchTextures_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("exec"), + num = this.inValueInt("num"), + defaultTransparent = op.inValueBool("Default Texture Transparent", true), + next = op.outTrigger("Next"), + textureOut = this.outTexture("texture"); + +const cgl = op.patch.cgl; +const texturePorts = []; +let index = 0; +let lastIndex = -1; +let tempTexture = CGL.Texture.getEmptyTexture(cgl); + +op.toWorkPortsNeedToBeLinked(exec); +exec.onTriggered = function () { updateTexture(); next.trigger(); }; + + +defaultTransparent.onChange = function () +{ + if (defaultTransparent.get()) tempTexture = CGL.Texture.getEmptyTexture(cgl); + else tempTexture = CGL.Texture.getTempTexture(cgl); + + updateTexture(true); +}; + +for (let i = 0; i < 16; i++) +{ + const tex = op.inTexture("texture" + i); + texturePorts.push(tex); + tex.onChange = forceUpdateTexture; +} + +function forceUpdateTexture() +{ + updateTexture(true); +} + +function updateTexture(force) +{ + index = parseInt(num.get(), 10); + if (!force) + { + if (index == lastIndex) return; + if (index != index) return; + } + if ( + isNaN(index) || + index < 0 || + index > texturePorts.length - 1 + ) + index = 0; + + if (texturePorts[index].get()) textureOut.set(texturePorts[index].get()); + else textureOut.set(tempTexture); + + lastIndex = index; +} + + +}; + +Ops.Gl.Textures.SwitchTextures_v2.prototype = new CABLES.Op(); +CABLES.OPS["a82ae429-ac07-4760-882b-595a857c7ae0"]={f:Ops.Gl.Textures.SwitchTextures_v2,objName:"Ops.Gl.Textures.SwitchTextures_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.TextTexture_v4 +// +// ************************************************************** + +Ops.Gl.Textures.TextTexture_v4 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"text_frag":"UNI sampler2D tex;\nUNI float a;\nUNI vec3 color;\nIN vec2 texCoord;\n\nvec4 myround(vec4 col)\n{\n if(col.a>0.5)return vec4(1.0,1.0,1.0,1.0);\n else return vec4(1.0,1.0,1.0,0.0);\n}\n\nvoid main()\n{\n vec4 col;\n\n #ifndef HARD_EDGE\n // col= vec4(1.0,1.0,1.0,texture(tex,vec2(texCoord.x,(1.0-texCoord.y))).r*a);\n col = texture(tex,vec2(texCoord.x,(1.0-texCoord.y)));\n #endif\n #ifdef HARD_EDGE\n\n float step=0.7;\n col=texture(tex,vec2(texCoord.x,1.0-texCoord.y));\n vec4 col2=texture(tex,vec2(texCoord.x-(fwidth(texCoord.x)*step),1.0-texCoord.y-(fwidth(texCoord.y)*step)));\n vec4 col3=texture(tex,vec2(texCoord.x+(fwidth(texCoord.x)*step),1.0-texCoord.y+(fwidth(texCoord.y)*step)));\n vec4 col4=texture(tex,vec2(texCoord.x+(fwidth(texCoord.x)*step),1.0-texCoord.y-(fwidth(texCoord.y)*step)));\n vec4 col5=texture(tex,vec2(texCoord.x-(fwidth(texCoord.x)*step),1.0-texCoord.y+(fwidth(texCoord.y)*step)));\n\n float f=smoothstep(col.a,0.5,0.8);\n\n col=myround(col);\n col2=myround(col2);\n col3=myround(col3);\n col4=myround(col4);\n col5=myround(col5);\n\n // col.a=(col.a+col2.a+col3.a+col4.a+col5.a)/5.0*a;\n\n #endif\n\n col.rgb=color.rgb;\n\n outColor=col;\n\n}\n","text_vert":"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\nUNI float aspect;\nOUT vec2 texCoord;\nIN vec2 attrTexCoord;\n\nvoid main()\n{\n vec4 pos=vec4(vPosition, 1.0);\n\n pos.x*=aspect;\n\n texCoord=vec2(attrTexCoord.x,1.0-attrTexCoord.y);;\n\n gl_Position = projMatrix * mvMatrix * pos;\n}\n",}; +function componentToHex(c) +{ + const hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; +} + +function rgbToHex(r, g, b) +{ + return "#" + componentToHex(Math.floor(r * 255)) + componentToHex(Math.floor(g * 255)) + componentToHex(Math.floor(b * 255)); +} + +const + render = op.inTriggerButton("Render"), + text = op.inString("text", "cables"), + font = op.inString("font", "Arial"), + weight = op.inString("weight", "normal"), + maximize = op.inValueBool("Maximize Size", true), + inFontSize = op.inValueFloat("fontSize", 30), + lineDistance = op.inValueFloat("Line Height", 1), + lineOffset = op.inValueFloat("Vertical Offset", 0), + // letterSpacing = op.inValueFloat("Spacing", 0), + drawDebug = op.inBool("Show Debug", false), + limitLines = op.inValueInt("Limit Lines", 0), + texWidth = op.inValueInt("texture width", 512), + texHeight = op.inValueInt("texture height", 128), + tfilter = op.inSwitch("filter", ["nearest", "linear", "mipmap"], "linear"), + wrap = op.inValueSelect("Wrap", ["repeat", "mirrored repeat", "clamp to edge"], "clamp to edge"), + aniso = op.inSwitch("Anisotropic", [0, 1, 2, 4, 8, 16], 0), + align = op.inSwitch("align", ["left", "center", "right"], "center"), + valign = op.inSwitch("vertical align", ["top", "center", "bottom"], "center"), + cachetexture = op.inValueBool("Reuse Texture", true), + drawMesh = op.inValueBool("Draw Mesh", true), + meshScale = op.inValueFloat("Scale Mesh", 1.0), + renderHard = op.inValueBool("Hard Edges", false), + inOpacity = op.inFloatSlider("Opacity", 1), + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + next = op.outTrigger("Next"), + outRatio = op.outNumber("Ratio"), + textureOut = op.outTexture("texture"), + outAspect = op.outNumber("Aspect", 1), + outLines = op.outNumber("Num Lines"); + +r.setUiAttribs({ "colorPick": true }); + +op.setPortGroup("Color", [r, g, b]); +op.setPortGroup("Size", [font, weight, maximize, inFontSize, lineDistance, lineOffset]); +op.setPortGroup("Texture", [texWidth, texHeight, tfilter, aniso]); +op.setPortGroup("Alignment", [valign, align]); +op.setPortGroup("Rendering", [drawMesh, renderHard, meshScale]); + +align.onChange = + valign.onChange = + text.onChange = + inFontSize.onChange = + weight.onChange = + aniso.onChange = + font.onChange = + lineOffset.onChange = + lineDistance.onChange = + cachetexture.onChange = + // letterSpacing.onChange = + limitLines.onChange = + texWidth.onChange = + texHeight.onChange = + maximize.onChange = function () { needsRefresh = true; }; + +wrap.onChange = () => +{ + if (tex)tex.delete(); + tex = null; + needsRefresh = true; +}; + +r.onChange = g.onChange = b.onChange = inOpacity.onChange = function () +{ + if (!drawMesh.get() || textureOut.isLinked()) + needsRefresh = true; +}; +textureOut.onLinkChanged = () => +{ + if (textureOut.isLinked()) needsRefresh = true; +}; + +op.patch.on("fontLoaded", (fontName) => +{ + if (fontName == font.get()) + { + needsRefresh = true; + } +}); + +render.onTriggered = doRender; + +aniso.onChange = +tfilter.onChange = () => +{ + tex = null; + needsRefresh = true; +}; + +textureOut.ignoreValueSerialize = true; + +const cgl = op.patch.cgl; +const body = document.getElementsByTagName("body")[0]; + +let tex = new CGL.Texture(cgl); +const fontImage = document.createElement("canvas"); +fontImage.id = "texturetext_" + CABLES.generateUUID(); +fontImage.style.display = "none"; +body.appendChild(fontImage); + +const ctx = fontImage.getContext("2d"); +let needsRefresh = true; +const mesh = CGL.MESHES.getSimpleRect(cgl, "texttexture rect"); +const vScale = vec3.create(); + +const shader = new CGL.Shader(cgl, "texttexture"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +shader.setSource(attachments.text_vert, attachments.text_frag); +const texUni = new CGL.Uniform(shader, "t", "tex"); +const aspectUni = new CGL.Uniform(shader, "f", "aspect", 0); +const opacityUni = new CGL.Uniform(shader, "f", "a", inOpacity); +const uniColor = new CGL.Uniform(shader, "3f", "color", r, g, b); +// const uniformColor = new CGL.Uniform(shader, "4f", "") + +if (op.patch.isEditorMode()) CABLES.UI.SIMPLEWIREFRAMERECT = CABLES.UI.SIMPLEWIREFRAMERECT || new CGL.WireframeRect(cgl); + +if (cgl.glVersion < 2) +{ + cgl.gl.getExtension("OES_standard_derivatives"); + shader.enableExtension("GL_OES_standard_derivatives"); +} + +renderHard.onChange = function () +{ + shader.toggleDefine("HARD_EDGE", renderHard.get()); +}; + +function doRender() +{ + if (ctx.canvas.width != texWidth.get())needsRefresh = true; + if (needsRefresh) + { + reSize(); + refresh(); + } + + if (drawMesh.get()) + { + vScale[0] = vScale[1] = vScale[2] = meshScale.get(); + cgl.pushBlendMode(CGL.BLEND_NORMAL, false); + cgl.pushModelMatrix(); + mat4.scale(cgl.mMatrix, cgl.mMatrix, vScale); + + shader.popTextures(); + shader.pushTexture(texUni, tex.tex); + aspectUni.set(outAspect.get()); + + if (cgl.shouldDrawHelpers(op)) + CABLES.UI.SIMPLEWIREFRAMERECT.render(outAspect.get(), 1, 1); + + cgl.pushShader(shader); + mesh.render(shader); + + cgl.popShader(); + cgl.popBlendMode(); + cgl.popModelMatrix(); + } + + next.trigger(); +} + +function reSize() +{ + if (!tex) return; + tex.setSize(texWidth.get(), texHeight.get()); + + ctx.canvas.width = fontImage.width = texWidth.get(); + ctx.canvas.height = fontImage.height = texHeight.get(); + + outAspect.set(fontImage.width / fontImage.height); + + needsRefresh = true; +} + +maximize.onChange = function () +{ + inFontSize.setUiAttribs({ "greyout": maximize.get() }); + needsRefresh = true; +}; + +function getLineHeight(fontSize) +{ + return lineDistance.get() * fontSize; +} + +function removeEmptyLines(lines) +{ + if (lines[lines.length - 1] === "" || lines[lines.length - 1] === "\n") + { + lines.length--; + lines = removeEmptyLines(lines); + } + return lines; +} + +function refresh() +{ + cgl.checkFrameStarted("texttrexture refresh"); + + // const num=String(parseInt(letterSpacing.get())); + // fontImage.style["letter-spacing"] = num+"px"; + // fontImage.style["font-kerning"]="normal"; + + ctx.clearRect(0, 0, fontImage.width, fontImage.height); + const rgbString = "rgba(" + Math.floor(r.get() * 255) + "," + + Math.floor(g.get() * 255) + "," + Math.floor(b.get() * 255) + "," + + inOpacity.get() + ")"; + // ctx.fillStyle = "white"; + ctx.fillStyle = rgbString; + // op.log("rgbstring", rgbString); + let fontSize = parseFloat(inFontSize.get()); + let fontname = font.get(); + if (fontname.indexOf(" ") > -1) fontname = "\"" + fontname + "\""; + ctx.font = weight.get() + " " + fontSize + "px " + fontname + ""; + // ctx["font-weight"] = 300; + + ctx.textAlign = align.get(); + + let txt = (text.get() + "").replace(//g, "\n"); + let strings = txt.split("\n"); + + needsRefresh = false; + + strings = removeEmptyLines(strings); + + if (maximize.get()) + { + fontSize = texWidth.get(); + let count = 0; + let maxWidth = 0; + let maxHeight = 0; + + do + { + count++; + if (count > (texHeight.get() + texWidth.get()) / 2) + { + op.log("too many iterations - maximize size"); + break; + } + fontSize -= 5; + ctx.font = weight.get() + " " + fontSize + "px \"" + font.get() + "\""; + maxWidth = 0; + + maxHeight = (fontSize + (strings.length - 1) * getLineHeight(fontSize)) * 1.2; + for (let i = 0; i < strings.length; i++) maxWidth = Math.max(maxWidth, ctx.measureText(strings[i]).width); + } + while (maxWidth > ctx.canvas.width || maxHeight > ctx.canvas.height); + } + else + { + let found = true; + + if (texWidth.get() > 128) + { + found = false; + let newString = ""; + + for (let i = 0; i < strings.length; i++) + { + if (!strings[i]) + { + newString += "\n"; + continue; + } + let sumWidth = 0; + const words = strings[i].split(" "); + + for (let j = 0; j < words.length; j++) + { + if (!words[j]) continue; + sumWidth += ctx.measureText(words[j] + " ").width; + + if (sumWidth > texWidth.get()) + { + found = true; + newString += "\n" + words[j] + " "; + sumWidth = ctx.measureText(words[j] + " ").width; + } + else + { + newString += words[j] + " "; + } + } + newString += "\n"; + } + txt = newString; + strings = txt.split("\n"); + } + strings = removeEmptyLines(strings); + + if (limitLines.get() > 0 && strings.length > limitLines.get()) + { + strings.length = limitLines.get(); + strings[strings.length - 1] += "..."; + } + } + + strings = removeEmptyLines(strings); + const firstLineHeight = fontSize; + const textHeight = firstLineHeight + (strings.length - 1) * getLineHeight(fontSize); + + let posy = lineOffset.get() * fontSize; + + if (valign.get() == "top") posy += firstLineHeight; + else if (valign.get() == "center") posy += (ctx.canvas.height / 2) - (textHeight / 2) + firstLineHeight; + else if (valign.get() == "bottom") posy += ctx.canvas.height - textHeight + firstLineHeight; + + let miny = 999999; + let maxy = -999999; + + const dbg = drawDebug.get(); + + for (let i = 0; i < strings.length; i++) + { + let posx = 0; + if (align.get() == "center") posx = ctx.canvas.width / 2; + if (align.get() == "right") posx = ctx.canvas.width; + + ctx.fillText(strings[i], posx, posy); + + miny = Math.min(miny, posy - firstLineHeight); + maxy = Math.max(maxy, posy); + + if (dbg) + { + ctx.lineWidth = 1; + ctx.strokeStyle = "#FF0000"; + ctx.beginPath(); + ctx.moveTo(0, posy); + ctx.lineTo(21000, posy); + ctx.stroke(); + } + + posy += getLineHeight(fontSize); + } + + if (dbg) + { + ctx.lineWidth = 3; + ctx.strokeStyle = "#FF0000"; + ctx.strokeRect(0, miny, ctx.canvas.width - 3, maxy - miny); + } + + ctx.restore(); + + outRatio.set(ctx.canvas.height / ctx.canvas.width); + outLines.set(strings.length); + textureOut.set(CGL.Texture.getEmptyTexture(cgl)); + + let cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + let f = CGL.Texture.FILTER_LINEAR; + if (tfilter.get() == "nearest") f = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "mipmap") f = CGL.Texture.FILTER_MIPMAP; + + if (!cachetexture.get() || !tex || !textureOut.get() || tex.width != fontImage.width || tex.height != fontImage.height || tex.anisotropic != parseFloat(aniso.get())) + { + if (tex)tex.delete(); + tex = new CGL.Texture.createFromImage(cgl, fontImage, { "filter": f, "anisotropic": parseFloat(aniso.get()), "wrap": cgl_wrap }); + } + + tex.flip = false; + tex.initTexture(fontImage, f); + textureOut.set(tex); + tex.unpackAlpha = true; +} + + +}; + +Ops.Gl.Textures.TextTexture_v4.prototype = new CABLES.Op(); +CABLES.OPS["afab973f-c9ac-47fd-914c-70b42af5c5d4"]={f:Ops.Gl.Textures.TextTexture_v4,objName:"Ops.Gl.Textures.TextTexture_v4"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.TextureInfo +// +// ************************************************************** + +Ops.Gl.Textures.TextureInfo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTex = op.inObject("Texture"), + outName = op.outNumber("Name"), + outWidth = op.outNumber("Width"), + outHeight = op.outNumber("Height"), + outRatio = op.outNumber("Ratio"), + outFilter = op.outNumber("Filter"), + outWrap = op.outNumber("Wrap"), + outFlipped = op.outNumber("Flipped"), + outFp = op.outNumber("HDR"), + outDefaultEmpty = op.outNumber("Is Empty Default Texture", false), + outDefaultTex = op.outNumber("Is Default Texture", false), + outId = op.outNumber("Id"); + +outFp.setUiAttribs({ "title": "Pixelformat Float 32bit" }); + +const emptyTex = CGL.Texture.getEmptyTexture(op.patch.cgl); +const defaultTex = CGL.Texture.getTempTexture(op.patch.cgl); + +inTex.onChange = function () +{ + if (inTex.get()) + { + outName.set(inTex.get().name); + outWidth.set(inTex.get().width); + outHeight.set(inTex.get().height); + outRatio.set(inTex.get().width / inTex.get().height); + + let strFilter = "unknown"; + if (inTex.get().filter == CGL.Texture.FILTER_NEAREST)strFilter = "nearest"; + else if (inTex.get().filter == CGL.Texture.FILTER_LINEAR)strFilter = "linear"; + else if (inTex.get().filter == CGL.Texture.FILTER_MIPMAP)strFilter = "mipmap"; + + outFilter.set(inTex.get().filter + " " + strFilter); + + let strWrap = "unknown"; + + if (inTex.get().wrap == CGL.Texture.WRAP_CLAMP_TO_EDGE) strWrap = "clamp to edge"; + else if (inTex.get().wrap == CGL.Texture.WRAP_REPEAT) strWrap = "repeat"; + else if (inTex.get().wrap == CGL.Texture.WRAP_MIRRORED_REPEAT) strWrap = "mirrored repeat"; + + outWrap.set(inTex.get().wrap + " " + strWrap); + + outId.set(inTex.get().id); + outFlipped.set(inTex.get().flipped); + outFp.set(inTex.get().textureType == CGL.Texture.TYPE_FLOAT); + } + else + { + outName.set("no texture"); + outWidth.set(0); + outHeight.set(0); + outRatio.set(0); + outFilter.set(null); + outWrap.set(null); + outId.set(null); + outFlipped.set(false); + outFp.set(false); + } + + outDefaultEmpty.set(inTex.get() == emptyTex); + outDefaultTex.set(inTex.get() == defaultTex); +}; + + +}; + +Ops.Gl.Textures.TextureInfo.prototype = new CABLES.Op(); +CABLES.OPS["71e5ee4c-3455-4c09-abb2-67abf382f35b"]={f:Ops.Gl.Textures.TextureInfo,objName:"Ops.Gl.Textures.TextureInfo"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.TextureSVG_v2 +// +// ************************************************************** + +Ops.Gl.Textures.TextureSVG_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + filename = op.inUrl("File"), + texWidth = op.inValueInt("Texture width"), + texHeight = op.inValueInt("Texture height"), + wrap = op.inValueSelect("Wrap", ["repeat", "mirrored repeat", "clamp to edge"], "repeat"), + tfilter = op.inValueSelect("Filter", ["nearest", "linear", "mipmap"], "mipmap"), + textureOut = op.outTexture("Texture"), + outLoaded = op.outBoolNum("Loaded"); + +tfilter.onChange = onFilterChange; +wrap.onChange = onWrapChange; + +texWidth.set(1024); +texHeight.set(1024); + +const cgl = op.patch.cgl; +let canvas = null; +let ctx = null; + +function removeCanvas() +{ + if (!canvas) return; + canvas.remove(); + canvas = null; +} + +function createCanvas() +{ + if (canvas)removeCanvas(); + canvas = document.createElement("canvas"); + canvas.id = "svgcanvas"; + ctx = canvas.getContext("2d", { "alpha": true }); + + ctx.canvas.width = canvas.width = texWidth.get(); + ctx.canvas.height = canvas.height = texHeight.get(); + + canvas.style.display = "none"; + const body = document.getElementsByTagName("body")[0]; + body.appendChild(canvas); +} + +textureOut.set(new CGL.Texture(cgl)); + +function reSize() +{ + update(); +} + +let data = "data:image/svg+xml," + + "" + + "" + + "
    " + + "
    " + + "
    " + + "
    "; + +let cgl_filter = CGL.Texture.FILTER_MIPMAP; +let cgl_wrap = CGL.Texture.WRAP_REPEAT; + +function onFilterChange() +{ + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + + reload(); +} + +function onWrapChange() +{ + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + else if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + else if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + reload(); +} + +function reload() +{ + const loadingId = op.patch.loading.start("svg file", filename.get()); + CABLES.ajax( + op.patch.getFilePath(filename.get()), + function (err, _data, xhr) + { + data = "data:image/svg+xml," + _data; + + data = data.replace(/#/g, "%23"); + + op.patch.loading.finished(loadingId); + update(); + } + ); +} + +let startTime = 0; + +function update() +{ + const img = new Image(); + const loadingId = op.patch.loading.start("svg2texture", filename.get()); + + img.onabort = img.onerror = function (e) + { + outLoaded.set(false); + op.logError("could not load file",); + op.patch.loading.finished(loadingId); + + // op.patch.loading.finished(loadingId); + op.setUiError("error", "Could not load SVG file!"); + }; + + outLoaded.set(false); + + img.onload = function () + { + cgl.addNextFrameOnceCallback(() => + { + createCanvas(); + op.patch.loading.finished(loadingId); + canvas.width = texWidth.get(); + canvas.height = texHeight.get(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + textureOut.set(new CGL.Texture.createFromImage(cgl, canvas, + { + "wrap": cgl_wrap, + "filter": cgl_filter, + "width": canvas.width, + "height": canvas.height, + "unpackAlpha": true + })); + removeCanvas(); + outLoaded.set(true); + }); + }; + + img.src = data; + startTime = performance.now(); +} + +op.onFileChanged = function (fn) +{ + if (filename.get() && filename.get().endsWith(fn)) + { + reload(); + } +}; + +filename.onChange = reload; +texWidth.onChange = texHeight.onChange = reSize; + +createCanvas(); +reSize(); + +tfilter.set("mipmap"); + + +}; + +Ops.Gl.Textures.TextureSVG_v2.prototype = new CABLES.Op(); +CABLES.OPS["e4f3d8f1-8a49-460a-896c-4d7adcda2494"]={f:Ops.Gl.Textures.TextureSVG_v2,objName:"Ops.Gl.Textures.TextureSVG_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.TextureToBase64 +// +// ************************************************************** + +Ops.Gl.Textures.TextureToBase64 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTex = op.inTexture("Texture"), + start = op.inTriggerButton("Trigger"), + jpeg = op.inBool("Use JPEG", false), + dataUrl = op.inBool("Output dataUrl", false), + outString = op.outString("Base64 string"), + outLoading = op.outBoolNum("Loading"); + +const gl = op.patch.cgl.gl; +let fb = null; +outString.ignoreValueSerialize = true; + +// Create a 2D canvas to store the result +const canvas = document.createElement("canvas"); + +jpeg.onChange = update; +dataUrl.onChange = update; +start.onTriggered = update; + +function update() +{ + op.uiAttr({ "error": null }); + if (!inTex.get() || !inTex.get().tex) return; + outLoading.set(true); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + + const width = inTex.get().width; + const height = inTex.get().height; + + if (!fb)fb = gl.createFramebuffer(); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, inTex.get().tex, 0); + + const canRead = (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + if (!canRead) + { + outLoading.set(true); + op.uiAttr({ "error": "cannot read texture!" }); + return; + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + const data = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + canvas.width = width; + canvas.height = height; + const context = canvas.getContext("2d"); + + // Copy the pixels to a 2D canvas + const imageData = context.createImageData(width, height); + imageData.data.set(data); + + const data2 = imageData.data; + + // flip image + Array.from({ "length": height }, (val, i) => { return data2.slice(i * width * 4, (i + 1) * width * 4); }) + .forEach((val, i) => { return data2.set(val, (height - i - 1) * width * 4); }); + + context.putImageData(imageData, 0, 0); + let dataString = ""; + if (jpeg.get()) + { + dataString = canvas.toDataURL("image/jpeg", 1.0); + } + else + { + dataString = canvas.toDataURL(); + } + if (!dataUrl.get()) + { + dataString = dataString.split(",", 2)[1]; + } + outString.set(dataString); + outLoading.set(false); +} + +function dataURIToBlob(dataURI, callback) +{ + const binStr = atob(dataURI.split(",")[1]), + len = binStr.length, + arr = new Uint8Array(len); + for (let i = 0; i < len; i++) arr[i] = binStr.charCodeAt(i); + callback(new Blob([arr], { "type": "image/png" })); +} + + +}; + +Ops.Gl.Textures.TextureToBase64.prototype = new CABLES.Op(); +CABLES.OPS["dcc29c06-c3df-434f-9782-736548220bda"]={f:Ops.Gl.Textures.TextureToBase64,objName:"Ops.Gl.Textures.TextureToBase64"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.TextureToCoordinateGrid +// +// ************************************************************** + +Ops.Gl.Textures.TextureToCoordinateGrid = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"rgbe2fp_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\nUNI float aspect;\nUNI float threshold;\n\nvoid main()\n{\n vec4 col=texture(tex,texCoord);\n vec4 outCol=vec4(111111110.0);\n\n if(col.r>threshold)\n {\n outCol=vec4(vec2((texCoord.x-0.5)*aspect,texCoord.y-0.5),0.0,1.0);\n // outCol=vec4(texCoord,0.0,1.0);\n }\n\n\n outColor = outCol;\n}",}; +const + exec = op.inTrigger("Execute"), + inTex = op.inTexture("RGBE Texture"), + inAspect = op.inFloat("Aspect", 1), + inThreshold = op.inFloatSlider("Threshold", 0.2), + next = op.outTrigger("Next"), + outFpTex = op.outTexture("HDR Texture"); + +const tc = new CGL.CopyTexture(op.patch.cgl, "rgbe2hdr", + { + "shader": attachments.rgbe2fp_frag, + "isFloatingPointTexture": true + }); + +const uni1 = new CGL.Uniform(tc.bgShader, "f", "aspect", inAspect); +const uni2 = new CGL.Uniform(tc.bgShader, "f", "threshold", inThreshold); + +exec.onTriggered = () => +{ + if (!inTex.get()) return; + + outFpTex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + outFpTex.set(tc.copy(inTex.get())); + + next.trigger(); +}; + + +}; + +Ops.Gl.Textures.TextureToCoordinateGrid.prototype = new CABLES.Op(); +CABLES.OPS["91f30465-4a4c-4447-9b16-072252f0a977"]={f:Ops.Gl.Textures.TextureToCoordinateGrid,objName:"Ops.Gl.Textures.TextureToCoordinateGrid"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.VideoTexture_v3 +// +// ************************************************************** + +Ops.Gl.Textures.VideoTexture_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Update"), + filename = op.inUrl("file", "video"), + play = op.inValueBool("play"), + loop = op.inValueBool("loop", true), + + volume = op.inValueSlider("Volume", 1), + muted = op.inValueBool("mute", true), + + fps = op.inValueFloat("Update FPS", 30), + tfilter = op.inSwitch("Filter", ["nearest", "linear"], "linear"), + wrap = op.inValueSelect("Wrap", ["repeat", "mirrored repeat", "clamp to edge"], "clamp to edge"), + flip = op.inValueBool("flip", true), + + speed = op.inValueFloat("speed", 1), + time = op.inValueFloat("set time"), + rewind = op.inTriggerButton("Rewind"), + + inPreload = op.inValueBool("Preload", true), + inShowSusp = op.inBool("Show Interaction needed Button", true), + + outNext = op.outTrigger("Next"), + textureOut = op.outTexture("texture", null, "texture"), + outDuration = op.outNumber("duration"), + outProgress = op.outNumber("progress"), + outInteractionNeeded = op.outBoolNum("Interaction Needed"), + outTime = op.outNumber("CurrentTime"), + loading = op.outBoolNum("Loading"), + outPlaying = op.outBoolNum("Playing"), + canPlayThrough = op.outBoolNum("Can Play Through", false), + + outWidth = op.outNumber("Width"), + outHeight = op.outNumber("Height"), + outAspect = op.outNumber("Aspect Ratio"), + outHasError = op.outBoolNum("Has Error"), + outError = op.outString("Error Message"); + +op.setPortGroup("Texture", [tfilter, wrap, flip, fps]); +op.setPortGroup("Audio", [muted, volume]); +op.setPortGroup("Timing", [time, rewind, speed]); + +let videoElementPlaying = false; +let embedded = false; +let interActionNeededButton = false; +let addedListeners = false; +let cgl_filter = 0; +let cgl_wrap = 0; +let tex = null; +let timeout = null; +let firstTime = true; +let needsUpdate = true; +let lastTime = 0; + +const cgl = op.patch.cgl; +const videoElement = document.createElement("video"); +videoElement.setAttribute("playsinline", ""); +videoElement.setAttribute("webkit-playsinline", ""); +videoElement.setAttribute("autoplay", "autoplay"); + +const emptyTexture = CGL.Texture.getEmptyTexture(cgl); +op.toWorkPortsNeedToBeLinked(textureOut); +textureOut.set(tex); +textureOut.set(CGL.Texture.getEmptyTexture(cgl)); +play.onChange = updatePlayState; +filename.onChange = reload; +volume.onChange = updateVolume; +op.onMasterVolumeChanged = updateVolume; + +tfilter.onChange = wrap.onChange = () => +{ + tex = null; +}; + +op.onDelete = () => +{ + if (tex)tex.delete(); + videoElement.remove(); +}; + +inExec.onTriggered = () => +{ + if (performance.now() - lastTime > 1000 / fps.get())needsUpdate = true; + + if (needsUpdate) + { + lastTime = performance.now(); + updateTexture(); + } + + outPlaying.set(!videoElement.paused); + + if (interActionNeededButton && !videoElement.paused && play.get()) + { + // remove button after player says no but plays anyhow after some time... + op.log("weirdness..."); + interActionNeededButton = false; + CABLES.interActionNeededButton.remove("videoplayer"); + } + outInteractionNeeded.set(interActionNeededButton); + + outNext.trigger(); +}; + +function reInitTexture() +{ + if (tex)tex.delete(); + + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + tex = new CGL.Texture(cgl, + { + "wrap": cgl_wrap, + "filter": cgl_filter + }); +} + +rewind.onTriggered = function () +{ + videoElement.currentTime = 0; + textureOut.set(emptyTexture); + needsUpdate = true; +}; + +time.onChange = function () +{ + videoElement.currentTime = time.get() || 0; + needsUpdate = true; +}; + +fps.onChange = function () +{ + needsUpdate = true; +}; + +function doPlay() +{ + videoElement.playbackRate = speed.get(); +} + +function updatePlayState() +{ + if (!embedded) + { + embedVideo(true); + } + + if (play.get()) + { + videoElement.currentTime = time.get() || 0; + + const promise = videoElement.play(); + + if (promise) + promise.then(function () + { + doPlay(); + }).catch(function (error) + { + op.warn("exc", error); + op.log(error); + op.log(videoElement); + + if (videoElement.paused && inShowSusp.get()) + { + interActionNeededButton = true; + CABLES.interActionNeededButton.add(op.patch, "videoplayer", () => + { + interActionNeededButton = false; + videoElement.play(); + doPlay(); + CABLES.interActionNeededButton.remove("videoplayer"); + }); + } + // Automatic playback failed. + // Show a UI element to let the user manually start playback. + }); + } + else videoElement.pause(); +} + +speed.onChange = function () +{ + videoElement.playbackRate = speed.get(); +}; + +loop.onChange = function () +{ + videoElement.loop = loop.get(); +}; + +muted.onChange = function () +{ + videoElement.muted = muted.get(); +}; + +function updateTexture() +{ + const force = needsUpdate; + + if (!filename.get()) + { + textureOut.set(emptyTexture); + return; + } + + if (!videoElementPlaying) return; + + if (!tex)reInitTexture(); + if (tex.width != videoElement.videoWidth || tex.height != videoElement.videoHeight) + { + op.log("video size", videoElement.videoWidth, videoElement.videoHeight); + tex.setSize(videoElement.videoWidth, videoElement.videoHeight); + } + + // tex.height = videoElement.videoHeight; + // tex.width = videoElement.videoWidth; + + outWidth.set(tex.width); + outHeight.set(tex.height); + outAspect.set(tex.width / tex.height); + + if (!canPlayThrough.get()) return; + if (!videoElementPlaying) return; + if (!videoElement) return; + if (videoElement.videoHeight <= 0) + { + op.setUiError("videosize", "video width is 0!"); + op.log(videoElement); + return; + } + if (videoElement.videoWidth <= 0) + { + op.setUiError("videosize", "video height is 0!"); + op.log(videoElement); + return; + } + + const perc = (videoElement.currentTime) / videoElement.duration; + if (!isNaN(perc)) outProgress.set(perc); + + outTime.set(videoElement.currentTime); + + cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, tex.tex); + + if (firstTime) + { + cgl.gl.pixelStorei(cgl.gl.UNPACK_FLIP_Y_WEBGL, flip.get()); + cgl.gl.texImage2D(cgl.gl.TEXTURE_2D, 0, cgl.gl.RGBA, cgl.gl.RGBA, cgl.gl.UNSIGNED_BYTE, videoElement); + tex._setFilter(); + } + else + { + cgl.gl.pixelStorei(cgl.gl.UNPACK_FLIP_Y_WEBGL, flip.get()); + cgl.gl.texSubImage2D(cgl.gl.TEXTURE_2D, 0, 0, 0, cgl.gl.RGBA, cgl.gl.UNSIGNED_BYTE, videoElement); + } + + if (flip.get()) cgl.gl.pixelStorei(cgl.gl.UNPACK_FLIP_Y_WEBGL, false); + + firstTime = false; + + textureOut.set(tex); + needsUpdate = false; + + op.patch.cgl.profileData.profileVideosPlaying++; + + if (videoElement.readyState == 4) loading.set(false); + else loading.set(false); +} + +function initVideo() +{ + videoElement.controls = false; + videoElement.muted = muted.get(); + videoElement.loop = loop.get(); + + needsUpdate = true; + canPlayThrough.set(true); +} + +function updateVolume() +{ + videoElement.volume = Math.min(1, Math.max(0, (volume.get() || 0) * op.patch.config.masterVolume)); +} + +function loadedMetaData() +{ + outDuration.set(videoElement.duration); + updatePlayState(); +} + +function embedVideo(force) +{ + outHasError.set(false); + outError.set(""); + canPlayThrough.set(false); + if (filename.get() && String(filename.get()).length > 1) firstTime = true; + + if (!filename.get()) + { + outError.set(true); + } + + if (inPreload.get() || force) + { + clearTimeout(timeout); + loading.set(true); + videoElement.preload = "true"; + + let url = op.patch.getFilePath(filename.get()); + if (String(filename.get()).indexOf("data:") == 0) url = filename.get(); + if (!url) return; + + op.setUiError("onerror", null); + videoElement.style.display = "none"; + videoElement.setAttribute("src", url); + videoElement.setAttribute("crossOrigin", "anonymous"); + videoElement.playbackRate = speed.get(); + + if (!addedListeners) + { + addedListeners = true; + videoElement.addEventListener("canplaythrough", initVideo, true); + videoElement.addEventListener("loadedmetadata", loadedMetaData); + videoElement.addEventListener("playing", function () { videoElementPlaying = true; }, true); + videoElement.onerror = function () + { + outHasError.set(true); + if (videoElement) + { + outError.set("Error " + videoElement.error.code + "/" + videoElement.error.message); + op.setUiError("onerror", "Could not load video / " + videoElement.error.message, 2); + } + }; + } + embedded = true; + } +} + +function loadVideo() +{ + setTimeout(embedVideo, 100); +} + +function reload() +{ + if (!filename.get()) return; + loadVideo(); +} + + +}; + +Ops.Gl.Textures.VideoTexture_v3.prototype = new CABLES.Op(); +CABLES.OPS["9d66516f-d234-4114-b1d3-67b8e60f5dc6"]={f:Ops.Gl.Textures.VideoTexture_v3,objName:"Ops.Gl.Textures.VideoTexture_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.Textures.WebcamTexture_v3 +// +// ************************************************************** + +Ops.Gl.Textures.WebcamTexture_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"texcopy_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\n\n\nvoid main()\n{\n vec2 tc=vec2(texCoord.x,texCoord.y);\n\n #ifdef FLIPX\n tc.x=1.0-texCoord.x;\n #endif\n #ifdef FLIPY\n tc.y=1.0-texCoord.y;\n #endif\n outColor=texture(tex,tc);\n}",}; +const + inTrigger = op.inTrigger("Render"), + inActive = op.inBool("Active", true), + inGenTex = op.inValueBool("Generate Texture", true), + inInputDevices = op.inDropDown("Webcam Input", ["Default"], "Default"), + inWidth = op.inValueInt("Requested Width", 1280), + inHeight = op.inValueInt("Requested Height", 720), + + flipX = op.inValueBool("Flip X", false), + flipY = op.inValueBool("Flip Y", false), + + inAsDOM = op.inValueBool("Show HTML Element", false), + inCss = op.inStringEditor("CSS", "z-index:99999;\nposition:absolute;\n", "inline-css"), + htmlFlipX = op.inValueBool("Element Flip X", false), + htmlFlipY = op.inValueBool("Element Flip Y", false), + + next = op.outTrigger("Next"), + textureOut = op.outTexture("Texture"), + + outRatio = op.outNumber("Ratio"), + available = op.outBoolNum("Available"), + outWidth = op.outNumber("Size Width"), + outHeight = op.outNumber("Size Height"), + outError = op.outString("Error"), + outElement = op.outObject("HTML Element", null, "element"), + outDevices = op.outArray("Available devices"), + outSelectedDevice = op.outString("Active device"), + outUpdate = op.outTrigger("Texture updated"); + +op.setPortGroup("Camera", [inInputDevices, inWidth, inHeight]); +op.setPortGroup("Texture", [flipX, flipY]); +op.setPortGroup("Video Element", [inAsDOM, inCss, htmlFlipX, htmlFlipY]); + +let tries = 0; +const cgl = op.patch.cgl; +const emptyTexture = CGL.Texture.getEmptyTexture(cgl); +const videoElement = document.createElement("video"); +const eleId = "webcam" + op.id; + +videoElement.setAttribute("id", eleId); +videoElement.setAttribute("autoplay", ""); +videoElement.setAttribute("muted", ""); +videoElement.setAttribute("playsinline", ""); +videoElement.setAttribute("style", inCss.get()); +op.patch.cgl.canvas.parentElement.appendChild(videoElement); + +let tex = null; +let initingDevices = false; +let restarting = false; +let started = false; +let camsLoaded = false; +let loadingId = null; +let currentStream = null; +let camInputDevices = null; +let active = false; +let alreadyRetried = false; +let constraints = null; + +textureOut.set(emptyTexture); + +flipX.onChange = +flipY.onChange = initCopyShader; + +inInputDevices.onChange = + inWidth.onChange = + inHeight.onChange = restartWebcam; +htmlFlipX.onChange = htmlFlipY.onChange = flipVideoElement; +op.onDelete = removeElement; +inAsDOM.onChange = inCss.onChange = updateStyle; + +initTexture(); +updateStyle(); + +let tc = null; + +op.on("loadedValueSet", () => +{ + if (inActive.get()) initDevices(); +}); + +inActive.onChange = () => +{ + if (inActive.get()) initDevices(); +}; + +function initCopyShader() +{ + if (!tc)tc = new CGL.CopyTexture(cgl, "webcamFlippedTexture", { "shader": attachments.texcopy_frag }); + tc.bgShader.toggleDefine("FLIPX", flipX.get()); + tc.bgShader.toggleDefine("FLIPY", !flipY.get()); +} + +function initTexture() +{ + if (tex)tex.delete(); + tex = new CGL.Texture(cgl, { "name": "webcam" }); + if (videoElement) tex.setSize(videoElement.videoWidth, videoElement.videoHeight); +} + +function removeElement() +{ + if (videoElement) videoElement.remove(); +} + +function updateStyle() +{ + if (!inAsDOM.get()) videoElement.setAttribute("style", "display:none;"); + else videoElement.setAttribute("style", inCss.get()); + + inCss.setUiAttribs({ "greyout": !inAsDOM.get() }); + htmlFlipX.setUiAttribs({ "greyout": !inAsDOM.get() }); + htmlFlipY.setUiAttribs({ "greyout": !inAsDOM.get() }); +} + +function flipVideoElement() +{ + if (htmlFlipX.get() && !htmlFlipY.get()) videoElement.style.transform = "scaleX(-1)"; + else if (!htmlFlipX.get() && htmlFlipY.get()) videoElement.style.transform = "scaleY(-1)"; + else if (htmlFlipX.get() && htmlFlipY.get()) videoElement.style.transform = "scale(-1, -1)"; + else videoElement.style.transform = "unset"; +} + +function playCam(shouldPlay) +{ + if (started && camsLoaded) + { + if (shouldPlay) + { + active = true; + videoElement.play(); + } + else + { + active = false; + videoElement.pause(); + } + } +} + +inGenTex.onChange = () => +{ + playCam(inGenTex.get()); +}; + +function updateTexture() +{ + cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, tex.tex); + + cgl.gl.texImage2D(cgl.gl.TEXTURE_2D, 0, cgl.gl.RGBA, cgl.gl.RGBA, cgl.gl.UNSIGNED_BYTE, videoElement); + cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, null); + textureOut.set(emptyTexture); + + if (!tc)initCopyShader(); + if (tc)textureOut.set(tc.copy(tex)); +} + +function stopStream() +{ + if (!currentStream) return; + + playCam(false); + available.set(false); + + currentStream.getTracks().forEach((track) => + { + track.stop(); + }); + currentStream = null; +} + +function camInitComplete(stream) +{ + currentStream = stream; + videoElement.srcObject = stream; + videoElement.onloadedmetadata = (e) => + { + outSelectedDevice.set(stream.getTracks()[0].label); + if (inInputDevices.get() != "Default" && stream.getTracks()[0].label != inInputDevices.get() && tries < 3) + { + tries++; + return restartWebcam(); + } + + const settings = stream.getTracks()[0].getSettings(); + restarting = false; + + const w = settings.width || inWidth.get(); + const h = settings.height || inHeight.get(); + + outHeight.set(h); + outWidth.set(w); + outRatio.set(settings.aspectRatio || w / h); + outError.set(""); + + videoElement.setAttribute("width", settings.width); + videoElement.setAttribute("height", settings.height); + + outElement.set(videoElement); + + tex.setSize(w, h); + + available.set(true); + playCam(inGenTex.get()); + }; +} + +function isCorrectSize() +{ + const constraints = getCamConstraints(); + const check = constraints.video.width == videoElement.videoWidth && constraints.video.height == videoElement.videoHeight; + return check; +} + +function getCamConstraints() +{ + let constr = { "audio": false, "video": {} }; + + if (camsLoaded) + { + let deviceLabel = inInputDevices.get(); + let deviceInfo = null; + + if (!deviceLabel || deviceLabel === "Default" || deviceLabel === "...") + { + deviceInfo = Object.values(camInputDevices)[0]; + } + else + { + deviceInfo = camInputDevices.filter((d) => { return d.label === deviceLabel; }); + if (deviceInfo) + { + deviceInfo = deviceInfo[0]; + } + else + { // otherwise get by number + deviceInfo = Object.values(camInputDevices)[deviceLabel]; + } + + if (!deviceInfo) + { + deviceInfo = Object.values(camInputDevices)[0]; + } + } + constr.video = { "deviceId": { "exact": deviceInfo.deviceId } }; + } + + // constr.video.facingMode = { "exact": inFacing.get() }; + + const w = inWidth.get(); + const h = inHeight.get(); + let width = { "min": 640 }; + let height = { "min": 480 }; + + if (w) + width.ideal = w; + + if (h) + height.ideal = h; + + constr.video.width = width; + constr.video.height = height; + + return constr; +} + +function restartWebcam() +{ + if (!inActive.get()) return; + stopStream(); + restarting = true; + + const constr = getCamConstraints(); + + navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia; + + if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) + { + navigator.mediaDevices.getUserMedia(constr) + .then(camInitComplete) + .catch((error) => + { + restarting = false; + op.logError(error.name + ": " + error.message, error); + outError.set(error.name + ": " + error.message); + }); + } + else if (navigator.getUserMedia) + { + restarting = false; + navigator.getUserMedia(constr, camInitComplete, () => { return available.set(false); }); + } +} + +function initDevices() +{ + if (!inActive.get()) return; + initingDevices = true; + loadingId = cgl.patch.loading.start("Webcam inputs", ""); + const constraints = getCamConstraints(); + + navigator.mediaDevices.getUserMedia(constraints) + .then((res) => { return navigator.mediaDevices.enumerateDevices(); }) + .then((devices) => + { + camInputDevices = devices + .filter((device) => { return device.kind === "videoinput"; }); + + initingDevices = false; + inInputDevices.uiAttribs.values = camInputDevices.map((d, idx) => { return d.label || idx; }); + inInputDevices.uiAttribs.values.unshift("Default"); + outDevices.set(inInputDevices.uiAttribs.values); + cgl.patch.loading.finished(loadingId); + + camsLoaded = true; + + restartWebcam(); + started = true; + }).catch((e) => + { + initingDevices = false; + op.error("error", e); + outError.set(e.name + ": " + e.message); + cgl.patch.loading.finished(loadingId); + camsLoaded = false; + }); +} + +inTrigger.onTriggered = () => +{ + if (!initingDevices && inActive.get()) + { + if (started && camsLoaded && active) + { + updateTexture(); + outUpdate.trigger(); + } + + if (!started && camsLoaded) + { + restartWebcam(); + } + } + + next.trigger(); +}; + + +}; + +Ops.Gl.Textures.WebcamTexture_v3.prototype = new CABLES.Op(); +CABLES.OPS["71c0468d-e942-4574-a91d-b3d7271922d0"]={f:Ops.Gl.Textures.WebcamTexture_v3,objName:"Ops.Gl.Textures.WebcamTexture_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.TriggerOnCanvasResize +// +// ************************************************************** + +Ops.Gl.TriggerOnCanvasResize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const onResize = op.outTrigger("Resized"); + +let listener = op.patch.cgl.on("resize", resize); + +function resize() +{ + onResize.trigger(); +} + +op.onDelete = () => +{ + op.patch.cgl.off(listener); +}; + + +}; + +Ops.Gl.TriggerOnCanvasResize.prototype = new CABLES.Op(); +CABLES.OPS["856de8cf-b8d1-4668-b8ff-80c68bc73ddd"]={f:Ops.Gl.TriggerOnCanvasResize,objName:"Ops.Gl.TriggerOnCanvasResize"}; + + + + +// ************************************************************** +// +// Ops.Gl.ValidTexture +// +// ************************************************************** + +Ops.Gl.ValidTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + const + inTex=op.inTexture("Texture"), + inWhich=op.inSwitch("Default",['Empty','Stripes'],'Empty'), + outTex=op.outTexture("Result"); + + +let tex=CGL.Texture.getEmptyTexture(op.patch.cgl); + +inWhich.onChange=function() +{ + if(inWhich.get()=="Empty")tex=CGL.Texture.getEmptyTexture(op.patch.cgl); + else tex=CGL.Texture.getTempTexture(op.patch.cgl); +}; + +inTex.onChange=function() +{ + let t=inTex.get(); + + if(!t) t=tex; + + outTex.set(t); +}; + +}; + +Ops.Gl.ValidTexture.prototype = new CABLES.Op(); +CABLES.OPS["51c24850-aa8b-41e4-936e-68ba718b5e39"]={f:Ops.Gl.ValidTexture,objName:"Ops.Gl.ValidTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.ViewPortSize +// +// ************************************************************** + +Ops.Gl.ViewPortSize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Exec"), + next = op.outTrigger("Next"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outW = op.outNumber("Width"), + outH = op.outNumber("Height"); + +exec.onTriggered = function () +{ + const vp = op.patch.cgl.getViewPort(); + + outX.set(vp[0]); + outY.set(vp[1]); + outW.set(vp[2]); + outH.set(vp[3]); + + next.trigger(); +}; + + +}; + +Ops.Gl.ViewPortSize.prototype = new CABLES.Op(); +CABLES.OPS["7cb99d8f-d7ef-478e-902b-54e054e387a0"]={f:Ops.Gl.ViewPortSize,objName:"Ops.Gl.ViewPortSize"}; + + + + +// ************************************************************** +// +// Ops.Gl.Viewport2 +// +// ************************************************************** + +Ops.Gl.Viewport2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + ratio = op.inDropDown("ratio", [0.5, 0.5625, 0.75, 1, 1.25, 1.3333333333, 1.777777777778, 2.33333333333333, 3, 4]), + trigger = op.outTrigger("trigger"); + +ratio.set(1.777777777778); + +const posX = op.inValueSelect("Pos X", ["Left", "Right", "Center"], "Center"); + +const cgl = op.patch.cgl; + +let w = 1000, h = 1000; + +function resize() +{ + let _w = cgl.canvasHeight * ratio.get(); + let _h = cgl.canvasHeight; + let _x = 0; + let _y = 0; + + if (_w > cgl.canvasWidth) + { + _w = cgl.canvasWidth; + _h = cgl.canvasWidth / ratio.get(); + } + + if (_w < cgl.canvasWidth) _x = (cgl.canvasWidth - _w) / 2; + if (_h < cgl.canvasHeight) _y = (cgl.canvasHeight - _h) / 2; + + if (_w != w || _h != h) + { + w = _w; + h = _h; + + const vp = cgl.getViewPort(); + + // cgl.setViewPort(0,vp[2]-h,w,h); + + for (let i = 0; i < op.patch.ops.length; i++) + { + if (op.patch.ops[i].onResize)op.patch.ops[i].onResize(); + } + } +} + +op.onDelete = function () +{ + cgl.resetViewPort(); +}; + +const prevViewPort = []; + +render.onTriggered = function () +{ + resize(); + + w = Math.round(w); + h = Math.round(h); + + cgl.gl.enable(cgl.gl.SCISSOR_TEST); + const vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + vp[2] = cgl.canvasWidth; + vp[3] = cgl.canvasHeight; + vp[0] = 0; + vp[1] = 0; + + let x = 0; + if (posX.get() == "Right") x = vp[2] - w; + if (posX.get() == "Center") x = (vp[2] - w) / 2; + + x = Math.round(x); + + cgl.gl.scissor(x, vp[3] - h, w, h); + cgl.setViewPort(x, vp[3] - h, w, h); + + mat4.perspective(cgl.pMatrix, 45, ratio.get(), 0.1, 1100.0); + + trigger.trigger(); + cgl.gl.disable(cgl.gl.SCISSOR_TEST); + cgl.setViewPort(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); +}; + + +}; + +Ops.Gl.Viewport2.prototype = new CABLES.Op(); +CABLES.OPS["1444ab6f-a4da-462c-8abe-a1deef2084da"]={f:Ops.Gl.Viewport2,objName:"Ops.Gl.Viewport2"}; + + + + +// ************************************************************** +// +// Ops.Gl.WebGlVersion +// +// ************************************************************** + +Ops.Gl.WebGlVersion = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const ver = op.outNumber("Major Version", op.patch.cgl.glVersion); + +op.onLoaded = function () +{ + ver.set(op.patch.cgl.glVersion); +}; + + +}; + +Ops.Gl.WebGlVersion.prototype = new CABLES.Op(); +CABLES.OPS["d6554d6c-0742-4559-b0a4-29ad0ae1e25b"]={f:Ops.Gl.WebGlVersion,objName:"Ops.Gl.WebGlVersion"}; + + + + +// ************************************************************** +// +// Ops.Graphics.Geometry.ObjGeometry +// +// ************************************************************** + +Ops.Graphics.Geometry.ObjGeometry = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// todo: support quads +const + inStr = op.inString("Obj", ""), + outGeom = op.outObject("Geometry", null, "geometry"), + outStatus = op.outString("Status"); + +inStr.onChange = () => { parse(inStr.get()); }; + +function obj(name) +{ + const a = { + "prim": 0, + "name": name, + "verts": [], + "vertNorms": [], + "texCoords": [], + "indexVerts": [], + "indexNorms": [], + "indexTexcoords": [] + }; + + return a; +} + +function parseTri(parts, o) +{ + for (let j = 0; j < parts.length; j++) + { + const faceParts = parts[j].split("/"); + + if (faceParts.length == 3) + { + o.indexVerts.push(parseInt(faceParts[0]) - 1); + if (faceParts[1])o.indexTexcoords.push(parseInt(faceParts[1]) - 1); + o.indexNorms.push(parseInt(faceParts[2]) - 1); + } + else if (faceParts.length == 1) + { + o.indexVerts.push(parseInt(faceParts[0]) - 1); + } + else if (faceParts.length == 2) + { + o.indexVerts.push(parseInt(faceParts[0]) - 1); + o.indexTexcoords.push(parseInt(faceParts[2]) - 1); + } + else op.warn("unknown face structure", faceParts); + } +} + +function parse(str) +{ + if (!str) return outGeom.set(null); + + let objects = []; + let strStatus = ""; + const lines = str.split("\n"); + + { + let o = obj("unknown"); + + for (let i = 0; i < lines.length; i++) + { + lines[i] = lines[i].replaceAll(" ", " "); + lines[i] = lines[i].replaceAll(" \r", ""); + lines[i] = lines[i].replaceAll(" \n", "\n"); + + if (lines[i].length < 3 || lines[i].charAt(0) == "#") continue; + + // vertices + if (lines[i].charAt(0) == "o" && lines[i].charAt(1) == " ") + { + const parts = lines[i].split(" "); + o.name = obj(parts[1] || "unknown"); + continue; + } + + if (lines[i].charAt(0) == "v") + { + if (o.indexVerts.length > 0) + { + // new object... + if (o.indexVerts.length > 0) objects.push(o); + o = obj("unknown"); + } + + // vertices + if (lines[i].charAt(1) == " ") + { + const parts = lines[i].split(" "); + o.verts.push(parseFloat(parts[1]), parseFloat(parts[2]), parseFloat(parts[3])); + continue; + } + + // texcoords + if (lines[i].charAt(1) == "t" && lines[i].charAt(2) == " ") + { + const parts = lines[i].split(" "); + o.texCoords.push(parseFloat(parts[1]), parseFloat(parts[2]), parseFloat(parts[3])); + continue; + } + + // normals + if (lines[i].charAt(1) == "n" && lines[i].charAt(2) == " ") + { + const parts = lines[i].split(" "); + o.vertNorms.push(parseFloat(parts[1]), parseFloat(parts[2]), parseFloat(parts[3])); + continue; + } + } + + // faces + if (lines[i].charAt(0) == "f" && lines[i].charAt(1) == " ") + { + const parts = lines[i].split(" "); + parts.shift(); + while (parts[parts.length - 1] == "") + { + parts.pop(); + } + + if (o.prim == 0) o.prim = parts.length; + + if (o.prim == 3 && parts.length == 3) // tris + { + parseTri(parts, o); + } + else if (o.prim == 4 && parts.length == 4) // quads + { + parseTri([parts[0], parts[1], parts[2]], o); + parseTri([parts[0], parts[2], parts[3]], o); + } + continue; + } + } + objects.push(o); + } + + op.log("objects", objects); + + let finalgeom = new CGL.Geometry("objfile"); + for (let io = 0; io < objects.length; io++) + { + const geom = new CGL.Geometry("objfile"); + geom.clear(); + + let o = objects[io]; + op.log(o); + + if (o.indexVerts.length > 0) + { + let gVerts = []; + let gNorms = []; + let gTexCoords = []; + let isIndexed = true; + + for (let i = 0; i < o.indexVerts.length; i++) + { + if (isIndexed) + { + if (!(o.indexVerts[i] == o.indexNorms[i] && o.indexNorms[i] == o.indexTexcoords[i])) + { + op.log("false"); + isIndexed = false; + break; + } + } + } + + op.log("isIndexed", isIndexed); + if (isIndexed) + { + geom.verticesIndices = o.indexVerts; + gNorms = o.vertNorms; + + gTexCoords = []; + for (let i = 0; i < o.texCoords.length; i += 3) + { + gTexCoords.push(o.texCoords[i + 0], o.texCoords[i + 1]); + } + gVerts = o.verts; + } + else + for (let i = 0; i < o.indexVerts.length; i++) + { + for (let j = 0; j < 3; j++) + { + gVerts.push(o.verts[o.indexVerts[i] * 3 + j]); + if (o.indexNorms.length > 0)gNorms.push(o.vertNorms[o.indexNorms[i] * 3 + j]); + if (o.indexTexcoords.length > 0 && j < 2)gTexCoords.push(o.texCoords[o.indexTexcoords[i] * 3 + j]); + } + } + + geom.vertices = gVerts; + geom.vertexNormals = gNorms; + geom.texCoords = gTexCoords; + + if (geom.vertexNormals.length == 0) + { + geom.calculateNormals(); + } + } + finalgeom.merge(geom); + // finalgeom = geom; + } + + finalgeom.calcTangentsBitangents(); + + outGeom.set(null); + outGeom.set(finalgeom); + + outStatus.set(strStatus); +} + + +}; + +Ops.Graphics.Geometry.ObjGeometry.prototype = new CABLES.Op(); +CABLES.OPS["8cebb27b-2a6e-4580-a543-b36a91f14879"]={f:Ops.Graphics.Geometry.ObjGeometry,objName:"Ops.Graphics.Geometry.ObjGeometry"}; + + + + +// ************************************************************** +// +// Ops.Html.AppendChild_v2 +// +// ************************************************************** + +Ops.Html.AppendChild_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// constants +let CANVAS_ELEMENT = op.patch.cgl.canvas.parentElement; + +// variables +let lastParent = null; +let lastChild = null; + +// inputs +let parentPort = op.inObject("Parent", null, "element"); +let childPort = op.inObject("Child", null, "element"); + +// outputs +let parentOutPort = op.outObject("Parent Out", null, "element"); +let childOutPort = op.outObject("Child Out", null, "element"); + +// change listeners +parentPort.onChange = update; +childPort.onChange = update; + +// functions + +function update() +{ + let parent = parentPort.get(); + let child = childPort.get(); + if (parent !== lastParent) + { + if (parent) + { + handleParentConnect(parent, child); + } + else + { + handleParentDisconnect(parent, child); + } + lastParent = parent; + } + if (child !== lastChild) + { + if (child) + { + handleChildConnect(parent, child); + } + else + { + handleChildDisconnect(parent, child); + } + lastChild = child; + } + parentOutPort.set(parent); + childOutPort.set(child); +} + +function handleParentConnect(parent, child) +{ + if (child) + { + parent.appendChild(child); + } +} + +function handleParentDisconnect(parent, child) +{ + if (child) + { + CANVAS_ELEMENT.appendChild(child); // if there is no parent, append to patch + } +} + +function handleChildConnect(parent, child) +{ + if (parent) + { + parent.appendChild(child); + } +} + +function handleChildDisconnect(parent, child) +{ + if (lastChild) + { + CANVAS_ELEMENT.appendChild(lastChild); + } +} + + +}; + +Ops.Html.AppendChild_v2.prototype = new CABLES.Op(); +CABLES.OPS["e15cfbc7-d2fa-4348-8964-66d02aec77aa"]={f:Ops.Html.AppendChild_v2,objName:"Ops.Html.AppendChild_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.AudioMediaElement +// +// ************************************************************** + +Ops.Html.AudioMediaElement = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + fileName = op.inUrl("file", "audio"), + inPlay = op.inValueBool("Play"), + volume = op.inValueSlider("Volume"), + + doLoop = op.inValueBool("Loop"), + outPlaying = op.outNumber("Playing"), + outEle = op.outObject("Element", null, "element"), + outEnded = op.outTrigger("Has Ended"); + +volume.set(1.0); +let audio = null; +let playing = false; +outPlaying.set(false); +volume.onChange = updateVolume; + +function play() +{ + if (audio) + { + playing = true; + audio.play(); + } +} + +inPlay.onChange = function () +{ + if (inPlay.get()) + { + play(); + } + else + { + playing = false; + audio.pause(); + } + outPlaying.set(playing); +}; + +this.onDelete = function () +{ + if (audio) audio.pause(); +}; + +doLoop.onChange = function () +{ + if (audio) audio.loop = doLoop.get(); +}; + +function playPause() +{ + if (!audio) return; + + if (op.patch.timer.isPlaying()) audio.play(); + else audio.pause(); +} + +function updateVolume() +{ + if (audio)audio.volume = volume.get() * op.patch.config.masterVolume; +} + +op.onMasterVolumeChanged = updateVolume; + +fileName.onChange = function () +{ + if (!fileName.get()) return; + + let loadingId = op.patch.loading.start("audioplayer", fileName.get()); + + if (audio) + { + audio.pause(); + outPlaying.set(false); + } + audio = new Audio(); + audio.crossOrigin = "anonymous"; + audio.src = op.patch.getFilePath(fileName.get()); + audio.loop = doLoop.get(); + audio.controls = "true"; + audio.crossOrigin = "anonymous"; + + outEle.set(audio); + + var canplaythrough = function () + { + if (inPlay.get()) play(); + op.patch.loading.finished(loadingId); + audio.removeEventListener("canplaythrough", canplaythrough, false); + }; + + audio.addEventListener("canplaythrough", canplaythrough, false); + + audio.addEventListener("ended", function () + { + outPlaying.set(false); + playing = false; + outEnded.trigger(); + if (doLoop.get()) play(); + }, false); +}; + + +}; + +Ops.Html.AudioMediaElement.prototype = new CABLES.Op(); +CABLES.OPS["82c14cd9-e2e7-4944-a04e-cca1bf3ed63e"]={f:Ops.Html.AudioMediaElement,objName:"Ops.Html.AudioMediaElement"}; + + + + +// ************************************************************** +// +// Ops.Html.BackgroundImage_v2 +// +// ************************************************************** + +Ops.Html.BackgroundImage_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inEle = op.inObject("Element"), + active = op.inValueBool("active", true), + filename = op.inUrl("image file"), + inSize = op.inValueSelect("Size", ["auto", "length", "cover", "contain", "initial", "inherit", "75%", "50%", "40%", "30%", "25%", "20%", "10%"], "cover"), + inRepeat = op.inValueSelect("Repeat", ["no-repeat", "repeat", "repeat-x", "repeat-y"], "no-repeat"), + inPosition = op.inValueSelect("Position", ["left top", "left center", "left bottom", "right top", "right center", "right bottom", "center top", "center center", "center bottom"], "center center"), + outEle = op.outObject("HTML Element"); + +op.onLoadedValueSet = +op.onLoaded = +inPosition.onChange = +inSize.onChange = +inEle.onChange = +inRepeat.onChange = +active.onChange = +filename.onChange = update; + +let ele = null; +let cacheBust = null; + +op.onFileChanged = function (fn) +{ + if (filename.get() && filename.get().indexOf(fn) > -1) + { + if (ele)ele.style["background-image"] = "none"; + cacheBust = CABLES.uuid(); + update(); + } +}; + +function remove() +{ + if (ele) + { + ele.style["background-image"] = "none"; + ele.style["background-size"] = "initial"; + ele.style["background-position"] = "initial"; + ele.style["background-repeat"] = "initial"; + } +} + +function update() +{ + if (!inEle.get()) + { + remove(); + return; + } + + op.setUiAttrib({ "extendTitle": CABLES.basename(filename.get()) }); + + ele = inEle.get(); + + if (ele && ele.style && filename.get()) + { + if (!active.get()) + { + ele.style["background-image"] = "none"; + } + else + { + let cb = ""; + if (cacheBust)cb = "?cb=" + cacheBust; + + ele.style["background-image"] = "url(" + op.patch.getFilePath(String(filename.get())) + cb + ")"; + ele.style["background-size"] = inSize.get(); + ele.style["background-position"] = inPosition.get(); + ele.style["background-repeat"] = inRepeat.get(); + } + } + // else + // { + // // really needed ? + // setTimeout(update,100); + // } + + outEle.set(inEle.get()); +} + + +}; + +Ops.Html.BackgroundImage_v2.prototype = new CABLES.Op(); +CABLES.OPS["081c4328-984d-4acd-8758-5d1379cc3a30"]={f:Ops.Html.BackgroundImage_v2,objName:"Ops.Html.BackgroundImage_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.BrowserSpecificFile_v2 +// +// ************************************************************** + +Ops.Html.BrowserSpecificFile_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +// input ports +const chromeFilePort = op.inUrl("Chrome File"); +const firefoxFilePort = op.inUrl("Firefox File"); +const safariFilePort = op.inUrl("Safari File"); +const ieFilePort = op.inUrl("IE <= 11 File"); +const edgeFilePort = op.inUrl("Edge File"); +const operaFilePort = op.inUrl("Opera File"); +const defaultFilePort = op.inUrl("Default File"); + +// output port +const outFile = op.outString("Browser Specific File"); +const detectedBrowserPort = op.outString("Detected Browser"); + +// change listeners +chromeFilePort.onChange = checkBrowserAndSetOutput; +firefoxFilePort.onChange = checkBrowserAndSetOutput; +safariFilePort.onChange = checkBrowserAndSetOutput; +ieFilePort.onChange = checkBrowserAndSetOutput; +edgeFilePort.onChange = checkBrowserAndSetOutput; +operaFilePort.onChange = checkBrowserAndSetOutput; +defaultFilePort.onChange = checkBrowserAndSetOutput; + +const pf = platform; + +// functions +const isOpera = pf.name === "Opera" || pf.name === "Opera Mobile"; +const isSafari = pf.name === "Safari" || pf.name === "Firefox for iOS"; +const isIE = pf.name === "IE" || pf.name === "IE Mobile"; +const isEdge = pf.name === "Microsoft Edge"; +const isChrome = pf.name === "Chrome" || pf.name === "Chrome Mobile"; +const isFirefox = pf.name === "Firefox" || pf.name === "Firefox Mobile"; + + +checkBrowserAndSetOutput(); + +function checkBrowserAndSetOutput() { + if(isOpera) { + outFile.set(operaFilePort.get() || defaultFilePort.get()); + } else if(isFirefox) { + detectedBrowserPort.set("Firefox"); + outFile.set(firefoxFilePort.get() || defaultFilePort.get()); + } else if(isSafari) { + detectedBrowserPort.set("Safari"); + outFile.set(safariFilePort.get() || defaultFilePort.get()); + } else if(isIE) { + detectedBrowserPort.set("IE"); + outFile.set(ieFilePort.get() || defaultFilePort.get()); + } else if(isEdge) { + detectedBrowserPort.set("Edge"); + outFile.set(edgeFilePort.get() || defaultFilePort.get()); + } else if(isChrome) { + detectedBrowserPort.set("Chrome"); + outFile.set(chromeFilePort.get() || defaultFilePort.get()); + } else { + detectedBrowserPort.set(pf.name); + outFile.set(defaultFilePort.get()); + } + detectedBrowserPort.set(pf.name); +} + + +}; + +Ops.Html.BrowserSpecificFile_v2.prototype = new CABLES.Op(); +CABLES.OPS["d6432fde-9bc5-4183-a9be-bd87ffcb5392"]={f:Ops.Html.BrowserSpecificFile_v2,objName:"Ops.Html.BrowserSpecificFile_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.CSSFilter +// +// ************************************************************** + +Ops.Html.CSSFilter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inEle = op.inObject("Element"); +const inMethod = op.inValueSelect("method", ["-", "blur", "brightness", "contrast", "grayscale", "hue-rotate", "invert", "opacity", "saturate", "sepia"]); +const inVal = op.inValue("Value"); + +let suffix = ""; +let prefix = ""; + +inVal.onChange = setValue; +inEle.onChange = setValue; + +let oldEle = null; + +function getCSSFilterString() +{ + return inMethod.get() + "(" + inVal.get() + suffix + ")"; +} + +inEle.onLinkChanged = function () +{ + // remove style when deleting op + if (inEle.isLinked()) return; + + const ele = oldEle;// inEle.get(); + + if (ele && ele.style) + { + let filter = ele.style.filter; + var str = ""; + + if (filter && filter.length > 0) + { + var str = ""; + let parts = filter.split(" "); + for (let i = 0; i < parts.length; i++) + { + if (parts[i].indexOf(inMethod.get()) == 0) + parts[i] = ""; + } + + str = parts.join(" "); + } + ele.style.filter = str; + } +}; + +function setValue() +{ + const ele = inEle.get(); + let str = ""; + + if (ele && ele.style) + { + if (ele != oldEle) oldEle = ele; + let foundMyFilter = false; + let filter = ele.style.filter; + + if (filter && filter.length > 0) + { + let parts = filter.split(" "); + for (let i = 0; i < parts.length; i++) + { + if (parts[i].indexOf(inMethod.get()) == 0) + { + foundMyFilter = true; + parts[i] = getCSSFilterString(); + } + } + + str = parts.join(" "); + } + + if (!foundMyFilter) + str += " " + getCSSFilterString(); + + ele.style.filter = str; + } +} + +inMethod.onChange = function () +{ + let m = inMethod.get(); + + prefix = inMethod.get() + ":"; + + if (m == "blur") suffix = "px"; + if (m == "brightness") suffix = ""; + if (m == "contrast") suffix = "%"; + if (m == "grayscale") suffix = "%"; + if (m == "hue-rotate") suffix = "deg"; + if (m == "invert") suffix = "%"; + if (m == "opacity") suffix = "%"; + if (m == "saturate") suffix = ""; + if (m == "sepia") suffix = "%"; + setValue(); +}; + + +}; + +Ops.Html.CSSFilter.prototype = new CABLES.Op(); +CABLES.OPS["33befabf-7eef-45f6-869f-30e0e4f44739"]={f:Ops.Html.CSSFilter,objName:"Ops.Html.CSSFilter"}; + + + + +// ************************************************************** +// +// Ops.Html.CSSPropertyString +// +// ************************************************************** + +Ops.Html.CSSPropertyString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inEle = op.inObject("Element"), + inProperty = op.inString("Property"), + inValue = op.inString("Value"), + outEle = op.outObject("HTML Element"); + +op.setPortGroup("Element", [inEle]); +op.setPortGroup("Attributes", [inProperty, inValue]); + +inProperty.onChange = updateProperty; +inValue.onChange = update; +let ele = null; + +inEle.onChange = inEle.onLinkChanged = function () +{ + if (ele && ele.style) + { + ele.style[inProperty.get()] = "initial"; + } + update(); +}; + +function updateProperty() +{ + update(); + op.setUiAttrib({ "extendTitle": inProperty.get() + "" }); +} + +function update() +{ + ele = inEle.get(); + if (ele && ele.style) + { + const str = inValue.get(); + try + { + ele.style[inProperty.get()] = str; + } + catch (e) + { + op.logError(e); + } + } + + outEle.set(inEle.get()); +} + + +}; + +Ops.Html.CSSPropertyString.prototype = new CABLES.Op(); +CABLES.OPS["a7abdfb9-4c2a-4ddb-8fc6-55b3fdfbdaf3"]={f:Ops.Html.CSSPropertyString,objName:"Ops.Html.CSSPropertyString"}; + + + + +// ************************************************************** +// +// Ops.Html.CSSProperty_v2 +// +// ************************************************************** + +Ops.Html.CSSProperty_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inEle = op.inObject("Element"), + inProperty = op.inString("Property"), + inValue = op.inFloat("Value"), + inValueSuffix = op.inString("Value Suffix", "px"), + outEle = op.outObject("HTML Element"); + +op.setPortGroup("Element", [inEle]); +op.setPortGroup("Attributes", [inProperty, inValue, inValueSuffix]); + +inProperty.onChange = updateProperty; +inValue.onChange = update; +inValueSuffix.onChange = update; +let ele = null; + +inEle.onChange = inEle.onLinkChanged = function () +{ + if (ele && ele.style) + { + ele.style[inProperty.get()] = "initial"; + } + update(); +}; + +function updateProperty() +{ + update(); + op.setUiAttrib({ "extendTitle": inProperty.get() + "" }); +} + +function update() +{ + ele = inEle.get(); + if (ele && ele.style) + { + const str = inValue.get() + inValueSuffix.get(); + try + { + if (ele.style[inProperty.get()] != str) + ele.style[inProperty.get()] = str; + } + catch (e) + { + op.logError(e); + } + } + else + { + setTimeout(update, 50); + } + + outEle.set(inEle.get()); +} + + +}; + +Ops.Html.CSSProperty_v2.prototype = new CABLES.Op(); +CABLES.OPS["c179aa0e-b558-4130-8c2d-2deab2919a07"]={f:Ops.Html.CSSProperty_v2,objName:"Ops.Html.CSSProperty_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.CSS_v2 +// +// ************************************************************** + +Ops.Html.CSS_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const code = op.inStringEditor("css code"); + +code.setUiAttribs( + { + "editorSyntax": "css", + "ignoreBigPort": true + }); + +let styleEle = null; +const eleId = "css_" + CABLES.uuid(); + +code.onChange = update; +update(); + +function getCssContent() +{ + let css = code.get(); + if (css) + { + let patchId = null; + if (op.storage && op.storage.blueprint && op.storage.blueprint.patchId) + { + patchId = op.storage.blueprint.patchId; + } + css = css.replace(new RegExp("{{ASSETPATH}}", "g"), op.patch.getAssetPath(patchId)); + } + return css; +} + +function update() +{ + styleEle = document.getElementById(eleId); + + if (styleEle) + { + styleEle.textContent = getCssContent(); + } + else + { + styleEle = document.createElement("style"); + styleEle.type = "text/css"; + styleEle.id = eleId; + styleEle.textContent = attachments.css_spinner; + + const head = document.getElementsByTagName("body")[0]; + head.appendChild(styleEle); + } +} + +op.onDelete = function () +{ + styleEle = document.getElementById(eleId); + if (styleEle)styleEle.remove(); +}; + + +}; + +Ops.Html.CSS_v2.prototype = new CABLES.Op(); +CABLES.OPS["a56d3edd-06ad-44ed-9810-dbf714600c67"]={f:Ops.Html.CSS_v2,objName:"Ops.Html.CSS_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.CablesLink +// +// ************************************************************** + +Ops.Html.CablesLink = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let size = op.inValue("size", 40); +let opacity = op.inValue("Opacity", 0.5); + +let element = document.createElement("div"); + +size.onChange = function () +{ + element.style.width = size.get() + "px"; + element.style.height = size.get() + "px"; +}; + +opacity.onChange = function () +{ + element.style.opacity = opacity.get(); +}; + +element.style.padding = "10px"; +element.style = "cableslink"; +element.style.position = "absolute"; +element.style.right = "0px"; +element.style.bottom = "0px"; +element.style.width = "40px"; +element.style.height = "40px"; +element.style.opacity = "0.4"; +element.style.cursor = "pointer"; +element.style["background-image"] = "url(https://cables.gl/img/cables-logo.svg)"; +element.style["z-index"] = "9999"; +element.style["background-size"] = "80%"; +element.style["background-repeat"] = "no-repeat"; + +let canvas = op.patch.cgl.canvas.parentElement; +canvas.appendChild(element); + +element.addEventListener("click", function () +{ + document.location.href = "https://cables.gl"; +}); + +op.onDelete = function () +{ + element.remove(); +}; + + +}; + +Ops.Html.CablesLink.prototype = new CABLES.Op(); +CABLES.OPS["aba114d1-b042-484a-9b66-a9bfe6590b3d"]={f:Ops.Html.CablesLink,objName:"Ops.Html.CablesLink"}; + + + + +// ************************************************************** +// +// Ops.Html.Cursor_v2 +// +// ************************************************************** + +Ops.Html.Cursor_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTriggerButton("Update"), + + cursorPort = op.inDropDown("CSS Cursors", ["auto", "crosshair", "pointer", "hand", "move", "n-resize", "ne-resize", "e-resize", "se-resize", "s-resize", "sw-resize", "w-resize", "nw-resize", "ew-resize", "text", "wait", "help", "none"], "pointer"), + + next = op.outTrigger("Next"); + +const cursorStr = ""; + +exec.onLinkChanged = +next.onLinkChanged = () => +{ + op.patch.cgl.setCursor("auto"); +}; + +exec.onTriggered = () => +{ + op.patch.cgl.setCursor(cursorPort.get()); + next.trigger(); +}; + + +}; + +Ops.Html.Cursor_v2.prototype = new CABLES.Op(); +CABLES.OPS["39486799-bdad-42d3-a300-4642c23578a8"]={f:Ops.Html.Cursor_v2,objName:"Ops.Html.Cursor_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.DivElement_v2 +// +// ************************************************************** + +Ops.Html.DivElement_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inText = op.inString("Text", "Hello Div"), + inId = op.inString("Id"), + inClass = op.inString("Class"), + inStyle = op.inValueEditor("Style", "position:absolute;\nz-index:100;", "inline-css"), + inInteractive = op.inValueBool("Interactive", false), + inVisible = op.inValueBool("Visible", true), + inBreaks = op.inValueBool("Convert Line Breaks", false), + inPropagation = op.inValueBool("Propagate Click-Events", true), + outElement = op.outObject("DOM Element", null, "element"), + outHover = op.outBoolNum("Hover"), + outClicked = op.outTrigger("Clicked"); + +let listenerElement = null; +let oldStr = null; +let prevDisplay = "block"; +let div = null; + +const canvas = op.patch.cgl.canvas.parentElement; + +createElement(); + +inClass.onChange = updateClass; +inBreaks.onChange = inText.onChange = updateText; +inStyle.onChange = updateStyle; +inInteractive.onChange = updateInteractive; +inVisible.onChange = updateVisibility; + +updateText(); +updateStyle(); +warning(); +updateInteractive(); + +op.onDelete = removeElement; + +outElement.onLinkChanged = updateStyle; + +function createElement() +{ + div = document.createElement("div"); + div.dataset.op = op.id; + div.classList.add("cablesEle"); + + if (inId.get()) div.id = inId.get(); + + canvas.appendChild(div); + outElement.set(div); +} + +function removeElement() +{ + if (div) removeClasses(); + if (div && div.parentNode) div.parentNode.removeChild(div); + oldStr = null; + div = null; +} + +function setCSSVisible(visible) +{ + if (!visible) + { + div.style.visibility = "hidden"; + prevDisplay = div.style.display || "block"; + div.style.display = "none"; + } + else + { + // prevDisplay=div.style.display||'block'; + if (prevDisplay == "none") prevDisplay = "block"; + div.style.visibility = "visible"; + div.style.display = prevDisplay; + } +} + +function updateVisibility() +{ + setCSSVisible(inVisible.get()); +} + +function updateText() +{ + let str = inText.get(); + + if (oldStr === str) return; + oldStr = str; + + if (str && inBreaks.get()) str = str.replace(/(?:\r\n|\r|\n)/g, "
    "); + + if (div.innerHTML != str) div.innerHTML = str; + outElement.set(null); + outElement.set(div); +} + +// inline css inisde div +function updateStyle() +{ + if (!div) return; + // if (inStyle.get() != div.style) + // { + div.setAttribute("style", inStyle.get()); + updateVisibility(); + outElement.set(null); + outElement.set(div); + // } + + if (!div.parentElement) + { + canvas.appendChild(div); + } + + warning(); +} + +let oldClassesStr = ""; + +function removeClasses() +{ + if (!div) return; + + const classes = (inClass.get() || "").split(" "); + for (let i = 0; i < classes.length; i++) + { + if (classes[i]) div.classList.remove(classes[i]); + } + oldClassesStr = ""; +} + +function updateClass() +{ + const classes = (inClass.get() || "").split(" "); + const oldClasses = (oldClassesStr || "").split(" "); + + let found = false; + + for (let i = 0; i < oldClasses.length; i++) + { + if ( + oldClasses[i] && + classes.indexOf(oldClasses[i].trim()) == -1) + { + found = true; + div.classList.remove(oldClasses[i]); + } + } + + for (let i = 0; i < classes.length; i++) + { + if (classes[i]) + { + div.classList.add(classes[i].trim()); + } + } + + oldClassesStr = inClass.get(); + warning(); +} + +function onMouseEnter(e) +{ + outHover.set(true); +} + +function onMouseLeave(e) +{ + outHover.set(false); +} + +function onMouseClick(e) +{ + if (!inPropagation.get()) + { + e.stopPropagation(); + } + outClicked.trigger(); +} + +function updateInteractive() +{ + removeListeners(); + if (inInteractive.get()) addListeners(); +} + +inId.onChange = function () +{ + div.id = inId.get(); +}; + +function removeListeners() +{ + if (listenerElement) + { + listenerElement.removeEventListener("pointerdown", onMouseClick); + listenerElement.removeEventListener("pointerleave", onMouseLeave); + listenerElement.removeEventListener("pointerenter", onMouseEnter); + listenerElement = null; + } +} + +function addListeners() +{ + if (listenerElement)removeListeners(); + + listenerElement = div; + + if (listenerElement) + { + listenerElement.addEventListener("pointerdown", onMouseClick); + listenerElement.addEventListener("pointerleave", onMouseLeave); + listenerElement.addEventListener("pointerenter", onMouseEnter); + } +} + +op.addEventListener("onEnabledChange", function (enabled) +{ + removeElement(); + if (enabled) + { + createElement(); + updateStyle(); + updateClass(); + updateText(); + updateInteractive(); + } + // if(enabled) updateVisibility(); + // else setCSSVisible(false); +}); + +function warning() +{ + if (inClass.get() && inStyle.get()) + { + op.setUiError("error", "DIV uses external and inline CSS", 1); + } + else + { + op.setUiError("error", null); + } +} + + +}; + +Ops.Html.DivElement_v2.prototype = new CABLES.Op(); +CABLES.OPS["db36db6d-83e4-4d27-b84c-8a20067aaffc"]={f:Ops.Html.DivElement_v2,objName:"Ops.Html.DivElement_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.ElementChilds_v2 +// +// ************************************************************** + +Ops.Html.ElementChilds_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + parentPort = op.inObject("Parent", null, "element"), + outParent = op.outObject("Parent Out", null, "element"); + +const canvas = op.patch.cgl.canvas.parentElement; + +const inPorts = []; +for (let i = 0; i < 10; i++) +{ + const p = op.inObject("Child " + (i + 1)); + inPorts.push(p); + p.onChange = () => + { + rebuild(); + if (!p.get()) + { + const selector = "[data-cables-child-id='" + op.id + "_" + i + "']"; + const currentChild = canvas.querySelector(selector); + if (currentChild) delete currentChild.dataset.cablesChildId; + } + }; + p.onLinkChanged = () => + { + if (!p.isLinked()) + { + const selector = "[data-cables-child-id='" + op.id + "_" + i + "']"; + const currentChild = canvas.querySelector(selector); + if (currentChild) currentChild.remove(); + } + }; +} + +parentPort.onLinkChanged = () => +{ + if (!parentPort.isLinked()) + { + cleanUp(); + } + else + { + rebuild(); + } +}; + +outParent.onLinkChanged = () => +{ + if (!outParent.isLinked()) + { + const parentDiv = parentPort.get(); + if (parentDiv && parentDiv.dataset.op) + { + const inDoc = canvas.querySelector("[data-op=' " + parentDiv.dataset.op + " ']"); + if (!inDoc) + { + canvas.appendChild(parentDiv); + } + } + } +}; + +parentPort.onChange = () => +{ + if (!parentPort.get()) + { + cleanUp(); + } + rebuild(); +}; + +function cleanUp() +{ + for (let i = 0; i < inPorts.length; i++) + { + const selector = "[data-cables-child-id='" + op.id + "_" + i + "']"; + const currentChild = canvas.querySelector(selector); + if (currentChild && currentChild.parentNode) + { + currentChild.remove(); + } + } + outParent.set(null); +} + +function rebuild() +{ + const parent = parentPort.get(); + if (!parent) + { + outParent.set(null); + return; + } + + + if (!parent.querySelector) + { + outParent.set(null); + return; + } + + for (let i = 0; i < inPorts.length; i++) + { + const selector = "[data-cables-child-id='" + op.id + "_" + i + "']"; + const currentChild = parent.querySelector(selector); + if (currentChild) + { + currentChild.remove(); + } + const p = inPorts[i].get(); + if (p && parent) + { + p.dataset.cablesChildId = op.id + "_" + i; + parent.appendChild(p); + } + } + + outParent.set(null); + outParent.set(parent); +} + + +}; + +Ops.Html.ElementChilds_v2.prototype = new CABLES.Op(); +CABLES.OPS["ad7eea9a-f4af-4ab7-bb70-922242529681"]={f:Ops.Html.ElementChilds_v2,objName:"Ops.Html.ElementChilds_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.ElementClientRect +// +// ************************************************************** + +Ops.Html.ElementClientRect = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUpd=op.inTriggerButton("Update"), + inEle=op.inObject("Element",null,"element"), + outWidth=op.outNumber("Width"), + outHeight=op.outNumber("Height"), + outX=op.outNumber("X"), + outY=op.outNumber("Y"); + +inUpd.onTriggered=()=> +{ + let ele=inEle.get(); + if(!ele) + { + outX.set(0); + outY.set(0); + outWidth.set(0); + outHeight.set(0); + return; + } + const r=ele.getBoundingClientRect(); + const rCanv=op.patch.cgl.canvas.getBoundingClientRect(); + + outX.set(r.left-rCanv.left); + outY.set(r.top-rCanv.top); + outWidth.set(r.width); + outHeight.set(r.height); + +}; + + +}; + +Ops.Html.ElementClientRect.prototype = new CABLES.Op(); +CABLES.OPS["7a046f98-2adc-4f8e-ad47-c600e3ef5bca"]={f:Ops.Html.ElementClientRect,objName:"Ops.Html.ElementClientRect"}; + + + + +// ************************************************************** +// +// Ops.Html.ElementCssTransform +// +// ************************************************************** + +Ops.Html.ElementCssTransform = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inEle = op.inObject("Element", null, "element"), + inDoTranslate = op.inBool("Translate Active", true), + inTransX = op.inFloat("Translate X", 0), + inTransY = op.inFloat("Translate Y", 0), + inTransUnit = op.inSwitch("Unit", ["px", "%"], "px"), + + inDoScale = op.inBool("Scale Active", true), + inScale = op.inFloat("Scale", 1), + + inDoRot = op.inBool("Rotate Active", true), + inRot = op.inFloat("Rot Z", 0), + + inDoOrigin = op.inBool("Set Origin", true), + inOriginX = op.inSwitch("Origin X", ["left", "center", "right"], "center"), + inOriginY = op.inSwitch("Origin Y", ["top", "center", "bottom"], "center"); + +op.setPortGroup("Element", [inEle]); +op.setPortGroup("Translation", [inDoTranslate, inTransY, inTransX, inTransUnit]); +op.setPortGroup("Scaling", [inScale, inDoScale]); +op.setPortGroup("Rotation", [inDoRot, inRot]); +op.setPortGroup("Origin", [inDoOrigin, inOriginX, inOriginY]); + +inTransUnit.onChange = + inDoScale.onChange = + inDoOrigin.onChange = + inOriginX.onChange = + inOriginY.onChange = + inDoRot.onChange = + inDoTranslate.onChange = + inDoRot.onChange = + inTransX.onChange = + inTransY.onChange = + inScale.onChange = + inRot.onChange = update; + +let ele = null; + +inEle.onChange = inEle.onLinkChanged = function () +{ + if (ele && ele.style) + { + ele.style.transform = "initial"; + } + update(); +}; + +function update() +{ + ele = inEle.get(); + if (ele && ele.style) + { + let str = ""; + + if (inDoTranslate.get()) + if (inTransY.get() || inTransX.get()) + str += "translate(" + inTransX.get() + inTransUnit.get() + " , " + inTransY.get() + inTransUnit.get() + ") "; + + if (inDoScale.get()) + if (inScale.get() != 1.0) + str += "scale(" + inScale.get() + ") "; + + if (inDoRot.get()) + if (inRot.get() != 0.0) + str += "rotateZ(" + inRot.get() + "deg) "; + + try + { + ele.style.transform = str; + + if (inDoOrigin.get()) + ele.style["transform-origin"] = inOriginY.get() + " " + inOriginX.get(); + else + ele.style["transform-origin"] = "initial"; + } + catch (e) + { + op.logError(e); + } + } + else + { + setTimeout(update, 50); + } + + // outEle.set(inEle.get()); +} + + +}; + +Ops.Html.ElementCssTransform.prototype = new CABLES.Op(); +CABLES.OPS["777d00c6-5605-43c5-9b6a-b20d465bd3ba"]={f:Ops.Html.ElementCssTransform,objName:"Ops.Html.ElementCssTransform"}; + + + + +// ************************************************************** +// +// Ops.Html.ElementFadeInOut +// +// ************************************************************** + +Ops.Html.ElementFadeInOut = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"fadeInOut_css":"\n.CABLES_animFadedOut\n{\n display:none !important;\n opacity:0;\n}\n\n.CABLES_animFadeOut\n{\n animation: CABLES_keysFadeOut $LENGTHs normal forwards ease-in-out;\n}\n\n.CABLES_animFadeIn\n{\n /*display:block;*/\n animation: CABLES_keysFadeIn $LENGTHs normal forwards ease-in-out;\n}\n\n@keyframes CABLES_keysFadeIn {\n from { opacity: 0; }\n to { opacity: $FULLOPACITY; }\n}\n\n@keyframes CABLES_keysFadeOut {\n from { opacity: $FULLOPACITY; }\n to { opacity: 0; }\n}\n",}; +const inEle = op.inObject("HTML Element"); +const inVisible = op.inValueBool("Visible", true); +const inDuration = op.inValue("Duration", 0.5); +const inOpacity = op.inValue("Opacity", 1); +const outShowing = op.outBoolNum("Is Showing", false); + +let theTimeout = null; +inDuration.onChange = update; +inOpacity.onChange = update; + +inVisible.onChange = updateVisibility; +inEle.onChange = updateVisibility; + +let styleEle = null; +const eleId = "css_" + CABLES.uuid(); + +update(); + +let oldEle = null; +let loaded = true; +const oldvis = null; +loaded = true; + +op.onLoaded = function () +{ + loaded = true; + updateVisibility(); + outShowing.set(inVisible.get()); +}; + +function updateVisibility() +{ + const ele = inEle.get(); + + if (!loaded) + { + setTimeout(updateVisibility, 50); + return; + } + + if (styleEle && ele) + { + // if (ele == oldEle) return; + // oldEle = ele; + if (inVisible.get()) + { + outShowing.set(true); + if (ele && ele.classList && !ele.classList.contains("CABLES_animFadeIn")) + { + clearTimeout(theTimeout); + ele.classList.remove("CABLES_animFadedOut"); + ele.classList.remove("CABLES_animFadeOut"); + ele.classList.add("CABLES_animFadeIn"); + theTimeout = setTimeout(function () + { + ele.classList.remove("CABLES_animFadeIn"); + outShowing.set(true); + }, inDuration.get() * 1000); + } + } + else + { + outShowing.set(true); + if (ele && ele.classList && !ele.classList.contains("CABLES_animFadeOut")) + { + clearTimeout(theTimeout); + ele.classList.remove("CABLES_animFadeIn"); + ele.classList.add("CABLES_animFadeOut"); + theTimeout = setTimeout(function () + { + ele.classList.add("CABLES_animFadedOut"); + outShowing.set(false); + }, inDuration.get() * 1000); + } + } + } + else + { + // op.logError("no html element"); + } +} + +function getCssContent() +{ + let css = attachments.fadeInOut_css; + + while (css.indexOf("$LENGTH") > -1)css = css.replace("$LENGTH", inDuration.get()); + while (css.indexOf("$FULLOPACITY") > -1)css = css.replace("$FULLOPACITY", inOpacity.get()); + + return css; +} + +function update() +{ + styleEle = document.getElementById(eleId); + + if (styleEle) + { + styleEle.textContent = getCssContent(); + } + else + { + styleEle = document.createElement("style"); + styleEle.type = "text/css"; + styleEle.id = eleId; + styleEle.textContent = getCssContent(); + + const head = document.getElementsByTagName("body")[0]; + head.appendChild(styleEle); + } +} + +op.onDelete = function () +{ + const ele = inEle.get(); + + if (ele && ele.classList) + { + ele.classList.remove("CABLES_animFadeIn"); + ele.classList.remove("CABLES_animFadedOut"); + ele.classList.remove("CABLES_animFadeOut"); + } + + styleEle = document.getElementById(eleId); + if (styleEle)styleEle.remove(); +}; + + +}; + +Ops.Html.ElementFadeInOut.prototype = new CABLES.Op(); +CABLES.OPS["392e65eb-4ebe-4adb-8711-e4cfe059c6c9"]={f:Ops.Html.ElementFadeInOut,objName:"Ops.Html.ElementFadeInOut"}; + + + + +// ************************************************************** +// +// Ops.Html.ElementInteraction +// +// ************************************************************** + +Ops.Html.ElementInteraction = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inEle = op.inObject("Element"), + inAct = op.inBool("Active", true), + outIsDownLeft = op.outBool("Mouse Is Down Left"), + outIsDownRight = op.outBool("Mouse Is Down Right"), + outDownLeft = op.outTrigger("Mouse Down Left"), + outDownRight = op.outTrigger("Mouse Down Right"), + outUpLeft = op.outTrigger("Mouse Up Left"), + outUpRight = op.outTrigger("Mouse Up Right"), + outOver = op.outBool("Mouse Over"), + outEnter = op.outTrigger("Mouse Enter"), + outLeave = op.outTrigger("Mouse Leave"), + outPosX = op.outNumber("Offset X"), + outPosY = op.outNumber("Offset Y"); + +let ele = null; + +inEle.onChange = () => +{ + const el = inEle.get(); + + if (el) addListeners(el); + else removeListeners(); +}; + +function addListeners(el) +{ + ele = el; + + ele.addEventListener("pointerenter", onEnter); + ele.addEventListener("pointerleave", onLeave); + ele.addEventListener("pointermove", onMove); + ele.addEventListener("pointerdown", onDown); + ele.addEventListener("pointerup", onUp); + // ele.addEventListener("touchstart",onDown); +} + +function removeListeners() +{ + if (!ele) return; + ele.removeEventListener("pointerenter", onEnter); + ele.removeEventListener("pointerleave", onLeave); + ele.removeEventListener("pointermove", onMove); + ele.removeEventListener("pointerdown", onDown); + ele.removeEventListener("pointerup", onUp); + // ele.removeEventListener("touchstart",onDown); +} + +function onMove(e) +{ + outPosX.set(e.offsetX); + outPosY.set(e.offsetY); + outIsDownLeft.set(e.buttons == 1); + outIsDownRight.set(e.buttons == 2); +} + +function onDown(e) +{ + outPosX.set(e.offsetX); + outPosY.set(e.offsetY); + + if (e.buttons == 1)outDownLeft.trigger(); + if (e.buttons == 2)outDownRight.trigger(); + + ele.setPointerCapture(e.pointerId); + + outIsDownLeft.set(e.buttons == 1); + outIsDownRight.set(e.buttons == 2); +} + +function onUp(e) +{ + outPosX.set(e.offsetX); + outPosY.set(e.offsetY); + + ele.releasePointerCapture(e.pointerId); + + if (e.buttons == 1)outUpLeft.trigger(); + if (e.buttons == 2)outUpRight.trigger(); + outIsDownRight.set(false); + outIsDownLeft.set(false); +} + +function onEnter() +{ + outEnter.trigger(); + outOver.set(true); +} + +function onLeave() +{ + outLeave.trigger(); + outIsDownLeft.set(false); + outOver.set(false); +} + + +}; + +Ops.Html.ElementInteraction.prototype = new CABLES.Op(); +CABLES.OPS["bc2903a0-ee7f-4918-b1d8-ea3a6262e3ee"]={f:Ops.Html.ElementInteraction,objName:"Ops.Html.ElementInteraction"}; + + + + +// ************************************************************** +// +// Ops.Html.ElementSize +// +// ************************************************************** + +Ops.Html.ElementSize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExe=op.inTrigger("Update"), + inMode=op.inSwitch("Position",['Relative','Absolute'],'Relative'), + inEle=op.inObject("Element"), + outX=op.outNumber("x"), + outY=op.outNumber("y"), + outWidth=op.outNumber("Width"), + outHeight=op.outNumber("Height"); + +inMode.onChange=updateMode; +updateMode(); + +function updateMode() +{ + if(inMode.get()=="Relative") + { + inEle.onChange=updateRel; + inExe.onTriggered=updateRel; + } + else + { + inEle.onChange=updateAbs; + inExe.onTriggered=updateAbs; + } +} + +function updateAbs() +{ + const ele=inEle.get(); + if(!ele)return; + + const r=ele.getBoundingClientRect(); + + outX.set(r.x); + outY.set(r.y); + outWidth.set(r.width); + outHeight.set(r.height); +} + +function updateRel() +{ + const ele=inEle.get(); + if(!ele)return; + + const rcanv=op.patch.cgl.canvas.getBoundingClientRect(); + const r=ele.getBoundingClientRect(); + outX.set(r.x-rcanv.x); + outY.set(r.y-rcanv.y); + outWidth.set(r.width); + outHeight.set(r.height); +} + +}; + +Ops.Html.ElementSize.prototype = new CABLES.Op(); +CABLES.OPS["fb23c251-a43e-4677-9d03-ccd512fee82e"]={f:Ops.Html.ElementSize,objName:"Ops.Html.ElementSize"}; + + + + +// ************************************************************** +// +// Ops.Html.ElementsPositionsByClass +// +// ************************************************************** + +Ops.Html.ElementsPositionsByClass = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUpd=op.inTriggerButton("Update"), + inClassName=op.inString("Classname",""), + outPosArr=op.outArray("Position"), + outSizeArr=op.outArray("Size"); + +inUpd.onTriggered=()=> +{ + outPosArr.set(null); + outSizeArr.set(null); + + const arrPos=[]; + const arrSize=[]; + + const els=document.getElementsByClassName(inClassName.get()); + const rCanv=op.patch.cgl.canvas.getBoundingClientRect(); + + for(let i=0;i= 0; i--) + { + removeListener(element, subscribedEvents[i]); + } + } +} + +function removeListener(element, eventName) +{ + if (!element || !eventName || !arrayContainsValue(subscribedEvents, eventName)) { return; } + let subscribedEventIndex = subscribedEvents.indexOf(eventName); + element.removeEventListener(eventName, getEventByName(eventName).handler); + subscribedEvents.splice(subscribedEventIndex, 1); +} + +/* +function removeFromArray(arr, v) { + let i = arr.indexOf(v); + if(i > -1) { + arr.splice(i, 1); + } +} +*/ + +function arrayContainsValue(arr, v) +{ + return arr && arr.indexOf(v) > -1; +} + +/** + * Returns an event-object from the events array + * @param {string} name e.g. 'mousedown' + */ +function getEventByName(eventName) +{ + for (let i = 0; i < events.length; i++) + { + if (events[i].name == eventName) + { + return events[i]; + } + } + return null; +} + +/** + * Adds all listeners to the element and saves it in the events array + */ +function addListeners(element) +{ + if (!element) { return; } + events.forEach(function (event) + { + addListener(element, event); + }); +} + +/** + * Adds a listener to the element + * @param {object} element the HTML DOM element + * @param {object} event the event object from the events-array + */ +function addListener(element, event) +{ + if (!element || !event) { return; } + if (subscribedEvents.indexOf(event.name) > -1) { return; } // already subscribed + if (!event.togglePort.get()) { return; } // toggle for event not set + element.addEventListener(event.name, event.handler); + subscribedEvents.push(event.name); +} + +op.onDelete = function () +{ + removeAllListeners(lastElement); + removeAllListeners(elementPort.get()); +}; + + +}; + +Ops.Html.EventListener.prototype = new CABLES.Op(); +CABLES.OPS["73dc05e9-7b63-444b-980b-bd63f511b94a"]={f:Ops.Html.EventListener,objName:"Ops.Html.EventListener"}; + + + + +// ************************************************************** +// +// Ops.Html.FontFile_v2 +// +// ************************************************************** + +Ops.Html.FontFile_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + filename = op.inUrl("file", [".otf", ".ttf", ".woff", ".woff2"]), + fontname = op.inString("family"), + outLoaded = op.outBoolNum("Loaded"), + loadedTrigger = op.outTrigger("Loaded Trigger"); + +let loadingId = null; + +filename.onChange = function () +{ + outLoaded.set(false); + addStyle(); +}; + +fontname.onChange = addStyle; + +let fontFaceObj; + +function addStyle() +{ + if (filename.get() && fontname.get()) + { + if (document.fonts) + { + fontFaceObj = new FontFace(fontname.get(), "url(" + op.patch.getFilePath(String(filename.get())) + ")"); + + loadingId = op.patch.cgl.patch.loading.start("FontFile", filename.get()); + + // Add the FontFace to the FontFaceSet + document.fonts.add(fontFaceObj); + + // Get the current status of the FontFace + // (should be 'unloaded') + + // Load the FontFace + fontFaceObj.load(); + + // Get the current status of the Fontface + // (should be 'loading' or 'loaded' if cached) + + // Wait until the font has been loaded, log the current status. + fontFaceObj.loaded.then((fontFace) => + { + outLoaded.set(true); + loadedTrigger.trigger(); + op.patch.cgl.patch.loading.finished(loadingId); + + op.patch.emitEvent("fontLoaded", fontname.get()); + + // Throw an error if loading wasn't successful + }, (fontFace) => + { + op.setUiError("loadingerror", "Font loading error!" + fontFaceObj.status); + // op.logError("Font loading error! Current status", fontFaceObj.status); + }); + } + else + { // font loading api not supported + const fileUrl = op.patch.getFilePath(String(filename.get())); + const styleStr = "" + .endl() + "@font-face" + .endl() + "{" + .endl() + " font-family: \"" + fontname.get() + "\";" + .endl() + " src: url(\"" + fileUrl + "\") format(\"truetype\");" + .endl() + "}"; + + const style = document.createElement("style"); + style.type = "text/css"; + style.innerHTML = styleStr; + document.getElementsByTagName("head")[document.getElementsByTagName("head").length - 1].appendChild(style); + // TODO: Poll if font loaded + } + } +} + + +}; + +Ops.Html.FontFile_v2.prototype = new CABLES.Op(); +CABLES.OPS["68177370-116e-4c76-aef3-3b10d68e7227"]={f:Ops.Html.FontFile_v2,objName:"Ops.Html.FontFile_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.FullscreenMode +// +// ************************************************************** + +Ops.Html.FullscreenMode = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + doRequest = op.inTriggerButton("Request Fullscreen"), + doExit = op.inTriggerButton("Exit Fullscreen"), + inEle = op.inSwitch("Element", ["Canvas", "Document"], "Canvas"), + isFullscreen = op.outBoolNum("Is Fullscreen"); + +doExit.onTriggered = exitFs; +doRequest.onTriggered = startFs; + +let countStarts = 0; + +function setState() +{ + const isFull = (!window.screenTop && !window.screenY); + isFullscreen.set(isFull); +} + +function startFs() +{ + countStarts++; + if (countStarts > 30) + { + doRequest.onTriggered = null; + op.setUiAttrib({ "error": "Fullscreen Request shound not triggered that often: op disabled" }); + exitFs(); + } + + let elem = null; + if (inEle == "Canvas") elem = op.patch.cgl.canvas.parentElement; + else elem = document.documentElement; + + if (elem.requestFullScreen) elem.requestFullScreen(); + else if (elem.mozRequestFullScreen) elem.mozRequestFullScreen(); + else if (elem.webkitRequestFullScreen)elem.webkitRequestFullScreen(); + else if (elem.msRequestFullScreen)elem.msRequestFullScreen(); + + setTimeout(setState, 100); + setTimeout(setState, 500); + setTimeout(setState, 1000); +} + +function exitFs() +{ + countStarts--; + if (document.exitFullscreen) document.exitFullscreen(); + else if (document.mozCancelFullScreen) document.mozCancelFullScreen(); + else if (document.webkitExitFullscreen) document.webkitExitFullscreen(); + else if (document.msExitFullscreen)document.msExitFullscreen(); + + setTimeout(setState, 100); + setTimeout(setState, 500); + setTimeout(setState, 1000); +} + + +}; + +Ops.Html.FullscreenMode.prototype = new CABLES.Op(); +CABLES.OPS["fe933b35-696d-4738-be03-c0c011ed67a0"]={f:Ops.Html.FullscreenMode,objName:"Ops.Html.FullscreenMode"}; + + + + +// ************************************************************** +// +// Ops.Html.GetCssVariable +// +// ************************************************************** + +Ops.Html.GetCssVariable = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTriggerButton("Update"), + varname = op.inString("Var Name"), + result = op.outString("Result"); + +const root = document.documentElement; + +exe.onTriggered = function () +{ + result.set(root.style.getPropertyValue("--" + varname.get())); +}; + + +}; + +Ops.Html.GetCssVariable.prototype = new CABLES.Op(); +CABLES.OPS["ef03f407-e365-47c7-9829-be54ba53c54d"]={f:Ops.Html.GetCssVariable,objName:"Ops.Html.GetCssVariable"}; + + + + +// ************************************************************** +// +// Ops.Html.HyperLink_v2 +// +// ************************************************************** + +Ops.Html.HyperLink_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTriggerButton("Open"), + inUrl = op.inString("URL", "https://cables.gl"), + inTarget = op.inString("Target Name", "_self"), + inSpecs = op.inString("Specs", ""); + +exec.onTriggered = function () +{ + // document.location.href=inUrl.get(); + window.open(inUrl.get(), inTarget.get(), inSpecs.get()); +}; + + +}; + +Ops.Html.HyperLink_v2.prototype = new CABLES.Op(); +CABLES.OPS["a669d4f7-1e35-463c-bf8b-08c9f1b68e04"]={f:Ops.Html.HyperLink_v2,objName:"Ops.Html.HyperLink_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.IFrame_v3 +// +// ************************************************************** + +Ops.Html.IFrame_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + src = op.inString("URL", "https://undev.studio"), + elId = op.inString("ID"), + active = op.inBool("Active", true), + inStyle = op.inStringEditor("Style", "position:absolute;\nz-index:9999;\nborder:0;\nwidth:50%;\nheight:50%;"), + outEle = op.outObject("Element"); + +op.setPortGroup("Attributes", [src, elId]); + +let element = null; + +op.onDelete = removeEle; + +op.onLoadedValueSet = op.init = () => +{ + addElement(); + updateSoon(); + inStyle.onChange = + src.onChange = + elId.onChange = updateSoon; + + active.onChange = updateActive; +}; + +function addElement() +{ + if (!active.get()) return; + if (element) removeEle(); + element = document.createElement("iframe"); + updateAttribs(); + const parent = op.patch.cgl.canvas.parentElement; + parent.appendChild(element); + outEle.set(element); +} + +let timeOut = null; + +function updateSoon() +{ + clearTimeout(timeOut); + timeOut = setTimeout(updateAttribs, 30); +} + +function updateAttribs() +{ + if (!element) return; + element.setAttribute("style", inStyle.get()); + element.setAttribute("src", src.get()); + element.setAttribute("id", elId.get()); +} + +function removeEle() +{ + if (element && element.parentNode)element.parentNode.removeChild(element); + element = null; + outEle.set(element); +} + +function updateActive() +{ + if (!active.get()) + { + removeEle(); + return; + } + + addElement(); +} + + +}; + +Ops.Html.IFrame_v3.prototype = new CABLES.Op(); +CABLES.OPS["9e74b275-a1ed-4d10-aba4-4b3311363a99"]={f:Ops.Html.IFrame_v3,objName:"Ops.Html.IFrame_v3"}; + + + + +// ************************************************************** +// +// Ops.Html.LoadingIndicator +// +// ************************************************************** + +Ops.Html.LoadingIndicator = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"css_ellipsis_css":".lds-ellipsis {\n\n}\n.lds-ellipsis div {\n position: absolute;\n /*top: 33px;*/\n margin-top:-12px;\n margin-left:-13px;\n width: 13px;\n height: 13px;\n border-radius: 50%;\n background: #fff;\n animation-timing-function: cubic-bezier(0, 1, 1, 0);\n}\n.lds-ellipsis div:nth-child(1) {\n left: 8px;\n animation: lds-ellipsis1 0.6s infinite;\n}\n.lds-ellipsis div:nth-child(2) {\n left: 8px;\n animation: lds-ellipsis2 0.6s infinite;\n}\n.lds-ellipsis div:nth-child(3) {\n left: 32px;\n animation: lds-ellipsis2 0.6s infinite;\n}\n.lds-ellipsis div:nth-child(4) {\n left: 56px;\n animation: lds-ellipsis3 0.6s infinite;\n}\n@keyframes lds-ellipsis1 {\n 0% {\n transform: scale(0);\n }\n 100% {\n transform: scale(1);\n }\n}\n@keyframes lds-ellipsis3 {\n 0% {\n transform: scale(1);\n }\n 100% {\n transform: scale(0);\n }\n}\n@keyframes lds-ellipsis2 {\n 0% {\n transform: translate(0, 0);\n }\n 100% {\n transform: translate(24px, 0);\n }\n}\n","css_ring_css":".lds-ring {\n}\n.lds-ring div {\n box-sizing: border-box;\n display: block;\n position: absolute;\n width: 100%;\n height: 100%;\n margin: 0;\n border: 3px solid #fff;\n border-radius: 50%;\n animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;\n border-color: #fff transparent transparent transparent;\n}\n.lds-ring div:nth-child(1) {\n animation-delay: -0.45s;\n}\n.lds-ring div:nth-child(2) {\n animation-delay: -0.3s;\n}\n.lds-ring div:nth-child(3) {\n animation-delay: -0.15s;\n}\n@keyframes lds-ring {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n","css_spinner_css":"._cables_spinner {\n /*width: 40px;*/\n /*height: 40px;*/\n /*margin: 100px auto;*/\n background-color: #777;\n\n border-radius: 100%;\n -webkit-animation: sk-scaleout 1.0s infinite ease-in-out;\n animation: sk-scaleout 1.0s infinite ease-in-out;\n}\n\n@-webkit-keyframes sk-scaleout {\n 0% { -webkit-transform: scale(0) }\n 100% {\n -webkit-transform: scale(1.0);\n opacity: 0;\n }\n}\n\n@keyframes sk-scaleout {\n 0% {\n -webkit-transform: scale(0);\n transform: scale(0);\n } 100% {\n -webkit-transform: scale(1.0);\n transform: scale(1.0);\n opacity: 0;\n }\n}",}; +const + inVisible = op.inBool("Visible", true), + inStyle = op.inSwitch("Style", ["Spinner", "Ring", "Ellipsis"], "Ring"); + +const div = document.createElement("div"); +div.dataset.op = op.id; +const canvas = op.patch.cgl.canvas.parentElement; + +inStyle.onChange = updateStyle; + +div.appendChild(document.createElement("div")); +div.appendChild(document.createElement("div")); +div.appendChild(document.createElement("div")); + +const size = 50; + +div.style.width = size + "px"; +div.style.height = size + "px"; +div.style.top = "50%"; +div.style.left = "50%"; +// div.style.border="1px solid red"; + +div.style["margin-left"] = "-" + size / 2 + "px"; +div.style["margin-top"] = "-" + size / 2 + "px"; + +div.style.position = "absolute"; +div.style["z-index"] = "9999999"; + +inVisible.onChange = updateVisible; + +let eleId = "css_loadingicon_" + CABLES.uuid(); + +const styleEle = document.createElement("style"); +styleEle.type = "text/css"; +styleEle.id = eleId; + +let head = document.getElementsByTagName("body")[0]; +head.appendChild(styleEle); + +op.onDelete = () => +{ + remove(); + if (styleEle)styleEle.remove(); +}; + +updateStyle(); + +function updateStyle() +{ + const st = inStyle.get(); + if (st == "Spinner") + { + div.classList.add("_cables_spinner"); + styleEle.textContent = attachments.css_spinner_css; + } + else div.classList.remove("_cables_spinner"); + + if (st == "Ring") + { + div.classList.add("lds-ring"); + styleEle.textContent = attachments.css_ring_css; + } + else div.classList.remove("lds-ring"); + + if (st == "Ellipsis") + { + div.classList.add("lds-ellipsis"); + styleEle.textContent = attachments.css_ellipsis_css; + } + else div.classList.remove("lds-ellipsis"); +} + +function remove() +{ + div.remove(); + // if (styleEle)styleEle.remove(); +} + +function updateVisible() +{ + remove(); + if (inVisible.get()) canvas.appendChild(div); +} + + +}; + +Ops.Html.LoadingIndicator.prototype = new CABLES.Op(); +CABLES.OPS["e102834c-6dcf-459c-9e22-44ebccfc0d3b"]={f:Ops.Html.LoadingIndicator,objName:"Ops.Html.LoadingIndicator"}; + + + + +// ************************************************************** +// +// Ops.Html.MailtoLink +// +// ************************************************************** + +Ops.Html.MailtoLink = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inEmail = op.inString("Email", "test@test.com"), + inSubject = op.inString("Subject", ""), + exec = op.inTriggerButton("Execute"); + +exec.onTriggered = function () +{ + let str = "mailto:"; + str += inEmail.get(); + if (inSubject.get())str += "?subject=" + inSubject.get(); + window.location.href = str; +}; + + +}; + +Ops.Html.MailtoLink.prototype = new CABLES.Op(); +CABLES.OPS["6d03ec3b-f8b2-4544-bdb3-df771e857069"]={f:Ops.Html.MailtoLink,objName:"Ops.Html.MailtoLink"}; + + + + +// ************************************************************** +// +// Ops.Html.MarkdownToHtml +// +// ************************************************************** + +Ops.Html.MarkdownToHtml = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inStringEditor("Markdown", "##hello\n\nthis is some text...", "markdown"), + outStr = op.outString("Html"); + +inStr.onChange = update; +update(); + +function update() +{ + let str = inStr.get(); + + str = marked.parse(str); + outStr.set(str); +} + + +}; + +Ops.Html.MarkdownToHtml.prototype = new CABLES.Op(); +CABLES.OPS["26c97760-6e6b-4ff7-9715-dc510d23ab22"]={f:Ops.Html.MarkdownToHtml,objName:"Ops.Html.MarkdownToHtml"}; + + + + +// ************************************************************** +// +// Ops.Html.ModalOverlay +// +// ************************************************************** + +Ops.Html.ModalOverlay = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inEle = op.inObject("Content Element"), + inShow = op.inTriggerButton("Show"), + inClose = op.inTriggerButton("Close"), + closeButton = op.inBool("Show Closebutton", true), + inOpacity = op.inFloatSlider("Opacity", 0.5), + outVisible = op.outBool("Visible"), + outClosed = op.outTrigger("Closed"), + outElement = op.outObject("Element"); + +const eleContainer = document.createElement("div"); +const eleClose = document.createElement("div"); + +eleClose.innerHTML = "×"; +eleClose.style.color = "white"; +eleClose.style.position = "fixed"; +eleClose.style.top = +eleClose.style.right = "25px"; +eleClose.style["line-height"] = "25px"; +eleClose.style["z-index"] = "9999"; +eleClose.style.cursor = "pointer"; +eleClose.style["font-size"] = "50px"; +eleClose.addEventListener("pointerdown", hide); +eleContainer.addEventListener("pointerdown", hide); +eleContainer.appendChild(eleClose); + +inOpacity.onChange = updateBgColor; +eleContainer.style.display = "none"; +inShow.onTriggered = show; +inClose.onTriggered = hide; + +closeButton.onChange = updateCloseButton; + +function updateCloseButton() +{ + if (!eleClose) return; + if (closeButton.get()) eleClose.style.display = "block"; + else eleClose.style.display = "none"; +} + +inEle.onChange = function () +{ + let ele = inEle.get(); + if (ele && eleContainer) eleContainer.appendChild(ele); +}; + +function hide() +{ + outVisible.set(false); + eleContainer.style.display = "none"; + outClosed.trigger(); +} + +function updateBgColor() +{ + eleContainer.style["background-color"] = "rgba(0,0,0," + inOpacity.get() + ")"; +} + +function show() +{ + outVisible.set(true); + updateCloseButton(); + eleContainer.style.display = "block"; + + eleContainer.dataset.op = op.id; + let parent = op.patch.cgl.canvas.parentElement; + + eleContainer.style = "overflow:auto;top:0;width:100%;height:100%;position:absolute;z-index:9999"; + updateBgColor(); + + parent.appendChild(eleContainer); + outElement.set(eleContainer); +} + + +}; + +Ops.Html.ModalOverlay.prototype = new CABLES.Op(); +CABLES.OPS["a8ba593a-4d5a-4b55-8ee7-9b6f9920da1f"]={f:Ops.Html.ModalOverlay,objName:"Ops.Html.ModalOverlay"}; + + + + +// ************************************************************** +// +// Ops.Html.MouseCursorImage_v2 +// +// ************************************************************** + +Ops.Html.MouseCursorImage_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTriggerButton("Update"), + filename = op.inUrl("file"), + offX = op.inValueInt("Offset X"), + offY = op.inValueInt("Offset Y"), + next = op.outTrigger("Next"); + +offX.onChange = + offY.onChange = + filename.onChange = updateStr; + +exec.onTriggered = update; + +let str = "auto"; + +function updateStr() +{ + str = "url(" + op.patch.getFilePath(String(filename.get())) + ") " + offX.get() + " " + offX.get() + ", auto"; +} + +function update() +{ + op.patch.cgl.setCursor(str); + next.trigger(); +} + +exec.onLinkChanged = +next.onLinkChanged = () => +{ + op.patch.cgl.setCursor("auto"); +}; + + +}; + +Ops.Html.MouseCursorImage_v2.prototype = new CABLES.Op(); +CABLES.OPS["2a6be938-3db8-438e-82f2-60167529eb47"]={f:Ops.Html.MouseCursorImage_v2,objName:"Ops.Html.MouseCursorImage_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.Notification +// +// ************************************************************** + +Ops.Html.Notification = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"defaultstyle_txt":"visibility: hidden;\nbackground-color: #282828;\ncolor: #fff;\n\npadding: 16px;\nposition: absolute;\nz-index: 9999;\nfont-size: 17px;\nopacity:0;\nborder-radius:10px;\ntext-align:center;\nleft: 50%;\ntransform: translate(-50%, 0);\n",}; +const + triggerAnim = op.inTriggerButton("Trigger animation"), + inText = op.inString("Text", "Hello!
    This is a pop up"), + inClass = op.inString("Class"), + inStyle = op.inValueEditor("Style", attachments.defaultstyle_txt, "none"), + inVisible = op.inValueBool("Active", true), + inBreaks = op.inValueBool("Convert Line Breaks", false), + fadeInDuration = op.inFloat("Fade in", 0.5), + holdDuration = op.inFloat("Hold ", 2.0), + fadeOutDuration = op.inFloat("Fade out", 0.8), + percentOrPixel = op.inSwitch("mode", ["%", "px"], "%"), + divSide = op.inSwitch("Side", ["bottom", "top"], "bottom"), + startPosition = op.inFloat("Starting position", 0), + endPosition = op.inFloat("Ending position", 5), + finishedTrigger = op.outTrigger("Finished trigger"), + finished = op.outBool("Finished", false), + outElement = op.outObject("DOM Element"); + +op.setPortGroup("Animation", [fadeInDuration, holdDuration, fadeOutDuration]); +op.setPortGroup("HTML CSS", [inText, inClass, inStyle, inVisible, inBreaks]); +op.setPortGroup("Positioning", [percentOrPixel, divSide, startPosition, endPosition]); + +const divid = "notification_" + CABLES.uuid(); + +// inStyle.setUiAttribs({editorSyntax:'css'}); +const listenerElement = null; +let oldStr = null; + +let prevDisplay = "block"; + +const div = document.createElement("div"); +div.dataset.op = op.id; +div.id = divid; + +const canvas = op.patch.cgl.canvas.parentElement; + +canvas.appendChild(div); +outElement.set(div); + +inClass.onChange = updateClass; +inBreaks.onChange = inText.onChange = updateText; +inStyle.onChange = updateStyle; +inVisible.onChange = updateVisibility; + +triggerAnim.onTriggered = popUpAnim; + +updateText(); +updateStyle(); +warning(); + +op.onDelete = removeElement; + +outElement.onLinkChanged = updateStyle; + +let animInProgress = false; + +function setCSSVisible(visible) +{ + if (!visible) + { + div.style.visibility = "hidden"; + prevDisplay = div.style.display || "block"; + div.style.display = "none"; + } + else + { + if (prevDisplay == "none") prevDisplay = "block"; + div.style.visibility = "visible"; + div.style.display = "none"; + } +} + +function updateVisibility() +{ + setCSSVisible(inVisible.get()); +} + +function updateText() +{ + let str = inText.get(); + + if (oldStr === str) return; + oldStr = str; + + if (str && inBreaks.get()) str = str.replace(/(?:\r\n|\r|\n)/g, "
    "); + + if (div.innerHTML != str) div.innerHTML = str; + outElement.set(null); + outElement.set(div); +} + +function removeElement() +{ + if (div && div.parentNode) div.parentNode.removeChild(div); +} +// inline css inisde div +function updateStyle() +{ + if (inStyle.get() != div.style) + { + div.setAttribute("style", inStyle.get()); + + updateVisibility(); + outElement.set(null); + outElement.set(div); + } + warning(); +} + +function updateClass() +{ + div.setAttribute("class", inClass.get()); + warning(); +} + +op.addEventListener("onEnabledChange", function (enabled) +{ + op.log("css changed"); + setCSSVisible(div.style.visibility != "visible"); +}); + +function warning() +{ + if (inClass.get() && inStyle.get()) op.setUiError("error", "DIV uses external and inline CSS", 1); + else op.setUiError("error", null); +} + +function popUpAnim() +{ + if (!inVisible.get()) return; + + const mode = percentOrPixel.get(); + const start = startPosition.get() + mode; + const end = endPosition.get() + mode; + + const targetDiv = document.getElementById(divid); + div.style.display = "block"; + + const animData = {}; + // this function cascades into each stage when started + startAnim(mode, start, end, animData); +} + +function startAnim(mode, start, end, animData) +{ + // stop the glitches from it being triggered multiple times + if (animInProgress) return; + + finished.set(false); + animInProgress = true; + + animData.easing = ["cubic-bezier(0.0, 0.0, 0.2, 1.0)", "linear"]; + animData.opacity = [0, 1]; + + if (divSide.get() == "bottom") animData.bottom = [start, end]; + else animData.top = [start, end]; + + document.getElementById(divid).animate( + animData, fadeInDuration.get() * 1000).onfinish = function () + { + holdAnim(mode, start, end, animData); + }; +} + +function holdAnim(mode, start, end, animData) +{ + animData.easing = ["linear", "linear"]; + animData.opacity = [1, 1]; + + if (divSide.get() == "bottom") animData.bottom = [end, end]; + else animData.top = [end, end]; + + document.getElementById(divid).animate(animData, holdDuration.get() * 1000).onfinish = + function () + { + endAnim(mode, start, end, animData); + }; +} + +function endAnim(mode, start, end, animData) +{ + animData.easing = ["cubic-bezier(0.0, 0.0, 0.2, 1.0)", "linear"]; + animData.opacity = [1, 0]; + + if (divSide.get() == "bottom") animData.bottom = [end, start]; + else animData.top = [end, start]; + + document.getElementById(divid).animate( + animData, fadeOutDuration.get() * 1000).onfinish = function () + { + div.style.display = "none"; + animInProgress = false; + finishedTrigger.trigger(); + finished.set(true); + }; +} + + +}; + +Ops.Html.Notification.prototype = new CABLES.Op(); +CABLES.OPS["cf3960f3-ced0-4928-9082-a9cf7f8573a6"]={f:Ops.Html.Notification,objName:"Ops.Html.Notification"}; + + + + +// ************************************************************** +// +// Ops.Html.PlayerControlPanel_v2 +// +// ************************************************************** + +Ops.Html.PlayerControlPanel_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"css_progressui_css":".progressUI\n{\n padding: 10px;\n position: absolute;\n border-radius: 10px;\n background-color: rgba(0,0,0,0.45);\n margin: 20px;\n bottom: 10px;\n height:30px;\n margin-left:50%;\n transform: translate(-50%);\n display: flex;\n align-items: center;\n}\n\n.progressUI .buttonContainer {\n display: flex;\n align-content: center;\n height: 100%;\n}\n\n.progressUI .progress\n{\n font-family: Monospace;\n float:left;\n color:white;\n width: auto;\n margin-left:15px;\n margin-right: 10px;\n}\n\n.progressUI .progressContainer\n{\n width:200px;\n overflow: hidden;\n background: rgba(0,0,0,0.4);\n border-radius: 4px;\n float:left;\n display: flex;\n flex-direction: row;\n align-items: center;\n height: 100%;\n}\n\n.progressUI .button\n{\n cursor: pointer;\n box-sizing: border-box;\n background: rgba(0,0,0,0.4);\n display: flex;\n align-content: center;\n padding: 5px;\n padding-left: 10px;\n padding-right: 10px;\n margin-right: 5px;\n border-radius: 5px;\n float:left;\n display: flex;\n align-items: center;\n}\n\n.progressUIIcon\n{\n display: inline-block;\n vertical-align: middle;\n width: 15px;\n height: 15px;\n background-color: white;\n -webkit-mask-repeat: no-repeat !important;\n -webkit-mask-size: 100% !important;\n}\n\n.progressUI .progressContainer .progressbar {\n height: 100%;\n width: 97%;\n}\n\n.progressUI_icon-play {\n -webkit-mask: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-play'%3E%3Cpath d='M5 3l14 9-14 9V3z'/%3E%3C/svg%3E\");\n mask: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-play'%3E%3Cpath d='M5 3l14 9-14 9V3z'/%3E%3C/svg%3E\");\n}\n\n.progressUI_icon-pause {\n -webkit-mask: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-pause'%3E%3Cpath d='M6 4h4v16H6zM14 4h4v16h-4z'/%3E%3C/svg%3E\");\n mask: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-pause'%3E%3Cpath d='M6 4h4v16H6zM14 4h4v16h-4z'/%3E%3C/svg%3E\");\n}\n\n.progressUI_icon-rewind {\n -webkit-mask: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-rewind'%3E%3Cpath d='M11 19l-9-7 9-7v14zM22 19l-9-7 9-7v14z'/%3E%3C/svg%3E\");\n mask: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-rewind'%3E%3Cpath d='M11 19l-9-7 9-7v14zM22 19l-9-7 9-7v14z'/%3E%3C/svg%3E\");\n}\n\n.progressUI_icon-fast-forward {\n -webkit-mask: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-fast-forward'%3E%3Cpath d='M13 19l9-7-9-7v14zM2 19l9-7-9-7v14z'/%3E%3C/svg%3E\");\n mask: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-fast-forward'%3E%3Cpath d='M13 19l9-7-9-7v14zM2 19l9-7-9-7v14z'/%3E%3C/svg%3E\");\n}\n\n.progressUI_icon-skip-back {\n -webkit-mask: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-skip-back'%3E%3Cpath d='M19 20L9 12l10-8v16zM5 19V5'/%3E%3C/svg%3E\");\n mask: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-skip-back'%3E%3Cpath d='M19 20L9 12l10-8v16zM5 19V5'/%3E%3C/svg%3E\");\n}\n",}; +const inMax = op.inFloat("Length", 30); +const inCurrent = op.inFloat("Current", 0); +const inClamp = op.inBool("Clamp", false); +const inIsPlaying = op.inBool("Is Playing", false); +const inVisible = op.inBool("Visible", true); +const inShowValue = op.inBool("Show Time"); +const inShowSkip = op.inBool("Show Skip Buttons"); + +const outPlay = op.outTrigger("Play clicked"); +const outPause = op.outTrigger("Pause clicked"); +const outRewind = op.outTrigger("Rewind clicked"); +const outBack = op.outTrigger("Skip Back clicked"); +const outForward = op.outTrigger("Skip Forward clicked"); + +const outDragged = op.outTrigger("Dragged"); +const outValue = op.outNumber("Current Value"); +const outDragging = op.outBoolNum("Dragging", false); +const outElement = op.outObject("DOM Element", null, "element"); + +let div = document.createElement("div"); +div.id = "progressUI_" + op.id; +div.classList.add("progressUI"); +outElement.set(div); + +const cgl = op.patch.cgl; +let canvas = op.patch.cgl.canvas.parentElement; +canvas.appendChild(div); + +let progressContainer = document.createElement("div"); +if (!inVisible.get()) +{ + div.style.display = "none"; +} +let progressbar = document.createElement("input"); +let progress = document.createElement("div"); +const buttonContainer = document.createElement("div"); + +progressContainer.classList.add("progressContainer"); + +progressContainer.appendChild(progressbar); + +progressbar.setAttribute("type", "range"); +progressbar.setAttribute("step", 0.01); +progressbar.setAttribute("min", 0); +progressbar.setAttribute("max", inMax.get()); +progressbar.setAttribute("value", inCurrent.get()); +progressbar.classList.add("progressbar"); +progressbar.addEventListener("input", handleInput); +let wasPlaying = false; +let isDragging = false; + +div.appendChild(buttonContainer); +div.appendChild(progressContainer); +div.appendChild(progress); + +let eleId = "css_progressui_" + CABLES.uuid(); + +const styleEle = document.createElement("style"); +styleEle.type = "text/css"; +styleEle.id = eleId; + +let head = document.getElementsByTagName("body")[0]; +head.appendChild(styleEle); + +buttonContainer.classList.add("buttonContainer"); + +let skipbackbutton = addButton("", "progressUI_icon-skip-back"); +buttonContainer.appendChild(skipbackbutton); + +let rewindButton = addButton("", "progressUI_icon-rewind", "skip"); +if (!inShowSkip.get()) +{ + rewindButton.style.display = "none"; +} +buttonContainer.appendChild(rewindButton); +rewindButton.addEventListener("pointerdown", () => +{ + outBack.trigger(); +}); + +let playButton = addButton("", "progressUI_icon-play"); +buttonContainer.appendChild(playButton); + +let forwardButton = addButton("", "progressUI_icon-fast-forward", "skip"); +if (!inShowSkip.get()) +{ + forwardButton.style.display = "none"; +} +buttonContainer.appendChild(forwardButton); + +forwardButton.addEventListener("pointerdown", () => +{ + outForward.trigger(); +}); + +progress.classList.add("progress"); +progress.innerHTML = "00:00:000"; + +function addButton(title, icon, additionalClass) +{ + let button = document.createElement("div"); + button.classList.add("button"); + button.classList.add(additionalClass); + button.innerHTML = title; + + if (icon) + { + let buttonicon = document.createElement("div"); + buttonicon.classList.add("progressUIIcon"); + buttonicon.classList.add(icon); + button.appendChild(buttonicon); + } + return button; +} + +inMax.onChange = () => +{ + progressbar.setAttribute("max", inMax.get()); +}; + +inVisible.onChange = () => +{ + if (inVisible.get()) + { + div.style.removeProperty("display"); + } + else + { + div.style.display = "none"; + } +}; + +inShowValue.onChange = () => +{ + if (inShowValue.get()) + { + div.classList.add("showValue"); + progress.style.display = "block"; + } + else + { + div.classList.remove("showValue"); + progress.style.display = "none"; + } +}; + +inShowSkip.onChange = () => +{ + if (inShowSkip.get()) + { + div.querySelectorAll(".button.skip").forEach((skip) => + { + skip.style.display = "block"; + }); + } + else + { + div.querySelectorAll(".button.skip").forEach((skip) => + { + skip.style.display = "none"; + }); + } +}; + +if (!inShowValue.get()) +{ + progress.style.display = "none"; +} +progressbar.addEventListener("pointerdown", () => +{ + isDragging = true; + if (inIsPlaying.get()) + { + wasPlaying = true; + outPause.trigger(); + updatePlayButton(); + } +}); +progressbar.addEventListener("pointermove", () => +{ + const currentProgress = progressbar.value; + if (isDragging) + { + outDragging.set(isDragging); + updateProgressDisplay(currentProgress); + outValue.set(currentProgress); + outDragged.trigger(); + } +}); + +progressbar.addEventListener("pointerup", () => +{ + const currentProgress = progressbar.value; + updateProgressDisplay(currentProgress); + outValue.set(currentProgress); + + if (isDragging) + { + outDragged.trigger(); + } + + isDragging = false; + outDragging.set(isDragging); + if (wasPlaying) + { + wasPlaying = false; + outPlay.trigger(); + updatePlayButton(); + } +}); + +function updateStyle() +{ + styleEle.textContent = attachments.css_progressui_css; +} + +function updatePlayButton() +{ + playButton.querySelector(".progressUIIcon").classList.remove("progressUI_icon-play"); + playButton.querySelector(".progressUIIcon").classList.remove("progressUI_icon-pause"); + if (inIsPlaying.get()) + { + playButton.querySelector(".progressUIIcon").classList.add("progressUI_icon-pause"); + } + else + { + playButton.querySelector(".progressUIIcon").classList.add("progressUI_icon-play"); + } +} + +playButton.addEventListener("pointerdown", function () +{ + if (inIsPlaying.get()) + { + outPause.trigger(); + } + else + { + outPlay.trigger(); + } + updatePlayButton(); +}); + +skipbackbutton.addEventListener("pointerdown", () => +{ + outValue.set(0); + outRewind.trigger(); +}); + +op.onDelete = function () +{ + if (div) div.remove(); + if (styleEle) styleEle.remove(); +}; + +function handleInput(e) +{ + inCurrent.onChange = null; + const newValue = e.target.value; + outValue.set(newValue); + inCurrent.onChange = currentValueChange; +} + +function currentValueChange() +{ + let currentValue = inCurrent.get(); + if (inClamp.get() && currentValue > inMax.get()) + { + currentValue = inMax.get(); + } + progressbar.value = currentValue; + outValue.set(currentValue); + updateProgressDisplay(); +} + +let lasttime = 0; +function updateProgressDisplay(currentValue = null) +{ + let displayValue = currentValue || inCurrent.get(); + let t = displayValue; + if (t != lasttime) + { + progress.innerHTML = formatValue(t); + lasttime = t; + } +} + +function formatValue(t) +{ + const minutes = String(new Date(t * 1000).getUTCMinutes()).padStart(2, "0"); + const seconds = String(new Date(t * 1000).getUTCSeconds()).padStart(2, "0"); + const millis = String(new Date(t * 1000).getUTCMilliseconds()).padStart(2, "0").padEnd(3, "0"); + const html = minutes + ":" + seconds + ":" + millis; + return html; +} + +updateStyle(); +updatePlayButton(); + +inCurrent.onChange = currentValueChange; +inIsPlaying.onChange = updatePlayButton; + + +}; + +Ops.Html.PlayerControlPanel_v2.prototype = new CABLES.Op(); +CABLES.OPS["12ac1d94-f043-454d-92a8-60733d2908b2"]={f:Ops.Html.PlayerControlPanel_v2,objName:"Ops.Html.PlayerControlPanel_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.QrCode +// +// ************************************************************** + +Ops.Html.QrCode = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + text = op.inString("Text", "https://cables.gl"), + outDataUrl = op.outString("Image DataUrl"), + outElement=op.outObject("Element",null,"element"); + +const cgl = op.patch.cgl; +const parentEle = document.createElement("div"); +parentEle.id = "qrcode_" + CABLES.generateUUID(); +parentEle.style.display = "none"; +const body = document.getElementsByTagName("body")[0]; +body.appendChild(parentEle); + + +const img = document.createElement("img"); + + +text.onChange = generate; + +let qrCanvas=null; +generate(); + + +function generate() +{ + + while (parentEle.hasChildNodes()) + { + parentEle.removeChild(parentEle.lastChild); + } + const q=new QRCode(parentEle, { + "text": text.get(), + "width": 256, + "height": 256, + "colorDark": "#000000", + "colorLight": "#ffffff", + "correctLevel": QRCode.CorrectLevel.H + }); + + + + + qrCanvas = document.querySelector("#" + parentEle.id + " canvas"); + qrCanvas.style.display="block"; + + + + outElement.set(null); + outElement.set(img); + + const dataurl=qrCanvas.toDataURL("image/png"); + img.src=dataurl; + outDataUrl.set(dataurl); + +} + + +}; + +Ops.Html.QrCode.prototype = new CABLES.Op(); +CABLES.OPS["7e27a058-8162-4fd3-b08d-eba16f0fb551"]={f:Ops.Html.QrCode,objName:"Ops.Html.QrCode"}; + + + + +// ************************************************************** +// +// Ops.Html.QuerySelectorAll +// +// ************************************************************** + +Ops.Html.QuerySelectorAll = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUpdate = op.inTriggerButton("Update"), + queryPort = op.inString("Query"), + inMode = op.inValueSelect("Mode", ["document", "string input"], "document"), + inMimeType = op.inValueSelect("Type", ["text/html", "text/xml"], "text/html"), + inSource = op.inStringEditor("Document", "xml"), + elementPort = op.outArray("Elements"); + +if (inMode.get() === "document") +{ + inSource.setUiAttribs({ "greyout": true }); + inMimeType.set("text/html"); + inMimeType.setUiAttribs({ "greyout": true }); +} + +inUpdate.onTriggered = +queryPort.onChange = +inMimeType.onChange = +inSource.onChange = update; + +inMode.onChange = modeChange; + +function update() +{ + const q = queryPort.get(); + const theDocument = inSource.get(); + const mode = inMode.get(); + if (mode === "string input" && theDocument) + { + let parser = new DOMParser(); + let htmlDoc = null; + try + { + htmlDoc = parser.parseFromString(theDocument, inMimeType.get()); + const el = Array.from(htmlDoc.querySelectorAll(q)); + elementPort.set(el); + } + catch (e) + { + op.logError(e); + } + } + else + { + try + { + const el = Array.from(document.querySelectorAll(q)); + elementPort.set(el); + } + catch (e) + { + op.logError(e); + } + } +} + +function modeChange() +{ + if (inMode.get() === "document") + { + inSource.setUiAttribs({ "greyout": true }); + inMimeType.set("text/html"); + inMimeType.setUiAttribs({ "greyout": true }); + } + else + { + inSource.setUiAttribs({ "greyout": false }); + inMimeType.setUiAttribs({ "greyout": false }); + } +} + + +}; + +Ops.Html.QuerySelectorAll.prototype = new CABLES.Op(); +CABLES.OPS["001799c7-9ddf-4f3e-b260-e865f0ed2c0e"]={f:Ops.Html.QuerySelectorAll,objName:"Ops.Html.QuerySelectorAll"}; + + + + +// ************************************************************** +// +// Ops.Html.QuerySelector_v2 +// +// ************************************************************** + +Ops.Html.QuerySelector_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUpdate = op.inTriggerButton("Update"), + queryPort = op.inString("Query"), + inMode = op.inValueSelect("Mode", ["document", "string input"], "document"), + inMimeType = op.inValueSelect("Type", ["text/html", "text/xml"], "text/html"), + inSource = op.inStringEditor("Document", "xml"), + elementPort = op.outObject("Element"); + +if (inMode.get() === "document") +{ + inSource.setUiAttribs({ "greyout": true }); + inMimeType.set("text/html"); + inMimeType.setUiAttribs({ "greyout": true }); +} + +inUpdate.onTriggered = +queryPort.onChange = +inMimeType.onChange = +inSource.onChange = update; + +inMode.onChange = modeChange; + +function update() +{ + const q = queryPort.get(); + const theDocument = inSource.get(); + const mode = inMode.get(); + if (mode === "string input" && theDocument) + { + let parser = new DOMParser(); + let htmlDoc = null; + try + { + htmlDoc = parser.parseFromString(theDocument, inMimeType.get()); + const el = htmlDoc.querySelector(q); + elementPort.set(el); + } + catch (e) + { + op.logError(e); + } + } + else + { + try + { + const el = document.querySelector(q); + elementPort.set(el); + } + catch (e) + { + op.logError(e); + } + } +} + +function modeChange() +{ + if (inMode.get() === "document") + { + inSource.setUiAttribs({ "greyout": true }); + inMimeType.set("text/html"); + inMimeType.setUiAttribs({ "greyout": true }); + } + else + { + inSource.setUiAttribs({ "greyout": false }); + inMimeType.setUiAttribs({ "greyout": false }); + } +} + + +}; + +Ops.Html.QuerySelector_v2.prototype = new CABLES.Op(); +CABLES.OPS["a1a2189b-564c-4dd7-b3d9-a6cebc0cd94e"]={f:Ops.Html.QuerySelector_v2,objName:"Ops.Html.QuerySelector_v2"}; + + + + +// ************************************************************** +// +// Ops.Html.ReloadPage +// +// ************************************************************** + +Ops.Html.ReloadPage = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +var exec=op.inTrigger("Exec"); + +exec.onTriggered=function() +{ + location.reload(); + +}; + +}; + +Ops.Html.ReloadPage.prototype = new CABLES.Op(); +CABLES.OPS["d0060a4e-ffed-4a8d-8f6d-bfd9a23319de"]={f:Ops.Html.ReloadPage,objName:"Ops.Html.ReloadPage"}; + + + + +// ************************************************************** +// +// Ops.Html.RemoveAllClasses +// +// ************************************************************** + +Ops.Html.RemoveAllClasses = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe=op.inTriggerButton("Remove"); +const inName=op.inValueString("Classname"); + +exe.onTriggered=function() +{ + var els=document.getElementsByClassName(inName.get()); + + for(var i=0;i +{ + if (!inActive.get()) return next.trigger(); + + setProperties(); + next.trigger(); +}; + +op.onDelete = removeProperties; + +let oldEle = null; + +inAlignHor.onChange = + inAlignVert.onChange = + inRotate.onChange = + inScale.onChange = updateTransform; + +function updateTransform() +{ + const ele = inEle.get(); + if (!ele) + { + oldEle = null; + return; + } + + let translateStr = ""; + if (inAlignVert.get() == "Left")translateStr = "0%"; + if (inAlignVert.get() == "Center")translateStr = "-50%"; + if (inAlignVert.get() == "Right")translateStr = "-100%"; + + translateStr += ", "; + if (inAlignHor.get() == "Top")translateStr += "0%"; + if (inAlignHor.get() == "Center")translateStr += "-50%"; + if (inAlignHor.get() == "Bottom")translateStr += "-100%"; + + const str = "translate(" + translateStr + ") scale(" + inScale.get() + ") rotate(" + inRotate.get() + "deg)"; + + if (ele.style.transform != str) ele.style.transform = str; +} + +inEle.onChange = function () +{ + const ele = inEle.get(); + if (!ele) + { + removeProperties(oldEle); + + oldEle = null; + return; + } + + updateTransform(); + setProperties(); +}; + +inEle.onLinkChanged = function () +{ + cachedLeft = -1; + cachedTop = -1; + + if (!inEle.isLinked()) + { + if (oldEle) + { + removeProperties(oldEle); + } + } + else + { + oldEle = inEle.get(); + } + updateTransform(); +}; + +function getScreenCoord() +{ + mat4.multiply(m, cgl.vMatrix, cgl.mMatrix); + vec3.transformMat4(pos, [0, 0, 0], m); + vec3.transformMat4(trans, pos, cgl.pMatrix); + + const vp = cgl.getViewPort(); + + const w = cgl.canvasWidth / cgl.pixelDensity; + const h = cgl.canvasHeight / cgl.pixelDensity; + + if (inOrtho.get()) + { + x = ((w * 0.5 + trans[0] * w * 0.5 / 1)); + y = ((h * 0.5 - trans[1] * h * 0.5 / 1)); + } + else + { + x = (w - (w * 0.5 - trans[0] * w * 0.5)); // / trans[2] + y = (h - (h * 0.5 + trans[1] * h * 0.5)); // / trans[2] + } + + visible = pos[2] < 0.0 && x > 0 && x < vp[2] && y > 0 && y < vp[3]; +} + +function setProperties() +{ + const ele = inEle.get(); + oldEle = ele; + if (ele && ele.style) + { + getScreenCoord(); + const yy = cgl.canvas.offsetTop + y; + + if (yy != cachedTop) + { + ele.style.top = yy + "px"; + cachedTop = yy; + } + + if (x != cachedLeft) + { + ele.style.left = x + "px"; + cachedLeft = x; + } + + if (inHideBehind.get()) + { + if (visible)ele.style.display = "initial"; + else ele.style.display = "none"; + } + } +} + +function removeProperties(ele) +{ + cachedLeft = -1; + cachedTop = -1; + + if (!ele) ele = inEle.get(); + if (ele && ele.style) + { + ele.style.top = "initial"; + ele.style.left = "initial"; + ele.style.transform = "initial"; + } +} + +op.addEventListener("onEnabledChange", function (enabled) +{ + if (enabled) setProperties(); + else removeProperties(); +}); + + +}; + +Ops.Html.TransformElement.prototype = new CABLES.Op(); +CABLES.OPS["caca0307-d460-47df-8674-b7d2601239ab"]={f:Ops.Html.TransformElement,objName:"Ops.Html.TransformElement"}; + + + + +// ************************************************************** +// +// Ops.Html.VideoElement +// +// ************************************************************** + +Ops.Html.VideoElement = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + // src=op.inString("URL",'https://undev.studio'), + src = op.inUrl("File", null, ""), + elId = op.inString("ID"), + play = op.inBool("Play"), + controls = op.inBool("Controls", true), + active = op.inBool("Active", true), + loop = op.inBool("Loop", false), + inMuted = op.inBool("Muted", false), + inStyle = op.inStringEditor("Style", "position:absolute;\nz-index:9999;\nborder:0;\nwidth:50%;\nheight:50%;"), + rewind = op.inTriggerButton("Rewind"), + outEle = op.outObject("Element"), + outPlaying = op.outBool("Playing"), + outCanplaythrough = op.outBool("Can Play Through"), + outTime = op.outNumber("Time"), + outEnded = op.outTrigger("Ended"), + outHasError = op.outBool("Has Error"), + outError = op.outString("Error Message"); +op.setPortGroup("Attributes", [src, elId]); + +let element = document.createElement("video"); +let timeOut = null; + +op.onDelete = removeEle; + +op.onLoaded = init; + +function init() +{ + addElement(); + updateSoon(); + + inStyle.onChange = + src.onChange = + elId.onChange = updateSoon; + + active.onChange = updateActive; +} + +init(); + +loop.onChange = +controls.onChange = +inMuted.onChange = updateVideoSettings; + +function updateVideoSettings() +{ + if (!element) return; + if (controls.get()) element.controls = "true"; + else + { + element.controls = "true"; + element.removeAttribute("controls"); + } + + if (loop.get()) element.loop = "true"; + else element.removeAttribute("loop"); + + if (inMuted.get()) element.muted = "true"; + else element.removeAttribute("muted"); +} + +function updatePlay() +{ + if (!element) return; + if (play.get())element.play(); + else element.pause(); +} + +play.onChange = () => +{ + updatePlay(); +}; + +rewind.onTriggered = function () +{ + if (element) element.currentTime = 0; +}; + +function addElement() +{ + if (!active.get()) return; + if (element) removeEle(); + element = document.createElement("video"); + element.setAttribute("playsinline", ""); + element.setAttribute("webkit-playsinline", ""); + element.preload = "true"; + updateVideoSettings(); + element.setAttribute("crossOrigin", "anonymous"); + outCanplaythrough.set(false); + + outHasError.set(false); + outError.set(""); + + element.addEventListener("canplaythrough", () => + { + outCanplaythrough.set(true); + }, true); + element.addEventListener("play", () => + { + outPlaying.set(true); + }, true); + element.addEventListener("ended", () => + { + outEnded.trigger(); + }, true); + element.addEventListener("pause", () => + { + outPlaying.set(false); + }, true); + + element.addEventListener("timeupdate", () => + { + if (element)outTime.set(element.currentTime); + }, true); + + element.onerror = function () + { + outHasError.set(true); + if (element && element.error) + { + outError.set("Error " + element.error.code + "/" + element.error.message); + op.log("Error " + element.error.code + "; details: " + element.error.message); + } + }; + + // element.playbackRate = speed.get(); + // if (!addedListeners) + // { + // addedListeners = true; + // element.addEventListener("canplaythrough", initVideo, true); + // element.addEventListener("loadedmetadata", loadedMetaData); + // element.addEventListener("playing", function () { videoElementPlaying = true; }, true); + // } + + updateAttribs(); + const parent = op.patch.cgl.canvas.parentElement; + parent.appendChild(element); + + updateVideoSettings(); + + if (play.get())updatePlay(); + + outEle.set(element); +} + +function updateSoon() +{ + clearTimeout(timeOut); + timeOut = setTimeout(updateAttribs, 30); +} + +function updateAttribs() +{ + if (!element || !src.get()) return; + element.setAttribute("style", inStyle.get()); + element.setAttribute("src", src.get()); + element.setAttribute("id", elId.get()); +} + +function removeEle() +{ + if (element && element.parentNode)element.parentNode.removeChild(element); + element = null; + outEle.set(element); +} + +function updateActive() +{ + if (!active.get()) + { + removeEle(); + return; + } + + addElement(); +} + + +}; + +Ops.Html.VideoElement.prototype = new CABLES.Op(); +CABLES.OPS["5315a156-3a4f-4ff3-80c7-1b73c839d388"]={f:Ops.Html.VideoElement,objName:"Ops.Html.VideoElement"}; + + + + +// ************************************************************** +// +// Ops.Html.WindowClose +// +// ************************************************************** + +Ops.Html.WindowClose = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec=op.inTriggerButton("Close"); + +inExec.onTriggered=function() +{ + window.close(); +}; + +}; + +Ops.Html.WindowClose.prototype = new CABLES.Op(); +CABLES.OPS["4f5d56b9-28a0-47fe-86f7-7b765268d694"]={f:Ops.Html.WindowClose,objName:"Ops.Html.WindowClose"}; + + + + +// ************************************************************** +// +// Ops.Html.WindowHasFocus +// +// ************************************************************** + +Ops.Html.WindowHasFocus = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + outFocussed = op.outBool("has focus"), + outVisible = op.outBool("Tab Visible", true); + +const focused = true; + +outFocussed.set(document.hasFocus()); + +window.addEventListener("blur", handleBlur); +window.addEventListener("focus", handleFocus); + +document.addEventListener("visibilitychange", updateVisibility); + +op.onDelete = function () +{ + document.removeEventListener("visibilitychange", updateVisibility); +}; + +function handleFocus() +{ + outFocussed.set(true); +} + +function handleBlur() +{ + outFocussed.set(false); +} + +function updateVisibility(e) +{ + outVisible.set(!document.hidden); +} + + +}; + +Ops.Html.WindowHasFocus.prototype = new CABLES.Op(); +CABLES.OPS["6542896e-aa13-4b57-81e0-163597f4149a"]={f:Ops.Html.WindowHasFocus,objName:"Ops.Html.WindowHasFocus"}; + + + + +// ************************************************************** +// +// Ops.Html.WindowInfo +// +// ************************************************************** + +Ops.Html.WindowInfo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + outWidth = op.outNumber("clientWidth"), + outHeight = op.outNumber("clientHeight"), + outHeightBody = op.outNumber("body scroll Height"), + outIframeChild = op.outBoolNum("Iframe Parent", window.top != window.self); + +window.addEventListener("resize", update); + +update(); + +function update() +{ + outWidth.set(window.innerWidth); + outHeight.set(window.innerHeight); + + outHeightBody.set(document.documentElement.scrollHeight); +} + + +}; + +Ops.Html.WindowInfo.prototype = new CABLES.Op(); +CABLES.OPS["9655045c-3539-457d-be65-a1456a58906a"]={f:Ops.Html.WindowInfo,objName:"Ops.Html.WindowInfo"}; + + + + +// ************************************************************** +// +// Ops.Html.YoutubePlayer +// +// ************************************************************** + +Ops.Html.YoutubePlayer = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + src = op.inString("Video Id", "dQw4w9WgXcQ"), + active = op.inBool("Active", true), + inStyle = op.inStringEditor("Style"), + elId = op.inString("ElementID"), + paramAutoplay = op.inBool("Autoplay", false), + paramCC = op.inBool("Display Captions", false), + paramLoop = op.inBool("Loop", false), + paramFs = op.inBool("Allow Fullscreen", true), + paramControls = op.inBool("Hide Controls", false), + paramStart = op.inInt("Start at Second", 0), + + outEle = op.outObject("Element"), + outDirectLink = op.outString("Direct Link"); + // outImageMax=op.outString("Thumbnail Max"); + +const defaultStyle = "position:absolute;\n\ +z-index:9;\n\ +border:0;\n"; + +op.setPortGroup("Youtube Options", [paramAutoplay, paramCC, paramLoop, paramFs, paramControls, paramStart]); + +// https://developers.google.com/youtube/player_parameters + +let element = null; +let initialized = false; + +paramStart.onChange = + paramAutoplay.onChange = + paramCC.onChange = + paramLoop.onChange = + paramFs.onChange = + paramControls.onChange = + src.onChange = updateURL; + +elId.onChange = updateID; +inStyle.onChange = updateStyle; +op.onDelete = removeEle; + +active.onChange = update; + +op.init = function () +{ + initialized = true; + setTimeout(() => { update(); }, 100); +}; + +inStyle.set(defaultStyle); + +function update() +{ + if (!active.get()) + { + removeEle(); + return; + } + + addElement(); +} + +function addElement() +{ + if (!initialized) return; + if (element) removeEle(); + + const parent = op.patch.cgl.canvas.parentElement; + element = document.createElement("iframe"); + element.dataset.op = op.id; + element.style.position = "absolute"; + element.allowfullscreen = true; + element.frameborder = 0; + element.allow = "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"; + + parent.appendChild(element); + + updateURL(); + updateID(); + updateStyle(); + + outEle.set(null); + outEle.set(element); +} + +function removeEle() +{ + if (element && element.parentNode) element.parentNode.removeChild(element); + element = null; + outEle.set(null); +} + +function updateURL() +{ + if (src.get()) outDirectLink.set("https://www.youtube.com/watch?v=" + src.get()); + + if (!initialized) return; + if (!active.get()) return; + const urlParams = []; + + if (paramAutoplay.get()) urlParams.push("autoplay=1"); + if (paramCC.get()) urlParams.push("cc_load_policy=1"); + if (paramLoop.get()) urlParams.push("loop=1"); + if (paramFs.get()) urlParams.push("fs=1"); + if (paramControls.get()) urlParams.push("controls=0"); + if (paramStart.get() > 0) urlParams.push("start=" + paramStart.get()); + + let urlParamsStr = ""; + if (urlParams.length > 0) urlParamsStr = "?" + urlParams.join("&") + "&rel=0"; + + const urlStr = "https://www.youtube.com/embed/" + src.get() + urlParamsStr; + if (element) + element.setAttribute("src", urlStr); +} + +function updateID() +{ + if (!active.get()) return; + if (element) element.setAttribute("id", elId.get()); +} + +function updateStyle() +{ + if (!active.get()) return; + if (element) element.style = inStyle.get(); +} + + +}; + +Ops.Html.YoutubePlayer.prototype = new CABLES.Op(); +CABLES.OPS["b60ad395-d6ad-4309-86c5-c6bbffaf176c"]={f:Ops.Html.YoutubePlayer,objName:"Ops.Html.YoutubePlayer"}; + + + + +// ************************************************************** +// +// Ops.Json.AjaxRequest_v2 +// +// ************************************************************** + +Ops.Json.AjaxRequest_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const filename = op.inUrl("file"), + jsonp = op.inValueBool("JsonP", false), + headers = op.inObject("headers", {}), + inBody = op.inStringEditor("body", ""), + inMethod = op.inDropDown("HTTP Method", ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "CONNECT", "OPTIONS", "TRACE"], "GET"), + inContentType = op.inString("Content-Type", "application/json"), + inParseJson = op.inBool("parse json", true), + inAutoRequest = op.inBool("Auto request", true), + reloadTrigger = op.inTriggerButton("reload"), + outData = op.outObject("data"), + outString = op.outString("response"), + isLoading = op.outBoolNum("Is Loading", false), + outTrigger = op.outTrigger("Loaded"); + +filename.setUiAttribs({ "title": "URL" }); +reloadTrigger.setUiAttribs({ "buttonTitle": "trigger request" }); + +outData.ignoreValueSerialize = true; +outString.ignoreValueSerialize = true; + +inAutoRequest.onChange = filename.onChange = jsonp.onChange = headers.onChange = inMethod.onChange = inParseJson.onChange = function () +{ + delayedReload(false); +}; + +reloadTrigger.onTriggered = function () +{ + delayedReload(true); +}; + +let loadingId = 0; +let reloadTimeout = 0; + +function delayedReload(force = false) +{ + clearTimeout(reloadTimeout); + reloadTimeout = setTimeout(function () { reload(null, force); }, 100); +} + +op.onFileChanged = function (fn) +{ + if (filename.get() && filename.get().indexOf(fn) > -1) reload(true); +}; + +function reload(addCachebuster, force = false) +{ + if (!inAutoRequest.get() && !force) return; + if (!filename.get()) return; + + op.patch.loading.finished(loadingId); + + loadingId = op.patch.loading.start("jsonFile", "" + filename.get()); + isLoading.set(true); + + op.setUiAttrib({ "extendTitle": CABLES.basename(filename.get()) }); + op.setUiError("jsonerr", null); + + let httpClient = CABLES.ajax; + if (jsonp.get()) httpClient = CABLES.jsonp; + + let url = op.patch.getFilePath(filename.get()); + if (addCachebuster)url += "?rnd=" + CABLES.generateUUID(); + + op.patch.loading.addAssetLoadingTask(() => + { + const body = inBody.get(); + httpClient( + url, + (err, _data, xhr) => + { + outData.set(null); + outString.set(null); + if (err) + { + op.patch.loading.finished(loadingId); + isLoading.set(false); + + op.logError(err); + return; + } + try + { + let data = _data; + if (typeof data === "string" && inParseJson.get()) + { + data = JSON.parse(_data); + outData.set(data); + } + outString.set(_data); + op.uiAttr({ "error": null }); + op.patch.loading.finished(loadingId); + outTrigger.trigger(); + isLoading.set(false); + } + catch (e) + { + op.logError(e); + op.setUiError("jsonerr", "Problem while loading json:
    " + e); + op.patch.loading.finished(loadingId); + isLoading.set(false); + } + }, + inMethod.get(), + (body && body.length > 0) ? body : null, + inContentType.get(), + null, + headers.get() || {} + ); + }); +} + + +}; + +Ops.Json.AjaxRequest_v2.prototype = new CABLES.Op(); +CABLES.OPS["e0879058-5505-4dc4-b9ff-47a3d3c8a71a"]={f:Ops.Json.AjaxRequest_v2,objName:"Ops.Json.AjaxRequest_v2"}; + + + + +// ************************************************************** +// +// Ops.Json.ArrayGetArrayByPath +// +// ************************************************************** + +Ops.Json.ArrayGetArrayByPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const objectIn = op.inArray("Array"); +const pathIn = op.inString("Path"); +const resultOut = op.outArray("Output"); +const foundOut = op.outBool("Found"); + +objectIn.onChange = update; +pathIn.onChange = update; + +function update() +{ + const data = objectIn.get(); + const path = pathIn.get(); + op.setUiError("missing", null); + if (data && path) + { + if (!Array.isArray(data) && !(typeof data === "object")) + { + foundOut.set(false); + op.setUiError("notiterable", "input object of type " + (typeof data) + " is not travesable by path"); + } + else + { + op.setUiError("notiterable", null); + const parts = path.split("."); + op.setUiAttrib({ "extendTitle": parts[parts.length - 1] + "" }); + let result = resolve(path, data); + if (result === undefined) + { + const errorMsg = "could not find element at path " + path; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else if (!Array.isArray(result)) + { + const errorMsg = "element at path " + path + " is not an array"; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else + { + foundOut.set(true); + } + resultOut.set(result); + } + } + else + { + foundOut.set(false); + } +} + +function resolve(path, obj = self, separator = ".") +{ + const properties = Array.isArray(path) ? path : path.split(separator); + return properties.reduce((prev, curr) => prev && prev[curr], obj); +} + + +}; + +Ops.Json.ArrayGetArrayByPath.prototype = new CABLES.Op(); +CABLES.OPS["f8fd1e41-d56a-4046-83a1-2ca194c828fc"]={f:Ops.Json.ArrayGetArrayByPath,objName:"Ops.Json.ArrayGetArrayByPath"}; + + + + +// ************************************************************** +// +// Ops.Json.ArrayGetArrayValuesByPath +// +// ************************************************************** + +Ops.Json.ArrayGetArrayValuesByPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const objectIn = op.inArray("Array"); +const pathIn = op.inString("Path"); +const resultOut = op.outArray("Output"); +const foundOut = op.outBool("Found"); + +objectIn.onChange = update; +pathIn.onChange = update; + +function update() +{ + const data = objectIn.get(); + let result = []; + const path = pathIn.get(); + op.setUiError("path", null); + + if (data && path) + { + if (!Array.isArray(data)) + { + foundOut.set(false); + op.setUiError("notiterable", "input of type " + (typeof data) + " is not an array"); + } + else + { + op.setUiError("notiterable", null); + const parts = path.split("."); + foundOut.set(false); + + const pathSuffix = parts.slice(1).join("."); + + for (let i = 0; i < data.length; i++) + { + const resolvePath = i + "." + pathSuffix; + const resolvedData = resolve(resolvePath, data); + if (typeof resolvedData !== "undefined") + { + foundOut.set(true); + } + result.push(resolvedData); + } + const titleParts = pathIn.get().split("."); + op.setUiAttrib({ "extendTitle": titleParts[titleParts.length - 1] + "" }); + if (foundOut.get()) + { + resultOut.set(result); + } + else + { + op.setUiError("path", "given path seems to be invalid!", 1); + resultOut.set([]); + } + } + } + else + { + foundOut.set(false); + } +} + +function resolve(path, obj = self, separator = ".") +{ + const properties = Array.isArray(path) ? path : path.split(separator); + return properties.reduce((prev, curr) => prev && prev[curr], obj); +} + + +}; + +Ops.Json.ArrayGetArrayValuesByPath.prototype = new CABLES.Op(); +CABLES.OPS["f4aa5756-c681-47ac-a997-b4f91760db96"]={f:Ops.Json.ArrayGetArrayValuesByPath,objName:"Ops.Json.ArrayGetArrayValuesByPath"}; + + + + +// ************************************************************** +// +// Ops.Json.ArrayGetNumberByPath +// +// ************************************************************** + +Ops.Json.ArrayGetNumberByPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const objectIn = op.inArray("Array"); +const pathIn = op.inString("Path"); +const resultOut = op.outNumber("Output"); +const foundOut = op.outBool("Found"); + +objectIn.onChange = update; +pathIn.onChange = update; + +function update() +{ + const data = objectIn.get(); + const path = pathIn.get(); + op.setUiError("missing", null); + if (data && path) + { + if (!Array.isArray(data) && !(typeof data === "object")) + { + foundOut.set(false); + op.setUiError("notiterable", "input object of type " + (typeof data) + " is not travesable by path"); + } + else + { + op.setUiError("notiterable", null); + const parts = path.split("."); + op.setUiAttrib({ "extendTitle": parts[parts.length - 1] + "" }); + let result = resolve(path, data); + if (result === undefined) + { + const errorMsg = "could not find element at path " + path; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else if (typeof result !== "number") + { + const errorMsg = "element at path " + path + " is not a number"; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else + { + foundOut.set(true); + } + resultOut.set(result); + } + } + else + { + foundOut.set(false); + } +} + +function resolve(path, obj = self, separator = ".") +{ + const properties = Array.isArray(path) ? path : path.split(separator); + return properties.reduce((prev, curr) => prev && prev[curr], obj); +} + + +}; + +Ops.Json.ArrayGetNumberByPath.prototype = new CABLES.Op(); +CABLES.OPS["9a8ac7c9-dc88-44cb-b4f7-fead5ebb9ed7"]={f:Ops.Json.ArrayGetNumberByPath,objName:"Ops.Json.ArrayGetNumberByPath"}; + + + + +// ************************************************************** +// +// Ops.Json.ArrayGetObjectByPath +// +// ************************************************************** + +Ops.Json.ArrayGetObjectByPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const objectIn = op.inArray("Array"); +const pathIn = op.inString("Path"); +const resultOut = op.outObject("Output"); +const foundOut = op.outBool("Found"); + +objectIn.onChange = update; +pathIn.onChange = update; + +function update() +{ + const data = objectIn.get(); + const path = pathIn.get(); + op.setUiError("missing", null); + if (data && path) + { + if (!Array.isArray(data) && !(typeof data === "object")) + { + foundOut.set(false); + op.setUiError("notiterable", "input object of type " + (typeof data) + " is not travesable by path"); + } + else + { + op.setUiError("notiterable", null); + const parts = path.split("."); + op.setUiAttrib({ "extendTitle": parts[parts.length - 1] + "" }); + let result = resolve(path, data); + if (result === undefined) + { + const errorMsg = "could not find element at path " + path; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else if (Array.isArray(result) || result === null || typeof result !== "object") + { + const errorMsg = "element at path " + path + " is not an object"; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else + { + foundOut.set(true); + } + resultOut.set(result); + } + } + else + { + foundOut.set(false); + } +} + +function resolve(path, obj = self, separator = ".") +{ + const properties = Array.isArray(path) ? path : path.split(separator); + return properties.reduce((prev, curr) => prev && prev[curr], obj); +} + + +}; + +Ops.Json.ArrayGetObjectByPath.prototype = new CABLES.Op(); +CABLES.OPS["c73970f0-f8fb-4d12-af19-649d17b0195f"]={f:Ops.Json.ArrayGetObjectByPath,objName:"Ops.Json.ArrayGetObjectByPath"}; + + + + +// ************************************************************** +// +// Ops.Json.ArrayGetStringByPath +// +// ************************************************************** + +Ops.Json.ArrayGetStringByPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const objectIn = op.inArray("Array"); +const pathIn = op.inString("Path"); +const returnPathIn = op.inBool("Return path if missing", false); +const resultOut = op.outString("Output"); +const foundOut = op.outBool("Found"); + +objectIn.onChange = update; +pathIn.onChange = update; +returnPathIn.onChange = update; + +function update() +{ + const data = objectIn.get(); + const path = pathIn.get(); + op.setUiError("missing", null); + if (data && path) + { + if (!Array.isArray(data) && !(typeof data === "object")) + { + foundOut.set(false); + op.setUiError("notiterable", "input object of type " + (typeof data) + " is not travesable by path"); + } + else + { + op.setUiError("notiterable", null); + const parts = path.split("."); + op.setUiAttrib({ "extendTitle": parts[parts.length - 1] + "" }); + let result = resolve(path, data); + if (result === undefined) + { + const errorMsg = "could not find element at path " + path; + let errorLevel = 2; + result = null; + foundOut.set(false); + if (returnPathIn.get()) + { + result = path; + errorLevel = 1; + } + else + { + result = null; + } + op.setUiError("missing", errorMsg, errorLevel); + } + else + { + foundOut.set(true); + result = String(result); + } + resultOut.set(result); + } + } + else + { + foundOut.set(false); + } +} + +function resolve(path, obj = self, separator = ".") +{ + const properties = Array.isArray(path) ? path : path.split(separator); + return properties.reduce((prev, curr) => prev && prev[curr], obj); +} + + +}; + +Ops.Json.ArrayGetStringByPath.prototype = new CABLES.Op(); +CABLES.OPS["064fc275-f61d-4c5f-9692-28c2062248bf"]={f:Ops.Json.ArrayGetStringByPath,objName:"Ops.Json.ArrayGetStringByPath"}; + + + + +// ************************************************************** +// +// Ops.Json.CsvArray +// +// ************************************************************** + +Ops.Json.CsvArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + filename = op.inUrl("file"), + result = op.outArray("result"), + len = op.outNumber("num items"); + +const reload = function () +{ + CABLES.ajax( + op.patch.getFilePath(filename.get()), + function (err, _data, xhr) + { + try + { + const data = JSON.parse(_data); + result.set(data); + len.set(data.length); + } + catch (e) + { + op.logError(e); + result.set(null); + len.set(0); + } + }); +}; + +filename.onChange = reload; + + +}; + +Ops.Json.CsvArray.prototype = new CABLES.Op(); +CABLES.OPS["2e76c1f8-19ac-4e9d-8db8-58b2f9fbc1d3"]={f:Ops.Json.CsvArray,objName:"Ops.Json.CsvArray"}; + + + + +// ************************************************************** +// +// Ops.Json.CsvColumnArray_v2 +// +// ************************************************************** + +Ops.Json.CsvColumnArray_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + colName = op.inString("Column Name", "name"), + inArr = op.inArray("CSV Array"), + inNumbers = op.inBool("Numbers", false), + result = op.outArray("Result"); + +colName.onChange = +inNumbers.onChange = + inArr.onChange = update; + +function update() +{ + let iArr = inArr.get(); + let iName = colName.get(); + + if (!iArr) + { + result.set(null); + return; + } + + op.setUiError("notfound", null); + op.setUiError("notnum", null); + + if (iArr[0].hasOwnProperty(iName)) + { + let arr = []; + + let hasStrings = false; + + if (inNumbers.get()) + { + for (let i = 0; i < iArr.length; i++) + { + let n = Number(iArr[i][iName] || 0); + arr.push(n); + if (!CABLES.UTILS.isNumeric(iArr[i][iName])) + { + hasStrings = true; + } + } + + if (hasStrings) + { + op.setUiError("notnum", "Parse Error / Not all values numerical!"); + } + } + else + { + for (let i = 0; i < iArr.length; i++) + arr.push(iArr[i][iName]); + } + result.set(arr); + } + else + op.setUiError("notfound", "Column not found"); +} + + +}; + +Ops.Json.CsvColumnArray_v2.prototype = new CABLES.Op(); +CABLES.OPS["2503021e-1599-40d9-b038-bf5a36c6b2c7"]={f:Ops.Json.CsvColumnArray_v2,objName:"Ops.Json.CsvColumnArray_v2"}; + + + + +// ************************************************************** +// +// Ops.Json.FilterValidObject +// +// ************************************************************** + +Ops.Json.FilterValidObject = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObj=op.inObject("Object"), + outObject=op.outObject("Last Valid Object"), + outValid=op.outBool("Is Valid"); + +inObj.onChange= + update; + + +function update() +{ + const obj=inObj.get(); + + var r=true; + + if(!obj) r=false; + + if(r) outObject.set(obj); + + outValid.set(r); +} + +}; + +Ops.Json.FilterValidObject.prototype = new CABLES.Op(); +CABLES.OPS["a851cddb-74e5-41e7-ac75-709beae914fd"]={f:Ops.Json.FilterValidObject,objName:"Ops.Json.FilterValidObject"}; + + + + +// ************************************************************** +// +// Ops.Json.GateObject +// +// ************************************************************** + +Ops.Json.GateObject = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + valueInPort = op.inObject("Object In"), + passThroughPort = op.inValueBool("Pass Through", false), + onlyValid = op.inValueBool("Only Valid Objects", false), + valueOutPort = op.outObject("Object Out"); + +valueInPort.onChange = + passThroughPort.onChange = update; +valueInPort.changeAlways = true; + +function update() +{ + if (!valueInPort.get() && onlyValid.get()) return; + if (passThroughPort.get()) valueOutPort.set(valueInPort.get()); + // else valueOutPort.set(null); +} + + +}; + +Ops.Json.GateObject.prototype = new CABLES.Op(); +CABLES.OPS["95e04331-49d6-42da-81d8-5a75261ab22f"]={f:Ops.Json.GateObject,objName:"Ops.Json.GateObject"}; + + + + +// ************************************************************** +// +// Ops.Json.GetStringFromObject +// +// ************************************************************** + +Ops.Json.GetStringFromObject = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + data = op.inObject("data"), + key = op.inString("key"), + result = op.outString("result"); + +result.ignoreValueSerialize = true; +data.ignoreValueSerialize = true; + +data.onChange = exec; + +key.onChange = function () +{ + op.setUiAttrib({ "extendTitle": key.get() }); + exec(); +}; + +function exec() +{ + const dat = data.get(); + const theKey = key.get(); + + if (dat && dat.hasOwnProperty(theKey)) result.set(dat[theKey]); + else result.set(null); +} + + +}; + +Ops.Json.GetStringFromObject.prototype = new CABLES.Op(); +CABLES.OPS["6562a543-2147-485d-8fd3-58fe3357684e"]={f:Ops.Json.GetStringFromObject,objName:"Ops.Json.GetStringFromObject"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectFilterContentByKey +// +// ************************************************************** + +Ops.Json.ObjectFilterContentByKey = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObj=op.inObject("Object"), + inStr=op.inString("name",""), + inRemoveNull=op.inBool("Remove Null",false), + outObj=op.outObject("Result"); + +inObj.onChange= + inStr.onChange= + inRemoveNull.onChange=update; + +function update() +{ + const obj=JSON.parse(JSON.stringify(inObj.get())); + + filter(obj); + + outObj.set(null); + outObj.set(obj); +} + +function filter(obj) +{ + for(let i in obj) + { + if(inStr.get() && i.indexOf(inStr.get())!==0)delete obj[i]; + if(inRemoveNull.get() && obj[i]===null)delete obj[i]; + + if(typeof obj[i] =="object") filter(obj[i]); + } +} + +}; + +Ops.Json.ObjectFilterContentByKey.prototype = new CABLES.Op(); +CABLES.OPS["89bddc38-000b-4982-8b37-b6e48f0b001b"]={f:Ops.Json.ObjectFilterContentByKey,objName:"Ops.Json.ObjectFilterContentByKey"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectFunnel +// +// ************************************************************** + +Ops.Json.ObjectFunnel = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObj1 = op.inObject("Object1"), + inObj2 = op.inObject("Object2"), + inObj3 = op.inObject("Object3"), + inObj4 = op.inObject("Object4"), + inObj5 = op.inObject("Object5"); + +let outObj = op.outObject("Out Object"); + +inObj1.onChange = function () +{ + outObj.set(null); + outObj.set(inObj1.get()); +}; + +inObj2.onChange = function () +{ + outObj.set(null); + outObj.set(inObj2.get()); +}; + +inObj3.onChange = function () +{ + outObj.set(null); + outObj.set(inObj3.get()); +}; + +inObj4.onChange = function () +{ + outObj.set(null); + outObj.set(inObj4.get()); +}; + +inObj5.onChange = function () +{ + outObj.set(null); + outObj.set(inObj5.get()); +}; + + +}; + +Ops.Json.ObjectFunnel.prototype = new CABLES.Op(); +CABLES.OPS["ff67d867-098e-4eb1-8b37-00ec0863ce5e"]={f:Ops.Json.ObjectFunnel,objName:"Ops.Json.ObjectFunnel"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectGetArrayByPath +// +// ************************************************************** + +Ops.Json.ObjectGetArrayByPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const objectIn = op.inObject("Object"); +const pathIn = op.inString("Path"); +const resultOut = op.outArray("Output"); +const foundOut = op.outBool("Found"); + +objectIn.onChange = update; +pathIn.onChange = update; + +function update() +{ + const data = objectIn.get(); + const path = pathIn.get(); + op.setUiError("missing", null); + if (data && path) + { + if (!Array.isArray(data) && !(typeof data === "object")) + { + foundOut.set(false); + op.setUiError("notiterable", "input object of type " + (typeof data) + " is not travesable by path"); + } + else + { + op.setUiError("notiterable", null); + const parts = path.split("."); + op.setUiAttrib({ "extendTitle": parts[parts.length - 1] + "" }); + let result = resolve(path, data); + if (result === undefined) + { + const errorMsg = "could not find element at path " + path; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else if (!Array.isArray(result)) + { + const errorMsg = "element at path " + path + " is not an array"; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else + { + foundOut.set(true); + } + resultOut.set(result); + } + } + else + { + foundOut.set(false); + } +} + +function resolve(path, obj = self, separator = ".") +{ + const properties = Array.isArray(path) ? path : path.split(separator); + return properties.reduce((prev, curr) => prev && prev[curr], obj); +} + + +}; + +Ops.Json.ObjectGetArrayByPath.prototype = new CABLES.Op(); +CABLES.OPS["a9354531-a42d-4216-ad8c-364df989a9a1"]={f:Ops.Json.ObjectGetArrayByPath,objName:"Ops.Json.ObjectGetArrayByPath"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectGetArrayValuesByPath +// +// ************************************************************** + +Ops.Json.ObjectGetArrayValuesByPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const objectIn = op.inObject("Object"); +const pathIn = op.inString("Path"); +const resultOut = op.outArray("Output"); +const foundOut = op.outBool("Found"); + +objectIn.onChange = update; +pathIn.onChange = update; + +function update() +{ + const data = objectIn.get(); + let result = []; + const path = pathIn.get(); + op.setUiError("path", null); + + if (data && path) + { + if (typeof data !== "object") + { + foundOut.set(false); + op.setUiError("notiterable", "input object of type " + (typeof data) + " is not travesable by path"); + } + else if (Array.isArray(data)) + { + foundOut.set(false); + op.setUiError("notiterable", "input of type " + (typeof data) + " is not an object"); + } + else + { + op.setUiError("notiterable", null); + const parts = path.split("."); + foundOut.set(false); + + // find first array in path + let checkPath = ""; + let pathPrefix = ""; + let pathSuffix = ""; + let checkData = null; + for (let i = 0; i < parts.length; i++) + { + checkPath += parts[i]; + checkData = resolve(checkPath, data); + if (Array.isArray(checkData)) + { + pathPrefix = checkPath; + pathSuffix = parts.splice(i + 2, parts.length - (i + 2)).join("."); + break; + } + checkPath += "."; + } + if (checkData) + { + if (parts.length > 1) + { + for (let i = 0; i < checkData.length; i++) + { + let resolvePath = pathPrefix + "." + i; + if (pathSuffix && pathSuffix !== "") + { + resolvePath += "." + pathSuffix; + } + const resolvedData = resolve(resolvePath, data); + if (typeof resolvedData !== "undefined") + { + foundOut.set(true); + } + result.push(resolvedData); + } + } + else + { + if (Array.isArray(checkData)) + { + result = checkData; + } + else + { + result = [checkData]; + } + foundOut.set(true); + } + + const titleParts = pathIn.get().split("."); + const extendTitle = titleParts[titleParts.length - 1] + ""; + op.setUiAttrib({ "extendTitle": extendTitle }); + } + if (foundOut.get()) + { + resultOut.set(result); + } + else + { + op.setUiError("path", "given path seems to be invalid!", 1); + resultOut.set([]); + } + } + } + else + { + foundOut.set(false); + } +} + +function resolve(path, obj = self, separator = ".") +{ + const properties = Array.isArray(path) ? path : path.split(separator); + return properties.reduce((prev, curr) => prev && prev[curr], obj); +} + + +}; + +Ops.Json.ObjectGetArrayValuesByPath.prototype = new CABLES.Op(); +CABLES.OPS["609a645e-5e24-4a5e-a379-804c5b64f5d5"]={f:Ops.Json.ObjectGetArrayValuesByPath,objName:"Ops.Json.ObjectGetArrayValuesByPath"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectGetArray_v2 +// +// ************************************************************** + +Ops.Json.ObjectGetArray_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + data = op.inObject("data"), + key = op.inString("key"), + result = op.outArray("result"), + arrLength = op.outNumber("Length"); + +result.ignoreValueSerialize = true; +data.ignoreValueSerialize = true; + +data.onChange = update; + +key.onChange = function () +{ + if (!key.isLinked())op.setUiAttrib({ "extendTitle": key.get() }); + update(); +}; + +function update() +{ + result.set(null); + const dat = data.get(); + const k = key.get(); + if (dat && (dat.hasOwnProperty(k) || dat[k])) + { + result.set(dat[k]); + if (!result.get()) + { + arrLength.set(0); + } + else + { + arrLength.set(result.get().length); + } + } + else + { + arrLength.set(0); + } +} + + +}; + +Ops.Json.ObjectGetArray_v2.prototype = new CABLES.Op(); +CABLES.OPS["7c06a818-9c07-493a-8c4f-04eb2c7796f5"]={f:Ops.Json.ObjectGetArray_v2,objName:"Ops.Json.ObjectGetArray_v2"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectGetNumberByPath +// +// ************************************************************** + +Ops.Json.ObjectGetNumberByPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const objectIn = op.inObject("Object"); +const pathIn = op.inString("Path"); +const resultOut = op.outNumber("Output"); +const foundOut = op.outBool("Found"); + +objectIn.onChange = update; +pathIn.onChange = update; + +function update() +{ + const data = objectIn.get(); + const path = pathIn.get(); + op.setUiError("missing", null); + if (data && path) + { + if (!Array.isArray(data) && !(typeof data === "object")) + { + foundOut.set(false); + op.setUiError("notiterable", "input object of type " + (typeof data) + " is not travesable by path"); + } + else + { + op.setUiError("notiterable", null); + const parts = path.split("."); + op.setUiAttrib({ "extendTitle": parts[parts.length - 1] + "" }); + let result = resolve(path, data); + if (result === undefined) + { + const errorMsg = "could not find element at path " + path; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else if (typeof result !== "number") + { + const errorMsg = "element at path " + path + " is not a number"; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else + { + foundOut.set(true); + } + resultOut.set(result); + } + } + else + { + foundOut.set(false); + } +} + +function resolve(path, obj = self, separator = ".") +{ + const properties = Array.isArray(path) ? path : path.split(separator); + return properties.reduce((prev, curr) => prev && prev[curr], obj); +} + + +}; + +Ops.Json.ObjectGetNumberByPath.prototype = new CABLES.Op(); +CABLES.OPS["ec736baf-feb8-4d8d-b04c-173fc197759c"]={f:Ops.Json.ObjectGetNumberByPath,objName:"Ops.Json.ObjectGetNumberByPath"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectGetNumber_v2 +// +// ************************************************************** + +Ops.Json.ObjectGetNumber_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + data = op.inObject("Data"), + key = op.inString("Key"), + result = op.outNumber("Result"), + outFound = op.outBoolNum("Found"); + +result.ignoreValueSerialize = true; +data.ignoreValueSerialize = true; + +data.onChange = exec; + +key.onChange = function () +{ + if (!key.isLinked())op.setUiAttrib({ "extendTitle": key.get() }); + exec(); +}; + +function exec() +{ + if (data.get()) + { + const val = data.get()[key.get()]; + result.set(val); + if (val === undefined) outFound.set(0); + else outFound.set(1); + } + else + { + result.set(null); + outFound.set(0); + } +} + + +}; + +Ops.Json.ObjectGetNumber_v2.prototype = new CABLES.Op(); +CABLES.OPS["a7335e79-046e-40da-9e9c-db779b0a5e53"]={f:Ops.Json.ObjectGetNumber_v2,objName:"Ops.Json.ObjectGetNumber_v2"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectGetObjectByPath +// +// ************************************************************** + +Ops.Json.ObjectGetObjectByPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const objectIn = op.inObject("Object"); +const pathIn = op.inString("Path"); +const resultOut = op.outObject("Output"); +const foundOut = op.outBool("Found"); + +objectIn.onChange = update; +pathIn.onChange = update; + +function update() +{ + const data = objectIn.get(); + const path = pathIn.get(); + op.setUiError("missing", null); + if (data && path) + { + if (!Array.isArray(data) && !(typeof data === "object")) + { + foundOut.set(false); + op.setUiError("notiterable", "input object of type " + (typeof data) + " is not travesable by path"); + } + else + { + op.setUiError("notiterable", null); + const parts = path.split("."); + op.setUiAttrib({ "extendTitle": parts[parts.length - 1] + "" }); + let result = resolve(path, data); + if (result === undefined) + { + const errorMsg = "could not find element at path " + path; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else if (Array.isArray(result) || result === null || typeof result !== "object") + { + const errorMsg = "element at path " + path + " is not an object"; + foundOut.set(false); + result = null; + op.setUiError("missing", errorMsg, 2); + } + else + { + foundOut.set(true); + } + resultOut.set(result); + } + } + else + { + foundOut.set(false); + } +} + +function resolve(path, obj = self, separator = ".") +{ + const properties = Array.isArray(path) ? path : path.split(separator); + return properties.reduce((prev, curr) => prev && prev[curr], obj); +} + + +}; + +Ops.Json.ObjectGetObjectByPath.prototype = new CABLES.Op(); +CABLES.OPS["574513c7-472b-433c-bf99-d906d3c737cd"]={f:Ops.Json.ObjectGetObjectByPath,objName:"Ops.Json.ObjectGetObjectByPath"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectGetObject_v2 +// +// ************************************************************** + +Ops.Json.ObjectGetObject_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + data = op.inObject("Object"), + key = op.inString("Key"), + result = op.outObject("Result"); + +result.ignoreValueSerialize = true; +data.ignoreValueSerialize = true; + +op.setUiAttrib({ "extendTitlePort": key.name }); + +key.onChange = +data.onChange = update; + +function update() +{ + if (data.get()) + { + result.set(data.get()[key.get()]); + } + else + { + result.set(null); + } +} + + +}; + +Ops.Json.ObjectGetObject_v2.prototype = new CABLES.Op(); +CABLES.OPS["d1dfa305-89db-4ca1-b0ac-2d6321d76ae8"]={f:Ops.Json.ObjectGetObject_v2,objName:"Ops.Json.ObjectGetObject_v2"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectGetString +// +// ************************************************************** + +Ops.Json.ObjectGetString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + data = op.inObject("data"), + key = op.inString("Key"), + result = op.outString("Result"); + +result.ignoreValueSerialize = true; +data.ignoreValueSerialize = true; + +op.setUiAttrib({ "extendTitlePort": key.name }); + +key.onChange = +data.onChange = exec; + +function exec() +{ + if (data.get()) + { + result.set(data.get()[key.get()]); + } + else + { + result.set(null); + } +} + + +}; + +Ops.Json.ObjectGetString.prototype = new CABLES.Op(); +CABLES.OPS["7d86cd28-f7d8-44a1-a4da-466c4782aaec"]={f:Ops.Json.ObjectGetString,objName:"Ops.Json.ObjectGetString"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectGetStringByPath +// +// ************************************************************** + +Ops.Json.ObjectGetStringByPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const objectIn = op.inObject("Object"); +const pathIn = op.inString("Path"); +const returnPathIn = op.inBool("Output path if missing", false); +const resultOut = op.outString("Output"); +const foundOut = op.outBool("Found"); + +objectIn.ignoreValueSerialize = true; + +objectIn.onChange = update; +pathIn.onChange = update; +returnPathIn.onChange = update; + +function update() +{ + const data = objectIn.get(); + const path = pathIn.get(); + op.setUiError("missing", null); + if (data && path) + { + if (!Array.isArray(data) && !(typeof data === "object")) + { + foundOut.set(false); + op.setUiError("notiterable", "input object of type " + (typeof data) + " is not travesable by path"); + } + else + { + op.setUiError("notiterable", null); + const parts = path.split("."); + op.setUiAttrib({ "extendTitle": parts[parts.length - 1] + "" }); + let result = resolve(path, data); + if (result === undefined) + { + const errorMsg = "could not find element at path " + path; + let errorLevel = 2; + result = null; + foundOut.set(false); + if (returnPathIn.get()) + { + result = path; + errorLevel = 1; + } + else + { + result = null; + } + op.setUiError("missing", errorMsg, errorLevel); + } + else + { + foundOut.set(true); + result = String(result); + } + resultOut.set(result); + } + } + else + { + foundOut.set(false); + } +} + +function resolve(path, obj = self, separator = ".") +{ + const properties = Array.isArray(path) ? path : path.split(separator); + return properties.reduce((prev, curr) => prev && prev[curr], obj); +} + + +}; + +Ops.Json.ObjectGetStringByPath.prototype = new CABLES.Op(); +CABLES.OPS["497a6b7c-e33c-45e4-8fb2-a9149d972b5b"]={f:Ops.Json.ObjectGetStringByPath,objName:"Ops.Json.ObjectGetStringByPath"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectIsNull +// +// ************************************************************** + +Ops.Json.ObjectIsNull = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inObj = op.inObject("Object"); +const outResult = op.outBoolNum("Result"); + +inObj.onChange = function () +{ + outResult.set(!inObj.get()); +}; + + +}; + +Ops.Json.ObjectIsNull.prototype = new CABLES.Op(); +CABLES.OPS["2fd858a8-3dff-43e4-bd85-a62c7d23b5a2"]={f:Ops.Json.ObjectIsNull,objName:"Ops.Json.ObjectIsNull"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectIterate +// +// ************************************************************** + +Ops.Json.ObjectIterate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObj = op.inObject("Object"), + outKey = op.outNumber("Key"); + +inObj.onChange = function () +{ + let obj = inObj.get(); + + if (obj) + { + for (let i in obj) + { + outKey.set(i); + } + } +}; + + +}; + +Ops.Json.ObjectIterate.prototype = new CABLES.Op(); +CABLES.OPS["128f5b07-17f9-43fb-ab61-c170a9a9cd8d"]={f:Ops.Json.ObjectIterate,objName:"Ops.Json.ObjectIterate"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectKeys +// +// ************************************************************** + +Ops.Json.ObjectKeys = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObj = op.inObject("Object"), + outNumKeys = op.outNumber("Num Keys"), + outKeys = op.outArray("Keys"); + +inObj.onChange = function () +{ + let o = inObj.get(); + if (!o) + { + outNumKeys.set(0); + outKeys.set([]); + return; + } + + let keys = Object.keys(o); + outNumKeys.set(keys.length); + outKeys.set(keys); +}; + + +}; + +Ops.Json.ObjectKeys.prototype = new CABLES.Op(); +CABLES.OPS["83b4d148-8cb3-4a45-8824-957eeaf02e22"]={f:Ops.Json.ObjectKeys,objName:"Ops.Json.ObjectKeys"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectOr +// +// ************************************************************** + +Ops.Json.ObjectOr = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + object0=op.inObject("Object 1"), + object1=op.inObject("Object 2"), + object2=op.inObject("Object 3"), + object3=op.inObject("Object 4"), + object4=op.inObject("Object 5"), + object5=op.inObject("Object 6"), + object6=op.inObject("Object 7"), + object7=op.inObject("Object 8"), + result=op.outObject("Result"); + +object0.onChange=exec; +object1.onChange=exec; +object2.onChange=exec; +object3.onChange=exec; +object4.onChange=exec; +object5.onChange=exec; +object6.onChange=exec; +object7.onChange=exec; + + +function exec() +{ + result.set( object0.get() || object1.get() || object2.get() || object3.get() || object4.get() || object5.get() || object6.get() || object7.get() ); +} + + + +}; + +Ops.Json.ObjectOr.prototype = new CABLES.Op(); +CABLES.OPS["98c9a790-c018-466a-9c36-7afd0d2a8826"]={f:Ops.Json.ObjectOr,objName:"Ops.Json.ObjectOr"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectRecorder +// +// ************************************************************** + +Ops.Json.ObjectRecorder = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTriggerButton("Exec"), + reset = op.inTriggerButton("reset"), + download = op.inTriggerButton("download"), + inObj = op.inObject("Object"), + numObjects = op.outValue("Num Objects"); + +let data = []; + +reset.onTriggered = function () +{ + data.length = 0; + numObjects.set(data.length); +}; + +exec.onTriggered = function () +{ + if (inObj.get()) data.push(JSON.parse(JSON.stringify(inObj.get()))); + numObjects.set(data.length); +}; +download.onTriggered = function () +{ + let dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(data)); + let downloadAnchorNode = document.createElement("a"); + downloadAnchorNode.setAttribute("href", dataStr); + downloadAnchorNode.setAttribute("download", "jsonexport.json"); + downloadAnchorNode.click(); + downloadAnchorNode.remove(); +}; + + +}; + +Ops.Json.ObjectRecorder.prototype = new CABLES.Op(); +CABLES.OPS["5a6c7435-34ee-4897-9fda-548db54aff02"]={f:Ops.Json.ObjectRecorder,objName:"Ops.Json.ObjectRecorder"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectSetArray +// +// ************************************************************** + +Ops.Json.ObjectSetArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObject = op.inObject("Object"), + outObject = op.outObject("Result Object"), + inKey = op.inString("Key"), + inValue = op.inArray("Value"); + +inObject.onChange = + inKey.onChange = + inValue.onChange = update; + +function update() +{ + let obj = inObject.get(); + if (!obj)obj = {}; + + if (inKey.get()) obj[inKey.get()] = inValue.get(); + + outObject.set(null); + outObject.set(obj); +} + + +}; + +Ops.Json.ObjectSetArray.prototype = new CABLES.Op(); +CABLES.OPS["d89cbbe0-5c6e-4b6c-84b3-1fa588f19a79"]={f:Ops.Json.ObjectSetArray,objName:"Ops.Json.ObjectSetArray"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectSetNumber +// +// ************************************************************** + +Ops.Json.ObjectSetNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObject=op.inObject("Object"), + outObject=op.outObject("Result Object"), + inKey=op.inString("Key"), + inValue=op.inValueFloat("Number"); + +inObject.onChange= + inKey.onChange= + inValue.onChange=update; + +function update() +{ + var obj=inObject.get(); + if(!obj)obj={}; + + obj[inKey.get()]=inValue.get(); + + outObject.set(null); + outObject.set(obj); +} + + +}; + +Ops.Json.ObjectSetNumber.prototype = new CABLES.Op(); +CABLES.OPS["e50fd010-5db1-48a7-91dc-94799ae1cb3b"]={f:Ops.Json.ObjectSetNumber,objName:"Ops.Json.ObjectSetNumber"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectSetObject +// +// ************************************************************** + +Ops.Json.ObjectSetObject = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inObject = op.inObject("Object"); +const inKey = op.inString("Key"); +const inValue = op.inObject("Object Value"); +const inUpdate = op.inTriggerButton("Set Object"); +const outObject = op.outObject("Result Object"); + +// inObject.onChange=update; +// inKey.onChange=update; +inUpdate.onTriggered = update; + +let obj = null; + +op.onDelete = +inObject.onChange = () => +{ + if (obj) + delete obj[inKey.get()]; +}; + +function update() +{ + obj = inObject.get(); + if (!obj) + { + obj = {}; + } + else + { + let changed = false; + + if (inKey.get().indexOf(",") > -1) + { + const keys = inKey.get().split(","); + + for (let i in keys) + { + if (keys[i] && keys[i].length > 0) + { + if (obj[keys[i]] != inValue.get())changed = true; + obj[keys[i]] = inValue.get(); + } + } + } + else + { + if (obj[inKey.get()] != inValue.get())changed = true; + obj[inKey.get()] = inValue.get(); + } + + outObject.set(null); + } + + outObject.set(obj); +} + + +}; + +Ops.Json.ObjectSetObject.prototype = new CABLES.Op(); +CABLES.OPS["ba7cad72-f226-4c27-b5e6-b20dabb8a3a4"]={f:Ops.Json.ObjectSetObject,objName:"Ops.Json.ObjectSetObject"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectSetObjectSimple +// +// ************************************************************** + +Ops.Json.ObjectSetObjectSimple = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inObject = op.inObject("Object"); +const inKey = op.inString("Key"); +const inValue = op.inObject("Object Value"); +const outObject = op.outObject("Result Object"); + +op.onDelete = removeKey; + +inObject.onLinkChanged = +inValue.onChange = +inKey.onChange = () => +{ + removeKey(); + update(); + op.setUiAttrib({ "extendTitle": inKey.get() }); +}; + +outObject.onLinkChanged = +inObject.onChange = update; + +let currentKey = ""; + +let obj = {}; + +function removeKey() +{ + delete obj[currentKey]; +} + +function copyObject() +{ + +} + +function update() +{ + obj = inObject.get() || {}; + + let changed = false; + + currentKey = inKey.get(); + if (obj[inKey.get()] != inValue.get())changed = true; + obj[inKey.get()] = inValue.get(); + + outObject.set(null); + outObject.set(obj); +} + + +}; + +Ops.Json.ObjectSetObjectSimple.prototype = new CABLES.Op(); +CABLES.OPS["448f0905-dd4c-4909-9ddb-352c7f4467de"]={f:Ops.Json.ObjectSetObjectSimple,objName:"Ops.Json.ObjectSetObjectSimple"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectSetString +// +// ************************************************************** + +Ops.Json.ObjectSetString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObject=op.inObject("Object"), + outObject=op.outObject("Result Object"), + inKey=op.inString("Key"), + inValue=op.inString("Value"); + +inObject.onChange= + inKey.onChange= + inValue.onChange=update; + +function update() +{ + var obj=inObject.get(); + if(!obj)obj={}; + + obj[inKey.get()]=inValue.get(); + + outObject.set(null); + outObject.set(obj); +} + + +}; + +Ops.Json.ObjectSetString.prototype = new CABLES.Op(); +CABLES.OPS["31f19b25-2ed8-4ac6-84d4-a146da9e3a05"]={f:Ops.Json.ObjectSetString,objName:"Ops.Json.ObjectSetString"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectStringify_v2 +// +// ************************************************************** + +Ops.Json.ObjectStringify_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObj = op.inObject("Object"), + inBeautify = op.inValueBool("Beautify"), + outString = op.outString("Result"), + outError = op.outBoolNum("Error"); + +inBeautify.onChange = inObj.onChange = update; + +function update() +{ + try + { + if (!inBeautify.get())outString.set(JSON.stringify(inObj.get())); + else outString.set(JSON.stringify(inObj.get(), false, 4)); + outError.set(0); + } + catch (e) + { + op.error(e); + outString.set("error"); + outError.set(1); + } +} + + +}; + +Ops.Json.ObjectStringify_v2.prototype = new CABLES.Op(); +CABLES.OPS["89fc70ea-2350-4a0e-9a24-4efca10cced6"]={f:Ops.Json.ObjectStringify_v2,objName:"Ops.Json.ObjectStringify_v2"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectToArray +// +// ************************************************************** + +Ops.Json.ObjectToArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inObj = op.inObject("Object"); +const outArray = op.outArray("Array"); + +inObj.onChange = function () +{ + outArray.set(inObj.get()); +}; + + +}; + +Ops.Json.ObjectToArray.prototype = new CABLES.Op(); +CABLES.OPS["f8ac4574-ffe3-4618-a27f-30d190308e2c"]={f:Ops.Json.ObjectToArray,objName:"Ops.Json.ObjectToArray"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectTrigger +// +// ************************************************************** + +Ops.Json.ObjectTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTrigger=op.inTriggerButton("Trigger"), + inObj=op.inObject("Object"), + outTrigger=op.outTrigger("Next"), + outObj=op.outObject("Result"); + +inTrigger.onTriggered=function() +{ + outObj.set(null); + outObj.set(inObj.get()); + outTrigger.trigger(); +}; + + +}; + +Ops.Json.ObjectTrigger.prototype = new CABLES.Op(); +CABLES.OPS["e437c074-190f-4adb-8265-3fddea27fe33"]={f:Ops.Json.ObjectTrigger,objName:"Ops.Json.ObjectTrigger"}; + + + + +// ************************************************************** +// +// Ops.Json.ObjectValues +// +// ************************************************************** + +Ops.Json.ObjectValues = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObj = op.inObject("Object"), + outNumValues = op.outNumber("Num values"), + outValues = op.outArray("Values"); + +inObj.onChange = () => +{ + const sourceObj = inObj.get(); + if (!sourceObj) + { + outNumValues.set(0); + outValues.set([]); + return; + } + + const values = Object.values(sourceObj); + outNumValues.set(values.length); + outValues.set(values); +}; + + +}; + +Ops.Json.ObjectValues.prototype = new CABLES.Op(); +CABLES.OPS["32ff73f5-7947-42b0-83fa-e079af7beb5c"]={f:Ops.Json.ObjectValues,objName:"Ops.Json.ObjectValues"}; + + + + +// ************************************************************** +// +// Ops.Json.ParseObject_v2 +// +// ************************************************************** + +Ops.Json.ParseObject_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + str = op.inStringEditor("JSON String", "{}", "json"), + outObj = op.outObject("Result"), + isValid = op.outBoolNum("Valid"); + +str.onChange = parse; +parse(); + +function parse() +{ + if (!str.get()) + { + outObj.set(null); + isValid.set(false); + return; + } + try + { + const obj = JSON.parse(str.get()); + outObj.set(null); + outObj.set(obj); + isValid.set(true); + op.setUiError("invalidjson", null); + } + catch (ex) + { + op.logError(ex); + isValid.set(false); + + let outStr = ""; + const parts = ex.message.split(" "); + for (let i = 0; i < parts.length - 1; i++) + { + const num = parseFloat(parts[i + 1]); + if (num && parts[i] == "position") + { + const outStrA = str.get().substring(num - 15, num); + const outStrB = str.get().substring(num, num + 1); + const outStrC = str.get().substring(num + 1, num + 15); + outStr = "" + outStrA + "" + outStrB + "" + outStrC + " "; + } + } + + op.setUiError("invalidjson", "INVALID JSON
    can not parse string to object:
    " + ex.message + "
    " + outStr); + } +} + + +}; + +Ops.Json.ParseObject_v2.prototype = new CABLES.Op(); +CABLES.OPS["2ce8a4d3-37d3-4cdc-abd1-a560fbe841ee"]={f:Ops.Json.ParseObject_v2,objName:"Ops.Json.ParseObject_v2"}; + + + + +// ************************************************************** +// +// Ops.Json.RouteObject +// +// ************************************************************** + +Ops.Json.RouteObject = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +const + NUM_PORTS = 10, + DEFAULT_OBJECT = {}, + indexPort = op.inInt('index'), + objectPort = op.inObject('Object in'), + defaultObjectPort = op.inObject('default object', DEFAULT_OBJECT), + objectPorts = createOutPorts(DEFAULT_OBJECT); + +indexPort.onChange = objectPort.onChange = defaultObjectPort.onChange = update; + +setDefaultValues(); +update(); + +function createOutPorts() +{ + var arrayObjects = []; + for(var i=0; i port.set(null)); + if(defaultObjectPort.get()) + { + objectPorts.forEach(port => port.set(defaultValue)); + } +}; + +function update() +{ + setDefaultValues(); + var index = indexPort.get(); + var value = objectPort.get(); + + index = Math.floor(index); + index = clamp(index, 0, NUM_PORTS-1); + objectPorts[index].set(value); +}; + +function clamp(value, min, max) +{ + return Math.min(Math.max(value, min), max); +}; + + +}; + +Ops.Json.RouteObject.prototype = new CABLES.Op(); +CABLES.OPS["bc969951-32b5-4226-9944-80a719a65497"]={f:Ops.Json.RouteObject,objName:"Ops.Json.RouteObject"}; + + + + +// ************************************************************** +// +// Ops.Json.SaveJsonFile +// +// ************************************************************** + +Ops.Json.SaveJsonFile = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +const + download = op.inTriggerButton("download"), + filename = op.inString("Filename", "jsonexport"), + inObject = op.inObject("Object"); + +download.onTriggered = function () +{ + const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(inObject.get(), null, 2)); + const downloadAnchorNode = document.createElement("a"); + downloadAnchorNode.setAttribute("href", dataStr); + downloadAnchorNode.setAttribute("download", filename.get() + ".json"); + downloadAnchorNode.click(); + downloadAnchorNode.remove(); +}; + + +}; + +Ops.Json.SaveJsonFile.prototype = new CABLES.Op(); +CABLES.OPS["04596a28-9563-4f42-8756-2d2a831baa60"]={f:Ops.Json.SaveJsonFile,objName:"Ops.Json.SaveJsonFile"}; + + + + +// ************************************************************** +// +// Ops.Json.SwitchObject +// +// ************************************************************** + +Ops.Json.SwitchObject = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const NUM_PORTS = 8; + +const inIndex = op.inValueInt("Object Index", 0); +const objectPorts = []; +const outObject = op.outObject("object out"); + +op.onLoaded = function () { indexChanged(); }; +inIndex.onChange = indexChanged; + +for (let i = 0; i < NUM_PORTS; i++) +{ + let port = op.inObject("object port " + i); + port.inputNum = i; + port.onChange = onPortChange.bind(port); + objectPorts[i] = port; +} + +function indexChanged() +{ + let index = Math.max(0, Math.floor(inIndex.get())); + if (index < 0) index = 0; + else if (index > NUM_PORTS - 1) index = NUM_PORTS - 1; + + outObject.set(null); + outObject.set(objectPorts[index].get()); +} + +function onPortChange() +{ + if (this.inputNum != inIndex.get()) return; + + outObject.set(null); + outObject.set(this.get()); +} + + +}; + +Ops.Json.SwitchObject.prototype = new CABLES.Op(); +CABLES.OPS["345d535e-267d-49fb-98c0-c8a8f9424160"]={f:Ops.Json.SwitchObject,objName:"Ops.Json.SwitchObject"}; + + + + +// ************************************************************** +// +// Ops.Libs.Lottie.LottieSVGPlayer +// +// ************************************************************** + +Ops.Libs.Lottie.LottieSVGPlayer = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inEle = op.inObject("HTML Element"), + inData = op.inObject("JSON Data"), + inPlayMode = op.inSwitch("Play Mode", ["Auto", "Frame"], "Auto"), + + inFrame = op.inFloat("Render Frame", 0), + inLoop = op.inValueBool("Loop", true), + + inPlay = op.inValueBool("Play", true), + + inDir = op.inBool("Play Backward"), + inRewind = op.inTriggerButton("Rewind"), + + outComplete = op.outBool("Completed", false), + outProgress = op.outNumber("Progress"), + outTotalFrames = op.outNumber("Total Frames"); + +op.setPortGroup("Timing", [inLoop, inPlayMode, inFrame, inPlay, inRewind, inDir]); + +let anim = null; +let playmodeAuto = true; + +inPlay.onChange = play; +inRewind.onTriggered = inLoop.onChange = inEle.onChange = inData.onChange = updateData; +inFrame.onChange = gotoFrame; +inDir.onChange = updateDir; +inPlayMode.onChange = updateUi; +updateUi(); + +function updateUi() +{ + playmodeAuto = inPlayMode.get() === "Auto"; + + inPlay.setUiAttribs({ "greyout": !playmodeAuto }); + inDir.setUiAttribs({ "greyout": !playmodeAuto }); + inRewind.setUiAttribs({ "greyout": !playmodeAuto }); + inFrame.setUiAttribs({ "greyout": playmodeAuto }); + if (playmodeAuto) play(); + else gotoFrame(); +} + +function dispose() +{ + if (anim) + { + anim.destroy(); + anim = null; + outTotalFrames.set(0); + } +} + +function play() +{ + if (!anim) return; + if (!playmodeAuto) return gotoFrame(); + + outComplete.set(false); + if (!inPlay.get())anim.pause(); + else anim.play(); +} + +function gotoFrame() +{ + if (playmodeAuto) return; + if (!anim) return; + + let fr = inFrame.get(); + if (inLoop.get())fr %= anim.totalFrames; + + anim.goToAndStop(fr, true); +} + +function updateDir() +{ + if (!anim) return; + if (!inDir.get()) anim.setDirection(1); + else anim.setDirection(-1); + + if (inPlay.get() && playmodeAuto) play(); +} + +function updateData() +{ + if (anim)dispose(); + if (!inEle.get() || !inData.get()) return; + if (Object.keys(inData.get()).length === 0) return; + + updateUi(); + + const params = { + "container": inEle.get(), + "renderer": "svg", + "loop": inLoop.get() == true, + "autoplay": (inPlay.get() == true && playmodeAuto), + "animationData": inData.get() + }; + + anim = lottie.loadAnimation(params); + + anim.addEventListener("complete", () => + { + outComplete.set(true); + }); + + anim.addEventListener("enterFrame", (e) => + { + outProgress.set(e.currentTime / (e.totalTime - 1)); + }); + outTotalFrames.set(anim.totalFrames); + updateDir(); + gotoFrame(); +} + + +}; + +Ops.Libs.Lottie.LottieSVGPlayer.prototype = new CABLES.Op(); +CABLES.OPS["c4ed075b-c897-4788-9cc0-2df638671f67"]={f:Ops.Libs.Lottie.LottieSVGPlayer,objName:"Ops.Libs.Lottie.LottieSVGPlayer"}; + + + + +// ************************************************************** +// +// Ops.Libs.Lottie.LottieTexturePlayer_v2 +// +// ************************************************************** + +Ops.Libs.Lottie.LottieTexturePlayer_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTrigger("exe"), + inData = op.inObject("JSON Data"), + inPlayMode = op.inSwitch("Play Mode", ["Auto", "Frame"], "Auto"), + inFrame = op.inFloat("frame"), + inPlay = op.inBool("play"), + inRewind = op.inTriggerButton("Rewind"), + speed = op.inFloat("speed", 1), + width = op.inInt("texture width", 1280), + height = op.inInt("texture height", 720), + tfilter = op.inDropDown("Filter", ["nearest", "linear", "mipmap"], "linear"), + wrap = op.inDropDown("wrap", ["repeat", "mirrored repeat", "clamp to edge"], "repeat"), + bmScale = op.inSwitch("scale", ["fit", "nofit"], "fit"), + textureOut = op.outTexture("texture"), + outTotalFrames = op.outNumber("Total Frames"); + +const cgl = op.patch.cgl; +const canvasId = "bodymovin_" + CABLES.generateUUID(); +let createTexture = false; +let canvasImage = null; +let anim = null; +let ctx = null; +let canvas = null; +let cgl_filter = CGL.Texture.FILTER_LINEAR; +let cgl_wrap = CGL.Texture.WRAP_REPEAT; +let lastFrame = -2; +let playmodeAuto = true; +let updateTexture = true; +let tex = null; + +op.setPortGroup("Player Timing", [inPlayMode, inFrame, inPlay, inRewind, speed]); +op.setPortGroup("Texture Settings", [tfilter, wrap, width, height, bmScale]); + +inPlayMode.onChange = updateUi; +tfilter.onChange = onLottieFilterChange; +inData.onChange = reload; +bmScale.onChange = +width.onChange = + height.onChange = reloadForce; + +inPlay.onChange = function () +{ + if (anim) + if (inPlay.get()) anim.play(); + else anim.pause(); +}; + +inRewind.onTriggered = function () +{ + if (inPlay.get()) anim.goToAndPlay(0, true); + else anim.goToAndStop(0, true); +}; + +speed.onChange = function () +{ + if (anim) anim.setSpeed(speed.get()); +}; + +wrap.onChange = function () +{ + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + createTexture = true; +}; + +function onLottieFilterChange() +{ + cgl_filter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + + createTexture = true; +} + +exe.onTriggered = function () +{ + if (!anim) return; + + outTotalFrames.set(anim.totalFrames); + + if (!canvasImage || !canvas) return; + + if (playmodeAuto) + { + updateTexture = true; + } + else + { + if (lastFrame != inFrame.get()) + { + lastFrame = inFrame.get(); + + if (inFrame.get() != -1.0) + { + anim.goToAndStop(inFrame.get(), true); + } + updateTexture = true; + } + } + + if (!textureOut.get() || createTexture) + { + const texOpts = + { + "wrap": cgl_wrap, + "filter": cgl_filter, + "unpackAlpha": false, + }; + if (tex) tex.delete(); + tex = new CGL.Texture.createFromImage(cgl, canvasImage, texOpts); + textureOut.set(tex); + createTexture = false; + } + if (updateTexture && tex) + { + tex.initTexture(canvasImage); + updateTexture = false; + } +}; + +op.onDelete = function () +{ + if (anim)anim.stop(); + anim = null; +}; + +function reloadForce() +{ + createTexture = true; + reload(true); +} + +function updateUi() +{ + playmodeAuto = inPlayMode.get() === "Auto"; + + inPlay.setUiAttribs({ "greyout": !playmodeAuto }); + inRewind.setUiAttribs({ "greyout": !playmodeAuto }); + speed.setUiAttribs({ "greyout": !playmodeAuto }); + inFrame.setUiAttribs({ "greyout": playmodeAuto }); + + if (anim) + { + if (playmodeAuto) if (inPlay.get()) anim.play(); + if (!playmodeAuto) anim.stop(); + } +} + +function reload(force) +{ + if (!inData.get() || Object.keys(inData.get()).length === 0) + { + if (anim)anim.stop(); + + anim = null; + if (tex)tex.delete(); + tex = null; + + createTexture = true; + canvasImage = null; + lastFrame = -2; + updateTexture = true; + + return; + } + + updateUi(); + + outTotalFrames.set(0); + if (anim) anim.stop(); + + if (!canvasImage || force) + { + if (tex)tex = tex.delete(); + if (canvas) canvas.remove(); + canvas = document.createElement("canvas"); + canvas.id = canvasId; + + canvas.width = width.get(); + canvas.height = height.get(); + canvas.style.display = "none"; + + const body = document.getElementsByTagName("body")[0]; + body.appendChild(canvas); + + canvasImage = document.getElementById(canvas.id); + ctx = canvasImage.getContext("2d"); + } + + const animData = { + "animType": "canvas", + "loop": false, + "prerender": true, + "autoplay": playmodeAuto && inPlay.get(), + "animationData": inData.get(), + "rendererSettings": + { + "context": ctx, + "clearCanvas": true, + "scaleMode": bmScale.get() + } + }; + + anim = bodymovin.loadAnimation(animData); + anim.setSpeed(speed.get()); + + anim.addEventListener("DOMLoaded", function (e) // sometimes anim loading seems to be async ? + { + finishedLoading(); + }); + + finishedLoading(); +} +function finishedLoading() +{ + if (!playmodeAuto) + { + anim.goToAndPlay(0, true); + lastFrame = -2; + } + if (playmodeAuto && inPlay.get()) anim.play(); +} + + +}; + +Ops.Libs.Lottie.LottieTexturePlayer_v2.prototype = new CABLES.Op(); +CABLES.OPS["2fc89999-18bc-4d68-a81e-79d2280051c0"]={f:Ops.Libs.Lottie.LottieTexturePlayer_v2,objName:"Ops.Libs.Lottie.LottieTexturePlayer_v2"}; + + + + +// ************************************************************** +// +// Ops.Libs.Mediapipe.FaceMesh +// +// ************************************************************** + +Ops.Libs.Mediapipe.FaceMesh = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inGeom = op.inObject("Geom"), + inPoints = op.inArray("Points"), + outGeom = op.outObject("result geom"); + +inPoints.onChange = () => +{ + const points = inPoints.get(); + + if (points && g) + { + for (let i = 0; i < g.vertices.length; i += 3) + { + g.vertices[i + 0] = points[i + 0]; + g.vertices[i + 1] = points[i + 1]; + } + + g.calculateNormals(); + outGeom.set(null); + outGeom.set(g); + } +}; + +const verts = [ + 0.000000, -3.406404, 5.979507, + 0.000000, -1.126865, 7.475604, + 0.000000, -2.089024, 6.058267, + -0.463928, 0.955357, 6.633583, + 0.000000, -0.463170, 7.586580, + 0.000000, 0.365669, 7.242870, + 0.000000, 2.473255, 5.788627, + -4.253081, 2.577646, 3.279702, + 0.000000, 4.019042, 5.284764, + 0.000000, 4.885979, 5.385258, + 0.000000, 8.261778, 4.481535, + 0.000000, -3.706811, 5.864924, + 0.000000, -3.918301, 5.569430, + 0.000000, -3.994436, 5.219482, + 0.000000, -4.542400, 5.404754, + 0.000000, -4.745577, 5.529457, + 0.000000, -5.019567, 5.601448, + 0.000000, -5.365123, 5.535441, + 0.000000, -6.149624, 5.071372, + 0.000000, -1.501095, 7.112196, + -0.416106, -1.466449, 6.447657, + -7.087960, 5.434801, 0.099620, + -2.628639, 2.035898, 3.848121, + -3.198363, 1.985815, 3.796952, + -3.775151, 2.039402, 3.646194, + -4.465819, 2.422950, 3.155168, + -2.164289, 2.189867, 3.851822, + -3.208229, 3.223926, 4.115822, + -2.673803, 3.205337, 4.092203, + -3.745193, 3.165286, 3.972409, + -4.161018, 3.059069, 3.719554, + -5.062006, 1.934418, 2.776093, + -2.266659, -7.425768, 4.389812, + -4.445859, 2.663991, 3.173422, + -7.214530, 2.263009, 0.073150, + -5.799793, 2.349546, 2.204059, + -2.844939, -0.720868, 4.433130, + -0.711452, -3.329355, 5.877044, + -0.606033, -3.924562, 5.444923, + -1.431615, -3.500953, 5.496189, + -1.914910, -3.803146, 5.028930, + -1.131043, -3.973937, 5.189648, + -1.563548, -4.082763, 4.842263, + -2.650112, -5.003649, 4.188483, + -0.427049, -1.094134, 7.360529, + -0.496396, -0.475659, 7.440358, + -5.253307, 3.881582, 3.363159, + -1.718698, 0.974609, 4.558359, + -1.608635, -0.942516, 5.814193, + -1.651267, -0.610868, 5.581319, + -4.765501, -0.701554, 3.534632, + -0.478306, 0.295766, 7.101013, + -3.734964, 4.508230, 4.550454, + -4.588603, 4.302037, 4.048484, + -6.279331, 6.615427, 1.425850, + -1.220941, 4.142165, 5.106035, + -2.193489, 3.100317, 4.000575, + -3.102642, -4.352984, 4.095905, + -6.719682, -4.788645, -1.745401, + -1.193824, -1.306795, 5.737747, + -0.729766, -1.593712, 5.833208, + -2.456206, -4.342621, 4.283884, + -2.204823, -4.304508, 4.162499, + -4.985894, 4.802461, 3.751977, + -1.592294, -1.257709, 5.456949, + -2.644548, 4.524654, 4.921559, + -2.760292, 5.100971, 5.015990, + -3.523964, 8.005976, 3.729163, + -5.599763, 5.715470, 2.724259, + -3.063932, 6.566144, 4.529981, + -5.720968, 4.254584, 2.830852, + -6.374393, 4.785590, 1.591691, + -0.672728, -3.688016, 5.737804, + -1.262560, -3.787691, 5.417779, + -1.732553, -3.952767, 5.000579, + -1.043625, -1.464973, 5.662455, + -2.321234, -4.329069, 4.258156, + -2.056846, -4.477671, 4.520883, + -2.153084, -4.276322, 4.038093, + -0.946874, -1.035249, 6.512274, + -1.469132, -4.036351, 4.604908, + -1.024340, -3.989851, 4.926693, + -0.533422, -3.993222, 5.138202, + -0.769720, -6.095394, 4.985883, + -0.699606, -5.291850, 5.448304, + -0.669687, -4.949770, 5.509612, + -0.630947, -4.695101, 5.449371, + -0.583218, -4.517982, 5.339869, + -1.537170, -4.423206, 4.745470, + -1.615600, -4.475942, 4.813632, + -1.729053, -4.618680, 4.854463, + -1.838624, -4.828746, 4.823737, + -2.368250, -3.106237, 4.868096, + -7.542244, -1.049282, -2.431321, + 0.000000, -1.724003, 6.601390, + -1.826614, -4.399531, 4.399021, + -1.929558, -4.411831, 4.497052, + -0.597442, -2.013686, 5.866456, + -1.405627, -1.714196, 5.241087, + -0.662449, -1.819321, 5.863759, + -2.342340, 0.572222, 4.294303, + -3.327324, 0.104863, 4.113860, + -1.726175, -0.919165, 5.273355, + -5.133204, 7.485602, 2.660442, + -4.538641, 6.319907, 3.683424, + -3.986562, 5.109487, 4.466315, + -2.169681, -5.440433, 4.455874, + -1.395634, 5.011963, 5.316032, + -1.619500, 6.599217, 4.921106, + -1.891399, 8.236377, 4.274997, + -4.195832, 2.235205, 3.375099, + -5.733342, 1.411738, 2.431726, + -1.859887, 2.355757, 3.843181, + -4.988612, 3.074654, 3.083858, + -1.303263, 1.416453, 4.831091, + -1.305757, -0.672779, 6.415959, + -6.465170, 0.937119, 1.689873, + -5.258659, 0.945811, 2.974312, + -4.432338, 0.722096, 3.522615, + -3.300681, 0.861641, 3.872784, + -2.430178, 1.131492, 4.039035, + -1.820731, 1.467954, 4.224124, + -0.563221, 2.307693, 5.566789, + -6.338145, -0.529279, 1.881175, + -5.587698, 3.208071, 2.687839, + -0.242624, -1.462857, 7.071491, + -1.611251, 0.339326, 4.895421, + -7.743095, 2.364999, -2.005167, + -1.391142, 1.851048, 4.448999, + -1.785794, -0.978284, 4.850470, + -4.670959, 2.664461, 3.084075, + -1.333970, -0.283761, 6.097047, + -7.270895, -2.890917, -2.252455, + -1.856432, 2.585245, 3.757904, + -0.923388, 0.073076, 6.671944, + -5.000589, -6.135128, 1.892523, + -5.085276, -7.178590, 0.714711, + -7.159291, -0.811820, -0.072044, + -5.843051, -5.248023, 0.924091, + -6.847258, 3.662916, 0.724695, + -2.412942, -8.258853, 4.119213, + -0.179909, -1.689864, 6.573301, + -2.103655, -0.163946, 4.566119, + -6.407571, 2.236021, 1.560843, + -3.670075, 2.360153, 3.635230, + -3.177186, 2.294265, 3.775704, + -2.196121, -4.598322, 4.479786, + -6.234883, -1.944430, 1.663542, + -1.292924, -9.295920, 4.094063, + -3.210651, -8.533278, 2.802001, + -4.068926, -7.993109, 1.925119, + 0.000000, 6.545390, 5.027311, + 0.000000, -9.403378, 4.264492, + -2.724032, 2.315802, 3.777151, + -2.288460, 2.398891, 3.697603, + -1.998311, 2.496547, 3.689148, + -6.130040, 3.399261, 2.038516, + -2.288460, 2.886504, 3.775031, + -2.724032, 2.961810, 3.871767, + -3.177186, 2.964136, 3.876973, + -3.670075, 2.927714, 3.724325, + -4.018389, 2.857357, 3.482983, + -7.555811, 4.106811, -0.991917, + -4.018389, 2.483695, 3.440898, + 0.000000, -2.521945, 5.932265, + -1.776217, -2.683946, 5.213116, + -1.222237, -1.182444, 5.952465, + -0.731493, -2.536683, 5.815343, + 0.000000, 3.271027, 5.236015, + -4.135272, -6.996638, 2.671970, + -3.311811, -7.660815, 3.382963, + -1.313701, -8.639995, 4.702456, + -5.940524, -6.223629, -0.631468, + -1.998311, 2.743838, 3.744030, + -0.901447, 1.236992, 5.754256, + 0.000000, -8.765243, 4.891441, + -2.308977, -8.974196, 3.609070, + -6.954154, -2.439843, -0.131163, + -1.098819, -4.458788, 5.120727, + -1.181124, -4.579996, 5.189564, + -1.255818, -4.787901, 5.237051, + -1.325085, -5.106507, 5.205010, + -1.546388, -5.819392, 4.757893, + -1.953754, -4.183892, 4.431713, + -2.117802, -4.137093, 4.555096, + -2.285339, -4.051196, 4.582438, + -2.850160, -3.665720, 4.484994, + -5.278538, -2.238942, 2.861224, + -0.946709, 1.907628, 5.196779, + -1.314173, 3.104912, 4.231404, + -1.780000, 2.860000, 3.881555, + -1.845110, -4.098880, 4.247264, + -5.436187, -4.030482, 2.109852, + -0.766444, 3.182131, 4.861453, + -1.938616, -6.614410, 4.521085, + 0.000000, 1.059413, 6.774605, + -0.516573, 1.583572, 6.148363, + 0.000000, 1.728369, 6.316750, + -1.246815, 0.230297, 5.681036, + 0.000000, -7.942194, 5.181173, + 0.000000, -6.991499, 5.153478, + -0.997827, -6.930921, 4.979576, + -3.288807, -5.382514, 3.795752, + -2.311631, -1.566237, 4.590085, + -2.680250, -6.111567, 4.096152, + -3.832928, -1.537326, 4.137731, + -2.961860, -2.274215, 4.440943, + -4.386901, -2.683286, 3.643886, + -1.217295, -7.834465, 4.969286, + -1.542374, -0.136843, 5.201008, + -3.878377, -6.041764, 3.311079, + -3.084037, -6.809842, 3.814195, + -3.747321, -4.503545, 3.726453, + -6.094129, -3.205991, 1.473482, + -4.588995, -4.728726, 2.983221, + -6.583231, -3.941269, 0.070268, + -3.492580, -3.195820, 4.130198, + -1.255543, 0.802341, 5.307551, + -1.126122, -0.933602, 6.538785, + -1.443109, -1.142774, 5.905127, + -0.923043, -0.529042, 7.003423, + -1.755386, 3.529117, 4.327696, + -2.632589, 3.713828, 4.364629, + -3.388062, 3.721976, 4.309028, + -4.075766, 3.675413, 4.076063, + -4.622910, 3.474691, 3.646321, + -5.171755, 2.535753, 2.670867, + -7.297331, 0.763172, -0.048769, + -4.706828, 1.651000, 3.109532, + -4.071712, 1.476821, 3.476944, + -3.269817, 1.470659, 3.731945, + -2.527572, 1.617311, 3.865444, + -1.970894, 1.858505, 3.961782, + -1.579543, 2.097941, 4.084996, + -7.664182, 0.673132, -2.435867, + -1.397041, -1.340139, 5.630378, + -0.884838, 0.658740, 6.233232, + -0.767097, -0.968035, 7.077932, + -0.460213, -1.334106, 6.787447, + -0.748618, -1.067994, 6.798303, + -1.236408, -1.585568, 5.480490, + -0.387306, -1.409990, 6.957705, + -0.319925, -1.607931, 6.508676, + -1.639633, 2.556298, 3.863736, + -1.255645, 2.467144, 4.203800, + -1.031362, 2.382663, 4.615849, + -4.253081, 2.772296, 3.315305, + -4.530000, 2.910000, 3.339685, + 0.463928, 0.955357, 6.633583, + 4.253081, 2.577646, 3.279702, + 0.416106, -1.466449, 6.447657, + 7.087960, 5.434801, 0.099620, + 2.628639, 2.035898, 3.848121, + 3.198363, 1.985815, 3.796952, + 3.775151, 2.039402, 3.646194, + 4.465819, 2.422950, 3.155168, + 2.164289, 2.189867, 3.851822, + 3.208229, 3.223926, 4.115822, + 2.673803, 3.205337, 4.092203, + 3.745193, 3.165286, 3.972409, + 4.161018, 3.059069, 3.719554, + 5.062006, 1.934418, 2.776093, + 2.266659, -7.425768, 4.389812, + 4.445859, 2.663991, 3.173422, + 7.214530, 2.263009, 0.073150, + 5.799793, 2.349546, 2.204059, + 2.844939, -0.720868, 4.433130, + 0.711452, -3.329355, 5.877044, + 0.606033, -3.924562, 5.444923, + 1.431615, -3.500953, 5.496189, + 1.914910, -3.803146, 5.028930, + 1.131043, -3.973937, 5.189648, + 1.563548, -4.082763, 4.842263, + 2.650112, -5.003649, 4.188483, + 0.427049, -1.094134, 7.360529, + 0.496396, -0.475659, 7.440358, + 5.253307, 3.881582, 3.363159, + 1.718698, 0.974609, 4.558359, + 1.608635, -0.942516, 5.814193, + 1.651267, -0.610868, 5.581319, + 4.765501, -0.701554, 3.534632, + 0.478306, 0.295766, 7.101013, + 3.734964, 4.508230, 4.550454, + 4.588603, 4.302037, 4.048484, + 6.279331, 6.615427, 1.425850, + 1.220941, 4.142165, 5.106035, + 2.193489, 3.100317, 4.000575, + 3.102642, -4.352984, 4.095905, + 6.719682, -4.788645, -1.745401, + 1.193824, -1.306795, 5.737747, + 0.729766, -1.593712, 5.833208, + 2.456206, -4.342621, 4.283884, + 2.204823, -4.304508, 4.162499, + 4.985894, 4.802461, 3.751977, + 1.592294, -1.257709, 5.456949, + 2.644548, 4.524654, 4.921559, + 2.760292, 5.100971, 5.015990, + 3.523964, 8.005976, 3.729163, + 5.599763, 5.715470, 2.724259, + 3.063932, 6.566144, 4.529981, + 5.720968, 4.254584, 2.830852, + 6.374393, 4.785590, 1.591691, + 0.672728, -3.688016, 5.737804, + 1.262560, -3.787691, 5.417779, + 1.732553, -3.952767, 5.000579, + 1.043625, -1.464973, 5.662455, + 2.321234, -4.329069, 4.258156, + 2.056846, -4.477671, 4.520883, + 2.153084, -4.276322, 4.038093, + 0.946874, -1.035249, 6.512274, + 1.469132, -4.036351, 4.604908, + 1.024340, -3.989851, 4.926693, + 0.533422, -3.993222, 5.138202, + 0.769720, -6.095394, 4.985883, + 0.699606, -5.291850, 5.448304, + 0.669687, -4.949770, 5.509612, + 0.630947, -4.695101, 5.449371, + 0.583218, -4.517982, 5.339869, + 1.537170, -4.423206, 4.745470, + 1.615600, -4.475942, 4.813632, + 1.729053, -4.618680, 4.854463, + 1.838624, -4.828746, 4.823737, + 2.368250, -3.106237, 4.868096, + 7.542244, -1.049282, -2.431321, + 1.826614, -4.399531, 4.399021, + 1.929558, -4.411831, 4.497052, + 0.597442, -2.013686, 5.866456, + 1.405627, -1.714196, 5.241087, + 0.662449, -1.819321, 5.863759, + 2.342340, 0.572222, 4.294303, + 3.327324, 0.104863, 4.113860, + 1.726175, -0.919165, 5.273355, + 5.133204, 7.485602, 2.660442, + 4.538641, 6.319907, 3.683424, + 3.986562, 5.109487, 4.466315, + 2.169681, -5.440433, 4.455874, + 1.395634, 5.011963, 5.316032, + 1.619500, 6.599217, 4.921106, + 1.891399, 8.236377, 4.274997, + 4.195832, 2.235205, 3.375099, + 5.733342, 1.411738, 2.431726, + 1.859887, 2.355757, 3.843181, + 4.988612, 3.074654, 3.083858, + 1.303263, 1.416453, 4.831091, + 1.305757, -0.672779, 6.415959, + 6.465170, 0.937119, 1.689873, + 5.258659, 0.945811, 2.974312, + 4.432338, 0.722096, 3.522615, + 3.300681, 0.861641, 3.872784, + 2.430178, 1.131492, 4.039035, + 1.820731, 1.467954, 4.224124, + 0.563221, 2.307693, 5.566789, + 6.338145, -0.529279, 1.881175, + 5.587698, 3.208071, 2.687839, + 0.242624, -1.462857, 7.071491, + 1.611251, 0.339326, 4.895421, + 7.743095, 2.364999, -2.005167, + 1.391142, 1.851048, 4.448999, + 1.785794, -0.978284, 4.850470, + 4.670959, 2.664461, 3.084075, + 1.333970, -0.283761, 6.097047, + 7.270895, -2.890917, -2.252455, + 1.856432, 2.585245, 3.757904, + 0.923388, 0.073076, 6.671944, + 5.000589, -6.135128, 1.892523, + 5.085276, -7.178590, 0.714711, + 7.159291, -0.811820, -0.072044, + 5.843051, -5.248023, 0.924091, + 6.847258, 3.662916, 0.724695, + 2.412942, -8.258853, 4.119213, + 0.179909, -1.689864, 6.573301, + 2.103655, -0.163946, 4.566119, + 6.407571, 2.236021, 1.560843, + 3.670075, 2.360153, 3.635230, + 3.177186, 2.294265, 3.775704, + 2.196121, -4.598322, 4.479786, + 6.234883, -1.944430, 1.663542, + 1.292924, -9.295920, 4.094063, + 3.210651, -8.533278, 2.802001, + 4.068926, -7.993109, 1.925119, + 2.724032, 2.315802, 3.777151, + 2.288460, 2.398891, 3.697603, + 1.998311, 2.496547, 3.689148, + 6.130040, 3.399261, 2.038516, + 2.288460, 2.886504, 3.775031, + 2.724032, 2.961810, 3.871767, + 3.177186, 2.964136, 3.876973, + 3.670075, 2.927714, 3.724325, + 4.018389, 2.857357, 3.482983, + 7.555811, 4.106811, -0.991917, + 4.018389, 2.483695, 3.440898, + 1.776217, -2.683946, 5.213116, + 1.222237, -1.182444, 5.952465, + 0.731493, -2.536683, 5.815343, + 4.135272, -6.996638, 2.671970, + 3.311811, -7.660815, 3.382963, + 1.313701, -8.639995, 4.702456, + 5.940524, -6.223629, -0.631468, + 1.998311, 2.743838, 3.744030, + 0.901447, 1.236992, 5.754256, + 2.308977, -8.974196, 3.609070, + 6.954154, -2.439843, -0.131163, + 1.098819, -4.458788, 5.120727, + 1.181124, -4.579996, 5.189564, + 1.255818, -4.787901, 5.237051, + 1.325085, -5.106507, 5.205010, + 1.546388, -5.819392, 4.757893, + 1.953754, -4.183892, 4.431713, + 2.117802, -4.137093, 4.555096, + 2.285339, -4.051196, 4.582438, + 2.850160, -3.665720, 4.484994, + 5.278538, -2.238942, 2.861224, + 0.946709, 1.907628, 5.196779, + 1.314173, 3.104912, 4.231404, + 1.780000, 2.860000, 3.881555, + 1.845110, -4.098880, 4.247264, + 5.436187, -4.030482, 2.109852, + 0.766444, 3.182131, 4.861453, + 1.938616, -6.614410, 4.521085, + 0.516573, 1.583572, 6.148363, + 1.246815, 0.230297, 5.681036, + 0.997827, -6.930921, 4.979576, + 3.288807, -5.382514, 3.795752, + 2.311631, -1.566237, 4.590085, + 2.680250, -6.111567, 4.096152, + 3.832928, -1.537326, 4.137731, + 2.961860, -2.274215, 4.440943, + 4.386901, -2.683286, 3.643886, + 1.217295, -7.834465, 4.969286, + 1.542374, -0.136843, 5.201008, + 3.878377, -6.041764, 3.311079, + 3.084037, -6.809842, 3.814195, + 3.747321, -4.503545, 3.726453, + 6.094129, -3.205991, 1.473482, + 4.588995, -4.728726, 2.983221, + 6.583231, -3.941269, 0.070268, + 3.492580, -3.195820, 4.130198, + 1.255543, 0.802341, 5.307551, + 1.126122, -0.933602, 6.538785, + 1.443109, -1.142774, 5.905127, + 0.923043, -0.529042, 7.003423, + 1.755386, 3.529117, 4.327696, + 2.632589, 3.713828, 4.364629, + 3.388062, 3.721976, 4.309028, + 4.075766, 3.675413, 4.076063, + 4.622910, 3.474691, 3.646321, + 5.171755, 2.535753, 2.670867, + 7.297331, 0.763172, -0.048769, + 4.706828, 1.651000, 3.109532, + 4.071712, 1.476821, 3.476944, + 3.269817, 1.470659, 3.731945, + 2.527572, 1.617311, 3.865444, + 1.970894, 1.858505, 3.961782, + 1.579543, 2.097941, 4.084996, + 7.664182, 0.673132, -2.435867, + 1.397041, -1.340139, 5.630378, + 0.884838, 0.658740, 6.233232, + 0.767097, -0.968035, 7.077932, + 0.460213, -1.334106, 6.787447, + 0.748618, -1.067994, 6.798303, + 1.236408, -1.585568, 5.480490, + 0.387306, -1.409990, 6.957705, + 0.319925, -1.607931, 6.508676, + 1.639633, 2.556298, 3.863736, + 1.255645, 2.467144, 4.203800, + 1.031362, 2.382663, 4.615849, + 4.253081, 2.772296, 3.315305, + 4.530000, 2.910000, 3.339685]; +const faces = [ + 174, 156, 134, + 247, 34, 8, + 383, 399, 363, + 264, 467, 250, + 309, 416, 325, + 79, 96, 192, + 357, 390, 265, + 128, 35, 163, + 369, 265, 390, + 140, 163, 35, + 268, 1, 303, + 38, 73, 1, + 12, 303, 1, + 12, 1, 73, + 350, 452, 351, + 121, 122, 232, + 453, 351, 452, + 233, 232, 122, + 268, 303, 270, + 38, 40, 73, + 304, 270, 303, + 74, 73, 40, + 358, 344, 351, + 129, 122, 115, + 278, 351, 344, + 48, 115, 122, + 351, 453, 358, + 122, 129, 233, + 454, 358, 453, + 234, 233, 129, + 300, 334, 298, + 70, 68, 105, + 333, 298, 334, + 104, 105, 68, + 176, 153, 397, + 176, 172, 153, + 378, 397, 153, + 149, 153, 172, + 382, 385, 383, + 155, 156, 158, + 399, 383, 385, + 174, 158, 156, + 281, 348, 331, + 51, 102, 119, + 349, 331, 348, + 120, 119, 102, + 270, 304, 271, + 40, 41, 74, + 305, 271, 304, + 75, 74, 41, + 10, 337, 152, + 10, 152, 108, + 338, 152, 337, + 109, 108, 152, + 345, 279, 361, + 116, 132, 49, + 280, 361, 279, + 50, 49, 132, + 263, 432, 419, + 33, 195, 212, + 425, 419, 432, + 205, 212, 195, + 305, 409, 271, + 75, 41, 185, + 410, 271, 409, + 186, 185, 41, + 273, 311, 408, + 43, 184, 81, + 416, 408, 311, + 192, 81, 184, + 323, 271, 411, + 93, 187, 41, + 410, 411, 271, + 186, 41, 187, + 348, 450, 349, + 119, 120, 230, + 451, 349, 450, + 231, 230, 120, + 435, 433, 431, + 215, 211, 213, + 423, 431, 433, + 203, 213, 211, + 314, 315, 19, + 84, 19, 85, + 18, 19, 315, + 18, 85, 19, + 308, 376, 307, + 78, 77, 147, + 292, 307, 376, + 62, 147, 77, + 260, 388, 261, + 30, 31, 161, + 389, 261, 388, + 162, 161, 31, + 287, 415, 385, + 57, 158, 191, + 399, 385, 415, + 174, 191, 158, + 419, 425, 407, + 195, 183, 205, + 336, 407, 425, + 107, 205, 183, + 368, 417, 365, + 139, 136, 193, + 435, 365, 417, + 215, 193, 136, + 392, 424, 328, + 166, 99, 204, + 359, 328, 424, + 130, 204, 99, + 299, 302, 285, + 69, 55, 72, + 252, 285, 302, + 22, 72, 55, + 5, 276, 6, + 5, 6, 46, + 282, 6, 276, + 52, 46, 6, + 255, 374, 254, + 25, 24, 145, + 375, 254, 374, + 146, 145, 24, + 321, 322, 308, + 91, 78, 92, + 376, 308, 322, + 147, 92, 78, + 281, 426, 412, + 51, 188, 206, + 428, 412, 426, + 208, 206, 188, + 422, 314, 201, + 202, 201, 84, + 19, 201, 314, + 19, 84, 201, + 336, 322, 407, + 107, 183, 92, + 406, 407, 322, + 182, 92, 183, + 406, 322, 405, + 182, 181, 92, + 321, 405, 322, + 91, 92, 181, + 18, 315, 17, + 18, 17, 85, + 316, 17, 315, + 86, 85, 17, + 426, 267, 427, + 206, 207, 37, + 424, 427, 267, + 204, 37, 207, + 370, 397, 401, + 141, 177, 172, + 378, 401, 397, + 149, 172, 177, + 392, 270, 323, + 166, 93, 40, + 271, 323, 270, + 41, 40, 93, + 418, 466, 414, + 194, 190, 246, + 465, 414, 466, + 245, 246, 190, + 258, 259, 387, + 28, 160, 29, + 386, 387, 259, + 159, 29, 160, + 261, 389, 468, + 31, 248, 162, + 467, 468, 389, + 247, 162, 248, + 249, 457, 420, + 4, 197, 237, + 400, 420, 457, + 175, 237, 197, + 334, 299, 333, + 105, 104, 69, + 285, 333, 299, + 55, 69, 104, + 286, 9, 418, + 56, 194, 9, + 169, 418, 9, + 169, 9, 194, + 341, 262, 347, + 112, 118, 32, + 449, 347, 262, + 229, 32, 118, + 286, 418, 442, + 56, 222, 194, + 414, 442, 418, + 190, 194, 222, + 328, 461, 327, + 99, 98, 241, + 329, 327, 461, + 100, 241, 98, + 278, 356, 330, + 48, 101, 127, + 372, 330, 356, + 143, 127, 101, + 310, 393, 439, + 80, 219, 167, + 440, 439, 393, + 220, 167, 219, + 382, 383, 257, + 155, 27, 156, + 342, 257, 383, + 113, 156, 27, + 361, 280, 421, + 132, 199, 50, + 430, 421, 280, + 210, 50, 199, + 366, 365, 380, + 137, 151, 136, + 395, 380, 365, + 170, 136, 151, + 356, 278, 438, + 127, 218, 48, + 344, 438, 278, + 115, 48, 218, + 444, 445, 283, + 224, 53, 225, + 284, 283, 445, + 54, 225, 53, + 282, 276, 364, + 52, 135, 46, + 441, 364, 276, + 221, 46, 135, + 432, 263, 396, + 212, 171, 33, + 370, 396, 263, + 141, 33, 171, + 338, 300, 339, + 109, 110, 70, + 298, 339, 300, + 68, 70, 110, + 336, 274, 322, + 107, 92, 44, + 376, 322, 274, + 147, 44, 92, + 349, 451, 350, + 120, 121, 231, + 452, 350, 451, + 232, 231, 121, + 468, 360, 343, + 248, 114, 131, + 447, 343, 360, + 227, 131, 114, + 283, 284, 335, + 53, 106, 54, + 294, 335, 284, + 64, 54, 106, + 251, 459, 463, + 21, 243, 239, + 462, 463, 459, + 242, 239, 243, + 277, 354, 301, + 47, 71, 125, + 384, 301, 354, + 157, 125, 71, + 326, 293, 325, + 97, 96, 63, + 309, 325, 293, + 79, 63, 96, + 284, 277, 294, + 54, 64, 47, + 301, 294, 277, + 71, 47, 64, + 448, 265, 346, + 228, 117, 35, + 373, 346, 265, + 144, 35, 117, + 353, 346, 347, + 124, 118, 117, + 341, 347, 346, + 112, 117, 118, + 2, 20, 275, + 2, 45, 20, + 355, 275, 20, + 126, 20, 45, + 249, 282, 457, + 4, 237, 52, + 364, 457, 282, + 135, 52, 237, + 426, 427, 428, + 206, 208, 207, + 437, 428, 427, + 217, 207, 208, + 381, 382, 253, + 154, 23, 155, + 257, 253, 382, + 27, 155, 23, + 392, 394, 270, + 166, 40, 168, + 268, 270, 394, + 38, 168, 40, + 200, 429, 201, + 200, 201, 209, + 422, 201, 429, + 202, 209, 201, + 331, 330, 267, + 102, 37, 101, + 372, 267, 330, + 143, 101, 37, + 423, 433, 274, + 203, 44, 213, + 288, 274, 433, + 58, 213, 44, + 291, 251, 329, + 61, 100, 21, + 463, 329, 251, + 243, 21, 100, + 259, 287, 386, + 29, 159, 57, + 385, 386, 287, + 158, 57, 159, + 343, 447, 354, + 114, 125, 227, + 266, 354, 447, + 36, 227, 125, + 258, 387, 260, + 28, 30, 160, + 388, 260, 387, + 161, 160, 30, + 431, 423, 432, + 211, 212, 203, + 425, 432, 423, + 205, 203, 212, + 446, 343, 277, + 226, 47, 114, + 354, 277, 343, + 125, 114, 47, + 425, 423, 336, + 205, 107, 203, + 274, 336, 423, + 44, 203, 107, + 307, 293, 308, + 77, 78, 63, + 326, 308, 293, + 97, 63, 78, + 367, 448, 353, + 138, 124, 228, + 346, 353, 448, + 117, 228, 124, + 303, 269, 304, + 73, 74, 39, + 272, 304, 269, + 42, 39, 74, + 372, 359, 267, + 143, 37, 130, + 424, 267, 359, + 204, 130, 37, + 328, 295, 461, + 99, 241, 65, + 456, 461, 295, + 236, 65, 241, + 295, 332, 279, + 65, 49, 103, + 280, 279, 332, + 50, 103, 49, + 304, 272, 305, + 74, 75, 42, + 273, 305, 272, + 43, 42, 75, + 428, 437, 435, + 208, 215, 217, + 433, 435, 437, + 213, 217, 215, + 305, 273, 409, + 75, 185, 43, + 408, 409, 273, + 184, 43, 185, + 395, 431, 396, + 170, 171, 211, + 432, 396, 431, + 212, 211, 171, + 396, 370, 379, + 171, 150, 141, + 401, 379, 370, + 177, 141, 150, + 297, 335, 300, + 67, 70, 106, + 334, 300, 335, + 105, 106, 70, + 418, 169, 352, + 194, 123, 169, + 7, 352, 169, + 7, 169, 123, + 281, 412, 353, + 51, 124, 188, + 377, 353, 412, + 148, 188, 124, + 320, 321, 326, + 90, 97, 91, + 308, 326, 321, + 78, 91, 97, + 286, 296, 337, + 56, 108, 66, + 297, 337, 296, + 67, 66, 108, + 405, 321, 404, + 181, 180, 91, + 320, 404, 321, + 90, 91, 180, + 331, 349, 330, + 102, 101, 120, + 350, 330, 349, + 121, 120, 101, + 335, 294, 334, + 106, 105, 64, + 299, 334, 294, + 69, 64, 105, + 324, 455, 367, + 94, 138, 235, + 448, 367, 455, + 228, 235, 138, + 17, 316, 16, + 17, 16, 86, + 317, 16, 316, + 87, 86, 16, + 430, 280, 359, + 210, 130, 50, + 332, 359, 280, + 103, 50, 130, + 16, 317, 15, + 16, 15, 87, + 318, 15, 317, + 88, 87, 15, + 9, 286, 10, + 9, 10, 56, + 337, 10, 286, + 108, 56, 10, + 330, 350, 278, + 101, 48, 121, + 351, 278, 350, + 122, 121, 48, + 253, 254, 381, + 23, 154, 24, + 375, 381, 254, + 146, 24, 154, + 403, 404, 319, + 179, 89, 180, + 320, 319, 404, + 90, 180, 89, + 352, 7, 420, + 123, 197, 7, + 198, 420, 7, + 198, 7, 197, + 325, 319, 326, + 96, 97, 89, + 320, 326, 319, + 90, 89, 97, + 398, 368, 366, + 173, 137, 139, + 365, 366, 368, + 136, 139, 137, + 289, 436, 398, + 59, 173, 216, + 368, 398, 436, + 139, 216, 173, + 439, 440, 345, + 219, 116, 220, + 279, 345, 440, + 49, 220, 116, + 272, 312, 273, + 42, 43, 82, + 311, 273, 312, + 81, 82, 43, + 6, 282, 196, + 6, 196, 52, + 249, 196, 282, + 4, 52, 196, + 274, 288, 376, + 44, 147, 58, + 292, 376, 288, + 62, 58, 147, + 397, 429, 176, + 172, 176, 209, + 200, 176, 429, + 200, 209, 176, + 269, 313, 272, + 39, 42, 83, + 312, 272, 313, + 82, 83, 42, + 445, 446, 284, + 225, 54, 226, + 277, 284, 446, + 47, 226, 54, + 255, 340, 374, + 25, 145, 111, + 391, 374, 340, + 164, 111, 145, + 296, 283, 297, + 66, 67, 53, + 335, 297, 283, + 106, 53, 67, + 347, 449, 348, + 118, 119, 229, + 450, 348, 449, + 230, 229, 119, + 455, 357, 448, + 235, 228, 128, + 265, 448, 357, + 35, 128, 228, + 337, 297, 338, + 108, 109, 67, + 300, 338, 297, + 70, 67, 109, + 152, 338, 11, + 152, 11, 109, + 339, 11, 338, + 110, 109, 11, + 279, 440, 295, + 49, 65, 220, + 456, 295, 440, + 236, 220, 65, + 408, 416, 293, + 184, 63, 192, + 309, 293, 416, + 79, 192, 63, + 359, 372, 430, + 130, 210, 143, + 356, 430, 372, + 127, 143, 210, + 346, 373, 341, + 117, 112, 144, + 266, 341, 373, + 36, 144, 112, + 389, 391, 467, + 162, 247, 164, + 250, 467, 391, + 8, 164, 247, + 353, 347, 281, + 124, 51, 118, + 348, 281, 347, + 119, 118, 51, + 296, 443, 283, + 66, 53, 223, + 444, 283, 443, + 224, 223, 53, + 20, 95, 355, + 20, 126, 95, + 371, 355, 95, + 142, 95, 126, + 296, 286, 443, + 66, 223, 56, + 442, 443, 286, + 222, 56, 223, + 420, 198, 249, + 197, 4, 198, + 196, 249, 198, + 196, 198, 4, + 360, 264, 256, + 131, 26, 34, + 250, 256, 264, + 8, 34, 26, + 276, 275, 441, + 46, 221, 45, + 458, 441, 275, + 238, 45, 221, + 301, 384, 302, + 71, 72, 157, + 369, 302, 384, + 140, 157, 72, + 418, 352, 466, + 194, 246, 123, + 413, 466, 352, + 189, 123, 246, + 467, 264, 468, + 247, 248, 34, + 360, 468, 264, + 131, 34, 248, + 390, 252, 369, + 163, 140, 22, + 302, 369, 252, + 72, 22, 140, + 375, 387, 381, + 146, 154, 160, + 386, 381, 387, + 159, 160, 154, + 380, 395, 379, + 151, 150, 170, + 396, 379, 395, + 171, 170, 150, + 352, 420, 413, + 123, 189, 197, + 400, 413, 420, + 175, 197, 189, + 427, 323, 437, + 207, 217, 93, + 411, 437, 323, + 187, 93, 217, + 388, 374, 389, + 161, 162, 145, + 391, 389, 374, + 164, 145, 162, + 394, 327, 165, + 168, 165, 98, + 3, 165, 327, + 3, 98, 165, + 355, 371, 462, + 126, 242, 142, + 463, 462, 371, + 243, 142, 242, + 1, 268, 165, + 1, 165, 38, + 394, 165, 268, + 168, 38, 165, + 12, 13, 303, + 12, 73, 13, + 269, 303, 13, + 39, 13, 73, + 387, 375, 388, + 160, 161, 146, + 374, 388, 375, + 145, 146, 161, + 13, 14, 269, + 13, 39, 14, + 313, 269, 14, + 83, 14, 39, + 294, 301, 299, + 64, 69, 71, + 302, 299, 301, + 72, 71, 69, + 341, 266, 262, + 112, 32, 36, + 447, 262, 266, + 227, 36, 32, + 381, 386, 382, + 154, 155, 159, + 385, 382, 386, + 158, 159, 155, + 281, 331, 426, + 51, 206, 102, + 267, 426, 331, + 37, 102, 206, + 424, 392, 427, + 204, 207, 166, + 323, 427, 392, + 93, 166, 207, + 430, 356, 421, + 210, 199, 127, + 438, 421, 356, + 218, 127, 199, + 392, 328, 394, + 166, 168, 99, + 327, 394, 328, + 98, 99, 168, + 458, 439, 441, + 238, 221, 219, + 345, 441, 439, + 116, 219, 221, + 383, 363, 342, + 156, 113, 134, + 464, 342, 363, + 244, 134, 113, + 458, 462, 460, + 238, 240, 242, + 459, 460, 462, + 239, 242, 240, + 435, 431, 365, + 215, 136, 211, + 395, 365, 431, + 170, 211, 136, + 415, 464, 399, + 191, 174, 244, + 363, 399, 464, + 134, 244, 174, + 263, 429, 370, + 33, 141, 209, + 397, 370, 429, + 172, 209, 141, + 458, 275, 462, + 238, 242, 45, + 355, 462, 275, + 126, 45, 242, + 317, 404, 318, + 87, 88, 180, + 403, 318, 404, + 179, 180, 88, + 316, 405, 317, + 86, 87, 181, + 404, 317, 405, + 180, 181, 87, + 315, 406, 316, + 85, 86, 182, + 405, 316, 406, + 181, 182, 86, + 314, 407, 315, + 84, 85, 183, + 406, 315, 407, + 182, 183, 85, + 419, 407, 422, + 195, 202, 183, + 314, 422, 407, + 84, 183, 202, + 367, 402, 324, + 138, 94, 178, + 362, 324, 402, + 133, 178, 94, + 409, 408, 307, + 185, 77, 184, + 293, 307, 408, + 63, 184, 77, + 409, 307, 410, + 185, 186, 77, + 292, 410, 307, + 62, 77, 186, + 411, 410, 288, + 187, 58, 186, + 292, 288, 410, + 62, 186, 58, + 437, 411, 433, + 217, 213, 187, + 288, 433, 411, + 58, 187, 213, + 435, 417, 428, + 215, 208, 193, + 412, 428, 417, + 188, 193, 208, + 265, 369, 373, + 35, 144, 140, + 384, 373, 369, + 157, 140, 144, + 458, 460, 439, + 238, 219, 240, + 310, 439, 460, + 80, 240, 219, + 353, 377, 367, + 124, 138, 148, + 402, 367, 377, + 178, 148, 138, + 5, 2, 276, + 5, 46, 2, + 275, 276, 2, + 45, 2, 46, + 429, 263, 422, + 209, 202, 33, + 419, 422, 263, + 195, 33, 202, + 328, 359, 295, + 99, 65, 130, + 332, 295, 359, + 103, 130, 65, + 368, 436, 417, + 139, 193, 216, + 434, 417, 436, + 214, 216, 193, + 456, 440, 290, + 236, 60, 220, + 393, 290, 440, + 167, 220, 60, + 329, 463, 327, + 100, 98, 243, + 371, 327, 463, + 142, 243, 98, + 327, 371, 3, + 98, 3, 142, + 95, 3, 371, + 95, 142, 3, + 461, 456, 306, + 241, 76, 236, + 290, 306, 456, + 60, 236, 76, + 449, 340, 450, + 229, 230, 111, + 255, 450, 340, + 25, 111, 230, + 262, 447, 256, + 32, 26, 227, + 360, 256, 447, + 131, 227, 26, + 450, 255, 451, + 230, 231, 25, + 254, 451, 255, + 24, 25, 231, + 451, 254, 452, + 231, 232, 24, + 253, 452, 254, + 23, 24, 232, + 452, 253, 453, + 232, 233, 23, + 257, 453, 253, + 27, 23, 233, + 257, 342, 453, + 27, 233, 113, + 454, 453, 342, + 234, 113, 233, + 414, 465, 415, + 190, 191, 245, + 464, 415, 465, + 244, 245, 191, + 442, 414, 287, + 222, 57, 190, + 415, 287, 414, + 191, 190, 57, + 442, 287, 443, + 222, 223, 57, + 259, 443, 287, + 29, 57, 223, + 443, 259, 444, + 223, 224, 29, + 258, 444, 259, + 28, 29, 224, + 445, 444, 260, + 225, 30, 224, + 258, 260, 444, + 28, 224, 30, + 260, 261, 445, + 30, 225, 31, + 446, 445, 261, + 226, 31, 225, + 261, 468, 446, + 31, 226, 248, + 343, 446, 468, + 114, 248, 226, + 251, 310, 459, + 21, 239, 80, + 460, 459, 310, + 240, 80, 239, + 291, 306, 393, + 61, 167, 76, + 290, 393, 306, + 60, 76, 167, + 461, 306, 329, + 241, 100, 76, + 291, 329, 306, + 61, 76, 100, + 377, 434, 402, + 148, 178, 214, + 436, 402, 434, + 216, 214, 178, + 251, 291, 310, + 21, 80, 61, + 393, 310, 291, + 167, 61, 80, + 412, 417, 377, + 188, 148, 193, + 434, 377, 417, + 214, 193, 148, + 342, 464, 454, + 113, 234, 244, + 465, 454, 464, + 245, 244, 234, + 454, 465, 358, + 234, 129, 245, + 466, 358, 465, + 246, 245, 129, + 413, 344, 466, + 189, 246, 115, + 358, 466, 344, + 129, 115, 246, + 438, 344, 400, + 218, 175, 115, + 413, 400, 344, + 189, 115, 175, + 364, 441, 361, + 135, 132, 221, + 345, 361, 441, + 116, 221, 132, + 457, 421, 400, + 237, 175, 199, + 438, 400, 421, + 218, 199, 175, + 457, 364, 421, + 237, 199, 135, + 361, 421, 364, + 132, 135, 199, + 362, 402, 289, + 133, 59, 178, + 436, 289, 402, + 216, 178, 59, + 354, 266, 384, + 125, 157, 36, + 373, 384, 266, + 144, 36, 157, + 256, 250, 340, + 26, 111, 8, + 391, 340, 250, + 164, 8, 111, + 262, 256, 449, + 32, 229, 26, + 340, 449, 256, + 111, 26, 229, + 15, 318, 14, + 15, 14, 88, + 313, 14, 318, + 83, 88, 14, + 318, 403, 313, + 88, 83, 179, + 312, 313, 403, + 82, 179, 83, + 403, 319, 312, + 179, 82, 89, + 311, 312, 319, + 81, 89, 82, + 319, 325, 311, + 89, 81, 96, + 416, 311, 325, + 192, 96, 81]; + +let tc = [0.499977, 0.652534, 0.500026, 0.5474870000000001, 0.499974, 0.602372, 0.482113, 0.47197900000000004, 0.500151, 0.527156, 0.49991, 0.49825299999999995, 0.499523, 0.40106200000000003, 0.289712, 0.380764, 0.499955, 0.31239799999999995, 0.499987, 0.269919, 0.500023, 0.10704999999999998, 0.500023, 0.666234, 0.500016, 0.679224, 0.500023, 0.692348, 0.499977, 0.6952780000000001, 0.499977, 0.7059340000000001, 0.499977, 0.7193849999999999, 0.499977, 0.737019, 0.499968, 0.781371, 0.499816, 0.562981, 0.473773, 0.5739099999999999, 0.104907, 0.25414099999999995, 0.36593, 0.40957600000000005, 0.338758, 0.413025, 0.31112, 0.40946000000000005, 0.274658, 0.389131, 0.393362, 0.403706, 0.345234, 0.34401099999999996, 0.370094, 0.34607600000000005, 0.319322, 0.34726500000000005, 0.297903, 0.353591, 0.247792, 0.41081, 0.396889, 0.842755, 0.280098, 0.37560000000000004, 0.10631, 0.399956, 0.209925, 0.39135299999999995, 0.355808, 0.5344059999999999, 0.471751, 0.650404, 0.474155, 0.680192, 0.439785, 0.6572290000000001, 0.414617, 0.666541, 0.450374, 0.6808609999999999, 0.428771, 0.6826909999999999, 0.374971, 0.727805, 0.486717, 0.5476289999999999, 0.485301, 0.5273950000000001, 0.257765, 0.31449000000000005, 0.401223, 0.455172, 0.429819, 0.5486150000000001, 0.421352, 0.533741, 0.276896, 0.532057, 0.48337, 0.499587, 0.337212, 0.282883, 0.296392, 0.29324300000000003, 0.169295, 0.19381400000000004, 0.44758, 0.30261000000000005, 0.39239, 0.353888, 0.35449, 0.6967840000000001, 0.067305, 0.730105, 0.442739, 0.5728260000000001, 0.457098, 0.584792, 0.381974, 0.6947110000000001, 0.392389, 0.694203, 0.277076, 0.27193199999999995, 0.422552, 0.563233, 0.385919, 0.28136399999999995, 0.383103, 0.25583999999999996, 0.331431, 0.11971399999999999, 0.229924, 0.23200299999999996, 0.364501, 0.189114, 0.229622, 0.29954099999999995, 0.173287, 0.278748, 0.472879, 0.6661980000000001, 0.446828, 0.668527, 0.422762, 0.67389, 0.445308, 0.580066, 0.388103, 0.693961, 0.403039, 0.70654, 0.403629, 0.693953, 0.460042, 0.557139, 0.431158, 0.692366, 0.452182, 0.692366, 0.475387, 0.692366, 0.465828, 0.77919, 0.472329, 0.736226, 0.473087, 0.717857, 0.473122, 0.704626, 0.473033, 0.6952780000000001, 0.427942, 0.6952780000000001, 0.426479, 0.70354, 0.423162, 0.711846, 0.418309, 0.720063, 0.390095, 0.639573, 0.013954, 0.5600339999999999, 0.499914, 0.580147, 0.4132, 0.6954, 0.409626, 0.701823, 0.46808, 0.6015349999999999, 0.422729, 0.585985, 0.46308, 0.593784, 0.37212, 0.473414, 0.334562, 0.496073, 0.411671, 0.5469649999999999, 0.242176, 0.14767600000000003, 0.290777, 0.20144600000000001, 0.327338, 0.25652699999999995, 0.39951, 0.748921, 0.441728, 0.261676, 0.429765, 0.18783399999999995, 0.412198, 0.10890100000000003, 0.288955, 0.398952, 0.218937, 0.435411, 0.412782, 0.39897000000000005, 0.257135, 0.35544, 0.427685, 0.43796100000000004, 0.44834, 0.5369360000000001, 0.17856, 0.457554, 0.247308, 0.457194, 0.286267, 0.46767499999999995, 0.332828, 0.460712, 0.368756, 0.447207, 0.398964, 0.432655, 0.47641, 0.405806, 0.189241, 0.5239240000000001, 0.228962, 0.348951, 0.490726, 0.5624009999999999, 0.40467, 0.48513300000000004, 0.019469, 0.40156400000000003, 0.426243, 0.420431, 0.396993, 0.548797, 0.26647, 0.376977, 0.439121, 0.518958, 0.032314, 0.6443570000000001, 0.419054, 0.387155, 0.462783, 0.505747, 0.238979, 0.779745, 0.198221, 0.8319380000000001, 0.10755, 0.540755, 0.18361, 0.7402569999999999, 0.13441, 0.33368299999999995, 0.385764, 0.883154, 0.490967, 0.579378, 0.382385, 0.5085729999999999, 0.174399, 0.397671, 0.318785, 0.396235, 0.343364, 0.400597, 0.3961, 0.710217, 0.187885, 0.588538, 0.430987, 0.944065, 0.318993, 0.898285, 0.266248, 0.8697010000000001, 0.500023, 0.19057599999999997, 0.499977, 0.954453, 0.36617, 0.398822, 0.393207, 0.395537, 0.410373, 0.39108, 0.194993, 0.342102, 0.388665, 0.36228400000000005, 0.365962, 0.35597100000000004, 0.343364, 0.35535700000000003, 0.318785, 0.35834, 0.301415, 0.36315600000000003, 0.058133, 0.319076, 0.301415, 0.38744900000000004, 0.499988, 0.6184339999999999, 0.415838, 0.624196, 0.445682, 0.5660769999999999, 0.465844, 0.620641, 0.499923, 0.35152399999999995, 0.288719, 0.8199460000000001, 0.335279, 0.85282, 0.440512, 0.902419, 0.128294, 0.791941, 0.408772, 0.37389399999999995, 0.455607, 0.451801, 0.499877, 0.90899, 0.375437, 0.924192, 0.11421, 0.615022, 0.448662, 0.6952780000000001, 0.44802, 0.7046319999999999, 0.447112, 0.715808, 0.444832, 0.7307939999999999, 0.430012, 0.766809, 0.406787, 0.685673, 0.400738, 0.6810689999999999, 0.3924, 0.6777029999999999, 0.367856, 0.6639189999999999, 0.247923, 0.601333, 0.45277, 0.42084999999999995, 0.436392, 0.35988699999999996, 0.416164, 0.368714, 0.413386, 0.692366, 0.228018, 0.6835720000000001, 0.468268, 0.35267099999999996, 0.411362, 0.804327, 0.499989, 0.46982500000000005, 0.479154, 0.442654, 0.499974, 0.43963699999999994, 0.432112, 0.49358900000000006, 0.499886, 0.8669169999999999, 0.499913, 0.8217289999999999, 0.456549, 0.8192010000000001, 0.344549, 0.745439, 0.378909, 0.57401, 0.374293, 0.780185, 0.319688, 0.570738, 0.357155, 0.60427, 0.295284, 0.6215809999999999, 0.44775, 0.8624769999999999, 0.410986, 0.508723, 0.313951, 0.775308, 0.354128, 0.812553, 0.324548, 0.703993, 0.189096, 0.6463, 0.279777, 0.714658, 0.133823, 0.682701, 0.336768, 0.644733, 0.429884, 0.466522, 0.455528, 0.5486230000000001, 0.437114, 0.5588960000000001, 0.467288, 0.529925, 0.414712, 0.33521999999999996, 0.377046, 0.322778, 0.344108, 0.32015099999999996, 0.312876, 0.32233199999999995, 0.283526, 0.33319, 0.241246, 0.38278599999999996, 0.102986, 0.46876300000000004, 0.267612, 0.42456000000000005, 0.297879, 0.433176, 0.333434, 0.433878, 0.366427, 0.42611600000000005, 0.396012, 0.41669599999999996, 0.420121, 0.41022800000000004, 0.007561, 0.480777, 0.432949, 0.569518, 0.458639, 0.479089, 0.473466, 0.545744, 0.476088, 0.56383, 0.468472, 0.555057, 0.433991, 0.582362, 0.483518, 0.5629839999999999, 0.482483, 0.5778490000000001, 0.42645, 0.389799, 0.438999, 0.39649500000000004, 0.450067, 0.40043399999999996, 0.289712, 0.36825300000000005, 0.27667, 0.36337299999999995, 0.517862, 0.47194800000000003, 0.710288, 0.380764, 0.526227, 0.5739099999999999, 0.895093, 0.25414099999999995, 0.63407, 0.40957600000000005, 0.661242, 0.413025, 0.68888, 0.40946000000000005, 0.725342, 0.389131, 0.60663, 0.403705, 0.654766, 0.34401099999999996, 0.629906, 0.34607600000000005, 0.680678, 0.34726500000000005, 0.702097, 0.353591, 0.752212, 0.410805, 0.602918, 0.842863, 0.719902, 0.37560000000000004, 0.893693, 0.39996, 0.790082, 0.391354, 0.643998, 0.5344880000000001, 0.528249, 0.650404, 0.52585, 0.680191, 0.560215, 0.6572290000000001, 0.585384, 0.666541, 0.549626, 0.6808609999999999, 0.571228, 0.6826920000000001, 0.624852, 0.728099, 0.51305, 0.547282, 0.515097, 0.527252, 0.742247, 0.314507, 0.598631, 0.454979, 0.570338, 0.548575, 0.578632, 0.533623, 0.723087, 0.532054, 0.516446, 0.49963900000000006, 0.662801, 0.282918, 0.703624, 0.29327099999999995, 0.830705, 0.19381400000000004, 0.552386, 0.30256799999999995, 0.60761, 0.353888, 0.645429, 0.696707, 0.932695, 0.730105, 0.557261, 0.5728260000000001, 0.542902, 0.584792, 0.618026, 0.6947110000000001, 0.607591, 0.694203, 0.722943, 0.27196299999999995, 0.577414, 0.563167, 0.614083, 0.28138700000000005, 0.616907, 0.25588599999999995, 0.668509, 0.11991399999999997, 0.770092, 0.23202100000000003, 0.635536, 0.189249, 0.770391, 0.29955600000000004, 0.826722, 0.278755, 0.527121, 0.6661980000000001, 0.553172, 0.668527, 0.577238, 0.67389, 0.554692, 0.580066, 0.611897, 0.693961, 0.596961, 0.70654, 0.596371, 0.693953, 0.539958, 0.557139, 0.568842, 0.692366, 0.547818, 0.692366, 0.524613, 0.692366, 0.53409, 0.779141, 0.527671, 0.736226, 0.526913, 0.717857, 0.526878, 0.704626, 0.526967, 0.6952780000000001, 0.572058, 0.6952780000000001, 0.573521, 0.70354, 0.576838, 0.711846, 0.581691, 0.720063, 0.609945, 0.63991, 0.986046, 0.5600339999999999, 0.5868, 0.6954, 0.590372, 0.701823, 0.531915, 0.601537, 0.577268, 0.585935, 0.536915, 0.5937859999999999, 0.627543, 0.473352, 0.665586, 0.49595100000000003, 0.588354, 0.546862, 0.757824, 0.14767600000000003, 0.70925, 0.20150800000000002, 0.672684, 0.25658099999999995, 0.600409, 0.7490049999999999, 0.558266, 0.261672, 0.570304, 0.187871, 0.588166, 0.10904400000000003, 0.711045, 0.398952, 0.78107, 0.43540500000000004, 0.587247, 0.39893199999999995, 0.74287, 0.35544600000000004, 0.572156, 0.43765200000000004, 0.551868, 0.53657, 0.821442, 0.45755599999999996, 0.752702, 0.457182, 0.713757, 0.467627, 0.667113, 0.460673, 0.631101, 0.44715400000000005, 0.600862, 0.432473, 0.523481, 0.40562699999999996, 0.810748, 0.523926, 0.771046, 0.348959, 0.509127, 0.562718, 0.595293, 0.485024, 0.980531, 0.40156400000000003, 0.5735, 0.42000000000000004, 0.602995, 0.5486880000000001, 0.73353, 0.376977, 0.560611, 0.5190170000000001, 0.967686, 0.6443570000000001, 0.580985, 0.38715999999999995, 0.537728, 0.505385, 0.760966, 0.779753, 0.801779, 0.8319380000000001, 0.892441, 0.540761, 0.816351, 0.7402599999999999, 0.865595, 0.33368699999999996, 0.614074, 0.883246, 0.508953, 0.579438, 0.617942, 0.508316, 0.825608, 0.397675, 0.681215, 0.396235, 0.656636, 0.400597, 0.6039, 0.710217, 0.812086, 0.5885389999999999, 0.568013, 0.944565, 0.681008, 0.898285, 0.733752, 0.8697010000000001, 0.63383, 0.398822, 0.606793, 0.395537, 0.58966, 0.391062, 0.805016, 0.34210799999999997, 0.611335, 0.36228400000000005, 0.634038, 0.35597100000000004, 0.656636, 0.35535700000000003, 0.681215, 0.35834, 0.698585, 0.36315600000000003, 0.941867, 0.319076, 0.698585, 0.38744900000000004, 0.584177, 0.624107, 0.554318, 0.5660769999999999, 0.534154, 0.6206400000000001, 0.711218, 0.819975, 0.66463, 0.8528709999999999, 0.5591, 0.902632, 0.871706, 0.791941, 0.591234, 0.37389399999999995, 0.544341, 0.451584, 0.624563, 0.924192, 0.88577, 0.615029, 0.551338, 0.6952780000000001, 0.55198, 0.7046319999999999, 0.552888, 0.715808, 0.555168, 0.7307939999999999, 0.569944, 0.767035, 0.593203, 0.685676, 0.599262, 0.6810689999999999, 0.6076, 0.6777029999999999, 0.631938, 0.6635, 0.752033, 0.601315, 0.547226, 0.42039499999999996, 0.563544, 0.35982800000000004, 0.583841, 0.368714, 0.586614, 0.692366, 0.771915, 0.683578, 0.531597, 0.352483, 0.588371, 0.804441, 0.520797, 0.442565, 0.567985, 0.493479, 0.543283, 0.8192550000000001, 0.655317, 0.7455149999999999, 0.621009, 0.5740179999999999, 0.62556, 0.780312, 0.680198, 0.570719, 0.642764, 0.604338, 0.704663, 0.62153, 0.552012, 0.862592, 0.589072, 0.508637, 0.685945, 0.775357, 0.645735, 0.81264, 0.675343, 0.703978, 0.810858, 0.646305, 0.720122, 0.7146669999999999, 0.866152, 0.682705, 0.663187, 0.644597, 0.570082, 0.466326, 0.544562, 0.548376, 0.562759, 0.558785, 0.531987, 0.5301400000000001, 0.585271, 0.33517699999999995, 0.622953, 0.32277900000000004, 0.655896, 0.320163, 0.687132, 0.322346, 0.716482, 0.33320099999999997, 0.758757, 0.382787, 0.897013, 0.468769, 0.732392, 0.424547, 0.702114, 0.43316299999999996, 0.666525, 0.433866, 0.633505, 0.426088, 0.603876, 0.41658700000000004, 0.579658, 0.409945, 0.99244, 0.480777, 0.567192, 0.56942, 0.541366, 0.47889899999999996, 0.526564, 0.546118, 0.523913, 0.56383, 0.531529, 0.555057, 0.566036, 0.582329, 0.516311, 0.5630539999999999, 0.517472, 0.577877, 0.573595, 0.389807, 0.560698, 0.395332, 0.549756, 0.39975099999999997, 0.710288, 0.36825300000000005, 0.72333, 0.36337299999999995]; + +for (let i = 0; i < faces.length; i++) +{ + faces[i]--; +} + +const geom = new CGL.Geometry("fratze"); +geom.clear(); + +geom.vertices = new Float32Array(verts); +geom.texCoords = new Float32Array(tc); + +geom.vertexNormals = new Float32Array(verts); +geom.verticesIndices = faces; + +outGeom.set(null); +outGeom.set(geom); + +const g = geom.copy(); + + +}; + +Ops.Libs.Mediapipe.FaceMesh.prototype = new CABLES.Op(); +CABLES.OPS["7e13efda-330f-4c5e-bc84-e14e5af99cd4"]={f:Ops.Libs.Mediapipe.FaceMesh,objName:"Ops.Libs.Mediapipe.FaceMesh"}; + + + + +// ************************************************************** +// +// Ops.Libs.Mediapipe.MpFaceTracking +// +// ************************************************************** + +Ops.Libs.Mediapipe.MpFaceTracking = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inEle = op.inObject("Element"), + inRefineLand = op.inBool("Refine LandMarks", false), + outPoints = op.outArray("Points"), + outFound = op.outNumber("Found"), + outResult = op.outObject("Result"); + +// https://google.github.io/mediapipe/solutions/face_mesh.html + +let camera = null; + +inRefineLand.onChange = setOptions; + +inEle.onChange = () => +{ + const el = inEle.get(); + if (!el) return; + + camera = new Camera(el, { + "onFrame": async () => + { + await faceMesh.send({ "image": el }); + }, + "width": el.width, + "height": el.height + }); + camera.start(); +}; + +const faceMesh = new FaceMesh({ "locateFile": (file) => + `https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh/${file}` }); + +faceMesh.setOptions({ + "maxNumFaces": 1, + "minDetectionConfidence": 0.5, + "minTrackingConfidence": 0.5 +}); + +setOptions(); + +function setOptions() +{ + faceMesh.setOptions({ + "maxNumFaces": 1, + "minDetectionConfidence": 0.5, + "minTrackingConfidence": 0.5, + "refineLandmarks": inRefineLand.get() + }); +} + +faceMesh.onResults((r) => +{ + let points = []; + + if (r && r.multiFaceLandmarks) + { + outFound.set(r.multiFaceLandmarks.length); + if (r.multiFaceLandmarks[0]) for (let i = 0; i < r.multiFaceLandmarks[0].length; i++) + { + points.push( + (r.multiFaceLandmarks[0][i].x - 0.5) * 2, + -1 * (r.multiFaceLandmarks[0][i].y - 0.5) * 2, 0); + } + } + else outFound.set(0); + + outPoints.set(points); + outResult.set(r); +}); + + +}; + +Ops.Libs.Mediapipe.MpFaceTracking.prototype = new CABLES.Op(); +CABLES.OPS["57df26fc-916c-469b-a343-83572c731025"]={f:Ops.Libs.Mediapipe.MpFaceTracking,objName:"Ops.Libs.Mediapipe.MpFaceTracking"}; + + + + +// ************************************************************** +// +// Ops.Libs.Mediapipe.MpHand +// +// ************************************************************** + +Ops.Libs.Mediapipe.MpHand = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inMpResult = op.inObject("Hands Result"), + inWhichHand = op.inSwitch("Hand", ["Left", "Right"], "Left"), + inMinScore = op.inFloatSlider("Min Score", 0.2), + outPoints = op.outArray("Points"), + outLines = op.outArray("Lines"), + outResult = op.outObject("Data"), + outFound = op.outBoolNum("Found Hand"), + outScore = op.outNumber("Score"); + +function getLines(points) +{ + const lines = []; + lines.push(points[0 * 3 + 0], points[0 * 3 + 1], points[0 * 3 + 2]); // thumb + lines.push(points[1 * 3 + 0], points[1 * 3 + 1], points[1 * 3 + 2]); + lines.push(points[1 * 3 + 0], points[1 * 3 + 1], points[1 * 3 + 2]); + lines.push(points[2 * 3 + 0], points[2 * 3 + 1], points[2 * 3 + 2]); + lines.push(points[2 * 3 + 0], points[2 * 3 + 1], points[2 * 3 + 2]); + lines.push(points[3 * 3 + 0], points[3 * 3 + 1], points[3 * 3 + 2]); + lines.push(points[3 * 3 + 0], points[3 * 3 + 1], points[3 * 3 + 2]); + lines.push(points[4 * 3 + 0], points[4 * 3 + 1], points[4 * 3 + 2]); + + lines.push(points[0 * 3 + 0], points[0 * 3 + 1], points[0 * 3 + 2]); // wrist + lines.push(points[5 * 3 + 0], points[5 * 3 + 1], points[5 * 3 + 2]); + lines.push(points[5 * 3 + 0], points[5 * 3 + 1], points[5 * 3 + 2]); + lines.push(points[9 * 3 + 0], points[9 * 3 + 1], points[9 * 3 + 2]); + lines.push(points[9 * 3 + 0], points[9 * 3 + 1], points[9 * 3 + 2]); + lines.push(points[13 * 3 + 0], points[13 * 3 + 1], points[13 * 3 + 2]); + lines.push(points[13 * 3 + 0], points[13 * 3 + 1], points[13 * 3 + 2]); + lines.push(points[17 * 3 + 0], points[17 * 3 + 1], points[17 * 3 + 2]); + lines.push(points[17 * 3 + 0], points[17 * 3 + 1], points[17 * 3 + 2]); + lines.push(points[0 * 3 + 0], points[0 * 3 + 1], points[0 * 3 + 2]); + + lines.push(points[5 * 3 + 0], points[5 * 3 + 1], points[5 * 3 + 2]); // index finger + lines.push(points[6 * 3 + 0], points[6 * 3 + 1], points[6 * 3 + 2]); + lines.push(points[6 * 3 + 0], points[6 * 3 + 1], points[6 * 3 + 2]); + lines.push(points[7 * 3 + 0], points[7 * 3 + 1], points[7 * 3 + 2]); + lines.push(points[7 * 3 + 0], points[7 * 3 + 1], points[7 * 3 + 2]); + lines.push(points[8 * 3 + 0], points[8 * 3 + 1], points[8 * 3 + 2]); + + lines.push(points[9 * 3 + 0], points[9 * 3 + 1], points[9 * 3 + 2]); // middle finger + lines.push(points[10 * 3 + 0], points[10 * 3 + 1], points[10 * 3 + 2]); + lines.push(points[10 * 3 + 0], points[10 * 3 + 1], points[10 * 3 + 2]); + lines.push(points[11 * 3 + 0], points[11 * 3 + 1], points[11 * 3 + 2]); + lines.push(points[11 * 3 + 0], points[11 * 3 + 1], points[11 * 3 + 2]); + lines.push(points[12 * 3 + 0], points[12 * 3 + 1], points[12 * 3 + 2]); + + lines.push(points[13 * 3 + 0], points[13 * 3 + 1], points[13 * 3 + 2]); // ring finger + lines.push(points[14 * 3 + 0], points[14 * 3 + 1], points[14 * 3 + 2]); + lines.push(points[14 * 3 + 0], points[14 * 3 + 1], points[14 * 3 + 2]); + lines.push(points[15 * 3 + 0], points[15 * 3 + 1], points[15 * 3 + 2]); + lines.push(points[15 * 3 + 0], points[15 * 3 + 1], points[15 * 3 + 2]); + lines.push(points[16 * 3 + 0], points[16 * 3 + 1], points[16 * 3 + 2]); + + lines.push(points[17 * 3 + 0], points[17 * 3 + 1], points[17 * 3 + 2]); // ring finger + lines.push(points[18 * 3 + 0], points[18 * 3 + 1], points[18 * 3 + 2]); + lines.push(points[18 * 3 + 0], points[18 * 3 + 1], points[18 * 3 + 2]); + lines.push(points[19 * 3 + 0], points[19 * 3 + 1], points[19 * 3 + 2]); + lines.push(points[19 * 3 + 0], points[19 * 3 + 1], points[19 * 3 + 2]); + lines.push(points[20 * 3 + 0], points[20 * 3 + 1], points[20 * 3 + 2]); + + return lines; +} + +inMpResult.onChange = () => +{ + let points = []; + let points2 = []; + let lines = null; + let lines2 = null; + let r = inMpResult.get(); + + if (r && r.multiHandedness) + { + + } + else + { + outFound.set(false); + outScore.set(0); + return; + } + + let idx = 0; + + let found = false; + + if (r.multiHandedness) + { + for (let i = 0; i < r.multiHandedness.length; i++) + { + if (r.multiHandedness[i].label == inWhichHand.get()) + { + idx = i; + outScore.set(r.multiHandedness[i].score); + found = true; + } + } + } + + if (found && outScore.get() > inMinScore.get()) + { + outFound.set(true); + if (r && r.multiHandLandmarks && r.multiHandLandmarks[idx]) + { + for (let i = 0; i < r.multiHandLandmarks[idx].length; i++) + { + points[i * 3] = (r.multiHandLandmarks[idx][i].x - 0.5) * 2; + points[i * 3 + 1] = -1 * (r.multiHandLandmarks[idx][i].y - 0.5) * 2; + points[i * 3 + 2] = 0; + } + lines = getLines(points); + + outPoints.set(points); + outLines.set(lines); + outResult.set(r.multiHandLandmarks[idx]); + } + } + else + { + outResult.set(null); + outPoints.set(null); + outLines.set(null); + + outFound.set(false); + } +}; + + +}; + +Ops.Libs.Mediapipe.MpHand.prototype = new CABLES.Op(); +CABLES.OPS["935b0d02-1dce-4d5c-87a8-f1fa36c5d25e"]={f:Ops.Libs.Mediapipe.MpHand,objName:"Ops.Libs.Mediapipe.MpHand"}; + + + + +// ************************************************************** +// +// Ops.Libs.Mediapipe.MpHandCoordinate +// +// ************************************************************** + +Ops.Libs.Mediapipe.MpHandCoordinate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +// https://google.github.io/mediapipe/solutions/hands.html + +const marks=["WRIST","THUMB_CMC","THUMB_MCP","THUMB_IP","THUMB_TIP","INDEX_FINGER_MCP","INDEX_FINGER_PIP","INDEX_FINGER_DIP","INDEX_FINGER_TIP","MIDDLE_FINGER_MCP","MIDDLE_FINGER_PIP","MIDDLE_FINGER_DIP","MIDDLE_FINGER_TIP","RING_FINGER_MCP","RING_FINGER_PIP","RING_FINGER_DIP","RING_FINGER_TIP","PINKY_MCP","PINKY_PIP","PINKY_DIP","PINKY_TIP"]; + +const + inHand=op.inArray("Hand Points"), + inWhich=op.inDropDown("Joint",marks,"WRIST"), + outX=op.outNumber("X"), + outY=op.outNumber("Y"), + outZ=op.outNumber("Z"); + +let idx=0; + +inWhich.onChange=()=> +{ + idx=marks.indexOf(inWhich.get()); + update(); +}; + +inHand.onChange=update; + +function update() +{ + const arr=inHand.get(); + + if(arr && arr.length>idx*3) + { + outX.set(arr[idx*3+0]); + outY.set(arr[idx*3+1]); + outZ.set(arr[idx*3+2]); + } + +} + +}; + +Ops.Libs.Mediapipe.MpHandCoordinate.prototype = new CABLES.Op(); +CABLES.OPS["d3adef3d-5c99-48fd-86f0-48ec4a290afb"]={f:Ops.Libs.Mediapipe.MpHandCoordinate,objName:"Ops.Libs.Mediapipe.MpHandCoordinate"}; + + + + +// ************************************************************** +// +// Ops.Libs.Mediapipe.MpHandTracking +// +// ************************************************************** + +Ops.Libs.Mediapipe.MpHandTracking = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// todo: warn if ele not dom object , e..g. texture! + +const + inEle = op.inObject("Element", null, "element"), + inMinConfDetect = op.inFloatSlider("Min Confidence Detect", 0.5), + inMinConfTrack = op.inFloatSlider("Min Confidence Tracking", 0.5), + + outResult = op.outObject("Result"), + outFound = op.outNumber("Found Hands"); + +const hands = new Hands({ "locateFile": (file) => +{ return `https://cdn.jsdelivr.net/npm/@mediapipe/hands@0.1/${file}`; } }); + +let camera = null; +updateOptions(); + +inMinConfTrack.onChange = + inMinConfDetect.onChange = updateOptions; + +inEle.onChange = () => +{ + if (!inEle.get()) return; + + camera = new Camera(inEle.get(), { + "onFrame": async () => + { + const ele = inEle.get(); + + if (ele) await hands.send({ "image": ele }); + }, + "width": inEle.get().width, + "height": inEle.get().height + }); + camera.start(); +}; + +function updateOptions() +{ + hands.setOptions({ + "maxNumHands": 2, + "minDetectionConfidence": inMinConfDetect.get(), + "minTrackingConfidence": inMinConfTrack.get() + }); +} + +hands.onResults((r) => +{ + if (r && r.multiHandedness) + { + outFound.set(r.multiHandedness.length); + } + else + { + outFound.set(0); + } + + outResult.set(r); +}); + + +}; + +Ops.Libs.Mediapipe.MpHandTracking.prototype = new CABLES.Op(); +CABLES.OPS["5251e50f-abaf-4ca3-a809-61f8ca58ec35"]={f:Ops.Libs.Mediapipe.MpHandTracking,objName:"Ops.Libs.Mediapipe.MpHandTracking"}; + + + + +// ************************************************************** +// +// Ops.Libs.Mediapipe.MpPoseGetCoordinate +// +// ************************************************************** + +Ops.Libs.Mediapipe.MpPoseGetCoordinate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + marks = ["Nose", "Left eye inner", "Left eye", "Left eye outer", "Right eye inner", "Right eye", "Right eye outer", "Left ear", "Right ear", "Mouth left", "Mouth right", "Left shoulder", "Right shoulder", "Left elbow", "Right elbow", "Left wrist", "Right wrist", "Left pinky #1 knuckle", "Right pinky #1 knuckle", "Left index #1 knuckle", "Right index #1 knuckle", "Left thumb #2 knuckle", "Right thumb #2 knuckle", "Left hip", "Right hip", "Left knee", "Right knee", "Left ankle", "Right ankle", "Left heel", "Right heel", "Left foot index", "Right foot index"], + + inArr = op.inArray("Landmarks"), + inWhich = op.inDropDown("Landmark", marks, "Nose"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outZ = op.outNumber("Z"); + +let index = 0; + +inWhich.onChange = () => +{ + index = marks.indexOf(inWhich.get()); +}; + +inArr.onChange = () => +{ + const arr = inArr.get(); + + if (arr && arr[index]) + { + outX.set((arr[index].x - 0.5) * 2.0); + outY.set((arr[index].y - 0.5) * -2.0); + outZ.set(arr[index].z); + } +}; + + +}; + +Ops.Libs.Mediapipe.MpPoseGetCoordinate.prototype = new CABLES.Op(); +CABLES.OPS["725c90f3-3526-420f-b921-4d214c281e25"]={f:Ops.Libs.Mediapipe.MpPoseGetCoordinate,objName:"Ops.Libs.Mediapipe.MpPoseGetCoordinate"}; + + + + +// ************************************************************** +// +// Ops.Libs.Mediapipe.MpPoseTracking +// +// ************************************************************** + +Ops.Libs.Mediapipe.MpPoseTracking = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"texcopy_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\n\n\nvoid main()\n{\n vec2 tc=vec2(texCoord.x,texCoord.y);\n\n #ifdef FLIPX\n tc.x=1.0-texCoord.x;\n #endif\n #ifdef FLIPY\n tc.y=1.0-texCoord.y;\n #endif\n outColor=texture(tex,tc);\n}",}; +// todo: warn if ele not dom object , e..g. texture! + +const + inEle = op.inObject("Element", null, "element"), + + inModelComplexity = op.inSwitch("Model Complexity", ["0", "1", "2"], "1"), + inSmoothLandmarks = op.inBool("Smooth Landmarks", true), + + inMinDetectionConfidence = op.inFloatSlider("Min Detection Confidence", 0.5), + inMinTrackingConfidence = op.inFloatSlider("Min Tracking Confidence", 0.5), + + inEnableSegmentation = op.inBool("Enable Segmentation", false), + inUpdateSeg = op.inTrigger("Update Texture"), + inSmoothSegmentation = op.inBool("Smooth Segmentation", false), + flipX = op.inValueBool("Flip X", false), + flipY = op.inValueBool("Flip Y", false), + + outPoints = op.outArray("Points"), + outTex = op.outTexture("Segmentation Mask"), + outLandmarks = op.outArray("Landmarks"), + outLines = op.outArray("Lines"), + outFound = op.outNumber("Found"); + +op.setPortGroup("Segmentation", [inEnableSegmentation, inSmoothSegmentation, flipX, flipY, inUpdateSeg]); + +const pose = new Pose({ "locateFile": (file) => +{ return `https://cdn.jsdelivr.net/npm/@mediapipe/pose/${file}`; } }); + +const cgl = op.patch.cgl; +let camera = null; +let lines = []; +let segMaskBitmap = null; +let points = []; +let points2 = []; +let tc = null; +let canvasTexture = null; + +updateOptions(); + +flipX.onChange = +flipY.onChange = initCopyShader; + +inSmoothLandmarks.onChange = + inModelComplexity.onChange = + inEnableSegmentation.onChange = + inSmoothSegmentation.onChange = + inMinDetectionConfidence.onChange = + inMinTrackingConfidence.onChange = updateOptions; + +function updateOptions() +{ + pose.setOptions({ + "modelComplexity": parseInt(inModelComplexity.get()), + "smoothLandmarks": inSmoothLandmarks.get(), + "enableSegmentation": inEnableSegmentation.get(), + "smoothSegmentation": inSmoothSegmentation.get(), + "minDetectionConfidence": inMinDetectionConfidence.get(), + "minTrackingConfidence": inMinTrackingConfidence.get() + }); + + inSmoothSegmentation.setUiAttribs({ "greyout": !inEnableSegmentation.get() }); + flipX.setUiAttribs({ "greyout": !inEnableSegmentation.get() }); + flipY.setUiAttribs({ "greyout": !inEnableSegmentation.get() }); +} + +function initCopyShader() +{ + if (!tc)tc = new CGL.CopyTexture(cgl, "webcamFlippedTexture", { "shader": attachments.texcopy_frag }); + tc.bgShader.toggleDefine("FLIPX", flipX.get()); + tc.bgShader.toggleDefine("FLIPY", !flipY.get()); +} + +inEle.onChange = () => +{ + const el = inEle.get(); + if (!el) + { + return; + } + camera = new Camera(el, { + "onFrame": async () => + { + await pose.send({ "image": el }); + }, + "width": el.width, + "height": el.height + }); + camera.start(); +}; + +inUpdateSeg.onTriggered = () => +{ + outTex.set(CGL.Texture.getEmptyTexture(cgl)); + if (!segMaskBitmap) return; + + if (!canvasTexture) + { + canvasTexture = new CGL.Texture.createFromImage(cgl, segMaskBitmap); + } + else + canvasTexture.initTexture(segMaskBitmap, CGL.Texture.FILTER_LINEAR); + + if (tc) + { + outTex.set(tc.copy(canvasTexture)); + } + else + outTex.set(canvasTexture); +}; + +pose.onResults((r) => +{ + if (r && r.poseLandmarks) + { + for (let i = 0; i < r.poseLandmarks.length; i++) + { + points[i * 3] = (r.poseLandmarks[i].x - 0.5) * 2.0; + points[i * 3 + 1] = (r.poseLandmarks[i].y - 0.5) * -2; + points[i * 3 + 2] = r.poseLandmarks[i].z * 1.0; + } + + if (r.segmentationMask) + { + segMaskBitmap = r.segmentationMask; + } + + outPoints.set(null); + outPoints.set(points); + + outLandmarks.set(r.poseLandmarks); + + lines = []; + + // top body + lines.push( + points[11 * 3 + 0], points[11 * 3 + 1], points[11 * 3 + 2], + points[12 * 3 + 0], points[12 * 3 + 1], points[12 * 3 + 2]); + lines.push( + points[12 * 3 + 0], points[12 * 3 + 1], points[12 * 3 + 2], + points[24 * 3 + 0], points[24 * 3 + 1], points[24 * 3 + 2]); + lines.push( + points[24 * 3 + 0], points[24 * 3 + 1], points[24 * 3 + 2], + points[23 * 3 + 0], points[23 * 3 + 1], points[23 * 3 + 2]); + lines.push( + points[11 * 3 + 0], points[11 * 3 + 1], points[11 * 3 + 2], + points[23 * 3 + 0], points[23 * 3 + 1], points[23 * 3 + 2]); + + // left arm + lines.push( + points[11 * 3 + 0], points[11 * 3 + 1], points[11 * 3 + 2], + points[13 * 3 + 0], points[13 * 3 + 1], points[13 * 3 + 2]); + lines.push( + points[13 * 3 + 0], points[13 * 3 + 1], points[13 * 3 + 2], + points[15 * 3 + 0], points[15 * 3 + 1], points[15 * 3 + 2]); + + // right arm + lines.push( + points[12 * 3 + 0], points[12 * 3 + 1], points[12 * 3 + 2], + points[14 * 3 + 0], points[14 * 3 + 1], points[14 * 3 + 2]); + lines.push( + points[14 * 3 + 0], points[14 * 3 + 1], points[14 * 3 + 2], + points[16 * 3 + 0], points[16 * 3 + 1], points[16 * 3 + 2]); + + // left leg + lines.push( + points[23 * 3 + 0], points[23 * 3 + 1], points[23 * 3 + 2], + points[25 * 3 + 0], points[25 * 3 + 1], points[25 * 3 + 2]); + lines.push( + points[25 * 3 + 0], points[25 * 3 + 1], points[25 * 3 + 2], + points[27 * 3 + 0], points[27 * 3 + 1], points[27 * 3 + 2]); + lines.push( + points[27 * 3 + 0], points[27 * 3 + 1], points[27 * 3 + 2], + points[29 * 3 + 0], points[29 * 3 + 1], points[29 * 3 + 2]); + + // right leg + lines.push( + points[24 * 3 + 0], points[24 * 3 + 1], points[24 * 3 + 2], + points[26 * 3 + 0], points[26 * 3 + 1], points[26 * 3 + 2]); + lines.push( + points[26 * 3 + 0], points[26 * 3 + 1], points[26 * 3 + 2], + points[28 * 3 + 0], points[28 * 3 + 1], points[28 * 3 + 2]); + lines.push( + points[28 * 3 + 0], points[28 * 3 + 1], points[28 * 3 + 2], + points[30 * 3 + 0], points[30 * 3 + 1], points[30 * 3 + 2]); + + // left hand + lines.push( + points[15 * 3 + 0], points[15 * 3 + 1], points[15 * 3 + 2], + points[21 * 3 + 0], points[21 * 3 + 1], points[21 * 3 + 2], + + points[15 * 3 + 0], points[15 * 3 + 1], points[15 * 3 + 2], + points[17 * 3 + 0], points[17 * 3 + 1], points[17 * 3 + 2], + + points[17 * 3 + 0], points[17 * 3 + 1], points[17 * 3 + 2], + points[19 * 3 + 0], points[19 * 3 + 1], points[19 * 3 + 2], + + points[19 * 3 + 0], points[19 * 3 + 1], points[19 * 3 + 2], + points[15 * 3 + 0], points[15 * 3 + 1], points[15 * 3 + 2]); + + // right hand + lines.push( + points[16 * 3 + 0], points[16 * 3 + 1], points[16 * 3 + 2], + points[22 * 3 + 0], points[22 * 3 + 1], points[22 * 3 + 2], + + points[16 * 3 + 0], points[16 * 3 + 1], points[16 * 3 + 2], + points[18 * 3 + 0], points[18 * 3 + 1], points[18 * 3 + 2], + + points[18 * 3 + 0], points[18 * 3 + 1], points[18 * 3 + 2], + points[20 * 3 + 0], points[20 * 3 + 1], points[20 * 3 + 2], + + points[20 * 3 + 0], points[20 * 3 + 1], points[20 * 3 + 2], + points[16 * 3 + 0], points[16 * 3 + 1], points[16 * 3 + 2]); + + lines.push( + points[27 * 3 + 0], points[27 * 3 + 1], points[27 * 3 + 2], + points[31 * 3 + 0], points[31 * 3 + 1], points[31 * 3 + 2], + + points[31 * 3 + 0], points[31 * 3 + 1], points[31 * 3 + 2], + points[29 * 3 + 0], points[29 * 3 + 1], points[29 * 3 + 2]); + + lines.push( + points[28 * 3 + 0], points[28 * 3 + 1], points[28 * 3 + 2], + points[32 * 3 + 0], points[32 * 3 + 1], points[32 * 3 + 2], + + points[32 * 3 + 0], points[32 * 3 + 1], points[32 * 3 + 2], + points[30 * 3 + 0], points[30 * 3 + 1], points[30 * 3 + 2]); + + outLines.set(lines); + } + else + { + outPoints.set(null); + outLandmarks.set(null); + outLines.set(null); + } +}); + + +}; + +Ops.Libs.Mediapipe.MpPoseTracking.prototype = new CABLES.Op(); +CABLES.OPS["279774b0-9897-4f36-840e-5f823cfc988a"]={f:Ops.Libs.Mediapipe.MpPoseTracking,objName:"Ops.Libs.Mediapipe.MpPoseTracking"}; + + + + +// ************************************************************** +// +// Ops.Libs.OpenType.OpentypeFont +// +// ************************************************************** + +Ops.Libs.OpenType.OpentypeFont = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + filename = op.inUrl("Font File", [".otf", ".ttf", ".woff", ".woff2"]), + outFont = op.outObject("Opentype Font", null, "opentype"); + +filename.onChange = async function () +{ + const font = await opentype.load(filename.get()); + outFont.set(font); +}; + + +}; + +Ops.Libs.OpenType.OpentypeFont.prototype = new CABLES.Op(); +CABLES.OPS["f85574bb-3869-4a14-8dcc-70414bd8cfcd"]={f:Ops.Libs.OpenType.OpentypeFont,objName:"Ops.Libs.OpenType.OpentypeFont"}; + + + + +// ************************************************************** +// +// Ops.Libs.OpenType.OpentypeToSvgPath +// +// ************************************************************** + +Ops.Libs.OpenType.OpentypeToSvgPath = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inFont = op.inObject("Opentype Font"), + inStr = op.inString("Text", "cables"), + inLs = op.inFloat("Letter Spacing", 0), + outPathStr = op.outString("Path String"); + +inStr.onChange = +inLs.onChange = +inFont.onChange = async function () +{ + const font = inFont.get(); + if (!font || !font.getPath) + { + outPathStr.set(""); + return; + } + + const paths = font.getPaths(inStr.get(), 0, 0, 72); + let str = ""; + + let ls = inLs.get(); + + for (let i = 0; i < paths.length; i++) + { + for (let j = 0; j < paths[i].commands.length; j++) + { + if (paths[i].commands[j].hasOwnProperty("x")) + paths[i].commands[j].x += i * ls; + if (paths[i].commands[j].hasOwnProperty("x1")) + paths[i].commands[j].x1 += i * ls; + } + str += paths[i].toPathData(); + } + + outPathStr.set(str); +}; + + +}; + +Ops.Libs.OpenType.OpentypeToSvgPath.prototype = new CABLES.Op(); +CABLES.OPS["4d901c72-b8dc-45dc-ac2e-608e5da40677"]={f:Ops.Libs.OpenType.OpentypeToSvgPath,objName:"Ops.Libs.OpenType.OpentypeToSvgPath"}; + + + + +// ************************************************************** +// +// Ops.Libs.Trackingjs.TrackWebcamColor +// +// ************************************************************** + +Ops.Libs.Trackingjs.TrackWebcamColor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUpdate=op.inTrigger("Update"), + inEle = op.inObject("Video Element"), + inThresh=op.inFloatSlider("Threshold",0.5), + + inResize=op.inFloatSlider("Resize Video",0.3), + + r = op.inValueSlider("r", Math.random()), + g = op.inValueSlider("g", Math.random()), + b = op.inValueSlider("b", Math.random()), + + outArrPos = op.outArray("Positions"), + outArrSizes = op.outArray("Sizes"); + +const color={r:0,g:80,b:150}; +let started=false; + +r.setUiAttribs({ "colorPick": true }); +r.onChange= + g.onChange= + b.onChange=()=> + { + color.r=Math.round(r.get()*255); + color.g=Math.round(g.get()*255); + color.b=Math.round(b.get()*255); + }; + + +let canvas= document.createElement("canvas"); +const eleId = "video2canv" + CABLES.uuid(); +canvas.setAttribute("id", eleId); + + +tracking.ColorTracker.registerColor("dynamic", function (r, g, b) +{ + return getColorDistance(color, { "r": r, "g": g, "b": b }) < inThresh.get()*255; +}); + + +let tracker = new tracking.ColorTracker("dynamic"); +// let tracker = new tracking.ObjectTracker("face"); + + + +tracker.on("track", function (e) +{ + const arrRects = []; + const arrSizes=[]; + + const r=canvas.width/canvas.height; + + + if (e.data.length !== 0) + { + + e.data.forEach(function (rect) + { + arrRects.push(r*(rect.x/canvas.width-0.5),1.0-rect.y/canvas.height-0.5,0); + arrSizes.push(r*(rect.width/canvas.width),rect.height/canvas.height,0); + }); + + } + + outArrPos.set(arrRects); + outArrSizes.set(arrSizes); +}); + +inUpdate.onTriggered=() => +{ + const videoEle=inEle.get(); + if(videoEle) + { + + if(videoEle.videoWidth && videoEle.videoHeight) + { + + let w=videoEle.videoWidth; + let h=videoEle.videoHeight; + + w=Math.round(w*inResize.get()); + h=Math.round(h*inResize.get()); + + canvas.setAttribute("width",w); + canvas.setAttribute("height",h); + + canvas.getContext('2d', { alpha: false }).drawImage(videoEle, 0, 0, w, h); + + } + } + + + // canvas = inCanvas.get(); + if (!started && canvas) + { + // started=true; + tracking.track(canvas, tracker, { "camera": true }); + } +}; + +function getColorDistance(target, actual) +{ + return Math.sqrt( + (target.r - actual.r) * (target.r - actual.r) + + (target.g - actual.g) * (target.g - actual.g) + + (target.b - actual.b) * (target.b - actual.b) + ); +} + + + +}; + +Ops.Libs.Trackingjs.TrackWebcamColor.prototype = new CABLES.Op(); +CABLES.OPS["546beec8-fcf3-4456-a7f5-8fd81ca717ab"]={f:Ops.Libs.Trackingjs.TrackWebcamColor,objName:"Ops.Libs.Trackingjs.TrackWebcamColor"}; + + + + +// ************************************************************** +// +// Ops.Math.Abs +// +// ************************************************************** + +Ops.Math.Abs = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number = op.inValue("number"), + result = op.outNumber("result"); + +number.onChange = function () +{ + result.set(Math.abs(number.get())); +}; + + +}; + +Ops.Math.Abs.prototype = new CABLES.Op(); +CABLES.OPS["6b5af21d-065f-44d2-9442-8b7a254753f6"]={f:Ops.Math.Abs,objName:"Ops.Math.Abs"}; + + + + +// ************************************************************** +// +// Ops.Math.Accumulator +// +// ************************************************************** + +Ops.Math.Accumulator = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("Trigger in"), + inAddNumber = op.inValueFloat("Add to number", 0.0), + inMultiplier = op.inValueFloat("Multiplier to add number", 1.0), + inSetNumber = op.inValueFloat("Default Value", 1.0), + inSet = op.inTriggerButton("Set Default Value"), + outNumber = op.outNumber("Current value"); + +let lastTime = performance.now(); +let currentNumber = 0.0; +let firsttime = true; + +inSet.onTriggered = resetNumber; + +function resetNumber() +{ + currentNumber = inSetNumber.get(); + outNumber.set(currentNumber); + firsttime = true; +} + +exe.onTriggered = function () +{ + if (!firsttime) + { + let diff = (performance.now() - lastTime) / 100; + currentNumber += inAddNumber.get() * diff * inMultiplier.get(); + outNumber.set(currentNumber); + } + lastTime = performance.now(); + firsttime = false; +}; + + +}; + +Ops.Math.Accumulator.prototype = new CABLES.Op(); +CABLES.OPS["460574ca-dca2-4283-8c37-57a8c446a51f"]={f:Ops.Math.Accumulator,objName:"Ops.Math.Accumulator"}; + + + + +// ************************************************************** +// +// Ops.Math.AddUp +// +// ************************************************************** + +Ops.Math.AddUp = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number = op.inValue("Number"), + doAdd = op.inTriggerButton("Add"), + doReset = op.inTriggerButton("Reset"), + result = op.outNumber("Result"); + +let value = 0; + +doAdd.onTriggered = function () +{ + value += number.get(); + result.set(value); +}; + +doReset.onTriggered = function () +{ + value = 0; + result.set(value); +}; + + +}; + +Ops.Math.AddUp.prototype = new CABLES.Op(); +CABLES.OPS["f1c76976-4c8f-43a5-a9c7-6e4c7d21c749"]={f:Ops.Math.AddUp,objName:"Ops.Math.AddUp"}; + + + + +// ************************************************************** +// +// Ops.Math.AngleBetweenPoints +// +// ************************************************************** + +Ops.Math.AngleBetweenPoints = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + p1x = op.inValue("Point 1 X"), + p1y = op.inValue("Point 1 Y"), + p2x = op.inValue("Point 2 X"), + p2y = op.inValue("Point 2 Y"), + outAngle = op.outNumber("Angle", 0); + +p1x.onChange = + p2x.onChange = + p1y.onChange = + p2y.onChange = update; + +function update() +{ + let theta = Math.atan2( + p1y.get() - p2y.get(), + p1x.get() - p2x.get()); + + let angle = theta * 180 / Math.PI * -1; + + outAngle.set(angle); +} + + +}; + +Ops.Math.AngleBetweenPoints.prototype = new CABLES.Op(); +CABLES.OPS["03e20aba-a22a-40de-9287-a32fc495a643"]={f:Ops.Math.AngleBetweenPoints,objName:"Ops.Math.AngleBetweenPoints"}; + + + + +// ************************************************************** +// +// Ops.Math.Array3MultiplyMatrix +// +// ************************************************************** + +Ops.Math.Array3MultiplyMatrix = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Update"), + inArr = op.inArray("Array", null, 3), + inMat = op.inArray("Matrix", null, 16), + outArr = op.outArray("Result", null, 3); + +let theArr = []; +exec.onTriggered = function () +{ + let arr = inArr.get(); + if (!arr) + { + outArr.set(null); + return; + } + theArr.length = arr.length; + let mMat = inMat.get(); + let vec = vec4.create(); + vec[3] = 1; + + for (let i = 0; i < arr.length / 3; i++) + { + vec[0] = arr[i * 3 + 0]; + vec[1] = arr[i * 3 + 1]; + vec[2] = arr[i * 3 + 2]; + + vec3.transformMat4(vec, vec, mMat); + + theArr[i * 3 + 0] = vec[0]; + theArr[i * 3 + 1] = vec[1]; + theArr[i * 3 + 2] = vec[2]; + } + + outArr.set(null); + outArr.set(theArr); +}; + + +}; + +Ops.Math.Array3MultiplyMatrix.prototype = new CABLES.Op(); +CABLES.OPS["a83aca4c-48ab-41ad-9943-3f4d03777ae8"]={f:Ops.Math.Array3MultiplyMatrix,objName:"Ops.Math.Array3MultiplyMatrix"}; + + + + +// ************************************************************** +// +// Ops.Math.Array3To2dProjection +// +// ************************************************************** + +Ops.Math.Array3To2dProjection = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("Exec"), + inArr = op.inArray("Array3x"), + fov = op.inValueFloat("fov", 45), + w = op.inValueFloat("w", 1), + h = op.inValueFloat("h", 1), + px = op.inValueFloat("Pos X", -0.5), + py = op.inValueFloat("Pos Y", -0.5), + coordmul = op.inValueFloat("mul"), + next = op.outTrigger("Next"), + outArr = op.outArray("Array2x"); + +exe.onTriggered = function () +{ + // if (needsUpdate) + update(); +}; + +exe.onChange = + inArr.onChange = + fov.onChange = + w.onChange = + h.onChange = + px.onChange = + py.onChange = + coordmul.onChange = update; + +const cgl = op.patch.cgl; + +let needsUpdate = false; + +let minX = 9999999; +let maxX = -9999999; +let minY = 9999999; +let maxY = -9999999; + +let pos = vec3.create(); +let m = mat4.create(); +let trans = vec3.create(); +let pm = mat4.create(); + +function proj(p) +{ + pm = mat4.perspective(pm, fov.get() * CGL.DEG2RAD, 1, 0.0001, 100); + + mat4.multiply(m, cgl.vMatrix, cgl.mMatrix); + vec3.transformMat4(pos, [px.get(), py.get(), 0], m); + vec3.add(pos, pos, p); + + vec3.transformMat4(trans, pos, pm); + + let height = h.get(); + let width = w.get(); + let x = trans[0] * width; + let y = trans[1] * height; + + return [x, y, 0]; +} + +inArr.onChange = function () +{ + needsUpdate = true; +}; + +function update() +{ + let points3d = inArr.get(); + if (!points3d) return; + + let ind = 0; + let laserArr = []; + let point = vec3.create(); + + for (let i = 0; i < points3d.length / 3; i++) + { + vec3.set(point, + points3d[i * 3 + 0], + points3d[i * 3 + 1], + points3d[i * 3 + 2] + ); + + let vv = proj(point); + + let x = vv[0]; + let y = vv[1]; + if (x == null)x = 0; + if (y == null)y = 0; + + x += w.get() / 2; + y += h.get() / 2; + + minX = Math.min(x, minX); + maxX = Math.max(x, maxX); + + minY = Math.min(y, minY); + maxY = Math.max(y, maxY); + + laserArr[ind++] = x; + laserArr[ind++] = y; + laserArr[ind++] = 0; + } + + outArr.set(null); + outArr.set(laserArr); + needsUpdate = false; + + next.trigger(); +} + + +}; + +Ops.Math.Array3To2dProjection.prototype = new CABLES.Op(); +CABLES.OPS["8ac0bd90-ba6f-40f7-8429-21bff899cb14"]={f:Ops.Math.Array3To2dProjection,objName:"Ops.Math.Array3To2dProjection"}; + + + + +// ************************************************************** +// +// Ops.Math.Atan2 +// +// ************************************************************** + +Ops.Math.Atan2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + x = op.inValue("X"), + y = op.inValue("Y"), + phase = op.inValue("Phase", 0.0), + mul = op.inValue("Frequency", 1.0), + result = op.outNumber("Result"); + +x.onChange = + y.onChange = update; + +function update() +{ + result.set( + mul.get() * Math.atan2(x.get(), y.get()) + phase.get() + ); +} + + +}; + +Ops.Math.Atan2.prototype = new CABLES.Op(); +CABLES.OPS["93ebaebf-0ebf-4aae-b0d8-2996ae44f1ed"]={f:Ops.Math.Atan2,objName:"Ops.Math.Atan2"}; + + + + +// ************************************************************** +// +// Ops.Math.Average +// +// ************************************************************** + +Ops.Math.Average = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number = op.inValue("number"), + infl = op.inValueSlider("Influence", 0.2), + result = op.outNumber("result"); + +number.onChange = function () +{ + let influence = infl.get(); + result.set((result.get() * (1.0 - influence) + number.get()) * influence); +}; + +number.set(1); + + +}; + +Ops.Math.Average.prototype = new CABLES.Op(); +CABLES.OPS["12a93cd3-348b-460a-aa02-f66690062322"]={f:Ops.Math.Average,objName:"Ops.Math.Average"}; + + + + +// ************************************************************** +// +// Ops.Math.ButterflyCurve +// +// ************************************************************** + +Ops.Math.ButterflyCurve = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inVal = op.inValue("Value"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"); + +inVal.onChange = update; + +function update() +{ + let t = inVal.get(); + + let x = Math.cos(t) * Math.pow(Math.exp(Math.cos(t)) - 2 * Math.cos(4 * t) - Math.sin(t / 12), 2); + let y = Math.sin(t) * Math.pow(Math.exp(Math.cos(t)) - 2 * Math.cos(4 * t) - Math.sin(t / 12), 2); + + outX.set(x); + outY.set(y); +} + + +}; + +Ops.Math.ButterflyCurve.prototype = new CABLES.Op(); +CABLES.OPS["d9023bba-8122-4eed-8d61-32461cad8aba"]={f:Ops.Math.ButterflyCurve,objName:"Ops.Math.ButterflyCurve"}; + + + + +// ************************************************************** +// +// Ops.Math.Ceil +// +// ************************************************************** + +Ops.Math.Ceil = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const number1 = op.inValue("Number"); +const result = op.outNumber("Result"); + +function exec() +{ + result.set(Math.ceil(number1.get())); +} + +number1.onChange = exec; + + +}; + +Ops.Math.Ceil.prototype = new CABLES.Op(); +CABLES.OPS["15ba7aa9-b1c3-4b20-b6bf-b52a3ba8c8c5"]={f:Ops.Math.Ceil,objName:"Ops.Math.Ceil"}; + + + + +// ************************************************************** +// +// Ops.Math.CircleCoordinates +// +// ************************************************************** + +Ops.Math.CircleCoordinates = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inPos = op.inFloat("Position"), + inRadius = op.inFloat("Radius", 1), + outX = op.outNumber("X"), + outY = op.outNumber("Y"); + +inPos.onChange = + inRadius.onChange = calc; + +function calc() +{ + const r = inRadius.get(); + const degInRad = (360 * inPos.get()) * CGL.DEG2RAD; + + outX.set(Math.sin(degInRad) * r); + outY.set(Math.cos(degInRad) * r); +} + + +}; + +Ops.Math.CircleCoordinates.prototype = new CABLES.Op(); +CABLES.OPS["76fdea4d-1653-46d6-80f3-af34018f043d"]={f:Ops.Math.CircleCoordinates,objName:"Ops.Math.CircleCoordinates"}; + + + + +// ************************************************************** +// +// Ops.Math.Clamp +// +// ************************************************************** + +Ops.Math.Clamp = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val = op.inValueFloat("val", 0.5), + min = op.inValueFloat("min", 0), + max = op.inValueFloat("max", 1), + ignore = op.inValueBool("ignore outside values"), + result = op.outNumber("result"); + +val.onChange = min.onChange = max.onChange = clamp; + +function clamp() +{ + if (ignore.get()) + { + if (val.get() > max.get()) return; + if (val.get() < min.get()) return; + } + result.set(Math.min(Math.max(val.get(), min.get()), max.get())); +} + + +}; + +Ops.Math.Clamp.prototype = new CABLES.Op(); +CABLES.OPS["cda1a98e-5e16-40bd-9b18-a67e9eaad5a1"]={f:Ops.Math.Clamp,objName:"Ops.Math.Clamp"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.Between +// +// ************************************************************** + +Ops.Math.Compare.Between = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number = op.inValue("value", 2), + number1 = op.inValue("number1", 1), + number2 = op.inValue("number2", 3), + result = op.outNumber("result"); + +number1.onChange = exec; +number2.onChange = exec; +number.onChange = exec; +exec(); + +function exec() +{ + result.set( + number.get() > Math.min(number1.get(), number2.get()) && + number.get() < Math.max(number1.get(), number2.get()) + ); +} + + +}; + +Ops.Math.Compare.Between.prototype = new CABLES.Op(); +CABLES.OPS["d629959e-838d-4541-b12f-15e2d6ff5131"]={f:Ops.Math.Compare.Between,objName:"Ops.Math.Compare.Between"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.BetweenEquals +// +// ************************************************************** + +Ops.Math.Compare.BetweenEquals = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number = op.inValueFloat("Value", 2), + range1 = op.inValueFloat("Range 1", 1), + range2 = op.inValueFloat("Range 2", 3), + result = op.outNumber("Result"); + +number.onChange = range1.onChange = range2.onChange = exec; +exec(); + +function exec() +{ + result.set( + number.get() >= Math.min(range1.get(), range2.get()) && + number.get() <= Math.max(range1.get(), range2.get()) + ); +} + + +}; + +Ops.Math.Compare.BetweenEquals.prototype = new CABLES.Op(); +CABLES.OPS["e2d6d6c4-84c7-42d7-a1b2-e9c5d4c5c13e"]={f:Ops.Math.Compare.BetweenEquals,objName:"Ops.Math.Compare.BetweenEquals"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.CompareNumbers +// +// ************************************************************** + +Ops.Math.Compare.CompareNumbers = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + logicSelectMode = op.inValueSelect("Comparison mode", [">", "<", ">=", "<=", "==", "!=", "><", ">=<"], ">"), + numberIn_1 = op.inFloat("Value in", 0), + numberIn_2 = op.inFloat("Condition value", 1), + numberIn_3 = op.inFloat("Max", 1), + resultNumberOut = op.outNumber("Result"); + +let logicFunc; + +logicSelectMode.onChange = onFilterChange; + +numberIn_1.onChange = numberIn_2.onChange = numberIn_3.onChange = update; + +onFilterChange(); + +function onFilterChange() +{ + let logicSelectValue = logicSelectMode.get(); + if (logicSelectValue === ">") logicFunc = function (a, b, c) { if (a > b) return 1; return 0; }; + else if (logicSelectValue === "<") logicFunc = function (a, b, c) { if (a < b) return 1; return 0; }; + else if (logicSelectValue === ">=") logicFunc = function (a, b, c) { if (a >= b) return 1; return 0; }; + else if (logicSelectValue === "<=") logicFunc = function (a, b, c) { if (a <= b) return 1; return 0; }; + else if (logicSelectValue === "==") logicFunc = function (a, b, c) { if (a === b) return 1; return 0; }; + else if (logicSelectValue === "!=") logicFunc = function (a, b, c) { if (a !== b) return 1; return 0; }; + else if (logicSelectValue === "><") logicFunc = function (a, b, c) { if (a > Math.min(b, c) && a < Math.max(b, c)) return 1; return 0; }; + else if (logicSelectValue === ">=<") logicFunc = function (a, b, c) { if (a >= Math.min(b, c) && a <= Math.max(b, c)) return 1; return 0; }; + + if (logicSelectValue === "><" || logicSelectValue === ">=<") + { + numberIn_3.setUiAttribs({ "greyout": false }); + numberIn_2.setUiAttribs({ "title": "Min" }); + } + else + { + numberIn_3.setUiAttribs({ "greyout": true }); + numberIn_2.setUiAttribs({ "title": "Condition value" }); + } + update(); + op.setUiAttrib({ "extendTitle": logicSelectValue }); +} + +function update() +{ + let n1 = numberIn_1.get(); + let n2 = numberIn_2.get(); + let n3 = numberIn_3.get(); + + let resultNumber = logicFunc(n1, n2, n3); + + resultNumberOut.set(resultNumber); +} + + +}; + +Ops.Math.Compare.CompareNumbers.prototype = new CABLES.Op(); +CABLES.OPS["169137db-9853-4384-ac5b-d10a0bbda5c2"]={f:Ops.Math.Compare.CompareNumbers,objName:"Ops.Math.Compare.CompareNumbers"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.Equals +// +// ************************************************************** + +Ops.Math.Compare.Equals = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValue("number1", 1), + number2 = op.inValue("number2", 1), + result = op.outBoolNum("result"); + +number1.onChange = + number2.onChange = exec; +exec(); + +function exec() +{ + result.set(number1.get() == number2.get()); +} + + +}; + +Ops.Math.Compare.Equals.prototype = new CABLES.Op(); +CABLES.OPS["4dd3cc55-eebc-4187-9d4e-2e053a956fab"]={f:Ops.Math.Compare.Equals,objName:"Ops.Math.Compare.Equals"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.GreaterOrEquals +// +// ************************************************************** + +Ops.Math.Compare.GreaterOrEquals = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + result = op.outBoolNum("result"), + number1 = op.inValueFloat("number1"), + number2 = op.inValueFloat("number2"); + +number1.onLinkChanged = + number2.onLinkChanged = + number1.onChange = + number2.onChange = exec; + +function exec() +{ + result.set(number1.get() >= number2.get()); +} + + +}; + +Ops.Math.Compare.GreaterOrEquals.prototype = new CABLES.Op(); +CABLES.OPS["5f9ce320-1e8d-49cb-9927-337e0b3f4d45"]={f:Ops.Math.Compare.GreaterOrEquals,objName:"Ops.Math.Compare.GreaterOrEquals"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.GreaterThan +// +// ************************************************************** + +Ops.Math.Compare.GreaterThan = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValueFloat("number1"), + number2 = op.inValueFloat("number2"), + result = op.outBoolNum("result"); + +op.setTitle(">"); + +number1.onChange = number2.onChange = exec; + +function exec() +{ + result.set(number1.get() > number2.get()); +} + + +}; + +Ops.Math.Compare.GreaterThan.prototype = new CABLES.Op(); +CABLES.OPS["b250d606-f7f8-44d3-b099-c29efff2608a"]={f:Ops.Math.Compare.GreaterThan,objName:"Ops.Math.Compare.GreaterThan"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.IfBetweenThen +// +// ************************************************************** + +Ops.Math.Compare.IfBetweenThen = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + number = op.inValue("number", 0), + min = op.inValue("min", 0), + max = op.inValue("max", 1), + triggerThen = op.outTrigger("then"), + triggerElse = op.outTrigger("else"), + outBetween = op.outBoolNum("bs between"); + +exe.onTriggered = function () +{ + if (number.get() >= min.get() && number.get() < max.get()) + { + outBetween.set(true); + triggerThen.trigger(); + } + else + { + outBetween.set(false); + triggerElse.trigger(); + } +}; + + +}; + +Ops.Math.Compare.IfBetweenThen.prototype = new CABLES.Op(); +CABLES.OPS["c80437f0-f0e1-465c-9cea-8a044aa2feaa"]={f:Ops.Math.Compare.IfBetweenThen,objName:"Ops.Math.Compare.IfBetweenThen"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.IsEven +// +// ************************************************************** + +Ops.Math.Compare.IsEven = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number = op.inValueFloat("number1"), + result = op.outBoolNum("result"); + +number.onChange = exec; +exec(); + +function exec() +{ + result.set(!(number.get() & 1)); +} + + +}; + +Ops.Math.Compare.IsEven.prototype = new CABLES.Op(); +CABLES.OPS["f462c64a-3e17-4de8-a77d-6735725fc9cc"]={f:Ops.Math.Compare.IsEven,objName:"Ops.Math.Compare.IsEven"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.LessThan +// +// ************************************************************** + +Ops.Math.Compare.LessThan = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const number1 = op.inValue("number1"); +const number2 = op.inValue("number2"); +const result = op.outBoolNum("result"); + +op.setTitle("<"); + +number1.onChange = exec; +number2.onChange = exec; +exec(); + +function exec() +{ + result.set(number1.get() < number2.get()); +} + + +}; + +Ops.Math.Compare.LessThan.prototype = new CABLES.Op(); +CABLES.OPS["04fd113f-ade1-43fb-99fa-f8825f8814c0"]={f:Ops.Math.Compare.LessThan,objName:"Ops.Math.Compare.LessThan"}; + + + + +// ************************************************************** +// +// Ops.Math.Cosine +// +// ************************************************************** + +Ops.Math.Cosine = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + value = op.inValue("Value"), + phase = op.inValue("Phase", 0.0), + mul = op.inValue("Frequency", 1.0), + amplitude = op.inValue("Amplitude", 1.0), + invert = op.inValueBool("asine", false), + result = op.outNumber("Result"); + +let calculate = Math.cos; + +value.onChange = function () +{ + result.set( + amplitude.get() * calculate((value.get() * mul.get()) + phase.get()) + ); +}; + +invert.onChange = function () +{ + if (invert.get()) calculate = Math.acos; + else calculate = Math.cos; +}; + + +}; + +Ops.Math.Cosine.prototype = new CABLES.Op(); +CABLES.OPS["b51166c4-e0a8-441a-b724-1531effdc52f"]={f:Ops.Math.Cosine,objName:"Ops.Math.Cosine"}; + + + + +// ************************************************************** +// +// Ops.Math.Crossfade +// +// ************************************************************** + +Ops.Math.Crossfade = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inFade = op.inFloatSlider("Crossfade",0.5); +const inMinOut = op.inFloat("Out Min", 0); +const inMaxOut = op.inFloat("Out Max", 1); +op.setPortGroup("Range",[inMinOut, inMaxOut]); + +const anim = new CABLES.Anim(); +const inDropdownAnim = anim.createPort(op,"Easing", function() { + anim.keys[0].setEasing(anim.defaultEasing); +}); + +anim.setValue(0,0); +anim.setValue(1,1); +op.setPortGroup("Easing",[inDropdownAnim]); + +const outA = op.outNumber("A"); +const outB = op.outNumber("B"); + + +function handleMinMaxChange() { + anim.keys[0].time= anim.keys[0].value = Math.min(inMinOut.get(),inMaxOut.get()); + anim.keys[1].time= anim.keys[1].value = Math.max(inMinOut.get(),inMaxOut.get()); + handleValueChange(); +} +function handleValueChange() { + const val = inFade.get(); + + const realMin = Math.min(inMaxOut.get(), inMinOut.get()); + const realMax = Math.max(inMaxOut.get(), inMinOut.get()); + + // Y = (X-A)/(B-A) * (D-C) + C + const resultA = anim.getValue((1-val) * (realMax - realMin) + realMin); + const resultB = anim.getValue(val * (realMax - realMin) + realMin); + + outA.set(resultA); + outB.set(resultB); +} + +function updateMinMax() { + anim.keys[0].time = anim.keys[0].value = Math.min(inMinOut.get(), inMaxOut.get()); + anim.keys[1].time = anim.keys[1].value = Math.max(inMinOut.get(), inMaxOut.get()); +} + +handleValueChange(); + +inMinOut.onChange = inMaxOut.onChange = handleMinMaxChange; +inFade.onChange = handleValueChange; + + +}; + +Ops.Math.Crossfade.prototype = new CABLES.Op(); +CABLES.OPS["6c8d01a2-dc8c-43c3-a569-c2ab0ba42747"]={f:Ops.Math.Crossfade,objName:"Ops.Math.Crossfade"}; + + + + +// ************************************************************** +// +// Ops.Math.DegreeToVector +// +// ************************************************************** + +Ops.Math.DegreeToVector = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + deg = op.inValueFloat("degree", 0), + x = op.outNumber("x"), + y = op.outNumber("y"); + +deg.onChange = update; + +function update() +{ + let rad = deg.get() * CGL.DEG2RAD; + x.set(-1 * Math.sin(rad)); + y.set(Math.cos(rad)); +} + + +}; + +Ops.Math.DegreeToVector.prototype = new CABLES.Op(); +CABLES.OPS["56b1618b-4eed-41a8-87a2-57397ffc9029"]={f:Ops.Math.DegreeToVector,objName:"Ops.Math.DegreeToVector"}; + + + + +// ************************************************************** +// +// Ops.Math.Degrees +// +// ************************************************************** + +Ops.Math.Degrees = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + value = op.inValue("Radians"), + result = op.outNumber("Result"); + +// convert radians into degrees +value.onChange = function () +{ + result.set( + value.get() * 180 / Math.PI + ); +}; + + +}; + +Ops.Math.Degrees.prototype = new CABLES.Op(); +CABLES.OPS["53385f87-13eb-4c19-8b04-6632a72e967c"]={f:Ops.Math.Degrees,objName:"Ops.Math.Degrees"}; + + + + +// ************************************************************** +// +// Ops.Math.Delta +// +// ************************************************************** + +Ops.Math.Delta = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val = op.inValue("Value"), + changeAlwaysPort = op.inValueBool("Change Always", false), + inReset = op.inTrigger("Reset"), + result = op.outNumber("Delta"); + +val.changeAlways = false; + +let oldVal = 0; +let firstTime = true; + +changeAlwaysPort.onChange = function () +{ + val.changeAlways = changeAlwaysPort.get(); +}; + +inReset.onTriggered = function () +{ + firstTime = true; +}; + +val.onChange = function () +{ + let change = oldVal - val.get(); + oldVal = val.get(); + if (firstTime) + { + firstTime = false; + return; + } + result.set(change); +}; + + +}; + +Ops.Math.Delta.prototype = new CABLES.Op(); +CABLES.OPS["0f203337-e13c-47ec-a09f-b309212540b0"]={f:Ops.Math.Delta,objName:"Ops.Math.Delta"}; + + + + +// ************************************************************** +// +// Ops.Math.DeltaSum +// +// ************************************************************** + +Ops.Math.DeltaSum = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inVal = op.inValue("Delta Value"), + defVal = op.inValue("Default Value", 0), + inMul = op.inValue("Multiply", 1), + inReset = op.inTriggerButton("Reset"), + inLimit = op.inValueBool("Limit", false), + inMin = op.inValue("Min", 0), + inMax = op.inValue("Max", 100), + inRubber = op.inValue("Rubberband", 0), + outVal = op.outNumber("Absolute Value"); + +inVal.changeAlways = true; + +op.setPortGroup("Limit", [inLimit, inMin, inMax, inRubber]); + +let value = 0; +let lastEvent = CABLES.now(); +let rubTimeout = null; + +inLimit.onChange = updateLimit; +defVal.onChange = + inReset.onTriggered = resetValue; + +inMax.onChange = + inMin.onChange = updateValue; + +updateLimit(); + +function resetValue() +{ + let v = defVal.get(); + + if (inLimit.get()) + { + v = Math.max(inMin.get(), v); + v = Math.min(inMax.get(), v); + } + + value = v; + outVal.set(value); +} + +function updateLimit() +{ + inMin.setUiAttribs({ "greyout": !inLimit.get() }); + inMax.setUiAttribs({ "greyout": !inLimit.get() }); + inRubber.setUiAttribs({ "greyout": !inLimit.get() }); + + updateValue(); +} + +function releaseRubberband() +{ + const min = inMin.get(); + const max = inMax.get(); + + if (value < min) value = min; + if (value > max) value = max; + + outVal.set(value); +} + +function updateValue() +{ + if (inLimit.get()) + { + const min = inMin.get(); + const max = inMax.get(); + const rubber = inRubber.get(); + const minr = inMin.get() - rubber; + const maxr = inMax.get() + rubber; + + if (value < minr) value = minr; + if (value > maxr) value = maxr; + + if (rubber !== 0.0) + { + clearTimeout(rubTimeout); + rubTimeout = setTimeout(releaseRubberband.bind(this), 300); + } + } + + outVal.set(value); +} + +inVal.onChange = function () +{ + let v = inVal.get(); + + const rubber = inRubber.get(); + + if (rubber !== 0.0) + { + const min = inMin.get(); + const max = inMax.get(); + const minr = inMin.get() - rubber; + const maxr = inMax.get() + rubber; + + if (value < min) + { + const aa = Math.abs(value - minr) / rubber; + v *= (aa * aa); + } + if (value > max) + { + const aa = Math.abs(maxr - value) / rubber; + v *= (aa * aa); + } + } + + lastEvent = CABLES.now(); + value += v * inMul.get(); + updateValue(); +}; + + +}; + +Ops.Math.DeltaSum.prototype = new CABLES.Op(); +CABLES.OPS["d9d4b3db-c24b-48da-b798-9e6230d861f7"]={f:Ops.Math.DeltaSum,objName:"Ops.Math.DeltaSum"}; + + + + +// ************************************************************** +// +// Ops.Math.Difference +// +// ************************************************************** + +Ops.Math.Difference = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + num1 = op.inValue("Number A"), + num2 = op.inValue("Number B"), + result = op.outNumber("Result"); + +num1.onChange = + num2.onChange = update; + +function update() +{ + let r = num1.get() - num2.get(); + r = Math.abs(r); + result.set(r); +} + + +}; + +Ops.Math.Difference.prototype = new CABLES.Op(); +CABLES.OPS["5431b943-18aa-46e4-bd32-a7eee30d4e51"]={f:Ops.Math.Difference,objName:"Ops.Math.Difference"}; + + + + +// ************************************************************** +// +// Ops.Math.Distance2d +// +// ************************************************************** + +Ops.Math.Distance2d = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + x1 = op.inValue("x1"), + y1 = op.inValue("y1"), + x2 = op.inValue("x2"), + y2 = op.inValue("y2"), + dist = op.outNumber("distance"); + +x1.onChange = +y1.onChange = +x2.onChange = +y2.onChange = calc; + +function calc() +{ + const xd = x2.get() - x1.get(); + const yd = y2.get() - y1.get(); + dist.set(Math.sqrt(xd * xd + yd * yd)); +} + + +}; + +Ops.Math.Distance2d.prototype = new CABLES.Op(); +CABLES.OPS["d181cc7c-adbb-467b-842b-e6a854c58bfc"]={f:Ops.Math.Distance2d,objName:"Ops.Math.Distance2d"}; + + + + +// ************************************************************** +// +// Ops.Math.Distance3d +// +// ************************************************************** + +Ops.Math.Distance3d = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let x1 = op.inValueFloat("x1"); +let y1 = op.inValueFloat("y1"); +let z1 = op.inValueFloat("z1"); + +let x2 = op.inValueFloat("x2"); +let y2 = op.inValueFloat("y2"); +let z2 = op.inValueFloat("z2"); + +let dist = op.addOutPort(new CABLES.Port(op, "distance")); +op.setPortGroup("Point 1", [x1, y1, z1]); +op.setPortGroup("Point 2", [x2, y2, z2]); + +x1.onChange = calc; +y1.onChange = calc; +z1.onChange = calc; +x2.onChange = calc; +y2.onChange = calc; +z2.onChange = calc; + +function calc() +{ + let xd = x2.get() - x1.get(); + let yd = y2.get() - y1.get(); + let zd = z2.get() - z1.get(); + dist.set(Math.sqrt(xd * xd + yd * yd + zd * zd)); +} + + +}; + +Ops.Math.Distance3d.prototype = new CABLES.Op(); +CABLES.OPS["6c5772d8-c6f4-4cad-a8e7-f669c0186964"]={f:Ops.Math.Distance3d,objName:"Ops.Math.Distance3d"}; + + + + +// ************************************************************** +// +// Ops.Math.Distance3dNew +// +// ************************************************************** + +Ops.Math.Distance3dNew = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + calc = op.inTriggerButton("Calc"), + + x1 = op.inValueFloat("x1"), + y1 = op.inValueFloat("y1"), + z1 = op.inValueFloat("z1"), + + x2 = op.inValueFloat("x2"), + y2 = op.inValueFloat("y2"), + z2 = op.inValueFloat("z2"), + + inMin = op.inValue("Min", 0); + +op.setPortGroup("Point 1", [x1, y1, z1]); +op.setPortGroup("Point 2", [x2, y2, z2]); + +const next = op.outTrigger("Next"); +const dist = op.addOutPort(new CABLES.Port(op, "distance")); + +let min = inMin.get(); + +inMin.onChange = function () +{ + min = inMin.get(); +}; + +calc.onTriggered = function () +{ + const xd = x2.get() - x1.get(); + if (Math.abs(xd) > min) return; + + const yd = y2.get() - y1.get(); + if (Math.abs(yd) > min) return; + + const zd = z2.get() - z1.get(); + if (Math.abs(zd) > min) return; + + dist.set(Math.sqrt(xd * xd + yd * yd + zd * zd)); + + next.trigger(); +}; + + +}; + +Ops.Math.Distance3dNew.prototype = new CABLES.Op(); +CABLES.OPS["6b344add-6c4d-4365-858f-a365e4adb183"]={f:Ops.Math.Distance3dNew,objName:"Ops.Math.Distance3dNew"}; + + + + +// ************************************************************** +// +// Ops.Math.Divide +// +// ************************************************************** + +Ops.Math.Divide = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValueFloat("number1", 1), + number2 = op.inValueFloat("number2", 2), + result = op.outNumber("result"); + +op.setTitle("/"); + +number1.onChange = number2.onChange = exec; +exec(); + +function exec() +{ + result.set(number1.get() / number2.get()); +} + + +}; + +Ops.Math.Divide.prototype = new CABLES.Op(); +CABLES.OPS["86fcfd8c-038d-4b91-9820-a08114f6b7eb"]={f:Ops.Math.Divide,objName:"Ops.Math.Divide"}; + + + + +// ************************************************************** +// +// Ops.Math.Ease +// +// ************************************************************** + +Ops.Math.Ease = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inVal = op.inValue("Value"), + inMin = op.inValue("Min", 0), + inMax = op.inValue("Max", 1), + result = op.outNumber("Result"), + anim = new CABLES.Anim(); + +anim.createPort(op, "Easing", updateAnimEasing); +anim.setValue(0, 0); +anim.setValue(1, 1); + +op.onLoaded = inMin.onChange = inMax.onChange = updateMinMax; + +function updateMinMax() +{ + anim.keys[0].time = anim.keys[0].value = Math.min(inMin.get(), inMax.get()); + anim.keys[1].time = anim.keys[1].value = Math.max(inMin.get(), inMax.get()); +} + +function updateAnimEasing() +{ + anim.keys[0].setEasing(anim.defaultEasing); +} + +inVal.onChange = function () +{ + const r = anim.getValue(inVal.get()); + result.set(r); +}; + + +}; + +Ops.Math.Ease.prototype = new CABLES.Op(); +CABLES.OPS["8f6e4a08-33e6-408f-ac4a-198bd03b417b"]={f:Ops.Math.Ease,objName:"Ops.Math.Ease"}; + + + + +// ************************************************************** +// +// Ops.Math.Exp +// +// ************************************************************** + +Ops.Math.Exp = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const number = op.inValueFloat("number"); +const result = op.outNumber("result"); + +number.onChange = function () +{ + let r = Math.exp(number.get()); + if (isNaN(r))r = 0; + result.set(r); +}; + + +}; + +Ops.Math.Exp.prototype = new CABLES.Op(); +CABLES.OPS["612727c2-b2e5-446c-99bb-7e30132f2ff6"]={f:Ops.Math.Exp,objName:"Ops.Math.Exp"}; + + + + +// ************************************************************** +// +// Ops.Math.FlipSign +// +// ************************************************************** + +Ops.Math.FlipSign = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inval = op.inValueFloat("Value", 1), + result = op.outNumber("Result"); + +inval.onChange = update; +update(); + +function update() +{ + result.set(inval.get() * -1); +} + + +}; + +Ops.Math.FlipSign.prototype = new CABLES.Op(); +CABLES.OPS["f5c858a2-2654-4108-86fe-319efa70ecec"]={f:Ops.Math.FlipSign,objName:"Ops.Math.FlipSign"}; + + + + +// ************************************************************** +// +// Ops.Math.Floor +// +// ************************************************************** + +Ops.Math.Floor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const number1 = op.inValue("Number"); +const result = op.outNumber("Result"); +number1.onChange = exec; + +function exec() +{ + result.set(Math.floor(number1.get())); +} + + +}; + +Ops.Math.Floor.prototype = new CABLES.Op(); +CABLES.OPS["0c77617c-b688-4b55-addf-2cbcaabf98af"]={f:Ops.Math.Floor,objName:"Ops.Math.Floor"}; + + + + +// ************************************************************** +// +// Ops.Math.Fract +// +// ************************************************************** + +Ops.Math.Fract = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + value = op.inValue("Value"), + result = op.outNumber("Result"); + +value.onChange = function () +{ + const v = value.get(); + result.set(v - Math.floor(v)); +}; + + +}; + +Ops.Math.Fract.prototype = new CABLES.Op(); +CABLES.OPS["12573ba7-866d-40c4-9ddd-14d9331ffe6f"]={f:Ops.Math.Fract,objName:"Ops.Math.Fract"}; + + + + +// ************************************************************** +// +// Ops.Math.GaussianRandomArray +// +// ************************************************************** + +Ops.Math.GaussianRandomArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inNum = op.inValueInt("Num", 100), + outArr = op.outArray("Array"), + inDev = op.inValue("Deviation", 1), + seed = op.inValueFloat("Random Seed"); + +let arr = []; +let stdDev = 1; +let previous = false; +let nextGaussian = null; +let y2; + +seed.onChange = inDev.onChange = inNum.onChange = update; +update(); + +// from https://github.com/processing/p5.js/blob/master/src/math/random.js + +function randomGaussian(mean, sd) +{ + let y1, x1, x2, w; + if (previous) + { + y1 = y2; + previous = false; + } + else + { + do + { + x1 = Math.seededRandom() * 2 - 1; + x2 = Math.seededRandom() * 2 - 1; + w = x1 * x1 + x2 * x2; + } while (w >= 1); + w = Math.sqrt((-2 * Math.log(w)) / w); + y1 = x1 * w; + y2 = x2 * w; + previous = true; + } + + let m = mean || 0; + let s = sd || 1; + return y1 * s + m; +} + +function update() +{ + stdDev = inDev.get(); + Math.randomSeed = seed.get(); + + arr.length = Math.floor(inNum.get()) || 0; + for (let i = 0; i < arr.length; i++) + { + arr[i] = randomGaussian(0, stdDev); + } + + outArr.set(null); + outArr.set(arr); +} + + +}; + +Ops.Math.GaussianRandomArray.prototype = new CABLES.Op(); +CABLES.OPS["1a8c3535-6fce-4cba-8601-ddb7a5dd7656"]={f:Ops.Math.GaussianRandomArray,objName:"Ops.Math.GaussianRandomArray"}; + + + + +// ************************************************************** +// +// Ops.Math.Incrementor +// +// ************************************************************** + +Ops.Math.Incrementor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + increment = op.inTriggerButton("Increment"), + decrement = op.inTriggerButton("Decrement"), + inLength = op.inValueInt("Length"), + inLimit = op.inBool("Limit", true), + reset = op.inTriggerButton("Reset"), + inMode = op.inSwitch("Mode", ["Rewind", "Stop at Max"], "Rewind"), + inDefault = op.inValueInt("Default", 0), + outChanged = op.outTrigger("Changed"), + value = op.outNumber("Value"), + outRestarted = op.outTrigger("Restarted"); + +value.ignoreValueSerialize = true; +inLength.set(10); +let val = 0; +value.set(0); + +inLength.onTriggered = reset; +inDefault.onChange = doReset; +reset.onTriggered = doReset; + +const MODE_REWIND = 0; +const MODE_STOP = 1; + +let mode = MODE_REWIND; + +inMode.onChange = function () +{ + if (inMode.get() == "Rewind") + { + mode = MODE_REWIND; + } + if (inMode.get() == "Stop at Max") + { + mode = MODE_STOP; + } +}; + +inLimit.onChange = () => +{ + inLength.setUiAttribs({ "greyout": !inLimit.get() }); +}; + +function doReset() +{ + value.set(null); + val = inDefault.get(); + value.set(val); + outRestarted.trigger(); +} + +decrement.onTriggered = function () +{ + val--; + if (inLimit.get()) + { + if (mode == MODE_REWIND && val < 0)val = inLength.get() - 1; + if (mode == MODE_STOP && val < 0)val = 0; + } + value.set(val); + + outChanged.trigger(); +}; + +increment.onTriggered = function () +{ + val++; + if (inLimit.get()) + { + if (mode == MODE_REWIND && val >= inLength.get()) + { + val = 0; + outRestarted.trigger(); + } + if (mode == MODE_STOP && val >= inLength.get())val = inLength.get() - 1; + } + + value.set(val); + + outChanged.trigger(); +}; + + +}; + +Ops.Math.Incrementor.prototype = new CABLES.Op(); +CABLES.OPS["45cc0011-ada8-4423-8f5b-39a3810b8389"]={f:Ops.Math.Incrementor,objName:"Ops.Math.Incrementor"}; + + + + +// ************************************************************** +// +// Ops.Math.IndexFraction +// +// ************************************************************** + +Ops.Math.IndexFraction = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inValue=op.inFloat("Number",0.5), + inIdx=op.inInt("Index",0), + outValue=op.outNumber("Result"); + + inIdx.onChange=inValue.onChange = update; + +function update() +{ + const idx=inIdx.get(); + const val=inValue.get(); + + if(idxMath.ceil(val)) outValue.set(0.0); + else outValue.set(val-Math.floor(val)); +} + +}; + +Ops.Math.IndexFraction.prototype = new CABLES.Op(); +CABLES.OPS["924f3516-7d1c-4829-a24d-515bf3e7f43f"]={f:Ops.Math.IndexFraction,objName:"Ops.Math.IndexFraction"}; + + + + +// ************************************************************** +// +// Ops.Math.Interpolate +// +// ************************************************************** + +Ops.Math.Interpolate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val1 = op.inFloat("Value 1"), + val2 = op.inFloat("Value 2"), + perc = op.inFloatSlider("Percentage"), + result = op.outNumber("Result"); + +val1.onChange = +val2.onChange = +perc.onChange = update; + +function update() +{ + result.set((val2.get() - val1.get()) * perc.get() + val1.get()); +} + + +}; + +Ops.Math.Interpolate.prototype = new CABLES.Op(); +CABLES.OPS["d126e2c8-221e-428f-8ff4-8b8c5f6b8905"]={f:Ops.Math.Interpolate,objName:"Ops.Math.Interpolate"}; + + + + +// ************************************************************** +// +// Ops.Math.IsNumberRising +// +// ************************************************************** + +Ops.Math.IsNumberRising = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inNum = op.inFloat("Number", 0), + outRising = op.outNumber("Rising"); + +let last = 0; + +inNum.onChange = () => +{ + outRising.set(inNum.get() > last); + last = inNum.get(); +}; + + +}; + +Ops.Math.IsNumberRising.prototype = new CABLES.Op(); +CABLES.OPS["1598fbdf-eab9-41a1-98de-794795255359"]={f:Ops.Math.IsNumberRising,objName:"Ops.Math.IsNumberRising"}; + + + + +// ************************************************************** +// +// Ops.Math.Log +// +// ************************************************************** + +Ops.Math.Log = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const number = op.inValueFloat("number"); +const result = op.outNumber("result"); + +number.onChange = function () +{ + let r = Math.log(number.get()); + if (isNaN(r))r = 0; + result.set(r); +}; + + +}; + +Ops.Math.Log.prototype = new CABLES.Op(); +CABLES.OPS["7440b1ca-71d9-42a3-a927-d7b45b8857f9"]={f:Ops.Math.Log,objName:"Ops.Math.Log"}; + + + + +// ************************************************************** +// +// Ops.Math.MapGeoCoordsSpherical +// +// ************************************************************** + +Ops.Math.MapGeoCoordsSpherical = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Coordinates"), + inRad = op.inFloat("Radius", 1), + outArr = op.outArray("Result"); + +let arr = []; +inRad.onChange = + inArr.onChange = update; + +function update() +{ + outArr.set(null); + let iarr = inArr.get(); + + if (!iarr) return; + + let l = iarr.length / 2 * 3; + arr.length = l; + const radius = inRad.get(); + + for (let i = 0; i < iarr.length; i += 2) + { + const + lat = iarr[i + 0], + lon = iarr[i + 1], + phi = (90 - lat) * (Math.PI / 180), + theta = (lon + 180) * (Math.PI / 180); + + const x = -((radius) * Math.sin(phi) * Math.cos(theta)), + z = ((radius) * Math.sin(phi) * Math.sin(theta)), + y = ((radius) * Math.cos(phi)); + + arr[i / 2 * 3 + 0] = x; + arr[i / 2 * 3 + 1] = y; + arr[i / 2 * 3 + 2] = z; + } + + outArr.set(arr); +} + + +}; + +Ops.Math.MapGeoCoordsSpherical.prototype = new CABLES.Op(); +CABLES.OPS["e4c6786b-abf6-4762-b8f9-ed89dd532e23"]={f:Ops.Math.MapGeoCoordsSpherical,objName:"Ops.Math.MapGeoCoordsSpherical"}; + + + + +// ************************************************************** +// +// Ops.Math.MapRange +// +// ************************************************************** + +Ops.Math.MapRange = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v = op.inValueFloat("value", 0), + old_min = op.inValueFloat("old min", 0), + old_max = op.inValueFloat("old max", 1), + new_min = op.inValueFloat("new min", -1), + new_max = op.inValueFloat("new max", 1), + easing = op.inValueSelect("Easing", ["Linear", "Smoothstep", "Smootherstep"], "Linear"), + result = op.outNumber("result", 0); + +op.setPortGroup("Input Range", [old_min, old_max]); +op.setPortGroup("Output Range", [new_min, new_max]); + +let ease = 0; +let r = 0; + +v.onChange = + old_min.onChange = + old_max.onChange = + new_min.onChange = + new_max.onChange = exec; + +exec(); + +easing.onChange = function () +{ + if (easing.get() == "Smoothstep") ease = 1; + else if (easing.get() == "Smootherstep") ease = 2; + else ease = 0; +}; + +function exec() +{ + const nMin = new_min.get(); + const nMax = new_max.get(); + const oMin = old_min.get(); + const oMax = old_max.get(); + let x = v.get(); + + if (x >= Math.max(oMax, oMin)) + { + result.set(nMax); + return; + } + else + if (x <= Math.min(oMax, oMin)) + { + result.set(nMin); + return; + } + + let reverseInput = false; + const oldMin = Math.min(oMin, oMax); + const oldMax = Math.max(oMin, oMax); + if (oldMin != oMin) reverseInput = true; + + let reverseOutput = false; + const newMin = Math.min(nMin, nMax); + const newMax = Math.max(nMin, nMax); + if (newMin != nMin) reverseOutput = true; + + let portion = 0; + + if (reverseInput) portion = (oldMax - x) * (newMax - newMin) / (oldMax - oldMin); + else portion = (x - oldMin) * (newMax - newMin) / (oldMax - oldMin); + + if (reverseOutput) r = newMax - portion; + else r = portion + newMin; + + if (ease === 0) + { + result.set(r); + } + else + if (ease == 1) + { + x = Math.max(0, Math.min(1, (r - nMin) / (nMax - nMin))); + result.set(nMin + x * x * (3 - 2 * x) * (nMax - nMin)); // smoothstep + } + else + if (ease == 2) + { + x = Math.max(0, Math.min(1, (r - nMin) / (nMax - nMin))); + result.set(nMin + x * x * x * (x * (x * 6 - 15) + 10) * (nMax - nMin)); // smootherstep + } +} + + +}; + +Ops.Math.MapRange.prototype = new CABLES.Op(); +CABLES.OPS["2617b407-60a0-4ff6-b4a7-18136cfa7817"]={f:Ops.Math.MapRange,objName:"Ops.Math.MapRange"}; + + + + +// ************************************************************** +// +// Ops.Math.Math +// +// ************************************************************** + +Ops.Math.Math = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const num0 = op.inFloat("number 0", 0), + num1 = op.inFloat("number 1", 0), + mathDropDown = op.inSwitch("math mode", ["+", "-", "*", "/", "%", "min", "max"], "+"), + result = op.outNumber("result"); + +let mathFunc; + +num0.onChange = num1.onChange = update; +mathDropDown.onChange = onFilterChange; + +let n0 = 0; +let n1 = 0; + +const mathFuncAdd = function (a, b) { return a + b; }; +const mathFuncSub = function (a, b) { return a - b; }; +const mathFuncMul = function (a, b) { return a * b; }; +const mathFuncDiv = function (a, b) { return a / b; }; +const mathFuncMod = function (a, b) { return a % b; }; +const mathFuncMin = function (a, b) { return Math.min(a, b); }; +const mathFuncMax = function (a, b) { return Math.max(a, b); }; + +function onFilterChange() +{ + let mathSelectValue = mathDropDown.get(); + + if (mathSelectValue == "+") mathFunc = mathFuncAdd; + else if (mathSelectValue == "-") mathFunc = mathFuncSub; + else if (mathSelectValue == "*") mathFunc = mathFuncMul; + else if (mathSelectValue == "/") mathFunc = mathFuncDiv; + else if (mathSelectValue == "%") mathFunc = mathFuncMod; + else if (mathSelectValue == "min") mathFunc = mathFuncMin; + else if (mathSelectValue == "max") mathFunc = mathFuncMax; + update(); + op.setUiAttrib({ "extendTitle": mathSelectValue }); +} + +function update() +{ + n0 = num0.get(); + n1 = num1.get(); + + result.set(mathFunc(n0, n1)); +} + +onFilterChange(); + + +}; + +Ops.Math.Math.prototype = new CABLES.Op(); +CABLES.OPS["e9fdcaca-a007-4563-8a4d-e94e08506e0f"]={f:Ops.Math.Math,objName:"Ops.Math.Math"}; + + + + +// ************************************************************** +// +// Ops.Math.MathExpression +// +// ************************************************************** + +Ops.Math.MathExpression = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inA = op.inFloat("A", 0); +const inB = op.inFloat("B", 1); +const inC = op.inFloat("C", 2); +const inD = op.inFloat("D", 3); +op.setPortGroup("Parameters", [inA, inB, inC, inD]); +const inExpression = op.inString("Expression", "a*(b+c+d)"); +op.setPortGroup("Expression", [inExpression]); +const outResult = op.outNumber("Result"); +const outExpressionIsValid = op.outBool("Expression Valid"); + +let currentFunction = inExpression.get(); +let functionValid = false; + +const createFunction = () => +{ + try + { + currentFunction = new Function("m", "a", "b", "c", "d", `with(m) { return ${inExpression.get()} }`); + functionValid = true; + evaluateFunction(); + outExpressionIsValid.set(functionValid); + } + catch (e) + { + functionValid = false; + outExpressionIsValid.set(functionValid); + if (e instanceof ReferenceError || e instanceof SyntaxError) return; + } +}; + +const evaluateFunction = () => +{ + if (functionValid) + { + outResult.set(currentFunction(Math, inA.get(), inB.get(), inC.get(), inD.get())); + if (!inExpression.get()) outResult.set(0); + } + + outExpressionIsValid.set(functionValid); +}; + + +inA.onChange = inB.onChange = inC.onChange = inD.onChange = evaluateFunction; +inExpression.onChange = createFunction; +createFunction(); + + +}; + +Ops.Math.MathExpression.prototype = new CABLES.Op(); +CABLES.OPS["d2343a1e-64ea-45b2-99ed-46e167bbdcd3"]={f:Ops.Math.MathExpression,objName:"Ops.Math.MathExpression"}; + + + + +// ************************************************************** +// +// Ops.Math.Max +// +// ************************************************************** + +Ops.Math.Max = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + value = op.inValueFloat("value", 1), + max = op.inValueFloat("Maximum", 1), + result = op.outNumber("result"); + +max.onChange = + value.onChange = exec; + +exec(); + +function exec() +{ + let v = Math.max(value.get(), max.get()); + if (v == v) result.set(v); +} + + +}; + +Ops.Math.Max.prototype = new CABLES.Op(); +CABLES.OPS["07f0be49-c226-4029-8039-3b620145dc2a"]={f:Ops.Math.Max,objName:"Ops.Math.Max"}; + + + + +// ************************************************************** +// +// Ops.Math.MaxSinceReset +// +// ************************************************************** + +Ops.Math.MaxSinceReset = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const valuePort = op.inValue("Value"); +const resetPort = op.inTriggerButton("Reset"); +const maxPort = op.outNumber("Maximum"); + +let first; +let lastMax; + +// change listeners +resetPort.onTriggered = reset; +valuePort.onChange = update; + +// init +reset(); + +/** + * On Value change + */ +function update() +{ + let value = valuePort.get(); + if (first) + { + maxPort.set(value); + lastMax = value; + } + else + { + lastMax = Math.max(lastMax, value); + maxPort.set(lastMax); + } + first = false; +} + +/** + * On Reset + */ +function reset() +{ + first = true; +} + + +}; + +Ops.Math.MaxSinceReset.prototype = new CABLES.Op(); +CABLES.OPS["d3fdb4e1-199d-487a-a719-1315aa49fcfb"]={f:Ops.Math.MaxSinceReset,objName:"Ops.Math.MaxSinceReset"}; + + + + +// ************************************************************** +// +// Ops.Math.MercatorCoord +// +// ************************************************************** + +Ops.Math.MercatorCoord = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inLat = op.inValue("Latitude"), + inLon = op.inValue("Longitude"), + inMapWidth = op.inValue("MapWidth", 1), + outX = op.outNumber("X"), + outY = op.outNumber("Y"); + +inLat.onChange = + inLon.onChange = + inMapWidth.onChange = update; + + +function update() +{ + let mapWidth = inMapWidth.get();// 1.289672544080605; + let mapHeight = 1; + + let latitude = inLat.get(); + let longitude = inLon.get(); + // get x value + let x = (longitude + 180) * (mapWidth / 360); + + // convert from degrees to radians + let latRad = latitude * Math.PI / 180; + + // get y value + let mercN = Math.log(Math.tan((Math.PI / 4) + (latRad / 2))); + let y = (mapHeight / 2) - (mapWidth * mercN / (2 * Math.PI)); + + // x-=mapWidth/2; + + outX.set(x); + outY.set(0 - y); +} + + +}; + +Ops.Math.MercatorCoord.prototype = new CABLES.Op(); +CABLES.OPS["d95c5ba8-ed6e-4147-bf5f-8e44be3ef6cf"]={f:Ops.Math.MercatorCoord,objName:"Ops.Math.MercatorCoord"}; + + + + +// ************************************************************** +// +// Ops.Math.MercatorCoordsArray +// +// ************************************************************** + +Ops.Math.MercatorCoordsArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("LatLon Array"), + inMapWidth = op.inValue("MapWidth", 100), + inCenterLat = op.inValue("Center Lat", 0), + inCenterLon = op.inValue("Center Lon", 0), + outArr = op.outArray("Result"); + +inArr.onChange = + inMapWidth.onChange = + // inMapHeight.onChange= + inCenterLat.onChange = + inCenterLon.onChange = update; + +function calcLon(lon, mapWidth) +{ + const x = (lon + 180) * (mapWidth / 360); + return x; +} + +function calcLat(lat, mapWidth, mapHeight) +{ + const latRad = lat * Math.PI / 180; + + // get y value + const mercN = Math.log(Math.tan((Math.PI / 4) + (latRad / 2))); + const y = (mapHeight / 2) - (mapWidth * mercN / (2 * Math.PI)); + return y; +} + +function update() +{ + const mapWidth = inMapWidth.get();// 1.289672544080605; + const mapHeight = 1;// inMapHeight.get();// 1; + + const centerLon = calcLon(inCenterLon.get(), mapWidth, mapHeight); + const centerLat = calcLat(inCenterLat.get(), mapWidth, mapHeight); + + const arr = inArr.get(); + + if (!arr) + { + outArr.set(null); + return; + } + + const newArray = []; + + for (let i = 0; i < arr.length; i += 2) + { + const latitude = arr[i];// inLat.get(); + const longitude = arr[i + 1];// inLon.get(); + + let lon = calcLon(longitude, mapWidth, mapHeight); + let lat = calcLat(latitude, mapWidth, mapHeight); + + // convert from degrees to radians + // x-=mapWidth/2; + + lon -= centerLon; + lat -= centerLat; + + newArray.push(lon, 0 - lat); + } + + outArr.set(null); + outArr.set(newArray); +} + + +}; + +Ops.Math.MercatorCoordsArray.prototype = new CABLES.Op(); +CABLES.OPS["96e77c88-58b8-4948-bd61-c95d604a2607"]={f:Ops.Math.MercatorCoordsArray,objName:"Ops.Math.MercatorCoordsArray"}; + + + + +// ************************************************************** +// +// Ops.Math.MinSinceReset +// +// ************************************************************** + +Ops.Math.MinSinceReset = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let valuePort = op.inValue("Value"); +let resetPort = op.inTriggerButton("Reset"); +let minPort = op.outNumber("Minimum"); + +let first; +let lastMin; + +resetPort.onTriggered = reset; +valuePort.onChange = update; + +reset(); + +function update() +{ + let value = valuePort.get(); + if (first) + { + minPort.set(value); + lastMin = value; + } + else + { + lastMin = Math.min(lastMin, value); + minPort.set(lastMin); + } + first = false; +} + +function reset() +{ + first = true; +} + + +}; + +Ops.Math.MinSinceReset.prototype = new CABLES.Op(); +CABLES.OPS["30da693f-70bd-46bf-97d5-9f31e7f76bba"]={f:Ops.Math.MinSinceReset,objName:"Ops.Math.MinSinceReset"}; + + + + +// ************************************************************** +// +// Ops.Math.Min_v3 +// +// ************************************************************** + +Ops.Math.Min_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val1 = op.inValue("Value 1", 1), + val2 = op.inValue("Value 2", 2), + result = op.outNumber("result"); + +val1.onChange = + val2.onChange = exec; + +exec(); + +function exec() +{ + let v = Math.min(val1.get(), val2.get()); + result.set(v); +} + + +}; + +Ops.Math.Min_v3.prototype = new CABLES.Op(); +CABLES.OPS["24a9062d-380c-4690-8fe7-6703787fa94c"]={f:Ops.Math.Min_v3,objName:"Ops.Math.Min_v3"}; + + + + +// ************************************************************** +// +// Ops.Math.Modulo +// +// ************************************************************** + +Ops.Math.Modulo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValueFloat("number1", 1), + number2 = op.inValueFloat("number2", 2), + pingpong = op.inValueBool("pingpong"), + result = op.outNumber("result"); + +let calculateFunction = calculateModule; + +number1.onChange = +number2.onChange = exec; + +pingpong.onChange = updatePingPong; + +exec(); + +function exec() +{ + let n2 = number2.get(); + let n1 = number1.get(); + + result.set(calculateFunction(n1, n2)); +} + +function calculateModule(n1, n2) +{ + let re = ((n1 % n2) + n2) % n2; + if (re != re) re = 0; + return re; +} + +function calculatePingPong(i, n) +{ + let cycle = 2 * n; + i %= cycle; + if (i >= n) return cycle - i; + else return i; +} + +function updatePingPong() +{ + if (pingpong.get()) calculateFunction = calculatePingPong; + else calculateFunction = calculateModule; +} + + +}; + +Ops.Math.Modulo.prototype = new CABLES.Op(); +CABLES.OPS["ebc13b25-3705-4265-8f06-5f985b6a7bb1"]={f:Ops.Math.Modulo,objName:"Ops.Math.Modulo"}; + + + + +// ************************************************************** +// +// Ops.Math.MulMatrixXyz +// +// ************************************************************** + +Ops.Math.MulMatrixXyz = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Update"), + inX = op.inFloat("X", 0), + inY = op.inFloat("Y", 0), + inZ = op.inFloat("Z", 0), + inMat = op.inArray("Matrix"), + outNext = op.outTrigger("Next"), + outX = op.outNumber("Result X"), + outY = op.outNumber("Result Y"), + outZ = op.outNumber("Result Z"); + +let theArr = []; +let vec = vec4.create(); + +exec.onTriggered = function () +{ + const mMat = inMat.get(); + + if (!mMat) return; + + vec4.set(vec, inX.get(), inY.get(), inZ.get(), 1); + + vec3.transformMat4(vec, vec, mMat); + + outX.set(vec[0]); + outY.set(vec[1]); + outZ.set(vec[2]); + + outNext.trigger(); +}; + + +}; + +Ops.Math.MulMatrixXyz.prototype = new CABLES.Op(); +CABLES.OPS["69cc5915-fd7f-4c6c-9a8c-8553c6bab0c5"]={f:Ops.Math.MulMatrixXyz,objName:"Ops.Math.MulMatrixXyz"}; + + + + +// ************************************************************** +// +// Ops.Math.Multiply +// +// ************************************************************** + +Ops.Math.Multiply = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValueFloat("number1", 1), + number2 = op.inValueFloat("number2", 2), + result = op.outNumber("result"); + +op.setTitle("*"); + +number1.onChange = number2.onChange = update; +update(); + +function update() +{ + const n1 = number1.get(); + const n2 = number2.get(); + + result.set(n1 * n2); +} + + +}; + +Ops.Math.Multiply.prototype = new CABLES.Op(); +CABLES.OPS["1bbdae06-fbb2-489b-9bcc-36c9d65bd441"]={f:Ops.Math.Multiply,objName:"Ops.Math.Multiply"}; + + + + +// ************************************************************** +// +// Ops.Math.NumberDivisible +// +// ************************************************************** + +Ops.Math.NumberDivisible = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + num = op.inValue("Number"), + divisor = op.inValue("Divisor"), + result = op.outBoolNum("Result"); + +num.onChange = divisor.onChange = function () +{ + result.set(num.get() % divisor.get() === 0); +}; + + +}; + +Ops.Math.NumberDivisible.prototype = new CABLES.Op(); +CABLES.OPS["4059e4ee-da93-490a-a00c-6fe0f0ee8efc"]={f:Ops.Math.NumberDivisible,objName:"Ops.Math.NumberDivisible"}; + + + + +// ************************************************************** +// +// Ops.Math.OneMinus +// +// ************************************************************** + +Ops.Math.OneMinus = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inValue = op.inValue("Value"), + result = op.outNumber("Result"); + +inValue.onChange = update; +update(); + +function update() +{ + result.set(1 - inValue.get()); +} + + +}; + +Ops.Math.OneMinus.prototype = new CABLES.Op(); +CABLES.OPS["f34d019d-59ae-40d6-a55d-a7691bbc40e0"]={f:Ops.Math.OneMinus,objName:"Ops.Math.OneMinus"}; + + + + +// ************************************************************** +// +// Ops.Math.PerlinNoise_v2 +// +// ************************************************************** + +Ops.Math.PerlinNoise_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inX = op.inFloat("X", 0), + inY = op.inFloat("Y", 0), + inZ = op.inFloat("Z", 0), + inScale = op.inFloat("Scale", 4), + inSeed = op.inFloat("Seed", 1), + + result = op.outNumber("Result"); + +const PERLIN_YWRAPB = 4; +const PERLIN_YWRAP = 1 << PERLIN_YWRAPB; +const PERLIN_ZWRAPB = 8; +const PERLIN_ZWRAP = 1 << PERLIN_ZWRAPB; +const PERLIN_SIZE = 4095; + +let perlin_octaves = 4; // default to medium smooth +let perlin_amp_falloff = 0.5; // 50% reduction/octave + +const scaled_cosine = (i) => 0.5 * (1.0 - Math.cos(i * Math.PI)); + +let perlin = null; + +inX.onChange = + inY.onChange = + inZ.onChange = update; + +function update() +{ + let s = inScale.get(); + let r = noise(inX.get() * s, inY.get() * s, inZ.get() * s); + result.set(r); +} + +function noise(x, y = 0, z = 0) +{ + if (perlin == null) + { + perlin = new Array(PERLIN_SIZE + 1); + for (let i = 0; i < PERLIN_SIZE + 1; i++) + { + perlin[i] = Math.random(); + } + } + + if (x < 0) + { + x = -x; + } + if (y < 0) + { + y = -y; + } + if (z < 0) + { + z = -z; + } + + let xi = Math.floor(x), + yi = Math.floor(y), + zi = Math.floor(z); + let xf = x - xi; + let yf = y - yi; + let zf = z - zi; + let rxf, ryf; + + let r = 0; + let ampl = 0.5; + + let n1, n2, n3; + + for (let o = 0; o < perlin_octaves; o++) + { + let of = xi + (yi << PERLIN_YWRAPB) + (zi << PERLIN_ZWRAPB); + + rxf = scaled_cosine(xf); + ryf = scaled_cosine(yf); + + n1 = perlin[of & PERLIN_SIZE]; + n1 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n1); + n2 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE]; + n2 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n2); + n1 += ryf * (n2 - n1); + + of += PERLIN_ZWRAP; + n2 = perlin[of & PERLIN_SIZE]; + n2 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n2); + n3 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE]; + n3 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n3); + n2 += ryf * (n3 - n2); + + n1 += scaled_cosine(zf) * (n2 - n1); + + r += n1 * ampl; + ampl *= perlin_amp_falloff; + xi <<= 1; + xf *= 2; + yi <<= 1; + yf *= 2; + zi <<= 1; + zf *= 2; + + if (xf >= 1.0) + { + xi++; + xf--; + } + if (yf >= 1.0) + { + yi++; + yf--; + } + if (zf >= 1.0) + { + zi++; + zf--; + } + } + return r; +} + +inSeed.onChange = () => +{ + Math.randomSeed = inSeed.get(); + perlin = new Array(PERLIN_SIZE + 1); + for (let i = 0; i < PERLIN_SIZE + 1; i++) + { + perlin[i] = Math.seededRandom(); + } +}; + + +}; + +Ops.Math.PerlinNoise_v2.prototype = new CABLES.Op(); +CABLES.OPS["b1a19b32-4ccd-4bce-a37d-e386e53dfc1c"]={f:Ops.Math.PerlinNoise_v2,objName:"Ops.Math.PerlinNoise_v2"}; + + + + +// ************************************************************** +// +// Ops.Math.Pi +// +// ************************************************************** + +Ops.Math.Pi = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + multiply = op.inValueFloat("Multiply amount", 1.0), + p = op.outNumber("Pi", Math.PI); + +multiply.onChange = function () +{ + p.setValue(Math.PI * multiply.get()); +}; + + +}; + +Ops.Math.Pi.prototype = new CABLES.Op(); +CABLES.OPS["311e8179-9a7c-43de-9eb2-84577d702974"]={f:Ops.Math.Pi,objName:"Ops.Math.Pi"}; + + + + +// ************************************************************** +// +// Ops.Math.PointInRectangle2d +// +// ************************************************************** + +Ops.Math.PointInRectangle2d = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + x = op.inValue("x"), + y = op.inValue("y"), + + rtop = op.inValue("rect top"), + rleft = op.inValue("rect left"), + rright = op.inValue("rect right"), + rbottom = op.inValue("rect bottom"), + + result = op.outBoolNum("Result"), + outX = op.outNumber("Pos x"), + outY = op.outNumber("Pos y"); + +x.onChange = y.onChange = function () +{ + let isIn = (x.get() > rleft.get() && x.get() < rright.get() && y.get() > rtop.get() && y.get() < rbottom.get()); + + outX.set(Math.max(0, Math.min(1.0, (x.get() - rleft.get()) / (rright.get() - rleft.get())))); + outY.set(Math.max(0, Math.min(1.0, (y.get() - rtop.get()) / (rbottom.get() - rtop.get())))); + + result.set(isIn == true); +}; + + +}; + +Ops.Math.PointInRectangle2d.prototype = new CABLES.Op(); +CABLES.OPS["6d722288-0fec-43da-bf41-0372426fb685"]={f:Ops.Math.PointInRectangle2d,objName:"Ops.Math.PointInRectangle2d"}; + + + + +// ************************************************************** +// +// Ops.Math.Pow +// +// ************************************************************** + +Ops.Math.Pow = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + base = op.inValueFloat("Base"), + exponent = op.inValueFloat("Exponent"), + result = op.outNumber("Result"); + +exponent.set(2); + +base.onChange = update; +exponent.onChange = update; + +function update() +{ + let r = Math.pow(base.get(), exponent.get()); + if (isNaN(r))r = 0; + result.set(r); +} + + +}; + +Ops.Math.Pow.prototype = new CABLES.Op(); +CABLES.OPS["3bb3f98f-27d6-44c4-b4e5-186e10f0809d"]={f:Ops.Math.Pow,objName:"Ops.Math.Pow"}; + + + + +// ************************************************************** +// +// Ops.Math.PowerOfTwoSize +// +// ************************************************************** + +Ops.Math.PowerOfTwoSize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inW = op.inValueInt("Width"), + inH = op.inValueInt("Height"), + inStrat = op.inValueSelect("Strategy", ["floor", "floor/2", "ceil"], "floor"), + outW = op.outNumber("Width Result"), + outH = op.outNumber("Height Result"); +inStrat.onChange = updateStrategy; +inW.onChange = inH.onChange = update; +let getPOT = null; +updateStrategy(); + +function isPOT(x) +{ + return (x == 1 || x == 2 || x == 4 || x == 8 || x == 16 || x == 32 || x == 64 || x == 128 || x == 256 || x == 512 || x == 1024 || x == 2048 || x == 4096 || x == 8192 || x == 16384); +} + +function updateStrategy() +{ + let s = inStrat.get(); + + if (s == "floor")getPOT = getPotNextfloor; + if (s == "floor/2")getPOT = getPotNextfloorx2; + if (s == "ceil")getPOT = getPotNextBigger; + if (s == "nearest")getPOT = getPotNearest; + + update(); +} + +function getPotNextBigger(x) +{ + // if(x>8192)return 16384; + // if(x>4096)return 8129; + if (x > 2048) return 4096; + if (x > 1024) return 2048; + if (x > 512) return 1024; + if (x > 256) return 512; + if (x > 128) return 256; + if (x > 64) return 128; + if (x > 32) return 64; + if (x > 16) return 32; + if (x > 8) return 16; + if (x > 4) return 8; + if (x > 2) return 4; +} + +function getPotNextfloorx2(x) +{ + return Math.ceil(getPotNextfloor(x) / 2); +} + +function getPotNextfloor(x) +{ + if (x < 2) return 1; + if (x < 4) return 2; + if (x < 8) return 4; + if (x < 16) return 8; + if (x < 32) return 16; + if (x < 64) return 32; + if (x < 128) return 64; + if (x < 256) return 128; + if (x < 512) return 256; + if (x < 1024) return 512; + if (x < 2048) return 1024; + if (x < 4096) return 2048; + if (x < 8192) return 4096; + // if(x<16384)return 8192; +} + +function getPotNearest(x) +{ + if (x > 3072) return 4096; + if (x > 1536) return 2048; + if (x > 768) return 1024; + if (x > 320) return 512; + if (x > 191) return 256; + if (x > 95) return 128; + if (x > 47) return 64; + if (x > 23) return 32; + if (x > 11) return 16; + if (x > 5) return 8; + if (x > 3) return 4; + return 2; +} + +function update() +{ + let w = inW.get(); + let h = inH.get(); + + if (!isPOT(w)) w = getPOT(w); + if (!isPOT(h)) h = getPOT(h); + + outW.set(w); + outH.set(h); +} + + +}; + +Ops.Math.PowerOfTwoSize.prototype = new CABLES.Op(); +CABLES.OPS["58e01e34-0f42-4861-ad9a-ed96e08f8565"]={f:Ops.Math.PowerOfTwoSize,objName:"Ops.Math.PowerOfTwoSize"}; + + + + +// ************************************************************** +// +// Ops.Math.Radians +// +// ************************************************************** + +Ops.Math.Radians = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + value = op.inValue("Degrees"), + result = op.outNumber("Result"), + calculate = Math.cos; + +// convert degrees into radians +value.onChange = function () +{ + result.set( + value.get() * Math.PI / 180 + ); +}; + + +}; + +Ops.Math.Radians.prototype = new CABLES.Op(); +CABLES.OPS["d6daab6e-791e-4c4c-ac01-d887447de4f9"]={f:Ops.Math.Radians,objName:"Ops.Math.Radians"}; + + + + +// ************************************************************** +// +// Ops.Math.RandomCounter +// +// ************************************************************** + +Ops.Math.RandomCounter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inCount=op.inTriggerButton("Count"), + inMin=op.inFloat("Step Min",0.5), + inMax=op.inFloat("Step Max",1), + outNum=op.outNumber("Result"); + +inCount.onTriggered=count; + +let v=0; + + +function count() +{ + + let r=Math.seededRandom() * (inMax.get() - inMin.get()) + inMin.get(); + + if(Math.seededRandom()>0.5) v+=r; + else v-=r; + + outNum.set(v); + +} + +}; + +Ops.Math.RandomCounter.prototype = new CABLES.Op(); +CABLES.OPS["48c712f0-bb8e-4a0b-9b97-26da68a68223"]={f:Ops.Math.RandomCounter,objName:"Ops.Math.RandomCounter"}; + + + + +// ************************************************************** +// +// Ops.Math.RandomNumbers_v3 +// +// ************************************************************** + +Ops.Math.RandomNumbers_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inSeed = op.inValueFloat("Seed", 1), + min = op.inValueFloat("Min", 0), + max = op.inValueFloat("Max", 1), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outZ = op.outNumber("Z"), + outW = op.outNumber("W"); + +inSeed.onChange = + min.onChange = + max.onChange = update; +update(); + +function update() +{ + const inMin = min.get(); + const inMax = max.get(); + Math.randomSeed = Math.abs(inSeed.get() || 0) * 571.1 + 1.0; + outX.set(Math.seededRandom() * (inMax - inMin) + inMin); + outY.set(Math.seededRandom() * (inMax - inMin) + inMin); + outZ.set(Math.seededRandom() * (inMax - inMin) + inMin); + outW.set(Math.seededRandom() * (inMax - inMin) + inMin); +} + + +}; + +Ops.Math.RandomNumbers_v3.prototype = new CABLES.Op(); +CABLES.OPS["d2b970e1-9406-4459-995c-5a594acd88e3"]={f:Ops.Math.RandomNumbers_v3,objName:"Ops.Math.RandomNumbers_v3"}; + + + + +// ************************************************************** +// +// Ops.Math.RotationFromNormal +// +// ************************************************************** + +Ops.Math.RotationFromNormal = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inNormalX = op.inValue("Normal X"); +const inNormalY = op.inValue("Normal Y"); +const inNormalZ = op.inValue("Normal Z"); +const inRecalculate = op.inTriggerButton("recalculate"); + +const outRotation = op.outArray("RotationMatrix"); + +const identMat = mat4.create(); +const outMat = mat4.create(); +const upVec = vec3.fromValues(0, 1, 0); +const tmpVec = vec3.create(); +const tmpQuat = quat.create(); + +outRotation.set(identMat); + +inRecalculate.onTriggered = function () +{ + vec3.set( + tmpVec, + inNormalX.get(), + inNormalY.get(), + inNormalZ.get() + ); + + vec3.normalize(tmpVec, tmpVec); + quat.identity(tmpQuat); + quat.rotationTo(tmpQuat, upVec, tmpVec); + mat4.fromQuat(outMat, tmpQuat); + outRotation.set(identMat); + outRotation.set(outMat); +}; + + +}; + +Ops.Math.RotationFromNormal.prototype = new CABLES.Op(); +CABLES.OPS["9b39dd02-9558-4297-ba8a-8f21f690d128"]={f:Ops.Math.RotationFromNormal,objName:"Ops.Math.RotationFromNormal"}; + + + + +// ************************************************************** +// +// Ops.Math.Round +// +// ************************************************************** + +Ops.Math.Round = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValueFloat("number"), + decPlaces = op.inInt("Decimal Places", 0), + result = op.outNumber("result"); + +let decm = 0; + +number1.onChange = exec; +decPlaces.onChange = updateDecm; + +updateDecm(); + +function updateDecm() +{ + decm = Math.pow(10, decPlaces.get()); + exec(); +} + +function exec() +{ + result.set(Math.round(number1.get() * decm) / decm); +} + + +}; + +Ops.Math.Round.prototype = new CABLES.Op(); +CABLES.OPS["1a1ef636-6d02-42ba-ae1e-627b917d0d2b"]={f:Ops.Math.Round,objName:"Ops.Math.Round"}; + + + + +// ************************************************************** +// +// Ops.Math.RoundEven +// +// ************************************************************** + +Ops.Math.RoundEven = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inNum = op.inValueFloat("Number"), + inMode = op.inSwitch("Mode", ["Ceil", "Floor"], "Ceil"), + result = op.outNumber("Result"); + +inMode.onChange = inNum.onChange = function () +{ + let value = 0; + switch (inMode.get()) + { + case "Floor": + value = 2 * (Math.floor(inNum.get() / 2.0)); + break; + default: + case "Ceil": + value = 2 * (Math.round(inNum.get() / 2.0)); + break; + } + result.set(value); +}; + + +}; + +Ops.Math.RoundEven.prototype = new CABLES.Op(); +CABLES.OPS["b4c116ba-ab64-4903-80bb-35c8d65819a1"]={f:Ops.Math.RoundEven,objName:"Ops.Math.RoundEven"}; + + + + +// ************************************************************** +// +// Ops.Math.SchlickBias +// +// ************************************************************** + +Ops.Math.SchlickBias = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inX=op.inFloat("Value",0), + inGain=op.inFloatSlider("Gain",0.5), + inBias=op.inFloatSlider("Bias",0.5), + result=op.outNumber("Result"); + +inX.onChange= +inBias.onChange= +inGain.onChange=getGain; + +function getBias(x,bias) +{ + const r= x / ((1 / bias - 2) * (1 - x) + 1); + return r; +} + +function getGain() +{ + const gain=inGain.get(); + const x=inX.get(); + let r= x < 0.5 ? getBias(x * 2.0, gain)/2.0 : getBias(x * 2.0 - 1.0,1.0 - gain)/2.0 + 0.5; + + r=getBias(r,inBias.get()); + + + result.set(r); + +} + + + + +}; + +Ops.Math.SchlickBias.prototype = new CABLES.Op(); +CABLES.OPS["ea957aae-c9c7-4d2a-a227-c2a10042f554"]={f:Ops.Math.SchlickBias,objName:"Ops.Math.SchlickBias"}; + + + + +// ************************************************************** +// +// Ops.Math.Sign +// +// ************************************************************** + +Ops.Math.Sign = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + value = op.inValue("Value"), + noZeroIn = op.inBool("Remove zero", false), + result = op.outNumber("Result"); + +value.onChange = function () +{ + let direction = 0; + let val = value.get() * 999; + let noZero = noZeroIn.get(); + + if (!noZero) + { + direction = Math.round(Math.max(Math.min(val, 1), -1)); + } + else + { + direction = (val < 0) ? -1 : 1; + } + result.set(direction); +}; + + +}; + +Ops.Math.Sign.prototype = new CABLES.Op(); +CABLES.OPS["a0d6ed0e-9b6b-4d84-a599-882feeadd769"]={f:Ops.Math.Sign,objName:"Ops.Math.Sign"}; + + + + +// ************************************************************** +// +// Ops.Math.SimpleMovingAverage +// +// ************************************************************** + +Ops.Math.SimpleMovingAverage = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val = op.inValue("Value"), + num = op.inValueInt("Number of Values", 10), + result = op.outNumber("Result"); + +val.changeAlways = true; +let buffer = []; +let index = 0; + +num.onChange = init; +init(); + +function init() +{ + let n = Math.abs(Math.floor(num.get())); + buffer.length = n; + + for (let i = 0; i < buffer.length; i++)buffer[i] = null; + index = 0; +} + +val.onChange = function () +{ + index++; + if (index >= buffer.length)index = 0; + buffer[index] = val.get(); + + let avg = 0; + let divide = 0; + for (let i = 0; i < buffer.length; i++) + { + if (buffer[i] !== null) // ignore zeroes + { + avg += buffer[i]; + divide++; + } + } + + result.set(avg / divide || 1); +}; + + +}; + +Ops.Math.SimpleMovingAverage.prototype = new CABLES.Op(); +CABLES.OPS["b28c1e66-1c88-4394-bf73-327c0e82ea34"]={f:Ops.Math.SimpleMovingAverage,objName:"Ops.Math.SimpleMovingAverage"}; + + + + +// ************************************************************** +// +// Ops.Math.Sine +// +// ************************************************************** + +Ops.Math.Sine = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + value = op.inValue("value"), + phase = op.inValue("phase", 0.0), + mul = op.inValue("frequency", 1.0), + amplitude = op.inValue("amplitude", 1.0), + invert = op.inValueBool("asine", false), + result = op.outNumber("result"); + +let calculate = Math.sin; + +mul.onChange = +amplitude.onChange = +phase.onChange = +value.onChange = function () +{ + result.set( + amplitude.get() * calculate((value.get() * mul.get()) + phase.get()) + ); +}; + +invert.onChange = function () +{ + if (invert.get()) calculate = Math.asin; + else calculate = Math.sin; +}; + + +}; + +Ops.Math.Sine.prototype = new CABLES.Op(); +CABLES.OPS["d24da018-9f3d-428b-85c9-6ff14d77548b"]={f:Ops.Math.Sine,objName:"Ops.Math.Sine"}; + + + + +// ************************************************************** +// +// Ops.Math.SmoothStep_v2 +// +// ************************************************************** + +Ops.Math.SmoothStep_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + result = op.outNumber("result"), + number = op.inValueFloat("number", 0), + min = op.inValueFloat("min", 0), + max = op.inValueFloat("max", 1); + +number.onChange = max.onChange = min.onChange = exec; +exec(); + +function exec() +{ + let x = Math.max(0, Math.min(1, (number.get() - min.get()) / (max.get() - min.get()))); + result.set(x * x * (3 - 2) * (max.get() - min.get())); // smoothstep +} + + +}; + +Ops.Math.SmoothStep_v2.prototype = new CABLES.Op(); +CABLES.OPS["b5c41eea-ac30-4ac7-9481-eefe42e8199c"]={f:Ops.Math.SmoothStep_v2,objName:"Ops.Math.SmoothStep_v2"}; + + + + +// ************************************************************** +// +// Ops.Math.SmootherStep +// +// ************************************************************** + +Ops.Math.SmootherStep = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val = op.inValueFloat("val", 0), + min = op.inValueFloat("min", 0), + max = op.inValueFloat("max", 1), + result = op.outNumber("result"); + +val.onChange = max.onChange = min.onChange = exec; +exec(); + +function exec() +{ + let x = Math.max(0, Math.min(1, (val.get() - min.get()) / (max.get() - min.get()))); + result.set(x * x * x * (x * (x * 6 - 15) + 10) * (max.get() - min.get())); // smootherstep +} + + +}; + +Ops.Math.SmootherStep.prototype = new CABLES.Op(); +CABLES.OPS["c66da84f-ff2f-45de-b3c2-557bdf1cb946"]={f:Ops.Math.SmootherStep,objName:"Ops.Math.SmootherStep"}; + + + + +// ************************************************************** +// +// Ops.Math.Speed +// +// ************************************************************** + +Ops.Math.Speed = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExe = op.inTrigger("Update"), + inVal = op.inValue("Value"), + result = op.outNumber("Speed"); + +inVal.alwaysChange = true; + +let lastVal = 0; +let lastTime = CABLES.now(); +inExe.onTriggered = update; + +function update() +{ + let diff = Math.abs(inVal.get() - lastVal); + let diffTime = CABLES.now() - lastTime; + + let speed = diff * (1000 / diffTime); + + result.set(speed); + + lastVal = inVal.get(); + lastTime = CABLES.now(); +} + + +}; + +Ops.Math.Speed.prototype = new CABLES.Op(); +CABLES.OPS["ff6b8c7b-00c6-48f2-9b43-d059e52143fe"]={f:Ops.Math.Speed,objName:"Ops.Math.Speed"}; + + + + +// ************************************************************** +// +// Ops.Math.Sqrt +// +// ************************************************************** + +Ops.Math.Sqrt = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number = op.inValue("number"), + result = op.outNumber("result"); + +number.onChange = function () +{ + let r = Math.sqrt(number.get()); + if (isNaN(r))r = 0; + result.set(r); +}; + + +}; + +Ops.Math.Sqrt.prototype = new CABLES.Op(); +CABLES.OPS["dec567c3-231d-4146-964d-891fde8924f1"]={f:Ops.Math.Sqrt,objName:"Ops.Math.Sqrt"}; + + + + +// ************************************************************** +// +// Ops.Math.Subtract +// +// ************************************************************** + +Ops.Math.Subtract = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValue("number1", 1), + number2 = op.inValue("number2", 1), + result = op.outNumber("result"); + +op.setTitle("-"); + +number1.onChange = + number2.onChange = exec; +exec(); + +function exec() +{ + let v = number1.get() - number2.get(); + if (!isNaN(v)) result.set(v); +} + + +}; + +Ops.Math.Subtract.prototype = new CABLES.Op(); +CABLES.OPS["a4ffe852-d200-4b96-9347-68feb01122ca"]={f:Ops.Math.Subtract,objName:"Ops.Math.Subtract"}; + + + + +// ************************************************************** +// +// Ops.Math.Sum +// +// ************************************************************** + +Ops.Math.Sum = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValueFloat("number1", 1), + number2 = op.inValueFloat("number2", 1), + result = op.outNumber("result"); + +op.setTitle("+"); + +number1.onChange = +number2.onChange = exec; +exec(); + +function exec() +{ + const v = number1.get() + number2.get(); + if (!isNaN(v)) + result.set(v); +} + + +}; + +Ops.Math.Sum.prototype = new CABLES.Op(); +CABLES.OPS["c8fb181e-0b03-4b41-9e55-06b6267bc634"]={f:Ops.Math.Sum,objName:"Ops.Math.Sum"}; + + + + +// ************************************************************** +// +// Ops.Math.Tangent +// +// ************************************************************** + +Ops.Math.Tangent = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + value = op.inValue("Value"), + phase = op.inValue("Phase", 0.0), + mul = op.inValue("Frequency", 1.0), + amplitude = op.inValue("Amplitude", 1.0), + invert = op.inValueBool("asine", false), + result = op.outNumber("Result"); + +let calculate = Math.tan; + +value.onChange = function () +{ + result.set( + amplitude.get() * calculate((value.get() * mul.get()) + phase.get()) + ); +}; + +invert.onChange = function () +{ + if (invert.get()) calculate = Math.atan; + else calculate = Math.tan; +}; + + +}; + +Ops.Math.Tangent.prototype = new CABLES.Op(); +CABLES.OPS["be52d1f0-1004-4161-9ba9-7921c38955c0"]={f:Ops.Math.Tangent,objName:"Ops.Math.Tangent"}; + + + + +// ************************************************************** +// +// Ops.Math.TriggerMathExpression +// +// ************************************************************** + +Ops.Math.TriggerMathExpression = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inExec = op.inTrigger("Calculate"), + inExpression = op.inString("Expression", "x*(y+z)"), + + inX = op.inFloat("X", 0), + inY = op.inFloat("Y", 0), + inZ = op.inFloat("Z", 0), + inW = op.inFloat("W", 0), + + inA = op.inFloat("A", 0), + inB = op.inFloat("B", 0), + inC = op.inFloat("C", 0), + inD = op.inFloat("D", 0), + + inI = op.inFloat("I", 0), + + next = op.outTrigger("Next"), + outResult = op.outNumber("Result"), + outExpressionIsValid = op.outBool("Expression Valid"); + +op.setPortGroup("Parameters", [inA, inB, inC, inD, inX, inY, inZ, inW, inI]); + +let currentFunction = inExpression.get(); +let functionValid = false; + +inExec.onTriggered = evaluateFunction; +inExpression.onChange = createFunction; + +createFunction(); + +function createFunction() +{ + try + { + currentFunction = new Function("m", "a", "b", "c", "d", "x", "y", "z", "w", "i", "with(m) { return " + inExpression.get() + " }"); + + functionValid = true; + evaluateFunction(); + outExpressionIsValid.set(functionValid); + } + catch (e) + { + functionValid = false; + outExpressionIsValid.set(functionValid); + if (e instanceof ReferenceError || e instanceof SyntaxError) return; + } +} + +function evaluateFunction() +{ + if (functionValid) + { + outResult.set(currentFunction(Math, inA.get(), inB.get(), inC.get(), inD.get(), inX.get(), inY.get(), inZ.get(), inW.get(), inI.get())); + if (!inExpression.get()) outResult.set(0); + } + + outExpressionIsValid.set(functionValid); + next.trigger(); +} + + +}; + +Ops.Math.TriggerMathExpression.prototype = new CABLES.Op(); +CABLES.OPS["7ba04c59-5b31-44f9-bd15-865a6db67c81"]={f:Ops.Math.TriggerMathExpression,objName:"Ops.Math.TriggerMathExpression"}; + + + + +// ************************************************************** +// +// Ops.Math.TriggerRandomNumber_v2 +// +// ************************************************************** + +Ops.Math.TriggerRandomNumber_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTriggerButton("Generate"), + min = op.inValue("min", 0), + max = op.inValue("max", 1), + outTrig = op.outTrigger("next"), + result = op.outNumber("result"), + inInteger = op.inValueBool("Integer", false), + noDupe = op.inValueBool("No consecutive duplicates", false); + +op.setPortGroup("Value Range", [min, max]); + +exe.onTriggered = + max.onChange = + min.onChange = + inInteger.onChange = genRandom; + +genRandom(); + +function genRandom() +{ + let r = (Math.random() * (max.get() - min.get())) + min.get(); + + if (inInteger.get())r = randInt(); + + if (min.get() != max.get() && max.get() > min.get()) + while (noDupe.get() && r == result.get()) r = randInt(); + + result.set(r); + outTrig.trigger(); +} + +function randInt() +{ + return Math.floor((Math.random() * ((max.get() - min.get() + 1))) + min.get()); +} + + +}; + +Ops.Math.TriggerRandomNumber_v2.prototype = new CABLES.Op(); +CABLES.OPS["26f446cc-9107-4164-8209-5254487fa132"]={f:Ops.Math.TriggerRandomNumber_v2,objName:"Ops.Math.TriggerRandomNumber_v2"}; + + + + +// ************************************************************** +// +// Ops.Math.VectorLength +// +// ************************************************************** + +Ops.Math.VectorLength = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + x = op.inValue("X"), + y = op.inValue("Y"), + z = op.inValue("Z"), + l = op.outNumber("Length"); + +x.onChange = + y.onChange = + z.onChange = update; + +let vec = vec3.create(); + +function update() +{ + vec3.set(vec, x.get(), y.get(), z.get()); + l.set(vec3.len(vec)); +} + + +}; + +Ops.Math.VectorLength.prototype = new CABLES.Op(); +CABLES.OPS["a9e7bda2-7f57-4df2-83e3-74cdf63371d7"]={f:Ops.Math.VectorLength,objName:"Ops.Math.VectorLength"}; + + + + +// ************************************************************** +// +// Ops.Net.CorsProxy_v2 +// +// ************************************************************** + +Ops.Net.CorsProxy_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let a = op.inString("URL", ""); +let result = op.outString("CORS URL"); +let CORS_CABLES_PROXY = "https://cors.cables.gl/"; + +a.onChange = update; + +update(); + +function update() +{ + result.set(CORS_CABLES_PROXY + a.get()); +} + + +}; + +Ops.Net.CorsProxy_v2.prototype = new CABLES.Op(); +CABLES.OPS["0cac2fb2-cac2-4b50-82c1-f7047ef96e0a"]={f:Ops.Net.CorsProxy_v2,objName:"Ops.Net.CorsProxy_v2"}; + + + + +// ************************************************************** +// +// Ops.Net.WebSocket.WebSocketSend +// +// ************************************************************** + +Ops.Net.WebSocket.WebSocketSend = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inConnection = op.inObject("Connection", null, "Websocket"), + inObject = op.inObject("Object"), + inSend = op.inTriggerButton("Send"), + inStringify = op.inBool("Send String", true), + outSent = op.outBoolNum("Sent"); + +let connection = null; + +inConnection.onChange = function () +{ + connection = inConnection.get(); +}; + +inSend.onTriggered = function () +{ + if (connection && inObject.get()) + { + if (inStringify.get())connection.send(JSON.stringify(inObject.get())); + else connection.send(inObject.get()); + + outSent.set(true); + } + else + { + outSent.set(false); + } +}; + + +}; + +Ops.Net.WebSocket.WebSocketSend.prototype = new CABLES.Op(); +CABLES.OPS["071149e8-5ac5-43dd-a052-e32dbd80cae2"]={f:Ops.Net.WebSocket.WebSocketSend,objName:"Ops.Net.WebSocket.WebSocketSend"}; + + + + +// ************************************************************** +// +// Ops.Net.WebSocket.WebSocket_v2 +// +// ************************************************************** + +Ops.Net.WebSocket.WebSocket_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUrl = op.inString("URL"), + outConnection = op.outObject("Connection", null, "Websocket"), + outResult = op.outObject("Result"), + outConnected = op.outBoolNum("Connected"), + outValidJson = op.outBoolNum("Valid JSON"), + outReceived = op.outTrigger("Received Data"), + outRaw = op.outString("Raw Data"); + +let connection = null; +let timeout = null; +let connectedTo = ""; + +inUrl.onChange = connect; +timeout = setTimeout(checkConnection, 2000); + +inUrl.set(); + +let connecting = false; + +function checkConnection() +{ + if (!outConnected.get() && !connecting) + { + op.log("reconnect websocket..."); + connect(); + } + + timeout = setTimeout(checkConnection, 2000); +} + +op.onDelete = function () +{ + if (outConnected.get())connection.close(); + connecting = false; + clearTimeout(timeout); +}; + +function connect() +{ + op.setUiError("connection", null); + op.setUiError("jsonvalid", null); + + if (outConnected.get() && connectedTo == inUrl.get()) return; + + if (!inUrl.get() || inUrl.get() === "") + { + op.log("websocket: invalid url "); + outConnected.set(false); + return; + } + + window.WebSocket = window.WebSocket || window.MozWebSocket; + + if (!window.WebSocket) + op.logError("Sorry, but your browser doesn't support WebSockets."); + + try + { + connecting = true; + if (connection !== null)connection.close(); + connection = new WebSocket(inUrl.get()); + } + catch (e) + { + op.log("could not connect to", inUrl.get()); + connecting = false; + op.log(e); + } + + if (connection) + { + connection.onerror = function (message) + { + connecting = false; + op.log("ws error"); + outConnected.set(false); + outConnection.set(null); + op.setUiError("connection", "Error connecting to websocket server", 2); + }; + + connection.onclose = function (message) + { + connecting = false; + op.log("ws close"); + outConnected.set(false); + outConnection.set(null); + }; + + connection.onopen = function (message) + { + connecting = false; + outConnected.set(true); + connectedTo = inUrl.get(); + outConnection.set(connection); + }; + + connection.onmessage = function (message) + { + op.setUiError("jsonvalid", null); + outRaw.set(message.data); + try + { + const json = JSON.parse(message.data); + outResult.set(null); + outResult.set(json); + outValidJson.set(true); + } + catch (e) + { + op.log(e); + op.log("This doesn't look like a valid JSON: ", message.data); + op.setUiError("jsonvalid", "Received message was not valid JSON", 0); + outValidJson.set(false); + } + outReceived.trigger(); + }; + } +} + + +}; + +Ops.Net.WebSocket.WebSocket_v2.prototype = new CABLES.Op(); +CABLES.OPS["e747dc72-8214-41ca-9aae-9041f20dd6ac"]={f:Ops.Net.WebSocket.WebSocket_v2,objName:"Ops.Net.WebSocket.WebSocket_v2"}; + + + + +// ************************************************************** +// +// Ops.Patch.CallBack_v2 +// +// ************************************************************** + +Ops.Patch.CallBack_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTriggerButton("exe"); +const callbackname = op.inString("Callback Name", "myFunction"); +const val0 = op.inString("Parameter 1", ""); +const val1 = op.inString("Parameter 2", ""); +const val2 = op.inString("Parameter 3", ""); + +let values = [0, 0, 0]; + +val0.onChange = function () { values[0] = val0.get(); }; +val1.onChange = function () { values[1] = val1.get(); }; +val2.onChange = function () { values[2] = val2.get(); }; + +exe.onTriggered = function () +{ + if (op.patch.config.hasOwnProperty(callbackname.get())) + { + op.patch.config[callbackname.get()](values); + } + else + { + op.log("callback ", callbackname.get(), " not found! Parameters: ", values); + } +}; + + +}; + +Ops.Patch.CallBack_v2.prototype = new CABLES.Op(); +CABLES.OPS["cfc87cb1-a74b-482f-9fad-e1777cb7ffd4"]={f:Ops.Patch.CallBack_v2,objName:"Ops.Patch.CallBack_v2"}; + + + + +// ************************************************************** +// +// Ops.Patch.Function_v2 +// +// ************************************************************** + +Ops.Patch.Function_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + funcName = op.inString("Function Name", "default"), + triggerButton = op.inTriggerButton("Trigger"), + inString1 = op.inString("Default Parameter 1"), + inString2 = op.inString("Default Parameter 2"), + inString3 = op.inString("Default Parameter 3"), + outTrigger = op.outTrigger("Next"), + outString1 = op.outString("Parameter 1"), + outString2 = op.outString("Parameter 2"), + outString3 = op.outString("Parameter 3"); + +triggerButton.onTriggered = triggered; + +funcName.onChange = function () +{ + op.patch.config[funcName.get()] = triggered; +}; + +function triggered() +{ + const arg1 = arguments.hasOwnProperty(0) ? arguments[0] : inString1.get(); + const arg2 = arguments.hasOwnProperty(1) ? arguments[1] : inString2.get(); + const arg3 = arguments.hasOwnProperty(2) ? arguments[2] : inString3.get(); + outString1.set(arg1); + outString2.set(arg2); + outString3.set(arg3); + outTrigger.trigger(); +} + + +}; + +Ops.Patch.Function_v2.prototype = new CABLES.Op(); +CABLES.OPS["39043a11-1ae0-4568-93a7-ec1493df2662"]={f:Ops.Patch.Function_v2,objName:"Ops.Patch.Function_v2"}; + + + + +// ************************************************************** +// +// Ops.Patch.LoadingStatusTask +// +// ************************************************************** + +Ops.Patch.LoadingStatusTask = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// vars +let stack = []; +let uuid = CABLES.uuid(); + +// inputs +let startPort = op.inTriggerButton("Start Task"); +let stopPort = op.inTriggerButton("End Task"); + +// listeners +startPort.onTriggered = startTask; +stopPort.onTriggered = stopTask; + +function startTask() +{ + let id = uuid + " (" + (stack.length + 1) + ")"; + let loadingId = op.patch.loading.start("task", id); + if (CABLES.UI) + { + gui.jobs().start({ "id": loadingId, "title": "loading task " + id }); + } + + stack.push(loadingId); +} + +function stopTask() +{ + let loadingId = stack.pop(); + op.patch.loading.finished(loadingId); + if (CABLES.UI) + { + gui.jobs().finish(loadingId); + } +} + + +}; + +Ops.Patch.LoadingStatusTask.prototype = new CABLES.Op(); +CABLES.OPS["38d0f6ba-a4d8-4093-9cab-17e1b5dd52ae"]={f:Ops.Patch.LoadingStatusTask,objName:"Ops.Patch.LoadingStatusTask"}; + + + + +// ************************************************************** +// +// Ops.Patch.LoadingStatus_v2 +// +// ************************************************************** + +Ops.Patch.LoadingStatus_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + preRenderOps = op.inValueBool("PreRender Ops"), + startTimeLine = op.inBool("Play Timeline", true), + + next = op.outTrigger("Next"), + outInitialFinished = op.outBool("Finished Initial Loading", false), + outLoading = op.outBool("Loading"), + outProgress = op.outNumber("Progress"), + outList = op.outArray("Jobs"), + loadingFinished = op.outTrigger("Trigger Loading Finished "); + +const cgl = op.patch.cgl; +const patch = op.patch; + +let finishedOnce = false; +const preRenderTimes = []; +let firstTime = true; + +document.body.classList.add("cables-loading"); + +let loadingId = cgl.patch.loading.start("loadingStatusInit", "loadingStatusInit"); + +exe.onTriggered = () => +{ + const jobs = op.patch.loading.getListJobs(); + outProgress.set(patch.loading.getProgress()); + + let hasFinished = jobs.length === 0; + const notFinished = !hasFinished; + // outLoading.set(!hasFinished); + + if (notFinished) + { + outList.set(op.patch.loading.getListJobs()); + } + + if (notFinished) + { + if (firstTime) + { + if (preRenderOps.get()) op.patch.preRenderOps(); + op.patch.timer.setTime(0); + if (startTimeLine.get()) + { + op.patch.timer.play(); + } + else + { + op.patch.timer.pause(); + } + } + firstTime = false; + + document.body.classList.remove("cables-loading"); + document.body.classList.add("cables-loaded"); + } + else + { + finishedOnce = true; + outList.set(op.patch.loading.getListJobs()); + + if (patch.loading.getProgress() < 1.0) + { + op.patch.timer.setTime(0); + op.patch.timer.pause(); + } + } + + outInitialFinished.set(finishedOnce); + + if (outLoading.get() && hasFinished) loadingFinished.trigger(); + + outLoading.set(notFinished); + + next.trigger(); + + if (loadingId) + { + cgl.patch.loading.finished(loadingId); + loadingId = null; + } +}; + + +}; + +Ops.Patch.LoadingStatus_v2.prototype = new CABLES.Op(); +CABLES.OPS["e62f7f4c-7436-437e-8451-6bc3c28545f7"]={f:Ops.Patch.LoadingStatus_v2,objName:"Ops.Patch.LoadingStatus_v2"}; + + + + +// ************************************************************** +// +// Ops.Patch.PlayButton +// +// ************************************************************** + +Ops.Patch.PlayButton = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"inner_css":"\nborder-style:solid;\nborder-color:transparent transparent transparent #ccc;\nbox-sizing:border-box;\nwidth:50px;\nheight:50px;\nmargin-top:25px;\nmargin-left:36px;\nborder-width:25px 0px 25px 40px;\npointer-events:none;\n","outer_css":"width:100px;\nheight:100px;\nleft:50%;\ntop:50%;\nborder-radius:100%;\nposition:absolute;\ncursor:pointer;\nopacity:0.7;\ntransform:translate(-50%,-50%);\nz-index:999999;\nbackground-color:#333;\nborder:5px solid #333;",}; +const + inExec = op.inTrigger("Trigger"), + inIfSuspended = op.inValueBool("Only if Audio Suspended"), + inReset = op.inTriggerButton("Reset"), + inStyleOuter = op.inStringEditor("Style Outer", attachments.outer_css), + inStyleInner = op.inStringEditor("Style Inner", attachments.inner_css), + inActive = op.inBool("Active", true), + outNext = op.outTrigger("Next"), + notClickedNext = op.outTrigger("Not Clicked"), + outState = op.outString("Audiocontext State"), + outEle = op.outObject("Element"), + outClicked = op.outBoolNum("Clicked", false), + outClickedTrigger = op.outTrigger("Clicked Trigger"); + +op.toWorkPortsNeedToBeLinked(inExec); +let audioCtx = CABLES.WEBAUDIO.createAudioContext(op); + +const canvas = op.patch.cgl.canvas.parentElement; +let wasClicked = false; +let ele = null; +let elePlay = null; +createElements(); + +inStyleOuter.onChange = + inStyleInner.onChange = createElements; + +audioCtx.addEventListener("statechange", updateState); + +inActive.onChange = () => +{ + if (!inActive.get())ele.style.display = "none"; + else ele.style.display = "block"; +}; + +function createElements() +{ + if (elePlay) elePlay.remove(); + if (ele) ele.remove(); + + ele = document.createElement("div"); + ele.style = inStyleOuter.get(); + outEle.set(ele); + + canvas.appendChild(ele); + + elePlay = document.createElement("div"); + elePlay.style = inStyleInner.get(); + + ele.appendChild(elePlay); + ele.classList.add("playButton"); + + ele.addEventListener("mouseenter", hover); + ele.addEventListener("mouseleave", hoverOut); + ele.addEventListener("click", clicked); + ele.addEventListener("touchStart", clicked); + op.onDelete = removeElements; +} + +inReset.onTriggered = function () +{ + createElements(); + wasClicked = false; + outClicked.set(wasClicked); +}; + +function updateState() +{ + outState.set(audioCtx.state); + if (inIfSuspended.get() && audioCtx.state == "running") clicked(); +} + +inExec.onTriggered = function () +{ + if (wasClicked) outNext.trigger(); + else notClickedNext.trigger(); +}; + +function clicked() +{ + removeElements(); + if (audioCtx && audioCtx.state == "suspended")audioCtx.resume(); + wasClicked = true; + outClicked.set(wasClicked); + outClickedTrigger.trigger(); +} + +function removeElements() +{ + if (elePlay) elePlay.remove(); + if (ele) ele.remove(); +} + +function hoverOut() +{ + if (ele) ele.style.opacity = 0.7; +} + +function hover() +{ + if (ele) ele.style.opacity = 1.0; +} + + +}; + +Ops.Patch.PlayButton.prototype = new CABLES.Op(); +CABLES.OPS["32e53fa2-4545-4c53-a94d-2204aa079246"]={f:Ops.Patch.PlayButton,objName:"Ops.Patch.PlayButton"}; + + + + +// ************************************************************** +// +// Ops.Physics.Ammo.AmmoBody +// +// ************************************************************** + +Ops.Physics.Ammo.AmmoBody = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// https://github.com/enable3d/enable3d/blob/master/packages/ammoPhysics/src/three-to-ammo.js + +const + inExec = op.inTrigger("Update"), + inName = op.inString("Name", ""), + inMass = op.inFloat("Mass", 0), + inFriction = op.inFloat("Friction", 0.5), + inRollingFriction = op.inFloat("Rolling Friction", 0.5), + inRestitution = op.inFloat("Restitution", 0.5), + + inShape = op.inDropDown("Shape", ["Box", "Sphere", "Cylinder", "Capsule", "Cone", "Geom Convex Hull", "Triangle Shape"], "Box"), + inGeom = op.inObject("Geometry", null, "geometry"), + inGeomSimplify = op.inInt("Simplify Max Triangles", 50), + inRadius = op.inFloat("Radius", 0.5), + inSizeX = op.inFloat("Size X", 1), + inSizeY = op.inFloat("Size Y", 1), + inSizeZ = op.inFloat("Size Z", 1), + + inPositions = op.inArray("Positions", null, 3), + inPosIndex = op.inBool("Append Index to name", true), + + inNeverDeactivate = op.inBool("Never Deactivate"), + inGhostObject = op.inBool("Ghost Object"), + inActive = op.inBool("Active", true), + inReset = op.inTriggerButton("Reset"), + inActivate = op.inTriggerButton("Activate"), + + next = op.outTrigger("next"), + outRayHit = op.outBoolNum("Ray Hit"), + transformed = op.outTrigger("Transformed"); + +op.setPortGroup("Array", [inPositions, inPosIndex]); + +inExec.onTriggered = update; + +const cgl = op.patch.cgl; +let bodies = []; +let world = null; +let tmpTrans = null; + +const tmpOrigin = vec3.create(); +const tmpQuat = quat.create(); +const tmpScale = vec3.create(); +let transMat = mat4.create(); + +let btOrigin = null; +let btQuat = null; +let doResetPos = false; +let colShape = null; + +let doScale = true; +let needsRemove = false; +inName.onChange = updateBodyMeta; + +op.setPortGroup("Parameters", [inRestitution, inFriction, inRollingFriction, inMass]); +op.setPortGroup("Shape", [inShape, inGeom, inGeomSimplify, inRadius, inSizeY, inSizeX, inSizeZ]); +op.setPortGroup("Flags", [inNeverDeactivate, inActive, inGhostObject]); + +inExec.onLinkChanged = + inFriction.onChange = + inRestitution.onChange = + inRollingFriction.onChange = + inGeomSimplify.onChange = + inGhostObject.onChange = + inGeom.onChange = + inShape.onChange = + inMass.onChange = + inRadius.onChange = + inSizeY.onChange = + inSizeZ.onChange = + inPositions.onChange = + inSizeX.onChange = () => + { + needsRemove = true; + }; + +inPosIndex.onChange = updateBodyMeta; +op.onDelete = removeBody; + +inActive.onChange = () => +{ + if (!inActive.get()) removeBody(); +}; + +inActivate.onTriggered = () => +{ + for (let i = 0; i < bodies.length; i++) + bodies[i].activate(); +}; + +function removeBody() +{ + if (world) + { + for (let i = 0; i < bodies.length; i++) + world.removeRigidBody(bodies[i]); + } + + bodies.length = 0; +} + +inReset.onTriggered = () => +{ + needsRemove = true; +}; + +function updateBodyMeta() +{ + const n = inName.get(); + const appendIndex = inPosIndex.get(); + const posArr = inPositions.get(); + + if (world) + for (let i = 0; i < bodies.length; i++) + { + let name = n; + if (appendIndex && posArr)name = n + "." + i; + + world.setBodyMeta(bodies[i], + { + "name": name, + "mass": inMass.get(), + }); + } + + op.setUiAttribs({ "extendTitle": inName.get() }); +} + +function setup() +{ + if (!inActive.get()) return; + doScale = true; + + removeBody(); + + if (!tmpTrans)tmpTrans = new Ammo.btTransform(); + // if (!transform)transform = new Ammo.btTransform(); + + if (colShape)Ammo.destroy(colShape); + colShape = null; + // transform.setIdentity(); + // CABLES.AmmoWorld.copyCglTransform(cgl, transform); + + op.setUiError("nogeom", null); + if (inShape.get() == "Box") colShape = new Ammo.btBoxShape(new Ammo.btVector3(inSizeX.get() / 2, inSizeY.get() / 2, inSizeZ.get() / 2)); + else if (inShape.get() == "Sphere") colShape = new Ammo.btSphereShape(inRadius.get()); + else if (inShape.get() == "Cylinder") colShape = new Ammo.btCylinderShape(new Ammo.btVector3(inSizeX.get() / 2, inSizeY.get() / 2, inSizeZ.get() / 2)); + else if (inShape.get() == "Capsule") colShape = new Ammo.btCapsuleShape(inRadius.get(), inSizeY.get()); + else if (inShape.get() == "Cone") colShape = new Ammo.btConeShape(inRadius.get(), inSizeY.get()); + else if (inShape.get() == "Triangle Shape") + { + const geom = inGeom.get(); + if (!geom || !inGeom.isLinked()) + { + op.setUiError("nogeom", "Shape needs geometry connected"); + return; + } + else op.setUiError("nogeom", null); + if (!geom) return; + + let mesh = new Ammo.btTriangleMesh(true, true); + + for (let i = 0; i < geom.verticesIndices.length / 3; i++) + { + mesh.addTriangle( + new Ammo.btVector3( + geom.vertices[geom.verticesIndices[i * 3] * 3 + 0], + geom.vertices[geom.verticesIndices[i * 3] * 3 + 1], + geom.vertices[geom.verticesIndices[i * 3] * 3 + 2] + ), + new Ammo.btVector3( + geom.vertices[geom.verticesIndices[i * 3 + 1] * 3 + 0], + geom.vertices[geom.verticesIndices[i * 3 + 1] * 3 + 1], + geom.vertices[geom.verticesIndices[i * 3 + 1] * 3 + 2] + ), + new Ammo.btVector3( + geom.vertices[geom.verticesIndices[i * 3 + 2] * 3 + 0], + geom.vertices[geom.verticesIndices[i * 3 + 2] * 3 + 1], + geom.vertices[geom.verticesIndices[i * 3 + 2] * 3 + 2] + ), + false); + } + + colShape = new Ammo.btBvhTriangleMeshShape(mesh, true, true); + } + else if (inShape.get() == "Geom Convex Hull") + { + const geom = inGeom.get(); + if (!geom || !inGeom.isLinked()) + { + op.setUiError("nogeom", "Shape needs geometry connected"); + return; + } + else op.setUiError("nogeom", null); + if (!geom) return; + + colShape = CABLES.AmmoWorld.createConvexHullFromGeom(geom, inGeomSimplify.get()); + + doScale = false; + } + else + { + inGeomSimplify.setUiAttribs({ "greyout": true }); + op.log("unknown shape type", inShape.get()); + return; + } + + if (inShape.get() == "Geom Convex Hull") + { + inRadius.setUiAttribs({ "greyout": true }); + inSizeX.setUiAttribs({ "greyout": true }); + inSizeY.setUiAttribs({ "greyout": true }); + inSizeZ.setUiAttribs({ "greyout": true }); + inGeomSimplify.setUiAttribs({ "greyout": false }); + } + else + { + inSizeX.setUiAttribs({ "greyout": inShape.get() == "Sphere" || inShape.get() == "Capsule" || inShape.get() == "Cone" }); + inSizeY.setUiAttribs({ "greyout": inShape.get() == "Sphere" }); + inSizeZ.setUiAttribs({ "greyout": inShape.get() == "Sphere" || inShape.get() == "Capsule" || inShape.get() == "Cone" }); + inRadius.setUiAttribs({ "greyout": inShape.get() == "Box" }); + } + + colShape.setMargin(0.05); + + let mass = inMass.get(); + let localInertia = new Ammo.btVector3(0, 0, 0); + colShape.calculateLocalInertia(mass, localInertia); + + let num = 1; + let posArr = null; + if (inPositions.isLinked()) + { + num = 0; + posArr = inPositions.get(); + + if (posArr && posArr.length) + { + num = Math.max(num, posArr.length / 3); + } + } + + for (let i = 0; i < num; i++) + { + let transform = new Ammo.btTransform(); + + const motionState = new Ammo.btDefaultMotionState(transform); + + let rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, colShape, localInertia); + const body = new Ammo.btRigidBody(rbInfo); + world.addRigidBody(body); + + // CF_STATIC_OBJECT= 1, + // CF_KINEMATIC_OBJECT= 2, + // CF_NO_CONTACT_RESPONSE = 4, + // CF_CUSTOM_MATERIAL_CALLBACK = 8,//this allows per-triangle material (friction/restitution) + // CF_CHARACTER_OBJECT = 16, + // CF_DISABLE_VISUALIZE_OBJECT = 32, //disable debug drawing + // CF_DISABLE_SPU_COLLISION_PROCESSING = 64//disable parallel/SPU processing + if (inGhostObject.get()) + body.setCollisionFlags(body.getCollisionFlags() | 4); + + bodies.push(body); + // motionStates.push(motionState); + } + setPositions(); + + world.on("rayCastHit", (name) => + { + outRayHit.set(name == inName.get()); + }); + + updateParams(); + updateBodyMeta(); +} + +function setPositions() +{ + let posArr = inPositions.get(); + + for (let i = 0; i < bodies.length; i++) + { + bodies[i].getMotionState().getWorldTransform(tmpTrans); + + if (posArr) + { + cgl.pushModelMatrix(); + + mat4.translate(cgl.mMatrix, cgl.mMatrix, [ + posArr[i * 3 + 0], posArr[i * 3 + 1], posArr[i * 3 + 2]]); + } + CABLES.AmmoWorld.copyCglTransform(cgl, tmpTrans); + if (posArr)cgl.popModelMatrix(); + + bodies[i].getMotionState().setWorldTransform(tmpTrans); + bodies[i].setWorldTransform(tmpTrans); + } +} + +function updateParams() +{ + if (!world || bodies.length == 0) return; + for (let i = 0; i < bodies.length; i++) + { + bodies[i].setFriction(inFriction.get()); + bodies[i].setRollingFriction(inRollingFriction.get()); + bodies[i].setRestitution(inRestitution.get()); + } +} + +function renderTransformed() +{ + if (!bodies.length) return; + if (!inActive.get()) return; + + ping(); + + if (transformed.isLinked()) + for (let i = 0; i < bodies.length; i++) + { + const body = bodies[i]; + let ms = body.getMotionState(); + if (ms) + { + ms.getWorldTransform(tmpTrans); + let p = tmpTrans.getOrigin(); + let q = tmpTrans.getRotation(); + + cgl.pushModelMatrix(); + + mat4.identity(cgl.mMatrix); + + let scale = [inSizeX.get(), inSizeY.get(), inSizeZ.get()]; + + if (inShape.get() == "Sphere") scale = [inRadius.get() * 2, inRadius.get() * 2, inRadius.get() * 2]; + if (inShape.get() == "Cone") scale = [inRadius.get() * 2, inSizeY.get(), inRadius.get() * 2]; + // if (inShape.get() == "Cylinder") scale = [inRadius.get() * 2, inSizeY.get(), inRadius.get() * 2]; + if (inShape.get() == "Capsule") scale = [inRadius.get() * 2, inSizeY.get() * 2, inRadius.get() * 2]; + + mat4.fromRotationTranslationScale(transMat, [q.x(), q.y(), q.z(), q.w()], [p.x(), p.y(), p.z()], scale); + mat4.mul(cgl.mMatrix, cgl.mMatrix, transMat); + + transformed.trigger(); + + cgl.popModelMatrix(); + } + } +} + +function ping() +{ + if (world) + for (let i = 0; i < bodies.length; i++) + world.pingBody(bodies[i]); +} + +function update() +{ + if (world && bodies.length && bodies[0] && world.getBodyMeta(bodies[0]) == undefined)removeBody(); + if (needsRemove) + { + removeBody(); + needsRemove = false; + } + if (world != cgl.frameStore.ammoWorld) removeBody(); + + world = cgl.frameStore.ammoWorld; + if (!world) + { + outRayHit.set(false); + return; + } + if (!bodies.length) setup(world); + if (!bodies.length) return; + if (bodies[0] && inNeverDeactivate.get()) bodies[0].activate(); // body.setActivationState(Ammo.DISABLE_DEACTIVATION); did not work..... + + if (inMass.get() == 0 || doResetPos) + { + setPositions(); + doResetPos = false; + } + + renderTransformed(); + + next.trigger(); +} + + +}; + +Ops.Physics.Ammo.AmmoBody.prototype = new CABLES.Op(); +CABLES.OPS["d9c57405-9cc0-475f-b81d-21401a7ab326"]={f:Ops.Physics.Ammo.AmmoBody,objName:"Ops.Physics.Ammo.AmmoBody"}; + + + + +// ************************************************************** +// +// Ops.Physics.Ammo.AmmoBodyCollision +// +// ************************************************************** + +Ops.Physics.Ammo.AmmoBodyCollision = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("update"), + inName0 = op.inString("Name 1", ""), + inName0Match = op.inSwitch("Match Name 1", ["exact", "start", "contain"], "exact"), + inName1 = op.inString("Name 2", ""), + inName1Match = op.inSwitch("Match Name 2", ["exact", "start", "contain"], "exact"), + next = op.outTrigger("Next"), + outColliding = op.outBoolNum("Colliding"), + outNumCollisions = op.outNumber("Num Collisions"), + outCollisions = op.outArray("Collisions"); + +let oldWorld = null; + +exec.onTriggered = () => +{ + const ammoWorld = op.patch.cgl.frameStore.ammoWorld; + if (!ammoWorld) return; + + if (oldWorld != ammoWorld) + { + oldWorld = ammoWorld; + } + const match0 = inName0Match.get(); + const match1 = inName1Match.get(); + const name0 = inName0.get(); + const name1 = inName1.get(); + + let filterFunc = (col) => + { + let isColliding = false; + let found = false; + let otherName = "name1"; + if (match0 === "exact") + { + if (col.name0 === name0) + { + found = true; + } + else if (col.name1 === name0) + { + found = true; + otherName = "name0"; + } + } + else if (match0 === "start") + { + if (col.name0.startsWith(name0)) + { + found = true; + } + else if (col.name1.startsWith(name0)) + { + found = true; + otherName = "name0"; + } + } + else if (match0 === "contain") + { + if (col.name0.includes(inName0.get())) + { + found = true; + } + else if (col.name1.includes(inName0.get())) + { + found = true; + otherName = "name0"; + } + } + if (found) + { + if (name1) + { + if (match1 === "exact") + { + if (col[otherName] === name1) isColliding = true; + } + else if (match1 === "start") + { + if (col[otherName].startsWith(name1)) isColliding = true; + } + else if (match1 === "contain") + { + if (col[otherName].includes(name1)) isColliding = true; + } + } + else + { + isColliding = true; + } + } + return isColliding; + }; + + const allCols = ammoWorld.getCollisions(); + const cols = allCols.filter(filterFunc); + outCollisions.set(cols); + outNumCollisions.set(cols.length); + outColliding.set(cols.length > 0); + + next.trigger(); +}; + + +}; + +Ops.Physics.Ammo.AmmoBodyCollision.prototype = new CABLES.Op(); +CABLES.OPS["6fd02be3-0c73-4fbd-88a1-d60313ad5622"]={f:Ops.Physics.Ammo.AmmoBodyCollision,objName:"Ops.Physics.Ammo.AmmoBodyCollision"}; + + + + +// ************************************************************** +// +// Ops.Physics.Ammo.AmmoCharacter +// +// ************************************************************** + +Ops.Physics.Ammo.AmmoCharacter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Update"), + inRadius = op.inFloat("Radius", 1), + inStyle = op.inSwitch("View", ["3rd Person", "1st Person"], "3rd Person"), + // inSizeX = op.inFloat("Size X", 1), + inSizeY = op.inFloat("Height", 2.04), + // inSizeZ = op.inFloat("Size Z", 1), + inMass = op.inFloat("Mass", 0), + inName = op.inString("Name", ""), + inActivate = op.inTriggerButton("Activate"), + + inMoveXP = op.inBool("Move X+", false), + inMoveXM = op.inBool("Move X-", false), + inMoveYP = op.inBool("Move Y+", false), + inMoveYM = op.inBool("Move Y-", false), + inMoveZP = op.inBool("Move Z+", false), + inMoveZM = op.inBool("Move Z-", false), + + inDirX = op.inFloat("Dir X"), + inDirY = op.inFloat("Dir Y"), + inDirZ = op.inFloat("Dir Z"), + + inResetX = op.inFloat("Set Pos X"), + inResetY = op.inFloat("Set Pos Y"), + inResetZ = op.inFloat("Set Pos Z"), + // inSetPos = op.inTriggerButton("Set Pos"), + inReset = op.inTriggerButton("Reset"), + + inSpeed = op.inFloat("Speed", 1), + inFallVelocity = op.inFloat("Add Velocity Y", 0.5), + + next = op.outTrigger("next"), + outX = op.outNumber("Position X"), + outY = op.outNumber("Position Y"), + outZ = op.outNumber("Position Z"), + transformed = op.outTrigger("Transformed"); + +inExec.onTriggered = update; + +op.setPortGroup("Reset", [inResetX, inResetY, inResetZ, inReset]); + +const cgl = op.patch.cgl; +let body = null; +let world = null; +let tmpTrans = null; +let transform = null; +let motionState = null; +const tmpOrigin = vec3.create(); +const tmpQuat = quat.create(); +const tmpScale = vec3.create(); +let transMat = mat4.create(); + +let forceQuat = null; +let initX = 0, initY = 0, initZ = 0; + +let btOrigin = null; +let btQuat = null; +let doResetPos = false; + +inName.onChange = updateBodyMeta; + +op.onDelete = + inMass.onChange = + inRadius.onChange = + inSizeY.onChange = () => + { + removeBody(); + }; + +inActivate.onTriggered = () => +{ + if (body)body.activate(); +}; + +function removeBody() +{ + if (world && body) world.removeRigidBody(body); + body = null; +} + +inReset.onTriggered = () => +{ + initX = inResetX.get(); + initY = inResetY.get(); + initZ = inResetZ.get(); + + removeBody(); +}; + +let btVelocity = null; + +function updateBodyMeta() +{ + if (world) + world.setBodyMeta(body, + { + "name": inName.get(), + "mass": inMass.get(), + }); + + op.setUiAttribs({ "extendTitle": inName.get() }); +} + +function setup() +{ + if (world && body) world.removeRigidBody(body); + + tmpTrans = new Ammo.btTransform(); + transform = new Ammo.btTransform(); + + transform.setIdentity(); + + copyCglTransform(transform); + + motionState = new Ammo.btDefaultMotionState(transform); + + let colShape = new Ammo.btCapsuleShape(inRadius.get(), inSizeY.get() - inRadius.get()); + + colShape.setMargin(0.05); + + let localInertia = new Ammo.btVector3(0, 0, 0); + colShape.calculateLocalInertia(inMass.get(), localInertia); + + let rbInfo = new Ammo.btRigidBodyConstructionInfo(inMass.get(), motionState, colShape, localInertia); + body = new Ammo.btRigidBody(rbInfo); + // body.setDamping(0.7, 0.01); + + world.addRigidBody(body); + + updateBodyMeta(); +} + +function renderTransformed() +{ + let ms = body.getMotionState(); + if (ms) + { + ms.getWorldTransform(tmpTrans); + let p = tmpTrans.getOrigin(); + let q = tmpTrans.getRotation(); + + if (inStyle.get() == "3rd Person") + q.setValue(tmpQuat[0], tmpQuat[1], tmpQuat[2], tmpQuat[3]); + + cgl.pushModelMatrix(); + + mat4.identity(cgl.mMatrix); + + let scale = [inRadius.get(), inRadius.get(), inRadius.get()]; + + mat4.fromRotationTranslationScale(transMat, [q.x(), q.y(), q.z(), q.w()], [ + p.x(), p.y(), p.z()], scale); + mat4.mul(cgl.mMatrix, cgl.mMatrix, transMat); + + transformed.trigger(); + + cgl.popModelMatrix(); + } +} + +function copyCglTransform(transform) +{ + if (!btOrigin) + { + btOrigin = new Ammo.btVector3(0, 0, 0); + btQuat = new Ammo.btQuaternion(0, 0, 0, 0); + } + mat4.getTranslation(tmpOrigin, cgl.mMatrix); + mat4.getRotation(tmpQuat, cgl.mMatrix); + + let changed = false; + + btOrigin.setValue(tmpOrigin[0], tmpOrigin[1], tmpOrigin[2]); + btOrigin = new Ammo.btVector3(initX, initY, initZ); + btQuat.setValue(tmpQuat[0], tmpQuat[1], tmpQuat[2], tmpQuat[3]); + + transform.setOrigin(btOrigin); + transform.setRotation(btQuat); +} + +function update() +{ + if (world != cgl.frameStore.ammoWorld) removeBody(); + + world = cgl.frameStore.ammoWorld; + if (!world) return; + if (!body) setup(world); + if (!body) return; + body.activate(); // body.setActivationState(Ammo.DISABLE_DEACTIVATION); did not work..... + + if (!btVelocity) + { + btVelocity = new Ammo.btVector3(0, 0, 0); + } + + let vx = 0, vy = 0, vz = 0.0; + let speed = inSpeed.get(); + + let doMove = false; + if (inMoveZP.get()) + { + vx = inDirX.get() * speed; + vy = inDirY.get() * speed; + vz = inDirZ.get() * speed; + doMove = true; + } + if (inMoveZM.get()) + { + vx = -inDirX.get() * speed; + vy = -inDirY.get() * speed; + vz = -inDirZ.get() * speed; + doMove = true; + } + + if (inMoveXP.get()) + { + vx = -inDirZ.get() * speed; + vy = inDirY.get() * speed; + vz = inDirX.get() * speed; + doMove = true; + } + if (inMoveXM.get()) + { + vx = inDirZ.get() * speed; + vy = inDirY.get() * speed; + vz = -inDirX.get() * speed; + doMove = true; + } + + if (inMoveYP.get()) vy = 3; + else vy = 0; + + doMove = true; + + if (doMove) + { + btVelocity.setValue(vx, vy - inFallVelocity.get(), vz); + body.setLinearVelocity(btVelocity); + } + + if (inMass.get() == 0 || doResetPos) + { + copyCglTransform(transform); + motionState.setWorldTransform(transform); + + body.setWorldTransform(transform); + + doResetPos = false; + } + + motionState.getWorldTransform(transform); + + // force upright position + if (!forceQuat) forceQuat = new Ammo.btQuaternion(); + forceQuat.setEulerZYX(0, 90, 0); + transform.setRotation(forceQuat); + body.setWorldTransform(transform); + let p = tmpTrans.getOrigin(); + + outX.set(p.x()); + outY.set(p.y()); + outZ.set(p.z()); + + renderTransformed(); + + next.trigger(); +} + + +}; + +Ops.Physics.Ammo.AmmoCharacter.prototype = new CABLES.Op(); +CABLES.OPS["5f6c2a84-8de9-41e5-948a-d9c5ed49022f"]={f:Ops.Physics.Ammo.AmmoCharacter,objName:"Ops.Physics.Ammo.AmmoCharacter"}; + + + + +// ************************************************************** +// +// Ops.Physics.Ammo.AmmoCharacterFpsCamera +// +// ************************************************************** + +Ops.Physics.Ammo.AmmoCharacterFpsCamera = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + enablePointerLock = op.inBool("Enable pointer lock", true), + trigger = op.outTrigger("trigger"), + isLocked = op.outBoolNum("isLocked", false), + inHeight = op.inFloat("Height", 2), + inName = op.inString("Character Name", "player1"), + mouseSpeed = op.inFloat("Mouse Speed", 1), + inActive = op.inBool("Active", true), + outMouseDown = op.outTrigger("Mouse Left"), + outMouseDownRight = op.outTrigger("Mouse Right"), + outDirX = op.outNumber("Dir X"), + outDirY = op.outNumber("Dir Y"), + outDirZ = op.outNumber("Dir Z"), + outRotX = op.outNumber("Rot X"), + outRotY = op.outNumber("Rot Y"); + +op.toWorkPortsNeedToBeLinked(render); + +const cgl = op.patch.cgl; +const viewMatrix = mat4.create(); +const vPos = vec3.create(); +let speedx = 0, speedy = 0, speedz = 0; +const movementSpeedFactor = 0.5; +const canvas = document.getElementById("glcanvas"); +const DEG2RAD = 3.14159 / 180.0; +let rotX = 0; +let rotY = 0; +let lastMove = 0; +let mouseNoPL = { "firstMove": true, + "deltaX": 0, + "deltaY": 0, +}; + +initListener(); + +enablePointerLock.onChange = initListener; + +inActive.onChange = () => +{ + document.exitPointerLock(); + removeListener(); + + lockChangeCallback(); + + if (inActive.get()) initListener(); +}; + +let tmpTrans = null; + +render.onTriggered = function () +{ + if (!Ammo) return; + if (!inActive.get()) return trigger.trigger(); + if (!tmpTrans) tmpTrans = new Ammo.btTransform(); + + if (cgl.frameStore.shadowPass) return trigger.trigger(); + + cgl.pushViewMatrix(); + + const ammoWorld = cgl.frameStore.ammoWorld; + + if (!ammoWorld) + { + op.log("char no ammoworld"); + return; + } + + const body = ammoWorld.getBodyByName(inName.get()); + + if (body) + { + let ms = body.getMotionState(); + ms.getWorldTransform(tmpTrans); + let p = tmpTrans.getOrigin(); + vec3.set(vPos, -p.x(), -p.y() - inHeight.get(), -p.z()); + } + else + { + op.log("char body not found!"); + } + + if (rotX < -90)rotX = -90; + if (rotX > 90)rotX = 90; + + mat4.identity(cgl.vMatrix); + + mat4.rotateX(cgl.vMatrix, cgl.vMatrix, DEG2RAD * rotX); + mat4.rotateY(cgl.vMatrix, cgl.vMatrix, DEG2RAD * rotY); + + mat4.translate(cgl.vMatrix, cgl.vMatrix, vPos); + + trigger.trigger(); + cgl.popViewMatrix(); + + outRotX.set(rotX); + outRotY.set(rotY); + + // for dir vec + mat4.identity(viewMatrix); + mat4.rotateX(viewMatrix, viewMatrix, DEG2RAD * rotX); + mat4.rotateY(viewMatrix, viewMatrix, DEG2RAD * rotY); + mat4.transpose(viewMatrix, viewMatrix); + + const dir = vec4.create(); + vec4.transformMat4(dir, [0, 0, 1, 1], viewMatrix); + + vec4.normalize(dir, dir); + outDirX.set(-dir[0]); + outDirY.set(-dir[1]); + outDirZ.set(-dir[2]); +}; + +function moveCallback(e) +{ + const mouseSensitivity = 0.1; + rotX += e.movementY * mouseSensitivity * mouseSpeed.get(); + rotY += e.movementX * mouseSensitivity * mouseSpeed.get(); + + if (rotX < -90.0) rotX = -90.0; + if (rotX > 90.0) rotX = 90.0; + if (rotY < -180.0) rotY += 360.0; + if (rotY > 180.0) rotY -= 360.0; +} + +function mouseDown(e) +{ + if (e.which == 3) outMouseDownRight.trigger(); + else outMouseDown.trigger(); +} + +function lockChangeCallback(e) +{ + if (document.pointerLockElement === canvas || + document.mozPointerLockElement === canvas || + document.webkitPointerLockElement === canvas) + { + document.addEventListener("pointerdown", mouseDown, false); + document.addEventListener("pointermove", moveCallback, false); + isLocked.set(true); + } + else + { + document.removeEventListener("pointerdown", mouseDown, false); + document.removeEventListener("pointermove", moveCallback, false); + isLocked.set(false); + } +} + +function startPointerLock(e) +{ + const test = false; + + if (render.isLinked() && enablePointerLock.get() && e.buttons == 1) + { + document.addEventListener("pointermove", moveCallback, false); + canvas.requestPointerLock = canvas.requestPointerLock || + canvas.mozRequestPointerLock || + canvas.webkitRequestPointerLock; + canvas.requestPointerLock(); + } +} + +function removeListener() +{ + cgl.canvas.removeEventListener("pointermove", moveCallbackNoPL, false); + cgl.canvas.removeEventListener("pointerup", upCallbackNoPL, false); + + document.removeEventListener("pointerlockchange", lockChangeCallback, false); + document.removeEventListener("mozpointerlockchange", lockChangeCallback, false); + document.removeEventListener("webkitpointerlockchange", lockChangeCallback, false); + document.getElementById("glcanvas").removeEventListener("mousedown", startPointerLock); +} + +function initListener() +{ + if (enablePointerLock.get()) + { + document.addEventListener("pointerlockchange", lockChangeCallback, false); + document.addEventListener("mozpointerlockchange", lockChangeCallback, false); + document.addEventListener("webkitpointerlockchange", lockChangeCallback, false); + document.getElementById("glcanvas").addEventListener("mousedown", startPointerLock); + + cgl.canvas.removeEventListener("pointermove", moveCallbackNoPL, false); + cgl.canvas.removeEventListener("pointerup", upCallbackNoPL, false); + } + else + { + cgl.canvas.addEventListener("pointermove", moveCallbackNoPL, false); + cgl.canvas.addEventListener("pointerup", upCallbackNoPL, false); + } +} + +function upCallbackNoPL(e) +{ + try { cgl.canvas.releasePointerCapture(e.pointerId); } + catch (e) {} + mouseNoPL.firstMove = true; +} + +function moveCallbackNoPL(e) +{ + if (e && e.buttons == 1) + { + try { cgl.canvas.setPointerCapture(e.pointerId); } + catch (_e) {} + + if (!mouseNoPL.firstMove) + { + const deltaX = (e.clientX - mouseNoPL.lastX) * mouseSpeed.get() * 0.5; + const deltaY = (e.clientY - mouseNoPL.lastY) * mouseSpeed.get() * 0.5; + + rotX += deltaY; + rotY += deltaX; + } + + mouseNoPL.firstMove = false; + mouseNoPL.lastX = e.clientX; + mouseNoPL.lastY = e.clientY; + } +} + + +}; + +Ops.Physics.Ammo.AmmoCharacterFpsCamera.prototype = new CABLES.Op(); +CABLES.OPS["0dca47aa-09e4-4b5d-b0d8-22390a950293"]={f:Ops.Physics.Ammo.AmmoCharacterFpsCamera,objName:"Ops.Physics.Ammo.AmmoCharacterFpsCamera"}; + + + + +// ************************************************************** +// +// Ops.Physics.Ammo.AmmoDebugRenderer +// +// ************************************************************** + +Ops.Physics.Ammo.AmmoDebugRenderer = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inRender = op.inTrigger("Render"), + inDrawWireframe = op.inBool("Draw Wireframe", true), + inDrawAABB = op.inBool("Draw AABB", false), + inDrawContacts = op.inBool("Draw Contact Points", true), + inIgnClear = op.inBool("Depth", true), + + inActive = op.inBool("Active", true), + + outNext = op.outTrigger("Next"); + +op.setPortGroup("Options", [inDrawContacts, inDrawWireframe, inDrawAABB, inIgnClear]); + +const cgl = op.patch.cgl; + +let debugDrawer = null; +let oldWorld = null; + +inRender.onTriggered = () => +{ + if (cgl.frameStore.shadowPass) return outNext.trigger(); + + const ammoWorld = cgl.frameStore.ammoWorld; + if (!ammoWorld) return; + + if (!debugDrawer || oldWorld != ammoWorld.world) + { + debugDrawer = new CABLES.AmmoDebugDrawer(ammoWorld.world, { }); + debugDrawer.enable(); + oldWorld = ammoWorld.world; + } + + if (!inActive.get()) + { + outNext.trigger(); + return; + } + + if (!ammoWorld) return; + + let debugmode = 0; + if (inDrawWireframe.get())debugmode |= 1; + if (inDrawAABB.get())debugmode |= 2; + if (inDrawContacts.get())debugmode |= 8; + + outNext.trigger(); + + debugmode |= 16384; + + if (debugmode) + { + cgl.pushModelMatrix(); + cgl.pushDepthTest(inIgnClear.get()); + cgl.pushDepthWrite(inIgnClear.get()); + + mat4.identity(cgl.mMatrix); + + debugDrawer.setDebugMode(debugmode); + debugDrawer.update(); + debugDrawer.render(cgl); + // outPoints.set(debugDrawer.verts); + + cgl.popDepthTest(); + cgl.popDepthWrite(); + cgl.popModelMatrix(); + } +}; + + +}; + +Ops.Physics.Ammo.AmmoDebugRenderer.prototype = new CABLES.Op(); +CABLES.OPS["e4b4f6c9-483b-486e-abbc-fbc4254a65d1"]={f:Ops.Physics.Ammo.AmmoDebugRenderer,objName:"Ops.Physics.Ammo.AmmoDebugRenderer"}; + + + + +// ************************************************************** +// +// Ops.Physics.Ammo.AmmoEmitter +// +// ************************************************************** + +Ops.Physics.Ammo.AmmoEmitter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Exec"), + inLimit = op.inInt("Limit Bodies", 10), + + inRadius = op.inFloat("Radius", 1), + inMass = op.inFloat("Mass", 1), + inNameIndex = op.inBool("Add index to name", true), + + inName = op.inString("Name", "emitterBody"), + + inFriction = op.inFloat("Friction", 0.5), + inRollingFriction = op.inFloat("Rolling Friction", 0.5), + inRestitution = op.inFloat("Restitution", 0.5), + + inDirX = op.inFloat("Dir X"), + inDirY = op.inFloat("Dir Y"), + inDirZ = op.inFloat("Dir Z"), + inSpeed = op.inFloat("Speed"), + + inSpawn = op.inTriggerButton("Spawn One"), + inRemove = op.inTriggerButton("Remove All"), + inActivate = op.inTriggerButton("Activate All"), + inRemoveFalling = op.inBool("Remove Y<-100", true), + // inLifeTime=op.inFloat("Lifetime",0), + + next = op.outTrigger("Next"), + outNum = op.outNumber("Total Bodies"), + outPos = op.outArray("Positions", 3), + outRot = op.outArray("Rotations Quats", 4); + +let shouldspawnOne = false; +inSpawn.onTriggered = () => { shouldspawnOne = true; }; + +const bodies = []; +const cgl = op.patch.cgl; + +let countAll = 0; +let world = null; +let tmpTrans = null; +let btVelocity = null; + +inRemove.onTriggered = removeAll; + +inExec.onLinkChanged = +inNameIndex.onChange = + op.onDelete = removeAll; + +inName.onChange = () => { op.setUiAttribs({ "extendTitle": inName.get() }); }; + +function removeAll() +{ + for (let i = 0; i < bodies.length; i++) + { + world.removeRigidBody(bodies[i].body); + } + + outPos.set(null); + outRot.set(null); + + bodies.length = 0; +} + +inActivate.onTriggered = () => +{ + for (let i = 0; i < bodies.length; i++) bodies[i].body.activate(); +}; + +function setArrayTransformed(body, i, arrPos) +{ + if (!body) return; + let ms = body.ms; + if (ms) + { + ms.getWorldTransform(tmpTrans); + let p = tmpTrans.getOrigin(); + let q = tmpTrans.getRotation(); + + // cgl.pushModelMatrix(); + + // mat4.identity(cgl.mMatrix); + + // let scale = [inSizeX.get(), inSizeY.get(), inSizeZ.get()]; + + // if (inShape.get() == "Sphere") scale = [inRadius.get() * 2, inRadius.get() * 2, inRadius.get() * 2]; + // if (inShape.get() == "Cone") scale = [inRadius.get() * 2, inSizeY.get(), inRadius.get() * 2]; + // if (inShape.get() == "Cylinder") scale = [inRadius.get() * 2, inSizeY.get(), inRadius.get() * 2]; + // if (inShape.get() == "Capsule") scale = [inRadius.get() * 2, inSizeY.get() * 2, inRadius.get() * 2]; + + arrPos[i * 3] = p.x(); + arrPos[i * 3 + 1] = p.y(); + arrPos[i * 3 + 2] = p.z(); + + arrRot[i * 4] = q.x(); + arrRot[i * 4 + 1] = q.y(); + arrRot[i * 4 + 2] = q.z(); + arrRot[i * 4 + 3] = q.w(); + + // mat4.fromRotationTranslationScale(transMat, [q.x(), q.y(), q.z(), q.w()], [p.x(), p.y(), p.z()], scale); + // mat4.mul(cgl.mMatrix, cgl.mMatrix, transMat); + + // transformed.trigger(); + + // cgl.popModelMatrix(); + } +} + +function spawn() +{ + if (!world) return; + if (!tmpTrans) tmpTrans = new Ammo.btTransform(); + + let transform = null; + let colShape = new Ammo.btSphereShape(inRadius.get()); + + colShape.setMargin(0.05); + + let localInertia = new Ammo.btVector3(0, 0, 0); + colShape.calculateLocalInertia(inMass.get(), localInertia); + + transform = new Ammo.btTransform(); + transform.setIdentity(); + + CABLES.AmmoWorld.copyCglTransform(cgl, transform); + + let motionState = new Ammo.btDefaultMotionState(transform); + + let rbInfo = new Ammo.btRigidBodyConstructionInfo(inMass.get(), motionState, colShape, localInertia); + let body = new Ammo.btRigidBody(rbInfo); + + body.setFriction(inFriction.get()); + body.setRollingFriction(inRollingFriction.get()); + body.setRestitution(inRestitution.get()); + + let speed = inSpeed.get(); + let vx = inDirX.get() * speed; + let vy = inDirY.get() * speed; + let vz = inDirZ.get() * speed; + + if (!btVelocity) btVelocity = new Ammo.btVector3(0, 0, 0); + + btVelocity.setValue(vx, vy, vz); + body.setLinearVelocity(btVelocity); + + world.addRigidBody(body); + + let name = inName.get() + "_" + countAll; + if (!inNameIndex.get())name = inName.get(); + + world.setBodyMeta(body, + { + "name": name + }); + + motionState.setWorldTransform(transform); + body.setWorldTransform(transform); + + if (bodies.length >= inLimit.get()) + { + world.removeRigidBody(bodies[0].body); + bodies.shift(); + } + + bodies.push({ "body": body, "ms": motionState }); + + countAll++; + + outNum.set(bodies.length); + + shouldspawnOne = false; +} + +function dispose() +{ + for (let i = 0; i < bodies.length; i++) + { + world.removeRigidBody(bodies[i].body); + } + + bodies.length = 0; +} + +let arrPos = []; +let arrRot = []; + +inExec.onTriggered = () => +{ + if (shouldspawnOne) spawn(); + if (world != cgl.frameStore.ammoWorld) + { + world = cgl.frameStore.ammoWorld; + world.on("dispose", dispose); + } + + if (inRemoveFalling.get()) + { + for (let i = 0; i < bodies.length; i++) + if (bodies[i]) + { + bodies[i].ms.getWorldTransform(tmpTrans); + let p = tmpTrans.getOrigin(); + if (p.y() < -100)bodies.splice(i, 1); + } + } + + arrPos.length = bodies.length * 3; + arrRot.length = bodies.length * 4; + for (let i = 0; i < bodies.length; i++) + { + if (!bodies[i]) continue; + setArrayTransformed(bodies[i], i, arrPos); + } + outPos.set(null); + outPos.set(arrPos); + outRot.set(null); + outRot.set(arrRot); + + next.trigger(); +}; + + +}; + +Ops.Physics.Ammo.AmmoEmitter.prototype = new CABLES.Op(); +CABLES.OPS["0c568898-f2e0-47b3-b44f-5d0e7c2cda57"]={f:Ops.Physics.Ammo.AmmoEmitter,objName:"Ops.Physics.Ammo.AmmoEmitter"}; + + + + +// ************************************************************** +// +// Ops.Physics.Ammo.AmmoRaycast +// +// ************************************************************** + +Ops.Physics.Ammo.AmmoRaycast = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Update"), + inCoords = op.inSwitch("Ray Coordinates", ["Screen XY", "Points 3d"], "Screen XY"), + inX = op.inValueFloat("Screen X"), + inY = op.inValueFloat("Screen Y"), + inRayPoints = op.inArray("Ray Points"), + active = op.inBool("Active", true), + inCursor = op.inBool("Change Cursor", true), + next = op.outTrigger("next"), + + outHasHit = op.outBoolNum("Has Hit", false), + outName = op.outString("Hit Body Name", ""), + outX = op.outNumber("Hit X"), + outY = op.outNumber("Hit Y"), + outZ = op.outNumber("Hit Z"); + +inExec.onTriggered = update; + +const cgl = op.patch.cgl; +let world = null; +let didsetCursor = false; +let mat = mat4.create(); +let isScreenCoords = true; + +inCoords.onChange = updateCoordsType; +updateCoordsType(); + +function updateCoordsType() +{ + isScreenCoords = inCoords.get() == "Screen XY"; + inX.setUiAttribs({ "greyout": !isScreenCoords }); + inY.setUiAttribs({ "greyout": !isScreenCoords }); + inRayPoints.setUiAttribs({ "greyout": isScreenCoords }); +} + +function update() +{ + world = cgl.frameStore.ammoWorld; + + // for(let i=0;i +{ + needsReset = true; +}; + +inActivateAll.onTriggered = () => +{ + if (ammoWorld) ammoWorld.activateAllBodies(); +}; + +inGravX.onChange = inGravZ.onChange = inGravY.onChange = updateGravity; + +function updateGravity() +{ + if (ammoWorld && ammoWorld.world) + ammoWorld.world.setGravity(new Ammo.btVector3(inGravX.get(), inGravY.get(), inGravZ.get())); +} + +function update() +{ + if (needsReset) + { + if (ammoWorld)ammoWorld.dispose(); + ammoWorld = null; + needsReset = false; + } + + if (!ammoWorld) + { + if (Ammo.cablesSetupDone) + { + ammoWorld = new CABLES.AmmoWorld(); + updateGravity(); + cgl.patch.loading.finished(loadingId); + loadingId = null; + } + else + { + if (!loadingId) loadingId = cgl.patch.loading.start("ammoWorld", "ammoWASM"); + return; + } + } + if (!ammoWorld.world) return; + + deltaTime = performance.now() - lastTime; + + if (inSim.get()) ammoWorld.frame(); + + const old = cgl.frameStore.ammoWorld; + cgl.frameStore.ammoWorld = ammoWorld; + + outNumBodies.set(ammoWorld.numBodies()); + outBodiesMeta.set(ammoWorld.getListBodies()); + outCollisions.set(ammoWorld.getCollisions()); + + ammoWorld.autoRemove = inAutoRemove.get(); + + next.trigger(); + + lastTime = performance.now(); + cgl.frameStore.ammoWorld = old; +} + + +}; + +Ops.Physics.Ammo.AmmoWorld.prototype = new CABLES.Op(); +CABLES.OPS["1005fcd0-5f40-49c5-8d46-45e95fcecf37"]={f:Ops.Physics.Ammo.AmmoWorld,objName:"Ops.Physics.Ammo.AmmoWorld"}; + + + + +// ************************************************************** +// +// Ops.Physics.Ammo.GltfAmmoBodies +// +// ************************************************************** + +Ops.Physics.Ammo.GltfAmmoBodies = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExec = op.inTrigger("Exec"), + inShape = op.inSwitch("Shape", ["Convex Hull", "Triangle Shape"], "Convex Hull"), + inNames = op.inString("Filter Meshes", ""), + inMass = op.inFloat("Mass kg", 0), + + inActive = op.inBool("Active", true), + outNum = op.outNumber("Meshes", 0); + +const cgl = op.patch.cgl; +const bodies = []; +const vec = vec3.create(); +const empty = vec3.create(); +const trMat = mat4.create(); +const size = 1.0; + +let world = null; +let scene = null; +let added = false; +let currentSceneLoaded = 0; + +inExec.onTriggered = update; + +const SHAPE_BOX = 0; +const SHAPE_SPHERE = 1; +const shape = SHAPE_BOX; +const sizeVec = vec3.create(); + +const meshCube = new CGL.WireCube(cgl); + +let tmpTrans = null; + +inMass.onChange = +inShape.onChange = +inNames.onChange = +inExec.onLinkChanged = () => +{ + removeFromWorld(); + added = false; +}; + +inActive.onChange = () => +{ + if (!inActive.get())removeFromWorld(); + update(); +}; + +function update() +{ + if (!inActive.get()) return; + if (!added || world != cgl.frameStore.ammoWorld) addToWorld(); + + if (world && bodies.length && bodies[0] && world.getBodyMeta(bodies[0].body) == undefined)removeFromWorld(); + + ping(); + for (let i = 0; i < bodies.length; i++) + { + cgl.pushModelMatrix(); + + mat4.identity(cgl.mMatrix); + + mat4.mul(cgl.mMatrix, cgl.mMatrix, bodies[i].node.modelMatAbs()); + + if (!tmpTrans)tmpTrans = new Ammo.btTransform(); + + CABLES.AmmoWorld.copyCglTransform(cgl, tmpTrans); + + bodies[i].motionState.setWorldTransform(tmpTrans); + bodies[i].body.setWorldTransform(tmpTrans); + + cgl.popModelMatrix(); + } +} + +function removeFromWorld() +{ + if (world) + { + for (let i = 0; i < bodies.length; i++) + { + world.removeRigidBody(bodies[i].body); + } + } + bodies.length = 0; + outNum.set(bodies.length); + world = null; + added = false; +} + +function ping() +{ + if (world) + for (let i = 0; i < bodies.length; i++) + world.pingBody(bodies[i].body); +} + +function addToWorld() +{ + scene = cgl.frameStore.currentScene; + if (!scene || !cgl.frameStore.ammoWorld) return; + + if (world != cgl.frameStore.ammoWorld || currentSceneLoaded != scene.loaded) removeFromWorld(); + + world = cgl.frameStore.ammoWorld; + + if (!world) + { + op.logError("no physics world!?"); + outNum.set(0); + return; + } + if (!scene) return; + + currentSceneLoaded = scene.loaded; + for (let i = 0; i < scene.nodes.length; i++) + { + if (!scene.nodes[i].mesh) continue; + if (scene.nodes[i].name.indexOf(inNames.get()) == -1) continue; + + let colShape = null; + + scene.nodes[i].transform(cgl, 0); + scene.nodes[i].updateMatrix(); + + const sc = scene.nodes[i]._scale || [1, 1, 1]; + const geom = scene.nodes[i].mesh.meshes[0].geom; + + if (inShape.get() == "Convex Hull") + { + colShape = CABLES.AmmoWorld.createConvexHullFromGeom(geom, 100, sc); + } + else + { + let mesh = new Ammo.btTriangleMesh(true, true); + + for (let i = 0; i < geom.verticesIndices.length / 3; i++) + { + mesh.addTriangle( + new Ammo.btVector3( + sc[0] * geom.vertices[geom.verticesIndices[i * 3] * 3 + 0], + sc[0] * geom.vertices[geom.verticesIndices[i * 3] * 3 + 1], + sc[0] * geom.vertices[geom.verticesIndices[i * 3] * 3 + 2] + ), + new Ammo.btVector3( + sc[0] * geom.vertices[geom.verticesIndices[i * 3 + 1] * 3 + 0], + sc[0] * geom.vertices[geom.verticesIndices[i * 3 + 1] * 3 + 1], + sc[0] * geom.vertices[geom.verticesIndices[i * 3 + 1] * 3 + 2] + ), + new Ammo.btVector3( + sc[0] * geom.vertices[geom.verticesIndices[i * 3 + 2] * 3 + 0], + sc[0] * geom.vertices[geom.verticesIndices[i * 3 + 2] * 3 + 1], + sc[0] * geom.vertices[geom.verticesIndices[i * 3 + 2] * 3 + 2] + ), + false); + } + + colShape = new Ammo.btBvhTriangleMeshShape(mesh, true, true); + } + + colShape.setMargin(0.05); + + let localInertia = new Ammo.btVector3(0, 0, 0); + colShape.calculateLocalInertia(inMass.get(), localInertia); + + let transform = new Ammo.btTransform(); + let motionState = new Ammo.btDefaultMotionState(transform); + + let rbInfo = new Ammo.btRigidBodyConstructionInfo(inMass.get(), motionState, colShape, localInertia); + let body = new Ammo.btRigidBody(rbInfo); + world.addRigidBody(body); + + world.setBodyMeta(body, + { + "name": scene.nodes[i].name, + "mass": inMass.get(), + + }); + + bodies.push( + { + "node": scene.nodes[i], + "motionState": motionState, + "body": body + }); + } + + outNum.set(bodies.length); + + added = true; +} + + +}; + +Ops.Physics.Ammo.GltfAmmoBodies.prototype = new CABLES.Op(); +CABLES.OPS["ea7553aa-0836-4512-9253-34b86d62accc"]={f:Ops.Physics.Ammo.GltfAmmoBodies,objName:"Ops.Physics.Ammo.GltfAmmoBodies"}; + + + + +// ************************************************************** +// +// Ops.Points.PointsCircle +// +// ************************************************************** + +Ops.Points.PointsCircle = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + outArr = op.outArray("Points", 3), + percent = op.inValueSlider("percent", 1), + segments = op.inValue("segments", 40), + radius = op.inValue("radius", 1), + outTotalPoints = op.outNumber("Total points"), + outArrayLength = op.outNumber("Array lengths"); + +radius.onChange = + percent.onChange = + segments.onChange = calcArray; + +function calcArray() +{ + const segs = Math.max(3, Math.floor(segments.get())); + const points = []; + + let count = 0; + for (let i = 0; i < segs * percent.get(); i++) + { + let degInRad = (360 / segs) * i * CGL.DEG2RAD; + let posx = Math.cos(degInRad) * radius.get(); + let posy = Math.sin(degInRad) * radius.get(); + + points.push(posx); + points.push(posy); + points.push(0); + } + + outArr.set(null); + outArr.set(points); + outTotalPoints.set(points.length / 3); + outArrayLength.set(points.length); +} + +calcArray(); + + +}; + +Ops.Points.PointsCircle.prototype = new CABLES.Op(); +CABLES.OPS["ef54ba67-8b1d-41b0-8140-bb97061eaa97"]={f:Ops.Points.PointsCircle,objName:"Ops.Points.PointsCircle"}; + + + + +// ************************************************************** +// +// Ops.Points.PointsCube +// +// ************************************************************** + +Ops.Points.PointsCube = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const numx = op.inValueInt("num x", 5), + numy = op.inValueInt("num y", 5), + numz = op.inValueInt("num z", 5), + mul = op.inValue("mul", 1), + center = op.inValueBool("center", true), + outArray = op.outArray("Array out"), + idx = op.outNumber("Total points"), + arrayLengthOut = op.outNumber("Array length"); + +let newArr = []; +outArray.set(newArr); + +numx.onChange = +numy.onChange = +numz.onChange = +mul.onChange = +center.onChange = update; + +function update() +{ + newArr.length = 0; + + let subX = 0; + let subY = 0; + let subZ = 0; + + if (center.get()) + { + subX = ((numx.get() - 1) * mul.get()) / 2.0; + subY = ((numy.get() - 1) * mul.get()) / 2.0; + subZ = ((numz.get() - 1) * mul.get()) / 2.0; + } + + let xTemp = 0; + let yTemp = 0; + let zTemp = 0; + + let m = mul.get(); + + for (var z = 0; z < numz.get(); z++) + { + zTemp = (z * m) - subZ; + + for (var y = 0; y < numy.get(); y++) + { + yTemp = (y * m) - subY; + + for (var x = 0; x < numx.get(); x++) + { + xTemp = (x * m) - subX; + + newArr.push(xTemp); + newArr.push(yTemp); + newArr.push(zTemp); + } + } + } + idx.set(x * y * z); + outArray.set(null); + outArray.set(newArr); + arrayLengthOut.set(newArr.length); +} + +update(); + + +}; + +Ops.Points.PointsCube.prototype = new CABLES.Op(); +CABLES.OPS["6030193b-089c-4565-a7b8-d837501ded52"]={f:Ops.Points.PointsCube,objName:"Ops.Points.PointsCube"}; + + + + +// ************************************************************** +// +// Ops.Points.PointsHexagonGrid +// +// ************************************************************** + +Ops.Points.PointsHexagonGrid = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const numxIn = op.inInt("Rows", 5), + numyIn = op.inInt("Colums", 5), + pointyOrTopped = op.inSwitch("Hex facing", ["Pointy", "Topped"], "Topped"), + flipCorners = op.inBool("Flip corners", false), + tileXOffsetIn = op.inFloat("Tile X offset", 1), + tileYOffsetIn = op.inFloat("Tile Y offset", 1), + multiplierIn = op.inFloat("Multiplier", 1), + arrayOut = op.outArray("Array out"); + +tileXOffsetIn.onChange = tileYOffsetIn.onChange = numxIn.onChange = numyIn.onChange = multiplierIn.onChange = flipCorners.onChange = pointyOrTopped.onChange = update; + +op.setPortGroup("Dimensions", [numxIn, numyIn]); +op.setPortGroup("Orientation", [flipCorners, pointyOrTopped]); +op.setPortGroup("Spacing", [tileXOffsetIn, tileYOffsetIn, multiplierIn]); + +update(); + +function update() +{ + const arr = []; + + let offsetX = 0; + let offsetY = 0; + + let w = 0; + let h = 0; + + const multiplier = multiplierIn.get(); + + if (pointyOrTopped.get() === "Pointy") + { + w = numyIn.get(); + h = numxIn.get(); + + offsetX = tileXOffsetIn.get() * 1.7; + offsetY = tileYOffsetIn.get() * 1.432; + + for (let x = 0; x < w; x++) + { + for (let y = 0; y < h; y++) + { + let yFlipped = y; + if (flipCorners.get()) yFlipped = y + 1; + + if (yFlipped % 2 == 0) + { + arr.push((x - w / 2) * offsetX * multiplier); + arr.push((y - h / 2) * offsetY * multiplier); + arr.push(0); + } + else + { + arr.push( + ((x - w / 2) * offsetX + offsetX / 2) * multiplier + ); + arr.push((y - h / 2) * offsetY * multiplier); + arr.push(0); + } + } + } + } + else + { + w = numxIn.get(); + h = numyIn.get(); + + offsetX = tileYOffsetIn.get() * 1.7; + offsetY = tileXOffsetIn.get() * 1.432; + + for (let x = 0; x < w; x++) + { + for (let y = 0; y < h; y++) + { + let yFlipped = y; + if (flipCorners.get()) yFlipped = y + 1; + + if (yFlipped % 2 == 0) + { + arr.push((y - h / 2) * offsetY * multiplier); + arr.push((x - w / 2) * offsetX * multiplier); + arr.push(0); + } + else + { + arr.push((y - h / 2) * offsetY * multiplier); + arr.push( + ((x - w / 2) * offsetX + offsetX / 2) * multiplier + ); + arr.push(0); + } + } + } + } + arrayOut.set(arr); +} + + +}; + +Ops.Points.PointsHexagonGrid.prototype = new CABLES.Op(); +CABLES.OPS["989c4d70-8567-47f2-8df3-85c68d422107"]={f:Ops.Points.PointsHexagonGrid,objName:"Ops.Points.PointsHexagonGrid"}; + + + + +// ************************************************************** +// +// Ops.Points.PointsPlane_v2 +// +// ************************************************************** + +Ops.Points.PointsPlane_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inNumX = op.inValueInt("Rows", 32), + inNumY = op.inValueInt("Columns", 32), + inHeight = op.inFloat("Width", 2), + inWidth = op.inFloat("Height", 2), + inRowOffset = op.inFloat("Row Offset", 0), + inCenter = op.inValueBool("Center", true), + outArr = op.outArray("Result", [], 3), + outTotalPoints = op.outNumber("Total points"), + outArrayLength = op.outNumber("Array length"), + outRowNums = op.outArray("Row Numbers", [], 1), + outColNums = op.outArray("Column Numbers", [], 1); + +inNumX.onChange = +inNumY.onChange = +inCenter.onChange = +inWidth.onChange = +inRowOffset.onChange = +inHeight.onChange = generate; + +const arr = []; +const arrRowNums = []; +const arrColNums = []; +outArr.set(arr); +generate(); + +function generate() +{ + arr.length = 0; + const numX = Math.max(0, inNumX.get()); + const numY = Math.max(0, inNumY.get()); + + let stepX = 0; + let stepY = 0; + + // to avoid divide by zero + if (numX == 1) + { + stepX = inWidth.get() / (numX); + } + else + { + stepX = inWidth.get() / (numX - 1); + } + if (numY == 1) + { + stepY = inHeight.get() / (numY); + } + else + { + stepY = inHeight.get() / (numY - 1); + } + + let i = 0; + + let centerX = 0; + let centerY = 0; + + if (inCenter.get()) + { + centerX = inWidth.get() / 2; + centerY = inHeight.get() / 2; + } + + const l = Math.floor(numX) * Math.floor(numY) * 3; + + arr.length = l; + arrColNums.length = l / 3; + arrRowNums.length = l / 3; + + let offRow = inRowOffset.get(); + let off = 0; + for (let y = 0; y < numY; y++) + { + for (let x = 0; x < numX; x++) + { + off = 0; + if (x % 2 == 0 && offRow)off = offRow; + + arrColNums[i / 3] = y; + arrRowNums[i / 3] = x; + + arr[i++] = stepY * y - centerY + off; + arr[i++] = stepX * x - centerX; + + arr[i++] = 0; + } + } + + outRowNums.set(null); + outRowNums.set(arrRowNums); + + outColNums.set(null); + outColNums.set(arrColNums); + + outArr.set(null); + outArr.set(arr); + outTotalPoints.set(arr.length / 3); + outArrayLength.set(arr.length); +} + + +}; + +Ops.Points.PointsPlane_v2.prototype = new CABLES.Op(); +CABLES.OPS["d453f898-17d4-4e2c-b8c7-7b7b34c0ff68"]={f:Ops.Points.PointsPlane_v2,objName:"Ops.Points.PointsPlane_v2"}; + + + + +// ************************************************************** +// +// Ops.Points.PointsSphereRandom +// +// ************************************************************** + +Ops.Points.PointsSphereRandom = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + num = op.inValueInt("Amount of points", 100), + size = op.inValue("Sphere size", 1), + seed = op.inValue("Random seed", 0), + distRand = op.inValueSlider("Random distance from sphere", 0), + distrib = op.inValueSelect("Distribution", ["Uniform", "Poles", "Half"], "Uniform"), + outArray = op.outArray("Array out", 3), + totalPointsOut = op.outNumber("Total points"), + arrayLengthOut = op.outNumber("Array length"); + +let newArr = []; +outArray.set(newArr); + +seed.onChange = + num.onChange = + size.onChange = + distrib.onChange = + distRand.onChange = + outArray.onLinkChanged = generate; + +generate(); + +function generate() +{ + const verts = []; + verts.length = Math.max(0, Math.round(num.get()) * 3); + + Math.randomSeed = seed.get(); + + let rndq = quat.create(); + let tempv = vec3.create(); + + let dist = 0; + if (distrib.get() == "Poles")dist = 1; + if (distrib.get() == "Half")dist = 2; + + let dRand = distRand.get(); + + for (let i = 0; i < num.get(); i++) + { + if (dist == 1 || dist == 2) + { + rndq[0] = Math.seededRandom(); + rndq[1] = Math.seededRandom(); + rndq[2] = Math.seededRandom(); + rndq[3] = Math.seededRandom(); + } + else + { + rndq[0] = Math.seededRandom() * 2.0 - 1.0; + rndq[1] = Math.seededRandom() * 2.0 - 1.0; + rndq[2] = Math.seededRandom() * 2.0 - 1.0; + rndq[3] = Math.seededRandom() * 2.0 - 1.0; + } + + quat.normalize(rndq, rndq); + + if (dist == 2) + { + tempv[0] = size.get(); + } + else + { + if (i % 2 === 0) tempv[0] = -size.get(); + else tempv[0] = size.get(); + } + + tempv[1] = 0; + tempv[2] = 0; + + if (dRand !== 0) tempv[0] -= Math.random() * dRand; + + vec3.transformQuat(tempv, tempv, rndq); + verts[i * 3] = tempv[0]; + verts[i * 3 + 1] = tempv[1]; + verts[i * 3 + 2] = tempv[2]; + } + + outArray.set(null); + outArray.set(verts); + totalPointsOut.set(verts.length / 3); + arrayLengthOut.set(verts.length); +} + + +}; + +Ops.Points.PointsSphereRandom.prototype = new CABLES.Op(); +CABLES.OPS["1ea17de7-adad-4053-943a-4874bccf54e9"]={f:Ops.Points.PointsSphereRandom,objName:"Ops.Points.PointsSphereRandom"}; + + + + +// ************************************************************** +// +// Ops.Sequence +// +// ************************************************************** + +Ops.Sequence = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + cleanup = op.inTriggerButton("Clean up connections"); + +const + exes = [], + triggers = [], + num = 16; + +let updateTimeout = null; + +exe.onTriggered = triggerAll; +cleanup.onTriggered = clean; +cleanup.setUiAttribs({ "hidePort": true }); +cleanup.setUiAttribs({ "hideParam": true }); + +for (let i = 0; i < num; i++) +{ + const p = op.outTrigger("trigger " + i); + triggers.push(p); + p.onLinkChanged = updateButton; + + if (i < num - 1) + { + let newExe = op.inTrigger("exe " + i); + newExe.onTriggered = triggerAll; + exes.push(newExe); + } +} + +function updateButton() +{ + clearTimeout(updateTimeout); + updateTimeout = setTimeout(() => + { + let show = false; + for (let i = 0; i < triggers.length; i++) + if (triggers[i].links.length > 1) show = true; + + cleanup.setUiAttribs({ "hideParam": !show }); + + if (op.isCurrentUiOp()) op.refreshParams(); + }, 60); +} + +function triggerAll() +{ + for (let i = 0; i < triggers.length; i++) triggers[i].trigger(); +} + +function clean() +{ + let count = 0; + for (let i = 0; i < triggers.length; i++) + { + let removeLinks = []; + + if (triggers[i].links.length > 1) + for (let j = 1; j < triggers[i].links.length; j++) + { + while (triggers[count].links.length > 0) count++; + + removeLinks.push(triggers[i].links[j]); + const otherPort = triggers[i].links[j].getOtherPort(triggers[i]); + op.patch.link(op, "trigger " + count, otherPort.parent, otherPort.name); + count++; + } + + for (let j = 0; j < removeLinks.length; j++) removeLinks[j].remove(); + } + updateButton(); +} + + +}; + +Ops.Sequence.prototype = new CABLES.Op(); +CABLES.OPS["a466bc1f-06e9-4595-8849-bffb9fe22f99"]={f:Ops.Sequence,objName:"Ops.Sequence"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.Button_v2 +// +// ************************************************************** + +Ops.Sidebar.Button_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +const parentPort = op.inObject("link"); +const buttonTextPort = op.inString("Text", "Button"); + +// outputs +const siblingsPort = op.outObject("childs"); +const buttonPressedPort = op.outTrigger("Pressed Trigger"); + +const inGreyOut = op.inBool("Grey Out", false); +const inVisible = op.inBool("Visible", true); + +// vars +const el = document.createElement("div"); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar--button"); +const input = document.createElement("div"); +input.classList.add("sidebar__button-input"); +el.appendChild(input); +input.addEventListener("click", onButtonClick); +const inputText = document.createTextNode(buttonTextPort.get()); +input.appendChild(inputText); +op.toWorkNeedsParent("Ops.Sidebar.Sidebar"); + +// events +parentPort.onChange = onParentChanged; +buttonTextPort.onChange = onButtonTextChanged; +op.onDelete = onDelete; + +const greyOut = document.createElement("div"); +greyOut.classList.add("sidebar__greyout"); +el.appendChild(greyOut); +greyOut.style.display = "none"; + +inGreyOut.onChange = function () +{ + greyOut.style.display = inGreyOut.get() ? "block" : "none"; +}; + +inVisible.onChange = function () +{ + el.style.display = inVisible.get() ? "block" : "none"; +}; + +function onButtonClick() +{ + buttonPressedPort.trigger(); +} + +function onButtonTextChanged() +{ + const buttonText = buttonTextPort.get(); + input.textContent = buttonText; + if (CABLES.UI) + { + op.setTitle("Button: " + buttonText); + } +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(el) +{ + if (el) + { + el.style.display = "block"; + } +} + +function hideElement(el) +{ + if (el) + { + el.style.display = "none"; + } +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.Button_v2.prototype = new CABLES.Op(); +CABLES.OPS["5e9c6933-0605-4bf7-8671-a016d917f327"]={f:Ops.Sidebar.Button_v2,objName:"Ops.Sidebar.Button_v2"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.ColorPicker_v3 +// +// ************************************************************** + +Ops.Sidebar.ColorPicker_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colorrick_css":".colorRick_dialog\n{\n --width:256px;\n --height:256px;\n --width-hue:20px;\n --pad:10px;\n --colorblock-height:20px;\n --inputcontainer-height:120px;\n\n background-color: #333;\n width:calc(var(--width) + 3*var(--pad) + var(--width-hue));\n height:calc(var(--height) + var(--colorblock-height) + 30px + var(--inputcontainer-height));\n position: absolute;\n border-radius: 10px;\n overflow: hidden;\n z-index: 999999;\n\n}\n\n.colorRick_dialog *\n{\n font-size: 13px;\n}\n\n.colorRick_area\n{\n width:var(--width);\n height:var(--height);\n left:var(--pad);\n top:var(--pad);\n background: linear-gradient(to right, rgb(255, 255, 255), rgb(0, 255, 11));\n position: absolute;\n cursor:pointer;\n}\n\n.colorRick_brightness\n{\n background: linear-gradient(hsla(0,0%,100%,0),#000);\n width:100%;\n height:100%;\n pointer-events: none;\n}\n\n.colorRick_hue\n{\n cursor:pointer;\n top:var(--pad);\n background:linear-gradient(red,#f0f 17%,#00f 34%,#0ff 50%,#0f0 67%,#ff0 84%,red);\n height:var(--height);\n\n width:var(--width-hue);\n position: absolute;\n left:calc(var(--width) + var(--pad) + var(--pad));\n}\n\n.colorRick_preview\n{\n position: absolute;\n width:calc(var(--width) / 10 * 9);\n height:var(--colorblock-height);\n top:calc(var(--height) + 20px);\n margin-left:10px;\n}\n\n.colorRick_preview_orig\n{\n margin-left:calc(calc(var(--width) / 10 * 9) + 10px);\n width:calc(var(--width) / 10);\n cursor:pointer;\n}\n\n.colorRick_cursor\n{\n width:4px;\n height:4px;\n border:1px solid transparent;\n background-color: white;\n position: absolute;\n pointer-events: none;\n border-radius: 100%;\n}\n\n.colorRick_cursor_hue\n{\n position: absolute;\n width:24px;\n margin-left: -2px;\n height:0px;\n border-top:1px solid white;\n border-bottom:1px solid white;\n position: absolute;\n pointer-events: none;\n}\n\n.colorRick_inputcontainer\n{\n position: absolute;\n\n height:var(--inputcontainer-height);\n /* top:250px; */\n top:calc(var(--height) + 20px + 20px + 10px);\n left:0px;\n width:100%;\n background-color: #000;\n padding:10px;\n}\n\n.colorRick_inputcontainer table\n{\n width:var(--width);\n}\n\n.colorRick_inputcontainer, .colorRick_inputcontainer table\n{\n color:#999;\n}\n\n.colorRick_input\n{\n background-color: #444;\n border:0px solid transparent;\n opacity: 1;\n color:#ddd;\n}\n\n.colorRick_input_small\n{\n width:60px;\n margin-left:10px;\n}\n\n.colorRick_input_hex\n{\n width:100px;\n}\n\n.colorRick_invalid\n{\n opacity: 0.5;\n}\n\n.colorRick_inputcontainer table\n{\n /* width:90%; */\n}\n\n.colorRick_inputcontainer table,.colorRick_inputcontainer table td, .colorRick_inputcontainer table tr\n{\n /* pointer-events: none; */\n user-select: none;\n\n vertical-align: top;\n}\n\n.colorRick_inputcontainer table td.right\n{\n text-align: right;\n height: 30px;\n}\n",}; +// constants +const DEFAULT_COLOR_HEX = "#07F78C"; + +// inputs +const parentPort = op.inObject("Link"); +const labelPort = op.inString("Text", "Hex Color"); +const defaultColorArr = hexToRgbNorm(DEFAULT_COLOR_HEX); +const inputRedPort = op.inValueSlider("Input Red", defaultColorArr[0]); +const inputGreenPort = op.inValueSlider("Input Green", defaultColorArr[1]); +const inputBluePort = op.inValueSlider("Input Blue", defaultColorArr[2]); +// const inputValuePort = op.inValueString('Input', DEFAULT_COLOR_HEX); +const setDefaultValueButtonPort = op.inTriggerButton("Set Default"); +const defaultValuePort = op.inValueString("Default", DEFAULT_COLOR_HEX); +defaultValuePort.setUiAttribs({ "hidePort": true, "greyout": true }); + +// outputs +const siblingsPort = op.outObject("Children"); +const redPort = op.outNumber("Red", 0.0); +const greenPort = op.outNumber("Green", 0.0); +const bluePort = op.outNumber("Blue", 0.0); + +const outHex = op.outString("Hex", DEFAULT_COLOR_HEX); + +// vars +const el = document.createElement("div"); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.addEventListener("dblclick", function () +{ + let defaultValue = defaultValuePort.get(); + input.setAttribute("value", defaultValue); + if (defaultValue) + { + if (defaultValue.length === 6 && defaultValue.charAt(0) !== "#") + { + defaultValue = "#" + defaultValue; + } + if (defaultValue.length === 7) + { + input.value = defaultValue; + colorInput.value = defaultValue; + setColorOutPorts(defaultValue); + } + } +}); + +el.classList.add("sidebar__item"); +el.classList.add("sidebar__color-picker"); +el.classList.add("sidebar__reloadable"); + +const styleEle = document.createElement("style"); +styleEle.type = "text/css"; +styleEle.textContent = attachments.colorrick_css; + +const head = document.getElementsByTagName("body")[0]; +head.appendChild(styleEle); + +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelTextNode = document.createTextNode(labelPort.get()); +label.appendChild(labelTextNode); +el.appendChild(label); +// var inputWrapper = document.createElement('div'); +// inputWrapper.classList.add('sidebar__text-input-input-wrapper'); +// el.appendChild(inputWrapper); +const input = document.createElement("input"); +input.classList.add("sidebar__color-picker-input"); +/* input.classList.add('jscolor'); */ /* color picker library */ +input.setAttribute("type", "text"); +input.setAttribute("value", defaultValuePort.get()); +// inputWrapper.appendChild(input); +el.appendChild(input); +input.addEventListener("input", onInput); +const colorInput = document.createElement("div"); +colorInput.classList.add("sidebar__color-picker-color-input"); +// colorInput.setAttribute("type", "color"); +colorInput.style.backgroundColor = defaultValuePort.get(); +// colorInput.addEventListener("change", onColorPickerChange, false); + +colorInput.addEventListener("click", function () +{ + new ColorRick( + { + "ele": this, + "color": this.style.backgroundColor || "#ff0000", + "onChange": (col) => + { + const hex = col.hex(); + this.style.backgroundColor = hex; + setColorOutPorts(hex); + input.value = hex; + outHex.set(hex); + setInputsByHex(hex); + + op.refreshParams(); + } + }); +}); + +el.appendChild(colorInput); +input.addEventListener("input", onInput); + +onDefaultValueChanged(); /* initialize once */ + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +defaultValuePort.onChange = onDefaultValueChanged; +op.onDelete = onDelete; +setDefaultValueButtonPort.onTriggered = setDefaultColor; +inputRedPort.onChange = inputColorChanged; +inputGreenPort.onChange = inputColorChanged; +inputBluePort.onChange = inputColorChanged; + +// functions + +function inputColorChanged() +{ + const hex = getInputColorHex(); + colorInput.style.backgroundColor = hex; + input.value = hex; + setColorOutPorts(hex); +} + +/** + * Returns the color of the op params ("input red", "input green", "input blue") as hex + */ +function getInputColorHex() +{ + const r = CABLES.clamp(inputRedPort.get(), 0, 1); + const g = CABLES.clamp(inputGreenPort.get(), 0, 1); + const b = CABLES.clamp(inputBluePort.get(), 0, 1); + const hex = rgbNormToHex(r, g, b); + return hex; +} + +function setDefaultColor() +{ + // let hexCol = inputValuePort.get().trim(); + const hex = getInputColorHex(); + defaultValuePort.set(hex); + outHex.set(hex); + op.refreshParams(); +} + +/* +function onInputValuePortChange() { + let hexCol = inputValuePort.get().trim(); + if(hexCol.length === 6 && hexCol.charAt(0) !== '#') { + hexCol = '#' + hexCol; + } + if(hexCol.length === 7) { + colorInput.value = hexCol; + input.value = hexCol; + setColorOutPorts(hexCol); + } +} +*/ + +function hexToRgbNorm(hexColor) +{ + if (!hexColor || hexColor.length !== 7) { return; } + return hexColor + .match(/[A-Za-z0-9]{2}/g) + .map(function (v) + { + return parseInt(v, 16) / 255; + }); +} + +/** + * Helper for rgbNormToHex / rgbToHex + * Converts a number in range [0..255] to hex [00..FF] (with left padding) + */ +function componentToHex(c) +{ + const hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; +} + +/** + * r, g, b in range [0..1] + * @returns {string} e.g. "#ff0000" + */ +function rgbNormToHex(r, g, b) +{ + return "#" + componentToHex(Math.floor(255 * r)) + componentToHex(Math.floor(255 * g)) + componentToHex(Math.floor(255 * b)); +} + +/** + * Sets the op param color input ports by hex value (e.g. "#FF0000") + * Does NOT update the gui + */ +function setInputsByHex(hex) +{ + const colorArr = hexToRgbNorm(hex); + inputRedPort.set(colorArr[0]); + inputGreenPort.set(colorArr[1]); + inputBluePort.set(colorArr[2]); + outHex.set(hex); +} + +function onInput(ev) +{ + let newValue = ev.target.value; + if (newValue.length === 6 && newValue.charAt(0) !== "#") + { + newValue = "#" + newValue; + } + if (newValue.length === 7) + { + colorInput.value = newValue; + setColorOutPorts(newValue); + // inputValuePort.set(newValue) + setInputsByHex(newValue); + outHex.set(newValue); + op.refreshParams(); + } +} + +// hex must be 7 digits +function setColorOutPorts(hex) +{ + const colorArr = hexToRgbNorm(hex); + outHex.set(hex); + redPort.set(colorArr[0]); + greenPort.set(colorArr[1]); + bluePort.set(colorArr[2]); +} + +function onDefaultValueChanged() +{ + let defaultValue = defaultValuePort.get(); + input.setAttribute("value", defaultValue); + if (defaultValue) + { + if (defaultValue.length === 6 && defaultValue.charAt(0) !== "#") + { + defaultValue = "#" + defaultValue; + } + if (defaultValue.length === 7) + { + input.value = defaultValue; + colorInput.value = defaultValue; + setColorOutPorts(defaultValue); + } + } +} + +function onLabelTextChanged() +{ + const labelText = labelPort.get(); + label.textContent = labelText; + + if (CABLES.UI) op.setTitle("Color Picker: " + labelText); +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(el) +{ + if (el) + { + el.style.display = "block"; + } +} + +function hideElement(el) +{ + if (el) + { + el.style.display = "none"; + } +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.ColorPicker_v3.prototype = new CABLES.Op(); +CABLES.OPS["52dc1ef8-deb0-4664-a924-4c5548aa8a55"]={f:Ops.Sidebar.ColorPicker_v3,objName:"Ops.Sidebar.ColorPicker_v3"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.DisplayValue_v2 +// +// ************************************************************** + +Ops.Sidebar.DisplayValue_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +const parentPort = op.inObject("link"); +const labelPort = op.inString("Text", "Value"); +const valuePort = op.inString("Value", ""); + +// outputs +const siblingsPort = op.outObject("childs"); + +// vars +const el = document.createElement("div"); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar__value-display"); +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelTextNode = document.createTextNode(labelPort.get()); +label.appendChild(labelTextNode); +el.appendChild(label); +const value = document.createElement("div"); +value.textContent = valuePort.get(); +value.classList.add("sidebar__item-value-label"); +el.appendChild(value); + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +valuePort.onChange = onValueChanged; +op.onDelete = onDelete; + +// functions + +function onValueChanged() +{ + value.textContent = valuePort.get(); +} + +function onLabelTextChanged() +{ + const labelText = labelPort.get(); + label.textContent = labelText; + if (CABLES.UI) + { + op.setTitle("Value: " + labelText); + } +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(element) +{ + if (element) + { + element.style.display = "block"; + } +} + +function hideElement(element) +{ + if (element) + { + element.style.display = "none"; + } +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(element) +{ + if (element && element.parentNode && element.parentNode.removeChild) + { + element.parentNode.removeChild(element); + } +} + + +}; + +Ops.Sidebar.DisplayValue_v2.prototype = new CABLES.Op(); +CABLES.OPS["3dd9927e-0d34-4442-8a8a-0ab843aee6e3"]={f:Ops.Sidebar.DisplayValue_v2,objName:"Ops.Sidebar.DisplayValue_v2"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.DropDown_v2 +// +// ************************************************************** + +Ops.Sidebar.DropDown_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +const parentPort = op.inObject("Link"); +const labelPort = op.inString("Text", "Value"); +const valuesPort = op.inArray("Values"); +const defaultValuePort = op.inString("Default", ""); +const inGreyOut = op.inBool("Grey Out", false); +const inVisible = op.inBool("Visible", true); +const inSize = op.inInt("Lines", 1); +const setDefaultValueButtonPort = op.inTriggerButton("Set Default"); +setDefaultValueButtonPort.onTriggered = setDefault; + +// outputs +const siblingsPort = op.outObject("Children"); +const valuePort = op.outString("Result", defaultValuePort.get()); +const outIndex = op.outNumber("Index"); + +defaultValuePort.setUiAttribs({ "title": "Input" }); + +// vars +const el = document.createElement("div"); +el.addEventListener("dblclick", function () +{ + valuePort.set(defaultValuePort.get()); + const optionElements = input.querySelectorAll("option"); + optionElements.forEach(function (optionElement, index) + { + if (optionElement.value.trim() === defaultValuePort.get()) + { + optionElement.selected = true; + outIndex.set(index); + } + else + { + optionElement.removeAttribute("selected"); + } + }); +}); + +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar__select"); +el.classList.add("sidebar__reloadable"); + +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelText = document.createTextNode(labelPort.get()); +label.appendChild(labelText); +el.appendChild(label); +const input = document.createElement("select"); + +input.classList.add("sidebar__select-select"); +el.appendChild(input); +input.addEventListener("input", onInput); + +const greyOut = document.createElement("div"); +greyOut.classList.add("sidebar__greyout"); +el.appendChild(greyOut); +greyOut.style.display = "none"; + +inGreyOut.onChange = function () +{ + greyOut.style.display = inGreyOut.get() ? "block" : "none"; +}; + +inVisible.onChange = function () +{ + el.style.display = inVisible.get() ? "block" : "none"; +}; + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +defaultValuePort.onChange = onDefaultValueChanged; +op.onDelete = onDelete; +valuesPort.onChange = onValuesPortChange; + +let options = []; +// functions + +inSize.onChange = () => +{ + input.setAttribute("size", inSize.get()); +}; + +op.onLoaded = function () +{ + valuePort.set(defaultValuePort.get()); +}; + +function onValuesPortChange() +{ + // remove all children + while (input.lastChild) + { + input.removeChild(input.lastChild); + } + options = valuesPort.get(); + const defaultValue = defaultValuePort.get(); + if (options) + { + options.forEach(function (option) + { + const optionEl = document.createElement("option"); + + optionEl.setAttribute("value", option); + if (option === defaultValue || option === valuePort.get()) + { + optionEl.setAttribute("selected", ""); + } + const textEl = document.createTextNode(option); + optionEl.appendChild(textEl); + input.appendChild(optionEl); + }); + } + else + { + valuePort.set(""); + } + + outIndex.set(0); + setSelectedProperty(); /* set the selected property for the default value */ +} + +let finalIndex = 0; +function setSelectedProperty(defaultinput) +{ + const optionElements = input.querySelectorAll("option"); + + let finalEle = null; + + optionElements.forEach(function (optionElement, index) + { + if (optionElement.value.trim() === valuePort.get()) + { + finalEle = optionElement; + finalIndex = index; + } + optionElement.removeAttribute("selected"); + }); + + if (defaultinput) + { + const defaultItem = defaultValuePort.get() + "".trim(); + + optionElements.forEach(function (optionElement, index) + { + if (optionElement.value.trim() === defaultItem) + { + finalEle = optionElement; + finalIndex = index; + } + + optionElement.removeAttribute("selected"); + }); + } + + if (finalEle) finalEle.setAttribute("selected", ""); + outIndex.set(finalIndex); +} + +function onInput(ev) +{ + valuePort.set(ev.target.value); + outIndex.set(options.indexOf(ev.target.value)); + setSelectedProperty(); +} + +function onDefaultValueChanged() +{ + const defaultValue = defaultValuePort.get(); + valuePort.set(defaultValue); + input.value = defaultValue; + setSelectedProperty(); +} + +function onLabelTextChanged() +{ + const lblText = labelPort.get(); + label.textContent = lblText; + if (CABLES.UI) + { + op.setTitle("Dropdown: " + lblText); + } +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(ele) +{ + if (ele) + { + ele.style.display = "block"; + } + setSelectedProperty(); +} + +function hideElement(ele) +{ + if (ele) + { + ele.style.display = "none"; + } +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(ele) +{ + if (ele && ele.parentNode && ele.parentNode.removeChild) + { + ele.parentNode.removeChild(ele); + } +} + +function setDefault() +{ + defaultValuePort.set(input.value); + op.refreshParams(); +} + + +}; + +Ops.Sidebar.DropDown_v2.prototype = new CABLES.Op(); +CABLES.OPS["7b3f93d6-4de1-41fd-aa26-e74c8285c662"]={f:Ops.Sidebar.DropDown_v2,objName:"Ops.Sidebar.DropDown_v2"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.Group +// +// ************************************************************** + +Ops.Sidebar.Group = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +let parentPort = op.inObject("link"); +let labelPort = op.inString("Text", "Group"); +const inShowTitle = op.inBool("Show Title", true); +let defaultMinimizedPort = op.inValueBool("Default Minimized"); +const inVisible = op.inBool("Visible", true); + +// outputs +let nextPort = op.outObject("next"); +let childrenPort = op.outObject("childs"); + +inVisible.onChange = function () +{ + el.style.display = inVisible.get() ? "block" : "none"; +}; + +// vars +let el = document.createElement("div"); +el.dataset.op = op.id; +el.classList.add("sidebar__group"); +onDefaultMinimizedPortChanged(); +let header = document.createElement("div"); +header.classList.add("sidebar__group-header"); +header.classList.add("cablesEle"); +el.appendChild(header); +header.addEventListener("click", onClick); +let headerTitle = document.createElement("div"); +headerTitle.classList.add("sidebar__group-header-title"); +// headerTitle.textContent = labelPort.get(); +header.appendChild(headerTitle); +let headerTitleText = document.createElement("span"); +headerTitleText.textContent = labelPort.get(); +headerTitleText.classList.add("sidebar__group-header-title-text"); +headerTitle.appendChild(headerTitleText); +let icon = document.createElement("span"); +icon.classList.add("sidebar__group-header-icon"); +icon.classList.add("iconsidebar-chevron-up"); +headerTitle.appendChild(icon); +let groupItems = document.createElement("div"); +groupItems.classList.add("sidebar__group-items"); +el.appendChild(groupItems); +op.toWorkPortsNeedToBeLinked(parentPort); + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +defaultMinimizedPort.onChange = onDefaultMinimizedPortChanged; +op.onDelete = onDelete; + +// functions + +inShowTitle.onChange = () => +{ + if (inShowTitle.get())header.style.display = "block"; + else header.style.display = "none"; +}; + +function onDefaultMinimizedPortChanged() +{ + if (defaultMinimizedPort.get()) + { + el.classList.add("sidebar__group--closed"); + } + else + { + el.classList.remove("sidebar__group--closed"); + } +} + +function onClick(ev) +{ + ev.stopPropagation(); + el.classList.toggle("sidebar__group--closed"); +} + +function onLabelTextChanged() +{ + let labelText = labelPort.get(); + headerTitleText.textContent = labelText; + if (CABLES.UI) + { + op.setTitle("Group: " + labelText); + } +} + +function onParentChanged() +{ + childrenPort.set(null); + let parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + childrenPort.set({ + "parentElement": groupItems, + "parentOp": op, + }); + nextPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(el) +{ + if (el) + { + el.style.display = "block"; + } +} + +function hideElement(el) +{ + if (el) + { + el.style.display = "none"; + } +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.Group.prototype = new CABLES.Op(); +CABLES.OPS["86ea2333-b51c-48ed-94c2-8b7b6e9ff34c"]={f:Ops.Sidebar.Group,objName:"Ops.Sidebar.Group"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.Incrementor_v2 +// +// ************************************************************** + +Ops.Sidebar.Incrementor_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +const parentPort = op.inObject("link"); +const labelPort = op.inString("Label", "Incrementor"); +const inMin = op.inValue("min", 0); +const inMax = op.inValue("max", 10); +const inStepsize = op.inValue("stepsize", 1); +const inDefault = op.inValue("Default", 0); +const inValues = op.inArray("Values"); +const inSetDefault = op.inTriggerButton("Set Default"); +inSetDefault.onTriggered = setDefaultValue; + +// outputs +const siblingsPort = op.outObject("childs"); +const outValue = op.outNumber("value"); + +// vars +let currentPosition = 0; + +const containerEl = document.createElement("div"); +containerEl.dataset.op = op.id; +containerEl.classList.add("cablesEle"); +containerEl.classList.add("sidebar__item"); +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +label.addEventListener("dblclick", function () +{ + outValue.set(inDefault.get()); +}); +const labelTextEl = document.createTextNode(labelPort.get()); +label.appendChild(labelTextEl); +containerEl.appendChild(label); + +const innerContainer = document.createElement("span"); +innerContainer.classList.add("sidebar__item__right"); + +// value +const valueEl = document.createElement("span"); +valueEl.style.marginRight = "10px"; + +let valueText = document.createTextNode(inMin.get()); +if (Array.isArray(inValues.get())) +{ + valueText = document.createTextNode(inValues.get()[currentPosition]); +} + +valueEl.appendChild(valueText); +innerContainer.appendChild(valueEl); + +// previous +const prevEl = document.createElement("span"); +prevEl.classList.add("sidebar--button"); +prevEl.style.marginRight = "3px"; +const prevInput = document.createElement("div"); +prevInput.classList.add("sidebar__button-input"); +prevInput.classList.add("minus"); +prevEl.appendChild(prevInput); +prevInput.addEventListener("click", onPrev); +const prevText = document.createTextNode("-"); +prevInput.appendChild(prevText); +innerContainer.appendChild(prevEl); + +// next +const nextEl = document.createElement("span"); +nextEl.classList.add("sidebar--button"); +const nextInput = document.createElement("div"); +nextInput.classList.add("sidebar__button-input"); +nextInput.classList.add("plus"); +nextEl.appendChild(nextInput); +nextInput.addEventListener("click", onNext); +const nextText = document.createTextNode("+"); +nextInput.appendChild(nextText); + +innerContainer.appendChild(nextEl); +containerEl.appendChild(innerContainer); + +op.toWorkNeedsParent("Ops.Sidebar.Sidebar"); + +function setDefaultValue() +{ + inDefault.set(outValue.get()); + op.refreshParams(); +} + +// events +parentPort.onChange = onParentChanged; +inValues.onChange = onValueChange; +labelPort.onChange = onLabelTextChanged; +op.onDelete = onDelete; + +op.onLoaded = op.onInit = function () +{ + if (Array.isArray(inValues.get())) + { + inDefault.setUiAttribs({ "greyout": true }); + } + else + { + outValue.set(inDefault.get()); + valueText.textContent = inDefault.get(); + } +}; + +function onValueChange() +{ + const values = inValues.get(); + let value = inMin.get(); + if (Array.isArray(values)) + { + value = values[currentPosition]; + inMin.setUiAttribs({ "greyout": true }); + inMax.set(values.length - 1); + inMax.setUiAttribs({ "greyout": true }); + inStepsize.setUiAttribs({ "greyout": true }); + inStepsize.set(1); + inDefault.setUiAttribs({ "greyout": true }); + inDefault.set(0); + } + else + { + inMin.setUiAttribs({ "greyout": false }); + inMax.setUiAttribs({ "greyout": false }); + inStepsize.setUiAttribs({ "greyout": false }); + inDefault.setUiAttribs({ "greyout": false }); + } + outValue.set(value); + valueText.textContent = value; +} + +function onNext() +{ + const values = inValues.get(); + let value = 0; + if (!Array.isArray(values)) + { + // no array given, increment/decrement according to params + const currentValue = outValue.get(); + value = Math.min(currentValue + inStepsize.get(), inMax.get()); + } + else + { + // user inputs an array, iterate fields, ignore min/max/stepsize + if (currentPosition < values.length - 1) + { + currentPosition += Math.ceil(inStepsize.get()); + } + value = values[currentPosition]; + } + valueText.textContent = value; + outValue.set(value); +} + +function onPrev() +{ + const values = inValues.get(); + let value = 0; + if (!Array.isArray(values)) + { + // no array given, increment/decrement according to params + const currentValue = outValue.get(); + value = Math.max(currentValue - inStepsize.get(), inMin.get()); + } + else + { + // user inputs an array, iterate fields, ignore min/max/stepsize + if (currentPosition > 0) + { + currentPosition -= Math.ceil(inStepsize.get()); + } + value = values[currentPosition]; + } + valueText.textContent = value; + outValue.set(value); +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(containerEl); + siblingsPort.set(parent); + } + else if (containerEl.parentElement) + { + // detach + containerEl.parentElement.removeChild(containerEl); + } +} + +function onLabelTextChanged() +{ + const labelText = labelPort.get(); + label.textContent = labelText; + + if (CABLES.UI) + { + op.setTitle(labelText); + } +} + +function onDelete() +{ + removeElementFromDOM(containerEl); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.Incrementor_v2.prototype = new CABLES.Op(); +CABLES.OPS["13932cbc-2bd4-4b2a-b6e0-cda6df4cec54"]={f:Ops.Sidebar.Incrementor_v2,objName:"Ops.Sidebar.Incrementor_v2"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.LocalFileToDataUrl +// +// ************************************************************** + +Ops.Sidebar.LocalFileToDataUrl = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +const parentPort = op.inObject("link"); +const labelPort = op.inString("Text", "Select File:"); +const inId = op.inValueString("Id", ""); +const inVisible = op.inBool("Visible", true); +const inGreyOut = op.inBool("Grey Out", false); +const inOpenDialog = op.inTriggerButton("Show Dialog"); +const reset = op.inTriggerButton("Reset"); + +// outputs + +const siblingsPort = op.outObject("childs"); +const outDataURL = op.outString("Data URL"); +outDataURL.ignoreValueSerialize = true; + +// vars +const el = document.createElement("div"); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar__text"); +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelText = document.createTextNode(labelPort.get()); +label.appendChild(labelText); +el.appendChild(label); + +const fileInputEle = document.createElement("input"); +fileInputEle.type = "file"; +fileInputEle.id = "file"; +fileInputEle.name = "file"; + +fileInputEle.style["background-color"] = "transparent"; +fileInputEle.style.width = "90%"; +fileInputEle.style.display = "none"; +// fileInputEle.style.float = "left"; + +const elReset = document.createElement("div"); +elReset.style.cursor = "pointer"; +elReset.style.position = "absolute"; +elReset.style.right = "10px"; +elReset.style["margin-top"] = "15px"; +elReset.innerHTML = "  ✕"; + +const fileInputButton = document.createElement("div"); +fileInputButton.classList.add("sidebar__button-input"); +fileInputButton.innerHTML = "Choose File"; +fileInputButton.onclick = () => { fileInputEle.click(); }; +inOpenDialog.onTriggered = () => { fileInputButton.click(); }; +fileInputButton.style["margin-top"] = "10px"; +fileInputButton.style.width = "80%"; + +el.appendChild(elReset); +el.appendChild(fileInputButton); +el.appendChild(fileInputEle); + +const imgEl = document.createElement("img"); + +fileInputEle.addEventListener("change", handleFileSelect, false); + +const greyOut = document.createElement("div"); +greyOut.classList.add("sidebar__greyout"); +el.appendChild(greyOut); +greyOut.style.display = "none"; + +inGreyOut.onChange = function () +{ + greyOut.style.display = inGreyOut.get() ? "block" : "none"; +}; + +inVisible.onChange = function () +{ + el.style.display = inVisible.get() ? "block" : "none"; +}; + +function onReset() +{ + fileInputEle.value = ""; + outDataURL.set(""); +} + +reset.onTriggered = onReset; +elReset.addEventListener("click", onReset); + +function handleFileSelect(evt) +{ + const reader = new FileReader(); + + reader.onabort = function (e) + { + op.log("File read cancelled"); + }; + + reader.onload = function (e) + { + outDataURL.set(e.target.result); + }; + + if (evt.target.files[0]) reader.readAsDataURL(evt.target.files[0]); + else outDataURL.set(""); +} + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +inId.onChange = onIdChanged; +op.onDelete = onDelete; + +op.toWorkNeedsParent("Ops.Sidebar.Sidebar"); + +// functions + +function onIdChanged() +{ + el.id = inId.get(); +} + +function onLabelTextChanged() +{ + const labelText = labelPort.get(); + label.textContent = labelText; +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(el) +{ + if (el) + { + el.style.display = "block"; + } +} + +function hideElement(el) +{ + if (el) + { + el.style.display = "none"; + } +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.LocalFileToDataUrl.prototype = new CABLES.Op(); +CABLES.OPS["c99d271e-aa5e-4a9d-a4d3-4c5137c189e8"]={f:Ops.Sidebar.LocalFileToDataUrl,objName:"Ops.Sidebar.LocalFileToDataUrl"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.LocalTextureFile +// +// ************************************************************** + +Ops.Sidebar.LocalTextureFile = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +let parentPort = op.inObject("link"), + labelPort = op.inString("Text", "Select File:"), + inId = op.inValueString("Id", ""), + + tfilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"], "linear"), + wrap = op.inValueSelect("Wrap", ["repeat", "mirrored repeat", "clamp to edge"], "clamp to edge"); + +// outputs +let siblingsPort = op.outObject("childs"); +const outTex = op.outTexture("Texture"); + +// vars +let el = document.createElement("div"); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar__text"); +let label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +let labelText = document.createTextNode(labelPort.get()); +label.appendChild(labelText); +el.appendChild(label); + +const fileInputEle = document.createElement("input"); +fileInputEle.type = "file"; +fileInputEle.id = "file"; +fileInputEle.name = "file"; +fileInputEle.style.width = "95%"; +el.appendChild(fileInputEle); + +outTex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + +const imgEl = document.createElement("img"); + +tfilter.onChange = wrap.onChange = () => +{ + fileInputEle.dispatchEvent(new Event("change")); +}; +fileInputEle.addEventListener("change", handleFileSelect, false); + +function handleFileSelect(evt) +{ + const reader = new FileReader(); + + reader.onabort = function (e) + { + op.log("File read cancelled"); + }; + + reader.onload = function (e) + { + let image = new Image(); + image.onerror = function (e) + { + outTex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + op.log("image error", e); + }; + image.onload = function (e) + { + let cgl_filter = CGL.Texture.FILTER_LINEAR; + let cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + else if (tfilter.get() == "Anisotropic") cgl_filter = CGL.Texture.FILTER_ANISOTROPIC; + + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + let tex = CGL.Texture.createFromImage(op.patch.cgl, image, { "filter": cgl_filter, "wrap": cgl_wrap }); + outTex.set(tex); + }; + image.src = e.target.result; + }; + + if (evt && evt.target && evt.target.files[0]) + reader.readAsDataURL(evt.target.files[0]); +} + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +inId.onChange = onIdChanged; +op.onDelete = onDelete; + +op.toWorkNeedsParent("Ops.Sidebar.Sidebar"); + +// functions + +function onIdChanged() +{ + el.id = inId.get(); +} + +function onLabelTextChanged() +{ + let labelText = labelPort.get(); + label.textContent = labelText; +} + +function onParentChanged() +{ + siblingsPort.set(null); + let parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(el) +{ + if (el) el.style.display = "block"; +} + +function hideElement(el) +{ + if (el) el.style.display = "none"; +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.LocalTextureFile.prototype = new CABLES.Op(); +CABLES.OPS["6e8600c4-fb7d-4315-88d4-4f10009538a7"]={f:Ops.Sidebar.LocalTextureFile,objName:"Ops.Sidebar.LocalTextureFile"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.NumberInput_v2 +// +// ************************************************************** + +Ops.Sidebar.NumberInput_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +const parentPort = op.inObject("Link"); +const labelPort = op.inString("Text", "Number"); +const inputValuePort = op.inValue("Input", 0); +const setDefaultValueButtonPort = op.inTriggerButton("Set Default"); +const defaultValuePort = op.inValue("Default", 0); +defaultValuePort.setUiAttribs({ "hidePort": true, "greyout": true }); + +// outputs +const siblingsPort = op.outObject("Children"); +const valuePort = op.outNumber("Result", defaultValuePort.get()); + +// vars +const el = document.createElement("div"); +el.addEventListener("dblclick", function () +{ + valuePort.set(parseFloat(defaultValuePort.get())); + inputValuePort.set(parseFloat(defaultValuePort.get())); +}); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar__text-input"); +el.classList.add("sidebar__reloadable"); + +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelTextNode = document.createTextNode(labelPort.get()); +label.appendChild(labelTextNode); +el.appendChild(label); +// var inputWrapper = document.createElement('div'); +// inputWrapper.classList.add('sidebar__text-input-input-wrapper'); +// el.appendChild(inputWrapper); +const input = document.createElement("input"); +input.classList.add("sidebar__text-input-input"); +input.setAttribute("type", "text"); +input.setAttribute("value", defaultValuePort.get()); +// inputWrapper.appendChild(input); +el.appendChild(input); +input.addEventListener("input", onInput); + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +defaultValuePort.onChange = onDefaultValueChanged; +op.onDelete = onDelete; +inputValuePort.onChange = onInputValuePortChanged; +setDefaultValueButtonPort.onTriggered = setDefaultValue; + +// functions + +function setDefaultValue() +{ + defaultValuePort.set(parseFloat(inputValuePort.get())); + op.refreshParams(); +} + +function onInputValuePortChanged() +{ + let val = parseFloat(inputValuePort.get()); + if (isNaN(val)) { val = 0; } + input.value = val; + valuePort.set(val); +} + +function onInput(ev) +{ + let newVal = parseFloat(ev.target.value); + if (isNaN(newVal)) { newVal = 0; } + valuePort.set(newVal); + inputValuePort.set(newVal); + op.refreshParams(); +} + +function onDefaultValueChanged() +{ + /* + var defaultValue = defaultValuePort.get(); + valuePort.set(defaultValue); + input.value = defaultValue; + */ +} + +function onLabelTextChanged() +{ + const labelText = labelPort.get(); + label.textContent = labelText; + if (CABLES.UI) + { + op.setTitle("Number Input: " + labelText); + } +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(element) +{ + if (element) + { + element.style.display = "block"; + } +} + +function hideElement(element) +{ + if (element) + { + element.style.display = "none"; + } +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(element) +{ + if (element && element.parentNode && element.parentNode.removeChild) + { + element.parentNode.removeChild(element); + } +} + + +}; + +Ops.Sidebar.NumberInput_v2.prototype = new CABLES.Op(); +CABLES.OPS["c4f3f1d7-de07-4c06-921e-32baeef4fc68"]={f:Ops.Sidebar.NumberInput_v2,objName:"Ops.Sidebar.NumberInput_v2"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.Presets_v2 +// +// ************************************************************** + +Ops.Sidebar.Presets_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const parentPort = op.inObject("link"); +const labelPort = op.inString("Text", "Presets"); +const siblingsPort = op.outObject("Children"); + +const inAddPreset = op.inTriggerButton("Add Preset"); +const inUpdatePreset = op.inTriggerButton("Update current Preset"); +const outIndex = op.outNumber("Index"); + +inAddPreset.onTriggered = addPreset; +inUpdatePreset.onTriggered = updatePreset; +parentPort.onChange = onParentChanged; + +const presetPorts = []; +const presetTitlePorts = []; + +const el = document.createElement("div"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar__select"); +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelText = document.createTextNode(labelPort.get()); +label.appendChild(labelText); +el.appendChild(label); +const selectList = document.createElement("select"); +selectList.classList.add("sidebar__select-select"); +el.appendChild(selectList); + +const MAX_PRESETS = 8; + +for (let i = 0; i < MAX_PRESETS; i++) +{ + const inpTitle = op.inString("Preset Title " + i); + const inp = op.inObject("Preset " + i); + presetPorts.push(inp); + presetTitlePorts.push(inpTitle); + + inpTitle.onLinkChanged = inp.onLinkChanged = updateSelect; +} + +selectList.onchange = function () +{ + outIndex.set(selectList.selectedIndex); + setSidebar(selectList.options[selectList.selectedIndex].value); +}; + +op.patch.addEventListener("patchLoadEnd", initialize); +op.init = initialize; + +function initialize() +{ + setTimeout(() => + { + for (let i = 0; i < MAX_PRESETS; i++) + if (presetPorts[i].isLinked()) + setSidebar(i); + setSidebar(0); + }, 1000); +} + +function updateSelect() +{ + while (selectList.firstChild) selectList.removeChild(selectList.firstChild); + + for (let i = 0; i < MAX_PRESETS; i++) + { + if (presetPorts[i].isLinked()) + { + const option = document.createElement("option"); + option.value = i; + + const other = presetPorts[i].links[0].getOtherPort(presetPorts[i]); + + // other.parent.removeListener("onTitleChange",updateSelect); + + // if (!other.parent.hasEventListener(other.parent.onTitlechangeevent)) + // other.parent.onTitlechangeevent = other.parent.addEventListener("onTitleChange", updateSelect); + + option.text = "" + presetTitlePorts[i].get(); + selectList.appendChild(option); + } + } +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function deSerializeSidebar(obj) +{ + if (!obj) return; + if (!obj.ops) return; + + for (let i = 0; i < obj.ops.length; i++) + { + const theOp = op.patch.getOpById(obj.ops[i].id); + if (theOp) + { + for (const portName in obj.ops[i].ports) + { + const p = theOp.getPortByName(portName); + if (p) + { + if (typeof obj.ops[i].ports[portName] !== "object") + { + p.set(obj.ops[i].ports[portName]); + } + else + { + p.set(obj.ops[i].ports[portName].value); + } + } + else + { + op.warn("unknown preset"); + } + + const def = theOp.getPortByName("Input"); + if (def) + { + def.set(obj.ops[i].ports[portName]); + } + const namedInPort = theOp.getPortByName("Input " + p.name); + if (namedInPort) + { + namedInPort.set(obj.ops[i].ports[portName]); + } + } + } + } +} + +function setSidebar(idx) +{ + const obj = presetPorts[idx].get(); + deSerializeSidebar(obj); +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(element) +{ + if (element && element.parentNode && element.parentNode.removeChild) + { + element.parentNode.removeChild(element); + } +} + +function updatePreset() +{ + const r = serializeSidebar(); + const idx = selectList.options[selectList.selectedIndex].value; + + const valueOp = presetPorts[idx].links[0].getOtherPort(presetPorts[idx]).parent; + valueOp.getPortByName("JSON String").set(JSON.stringify(r)); +} + +function serializeSidebar() +{ + const values = []; + for (let i = 0; i < op.patch.ops.length; i++) + { + if ( + op.patch.ops[i].objName.indexOf("Ops.Sidebar.Sidebar") == -1 && + op.patch.ops[i].objName.indexOf("AsObject") == -1 && + op.patch.ops[i].objName.indexOf("Group") == -1 && + op.patch.ops[i].objName.indexOf("Preset") == -1 && + op.patch.ops[i].objName.indexOf("Ops.Sidebar") === 0 + ) + { + let foundPort = false; + + const theOp = op.patch.ops[i]; + const p = {}; + p.id = theOp.id; + p.objName = theOp.objName; + p.ports = {}; + + for (let j = 0; j < op.patch.ops[i].portsOut.length; j++) + { + if (theOp.portsOut[j].type == CABLES.OP_PORT_TYPE_VALUE) + { + p.ports[theOp.portsOut[j].name] = theOp.portsOut[j].get(); + foundPort = true; + } + } + + if (foundPort)values.push(p); + } + } + + const r = { "ops": values }; + + if (CABLES.UI && gui) gui.setStateUnsaved(); + return r; +} + +function addPreset() +{ + let freePort = 0; + let i = 0; + for (i = 0; i < MAX_PRESETS; i++) + { + if (!presetPorts[i].isLinked()) + { + freePort = presetPorts[i]; + break; + } + } + + const r = serializeSidebar(); + + const newOp = op.patch.addOp("Ops.Json.ParseObject_v2"); + + newOp.getPortByName("JSON String").set(JSON.stringify(r)); + + if (CABLES.UI) gui.patchView.centerSelectOp(newOp.id); + + op.patch.link(op, freePort.name, newOp, "Result"); +} + +op.serializeSidebar = serializeSidebar; +op.deSerializeSidebar = deSerializeSidebar; + + +}; + +Ops.Sidebar.Presets_v2.prototype = new CABLES.Op(); +CABLES.OPS["9ef53cbc-f47b-4631-92f1-470bd40c2866"]={f:Ops.Sidebar.Presets_v2,objName:"Ops.Sidebar.Presets_v2"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.SideBarStyle +// +// ************************************************************** + +Ops.Sidebar.SideBarStyle = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const parentPort = op.inObject("link"), + inWidth = op.inInt("Width", 220), + inBorderRadius = op.inFloat("Round Corners", 10), + inColorSpecial = op.inString("Special Color", "#07f78c"), + + siblingsPort = op.outObject("childs"); + +inColorSpecial.onChange = +inBorderRadius.onChange = +inWidth.onChange = setStyle; + +parentPort.onChange = onParentChanged; +op.onDelete = onDelete; + +op.toWorkNeedsParent("Ops.Sidebar.Sidebar"); + +let sideBarEle = null; + +function setStyle() +{ + if (!sideBarEle) return; + + sideBarEle.style.setProperty("--sidebar-width", inWidth.get() + "px"); + + sideBarEle.style.setProperty("--sidebar-color", inColorSpecial.get()); + + sideBarEle.style.setProperty("--sidebar-border-radius", Math.round(inBorderRadius.get()) + "px"); + + op.patch.emitEvent("sidebarStylesChanged"); +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + siblingsPort.set(parent); + sideBarEle = parent.parentElement.parentElement; + setStyle(); + } + else + { + sideBarEle = null; + } +} + +function showElement(el) +{ + if (!el) return; + el.style.display = "block"; +} + +function hideElement(el) +{ + if (!el) return; + el.style.display = "none"; +} + +function onDelete() +{ +} + + +}; + +Ops.Sidebar.SideBarStyle.prototype = new CABLES.Op(); +CABLES.OPS["87d78a59-c8d4-4269-a3f8-af273741aae4"]={f:Ops.Sidebar.SideBarStyle,objName:"Ops.Sidebar.SideBarStyle"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.SideBarSwitch +// +// ************************************************************** + +Ops.Sidebar.SideBarSwitch = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const parentPort = op.inObject("link"), + inArr = op.inArray("Names"), + inStyle = op.inSwitch("Style", ["Tabs", "Switch"], "Switch"), + labelPort = op.inString("Text", "Switch"), + + inInput = op.inInt("Input", 0), + + setDefaultValueButtonPort = op.inTriggerButton("Set Default"), + inGreyOut = op.inBool("Grey Out", false), + + inDefault = op.inValue("Default", 0), + + siblingsPort = op.outObject("childs"), + outIndex = op.outNumber("Index", -1), + outStr = op.outString("String"); + +let elTabActive = null; +const el = document.createElement("div"); +el.classList.add("sidebar__item"); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +inDefault.setUiAttribs({ "greyout": true }); + +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelText = document.createTextNode(labelPort.get()); +label.appendChild(labelText); +el.appendChild(label); + +const switchGroup = document.createElement("div"); +el.appendChild(switchGroup); + +const greyOut = document.createElement("div"); +greyOut.classList.add("sidebar__greyout"); +el.appendChild(greyOut); +greyOut.style.display = "none"; + +parentPort.onChange = onParentChanged; +op.onDelete = onDelete; + +op.toWorkNeedsParent("Ops.Sidebar.Sidebar"); +op.setPortGroup("Default Item", [inDefault, setDefaultValueButtonPort]); +const tabEles = []; + +inArr.onChange = rebuildHtml; +inStyle.onChange = updateStyle; +updateStyle(); + +labelPort.onChange = () => +{ + label.innerHTML = labelPort.get(); +}; + +inGreyOut.onChange = function () +{ + greyOut.style.display = inGreyOut.get() ? "block" : "none"; +}; + +function rebuildHtml() +{ + tabEles.length = 0; + switchGroup.innerHTML = ""; + elTabActive = null; + + const arr = inArr.get(); + if (!arr) return; + + for (let i = 0; i < arr.length; i++) + { + const el = addTab(String(arr[i])); + if (i == inDefault.get())setActiveTab(el); + } +} + +setDefaultValueButtonPort.onTriggered = () => +{ + inDefault.set(outIndex.get()); + op.refreshParams(); +}; + +function updateStyle() +{ + if (inStyle.get() == "Tabs") + { + el.classList.add("sidebar_tabs"); + switchGroup.classList.remove("sidebar_switchs"); + label.style.display = "none"; + } + else + { + el.classList.remove("sidebar_tabs"); + switchGroup.classList.add("sidebar_switchs"); + label.style.display = "inline-block"; + } + + labelPort.setUiAttribs({ "greyout": inStyle.get() == "Tabs" }); + + rebuildHtml(); +} + +function addTab(title) +{ + const tabEle = document.createElement("div"); + + if (inStyle.get() == "Tabs") tabEle.classList.add("sidebar_tab"); + else tabEle.classList.add("sidebar_switch"); + + tabEle.id = "tabEle" + tabEles.length; + tabEle.innerHTML = title; + tabEle.dataset.index = tabEles.length; + tabEle.dataset.txt = title; + + tabEle.addEventListener("click", tabClicked); + + switchGroup.appendChild(tabEle); + + tabEles.push(tabEle); + + return tabEle; +} + +inInput.onChange = () => +{ + if (tabEles.length > inInput.get()) + tabClicked({ "target": tabEles[inInput.get()] }); + // setActiveTab(tabEles[inInput.get()]); +}; + +function setActiveTab(el) +{ + if (el) + { + elTabActive = el; + op.log(el.dataset.index); + outIndex.set(parseInt(el.dataset.index)); + outStr.set(el.dataset.txt); + + if (inStyle.get() == "Tabs") el.classList.add("sidebar_tab_active"); + else el.classList.add("sidebar_switch_active"); + } +} + +function tabClicked(e) +{ + if (elTabActive) + if (inStyle.get() == "Tabs") elTabActive.classList.remove("sidebar_tab_active"); + else elTabActive.classList.remove("sidebar_switch_active"); + setActiveTab(e.target); +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { + if (el.parentElement) + el.parentElement.removeChild(el); + } +} + +function showElement(el) +{ + if (!el) return; + el.style.display = "block"; +} + +function hideElement(el) +{ + if (!el) return; + el.style.display = "none"; +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.SideBarSwitch.prototype = new CABLES.Op(); +CABLES.OPS["ebc8c92c-5fa6-4598-a9c6-b8e12f22f7c2"]={f:Ops.Sidebar.SideBarSwitch,objName:"Ops.Sidebar.SideBarSwitch"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.Sidebar +// +// ************************************************************** + +Ops.Sidebar.Sidebar = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"style_css":" /*\n * SIDEBAR\n http://danielstern.ca/range.css/#/\n https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-progress-value\n */\n\n.sidebar-icon-undo\n{\n width:10px;\n height:10px;\n background-image: url(\"data:image/svg+xml;charset=utf8, %3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='grey' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 7v6h6'/%3E%3Cpath d='M21 17a9 9 0 00-9-9 9 9 0 00-6 2.3L3 13'/%3E%3C/svg%3E\");\n background-size: 19px;\n background-repeat: no-repeat;\n top: -19px;\n margin-top: -7px;\n}\n\n.icon-chevron-down {\n top: 2px;\n right: 9px;\n}\n\n.iconsidebar-chevron-up,.sidebar__close-button {\n\tbackground-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM4ODg4ODgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0iZmVhdGhlciBmZWF0aGVyLWNoZXZyb24tdXAiPjxwb2x5bGluZSBwb2ludHM9IjE4IDE1IDEyIDkgNiAxNSI+PC9wb2x5bGluZT48L3N2Zz4=);\n}\n\n.iconsidebar-minimizebutton {\n background-position: 98% center;\n background-repeat: no-repeat;\n}\n\n.sidebar-cables-right\n{\n right: 15px;\n left: initial !important;\n}\n\n.sidebar-cables {\n --sidebar-color: #07f78c;\n --sidebar-width: 220px;\n --sidebar-border-radius: 10px;\n --sidebar-monospace-font-stack: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, Courier, monospace;\n --sidebar-hover-transition-time: .2s;\n\n position: absolute;\n top: 15px;\n left: 15px;\n border-radius: var(--sidebar-border-radius);\n z-index: 100000;\n color: #BBBBBB;\n width: var( --sidebar-width);\n max-height: 100%;\n box-sizing: border-box;\n overflow-y: auto;\n overflow-x: hidden;\n font-size: 13px;\n font-family: Arial;\n line-height: 1em; /* prevent emojis from breaking height of the title */\n}\n\n.sidebar-cables::selection {\n background-color: var(--sidebar-color);\n color: #EEEEEE;\n}\n\n.sidebar-cables::-webkit-scrollbar {\n background-color: transparent;\n --cables-scrollbar-width: 8px;\n width: var(--cables-scrollbar-width);\n}\n\n.sidebar-cables::-webkit-scrollbar-track {\n background-color: transparent;\n width: var(--cables-scrollbar-width);\n}\n\n.sidebar-cables::-webkit-scrollbar-thumb {\n background-color: #333333;\n border-radius: 4px;\n width: var(--cables-scrollbar-width);\n}\n\n.sidebar-cables--closed {\n width: auto;\n}\n\n.sidebar__close-button {\n background-color: #222;\n /*-webkit-user-select: none; */\n /*-moz-user-select: none; */\n /*-ms-user-select: none; */\n /*user-select: none; */\n /*transition: background-color var(--sidebar-hover-transition-time);*/\n /*color: #CCCCCC;*/\n height: 2px;\n /*border-bottom:20px solid #222;*/\n\n /*box-sizing: border-box;*/\n /*padding-top: 2px;*/\n /*text-align: center;*/\n /*cursor: pointer;*/\n /*border-radius: 0 0 var(--sidebar-border-radius) var(--sidebar-border-radius);*/\n /*opacity: 1.0;*/\n /*transition: opacity 0.3s;*/\n /*overflow: hidden;*/\n}\n\n.sidebar__close-button-icon {\n display: inline-block;\n /*opacity: 0;*/\n width: 20px;\n height: 20px;\n /*position: relative;*/\n /*top: -1px;*/\n\n\n}\n\n.sidebar--closed {\n width: auto;\n margin-right: 20px;\n}\n\n.sidebar--closed .sidebar__close-button {\n margin-top: 8px;\n margin-left: 8px;\n padding:10px;\n\n height: 25px;\n width:25px;\n border-radius: 50%;\n cursor: pointer;\n opacity: 0.3;\n background-repeat: no-repeat;\n background-position: center center;\n transform:rotate(180deg);\n}\n\n.sidebar--closed .sidebar__group\n{\n display:none;\n\n}\n.sidebar--closed .sidebar__close-button-icon {\n background-position: 0px 0px;\n}\n\n.sidebar__close-button:hover {\n background-color: #111111;\n opacity: 1.0 !important;\n}\n\n/*\n * SIDEBAR ITEMS\n */\n\n.sidebar__items {\n /* max-height: 1000px; */\n /* transition: max-height 0.5;*/\n background-color: #222;\n padding-bottom: 20px;\n}\n\n.sidebar--closed .sidebar__items {\n /* max-height: 0; */\n height: 0;\n display: none;\n pointer-interactions: none;\n}\n\n.sidebar__item__right {\n float: right;\n}\n\n/*\n * SIDEBAR GROUP\n */\n\n.sidebar__group {\n /*background-color: #1A1A1A;*/\n overflow: hidden;\n box-sizing: border-box;\n animate: height;\n /*background-color: #151515;*/\n /* max-height: 1000px; */\n /* transition: max-height 0.5s; */\n--sidebar-group-header-height: 33px;\n}\n\n.sidebar__group-items\n{\n padding-top: 15px;\n padding-bottom: 15px;\n}\n\n.sidebar__group--closed {\n /* max-height: 13px; */\n height: var(--sidebar-group-header-height);\n}\n\n.sidebar__group-header {\n box-sizing: border-box;\n color: #EEEEEE;\n background-color: #151515;\n -webkit-user-select: none; /* Chrome all / Safari all */\n -moz-user-select: none; /* Firefox all */\n -ms-user-select: none; /* IE 10+ */\n user-select: none; /* Likely future */\n\n /*height: 100%;//var(--sidebar-group-header-height);*/\n\n padding-top: 7px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n cursor: pointer;\n /*transition: background-color var(--sidebar-hover-transition-time);*/\n position: relative;\n}\n\n.sidebar__group-header:hover {\n background-color: #111111;\n}\n\n.sidebar__group-header-title {\n /*float: left;*/\n overflow: hidden;\n padding: 0 15px;\n padding-top:5px;\n padding-bottom:10px;\n font-weight:bold;\n}\n\n.sidebar__group-header-undo {\n float: right;\n overflow: hidden;\n padding-right: 15px;\n padding-top:5px;\n font-weight:bold;\n }\n\n.sidebar__group-header-icon {\n width: 17px;\n height: 14px;\n background-repeat: no-repeat;\n display: inline-block;\n position: absolute;\n background-size: cover;\n\n /* icon open */\n /* feather icon: chevron up */\n background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM4ODg4ODgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0iZmVhdGhlciBmZWF0aGVyLWNoZXZyb24tdXAiPjxwb2x5bGluZSBwb2ludHM9IjE4IDE1IDEyIDkgNiAxNSI+PC9wb2x5bGluZT48L3N2Zz4=);\n top: 4px;\n right: 5px;\n opacity: 0.0;\n transition: opacity 0.3;\n}\n\n.sidebar__group-header:hover .sidebar__group-header-icon {\n opacity: 1.0;\n}\n\n/* icon closed */\n.sidebar__group--closed .sidebar__group-header-icon {\n /* feather icon: chevron down */\n background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM4ODg4ODgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0iZmVhdGhlciBmZWF0aGVyLWNoZXZyb24tZG93biI+PHBvbHlsaW5lIHBvaW50cz0iNiA5IDEyIDE1IDE4IDkiPjwvcG9seWxpbmU+PC9zdmc+);\n top: 4px;\n right: 5px;\n}\n\n/*\n * SIDEBAR ITEM\n */\n\n.sidebar__item\n{\n box-sizing: border-box;\n padding: 7px;\n padding-left:15px;\n padding-right:15px;\n\n overflow: hidden;\n position: relative;\n}\n\n.sidebar__item-label {\n display: inline-block;\n -webkit-user-select: none; /* Chrome all / Safari all */\n -moz-user-select: none; /* Firefox all */\n -ms-user-select: none; /* IE 10+ */\n user-select: none; /* Likely future */\n width: calc(50% - 7px);\n margin-right: 7px;\n margin-top: 2px;\n text-overflow: ellipsis;\n /* overflow: hidden; */\n}\n\n.sidebar__item-value-label {\n font-family: var(--sidebar-monospace-font-stack);\n display: inline-block;\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n max-width: 60%;\n}\n\n.sidebar__item-value-label::selection {\n background-color: var(--sidebar-color);\n color: #EEEEEE;\n}\n\n.sidebar__item + .sidebar__item,\n.sidebar__item + .sidebar__group,\n.sidebar__group + .sidebar__item,\n.sidebar__group + .sidebar__group {\n /*border-top: 1px solid #272727;*/\n}\n\n/*\n * SIDEBAR ITEM TOGGLE\n */\n\n/*.sidebar__toggle */\n.icon_toggle{\n cursor: pointer;\n}\n\n.sidebar__toggle-input {\n --sidebar-toggle-input-color: #CCCCCC;\n --sidebar-toggle-input-color-hover: #EEEEEE;\n --sidebar-toggle-input-border-size: 2px;\n display: inline;\n float: right;\n box-sizing: border-box;\n border-radius: 50%;\n cursor: pointer;\n --toggle-size: 11px;\n margin-top: 2px;\n background-color: transparent !important;\n border: var(--sidebar-toggle-input-border-size) solid var(--sidebar-toggle-input-color);\n width: var(--toggle-size);\n height: var(--toggle-size);\n transition: background-color var(--sidebar-hover-transition-time);\n transition: border-color var(--sidebar-hover-transition-time);\n}\n.sidebar__toggle:hover .sidebar__toggle-input {\n border-color: var(--sidebar-toggle-input-color-hover);\n}\n\n.sidebar__toggle .sidebar__item-value-label {\n -webkit-user-select: none; /* Chrome all / Safari all */\n -moz-user-select: none; /* Firefox all */\n -ms-user-select: none; /* IE 10+ */\n user-select: none; /* Likely future */\n max-width: calc(50% - 12px);\n}\n.sidebar__toggle-input::after { clear: both; }\n\n.sidebar__toggle--active .icon_toggle\n{\n\n background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE1cHgiIHdpZHRoPSIzMHB4IiBmaWxsPSIjMDZmNzhiIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTAwIDEwMCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PGcgZGlzcGxheT0ibm9uZSI+PGcgZGlzcGxheT0iaW5saW5lIj48Zz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZmlsbD0iIzA2Zjc4YiIgZD0iTTMwLDI3QzE3LjM1LDI3LDcsMzcuMzUsNyw1MGwwLDBjMCwxMi42NSwxMC4zNSwyMywyMywyM2g0MCBjMTIuNjUsMCwyMy0xMC4zNSwyMy0yM2wwLDBjMC0xMi42NS0xMC4zNS0yMy0yMy0yM0gzMHogTTcwLDY3Yy05LjM4OSwwLTE3LTcuNjEtMTctMTdzNy42MTEtMTcsMTctMTdzMTcsNy42MSwxNywxNyAgICAgUzc5LjM4OSw2Nyw3MCw2N3oiPjwvcGF0aD48L2c+PC9nPjwvZz48Zz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTMwLDI3QzE3LjM1LDI3LDcsMzcuMzUsNyw1MGwwLDBjMCwxMi42NSwxMC4zNSwyMywyMywyM2g0MCAgIGMxMi42NSwwLDIzLTEwLjM1LDIzLTIzbDAsMGMwLTEyLjY1LTEwLjM1LTIzLTIzLTIzSDMweiBNNzAsNjdjLTkuMzg5LDAtMTctNy42MS0xNy0xN3M3LjYxMS0xNywxNy0xN3MxNyw3LjYxLDE3LDE3ICAgUzc5LjM4OSw2Nyw3MCw2N3oiPjwvcGF0aD48L2c+PGcgZGlzcGxheT0ibm9uZSI+PGcgZGlzcGxheT0iaW5saW5lIj48cGF0aCBmaWxsPSIjMDZmNzhiIiBzdHJva2U9IiMwNmY3OGIiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBkPSJNNyw1MGMwLDEyLjY1LDEwLjM1LDIzLDIzLDIzaDQwICAgIGMxMi42NSwwLDIzLTEwLjM1LDIzLTIzbDAsMGMwLTEyLjY1LTEwLjM1LTIzLTIzLTIzSDMwQzE3LjM1LDI3LDcsMzcuMzUsNyw1MEw3LDUweiI+PC9wYXRoPjwvZz48Y2lyY2xlIGRpc3BsYXk9ImlubGluZSIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiMwNmY3OGIiIHN0cm9rZT0iIzA2Zjc4YiIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIGN4PSI3MCIgY3k9IjUwIiByPSIxNyI+PC9jaXJjbGU+PC9nPjxnIGRpc3BsYXk9Im5vbmUiPjxwYXRoIGRpc3BsYXk9ImlubGluZSIgZD0iTTcwLDI1SDMwQzE2LjIxNSwyNSw1LDM2LjIxNSw1LDUwczExLjIxNSwyNSwyNSwyNWg0MGMxMy43ODUsMCwyNS0xMS4yMTUsMjUtMjVTODMuNzg1LDI1LDcwLDI1eiBNNzAsNzEgICBIMzBDMTguNDIxLDcxLDksNjEuNTc5LDksNTBzOS40MjEtMjEsMjEtMjFoNDBjMTEuNTc5LDAsMjEsOS40MjEsMjEsMjFTODEuNTc5LDcxLDcwLDcxeiBNNzAsMzFjLTEwLjQ3NywwLTE5LDguNTIzLTE5LDE5ICAgczguNTIzLDE5LDE5LDE5czE5LTguNTIzLDE5LTE5UzgwLjQ3NywzMSw3MCwzMXogTTcwLDY1Yy04LjI3MSwwLTE1LTYuNzI5LTE1LTE1czYuNzI5LTE1LDE1LTE1czE1LDYuNzI5LDE1LDE1Uzc4LjI3MSw2NSw3MCw2NXoiPjwvcGF0aD48L2c+PC9zdmc+);\n opacity: 1;\n transform: rotate(0deg);\n}\n\n\n.icon_toggle\n{\n float: right;\n width:40px;\n height:18px;\n background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE1cHgiIHdpZHRoPSIzMHB4IiBmaWxsPSIjYWFhYWFhIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTAwIDEwMCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PGcgZGlzcGxheT0ibm9uZSI+PGcgZGlzcGxheT0iaW5saW5lIj48Zz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZmlsbD0iI2FhYWFhYSIgZD0iTTMwLDI3QzE3LjM1LDI3LDcsMzcuMzUsNyw1MGwwLDBjMCwxMi42NSwxMC4zNSwyMywyMywyM2g0MCBjMTIuNjUsMCwyMy0xMC4zNSwyMy0yM2wwLDBjMC0xMi42NS0xMC4zNS0yMy0yMy0yM0gzMHogTTcwLDY3Yy05LjM4OSwwLTE3LTcuNjEtMTctMTdzNy42MTEtMTcsMTctMTdzMTcsNy42MSwxNywxNyAgICAgUzc5LjM4OSw2Nyw3MCw2N3oiPjwvcGF0aD48L2c+PC9nPjwvZz48Zz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTMwLDI3QzE3LjM1LDI3LDcsMzcuMzUsNyw1MGwwLDBjMCwxMi42NSwxMC4zNSwyMywyMywyM2g0MCAgIGMxMi42NSwwLDIzLTEwLjM1LDIzLTIzbDAsMGMwLTEyLjY1LTEwLjM1LTIzLTIzLTIzSDMweiBNNzAsNjdjLTkuMzg5LDAtMTctNy42MS0xNy0xN3M3LjYxMS0xNywxNy0xN3MxNyw3LjYxLDE3LDE3ICAgUzc5LjM4OSw2Nyw3MCw2N3oiPjwvcGF0aD48L2c+PGcgZGlzcGxheT0ibm9uZSI+PGcgZGlzcGxheT0iaW5saW5lIj48cGF0aCBmaWxsPSIjYWFhYWFhIiBzdHJva2U9IiNhYWFhYWEiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBkPSJNNyw1MGMwLDEyLjY1LDEwLjM1LDIzLDIzLDIzaDQwICAgIGMxMi42NSwwLDIzLTEwLjM1LDIzLTIzbDAsMGMwLTEyLjY1LTEwLjM1LTIzLTIzLTIzSDMwQzE3LjM1LDI3LDcsMzcuMzUsNyw1MEw3LDUweiI+PC9wYXRoPjwvZz48Y2lyY2xlIGRpc3BsYXk9ImlubGluZSIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNhYWFhYWEiIHN0cm9rZT0iI2FhYWFhYSIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIGN4PSI3MCIgY3k9IjUwIiByPSIxNyI+PC9jaXJjbGU+PC9nPjxnIGRpc3BsYXk9Im5vbmUiPjxwYXRoIGRpc3BsYXk9ImlubGluZSIgZD0iTTcwLDI1SDMwQzE2LjIxNSwyNSw1LDM2LjIxNSw1LDUwczExLjIxNSwyNSwyNSwyNWg0MGMxMy43ODUsMCwyNS0xMS4yMTUsMjUtMjVTODMuNzg1LDI1LDcwLDI1eiBNNzAsNzEgICBIMzBDMTguNDIxLDcxLDksNjEuNTc5LDksNTBzOS40MjEtMjEsMjEtMjFoNDBjMTEuNTc5LDAsMjEsOS40MjEsMjEsMjFTODEuNTc5LDcxLDcwLDcxeiBNNzAsMzFjLTEwLjQ3NywwLTE5LDguNTIzLTE5LDE5ICAgczguNTIzLDE5LDE5LDE5czE5LTguNTIzLDE5LTE5UzgwLjQ3NywzMSw3MCwzMXogTTcwLDY1Yy04LjI3MSwwLTE1LTYuNzI5LTE1LTE1czYuNzI5LTE1LDE1LTE1czE1LDYuNzI5LDE1LDE1Uzc4LjI3MSw2NSw3MCw2NXoiPjwvcGF0aD48L2c+PC9zdmc+);\n background-size: 50px 37px;\n background-position: -6px -10px;\n transform: rotate(180deg);\n opacity: 0.4;\n}\n\n\n\n/*.sidebar__toggle--active .sidebar__toggle-input {*/\n/* transition: background-color var(--sidebar-hover-transition-time);*/\n/* background-color: var(--sidebar-toggle-input-color);*/\n/*}*/\n/*.sidebar__toggle--active .sidebar__toggle-input:hover*/\n/*{*/\n/* background-color: var(--sidebar-toggle-input-color-hover);*/\n/* border-color: var(--sidebar-toggle-input-color-hover);*/\n/* transition: background-color var(--sidebar-hover-transition-time);*/\n/* transition: border-color var(--sidebar-hover-transition-time);*/\n/*}*/\n\n/*\n * SIDEBAR ITEM BUTTON\n */\n\n.sidebar__button {}\n\n.sidebar__button-input {\n -webkit-user-select: none; /* Chrome all / Safari all */\n -moz-user-select: none; /* Firefox all */\n -ms-user-select: none; /* IE 10+ */\n user-select: none; /* Likely future */\n min-height: 24px;\n background-color: transparent;\n color: #CCCCCC;\n box-sizing: border-box;\n padding-top: 3px;\n text-align: center;\n border-radius: 125px;\n border:2px solid #555;\n cursor: pointer;\n padding-bottom: 3px;\n}\n\n.sidebar__button-input.plus, .sidebar__button-input.minus {\n display: inline-block;\n min-width: 20px;\n}\n\n.sidebar__button-input:hover {\n background-color: #333;\n border:2px solid var(--sidebar-color);\n}\n\n/*\n * VALUE DISPLAY (shows a value)\n */\n\n.sidebar__value-display {}\n\n/*\n * SLIDER\n */\n\n.sidebar__slider {\n --sidebar-slider-input-height: 3px;\n}\n\n.sidebar__slider-input-wrapper {\n width: 100%;\n\n margin-top: 8px;\n position: relative;\n}\n\n.sidebar__slider-input {\n -webkit-appearance: none;\n appearance: none;\n margin: 0;\n width: 100%;\n height: var(--sidebar-slider-input-height);\n background: #555;\n cursor: pointer;\n outline: 0;\n\n -webkit-transition: .2s;\n transition: background-color .2s;\n border: none;\n}\n\n.sidebar__slider-input:focus, .sidebar__slider-input:hover {\n border: none;\n}\n\n.sidebar__slider-input-active-track {\n user-select: none;\n position: absolute;\n z-index: 11;\n top: 0;\n left: 0;\n background-color: var(--sidebar-color);\n pointer-events: none;\n height: var(--sidebar-slider-input-height);\n max-width: 100%;\n}\n\n/* Mouse-over effects */\n.sidebar__slider-input:hover {\n /*background-color: #444444;*/\n}\n\n/*.sidebar__slider-input::-webkit-progress-value {*/\n/* background-color: green;*/\n/* color:green;*/\n\n/* }*/\n\n/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */\n\n.sidebar__slider-input::-moz-range-thumb\n{\n position: absolute;\n height: 15px;\n width: 15px;\n z-index: 900 !important;\n border-radius: 20px !important;\n cursor: pointer;\n background: var(--sidebar-color) !important;\n user-select: none;\n\n}\n\n.sidebar__slider-input::-webkit-slider-thumb\n{\n position: relative;\n appearance: none;\n -webkit-appearance: none;\n user-select: none;\n height: 15px;\n width: 15px;\n display: block;\n z-index: 900 !important;\n border: 0;\n border-radius: 20px !important;\n cursor: pointer;\n background: #777 !important;\n}\n\n.sidebar__slider-input:hover ::-webkit-slider-thumb {\n background-color: #EEEEEE !important;\n}\n\n/*.sidebar__slider-input::-moz-range-thumb {*/\n\n/* width: 0 !important;*/\n/* height: var(--sidebar-slider-input-height);*/\n/* background: #EEEEEE;*/\n/* cursor: pointer;*/\n/* border-radius: 0 !important;*/\n/* border: none;*/\n/* outline: 0;*/\n/* z-index: 100 !important;*/\n/*}*/\n\n.sidebar__slider-input::-moz-range-track {\n background-color: transparent;\n z-index: 11;\n}\n\n/*.sidebar__slider-input::-moz-range-thumb:hover {*/\n /* background-color: #EEEEEE; */\n/*}*/\n\n\n/*.sidebar__slider-input-wrapper:hover .sidebar__slider-input-active-track {*/\n/* background-color: #EEEEEE;*/\n/*}*/\n\n/*.sidebar__slider-input-wrapper:hover .sidebar__slider-input::-moz-range-thumb {*/\n/* background-color: #fff !important;*/\n/*}*/\n\n/*.sidebar__slider-input-wrapper:hover .sidebar__slider-input::-webkit-slider-thumb {*/\n/* background-color: #EEEEEE;*/\n/*}*/\n\n.sidebar__slider input[type=text],\n.sidebar__slider input[type=paddword]\n{\n box-sizing: border-box;\n /*background-color: #333333;*/\n text-align: right;\n color: #BBBBBB;\n display: inline-block;\n background-color: transparent !important;\n\n width: 40%;\n height: 18px;\n outline: none;\n border: none;\n border-radius: 0;\n padding: 0 0 0 4px !important;\n margin: 0;\n}\n\n.sidebar__slider input[type=text]:active,\n.sidebar__slider input[type=text]:focus,\n.sidebar__slider input[type=text]:hover\n.sidebar__slider input[type=password]:active,\n.sidebar__slider input[type=password]:focus,\n.sidebar__slider input[type=password]:hover\n{\n\n color: #EEEEEE;\n}\n\n/*\n * TEXT / DESCRIPTION\n */\n\n.sidebar__text .sidebar__item-label {\n width: auto;\n display: block;\n max-height: none;\n margin-right: 0;\n line-height: 1.1em;\n}\n\n/*\n * SIDEBAR INPUT\n */\n.sidebar__text-input textarea,\n.sidebar__text-input input[type=text],\n.sidebar__text-input input[type=password] {\n box-sizing: border-box;\n background-color: #333333;\n color: #BBBBBB;\n display: inline-block;\n width: 50%;\n height: 18px;\n outline: none;\n border: none;\n border-radius: 0;\n border:1px solid #666;\n padding: 0 0 0 4px !important;\n margin: 0;\n}\n\n.sidebar__text-input textarea:focus::placeholder {\n color: transparent;\n}\n\n.sidebar__color-picker .sidebar__item-label\n{\n width:45%;\n}\n\n.sidebar__text-input textarea,\n.sidebar__text-input input[type=text]:active,\n.sidebar__text-input input[type=text]:focus,\n.sidebar__text-input input[type=text]:hover,\n.sidebar__text-input input[type=password]:active,\n.sidebar__text-input input[type=password]:focus,\n.sidebar__text-input input[type=password]:hover {\n background-color: transparent;\n color: #EEEEEE;\n}\n\n.sidebar__text-input textarea\n{\n margin-top:10px;\n height:60px;\n width:100%;\n}\n\n/*\n * SIDEBAR SELECT\n */\n\n\n\n .sidebar__select {}\n .sidebar__select-select {\n color: #BBBBBB;\n /*-webkit-appearance: none;*/\n /*-moz-appearance: none;*/\n appearance: none;\n /*box-sizing: border-box;*/\n width: 50%;\n /*height: 20px;*/\n background-color: #333333;\n /*background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM4ODg4ODgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0iZmVhdGhlciBmZWF0aGVyLWNoZXZyb24tZG93biI+PHBvbHlsaW5lIHBvaW50cz0iNiA5IDEyIDE1IDE4IDkiPjwvcG9seWxpbmU+PC9zdmc+);*/\n background-repeat: no-repeat;\n background-position: right center;\n background-size: 16px 16px;\n margin: 0;\n /*padding: 0 2 2 6px;*/\n border-radius: 5px;\n border: 1px solid #777;\n background-color: #444;\n cursor: pointer;\n outline: none;\n padding-left: 5px;\n\n }\n\n.sidebar__select-select:hover,\n.sidebar__select-select:active,\n.sidebar__select-select:active {\n background-color: #444444;\n color: #EEEEEE;\n}\n\n/*\n * COLOR PICKER\n */\n\n\n .sidebar__color-picker input[type=text] {\n box-sizing: border-box;\n background-color: #333333;\n color: #BBBBBB;\n display: inline-block;\n width: calc(50% - 21px); /* 50% minus space of picker circle */\n height: 18px;\n outline: none;\n border: none;\n border-radius: 0;\n padding: 0 0 0 4px !important;\n margin: 0;\n margin-right: 7px;\n}\n\n.sidebar__color-picker input[type=text]:active,\n.sidebar__color-picker input[type=text]:focus,\n.sidebar__color-picker input[type=text]:hover {\n background-color: #444444;\n color: #EEEEEE;\n}\n\ndiv.sidebar__color-picker-color-input,\n.sidebar__color-picker input[type=color],\n.sidebar__palette-picker input[type=color] {\n display: inline-block;\n border-radius: 100%;\n height: 14px;\n width: 14px;\n\n padding: 0;\n border: none;\n /*border:2px solid red;*/\n border-color: transparent;\n outline: none;\n background: none;\n appearance: none;\n -moz-appearance: none;\n -webkit-appearance: none;\n cursor: pointer;\n position: relative;\n top: 3px;\n}\n.sidebar__color-picker input[type=color]:focus,\n.sidebar__palette-picker input[type=color]:focus {\n outline: none;\n}\n.sidebar__color-picker input[type=color]::-moz-color-swatch,\n.sidebar__palette-picker input[type=color]::-moz-color-swatch {\n border: none;\n}\n.sidebar__color-picker input[type=color]::-webkit-color-swatch-wrapper,\n.sidebar__palette-picker input[type=color]::-webkit-color-swatch-wrapper {\n padding: 0;\n}\n.sidebar__color-picker input[type=color]::-webkit-color-swatch,\n.sidebar__palette-picker input[type=color]::-webkit-color-swatch {\n border: none;\n border-radius: 100%;\n}\n\n/*\n * Palette Picker\n */\n.sidebar__palette-picker .sidebar__palette-picker-color-input.first {\n margin-left: 0;\n}\n.sidebar__palette-picker .sidebar__palette-picker-color-input.last {\n margin-right: 0;\n}\n.sidebar__palette-picker .sidebar__palette-picker-color-input {\n margin: 0 4px;\n}\n\n.sidebar__palette-picker .circlebutton {\n width: 14px;\n height: 14px;\n border-radius: 1em;\n display: inline-block;\n top: 3px;\n position: relative;\n}\n\n/*\n * Preset\n */\n.sidebar__item-presets-preset\n{\n padding:4px;\n cursor:pointer;\n padding-left:8px;\n padding-right:8px;\n margin-right:4px;\n background-color:#444;\n}\n\n.sidebar__item-presets-preset:hover\n{\n background-color:#666;\n}\n\n.sidebar__greyout\n{\n background: #222;\n opacity: 0.8;\n width: 100%;\n height: 100%;\n position: absolute;\n z-index: 1000;\n right: 0;\n top: 0;\n}\n\n.sidebar_tabs\n{\n background-color: #151515;\n padding-bottom: 0px;\n}\n\n.sidebar_switchs\n{\n float: right;\n}\n\n.sidebar_tab\n{\n float:left;\n background-color: #151515;\n border-bottom:1px solid transparent;\n padding-right:7px;\n padding-left:7px;\n padding-bottom: 5px;\n padding-top: 5px;\n cursor:pointer;\n}\n\n.sidebar_tab_active\n{\n background-color: #272727;\n color:white;\n}\n\n.sidebar_tab:hover\n{\n border-bottom:1px solid #777;\n color:white;\n}\n\n\n.sidebar_switch\n{\n float:left;\n background-color: #444;\n padding-right:7px;\n padding-left:7px;\n padding-bottom: 5px;\n padding-top: 5px;\n cursor:pointer;\n}\n\n.sidebar_switch:last-child\n{\n border-top-right-radius: 7px;\n border-bottom-right-radius: 7px;\n}\n\n.sidebar_switch:first-child\n{\n border-top-left-radius: 7px;\n border-bottom-left-radius: 7px;\n}\n\n\n.sidebar_switch_active\n{\n background-color: #999;\n color:white;\n}\n\n.sidebar_switch:hover\n{\n color:white;\n}\n",}; +// vars +const CSS_ELEMENT_CLASS = "cables-sidebar-style"; /* class for the style element to be generated */ +const CSS_ELEMENT_DYNAMIC_CLASS = "cables-sidebar-dynamic-style"; /* things which can be set via op-port, but not attached to the elements themselves, e.g. minimized opacity */ +const SIDEBAR_CLASS = "sidebar-cables"; +const SIDEBAR_ID = "sidebar" + CABLES.uuid(); +const SIDEBAR_ITEMS_CLASS = "sidebar__items"; +const SIDEBAR_OPEN_CLOSE_BTN_CLASS = "sidebar__close-button"; + +const BTN_TEXT_OPEN = ""; // 'Close'; +const BTN_TEXT_CLOSED = ""; // 'Show Controls'; + +let openCloseBtn = null; +let openCloseBtnIcon = null; +let headerTitleText = null; + +// inputs +const visiblePort = op.inValueBool("Visible", true); +const opacityPort = op.inValueSlider("Opacity", 1); +const defaultMinimizedPort = op.inValueBool("Default Minimized"); +const minimizedOpacityPort = op.inValueSlider("Minimized Opacity", 0.5); +const undoButtonPort = op.inValueBool("Show undo button", false); +const inMinimize = op.inValueBool("Show Minimize", false); + +const inTitle = op.inString("Title", "Sidebar"); +const side = op.inValueBool("Side"); + +// outputs +const childrenPort = op.outObject("childs"); +childrenPort.setUiAttribs({ "title": "Children" }); + +const isOpenOut = op.outBool("Opfened"); +isOpenOut.setUiAttribs({ "title": "Opened" }); + +let sidebarEl = document.querySelector("." + SIDEBAR_ID); +if (!sidebarEl) +{ + sidebarEl = initSidebarElement(); +} +// if(!sidebarEl) return; +const sidebarItemsEl = sidebarEl.querySelector("." + SIDEBAR_ITEMS_CLASS); +childrenPort.set({ + "parentElement": sidebarItemsEl, + "parentOp": op, +}); +onDefaultMinimizedPortChanged(); +initSidebarCss(); +updateDynamicStyles(); + +// change listeners +visiblePort.onChange = onVisiblePortChange; +opacityPort.onChange = onOpacityPortChange; +defaultMinimizedPort.onChange = onDefaultMinimizedPortChanged; +minimizedOpacityPort.onChange = onMinimizedOpacityPortChanged; +undoButtonPort.onChange = onUndoButtonChange; +op.onDelete = onDelete; + +// functions + +function onMinimizedOpacityPortChanged() +{ + updateDynamicStyles(); +} + +inMinimize.onChange = updateMinimize; + +function updateMinimize(header) +{ + if (!header || header.uiAttribs) header = document.querySelector(".sidebar-cables .sidebar__group-header"); + if (!header) return; + + const undoButton = document.querySelector(".sidebar-cables .sidebar__group-header .sidebar__group-header-undo"); + + if (inMinimize.get()) + { + header.classList.add("iconsidebar-chevron-up"); + header.classList.add("iconsidebar-minimizebutton"); + + if (undoButton)undoButton.style.marginRight = "20px"; + } + else + { + header.classList.remove("iconsidebar-chevron-up"); + header.classList.remove("iconsidebar-minimizebutton"); + + if (undoButton)undoButton.style.marginRight = "initial"; + } +} + +side.onChange = function () +{ + if (side.get()) sidebarEl.classList.add("sidebar-cables-right"); + else sidebarEl.classList.remove("sidebar-cables-right"); +}; + +function onUndoButtonChange() +{ + const header = document.querySelector(".sidebar-cables .sidebar__group-header"); + if (header) + { + initUndoButton(header); + } +} + +function initUndoButton(header) +{ + if (header) + { + const undoButton = document.querySelector(".sidebar-cables .sidebar__group-header .sidebar__group-header-undo"); + if (undoButton) + { + if (!undoButtonPort.get()) + { + // header.removeChild(undoButton); + undoButton.remove(); + } + } + else + { + if (undoButtonPort.get()) + { + const headerUndo = document.createElement("span"); + headerUndo.classList.add("sidebar__group-header-undo"); + headerUndo.classList.add("sidebar-icon-undo"); + + headerUndo.addEventListener("click", function (event) + { + event.stopPropagation(); + const reloadables = document.querySelectorAll(".sidebar-cables .sidebar__reloadable"); + const doubleClickEvent = document.createEvent("MouseEvents"); + doubleClickEvent.initEvent("dblclick", true, true); + reloadables.forEach((reloadable) => + { + reloadable.dispatchEvent(doubleClickEvent); + }); + }); + header.appendChild(headerUndo); + } + } + } + updateMinimize(header); +} + +function onDefaultMinimizedPortChanged() +{ + if (!openCloseBtn) { return; } + if (defaultMinimizedPort.get()) + { + sidebarEl.classList.add("sidebar--closed"); + if (visiblePort.get()) + { + isOpenOut.set(false); + } + // openCloseBtn.textContent = BTN_TEXT_CLOSED; + } + else + { + sidebarEl.classList.remove("sidebar--closed"); + if (visiblePort.get()) + { + isOpenOut.set(true); + } + // openCloseBtn.textContent = BTN_TEXT_OPEN; + } +} + +function onOpacityPortChange() +{ + const opacity = opacityPort.get(); + sidebarEl.style.opacity = opacity; +} + +function onVisiblePortChange() +{ + if (visiblePort.get()) + { + sidebarEl.style.display = "block"; + if (!sidebarEl.classList.contains("sidebar--closed")) + { + isOpenOut.set(true); + } + } + else + { + sidebarEl.style.display = "none"; + isOpenOut.set(false); + } +} + +side.onChanged = function () +{ + +}; + +/** + * Some styles cannot be set directly inline, so a dynamic stylesheet is needed. + * Here hover states can be set later on e.g. + */ +function updateDynamicStyles() +{ + const dynamicStyles = document.querySelectorAll("." + CSS_ELEMENT_DYNAMIC_CLASS); + if (dynamicStyles) + { + dynamicStyles.forEach(function (e) + { + e.parentNode.removeChild(e); + }); + } + const newDynamicStyle = document.createElement("style"); + newDynamicStyle.classList.add(CSS_ELEMENT_DYNAMIC_CLASS); + let cssText = ".sidebar--closed .sidebar__close-button { "; + cssText += "opacity: " + minimizedOpacityPort.get(); + cssText += "}"; + const cssTextEl = document.createTextNode(cssText); + newDynamicStyle.appendChild(cssTextEl); + document.body.appendChild(newDynamicStyle); +} + +function initSidebarElement() +{ + const element = document.createElement("div"); + element.classList.add(SIDEBAR_CLASS); + element.classList.add(SIDEBAR_ID); + const canvasWrapper = op.patch.cgl.canvas.parentElement; /* maybe this is bad outside cables!? */ + + // header... + const headerGroup = document.createElement("div"); + headerGroup.classList.add("sidebar__group"); + + element.appendChild(headerGroup); + const header = document.createElement("div"); + header.classList.add("sidebar__group-header"); + + element.appendChild(header); + const headerTitle = document.createElement("span"); + headerTitle.classList.add("sidebar__group-header-title"); + headerTitleText = document.createElement("span"); + headerTitleText.classList.add("sidebar__group-header-title-text"); + headerTitleText.innerHTML = inTitle.get(); + headerTitle.appendChild(headerTitleText); + header.appendChild(headerTitle); + + initUndoButton(header); + updateMinimize(header); + + headerGroup.appendChild(header); + element.appendChild(headerGroup); + headerGroup.addEventListener("click", onOpenCloseBtnClick); + + if (!canvasWrapper) + { + op.warn("[sidebar] no canvas parentelement found..."); + return; + } + canvasWrapper.appendChild(element); + const items = document.createElement("div"); + items.classList.add(SIDEBAR_ITEMS_CLASS); + element.appendChild(items); + openCloseBtn = document.createElement("div"); + openCloseBtn.classList.add(SIDEBAR_OPEN_CLOSE_BTN_CLASS); + openCloseBtn.addEventListener("click", onOpenCloseBtnClick); + // openCloseBtn.textContent = BTN_TEXT_OPEN; + element.appendChild(openCloseBtn); + // openCloseBtnIcon = document.createElement("span"); + + // openCloseBtnIcon.classList.add("sidebar__close-button-icon"); + // openCloseBtnIcon.classList.add("iconsidebar-chevron-up"); + + // openCloseBtn.appendChild(openCloseBtnIcon); + + return element; +} + +inTitle.onChange = function () +{ + if (headerTitleText)headerTitleText.innerHTML = inTitle.get(); +}; + +function setClosed(b) +{ + +} + +function onOpenCloseBtnClick(ev) +{ + ev.stopPropagation(); + if (!sidebarEl) { op.logError("Sidebar could not be closed..."); return; } + sidebarEl.classList.toggle("sidebar--closed"); + const btn = ev.target; + let btnText = BTN_TEXT_OPEN; + if (sidebarEl.classList.contains("sidebar--closed")) + { + btnText = BTN_TEXT_CLOSED; + isOpenOut.set(false); + } + else + { + isOpenOut.set(true); + } +} + +function initSidebarCss() +{ + // var cssEl = document.getElementById(CSS_ELEMENT_ID); + const cssElements = document.querySelectorAll("." + CSS_ELEMENT_CLASS); + // remove old script tag + if (cssElements) + { + cssElements.forEach(function (e) + { + e.parentNode.removeChild(e); + }); + } + const newStyle = document.createElement("style"); + newStyle.innerHTML = attachments.style_css; + newStyle.classList.add(CSS_ELEMENT_CLASS); + document.body.appendChild(newStyle); +} + +function onDelete() +{ + removeElementFromDOM(sidebarEl); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) el.parentNode.removeChild(el); +} + + +}; + +Ops.Sidebar.Sidebar.prototype = new CABLES.Op(); +CABLES.OPS["5a681c35-78ce-4cb3-9858-bc79c34c6819"]={f:Ops.Sidebar.Sidebar,objName:"Ops.Sidebar.Sidebar"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.SidebarText_v2 +// +// ************************************************************** + +Ops.Sidebar.SidebarText_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +const parentPort = op.inObject("link"); +const labelPort = op.inString("Text", "Value"); +const inId = op.inValueString("Id", ""); + +// outputs +const siblingsPort = op.outObject("childs"); + +// vars +const el = document.createElement("div"); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar__text"); +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelText = document.createElement("div");// document.createTextNode(labelPort.get()); +label.appendChild(labelText); +el.appendChild(label); + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +inId.onChange = onIdChanged; +op.onDelete = onDelete; + +op.toWorkNeedsParent("Ops.Sidebar.Sidebar"); + +// functions + +function onIdChanged() +{ + el.id = inId.get(); +} + +function onLabelTextChanged() +{ + const labelText = labelPort.get(); + label.innerHTML = labelText; + if (CABLES.UI) + { + if (labelText && typeof labelText === "string") + { + op.setTitle("Text: " + labelText.substring(0, 10)); // display first 10 characters of text in op title + } + else + { + op.setTitle("Text"); + } + } +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(el) +{ + if (el) + { + el.style.display = "block"; + } +} + +function hideElement(el) +{ + if (el) + { + el.style.display = "none"; + } +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.SidebarText_v2.prototype = new CABLES.Op(); +CABLES.OPS["cc591cc3-ff23-4817-907c-e5be7d5c059d"]={f:Ops.Sidebar.SidebarText_v2,objName:"Ops.Sidebar.SidebarText_v2"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.SidebarVariables +// +// ************************************************************** + +Ops.Sidebar.SidebarVariables = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// inputs +let parentPort = op.inObject("link"); +let inId = op.inValueString("Id", ""); +let inUpdate = op.inTriggerButton("update"); + +// outputs +let siblingsPort = op.outObject("childs"); + +// vars +let el = document.createElement("div"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar__text"); +let label = document.createElement("div"); +// label.classList.add('sidebar__item-label'); + +el.appendChild(label); + +// events +parentPort.onChange = onParentChanged; +inUpdate.onTriggered = update; + +inId.onChange = onIdChanged; +op.onDelete = onDelete; + +op.toWorkNeedsParent("Ops.Sidebar.Sidebar"); + +update(); + +// functions + +function onIdChanged() +{ + el.id = inId.get(); +} + +function update() +{ + // var labelText = labelPort.get(); + // label.textContent = labelText; + // if(CABLES.UI) { + // if(labelText && typeof labelText === 'string') { + // op.setTitle('Text: ' + labelText.substring(0, 10)); // display first 10 characters of text in op title + // } else { + // op.setTitle('Text'); + // } + // } + + let vars = op.patch.getVars(); + let html = ""; + for (let ki in vars) + { + let v = vars[ki].getValue(); + + if (typeof v == "object") v = "[object]"; + html += ""; + } + html += "
    " + ki + "" + v + "
    "; + + label.innerHTML = html; +} + +function onParentChanged() +{ + siblingsPort.set(null); + let parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(el) +{ + if (el) + { + el.style.display = "block"; + } +} + +function hideElement(el) +{ + if (el) + { + el.style.display = "none"; + } +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.SidebarVariables.prototype = new CABLES.Op(); +CABLES.OPS["55e502d6-0360-41aa-9c84-deb9f9e0be24"]={f:Ops.Sidebar.SidebarVariables,objName:"Ops.Sidebar.SidebarVariables"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.Slider_v3 +// +// ************************************************************** + +Ops.Sidebar.Slider_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// constants +const STEP_DEFAULT = 0.00001; + +// inputs +const parentPort = op.inObject("link"); +const labelPort = op.inString("Text", "Slider"); +const minPort = op.inValue("Min", 0); +const maxPort = op.inValue("Max", 1); +const stepPort = op.inValue("Step", STEP_DEFAULT); +const labelSuffix = op.inString("Suffix", ""); + +const inGreyOut = op.inBool("Grey Out", false); +const inVisible = op.inBool("Visible", true); + +const inputValuePort = op.inValue("Input", 0.5); +const setDefaultValueButtonPort = op.inTriggerButton("Set Default"); +const reset = op.inTriggerButton("Reset"); + +let parent = null; + +const defaultValuePort = op.inValue("Default", 0.5); +defaultValuePort.setUiAttribs({ "hidePort": true, "greyout": true }); + +// outputs +const siblingsPort = op.outObject("childs"); +const valuePort = op.outNumber("Result", defaultValuePort.get()); + +op.toWorkNeedsParent("Ops.Sidebar.Sidebar"); +op.setPortGroup("Range", [minPort, maxPort, stepPort]); +op.setPortGroup("Display", [inGreyOut, inVisible]); + +// vars +const el = document.createElement("div"); +el.addEventListener("dblclick", function () +{ + valuePort.set(parseFloat(defaultValuePort.get())); + inputValuePort.set(parseFloat(defaultValuePort.get())); +}); + +el.dataset.op = op.id; +el.classList.add("cablesEle"); + +el.classList.add("sidebar__item"); +el.classList.add("sidebar__slider"); +el.classList.add("sidebar__reloadable"); + +op.patch.on("sidebarStylesChanged", () => { updateActiveTrack(); }); + +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); + +const greyOut = document.createElement("div"); +greyOut.classList.add("sidebar__greyout"); +el.appendChild(greyOut); +greyOut.style.display = "none"; + +const labelText = document.createTextNode(labelPort.get()); +label.appendChild(labelText); +el.appendChild(label); + +const value = document.createElement("input"); +value.value = defaultValuePort.get(); +value.classList.add("sidebar__text-input-input"); +value.setAttribute("type", "text"); +value.oninput = onTextInputChanged; +el.appendChild(value); + +const suffixEle = document.createElement("span"); +// setValueFieldValue(defaultValuePort).get(); +// value.setAttribute("type", "text"); +// value.oninput = onTextInputChanged; + +el.appendChild(suffixEle); + +labelSuffix.onChange = () => +{ + suffixEle.innerHTML = labelSuffix.get(); +}; + +const inputWrapper = document.createElement("div"); +inputWrapper.classList.add("sidebar__slider-input-wrapper"); +el.appendChild(inputWrapper); + +const activeTrack = document.createElement("div"); +activeTrack.classList.add("sidebar__slider-input-active-track"); +inputWrapper.appendChild(activeTrack); +const input = document.createElement("input"); +input.classList.add("sidebar__slider-input"); +input.setAttribute("min", minPort.get()); +input.setAttribute("max", maxPort.get()); +input.setAttribute("type", "range"); +input.setAttribute("step", stepPort.get()); +input.setAttribute("value", defaultValuePort.get()); +input.style.display = "block"; /* needed because offsetWidth returns 0 otherwise */ +inputWrapper.appendChild(input); + +updateActiveTrack(); +input.addEventListener("input", onSliderInput); + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +inputValuePort.onChange = onInputValuePortChanged; +defaultValuePort.onChange = onDefaultValueChanged; +setDefaultValueButtonPort.onTriggered = onSetDefaultValueButtonPress; +minPort.onChange = onMinPortChange; +maxPort.onChange = onMaxPortChange; +stepPort.onChange = stepPortChanged; +op.onDelete = onDelete; + +// op.onLoadedValueSet=function() +op.onLoaded = op.onInit = function () +{ + if (op.patch.config.sidebar) + { + op.patch.config.sidebar[labelPort.get()]; + valuePort.set(op.patch.config.sidebar[labelPort.get()]); + } + else + { + valuePort.set(parseFloat(defaultValuePort.get())); + inputValuePort.set(parseFloat(defaultValuePort.get())); + // onInputValuePortChanged(); + } +}; + +reset.onTriggered = function () +{ + const newValue = parseFloat(defaultValuePort.get()); + valuePort.set(newValue); + setValueFieldValue(newValue); + setInputFieldValue(newValue); + inputValuePort.set(newValue); + updateActiveTrack(); +}; + +inGreyOut.onChange = function () +{ + greyOut.style.display = inGreyOut.get() ? "block" : "none"; +}; + +inVisible.onChange = function () +{ + el.style.display = inVisible.get() ? "block" : "none"; +}; + +function onTextInputChanged(ev) +{ + let newValue = parseFloat(ev.target.value); + if (isNaN(newValue)) newValue = 0; + const min = minPort.get(); + const max = maxPort.get(); + if (newValue < min) { newValue = min; } + else if (newValue > max) { newValue = max; } + // setInputFieldValue(newValue); + valuePort.set(newValue); + updateActiveTrack(); + inputValuePort.set(newValue); + op.refreshParams(); +} + +function onInputValuePortChanged() +{ + let newValue = parseFloat(inputValuePort.get()); + const minValue = minPort.get(); + const maxValue = maxPort.get(); + if (newValue > maxValue) { newValue = maxValue; } + else if (newValue < minValue) { newValue = minValue; } + setValueFieldValue(newValue); + setInputFieldValue(newValue); + valuePort.set(newValue); + updateActiveTrack(); +} + +function onSetDefaultValueButtonPress() +{ + let newValue = parseFloat(inputValuePort.get()); + const minValue = minPort.get(); + const maxValue = maxPort.get(); + if (newValue > maxValue) { newValue = maxValue; } + else if (newValue < minValue) { newValue = minValue; } + setValueFieldValue(newValue); + setInputFieldValue(newValue); + valuePort.set(newValue); + defaultValuePort.set(newValue); + op.refreshParams(); + + updateActiveTrack(); +} + +function onSliderInput(ev) +{ + ev.preventDefault(); + ev.stopPropagation(); + setValueFieldValue(ev.target.value); + const inputFloat = parseFloat(ev.target.value); + valuePort.set(inputFloat); + inputValuePort.set(inputFloat); + op.refreshParams(); + + updateActiveTrack(); + return false; +} + +function stepPortChanged() +{ + const step = stepPort.get(); + input.setAttribute("step", step); + updateActiveTrack(); +} + +function updateActiveTrack(val) +{ + let valueToUse = parseFloat(input.value); + if (typeof val !== "undefined") valueToUse = val; + let availableWidth = activeTrack.parentElement.getBoundingClientRect().width || 220; + if (parent) availableWidth = parseInt(getComputedStyle(parent.parentElement).getPropertyValue("--sidebar-width")) - 20; + + const trackWidth = CABLES.map( + valueToUse, + parseFloat(input.min), + parseFloat(input.max), + 0, + availableWidth - 16 /* subtract slider thumb width */ + ); + activeTrack.style.width = trackWidth + "px"; +} + +function onMinPortChange() +{ + const min = minPort.get(); + input.setAttribute("min", min); + updateActiveTrack(); +} + +function onMaxPortChange() +{ + const max = maxPort.get(); + input.setAttribute("max", max); + updateActiveTrack(); +} + +function onDefaultValueChanged() +{ + const defaultValue = defaultValuePort.get(); + valuePort.set(parseFloat(defaultValue)); + onMinPortChange(); + onMaxPortChange(); + setInputFieldValue(defaultValue); + setValueFieldValue(defaultValue); + + updateActiveTrack(defaultValue); // needs to be passed as argument, is this async? +} + +function onLabelTextChanged() +{ + const labelText = labelPort.get(); + label.textContent = labelText; + if (CABLES.UI) op.setTitle("Slider: " + labelText); +} + +function onParentChanged() +{ + siblingsPort.set(null); + parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else if (el.parentElement) el.parentElement.removeChild(el); + + updateActiveTrack(); +} + +function setValueFieldValue(v) +{ + value.value = v; +} + +function setInputFieldValue(v) +{ + input.value = v; +} + +function showElement(el) +{ + if (el)el.style.display = "block"; +} + +function hideElement(el) +{ + if (el)el.style.display = "none"; +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) el.parentNode.removeChild(el); +} + + +}; + +Ops.Sidebar.Slider_v3.prototype = new CABLES.Op(); +CABLES.OPS["74730122-5cba-4d0d-b610-df334ec6220a"]={f:Ops.Sidebar.Slider_v3,objName:"Ops.Sidebar.Slider_v3"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.TextInput_v2 +// +// ************************************************************** + +Ops.Sidebar.TextInput_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + parentPort = op.inObject("Link"), + labelPort = op.inString("Text", "Text"), + defaultValuePort = op.inString("Default", ""), + inPlaceholder = op.inString("Placeholder", ""), + inType = op.inSwitch("Type", ["text", "password"], "text"), + inTextArea = op.inBool("TextArea", false), + inGreyOut = op.inBool("Grey Out", false), + inVisible = op.inBool("Visible", true), + inClear = op.inTriggerButton("Clear"), + siblingsPort = op.outObject("Children"), + valuePort = op.outString("Result", defaultValuePort.get()), + outFocus = op.outBool("Focus"); + +const el = document.createElement("div"); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar__text-input"); +el.classList.add("sidebar__reloadable"); + +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelText = document.createTextNode(labelPort.get()); +label.appendChild(labelText); +el.appendChild(label); + +label.addEventListener("dblclick", function () +{ + valuePort.set(defaultValuePort.get()); + input.value = defaultValuePort.get(); +}); + +let input = null; +creatElement(); + +op.toWorkPortsNeedToBeLinked(parentPort); + +inTextArea.onChange = creatElement; +inType.onChange = setAttribs; + +function setAttribs() +{ + input.setAttribute("type", inType.get()); + input.setAttribute("value", defaultValuePort.get()); + input.setAttribute("placeholder", inPlaceholder.get()); +} + +function creatElement() +{ + if (input)input.remove(); + if (!inTextArea.get()) + { + input = document.createElement("input"); + } + else + { + input = document.createElement("textarea"); + onDefaultValueChanged(); + } + + input.classList.add("sidebar__text-input-input"); + + setAttribs(); + + el.appendChild(input); + input.addEventListener("input", onInput); + input.addEventListener("focus", onFocus); + input.addEventListener("blur", onBlur); +} + +const greyOut = document.createElement("div"); +greyOut.classList.add("sidebar__greyout"); +el.appendChild(greyOut); +greyOut.style.display = "none"; + +inClear.onTriggered = () => +{ + input.value = ""; +}; + +function onFocus() +{ + outFocus.set(true); +} + +function onBlur() +{ + outFocus.set(false); +} + +inPlaceholder.onChange = () => +{ + input.setAttribute("placeholder", inPlaceholder.get()); +}; + +inGreyOut.onChange = function () +{ + greyOut.style.display = inGreyOut.get() ? "block" : "none"; +}; + +inVisible.onChange = function () +{ + el.style.display = inVisible.get() ? "block" : "none"; +}; + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +defaultValuePort.onChange = onDefaultValueChanged; +op.onDelete = onDelete; + +// functions + +function onInput(ev) +{ + valuePort.set(ev.target.value); +} + +function onDefaultValueChanged() +{ + const defaultValue = defaultValuePort.get(); + valuePort.set(defaultValue); + input.value = defaultValue; +} + +function onLabelTextChanged() +{ + const labelText = labelPort.get(); + label.textContent = labelText; + if (CABLES.UI) + { + op.setTitle("Text Input: " + labelText); + } +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else + { // detach + if (el.parentElement) + { + el.parentElement.removeChild(el); + } + } +} + +function showElement(el) +{ + if (el) + { + el.style.display = "block"; + } +} + +function hideElement(el) +{ + if (el) + { + el.style.display = "none"; + } +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.TextInput_v2.prototype = new CABLES.Op(); +CABLES.OPS["6538a190-e73c-451b-964e-d010ee267aa9"]={f:Ops.Sidebar.TextInput_v2,objName:"Ops.Sidebar.TextInput_v2"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.Toggle_v3 +// +// ************************************************************** + +Ops.Sidebar.Toggle_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const DEFAULT_VALUE_DEFAULT = true; + +// inputs +const parentPort = op.inObject("link"); +const labelPort = op.inString("Text", "Toggle"); +const inputValuePort = op.inValueBool("Input", DEFAULT_VALUE_DEFAULT); +const setDefaultValueButtonPort = op.inTriggerButton("Set Default"); +const defaultValuePort = op.inValueBool("Default", DEFAULT_VALUE_DEFAULT); +defaultValuePort.setUiAttribs({ "hidePort": true, "greyout": true }); +const inGreyOut = op.inBool("Grey Out", false); +const inVisible = op.inBool("Visible", true); + +// outputs +const siblingsPort = op.outObject("childs"); +const valuePort = op.outNumber("Value", defaultValuePort.get()); + +// vars +const el = document.createElement("div"); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.classList.add("sidebar__item"); +el.classList.add("sidebar__toggle"); +el.classList.add("sidebar__reloadable"); + +if (DEFAULT_VALUE_DEFAULT) el.classList.add("sidebar__toggle--active"); + +el.addEventListener("dblclick", function () +{ + valuePort.set(defaultValuePort.get()); + inputValuePort.set(defaultValuePort.get()); +}); + +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelText = document.createTextNode(labelPort.get()); +label.appendChild(labelText); +el.appendChild(label); + +const icon = document.createElement("div"); +icon.classList.add("icon_toggle"); +icon.addEventListener("click", onInputClick); +el.appendChild(icon); + +const greyOut = document.createElement("div"); +greyOut.classList.add("sidebar__greyout"); +el.appendChild(greyOut); +greyOut.style.display = "none"; + +// events +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +inputValuePort.onChange = onInputValuePortChanged; +op.onDelete = onDelete; +setDefaultValueButtonPort.onTriggered = setDefaultValue; + +function setDefaultValue() +{ + const defaultValue = inputValuePort.get(); + + defaultValuePort.set(defaultValue); + valuePort.set(defaultValue); + op.refreshParams(); +} + +function onInputClick() +{ + el.classList.toggle("sidebar__toggle--active"); + if (el.classList.contains("sidebar__toggle--active")) + { + valuePort.set(true); + inputValuePort.set(true); + icon.classList.add("icon_toggle_true"); + icon.classList.remove("icon_toggle_false"); + } + else + { + icon.classList.remove("icon_toggle_true"); + icon.classList.add("icon_toggle_false"); + + valuePort.set(false); + inputValuePort.set(false); + } + op.refreshParams(); +} + +function onInputValuePortChanged() +{ + const inputValue = inputValuePort.get(); + if (inputValue) + { + el.classList.add("sidebar__toggle--active"); + valuePort.set(true); + } + else + { + el.classList.remove("sidebar__toggle--active"); + valuePort.set(false); + } +} + +function onLabelTextChanged() +{ + const text = labelPort.get(); + label.textContent = text; + if (CABLES.UI) op.setTitle("Toggle: " + text); +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else if (el.parentElement) el.parentElement.removeChild(el); +} + +function showElement(element) +{ + if (element) element.style.display = "block"; +} + +function hideElement(element) +{ + if (element) element.style.display = "none"; +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(element) +{ + if (element && element.parentNode && element.parentNode.removeChild) element.parentNode.removeChild(el); +} + +inGreyOut.onChange = function () +{ + greyOut.style.display = inGreyOut.get() ? "block" : "none"; +}; + +inVisible.onChange = function () +{ + el.style.display = inVisible.get() ? "block" : "none"; +}; + + +}; + +Ops.Sidebar.Toggle_v3.prototype = new CABLES.Op(); +CABLES.OPS["fb60ab7d-f2f2-4fc5-bcd0-88c6ed481908"]={f:Ops.Sidebar.Toggle_v3,objName:"Ops.Sidebar.Toggle_v3"}; + + + + +// ************************************************************** +// +// Ops.Sidebar.XYPad +// +// ************************************************************** + +Ops.Sidebar.XYPad = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + parentPort = op.inObject("Link"), + labelPort = op.inString("Text", "XY Pad"), + inRange = op.inSwitch("Range", ["0-1", "-1-1"], "0-1"), + inputX = op.inValueSlider("Input X", 0), + inputY = op.inValueSlider("Input Y", 0), + + flipX = op.inBool("Flip X", false), + flipY = op.inBool("Flip Y", false), + + setDefaultValueButtonPort = op.inTriggerButton("Set Default"), + defaultValuePortX = op.inValueString("Default X", 0.5), + defaultValuePortY = op.inValueString("Default Y", 0.5), + + inVisible = op.inBool("Visible", true); + +const siblingsPort = op.outObject("Children"); + +defaultValuePortX.setUiAttribs({ "hidePort": true, "greyout": true }); +defaultValuePortY.setUiAttribs({ "hidePort": true, "greyout": true }); + +const outX = op.outNumber("X", 0.0); +const outY = op.outNumber("Y", 0.0); + +const el = document.createElement("div"); +el.dataset.op = op.id; +el.classList.add("cablesEle"); +el.addEventListener("dblclick", function () +{ + setOutValue(defaultValuePortX.get(), defaultValuePortY.get()); + redraw(); +}); + +el.classList.add("sidebar__item"); +el.classList.add("sidebar__color-picker"); +el.classList.add("sidebar__reloadable"); + +const label = document.createElement("div"); +label.classList.add("sidebar__item-label"); +const labelTextNode = document.createTextNode(labelPort.get()); +label.appendChild(labelTextNode); +el.appendChild(label); + +const valueX = document.createElement("input"); +valueX.classList.add("sidebar__text-input-input"); +valueX.setAttribute("type", "text"); +valueX.style.width = "40px"; +valueX.style.backgroundColor = "transparent"; +el.appendChild(valueX); + +const valueY = document.createElement("input"); +valueY.classList.add("sidebar__text-input-input"); +valueY.setAttribute("type", "text"); +valueY.style.width = "40px"; +valueY.style.backgroundColor = "transparent"; +el.appendChild(valueY); + +valueX.addEventListener("input", valueInputChanged); +valueY.addEventListener("input", valueInputChanged); +setDefaultValueButtonPort.onTriggered = setDefaultValues; + +inVisible.onChange = function () +{ + el.style.display = inVisible.get() ? "block" : "none"; +}; + +function valueInputChanged() +{ + let x = parseFloat(valueX.value); + let y = parseFloat(valueY.value); + + if (x != x || y != y) return; + + let minX = 0; + let maxX = 1; + let minY = 0; + let maxY = 1; + + x = Math.max(Math.min(x, maxX), minX); + y = Math.max(Math.min(y, maxY), minY); + + if (inRange.get() == "-1-1") + { + minY = minX = -1; + maxY = maxX = 1; + } + + setOutValue( + CABLES.map(x, minX, maxX, 0, 1), + CABLES.map(y, minY, maxY, 0, 1) + ); +} + +const size = 190; + +const canv = document.createElement("canvas"); +canv.width = canv.height = size; +canv.style.width = size + "px"; +canv.style.height = size + "px"; +canv.style.marginTop = "6px"; +canv.style.position = "initial"; +el.appendChild(canv); + +const ctx = canv.getContext("2d"); + +parentPort.onChange = onParentChanged; +labelPort.onChange = onLabelTextChanged; +op.onDelete = onDelete; + +inRange.onChange = () => +{ + setOutValue(defaultValuePortX.get(), defaultValuePortY.get()); + + redraw(); +}; + +redraw(); + +flipX.onChange = flipY.onChange = () => +{ + setOutValue(inputX.get(), inputY.get()); +}; + +canv.addEventListener("pointerdown", (e) => +{ + try { canv.setPointerCapture(e.pointerId); } + catch (e) {} +}); + +canv.addEventListener("pointerup", (e) => +{ + try { canv.releasePointerCapture(e.pointerId); } + catch (e) {} +}); + +inputX.onChange = () => +{ + // outX.set(Math.min(1, Math.max(inputX.get(), 0))); + setOutValue(inputX.get(), inputY.get()); + // redraw(); +}; + +inputY.onChange = () => +{ + // outY.set(Math.min(1, Math.max(inputY.get(), 0))); + setOutValue(inputX.get(), inputY.get()); + // redraw(); +}; + +function move(e) +{ + if (e.buttons == 1) + { + let x = Math.min(size, Math.max(e.offsetX, 0)); + let y = Math.min(size, Math.max(e.offsetY, 0)); + + if (e.shiftKey) + { + const s = size / 10; + x = Math.round(x / s) * s; + y = Math.round(y / s) * s; + } + + setOutValue(x / size, y / size); + inputX.set(x / size); + inputY.set(y / size); + op.refreshParams(); + } +} + +canv.addEventListener("pointermove", move, false); +canv.addEventListener("pointerdown", move, false); + +function setOutValue(x, y) +{ + inputX.set(x); + inputY.set(y); + + if (flipX.get())x = 1 - x; + if (flipY.get())y = 1 - y; + + let ox = x; + let oy = y; + + if (inRange.get() == "0-1") + { + outX.set(ox); + outY.set(oy); + } + else + { + outX.set(((ox) - 0.5) * 2.0); + outY.set(((oy) - 0.5) * 2.0); + } + + if (valueX.value != "" + outX.get()) valueX.value = "" + outX.get(); + if (valueY.value != "" + outY.get()) valueY.value = "" + outY.get(); + + redraw(); +} + +function redraw() +{ + ctx.lineWidth = 1; + ctx.strokeWidth = 1; + + ctx.clearRect(0, 0, size, size); + ctx.fillStyle = "#333"; + ctx.fillRect(0, 0, size, size); + + ctx.strokeStyle = "#555"; + ctx.strokeRect(0, size / 2, size, 0); + ctx.strokeRect(size / 2, 0, 0, size); + ctx.strokeRect(0, 0, size, size); + + ctx.strokeStyle = "#fff"; + ctx.beginPath(); + ctx.arc(inputX.get() * size, inputY.get() * size, 5, 0, Math.PI * 2, true); + ctx.stroke(); +} + +function setDefaultValues() +{ + // const hex = getInputColorHex(); + defaultValuePortY.set(inputY.get()); + defaultValuePortX.set(inputX.get()); + + setOutValue(defaultValuePortX.get(), defaultValuePortY.get()); + + // defaultValuePort.set(hex); + // outHex.set(hex); + + redraw(); + op.refreshParams(); +} + +function inputColorChanged() +{ + redraw(); +} + +function onLabelTextChanged() +{ + const labelText = labelPort.get(); + label.textContent = labelText; + + op.setUiAttrib({ "extendTitle": labelText }); +} + +function onParentChanged() +{ + siblingsPort.set(null); + const parent = parentPort.get(); + if (parent && parent.parentElement) + { + parent.parentElement.appendChild(el); + siblingsPort.set(parent); + } + else if (el.parentElement) el.parentElement.removeChild(el); +} + +function showElement(el) +{ + if (el) el.style.display = "block"; +} + +function hideElement(el) +{ + if (el) el.style.display = "none"; +} + +function onDelete() +{ + removeElementFromDOM(el); +} + +function removeElementFromDOM(el) +{ + if (el && el.parentNode && el.parentNode.removeChild) + { + el.parentNode.removeChild(el); + } +} + + +}; + +Ops.Sidebar.XYPad.prototype = new CABLES.Op(); +CABLES.OPS["84f3d05f-0991-4721-88cb-66776b16a094"]={f:Ops.Sidebar.XYPad,objName:"Ops.Sidebar.XYPad"}; + + + + +// ************************************************************** +// +// Ops.String.AddLineBreaks +// +// ************************************************************** + +Ops.String.AddLineBreaks = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr=op.inString("String",""), + inMaxLineChars=op.inInt("Max Characters per Line",20), + outStr=op.outString("Result"); + +inMaxLineChars.onChange= + inStr.onChange=update; + +function update() +{ + const str=inStr.get(); + if(!str) + { + outStr.set(""); + return; + } + + const numChars=inMaxLineChars.get(); + const parts=str.split(" "); + const lines=[]; + var line=""; + for(var i=0;inumChars) + { + lines.push(line); + line=""; + } + + line+=word; + } + lines.push(line); + outStr.set(lines.join("\n")); +} + + +}; + +Ops.String.AddLineBreaks.prototype = new CABLES.Op(); +CABLES.OPS["9746be6e-bf86-4a5f-86d9-d3b1c26d7f28"]={f:Ops.String.AddLineBreaks,objName:"Ops.String.AddLineBreaks"}; + + + + +// ************************************************************** +// +// Ops.String.ArrayContainsString +// +// ************************************************************** + +Ops.String.ArrayContainsString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + inValue = op.inString("SearchValue"), + outFound = op.outBoolNum("Found", false), + outIndex = op.outNumber("Index", -1); + +inValue.onChange = () => +{ + op.setUiAttrib({ "extendTitle": inValue.get() }); + exec(); +}; + +inArr.onChange = exec; + +function exec() +{ + if (inArr.get()) + { + const index = inArr.get().indexOf(inValue.get()); + + outIndex.set(index); + outFound.set(index > -1); + } +} + + +}; + +Ops.String.ArrayContainsString.prototype = new CABLES.Op(); +CABLES.OPS["bace9c9b-5e96-4a82-9bcd-02e316afb9de"]={f:Ops.String.ArrayContainsString,objName:"Ops.String.ArrayContainsString"}; + + + + +// ************************************************************** +// +// Ops.String.ArrayOfStrings +// +// ************************************************************** + +Ops.String.ArrayOfStrings = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("String", "default"), + inLength = op.inInt("Length", 10), + inAttachNum = op.inBool("Attach Number", false), + outArr = op.outArray("Array"); + +inStr.onChange = + inLength.onChange = + inAttachNum.onChange = update; + +function update() +{ + let arr = []; + let str = inStr.get(); + let l = inLength.get(); + + if (inAttachNum.get()) + for (let i = 0; i < l; i++) + arr[i] = str + i; + else + for (let i = 0; i < l; i++) + arr[i] = str; + + outArr.set(arr); +} + + +}; + +Ops.String.ArrayOfStrings.prototype = new CABLES.Op(); +CABLES.OPS["846f9998-1c05-4757-8011-21020ed01c6d"]={f:Ops.String.ArrayOfStrings,objName:"Ops.String.ArrayOfStrings"}; + + + + +// ************************************************************** +// +// Ops.String.Base64Decode_v2 +// +// ************************************************************** + +Ops.String.Base64Decode_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let r = ""; +const + inString = op.inString("String"), + result = op.outString("Result"); +inString.onChange = function () +{ + result.set(b64DecodeUnicode(inString.get() || "")); +}; + +function b64DecodeUnicode(str) +{ + // Going backwards: from bytestream, to percent-encoding, to original string. + + if (str.indexOf("base64,") > 1) + { + str = str.substring(str.indexOf("base64,") + "base64,".length); + } + + let r = ""; + + try + { + r = decodeURIComponent(atob(str).split("").map(function (c) + { + return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); + }).join("")); + } + catch (e) + { + op.error(e); + } + + return r; +} + + +}; + +Ops.String.Base64Decode_v2.prototype = new CABLES.Op(); +CABLES.OPS["4986a1d8-390f-48ee-aff4-c4257ddb480a"]={f:Ops.String.Base64Decode_v2,objName:"Ops.String.Base64Decode_v2"}; + + + + +// ************************************************************** +// +// Ops.String.Base64Encode_v2 +// +// ************************************************************** + +Ops.String.Base64Encode_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let inString = op.inString("String"); +let result = op.outString("Result"); + +inString.onChange = function () +{ + result.set(b64EncodeUnicode(inString.get())); +}; + +function b64EncodeUnicode(str) +{ + // first we use encodeURIComponent to get percent-encoded UTF-8, + // then we convert the percent encodings into raw bytes which + // can be fed into btoa. + return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, + function toSolidBytes(match, p1) + { + return String.fromCharCode("0x" + p1); + })); +} + + +}; + +Ops.String.Base64Encode_v2.prototype = new CABLES.Op(); +CABLES.OPS["e6ff2e1b-0c7a-4d0b-b1cf-720c2bbe3514"]={f:Ops.String.Base64Encode_v2,objName:"Ops.String.Base64Encode_v2"}; + + + + +// ************************************************************** +// +// Ops.String.CharacterRotate +// +// ************************************************************** + +Ops.String.CharacterRotate = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUpdate = op.inTriggerButton("Update"), + inReset = op.inTriggerButton("Reset"), + inText = op.inString("Text"), + inSeed = op.inFloat("Random Seed", 0), + inChars = op.inString("Characters", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 -_+!"), + result = op.outString("Result"); + +let positions = []; +let position = 0; +let resultString = ""; + +inSeed.onChange = init; +inText.onChange = init; +inReset.onTriggered = init; + +function init() +{ + position++; + let txt = inText.get(); + if (!txt) + { + result.set(""); + return; + } + let alphab = inChars.get(); + + resultString = ""; + + Math.randomSeed = inSeed.get(); + + for (let i = 0; i < txt.length; i++) + { + if (inSeed.get() == 0) + { + resultString += alphab[0]; + } + else + { + resultString += alphab[Math.floor(Math.seededRandom() * alphab.length)]; + } + } + + result.set(resultString); +} + +inUpdate.onTriggered = function () +{ + let txt = inText.get(); + let alphab = inChars.get(); + + if (!txt) + { + result.set(""); + return; + } + + if (!resultString) init(); + let newStr = ""; + + for (let i = 0; i < txt.length; i++) + { + if (txt[i] == "\n") + { + newStr += "\n"; + } + else + if (txt[i] != resultString[i]) + { + let newindex = alphab.indexOf(resultString[i]) + 1; + + if (newindex > alphab.length - 1)newindex = 0; + newStr += alphab[newindex]; + } + else + { + newStr += txt[i]; + } + } + resultString = newStr; + + result.set(resultString); +}; + + +}; + +Ops.String.CharacterRotate.prototype = new CABLES.Op(); +CABLES.OPS["9dcc7ad1-c790-4c9d-9f2a-a902bd8d6cc8"]={f:Ops.String.CharacterRotate,objName:"Ops.String.CharacterRotate"}; + + + + +// ************************************************************** +// +// Ops.String.ConcatMulti_v2 +// +// ************************************************************** + +Ops.String.ConcatMulti_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const addSpacesCheckBox = op.inBool("add spaces", false), + newLinesCheckBox = op.inBool("new lines", false), + stringPorts = [], + result = op.outString("concat string"); + +stringPorts.onChange = addSpacesCheckBox.onChange = +newLinesCheckBox.onChange = update; + +addSpacesCheckBox.setUiAttribs({ "hidePort": true }); +newLinesCheckBox.setUiAttribs({ "hidePort": true }); + +for (let i = 0; i < 8; i++) +{ + let p = op.inString("string " + i); + stringPorts.push(p); + p.onChange = update; +} + +function update() +{ + let str = ""; + let nl = ""; + let space = addSpacesCheckBox.get(); + + for (let i = 0; i < stringPorts.length; i++) + { + const inString = stringPorts[i].get(); + if (!inString) continue; + if (i > 0 && space) str += " "; + if (i > 0 && newLinesCheckBox.get()) nl = "\n"; + str += nl; + str += inString; + } + result.set(str); +} + + +}; + +Ops.String.ConcatMulti_v2.prototype = new CABLES.Op(); +CABLES.OPS["bc110e48-812d-489d-b1b3-b09c644c6982"]={f:Ops.String.ConcatMulti_v2,objName:"Ops.String.ConcatMulti_v2"}; + + + + +// ************************************************************** +// +// Ops.String.Concat_v2 +// +// ************************************************************** + +Ops.String.Concat_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + string1 = op.inString("string1", "ABC"), + string2 = op.inString("string2", "XYZ"), + newLine = op.inValueBool("New Line", false), + result = op.outString("result"); + +newLine.onChange = string2.onChange = string1.onChange = exec; +exec(); + +function exec() +{ + let s1 = string1.get(); + let s2 = string2.get(); + if (!s1 && !s2) + { + result.set(""); + return; + } + if (!s1)s1 = ""; + if (!s2)s2 = ""; + + let nl = ""; + if (s1 && s2 && newLine.get())nl = "\n"; + result.set(String(s1) + nl + String(s2)); +} + + +}; + +Ops.String.Concat_v2.prototype = new CABLES.Op(); +CABLES.OPS["a52722aa-0ca9-402c-a844-b7e98a6c6e60"]={f:Ops.String.Concat_v2,objName:"Ops.String.Concat_v2"}; + + + + +// ************************************************************** +// +// Ops.String.CopyToClipboard +// +// ************************************************************** + +Ops.String.CopyToClipboard = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inCopy= op.inTriggerButton("Copy"), + inStr=op.inString("String","cablez"); + +inCopy.onTriggered=()=> +{ + navigator.clipboard.writeText(inStr.get()); +}; + + +}; + +Ops.String.CopyToClipboard.prototype = new CABLES.Op(); +CABLES.OPS["283c7eef-680b-45f2-880a-5d9f0762854b"]={f:Ops.String.CopyToClipboard,objName:"Ops.String.CopyToClipboard"}; + + + + +// ************************************************************** +// +// Ops.String.FilterValidString +// +// ************************************************************** + +Ops.String.FilterValidString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("String", ""), + checkNull = op.inBool("Invalid if null", true), + checkUndefined = op.inBool("Invalid if undefined", true), + checkEmpty = op.inBool("Invalid if empty", true), + checkZero = op.inBool("Invalid if 0", true), + outStr = op.outString("Last Valid String"), + result = op.outBoolNum("Is Valid"); + +inStr.onChange = +checkNull.onChange = +checkUndefined.onChange = +checkEmpty.onChange = +function () +{ + const str = inStr.get(); + let r = true; + + if (r === false)r = false; + if (r && checkZero.get() && (str === 0 || str === "0")) r = false; + if (r && checkNull.get() && str === null) r = false; + if (r && checkUndefined.get() && str === undefined) r = false; + if (r && checkEmpty.get() && str === "") r = false; + + result.set(r); + if (r)outStr.set(str); +}; + + +}; + +Ops.String.FilterValidString.prototype = new CABLES.Op(); +CABLES.OPS["a522235d-f220-46ea-bc26-13a5b20ec8c6"]={f:Ops.String.FilterValidString,objName:"Ops.String.FilterValidString"}; + + + + +// ************************************************************** +// +// Ops.String.GateString +// +// ************************************************************** + +Ops.String.GateString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + valueInPort = op.inString('String In', 'hello'), + passThroughPort = op.inValueBool('Pass Through',false), + valueOutPort = op.outString('String Out',''); + +valueInPort.onChange = +passThroughPort.onChange = update; + +function update() +{ + if(passThroughPort.get()) + { + valueOutPort.set(null); + valueOutPort.set(valueInPort.get()); + } + // else + // valueOutPort.set(''); +} + +}; + +Ops.String.GateString.prototype = new CABLES.Op(); +CABLES.OPS["0ce14933-2d91-4381-9d82-2304aae22c0e"]={f:Ops.String.GateString,objName:"Ops.String.GateString"}; + + + + +// ************************************************************** +// +// Ops.String.LeftPad_v2 +// +// ************************************************************** + +Ops.String.LeftPad_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let val = op.inString("Value", 1); +let char = op.inString("Char", "0"); +let num = op.inInt("Num", 4); +let out = op.outString("String"); + +val.onChange = update; +char.onChange = update; +num.onChange = update; + +function update() +{ + let v = val.get(); + let n = num.get(); + + let pad = ""; + for (let i = 0; i < n; i++)pad += ("" + char.get()); + + let str = v + ""; + str = pad.substring(0, pad.length - str.length) + str; + + out.set(str); +} + + +}; + +Ops.String.LeftPad_v2.prototype = new CABLES.Op(); +CABLES.OPS["da8aba9f-9e9c-4fca-87c9-2726b6e101e2"]={f:Ops.String.LeftPad_v2,objName:"Ops.String.LeftPad_v2"}; + + + + +// ************************************************************** +// +// Ops.String.LimitLineBreaks_v2 +// +// ************************************************************** + +Ops.String.LimitLineBreaks_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("String", "default"), + inNum = op.inInt("Num Lines", 5), + inRev = op.inBool("Reverse", false), + inAppend = op.inBool("Force Num Lines", false), + outStr = op.outString("Result", "default"); + +let stringsNew = []; + +inRev.onChange = + inAppend.onChange = + inStr.onChange = + inNum.onChange = update; + +function update() +{ + let strings = inStr.get().split("\n"); + var num = num = Math.max(0, Math.floor(inNum.get()) + 1); + + if (inRev.get()) + { + if (strings.length > num) + { + for (var i = 0; i < num; i++) + stringsNew[num - i] = strings[strings.length - i]; + + strings = stringsNew; + } + } + else + { + strings.length = Math.min(num, strings.length); + } + + let str = strings.join("\n"); + + if (inAppend.get()) + { + if (strings.length < num) + for (var i = strings.length; i < num; i++) + str += "\n"; + } + + outStr.set(str); +} + + +}; + +Ops.String.LimitLineBreaks_v2.prototype = new CABLES.Op(); +CABLES.OPS["fcf0f2a3-42db-485c-8c0b-15c5eb1f18c7"]={f:Ops.String.LimitLineBreaks_v2,objName:"Ops.String.LimitLineBreaks_v2"}; + + + + +// ************************************************************** +// +// Ops.String.LineBreaksHtml +// +// ************************************************************** + +Ops.String.LineBreaksHtml = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr=op.inString("String",""), + inNum=op.inInt("Add Num Breaks",1), + outStr=op.outString("HTML"); + +inNum.onChange= +inStr.onChange=function() +{ + var str=inStr.get(); + var newlines=''; + + for(var i=0;i
    ')); + + +}; + +Ops.String.LoremIpsum.prototype = new CABLES.Op(); +CABLES.OPS["c19ebc53-82d5-429c-a6cc-ff350a2c5d3e"]={f:Ops.String.LoremIpsum,objName:"Ops.String.LoremIpsum"}; + + + + +// ************************************************************** +// +// Ops.String.Lowercase_v2 +// +// ************************************************************** + +Ops.String.Lowercase_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("String"), + outStr = op.outString("Result", ""); + +inStr.onChange = function () +{ + if (inStr.get() == 0)outStr.set(""); + else outStr.set((inStr.get() || "").toLowerCase()); +}; + + +}; + +Ops.String.Lowercase_v2.prototype = new CABLES.Op(); +CABLES.OPS["bff9c3d9-e63a-46d2-a59f-932c715aceab"]={f:Ops.String.Lowercase_v2,objName:"Ops.String.Lowercase_v2"}; + + + + +// ************************************************************** +// +// Ops.String.NumTotalLineBreaks +// +// ************************************************************** + +Ops.String.NumTotalLineBreaks = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr=op.inString("String","default"), + outNum=op.outNumber("Total Lines",0); + + +inStr.onChange=function() +{ + var strings=inStr.get().split('\n'); + outNum.set(strings.length); + + +}; + +}; + +Ops.String.NumTotalLineBreaks.prototype = new CABLES.Op(); +CABLES.OPS["76a958cd-def3-40e5-ab2b-c08a3325d9cb"]={f:Ops.String.NumTotalLineBreaks,objName:"Ops.String.NumTotalLineBreaks"}; + + + + +// ************************************************************** +// +// Ops.String.NumberFormatter +// +// ************************************************************** + +Ops.String.NumberFormatter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inNum = op.inFloat("Input Number"); +const inLocaleSwitch = op.inSwitch("Locale", ["browser", "custom"], "browser"); +const inLocaleString = op.inString("Locale string", "en-US"); +const inStyle = op.inSwitch("Style", ["decimal", "currency", "percent"], "decimal"); +const inMinDig = op.inInt("Minimum Integer Digits", 1); +const inMinFrac = op.inInt("Minimum Fraction Digits", 3); +const inMaxFrac = op.inInt("Maximum Fraction Digits", 3); +op.setPortGroup("Format", [inMinDig, inMinFrac, inMaxFrac]); +const inMinSign = op.inInt("Minimum Significant Digits", 0); +const inMaxSign = op.inInt("Maximum Significant Digits", 0); +op.setPortGroup("Significant Digits", [inMaxSign, inMinSign]); +const inUseGroup = op.inBool("Use Grouping", true); + +const inCurrencyName = op.inString("Currency Name", "EUR"); +const inCurrencyDisplay = op.inSwitch("Currency Display", ["symbol", "code", "name"], "symbol"); +op.setPortGroup("Currency", [inCurrencyName, inCurrencyDisplay]); + +const outString = op.outString("Formatted Number", "0,000"); +const outError = op.outBoolNum("Has error"); + +// Bind functions +inNum.onChange = inLocaleString.onChange = +inStyle.onChange = inMaxFrac.onChange = +inMaxSign.onChange = inMinFrac.onChange = +inMinDig.onChange = inMinSign.onChange = +inUseGroup.onChange = inCurrencyName.onChange = +inCurrencyDisplay.onChange = inLocaleSwitch.onChange = formatNumber; + +function formatNumber() +{ + const num = inNum.get(); + const style = inStyle.get(); + + let minimumFractionDigits = CABLES.clamp(inMinFrac.get(), 0, 20); + let maximumFractionDigits = CABLES.clamp(inMaxFrac.get(), 0, 20); + + op.setUiError("minmaxfrac", null); + if(minimumFractionDigits > maximumFractionDigits) { + op.setUiError("minmaxfrac", "Minimum bigger than maximum for fraction digits, using minimum", 1); + maximumFractionDigits = minimumFractionDigits; + } + + let opts = { + "style": style, + "minimumFractionDigits": minimumFractionDigits, + "maximumFractionDigits": maximumFractionDigits, + "minimumIntegerDigits": CABLES.clamp(inMinDig.get(), 1, 21), + "useGrouping": inUseGroup.get() + }; + + if (inMinSign.get() > 0) opts.minimumSignificantDigits = CABLES.clamp(inMinSign.get(), 1, 21); + if (inMaxSign.get() > 0) opts.maximumSignificantDigits = CABLES.clamp(inMaxSign.get(), 1, 21); + + + op.setUiError("minmaxsig", null); + if(opts.minimumSignificantDigits > opts.maximumSignificantDigits) { + op.setUiError("minmaxsig", "Minimum bigger than maximum for significant digits, using minimum", 1); + opts.maximumSignificantDigits = opts.minimumSignificantDigits; + } + + if (style === "currency") + { + opts.currency = inCurrencyName.get(); + opts.currencyDisplay = inCurrencyDisplay.get(); + } + + try + { + let res = ""; + if (inLocaleSwitch.get() === "browser") + res = num.toLocaleString([], opts); + else + res = num.toLocaleString(inLocaleString.get(), opts); + + outString.set(res); + + if (outError.get()) + op.setUiError("format_error", null); + outError.set(false); + } + catch (e) + { + outError.set(true); + outString.set(""); + op.setUiError("format_error", e); + } +} + + +}; + +Ops.String.NumberFormatter.prototype = new CABLES.Op(); +CABLES.OPS["fb2ac304-5c36-419c-ba71-cdf43aed8a53"]={f:Ops.String.NumberFormatter,objName:"Ops.String.NumberFormatter"}; + + + + +// ************************************************************** +// +// Ops.String.NumberSwitchByString +// +// ************************************************************** + +Ops.String.NumberSwitchByString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inStr = op.inString("String", "default"); +const outNum = op.outNumber("Result", 0); +const numberStrings = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; + +const strings = []; +const numbers = []; + +inStr.onChange = update; + +for (let i = 0; i < numberStrings.length; i++) +{ + const idx = i + 1; + const istr = op.inString("String " + idx, numberStrings[i]); + const inum = op.inFloat("Number " + idx, idx); + + strings.push(istr); + numbers.push(inum); +} + +function update() +{ + const s = inStr.get(); + + for (let i = 0; i < numberStrings.length; i++) + { + if (strings[i].get() == s)outNum.set(numbers[i].get()); + } +} + + +}; + +Ops.String.NumberSwitchByString.prototype = new CABLES.Op(); +CABLES.OPS["bb3f53ab-c4ad-4c07-81df-d9ca7ee976f2"]={f:Ops.String.NumberSwitchByString,objName:"Ops.String.NumberSwitchByString"}; + + + + +// ************************************************************** +// +// Ops.String.NumberToString_v2 +// +// ************************************************************** + +Ops.String.NumberToString_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val = op.inValue("Number"), + result = op.outString("Result"); + +val.onChange = update; +update(); + +function update() +{ + result.set(String(val.get() || 0)); +} + + +}; + +Ops.String.NumberToString_v2.prototype = new CABLES.Op(); +CABLES.OPS["5c6d375a-82db-4366-8013-93f56b4061a9"]={f:Ops.String.NumberToString_v2,objName:"Ops.String.NumberToString_v2"}; + + + + +// ************************************************************** +// +// Ops.String.OrString +// +// ************************************************************** + +Ops.String.OrString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + str0=op.inString("String 1"), + str1=op.inString("String 2"), + str2=op.inString("String 3"), + str3=op.inString("String 4"), + str4=op.inString("String 5"), + str5=op.inString("String 6"), + str6=op.inString("String 7"), + str7=op.inString("String 8"), + result=op.outString("Result"); + +str0.onChange= + str1.onChange= + str2.onChange= + str3.onChange= + str4.onChange= + str5.onChange= + str6.onChange= + str7.onChange=exec; + +function exec() +{ + result.set( str0.get() || str1.get() || str2.get() || str3.get() || str4.get() || str5.get() || str6.get() || str7.get() ); +} + + + +}; + +Ops.String.OrString.prototype = new CABLES.Op(); +CABLES.OPS["6b7f9561-6faf-4df7-ac3b-64235dded700"]={f:Ops.String.OrString,objName:"Ops.String.OrString"}; + + + + +// ************************************************************** +// +// Ops.String.ParseFloat +// +// ************************************************************** + +Ops.String.ParseFloat = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + str = op.inString("String", 5711), + outNum = op.outNumber("Number"); + +str.onChange = function () +{ + let num = parseFloat(str.get()); + if (num != num) num = 0; + outNum.set(num); +}; + + +}; + +Ops.String.ParseFloat.prototype = new CABLES.Op(); +CABLES.OPS["fa36a56b-a64d-4269-9a9e-addc16493006"]={f:Ops.String.ParseFloat,objName:"Ops.String.ParseFloat"}; + + + + +// ************************************************************** +// +// Ops.String.ParseInt_v2 +// +// ************************************************************** + +Ops.String.ParseInt_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + str = op.inString("String", 5711), + outNum = op.outNumber("Number"); + +str.onChange = function () +{ + let num = parseInt(str.get()); + if (num != num) num = 0; + outNum.set(num); +}; + + +}; + +Ops.String.ParseInt_v2.prototype = new CABLES.Op(); +CABLES.OPS["6d208424-daf2-4a2b-874f-daac406c1f66"]={f:Ops.String.ParseInt_v2,objName:"Ops.String.ParseInt_v2"}; + + + + +// ************************************************************** +// +// Ops.String.RandomString_v2 +// +// ************************************************************** + +Ops.String.RandomString_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + chars = op.inString("chars", "cables"), + len = op.inInt("Length", 10), + generate = op.inTriggerButton("Generate"), + result = op.outString("Result"); + +generate.onTriggered = gen; +gen(); + +function gen() +{ + const charArray = Array.from(chars.get()); + let str = ""; + if (charArray.length > 0) + { + let numChars = charArray.length - 1; + for (let i = 0; i < Math.abs(len.get()); i++) + str += charArray[Math.round(Math.random() * numChars)]; + } + + result.set(str); +} + + +}; + +Ops.String.RandomString_v2.prototype = new CABLES.Op(); +CABLES.OPS["55285d4a-f542-4c8b-9839-02b33b15c916"]={f:Ops.String.RandomString_v2,objName:"Ops.String.RandomString_v2"}; + + + + +// ************************************************************** +// +// Ops.String.RightPadNumber_v2 +// +// ************************************************************** + +Ops.String.RightPadNumber_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const val=op.inValue("Value",1); +const num=op.inValueInt("Num",4); +const out=op.outString("String"); + +val.onChange=update; +num.onChange=update; + +function update() +{ + var str=val.get()+''; + + var start=str.indexOf("."); + var numChars=num.get(); + + if(start==-1) + { + str+='.'; + } + else + { + var parts=str.split("."); + numChars=num.get()-parts[1].length; + } + + for(var i=0;i port.set(defaultValue)); +}; + +function update() +{ + setDefaultValues(); + var index = indexPort.get(); + var value = valuePort.get(); + index = Math.round(index); + index = clamp(index, 0, NUM_PORTS-1); + valuePorts[index].set(value); +}; + +function clamp(value, min, max) +{ + return Math.min(Math.max(value, min), max); +}; + + +}; + +Ops.String.RouteString.prototype = new CABLES.Op(); +CABLES.OPS["9998ff83-335b-40cd-aa0e-4cae558cb551"]={f:Ops.String.RouteString,objName:"Ops.String.RouteString"}; + + + + +// ************************************************************** +// +// Ops.String.SaveTextFile +// +// ************************************************************** + +Ops.String.SaveTextFile = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + download = op.inTriggerButton("Download"), + filename = op.inString("Filename", "textfile.txt"), + inStr = op.inString("Content String"); + +download.onTriggered = function () +{ + const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(inStr.get()); + const downloadAnchorNode = document.createElement("a"); + downloadAnchorNode.setAttribute("href", dataStr); + downloadAnchorNode.setAttribute("download", filename.get() ); + downloadAnchorNode.click(); + downloadAnchorNode.remove(); +}; + + +}; + +Ops.String.SaveTextFile.prototype = new CABLES.Op(); +CABLES.OPS["ef5929f0-8163-460b-9042-6f20f4f7150b"]={f:Ops.String.SaveTextFile,objName:"Ops.String.SaveTextFile"}; + + + + +// ************************************************************** +// +// Ops.String.StringCompose_v3 +// +// ************************************************************** + +Ops.String.StringCompose_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + format=op.inString('Format',"hello $a, $b $c und $d"), + a=op.inString('String A','world'), + b=op.inString('String B',1), + c=op.inString('String C',2), + d=op.inString('String D',3), + e=op.inString('String E'), + f=op.inString('String F'), + result=op.outString("Result"); + +format.onChange= + a.onChange= + b.onChange= + c.onChange= + d.onChange= + e.onChange= + f.onChange=update; + +update(); + +function update() +{ + var str=format.get()||''; + if(typeof str!='string') + str=''; + + str = str.replace(/\$a/g, a.get()); + str = str.replace(/\$b/g, b.get()); + str = str.replace(/\$c/g, c.get()); + str = str.replace(/\$d/g, d.get()); + str = str.replace(/\$e/g, e.get()); + str = str.replace(/\$f/g, f.get()); + + result.set(str); +} + +}; + +Ops.String.StringCompose_v3.prototype = new CABLES.Op(); +CABLES.OPS["6afea9f4-728d-4f3c-9e75-62ddc1448bf0"]={f:Ops.String.StringCompose_v3,objName:"Ops.String.StringCompose_v3"}; + + + + +// ************************************************************** +// +// Ops.String.StringContains_v2 +// +// ************************************************************** + +Ops.String.StringContains_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("String"), + inValue = op.inString("SearchValue"), + outFound = op.outBoolNum("Found", false), + outIndex = op.outNumber("Index", -1); + +inValue.onChange = + inStr.onChange = exec; + +exec(); + +function exec() +{ + if (inStr.get() && inValue.get().length > 0) + { + const index = inStr.get().indexOf(inValue.get()); + outIndex.set(index); + outFound.set(index > -1); + } + else + { + outIndex.set(-1); + outFound.set(false); + } +} + + +}; + +Ops.String.StringContains_v2.prototype = new CABLES.Op(); +CABLES.OPS["2ca3e5d7-e6b4-46a7-8381-3fe1ad8b6879"]={f:Ops.String.StringContains_v2,objName:"Ops.String.StringContains_v2"}; + + + + +// ************************************************************** +// +// Ops.String.StringEditor +// +// ************************************************************** + +Ops.String.StringEditor = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v = op.inStringEditor("value", ""), + syntax = op.inValueSelect("Syntax", ["text", "glsl", "css", "html", "xml", "json", "javascript", "inline-css", "sql"], "text"), + result = op.outString("Result"); + +syntax.onChange = updateSyntax; + +function updateSyntax() +{ + let s = syntax.get(); + if (s == "javascript")s = "js"; + v.setUiAttribs({ "editorSyntax": s }); +} + +v.onChange = function () +{ + result.set(v.get()); +}; + + +}; + +Ops.String.StringEditor.prototype = new CABLES.Op(); +CABLES.OPS["6468b7c1-f63e-4db4-b809-4b203d27ead3"]={f:Ops.String.StringEditor,objName:"Ops.String.StringEditor"}; + + + + +// ************************************************************** +// +// Ops.String.StringEquals +// +// ************************************************************** + +Ops.String.StringEquals = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + str1 = op.inString("String 1"), + str2 = op.inString("String 2"), + result = op.outBoolNum("Result"); + +str1.onChange = +str2.onChange = + function () + { + result.set(str1.get() == str2.get()); + }; + + +}; + +Ops.String.StringEquals.prototype = new CABLES.Op(); +CABLES.OPS["ef15195a-760b-4ac5-9630-322b0ba7b722"]={f:Ops.String.StringEquals,objName:"Ops.String.StringEquals"}; + + + + +// ************************************************************** +// +// Ops.String.StringIterator_v2 +// +// ************************************************************** + +Ops.String.StringIterator_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inExe = op.inTrigger("Exec"), + inString = op.inString("String", "cables"), + next = op.outTrigger("Next"), + outChar = op.outString("Character"), + outIndex = op.outNumber("Index"), + outLength = op.outNumber("Length"); + +inExe.onTriggered = function () +{ + let str = inString.get(); + outLength.set(str.length); + + for (let i = 0; i < str.length; i++) + { + outChar.set(str[i]); + outIndex.set(i); + next.trigger(); + } +}; + + +}; + +Ops.String.StringIterator_v2.prototype = new CABLES.Op(); +CABLES.OPS["2def6fdb-de39-4f56-9818-05b7a302583c"]={f:Ops.String.StringIterator_v2,objName:"Ops.String.StringIterator_v2"}; + + + + +// ************************************************************** +// +// Ops.String.StringLength_v2 +// +// ************************************************************** + +Ops.String.StringLength_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("String"), + result = op.outNumber("Result"); + +inStr.onChange = function () +{ + if (!inStr.get()) result.set(0); + else result.set(String(inStr.get()).length); +}; + + +}; + +Ops.String.StringLength_v2.prototype = new CABLES.Op(); +CABLES.OPS["aa47bb8b-d5d7-4175-b217-ab0157d3365d"]={f:Ops.String.StringLength_v2,objName:"Ops.String.StringLength_v2"}; + + + + +// ************************************************************** +// +// Ops.String.StringReplace +// +// ************************************************************** + +Ops.String.StringReplace = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("String"), + inSearch = op.inString("Search For", "foo"), + inRepl = op.inString("Replace", "bar"), + inWhat = op.inSwitch("Replace What", ["All", "First"], "All"), + outStr = op.outString("Result"); + +inRepl.onChange = +inStr.onChange = +inWhat.onChange = +inSearch.onChange = update; + +function update() +{ + let str = ""; + + if (inWhat.get() == "All") str = String(inStr.get()).replace(new RegExp(inSearch.get(), "g"), inRepl.get()); + else str = String(inStr.get()).replace(inSearch.get(), inRepl.get()); + + outStr.set(str); +} + + +}; + +Ops.String.StringReplace.prototype = new CABLES.Op(); +CABLES.OPS["4a053e7a-6b00-4e71-bd51-90cdb190994c"]={f:Ops.String.StringReplace,objName:"Ops.String.StringReplace"}; + + + + +// ************************************************************** +// +// Ops.String.StringTrim_v2 +// +// ************************************************************** + +Ops.String.StringTrim_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr=op.inString("String"), + outStr=op.outString("Result",''); + +inStr.onChange=function() +{ + if(!inStr.get())outStr.set(''); + else outStr.set(inStr.get().trim()); +}; + + +}; + +Ops.String.StringTrim_v2.prototype = new CABLES.Op(); +CABLES.OPS["a9aed302-328a-4d33-bd3f-27e3e6690b9e"]={f:Ops.String.StringTrim_v2,objName:"Ops.String.StringTrim_v2"}; + + + + +// ************************************************************** +// +// Ops.String.String_v2 +// +// ************************************************************** + +Ops.String.String_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v=op.inString("value",""), + result=op.outString("String"); + +v.onChange=function() +{ + result.set(v.get()); +}; + + + +}; + +Ops.String.String_v2.prototype = new CABLES.Op(); +CABLES.OPS["d697ff82-74fd-4f31-8f54-295bc64e713d"]={f:Ops.String.String_v2,objName:"Ops.String.String_v2"}; + + + + +// ************************************************************** +// +// Ops.String.StripHtml +// +// ************************************************************** + +Ops.String.StripHtml = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("String", ""), + outStr = op.outString("Result"); + +inStr.onChange = function () +{ + // outStr.set((inStr.get() || "").replace(/(<([^>]+)>)/ig, "")); + + const parser = new DOMParser(); + const dom = parser.parseFromString(inStr.get(), "text/html"); + + outStr.set(dom.body.textContent); +}; + + +}; + +Ops.String.StripHtml.prototype = new CABLES.Op(); +CABLES.OPS["8a868fc7-363f-4221-9789-67ffe5830e36"]={f:Ops.String.StripHtml,objName:"Ops.String.StripHtml"}; + + + + +// ************************************************************** +// +// Ops.String.SubString_v2 +// +// ************************************************************** + +Ops.String.SubString_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr=op.inString("String","cables"), + inStart=op.inValueInt("Start",0), + inEnd=op.inValueInt("End",4), + result=op.outString("Result"); + +inStr.onChange= + inStart.onChange= + inEnd.onChange=update; + +update(); + +function update() +{ + var start=inStart.get(); + var end=inEnd.get(); + var str=inStr.get()+''; + result.set( str.substring(start,end) ); +} + +}; + +Ops.String.SubString_v2.prototype = new CABLES.Op(); +CABLES.OPS["6e994ba8-01d1-4da6-98b4-af7e822a2e6c"]={f:Ops.String.SubString_v2,objName:"Ops.String.SubString_v2"}; + + + + +// ************************************************************** +// +// Ops.String.SwitchString +// +// ************************************************************** + +Ops.String.SwitchString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + idx=op.inValueInt("Index"), + result=op.outString("Result"); + +const valuePorts=[]; + +idx.onChange=update; + +for(var i=0;i<10;i++) +{ + var p=op.inString("String "+i); + valuePorts.push( p ); + p.onChange=update; +} + +function update() +{ + if(idx.get()>=0 && valuePorts[idx.get()]) + { + result.set( valuePorts[idx.get()].get() ); + } +} + +}; + +Ops.String.SwitchString.prototype = new CABLES.Op(); +CABLES.OPS["2a7a0c68-f7c9-4249-b19a-d2de5cb4862c"]={f:Ops.String.SwitchString,objName:"Ops.String.SwitchString"}; + + + + +// ************************************************************** +// +// Ops.String.SwitchStringBoolean +// +// ************************************************************** + +Ops.String.SwitchStringBoolean = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inBool=op.inValueBool("Boolean"), + inStrTrue=op.inString("True","Yes"), + inStrFalse=op.inString("False","No"), + result=op.outString("Result"); + +inBool.onChange= +inStrFalse.onChange= +inStrTrue.onChange=update; + +function update() +{ + if(inBool.get())result.set(inStrTrue.get()); + else result.set(inStrFalse.get()); +} + +update(); + +}; + +Ops.String.SwitchStringBoolean.prototype = new CABLES.Op(); +CABLES.OPS["19e3c428-22ce-45a3-b903-fddfc46fc0a3"]={f:Ops.String.SwitchStringBoolean,objName:"Ops.String.SwitchStringBoolean"}; + + + + +// ************************************************************** +// +// Ops.String.UUID +// +// ************************************************************** + +Ops.String.UUID = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + generate = op.inTriggerButton("Generate"), + result = op.outString("Id"); + +generate.onTriggered = update; +update(); + +function update() +{ + result.set(CABLES.uuid()); +} + + +}; + +Ops.String.UUID.prototype = new CABLES.Op(); +CABLES.OPS["0285022c-846a-43d4-8656-35ba3b5e03a6"]={f:Ops.String.UUID,objName:"Ops.String.UUID"}; + + + + +// ************************************************************** +// +// Ops.String.Uppercase_v2 +// +// ************************************************************** + +Ops.String.Uppercase_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr=op.inString("String"), + outStr=op.outString("Result"); + +inStr.onChange=function() +{ + if(!inStr.get())outStr.set(''); + else outStr.set(inStr.get().toUpperCase()); +}; + +}; + +Ops.String.Uppercase_v2.prototype = new CABLES.Op(); +CABLES.OPS["3f7e6ede-63b6-4262-9dcd-176b43374c4d"]={f:Ops.String.Uppercase_v2,objName:"Ops.String.Uppercase_v2"}; + + + + +// ************************************************************** +// +// Ops.Systems.ArraySpray +// +// ************************************************************** + +Ops.Systems.ArraySpray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTrigger("exe"); +const timer = op.inValue("time"); +const num = op.inValue("num", 100); +const sizeX = op.inValue("Size X"); +const sizeY = op.inValue("Size Y"); +const sizeZ = op.inValue("Size Z"); +const movementX = op.inValue("movement x", 1); +const movementY = op.inValue("movement y", 1); +const movementZ = op.inValue("movement z", 1); + +const centerX = op.inBool("Center X", false); +const centerY = op.inBool("Center Y", false); +const centerZ = op.inBool("Center Z", false); + +const inReset = op.inTriggerButton("Reset"); +const lifetime = op.inValue("lifetime", 10); +const lifetimeMin = op.inValue("Lifetime Minimum", 5); + +const + outTrigger = op.outTrigger("Trigger Out"), + outPositions = op.outArray("Positions"), + outLifetimes = op.outArray("Lifetime"); +inReset.onTriggered = reset; +const cgl = op.patch.cgl; + +const particles = []; +const transVec = vec3.create(); +const positions = []; +const lifetimes = []; + +num.onChange = + sizeX.onChange = + sizeY.onChange = + sizeZ.onChange = + lifetime.onChange = + centerX.onChange = + centerY.onChange = + centerZ.onChange = + lifetimeMin.onChange = reset; + +reset(); + +function Particle() +{ + this.pos = null; + this.startPos = null; + this.startTime = 0;// timer.get()- (Math.random() * ( lifetime.get() - lifetimeMin.get() ) + lifetimeMin.get())/2.0; + this.lifeTime = 0; + this.lifeTimePercent = 0; + this.endTime = 0; + + this.pos = [0, 0, 0]; + this.moveVec = [0, 0, 0]; + this.idDead = false; + + this.random1 = Math.random(); + this.random2 = Math.random(); + this.random3 = Math.random(); + + this.update = function (time) + { + const timeRunning = time - this.startTime; + if (time > this.endTime) this.isDead = true; + this.lifeTimePercent = timeRunning / (this.lifeTime); + + this.pos = vec3.fromValues( + this.startPos[0] + timeRunning * this.moveVec[0], + (this.startPos[1] + timeRunning * this.moveVec[1]), // -this.lifeTimePercent*this.lifeTimePercent*2.8, + this.startPos[2] + timeRunning * this.moveVec[2] + ); + }; + + this.reAnimate = function (time) + { + this.isDead = false; + this.lifeTime = Math.random() * (lifetime.get() - lifetimeMin.get()) + lifetimeMin.get(); + if (time !== undefined) + { + this.startTime = time; + this.endTime = time + this.lifeTime; + } + else + { + this.startTime = timer.get() - this.lifeTime * Math.random(); + this.endTime = timer.get() + this.lifeTime * Math.random(); + } + + let r = Math.random(); + + if (centerX.get())r -= 0.5; + const x = r * sizeX.get(); + + r = Math.random(); + if (centerY.get())r -= 0.5; + const y = r * sizeY.get(); + + r = Math.random(); + if (centerZ.get())r -= 0.5; + const z = r * sizeZ.get(); + + this.startPos = vec3.fromValues( + x, + y, + z); + + this.moveVec = [ + Math.random() * movementX.get(), + Math.random() * movementY.get(), + Math.random() * movementZ.get() + ]; + }; + this.reAnimate(0); +} + +exe.onTriggered = function () +{ + const time = timer.get(); + + if (positions.length != particles.length * 3) positions.length = particles.length * 3; + if (lifetimes.length != particles.length) lifetimes.length = particles.length; + + for (let i = 0; i < particles.length; i++) + { + if (particles[i].isDead)particles[i].reAnimate(time); + particles[i].update(time); + + positions[i * 3 + 0] = particles[i].pos[0]; + positions[i * 3 + 1] = particles[i].pos[1]; + positions[i * 3 + 2] = particles[i].pos[2]; + + lifetimes[i] = particles[i].lifeTimePercent; + if (lifetimes[i] > 1.0)lifetimes[i] = 1.0; + } + + outPositions.set(null); + outLifetimes.set(null); + outPositions.set(positions); + outLifetimes.set(lifetimes); + outTrigger.trigger(); +}; + +function reset() +{ + particles.length = 0; + + for (let i = 0; i < num.get(); i++) + { + const p = new Particle(); + p.reAnimate(); + particles.push(p); + } +} + + +}; + +Ops.Systems.ArraySpray.prototype = new CABLES.Op(); +CABLES.OPS["27023519-9e88-4b85-9fcf-b316847e0df7"]={f:Ops.Systems.ArraySpray,objName:"Ops.Systems.ArraySpray"}; + + + + +// ************************************************************** +// +// Ops.Systems.Lsystem_v2 +// +// ************************************************************** + +Ops.Systems.Lsystem_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inExe = op.inTrigger("Trigger"); +const inStrAxiom = op.inString("Axiom / seed", "F"); +const inStrConstant1 = op.inString("Constant 1", "F"); +const inStrRule1 = op.inString("Rule 1", "Fx5y2.2F[yxFF]fF[yXFFfzFZF]fF[yXzFFfzFZF]"); +const inStrConstant2 = op.inString("Constant 2", "x"); +const inStrRule2 = op.inString("Rule 2"); +const inStrConstant3 = op.inString("Constant 3", "y"); +const inStrRule3 = op.inString("Rule 3"); +const inStrConstant4 = op.inString("Constant 4", "z"); +const inStrRule4 = op.inString("Rule 4"); + +inStrAxiom.setUiAttribs({ "hidePort": true }); +inStrConstant1.setUiAttribs({ "hidePort": true }); +inStrRule1.setUiAttribs({ "hidePort": true }); +inStrConstant2.setUiAttribs({ "hidePort": true }); +inStrRule2.setUiAttribs({ "hidePort": true }); +inStrConstant3.setUiAttribs({ "hidePort": true }); +inStrRule3.setUiAttribs({ "hidePort": true }); +inStrConstant4.setUiAttribs({ "hidePort": true }); +inStrRule4.setUiAttribs({ "hidePort": true }); + +const inIterations = op.inValueInt("Iterations", 1); +const inStepLength = op.inValue("Step length", 1.0); +const inStepScale = op.inValue("Step scale multiplier", 1.0); +const inDefaultAngle = op.inValue("Default angle", 45.0); +const inRotateMutliplier = op.inValue("Rotation multiplier", 1.0); + +const outTrigger = op.outTrigger("Out trigger geometry"); +const lineTrigger = op.outTrigger("Line/point trigger"); +// const outArray = op.outArray("Matrix Array out"); +const outPoints = op.outArray("Points out"); +const outMax = op.outNumber("Max Size"); +const stringOut = op.outString("Final generated string"); + +const seed = op.inValue("random seed"); +const inRandStr = op.inValue("random strength"); + +let angleMultiplier = inRotateMutliplier.get(); +let len = inStepLength.get(); +let lenScale = inStepScale.get(); + +const cgl = op.patch.cgl; + +// create a matrix +let trans = mat4.create(); +// array to put trans into +const transforms = []; +// pop in and out matrix for branch transforms +const stack = []; + +// used for splines array +const pointArrays = []; +let currentPointArray = []; +const lastPointArray = [0, 0, 0]; + +// this becomes the axiom after the resetAll() is called +let sentence = "";// axiom; + +// an array that holds the string replacment rules +let rules = []; + +let needsCalc = true; +// UI input +inRotateMutliplier.onChange = calcLater; +inStepLength.onChange = calcLater; +inStepScale.onChange = calcLater; +inIterations.onChange = calcLater; +inExe.onTriggered = render; +seed.onChange = calcLater; +inRandStr.onChange = calcLater; +inDefaultAngle.onChange = calcLater; + +// if user changes rules or constants definesRules is updated +inStrAxiom.onChange = defineRules; +inStrConstant1.onChange = defineRules; +inStrRule1.onChange = defineRules; +inStrConstant2.onChange = defineRules; +inStrRule2.onChange = defineRules; +inStrConstant3.onChange = defineRules; +inStrRule3.onChange = defineRules; +inStrConstant4.onChange = defineRules; +inStrRule4.onChange = defineRules; + +op.init = function () +{ + defineRules(); +}; +// creates the constants and the rule sets +function defineRules() +{ + rules = + [ + inStrConstant1.get(), + inStrRule1.get(), + inStrConstant2.get(), + inStrRule2.get(), + inStrConstant3.get(), + inStrRule3.get(), + inStrConstant4.get(), + inStrRule4.get() + ]; + generate(); +} + +// reset everything +function resetAll() +{ + // define axiom here + sentence = inStrAxiom.get(); + angleMultiplier = inRotateMutliplier.get(); + len = inStepLength.get(); + lenScale = inStepScale.get(); +} + +// generates the string from the ruleset array +function generate() +{ + // reset the state of everything + resetAll(); + + let nextSentence = ""; + const iterationsLimit = Math.min(6, inIterations.get()); + let iter; + let i; + let r; + + for (iter = 0; iter < iterationsLimit; iter++) + { + const sentenceArrayLength = sentence.length; + const rulesArrayLength = rules.length;// + + for (i = 0; i < sentenceArrayLength; i++) + { + const current = sentence.charAt(i); + let found = false; + + for (r = 0; r < rulesArrayLength; r += 2) + { + if (current === rules[r]) + { + found = true; + nextSentence += rules[r + 1]; + } + } + // if nothing is found continue no matter what + if (!found)nextSentence += current; + } + // final result + sentence = nextSentence; + nextSentence = ""; + // removing this will add everything on top of each other recursively + // nextSentence=""; + } + stringOut.set(sentence); + // draw everything once with the turtle function + turtle(); + needsCalc = false; +} + +// used to connect start and end of branches together correctly +const pos = vec3.create(); +const empty = vec3.create(); + +// extracts the user defined angle +// FfFx45FF returns 45 on the x axis +function extract(str, pos) +{ + const slicedSentence = str.slice(pos); + + let output = ""; + let parsedNumber = ""; + let currentChar = ""; + let canceled = true; + // starts i at 1 to drop character which is actually the identifying key! + for (let i = 1; i < slicedSentence.length; i++) + { + // output = slicedSentence.slice(i); + output = slicedSentence.substr(i, 8); + for (let j = 0; j < output.length; j++) + { + currentChar = output.charAt(j); + if (!Number.isNaN(currentChar)) + { + parsedNumber += currentChar; + } + else if (Number.isNaN(currentChar)) + { + canceled = true; + break; + } + } + if (canceled) break; + } + return parseFloat(parsedNumber); +} + +// recreates the turtle algorithm +function turtle() +{ + // used to find max distance from starting point + let max = 0; + // create the main transform array + trans = mat4.create(); + transforms.length = 0; + pointArrays.length = 0; + stack.length = 0; + const branchCoordStack = []; + + currentPointArray = []; + let angleUi = ""; + + let currentBranch = 0; + for (let i = 0; i < sentence.length; i++) + { + const current = sentence.charAt(i); + // step forward, create point + if (current == "F") + { + // apply to the trans matrix, take a step + mat4.translate(trans, trans, [0.0, len, 0.0]); + // push matrix for geometry + transforms.push(mat4.clone(trans)); + // get xyz for point array + vec3.transformMat4(pos, empty, trans); + // spline + currentPointArray.push(pos[0], pos[1], pos[2]); + + max = Math.max(max, Math.abs(pos[0])); + max = Math.max(max, Math.abs(pos[1])); + max = Math.max(max, Math.abs(pos[2])); + } + // step forward do not add a point + else if (current == "f") + { + mat4.translate(trans, trans, [0.0, len, 0.0]); + } + // alter step length by step multiplier + else if (current == ">") + { + len *= lenScale; + } + else if (current == "<") + { + len /= lenScale; + } + // turn counter clockwise x defined by angle + else if (current == "x") + { + if (isNaN(sentence.charAt(i + 1))) + angleUi = -inDefaultAngle.get() * angleMultiplier; + else + angleUi = -extract(sentence, i) * angleMultiplier; + mat4.rotateX(trans, trans, CGL.DEG2RAD * (angleUi - Math.seededRandom() * inRandStr.get())); + } + // turn clockwise x defined by angleUi + else if (current == "X") + { + if (isNaN(sentence.charAt(i + 1))) + angleUi = inDefaultAngle.get() * angleMultiplier; + else + angleUi = extract(sentence, i) * angleMultiplier; + mat4.rotateX(trans, trans, CGL.DEG2RAD * (angleUi + Math.seededRandom() * inRandStr.get())); + } + // turn counter clockwise y defined by angleUi + else if (current == "y") + { + if (isNaN(sentence.charAt(i + 1))) + angleUi = -inDefaultAngle.get() * angleMultiplier; + else + angleUi = -extract(sentence, i) * angleMultiplier; + mat4.rotateY(trans, trans, CGL.DEG2RAD * (angleUi - Math.seededRandom() * inRandStr.get())); + } + // turn clockwise y defined by angleUi + else if (current == "Y") + { + if (isNaN(sentence.charAt(i + 1))) + angleUi = inDefaultAngle.get() * angleMultiplier; + else + angleUi = extract(sentence, i) * angleMultiplier; + mat4.rotateY(trans, trans, CGL.DEG2RAD * (angleUi + Math.seededRandom() * inRandStr.get())); + } + // turn counter clockwise z defined by angleUi + else if (current == "z") + { + if (isNaN(sentence.charAt(i + 1))) + angleUi = -inDefaultAngle.get() * angleMultiplier; + else + angleUi = -extract(sentence, i) * angleMultiplier; + mat4.rotateZ(trans, trans, CGL.DEG2RAD * (angleUi - Math.seededRandom() * inRandStr.get())); + } + // turn clockwise z defined by angleUi + else if (current == "Z") + { + if (isNaN(sentence.charAt(i + 1))) + angleUi = inDefaultAngle.get() * angleMultiplier; + else + angleUi = extract(sentence, i) * angleMultiplier; + mat4.rotateZ(trans, trans, CGL.DEG2RAD * (angleUi + Math.seededRandom() * inRandStr.get())); + } + else if (current == "[") + { + // get current transform matrix push into stack, branch start + stack.push(mat4.clone(trans)); + + vec3.transformMat4(pos, empty, trans); + + branchCoordStack.push([pos[0], pos[1], pos[2]]); + + // output current branch number + currentBranch += 1; + } + else if (current == "]") + { + // get the current branch push into the transform matrix + // check if branch has a start to avoid error + if (stack.length === 0) + { + break; + } + + trans = stack[stack.length - 1]; + stack.pop(); + + // this code section is used to correctly connect the branches together with spline + const branchStartCoord = branchCoordStack[branchCoordStack.length - 1]; + + branchCoordStack.pop(); + + pointArrays.push(currentPointArray); + + if (branchStartCoord) currentPointArray = [branchStartCoord[0], branchStartCoord[1], branchStartCoord[2]]; + else currentPointArray = []; + } + } + needsCalc = false; + pointArrays.push(currentPointArray); + // outArray.set(transforms); + outMax.set(max); +} + +function calcLater() +{ + needsCalc = true; +} + +function render() +{ + if (needsCalc) + { + generate(); + } + needsCalc = false; + + // iterate through transforms array and trigger all geometry + for (var i = 0; i < transforms.length; i++) + { + cgl.pushModelMatrix(); + mat4.multiply(cgl.mMatrix, cgl.mMatrix, transforms[i]); + outTrigger.trigger(); + cgl.popModelMatrix(); + } + // iterate through points array for spline xyz data + for (var i = 0; i < pointArrays.length; i++) + { + outPoints.set(pointArrays[i]); + lineTrigger.trigger(); + } +} + + +}; + +Ops.Systems.Lsystem_v2.prototype = new CABLES.Op(); +CABLES.OPS["ef4c436e-3de5-4a97-a392-10a52a5b3dc6"]={f:Ops.Systems.Lsystem_v2,objName:"Ops.Systems.Lsystem_v2"}; + + + + +// ************************************************************** +// +// Ops.Templates.ExampleVizOp +// +// ************************************************************** + +Ops.Templates.ExampleVizOp = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inNum = op.inFloat("Number", 23); + +op.setUiAttrib({ "height": 200, "width": 200, "resizable": true }); + + +op.renderVizLayer = (ctx, layer) => +{ + ctx.fillStyle = "#222"; + ctx.fillRect( + layer.x, layer.y, + layer.width, layer.height); + + const padding = 10; + + ctx.fillStyle = "#05f"; + + let circle = new Path2D(); + circle.arc(layer.x + layer.height / 2, layer.y + layer.height / 2, layer.height/2, 0, 2 * Math.PI, false); + ctx.fill(circle); + + + // draw text, use layer height for fontsize, so it is fixed when resizing the op or zooming the patch + ctx.fillStyle = "#fff"; + const fontSize = layer.height * 0.7; + ctx.font = "normal " + fontSize + "px sourceCodePro"; + ctx.fillText(Math.round(inNum.get() * 10000) / 10000, layer.x + padding, layer.y + fontSize); + + + // this text will not scale and be at fixed size when zooming in/out + ctx.font = "normal " + 12 + "px sourceCodePro"; + ctx.fillText(Math.round(inNum.get() * 10000) / 10000, layer.x + padding, layer.y+20); + + +}; + + +}; + +Ops.Templates.ExampleVizOp.prototype = new CABLES.Op(); +CABLES.OPS["08f3a509-1cad-4dfc-961c-2bd925e066dd"]={f:Ops.Templates.ExampleVizOp,objName:"Ops.Templates.ExampleVizOp"}; + + + + +// ************************************************************** +// +// Ops.Templates.MinimalMaterial +// +// ************************************************************** + +Ops.Templates.MinimalMaterial = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"shader_frag":"{{MODULE_BEGIN_FRAG}}\n\nvoid main()\n{\n vec4 col=vec4(red,0.2,0.2,1.0);\n {{MODULE_COLOR}}\n outColor= col;\n}\n","shader_vert":"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\n\nvoid main()\n{\n vec4 pos=vec4(vPosition, 1.0);\n {{MODULE_VERTEX_POSITION}}\n gl_Position = projMatrix * mvMatrix * pos;\n}\n",}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + inRed = op.inValueSlider("Red"); + +const cgl = op.patch.cgl; + +function doRender() +{ + cgl.pushShader(shader); + trigger.trigger(); + cgl.popShader(); +} + +const shader = new CGL.Shader(cgl, "MinimalMaterial"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); +shader.setSource(attachments.shader_vert, attachments.shader_frag); + +shader.addUniformFrag("f", "red", inRed); + +render.onTriggered = doRender; + + +}; + +Ops.Templates.MinimalMaterial.prototype = new CABLES.Op(); +CABLES.OPS["2df1b492-6a97-4064-b939-ff926d4414d0"]={f:Ops.Templates.MinimalMaterial,objName:"Ops.Templates.MinimalMaterial"}; + + + + +// ************************************************************** +// +// Ops.Templates.PortsArrayExample +// +// ************************************************************** + +Ops.Templates.PortsArrayExample = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// Create a input port of the type Array +const inArr = op.inArray("Array in"); + +// Create a output port of the type Array +const outArray = op.outArray("Array out"); + +// cache for errors +let showingError = false; + +// when array in changes call the function update +inArr.onChange = update; + +// TODO revalute error checking code !! +function update() +{ + // create an array called 'tempArray' and assign + // the array coming in to it + let tempArray = inArr.get(); + + // error checking section + // check if arrays come in correctly on startup + // if no array comes in just return to avoid errors + if (!inArr) + { + return; + } + // set outArray to tempArray + outArray.set(tempArray); +} + + +}; + +Ops.Templates.PortsArrayExample.prototype = new CABLES.Op(); +CABLES.OPS["c31a7377-0eca-476e-8146-c2d9b8d3b908"]={f:Ops.Templates.PortsArrayExample,objName:"Ops.Templates.PortsArrayExample"}; + + + + +// ************************************************************** +// +// Ops.Templates.PortsBooleanExample +// +// ************************************************************** + +Ops.Templates.PortsBooleanExample = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// Create a input port of the type Boolean +const inBoolean = op.inBool("Boolean in", false); + +// Create a output port of the type value +const outBoolean = op.outBoolNum("Boolean out"); + +// when input port changes call the function 'update' +inBoolean.onChange = update; + +// this function runs every time the input port changes +function update() +{ + // set the ouput port to the value of the input port + outBoolean.set(inBoolean.get()); +} + + +}; + +Ops.Templates.PortsBooleanExample.prototype = new CABLES.Op(); +CABLES.OPS["a21cd004-5cdf-4da2-8866-d1372497c3ff"]={f:Ops.Templates.PortsBooleanExample,objName:"Ops.Templates.PortsBooleanExample"}; + + + + +// ************************************************************** +// +// Ops.Templates.PortsObjectExample +// +// ************************************************************** + +Ops.Templates.PortsObjectExample = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +//Create a input port of the type value +const inObj = op.inObject("Object in"); + +//Create a output port of the type value +const outKeyVal=op.outObject("Object Out"); + +//when input port changes call the function 'update' +inObj.onChange = update; + +//this function runs every time the input port changes +function update() +{ + outKeyVal.set(inObj.get()); +}; + + + +}; + +Ops.Templates.PortsObjectExample.prototype = new CABLES.Op(); +CABLES.OPS["d7f414bf-804f-4c1a-987f-dfdbb3d80e27"]={f:Ops.Templates.PortsObjectExample,objName:"Ops.Templates.PortsObjectExample"}; + + + + +// ************************************************************** +// +// Ops.Templates.PortsStringExample +// +// ************************************************************** + +Ops.Templates.PortsStringExample = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +//Create a input port of the type String +const inString = op.inString("String in", "default string"); +//Create a output port of the type String +const outString = op.outString("String out"); + +//when input port changes call the function 'update' +inString.onChange = update; + +//this function runs every time the input port changes +function update() +{ + //set the output to the input string + outString.set(inString.get()); +} + +}; + +Ops.Templates.PortsStringExample.prototype = new CABLES.Op(); +CABLES.OPS["3d387694-022f-431f-b25e-636c6d666518"]={f:Ops.Templates.PortsStringExample,objName:"Ops.Templates.PortsStringExample"}; + + + + +// ************************************************************** +// +// Ops.Templates.PortsTriggerExample +// +// ************************************************************** + +Ops.Templates.PortsTriggerExample = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +//Create a input port of the type Trigger +const inExecute = op.inTrigger("Trigger In"); +//create a button in UI panel of the op which can be clicked +const inButton = op.inTriggerButton("Press me"); + +//Create a output port of the type Trigger +const outTrigger = op.outTrigger("Trigger out"); + +//when input port is triggered call the function 'update' +//Or +//if user presses the button in the op pane call function 'update' +inExecute.onTriggered = inButton.onTriggered= update; + +//this function runs every time the input port is triggered +function update() +{ + //send a trigger out of the output port + outTrigger.trigger(); +} + +}; + +Ops.Templates.PortsTriggerExample.prototype = new CABLES.Op(); +CABLES.OPS["10728b97-ea46-4491-bb66-e6ad01289001"]={f:Ops.Templates.PortsTriggerExample,objName:"Ops.Templates.PortsTriggerExample"}; + + + + +// ************************************************************** +// +// Ops.Templates.PortsValueExample +// +// ************************************************************** + +Ops.Templates.PortsValueExample = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +//Create a input port of the type value +const inVal = op.inFloat("Number in ",0); + +//Create a output port of the type value +const outResult = op.outNumber("Value out"); + +//when input port changes call the function 'update' +inVal.onChange = update; + +//this function runs every time the input port changes +function update() +{ + //set the ouput port to the value of the input port + outResult.set(inVal.get()); +} + +}; + +Ops.Templates.PortsValueExample.prototype = new CABLES.Op(); +CABLES.OPS["362a1c2f-c4e8-41eb-944b-d16c73a8d27b"]={f:Ops.Templates.PortsValueExample,objName:"Ops.Templates.PortsValueExample"}; + + + + +// ************************************************************** +// +// Ops.Templates.ShaderEffectExample +// +// ************************************************************** + +Ops.Templates.ShaderEffectExample = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"shaderEffectExample_body_frag":"\nfloat MOD_v=mod(MOD_posz,MOD_width)/MOD_width;\ncol*=vec4(MOD_v,MOD_v,MOD_v,1.0);","shaderEffectExample_body_vert":"MOD_posz=(viewMatrix*mMatrix*pos).z;\n","shaderEffectExample_head_frag":"\nIN float MOD_posz;\nUNI float MOD_width;","shaderEffectExample_head_vert":"OUT float MOD_posz;",}; +const render = op.inTrigger("Render"); +const inWidth = op.inValue("Width", 0.5); +const next = op.outTrigger("Next"); + +const cgl = op.patch.cgl; + +let shader = null; +let moduleFrag = null; +let moduleVert = null; + +render.onLinkChanged = removeModule; + +function removeModule() +{ + if (shader && moduleFrag) shader.removeModule(moduleFrag); + if (shader && moduleVert) shader.removeModule(moduleVert); + shader = null; +} + +render.onTriggered = function () +{ + if (!cgl.getShader()) return; + + if (cgl.getShader() != shader) + { + if (shader) removeModule(); + shader = cgl.getShader(); + + moduleVert = shader.addModule( + { + "title": op.objName, + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": attachments.shaderEffectExample_head_vert || "", + "srcBodyVert": attachments.shaderEffectExample_body_vert || "" + }); + + moduleFrag = shader.addModule( + { + "title": op.objName, + "name": "MODULE_COLOR", + "srcHeadFrag": attachments.shaderEffectExample_head_frag || "", + "srcBodyFrag": attachments.shaderEffectExample_body_frag || "" + }, moduleVert); + + inWidth.uniform = new CGL.Uniform(shader, "f", moduleFrag.prefix + "width", inWidth); + } + + if (!shader) return; + + next.trigger(); +}; + + +}; + +Ops.Templates.ShaderEffectExample.prototype = new CABLES.Op(); +CABLES.OPS["a0df10ce-7ae5-49a4-83df-6afc1f5a3f3a"]={f:Ops.Templates.ShaderEffectExample,objName:"Ops.Templates.ShaderEffectExample"}; + + + + +// ************************************************************** +// +// Ops.Templates.UiTestOp +// +// ************************************************************** + +Ops.Templates.UiTestOp = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inLoadingTask = op.inBool("Loading Task", false), + inWarning1 = op.inBool("Warning", false), + inUiError = op.inBool("Error", false), + inUiHint = op.inBool("Hint", false), + innum = op.inFloatSlider("Slider", 0), + inResize = op.inBool("Resizable"), + trig = op.inTrigger("trigger"), + + inSwit = op.inBool("greyout"), + ingreyoutLine = op.inFloatSlider("this will greyout"), + + inLog = op.inTriggerButton("op.log()"), + inLogWarn = op.inTriggerButton("op.logWarn()"), + inLogErr = op.inTriggerButton("op.logError()"), + + inPrompt = op.inTriggerButton("Open Prompt"), + inModal = op.inTriggerButton("Open Modal"), + inTab = op.inTriggerButton("Open new Tab"), + inExc = op.inTriggerButton("Throw Exception"), + outBlah = op.outNumber("Something"); + +op.setPortGroup("Greyout", [inSwit, ingreyoutLine]); +op.setPortGroup("Warnings", [inWarning1, inUiHint, inUiError]); +op.setPortGroup("Logging", [inLog, inLogWarn, inLogErr]); +op.setPortGroup("Modal", [inPrompt, inModal]); + +let loadingId = null; + +op.onDelete = () => +{ + if (loadingId) op.patch.loading.finished(loadingId); +}; + +inSwit.onChange = () => +{ + ingreyoutLine.setUiAttribs({ "greyout": inSwit.get() }); +}; + +inLoadingTask.onChange = () => +{ + if (inLoadingTask.get()) loadingId = op.patch.loading.start("test ui op file", "test.txt"); + else + { + op.patch.loading.finished(loadingId); + loadingId = null; + } +}; + +inWarning1.onChange = () => +{ + if (inWarning1.get()) op.setUiError("Warn1", "Warning one", 1); + else op.setUiError("Warn1", null); +}; + +inUiHint.onChange = () => +{ + if (inUiHint.get()) op.setUiError("Hint1", "This is a hint!", 0); + else op.setUiError("Hint1", null); +}; + +inUiError.onChange = () => +{ + if (inUiError.get()) op.setUiError("Warn2", "Warning two", 2); + else op.setUiError("Warn2", null); +}; + +innum.onChange = () => +{ + const q = innum.get(); + + if (q < 0.3) op.setUiError("qRange", "number to small", 1); + else if (q > 0.6) op.setUiError("qRange", "number to big", 1); + else + { + op.log("no error!"); + op.setUiError("qRange", null); + } + op.log(q); +}; + +inLog.onTriggered = () => +{ + // if you dont see this open the logging filter + // by pressing cmd/ctrl+p "logging" + // and deactivate the filter for this op name. + op.log("this is verbose logging!"); +}; + +inLogWarn.onTriggered = () => +{ + op.logWarn("this is a warning!"); +}; + +inLogErr.onTriggered = () => +{ + op.logError("this is an error!"); +}; + +inPrompt.onTriggered = () => +{ + new CABLES.UI.ModalDialog({ + "prompt": true, + "title": "How Much", + "text": "please enter something", + "promptValue": "default", + "promptOk": (v) => + { + console.log("yes! prompt finished", v); + } + }); +}; + +inResize.onChange = () => +{ + op.setUiAttrib({ "resizable": inResize.get() }); +}; + +inModal.onTriggered = () => +{ + new CABLES.UI.ModalDialog({ + "title": "Title", + "text": "Dialog content can be html" + }); +}; + +inExc.onTriggered = () => +{ + throw new Error("crash0r"); +}; + +inTab.onTriggered = () => +{ + const tab = new CABLES.UI.Tab("Example", + { + "icon": "cube", + "padding": true, + "singleton": false + }); + + gui.mainTabs.addTab(tab, true); + tab.addEventListener("onClose", () => { console.log("tab was closed!"); }); + tab.html("this is the tab content. "); + gui.maintabPanel.show(true); +}; + + +}; + +Ops.Templates.UiTestOp.prototype = new CABLES.Op(); +CABLES.OPS["14fb5b5b-2a4e-419d-a15b-f20d41cf663b"]={f:Ops.Templates.UiTestOp,objName:"Ops.Templates.UiTestOp"}; + + + + +// ************************************************************** +// +// Ops.Time.DelayedTrigger +// +// ************************************************************** + +Ops.Time.DelayedTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + delay = op.inValueFloat("delay", 1), + cancel = op.inTriggerButton("Cancel"), + next = op.outTrigger("next"), + outDelaying = op.outBool("Delaying"); + +let lastTimeout = null; + +cancel.onTriggered = function () +{ + if (lastTimeout)clearTimeout(lastTimeout); + lastTimeout = null; +}; + +exe.onTriggered = function () +{ + outDelaying.set(true); + if (lastTimeout)clearTimeout(lastTimeout); + + lastTimeout = setTimeout( + function () + { + outDelaying.set(false); + lastTimeout = null; + next.trigger(); + }, + delay.get() * 1000); +}; + + +}; + +Ops.Time.DelayedTrigger.prototype = new CABLES.Op(); +CABLES.OPS["f4ff66b0-8500-46f7-9117-832aea0c2750"]={f:Ops.Time.DelayedTrigger,objName:"Ops.Time.DelayedTrigger"}; + + + + +// ************************************************************** +// +// Ops.Time.Milliseconds +// +// ************************************************************** + +Ops.Time.Milliseconds = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + + +const + inUpdate=op.inTrigger("update"), + res=op.outNumber("Result"); + +inUpdate.onTriggered=function() +{ + res.set(performance.now()); +}; + +}; + +Ops.Time.Milliseconds.prototype = new CABLES.Op(); +CABLES.OPS["2f447fcb-3c20-4c2f-9527-84daed3a6b0e"]={f:Ops.Time.Milliseconds,objName:"Ops.Time.Milliseconds"}; + + + + +// ************************************************************** +// +// Ops.Time.TimeSinceTrigger +// +// ************************************************************** + +Ops.Time.TimeSinceTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + trigger = op.inTriggerButton("trigger"), + reset = op.inTriggerButton("reset"), + next = op.outTrigger("next"), + time = op.outNumber("time"); + +let lastTrigger = op.patch.freeTimer.get(); +time.set(0); + +exe.onTriggered = function () +{ + time.set(op.patch.freeTimer.get() - lastTrigger); + next.trigger(); +}; + +reset.onTriggered = function () +{ + time.set(0); +}; + +trigger.onTriggered = function () +{ + lastTrigger = op.patch.freeTimer.get(); + time.set(0); +}; + + +}; + +Ops.Time.TimeSinceTrigger.prototype = new CABLES.Op(); +CABLES.OPS["df54103f-5ef6-4a4f-adcf-dddc005132f8"]={f:Ops.Time.TimeSinceTrigger,objName:"Ops.Time.TimeSinceTrigger"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.AutoPlay +// +// ************************************************************** + +Ops.TimeLine.AutoPlay = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +op.patch.timer.play(); + +}; + +Ops.TimeLine.AutoPlay.prototype = new CABLES.Op(); +CABLES.OPS["6069e8fe-6cf5-4be6-be38-4d46056ec9f7"]={f:Ops.TimeLine.AutoPlay,objName:"Ops.TimeLine.AutoPlay"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.DemoPrerender +// +// ************************************************************** + +Ops.TimeLine.DemoPrerender = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Render"), + inEvents = op.inArray("Times"), + inRecord = op.inBool("Record Events", false), + inReset = op.inTriggerButton("Reset"), + inClear = op.inTriggerButton("Clear"), + inReResize = op.inBool("ReRender on Resize", true), + next = op.outTrigger("Next"), + nextPrerendered = op.outTrigger("Prerendered Frame"), + outProgress = op.outNumber("Progress", 0), + numEvents = op.outNumber("Num Events"); + +exec.onTriggered = render; +inEvents.setUiAttribs({ "hidePort": true }); + +let isPrerendering = true; + +let prerenderCount = 0; +let delaystart = false; +let restartTime = 0; +let restartTimeFreeTimer = 0; +let events = [0]; + +op.patch.cgl.on("resize", () => +{ + if (inReResize.get()) + { + if (!isPrerendering && outProgress.get() == 1) + { + restartTime = op.patch.timer.getTime(); + restartTimeFreeTimer = op.patch.freeTimer.getTime(); + + isPrerendering = true; + prerenderCount = 0; + } + } +}); + +inEvents.onChange = () => +{ + numEvents.set(events.length); + events = inEvents.get() || [0]; +}; + +op.patch.cgl.on("heavyEvent", (e) => +{ + if (inRecord.get() && !isPrerendering && CABLES.uniqueArray) + { + events.push(Math.round(op.patch.timer.getTime() * 60) / 60); + events = CABLES.uniqueArray(events); + inEvents.set(events); + } +}); + +let curTime = 0; + +inReset.onTriggered = () => +{ + isPrerendering = true; +}; + +inClear.onTriggered = () => +{ + events = [0]; + inEvents.set(events); +}; + +function fakeNow() +{ + return curTime * 1000; +} + +function render() +{ + if (inRecord.get()) + { + isPrerendering = false; + outProgress.set(1); + next.trigger(); + + return; + } + if (isPrerendering) + { + // for(let i=0;i= events.length) + { + const t = (numExtraFrames - (prerenderCount - events.length)) / numExtraFrames; + + // op.log("empty prerender...", t); + op.patch.timer.setTime(t); + op.patch.freeTimer.setTime(t); + } + + next.trigger(); + next.trigger(); + // next.trigger(); + // next.trigger(); + outProgress.set(Math.min(1, prerenderCount / (events.length + numExtraFrames))); + // op.log("progress...", outProgress.get()); + + nextPrerendered.trigger(); + + if (prerenderCount >= events.length + numExtraFrames) + { + op.patch.timer.setTime(restartTime); + op.patch.freeTimer.setTime(restartTimeFreeTimer); + + // setTimeout(() => + // { + // delaystart = false; + isPrerendering = false; + // op.patch.timer.setTime(0); + // op.patch.freeTimer.setTime(0); + // }, 500); + } + else + prerenderCount++; + } + else + { + next.trigger(); + } +} + + +}; + +Ops.TimeLine.DemoPrerender.prototype = new CABLES.Op(); +CABLES.OPS["962ea9e5-39ee-448c-9a2b-b71ceee47c11"]={f:Ops.TimeLine.DemoPrerender,objName:"Ops.TimeLine.DemoPrerender"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.GotoFrame +// +// ************************************************************** + +Ops.TimeLine.GotoFrame = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +var inFrame=op.inValueInt("Frame"); + +inFrame.onChange=function() +{ + op.patch.timer.setTime(inFrame.get()/30.0); + + if(CABLES.UI) gui.timeLine().updateTime(); +}; + + +}; + +Ops.TimeLine.GotoFrame.prototype = new CABLES.Op(); +CABLES.OPS["5ff97165-b16c-46cc-8ce5-119fddccf1a3"]={f:Ops.TimeLine.GotoFrame,objName:"Ops.TimeLine.GotoFrame"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.PreRender +// +// ************************************************************** + +Ops.TimeLine.PreRender = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Render"), + next = op.outTrigger("Next"), + progress = op.outTrigger("Render Progress"), + done = op.outTrigger("Done"), + inMaxTime = op.inInt("Max Time", 1), + + inStep = op.inInt("Step", 30), + inReset = op.inTriggerButton("Reset"), + outProgress = op.outNumber("Progress", 0); + +exec.onTriggered = render; + +let start = true; +let curTime = -1; +let wait = true; + +inReset.onTriggered = function () +{ + if (curTime < 0) + start = true; +}; + +function render() +{ + if (!start && curTime < 0) + { + if (!wait) + next.trigger(); + wait = false; + return; + } + + const maxTime = inMaxTime.get(); + if (start) + { + curTime = maxTime; + start = false; + wait = true; + } + + const oldInternalNow = CABLES.internalNow; + CABLES.internalNow = function () + { + return curTime * 1000; + }; + + CABLES.overwriteTime = curTime; + op.patch.timer.setTime(curTime); + op.patch.freeTimer.setTime(curTime); + + outProgress.set((maxTime - curTime) / maxTime); + + next.trigger(); + next.trigger(); + next.trigger(); + + CABLES.overwriteTime = undefined; + CABLES.internalNow = oldInternalNow; + + progress.trigger(); + + if (curTime === 0) + { + done.trigger(); + curTime = -1; + } + else + { + curTime -= inStep.get(); + if (curTime < 0) + curTime = 0; + } +} + + +}; + +Ops.TimeLine.PreRender.prototype = new CABLES.Op(); +CABLES.OPS["294efbca-f2b6-46fa-8b28-0530d6b9f9c8"]={f:Ops.TimeLine.PreRender,objName:"Ops.TimeLine.PreRender"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLineControls +// +// ************************************************************** + +Ops.TimeLine.TimeLineControls = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const plauPause = op.outBoolNum("Play/Stop"); +const time = op.outNumber("time"); + +op.patch.timer.on("playPause", seek); +op.patch.timer.on("timeChange", seek); + +function seek() +{ + plauPause.set(false); + + setTimeout(function () + { + time.set(op.patch.timer.getTime()); + plauPause.set(op.patch.timer.isPlaying()); + }, 10); +} + + +}; + +Ops.TimeLine.TimeLineControls.prototype = new CABLES.Op(); +CABLES.OPS["53cb7b1a-56c7-405f-b427-12db78fbfd2f"]={f:Ops.TimeLine.TimeLineControls,objName:"Ops.TimeLine.TimeLineControls"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLineFrame +// +// ************************************************************** + +Ops.TimeLine.TimeLineFrame = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +var theTime=op.addOutPort(new CABLES.Port(this,"time")); + +op.onAnimFrame=function(time) +{ + theTime.set(Math.round(time*30.0)); +}; + +}; + +Ops.TimeLine.TimeLineFrame.prototype = new CABLES.Op(); +CABLES.OPS["a6c66f1f-2102-4b68-882b-afd25a4da538"]={f:Ops.TimeLine.TimeLineFrame,objName:"Ops.TimeLine.TimeLineFrame"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLineLength +// +// ************************************************************** + +Ops.TimeLine.TimeLineLength = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// uses its input port for caching the length of the timeline... + +const + inLength = op.inFloat("len", 0), + outLength = op.outNumber("Length"); + +inLength.setUiAttribs({ "hidePort": true, "hideParam": true }); + +if (CABLES.UI) +{ + inLength.set(gui.timeLine().getTimeLineLength()); + + gui.on("timelineControl", (cmd, l) => + { + if (cmd === "setLength") + { + inLength.set(l); + outLength.set(inLength.get()); + } + }); + outLength.set(inLength.get()); +} + +op.on("loadedValueSet", () => +{ + outLength.set(inLength.get()); +}); + + +}; + +Ops.TimeLine.TimeLineLength.prototype = new CABLES.Op(); +CABLES.OPS["c8aee7f4-ad53-406d-b7fe-527bf271c088"]={f:Ops.TimeLine.TimeLineLength,objName:"Ops.TimeLine.TimeLineLength"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLineLoop +// +// ************************************************************** + +Ops.TimeLine.TimeLineLoop = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + execute = op.inTrigger("Execute"), + duration = op.inValueFloat("duration", 2), + trigger = op.outTrigger("trigger"); + +execute.onTriggered = function () +{ + if (op.patch.timer.getTime() > duration.get()) + op.patch.timer.setTime(0); + + trigger.trigger(); +}; + + +}; + +Ops.TimeLine.TimeLineLoop.prototype = new CABLES.Op(); +CABLES.OPS["e450a850-7311-48b5-abfc-07ec1fea3b8e"]={f:Ops.TimeLine.TimeLineLoop,objName:"Ops.TimeLine.TimeLineLoop"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLineOverwrite +// +// ************************************************************** + +Ops.TimeLine.TimeLineOverwrite = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exec = op.inTrigger("exe"); +const newTime = op.inValueFloat("new time"); +const next = op.outTrigger("trigger"); + +let realTime = 0; +exec.onTriggered = function () +{ + realTime = op.patch.timer.getTime(); + + op.patch.timer.overwriteTime = newTime.get(); + next.trigger(); + op.patch.timer.overwriteTime = -1; +}; + + +}; + +Ops.TimeLine.TimeLineOverwrite.prototype = new CABLES.Op(); +CABLES.OPS["f0492ba2-5009-4a9a-bfd9-8a03633df31c"]={f:Ops.TimeLine.TimeLineOverwrite,objName:"Ops.TimeLine.TimeLineOverwrite"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLinePlay +// +// ************************************************************** + +Ops.TimeLine.TimeLinePlay = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + play = op.inTriggerButton("Play"), + pause = op.inTriggerButton("Pause"), + next = op.outTrigger("Next"); + +play.onTriggered = function () +{ + op.patch.timer.play(); + next.trigger(); +}; + +pause.onTriggered = function () +{ + op.patch.timer.pause(); + next.trigger(); +}; + + +}; + +Ops.TimeLine.TimeLinePlay.prototype = new CABLES.Op(); +CABLES.OPS["fc75b841-a55f-4474-8746-61218588598d"]={f:Ops.TimeLine.TimeLinePlay,objName:"Ops.TimeLine.TimeLinePlay"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLinePlayer +// +// ************************************************************** + +Ops.TimeLine.TimeLinePlayer = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + play = op.inTriggerButton("Play"), + pause = op.inTriggerButton("Pause"), + rewind = op.inTriggerButton("rewind"), + setTime = op.inFloat("Set current time", 0), + + outPlayTrigger = op.outTrigger("play trigger"), + outPauseTrigger = op.outTrigger("pause trigger"), + outrewindTrigger = op.outTrigger("rewind trigger"), + isPlaying = op.outBool("is Playing"), + outSetTimeTrigger = op.outNumber("set time (seconds)"), + currentTime = op.outNumber("current time"), + currentFrame = op.outNumber("current frame"); + +play.onTriggered = function () +{ + op.patch.timer.play(); + + op.patch.timer.setTime(setTime.get()); + outSetTimeTrigger.set(setTime.get()); + outPlayTrigger.trigger(); +}; + +pause.onTriggered = function () +{ + op.patch.timer.pause(); + outPauseTrigger.trigger(); +}; + +op.onAnimFrame = function (time) +{ + currentFrame.set(Math.round(time * 30.0)); + currentTime.set(time); + isPlaying.set(op.patch.timer.isPlaying()); +}; + +rewind.onTriggered = function () +{ + op.patch.timer.setTime(0); + outrewindTrigger.trigger(); +}; + + +}; + +Ops.TimeLine.TimeLinePlayer.prototype = new CABLES.Op(); +CABLES.OPS["97e57613-6a51-41cf-9de5-fe3dbc2c69b2"]={f:Ops.TimeLine.TimeLinePlayer,objName:"Ops.TimeLine.TimeLinePlayer"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLineRewind +// +// ************************************************************** + +Ops.TimeLine.TimeLineRewind = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTriggerButton("exe"), + next = op.outTrigger("Next"); + +exe.onTriggered = function () +{ + op.patch.timer.setTime(0); + next.trigger(); +}; + + +}; + +Ops.TimeLine.TimeLineRewind.prototype = new CABLES.Op(); +CABLES.OPS["d3408604-858c-4226-8d4c-1ef669956f35"]={f:Ops.TimeLine.TimeLineRewind,objName:"Ops.TimeLine.TimeLineRewind"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLineSetTime +// +// ************************************************************** + +Ops.TimeLine.TimeLineSetTime = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTriggerButton("Update"), + inTime = op.inFloat("Time", 0), + next = op.outTrigger("Next"); + +exe.onTriggered = function () +{ + op.patch.timer.setTime(parseFloat(inTime.get())); + next.trigger(); +}; + + +}; + +Ops.TimeLine.TimeLineSetTime.prototype = new CABLES.Op(); +CABLES.OPS["c65f3975-0901-4252-b040-f21f9b144d70"]={f:Ops.TimeLine.TimeLineSetTime,objName:"Ops.TimeLine.TimeLineSetTime"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLineTime +// +// ************************************************************** + +Ops.TimeLine.TimeLineTime = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const theTime = op.outNumber("time"); + +op.onAnimFrame = function (time) +{ + theTime.set(time); +}; + + +}; + +Ops.TimeLine.TimeLineTime.prototype = new CABLES.Op(); +CABLES.OPS["3ab26f26-a12a-4c48-9411-20591a5f569d"]={f:Ops.TimeLine.TimeLineTime,objName:"Ops.TimeLine.TimeLineTime"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimeLineTogglePlay +// +// ************************************************************** + +Ops.TimeLine.TimeLineTogglePlay = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const play=op.inBool("Play",false); + +play.onChange=function() +{ + if(play.get()) op.patch.timer.play(); + else op.patch.timer.pause(); +} + + +}; + +Ops.TimeLine.TimeLineTogglePlay.prototype = new CABLES.Op(); +CABLES.OPS["930c6f38-9271-4021-a58b-14dcfc5763b2"]={f:Ops.TimeLine.TimeLineTogglePlay,objName:"Ops.TimeLine.TimeLineTogglePlay"}; + + + + +// ************************************************************** +// +// Ops.TimeLine.TimelineValue +// +// ************************************************************** + +Ops.TimeLine.TimelineValue = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTime = op.inValue("Time"), + animVal = op.inValue("Value"), + timeUnit = op.inValueSelect("Unit", ["Seconds", "Frames"], "Seconds"), + outVal = op.outNumber("Result"), + outArr = op.outArray("Anim Array"), + outEnded = op.outBoolNum("Anim Finished"); + +inTime.onChange = update; +let hasError = false; + +animVal.setAnimated(true); +animVal.onChange = update; +animVal.anim.onChange = animChange; +op.onLoaded = animChange; + +let useFrames = false; + +timeUnit.onChange = function () +{ + useFrames = (timeUnit.get() == "Frames"); +}; + +function update() +{ + inTime.get(); + + if (animVal.isAnimated()) + { + let t = inTime.get(); + if (useFrames) t /= 30.0; + const v = animVal.anim.getValue(t); + outEnded.set(animVal.anim.hasEnded(t)); + outVal.set(v); + if (hasError) + { + op.setUiError("noanim", null); + hasError = false; + + animVal.anim.onChange = animChange; + } + } + else + { + op.setUiError("noanim", "Port \"animVal\" should be animated"); + hasError = true; + } +} + +function animChange() +{ + const arr = []; + if (animVal.anim.keys && animVal.anim.keys.length > 0) + { + arr.length = animVal.anim.keys.length * 2; + + for (let i = 0; i < animVal.anim.keys.length; i++) + { + arr[i * 2 + 0] = animVal.anim.keys[i].time; + arr[i * 2 + 1] = animVal.anim.keys[i].value; + } + } + + outArr.set(null); + outArr.set(arr); +} + + +}; + +Ops.TimeLine.TimelineValue.prototype = new CABLES.Op(); +CABLES.OPS["c5f260da-c8e3-4890-a37e-937d49b9da7d"]={f:Ops.TimeLine.TimelineValue,objName:"Ops.TimeLine.TimelineValue"}; + + + + +// ************************************************************** +// +// Ops.Trigger.GateTrigger +// +// ************************************************************** + +Ops.Trigger.GateTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger('Execute'), + passThrough = op.inValueBool('Pass Through',true), + triggerOut = op.outTrigger('Trigger out'); + +exe.onTriggered = function() +{ + if(passThrough.get()) + triggerOut.trigger(); +} + + +}; + +Ops.Trigger.GateTrigger.prototype = new CABLES.Op(); +CABLES.OPS["65e8b8a2-ba13-485f-883a-2bcf377989da"]={f:Ops.Trigger.GateTrigger,objName:"Ops.Trigger.GateTrigger"}; + + + + +// ************************************************************** +// +// Ops.Trigger.IfEqualsThen +// +// ************************************************************** + +Ops.Trigger.IfEqualsThen = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTrigger("exe"); +let value1 = op.inValue("Value 1", 0); +let value2 = op.inValue("Value 2", 0); + +let triggerThen = op.addOutPort(new CABLES.Port(op, "then", CABLES.OP_PORT_TYPE_FUNCTION)); +let triggerElse = op.addOutPort(new CABLES.Port(op, "else", CABLES.OP_PORT_TYPE_FUNCTION)); + +function exec() +{ + if (value1.get() == value2.get()) + { + triggerThen.trigger(); + } + else + { + triggerElse.trigger(); + } +} + +exe.onTriggered = exec; + + +}; + +Ops.Trigger.IfEqualsThen.prototype = new CABLES.Op(); +CABLES.OPS["e8196d70-d0a6-470a-9448-a7ac0c0e956e"]={f:Ops.Trigger.IfEqualsThen,objName:"Ops.Trigger.IfEqualsThen"}; + + + + +// ************************************************************** +// +// Ops.Trigger.Interval +// +// ************************************************************** + +Ops.Trigger.Interval = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + interval = op.inValue("interval"), + trigger = op.outTrigger("trigger"), + active = op.inValueBool("Active", true); + +active.onChange = function () +{ + if (!active.get()) + { + clearTimeout(timeOutId); + timeOutId = -1; + } + else exec(); +}; + +interval.set(1000); +let timeOutId = -1; + +function exec() +{ + if (!active.get()) return; + if (timeOutId != -1) return; + + timeOutId = setTimeout(function () + { + timeOutId = -1; + trigger.trigger(); + exec(); + }, + interval.get()); +} + +interval.onChange = exec; + +exec(); + + +}; + +Ops.Trigger.Interval.prototype = new CABLES.Op(); +CABLES.OPS["3e9bae10-38af-4e36-9fcc-35faeeaf57f8"]={f:Ops.Trigger.Interval,objName:"Ops.Trigger.Interval"}; + + + + +// ************************************************************** +// +// Ops.Trigger.IsTriggered +// +// ************************************************************** + +Ops.Trigger.IsTriggered = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Trigger"), + next = op.outTrigger("Next"), + result = op.outBool("Was Triggered", false); + +let frameCount = 0; + +op.onAnimFrame = function (tt) +{ + frameCount++; + if (frameCount > 1) result.set(false); +}; + +exec.onTriggered = function () +{ + frameCount = 0; + result.set(true); + next.trigger(); +}; + + +}; + +Ops.Trigger.IsTriggered.prototype = new CABLES.Op(); +CABLES.OPS["7c96fee9-4c2f-45e1-a41b-096b06d286b8"]={f:Ops.Trigger.IsTriggered,objName:"Ops.Trigger.IsTriggered"}; + + + + +// ************************************************************** +// +// Ops.Trigger.NthTrigger_v2 +// +// ************************************************************** + +Ops.Trigger.NthTrigger_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let DEFAULT_NTH = 5; + +// inputs +let exePort = op.inTriggerButton("Execute"); +let nthPort = op.inValue("Nth", DEFAULT_NTH); + +// outputs +let triggerPort = op.outTrigger("Next"); + +let count = 0; +let nth = DEFAULT_NTH; + +exePort.onTriggered = onExeTriggered; +nthPort.onChange = valueChanged; + +function onExeTriggered() +{ + count++; + if (count % nth === 0) + { + count = 0; + triggerPort.trigger(); + } +} + +function valueChanged() +{ + nth = nthPort.get(); + count = 0; +} + + +}; + +Ops.Trigger.NthTrigger_v2.prototype = new CABLES.Op(); +CABLES.OPS["ea43c184-5842-4aa1-b298-5db4515cbed0"]={f:Ops.Trigger.NthTrigger_v2,objName:"Ops.Trigger.NthTrigger_v2"}; + + + + +// ************************************************************** +// +// Ops.Trigger.ProbabilityTrigger +// +// ************************************************************** + +Ops.Trigger.ProbabilityTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inTrigger = op.inTrigger("Trigger In"); +const inProbability = op.inFloatSlider("Probability", 0.5); +const outTrigger = op.outTrigger("Trigger Output"); +const outInverseTrigger = op.outTrigger("Inverse Trigger Output"); +Math.randomSeed = 0; + +let scheduleUpdate = false; +let probability = inProbability.get(); +inProbability.onChange = () => +{ + if (inTrigger.isLinked()) + { + scheduleUpdate = true; + return; + } + probability = inProbability.get(); +}; + +inTrigger.onTriggered = () => +{ + if (scheduleUpdate) + { + probability = inProbability.get(); + scheduleUpdate = false; + } + if (probability >= 1) + { + outTrigger.trigger(); + return; + } + + if (probability <= 0) + { + outInverseTrigger.trigger(); + return; + } + if (Math.seededRandom() < probability) + { + outTrigger.trigger(); + } + else + { + outInverseTrigger.trigger(); + } +}; + + +}; + +Ops.Trigger.ProbabilityTrigger.prototype = new CABLES.Op(); +CABLES.OPS["69436c67-eab0-4829-b0dc-d7bfd863a116"]={f:Ops.Trigger.ProbabilityTrigger,objName:"Ops.Trigger.ProbabilityTrigger"}; + + + + +// ************************************************************** +// +// Ops.Trigger.RandomTrigger +// +// ************************************************************** + +Ops.Trigger.RandomTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Render"), + inNum = op.inValue("Num Times", 3), + inSeed = op.inValue("Seed", 1), + inOnlyOnce = op.inValueBool("Only Once"), + outIndex = op.outNumber("Index"); + +const linked = []; +const triggers = []; + +for (let i = 0; i < 8; i++) +{ + const newIn = op.addOutPort(new CABLES.Port(op, "trigger " + i, CABLES.OP_PORT_TYPE_FUNCTION)); + triggers.push(newIn); + newIn.onLinkChanged = updateLinkedArray; +} + +exec.onTriggered = function () +{ + if (linked.length == 0) return; + + Math.randomSeed = inSeed.get(); + + let numTriggered = 0; + + if (inOnlyOnce.get()) + { + for (var i = 0; i < linked.length; i++) + linked[i].RTwasTriggered = false; + } + + for (var i = 0; i < inNum.get(); i++) + { + outIndex.set(i); + + const r = Math.floor(Math.seededRandom() * linked.length); + + if (linked[r]) + { + if (inOnlyOnce.get()) + { + if (numTriggered == linked.length) + { + return; + } + if (!linked[r].RTwasTriggered) + { + linked[r].trigger(); + numTriggered++; + } + else i--; + } + else + { + linked[r].trigger(); + } + linked[r].RTwasTriggered = true; + } + } +}; + +function updateLinkedArray() +{ + linked.length = 0; + for (let i = 0; i < triggers.length; i++) + { + if (triggers[i].isLinked()) + linked.push(triggers[i]); + } +} + + +}; + +Ops.Trigger.RandomTrigger.prototype = new CABLES.Op(); +CABLES.OPS["3996175e-0424-407b-bcff-a7b4791d75df"]={f:Ops.Trigger.RandomTrigger,objName:"Ops.Trigger.RandomTrigger"}; + + + + +// ************************************************************** +// +// Ops.Trigger.Repeat2d +// +// ************************************************************** + +Ops.Trigger.Repeat2d = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + numx = op.inValueInt("num x", 5), + numy = op.inValueInt("num y", 5), + mul = op.inValueFloat("mul", 1), + center = op.inValueBool("center"), + trigger = op.outTrigger("trigger"), + outX = op.outNumber("x"), + outY = op.outNumber("y"), + idx = op.outNumber("index"), + total = op.outNumber("total iterations"); + +exe.onTriggered = function () +{ + let subX = 0; + let subY = 0; + const m = mul.get(); + const nx = numx.get(); + const ny = numy.get(); + + if (center.get()) + { + subX = ((nx - 1) * m) / 2.0; + subY = ((ny - 1) * m) / 2.0; + } + + for (let y = 0; y < ny; y++) + { + outY.set((y * m) - subY); + for (let x = 0; x < nx; x++) + { + outX.set((x * m) - subX); + idx.set(x + y * nx); + trigger.trigger(); + } + } + total.set(numx.get() * numy.get()); +}; + + +}; + +Ops.Trigger.Repeat2d.prototype = new CABLES.Op(); +CABLES.OPS["79934693-5887-4173-8b48-3e3a18fcf225"]={f:Ops.Trigger.Repeat2d,objName:"Ops.Trigger.Repeat2d"}; + + + + +// ************************************************************** +// +// Ops.Trigger.Repeat_v2 +// +// ************************************************************** + +Ops.Trigger.Repeat_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe=op.inTrigger("Execute"), + num=op.inValueInt("Repeats",5), + dir=op.inSwitch("Direction",['Forward','Backward'],'Forward'), + next=op.outTrigger("Next"), + idx=op.addOutPort(new CABLES.Port(op,"index")); + +dir.onChange=updateDir; +updateDir(); + +function updateDir() +{ + if(dir.get()=="Forward") exe.onTriggered=forward; + else exe.onTriggered=backward; +} + +function forward() +{ + const max=Math.floor(num.get()); + + for(var i=0;i-1;i--) + { + idx.set(i); + next.trigger(); + } +} + + +}; + +Ops.Trigger.Repeat_v2.prototype = new CABLES.Op(); +CABLES.OPS["a4deea80-db97-478f-ad1a-5ee30f2f47cc"]={f:Ops.Trigger.Repeat_v2,objName:"Ops.Trigger.Repeat_v2"}; + + + + +// ************************************************************** +// +// Ops.Trigger.RouteTrigger +// +// ************************************************************** + +Ops.Trigger.RouteTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const NUM_PORTS = 24; + +const exePort = op.inTriggerButton("Execute"); +const switchPort = op.inValueInt("Switch Value"); +const nextTriggerPort = op.outTrigger("Next Trigger"); +const valueOutPort = op.outNumber("Switched Value"); +const triggerPorts = []; +for (let j = 0; j < NUM_PORTS; j++) +{ + triggerPorts[j] = op.outTrigger("Trigger " + j); +} +const defaultTriggerPort = op.outTrigger("Default Trigger"); + +// functions + +function update() +{ + const index = Math.round(switchPort.get()); + if (index >= 0 && index < NUM_PORTS) + { + valueOutPort.set(index); + triggerPorts[index].trigger(); + } + else + { + valueOutPort.set(-1); + defaultTriggerPort.trigger(); + } + nextTriggerPort.trigger(); +} + +// change listeners / trigger events +exePort.onTriggered = update; + + +}; + +Ops.Trigger.RouteTrigger.prototype = new CABLES.Op(); +CABLES.OPS["44ceb5d8-b040-4722-b189-a6fb8172517d"]={f:Ops.Trigger.RouteTrigger,objName:"Ops.Trigger.RouteTrigger"}; + + + + +// ************************************************************** +// +// Ops.Trigger.RouteTriggerString +// +// ************************************************************** + +Ops.Trigger.RouteTriggerString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const NUM_PORTS = 24; +const exePort = op.inTriggerButton("Execute"); +const switchPort = op.inString("Switch Value"); +const nextTriggerPort = op.outTrigger("Next Trigger"); +const valueOutPort = op.outNumber("Switched Index"); + +let index = -1; + +const triggerPorts = []; +const namePorts = []; + +for (let j = 0; j < NUM_PORTS; j++) +{ + triggerPorts[j] = op.outTrigger("Trigger " + j); + namePorts[j] = op.inString("String " + j); + namePorts[j].onChange = updateIndex; +} +// const defaultTriggerPort = op.outTrigger("Default Trigger"); + +op.onLoad = +op.onInit = +switchPort.onChange = updateIndex; + +function updateIndex() +{ + index = -1; + for (let i = 0; i < namePorts.length; i++) + { + if (namePorts[i].get() == switchPort.get()) + { + index = i; + } + } + // if (index >= 0 && index < NUM_PORTS) + // { + // valueOutPort.set(index); + // triggerPorts[index].trigger(); + // } + // else + // { + // valueOutPort.set(-1); + // defaultTriggerPort.trigger(); + // } +} + +exePort.onTriggered = () => +{ + if (index >= 0) triggerPorts[index].trigger(); + + valueOutPort.set(index); + nextTriggerPort.trigger(); +}; + + +}; + +Ops.Trigger.RouteTriggerString.prototype = new CABLES.Op(); +CABLES.OPS["973746cc-8b57-4510-af73-780b771e0dc7"]={f:Ops.Trigger.RouteTriggerString,objName:"Ops.Trigger.RouteTriggerString"}; + + + + +// ************************************************************** +// +// Ops.Trigger.SetNumberOnTrigger +// +// ************************************************************** + +Ops.Trigger.SetNumberOnTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + setValuePort = op.inTriggerButton("Set"), + valuePort = op.inValueFloat("Number"), + outNext = op.outTrigger("Next"), + outValuePort = op.outNumber("Out Value"); + +outValuePort.changeAlways = true; + +setValuePort.onTriggered = function () +{ + outValuePort.set(valuePort.get()); + outNext.trigger(); +}; + + +}; + +Ops.Trigger.SetNumberOnTrigger.prototype = new CABLES.Op(); +CABLES.OPS["9989b1c0-1073-4d5f-bfa0-36dd98b66e27"]={f:Ops.Trigger.SetNumberOnTrigger,objName:"Ops.Trigger.SetNumberOnTrigger"}; + + + + +// ************************************************************** +// +// Ops.Trigger.SwitchTrigger +// +// ************************************************************** + +Ops.Trigger.SwitchTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const NUM_PORTS = 16; + +const + inIndex = op.inValueInt("Trigger Index", 0), + triggerPorts = [], + outTrig = op.outTrigger("Trigger out"); + +for (let i = 0; i < NUM_PORTS; i++) +{ + const port = op.inTrigger("Trigger in " + i); + port.onTriggered = function () { update(i); }; + triggerPorts.onChange = function () { update(i); }; + triggerPorts.push(port); +} + +function update(inputNum) +{ + const index = Math.min(Math.max(inIndex.get(), 0), 15); + if (inputNum == index) outTrig.trigger(); +} + + +}; + +Ops.Trigger.SwitchTrigger.prototype = new CABLES.Op(); +CABLES.OPS["aee29293-0c4f-404d-b724-484bbb57361e"]={f:Ops.Trigger.SwitchTrigger,objName:"Ops.Trigger.SwitchTrigger"}; + + + + +// ************************************************************** +// +// Ops.Trigger.Threshold +// +// ************************************************************** + +Ops.Trigger.Threshold = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// this op will send one trigger out if the threshold has been crossed +// but will not send another until the incoming inValue +// drops below the threshold and go's above it again + +const inValue = op.inValue("Input"), + inThreshold = op.inValue("Threshold"), + output = op.outTrigger("Output"); + +let hasThresholdBeenExceeded = false; + +inValue.onChange = update; +function update() +{ + if (!hasThresholdBeenExceeded && inValue.get() >= inThreshold.get()) + { + hasThresholdBeenExceeded = true; + output.trigger(); + } + else if (hasThresholdBeenExceeded && inValue.get() <= inThreshold.get()) + { + hasThresholdBeenExceeded = false; + } +} + + +}; + +Ops.Trigger.Threshold.prototype = new CABLES.Op(); +CABLES.OPS["ef0891db-6053-42ba-b7d5-29c7cf6d8208"]={f:Ops.Trigger.Threshold,objName:"Ops.Trigger.Threshold"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TimedSequence +// +// ************************************************************** + +Ops.Trigger.TimedSequence = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTrigger("exe"); +const current = op.inValueInt("current", 0); +const overwriteTime = op.inValueBool("overwriteTime"); +const ignoreInSubPatch = op.inValueBool("ignoreInSubPatch", false); +const triggerAlways = op.outTrigger("triggerAlways"); +const outNames = op.outArray("Names", []); +const currentKeyTime = op.outNumber("currentKeyTime"); +const outCurrent = op.outNumber("Current"); +let triggers = []; + +for (let i = 0; i < 32; i++) +{ + let p = op.outTrigger("trigger " + i); + p.onLinkChanged = updateNames; + triggers.push(p); +} + +function updateNames() +{ + let names = []; + for (let i = 0; i < triggers.length; i++) + if (triggers[i].isLinked()) names.push(triggers[i].links[0].getOtherPort(triggers[i]).parent.uiAttribs.title); + else names.push("none"); + + outNames.set(names); +} + +op.onLoaded = updateNames; + +let lastUiValue = -1; + +exe.onTriggered = doTrigger; + +function doTrigger(_time) +{ + let spl = 0; + + let outIndex = Math.round(current.get() - 0.5); + + if (window.gui) + { + if (current.get() != lastUiValue) + { + lastUiValue = current.get(); + for (spl = 0; spl < triggers.length; spl++) + { + if (spl == lastUiValue) triggers[spl].setUiActiveState(true); + else triggers[spl].setUiActiveState(false); + } + } + } + + if (current.anim) + { + let time = _time; + if (_time === undefined) time = current.parent.patch.timer.getTime(); + let key = current.anim.getKey(time); + + let timeOff = 0; + if (key) timeOff = key.time; + + currentKeyTime.set(time - timeOff); + + if (current.isAnimated()) + { + if (overwriteTime.get()) + { + current.parent.patch.timer.overwriteTime = currentKeyTime.get(); // todo why current ? why not self ? + } + } + } + + if (op.patch.gui && ignoreInSubPatch.get()) + { + for (let i = 0; i < triggers.length; i++) + { + for (spl = 0; spl < triggers[i].links.length; spl++) + { + if (triggers[i].links[spl]) + { + if (triggers[i].links[spl].portIn.parent.patchId) + { + if (CABLES.UI) + if (gui.patchView.getCurrentSubPatch() == triggers[i].links[spl].portIn.parent.patchId.get()) + { + op.patch.timer.overwriteTime = -1; + + triggers[i].trigger(); + return; + } + } + } + } + } + } + + if (outIndex >= 0 && outIndex < triggers.length) + { + outCurrent.set(outIndex); + triggers[outIndex].trigger(); + } + + op.patch.timer.overwriteTime = -1; + triggerAlways.trigger(); +} + + +}; + +Ops.Trigger.TimedSequence.prototype = new CABLES.Op(); +CABLES.OPS["73c892c9-3e81-4ddc-952a-fbe6bf00ef2c"]={f:Ops.Trigger.TimedSequence,objName:"Ops.Trigger.TimedSequence"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerButton +// +// ************************************************************** + +Ops.Trigger.TriggerButton = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTrig = op.inTriggerButton("Trigger"), + outTrig = op.outTrigger("Next"); + +inTrig.onTriggered = function () +{ + outTrig.trigger(); +}; + + +}; + +Ops.Trigger.TriggerButton.prototype = new CABLES.Op(); +CABLES.OPS["21630924-39e4-4df5-9965-b9136510d156"]={f:Ops.Trigger.TriggerButton,objName:"Ops.Trigger.TriggerButton"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerCounter +// +// ************************************************************** + +Ops.Trigger.TriggerCounter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTriggerButton("exe"), + reset = op.inTriggerButton("reset"), + trigger = op.outTrigger("trigger"), + num = op.outNumber("timesTriggered"); + +op.toWorkPortsNeedToBeLinked(exe); + +let n = 0; + +reset.onTriggered = +op.onLoaded = + doReset; + +exe.onTriggered = function () +{ + n++; + num.set(n); + op.setUiAttrib({ "extendTitle": n }); + trigger.trigger(); +}; + +function doReset() +{ + n = 0; + op.setUiAttrib({ "extendTitle": n }); + num.set(n); +} + + +}; + +Ops.Trigger.TriggerCounter.prototype = new CABLES.Op(); +CABLES.OPS["e640619f-235c-4543-bbf8-b358e0283180"]={f:Ops.Trigger.TriggerCounter,objName:"Ops.Trigger.TriggerCounter"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerCounterLoop +// +// ************************************************************** + +Ops.Trigger.TriggerCounterLoop = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTriggerButton("trigger in"), + reset = op.inTriggerButton("reset"), + trigger = op.outTrigger("trigger out"), + num = op.outNumber("current count"), + + inMinLoopValue = op.inValueInt("Loop min", 0.0), + inMaxLoopValue = op.inValueInt("Loop max", 4.0); + +let n = Math.floor(inMinLoopValue.get()); + +// increments with each trigger and loops +// depending on min and max loop values +// can also work with negative numbers +// if min is greater than max then it decrements +// instead of incrementing +exe.onTriggered = function () +{ + let inMin = Math.floor(inMinLoopValue.get()); + let inMax = Math.floor(inMaxLoopValue.get()); + + if (inMin < inMax) + { + if (n < inMin) + { + n = inMinLoopValue.get(); + } + else if (n >= inMax) + { + n = inMinLoopValue.get(); + } + else + { + n++; + } + } + else if (inMin > inMax) + { + if (n < inMax) + { + n = inMin; + } + else if (n > inMin) + { + inMin; + } + else if (n <= inMax) + { + n = inMin; + } + else + { + n--; + } + } + num.set(n); + op.setUiAttrib({ "extendTitle": n }); + trigger.trigger(); +}; + +reset.onTriggered = function () +{ + let inMin = Math.floor(inMinLoopValue.get()); + let inMax = Math.floor(inMaxLoopValue.get()); + + if (inMin < inMax) + { + n = inMin; + } + else if (inMax < inMin) + { + n = inMin; + } + else + { + n = 0; + } + op.setUiAttrib({ "extendTitle": n }); + num.set(n); +}; + + +}; + +Ops.Trigger.TriggerCounterLoop.prototype = new CABLES.Op(); +CABLES.OPS["d3356c53-e278-433f-af0b-d8327cd99a2d"]={f:Ops.Trigger.TriggerCounterLoop,objName:"Ops.Trigger.TriggerCounterLoop"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerDistributeByValue +// +// ************************************************************** + +Ops.Trigger.TriggerDistributeByValue = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const exe = op.inTrigger("exe"); +let number = op.addInPort(new CABLES.Port(op, "number")); +let max = op.addInPort(new CABLES.Port(op, "max")); +let numOut = op.addInPort(new CABLES.Port(op, "num outputs")); +let num = op.addOutPort(new CABLES.Port(op, "num", CABLES.OP_PORT_TYPE_VALUE)); + +number.set(0); +max.set(1); +numOut.set(2); +num.set(0); + +let triggers = []; +let numTriggers = 20; + +let trigger = function () +{ + let s = max.get() / numOut.get(); + let index = Math.abs(Math.floor(number.get() / s)); + + num.set(index); + + if (!isNaN(index) && index < numTriggers) + { + triggers[index].trigger(); + } +}; + +exe.onTriggered = trigger; + +for (let i = 0; i < numTriggers; i++) +{ + triggers.push(op.addOutPort(new CABLES.Port(op, "trigger " + i, CABLES.OP_PORT_TYPE_FUNCTION))); +} + + +}; + +Ops.Trigger.TriggerDistributeByValue.prototype = new CABLES.Op(); +CABLES.OPS["e697380f-ac93-4d4a-9e9c-b54287773668"]={f:Ops.Trigger.TriggerDistributeByValue,objName:"Ops.Trigger.TriggerDistributeByValue"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerExtender +// +// ************************************************************** + +Ops.Trigger.TriggerExtender = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTriggerPort = op.inTriggerButton("Execute"), + outTriggerPort = op.outTrigger("Next"); + +inTriggerPort.onTriggered = function () +{ + outTriggerPort.trigger(); +}; + + +}; + +Ops.Trigger.TriggerExtender.prototype = new CABLES.Op(); +CABLES.OPS["7ef594f3-4907-47b0-a2d3-9854eda1679d"]={f:Ops.Trigger.TriggerExtender,objName:"Ops.Trigger.TriggerExtender"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerIfDecreased +// +// ************************************************************** + +Ops.Trigger.TriggerIfDecreased = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + value = op.inValue("Value"), + trigger = op.outTrigger("Trigger"); + +let lastValue = -Number.MAX_VALUE; + +value.onChange = function () +{ + const v = value.get(); + if (v < lastValue) + { + trigger.trigger(); + } + lastValue = v; +}; + + +}; + +Ops.Trigger.TriggerIfDecreased.prototype = new CABLES.Op(); +CABLES.OPS["16ec4069-3682-461e-95ff-1d86e3f44512"]={f:Ops.Trigger.TriggerIfDecreased,objName:"Ops.Trigger.TriggerIfDecreased"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerIfIncreased +// +// ************************************************************** + +Ops.Trigger.TriggerIfIncreased = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + value = op.inFloat("Value"), + trigger = op.outTrigger("Trigger"); + +let lastValue = -Number.MAX_VALUE; + +value.onChange = function () +{ + const v = value.get(); + if (v > lastValue) + { + trigger.trigger(); + } + lastValue = v; +}; + + +}; + +Ops.Trigger.TriggerIfIncreased.prototype = new CABLES.Op(); +CABLES.OPS["bc820891-48c7-4287-9b5e-4196e192741b"]={f:Ops.Trigger.TriggerIfIncreased,objName:"Ops.Trigger.TriggerIfIncreased"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerLimiter +// +// ************************************************************** + +Ops.Trigger.TriggerLimiter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inTriggerPort = op.inTrigger("In Trigger"), + timePort = op.inValue("Milliseconds", 300), + outTriggerPort = op.outTrigger("Out Trigger"), + progress = op.outNumber("Progress"); + +let lastTriggerTime = 0; + +// change listeners +inTriggerPort.onTriggered = function () +{ + const now = CABLES.now(); + let prog = (now - lastTriggerTime) / timePort.get(); + + if (prog > 1.0)prog = 1.0; + if (prog < 0.0)prog = 0.0; + + progress.set(prog); + + if (now >= lastTriggerTime + timePort.get()) + { + lastTriggerTime = now; + outTriggerPort.trigger(); + } +}; + + +}; + +Ops.Trigger.TriggerLimiter.prototype = new CABLES.Op(); +CABLES.OPS["47641d85-9f81-4287-8aa2-35753b0727e0"]={f:Ops.Trigger.TriggerLimiter,objName:"Ops.Trigger.TriggerLimiter"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerNumber +// +// ************************************************************** + +Ops.Trigger.TriggerNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe0 = op.inTriggerButton("0"), + exe1 = op.inTriggerButton("1"), + exe2 = op.inTriggerButton("2"), + exe3 = op.inTriggerButton("3"), + exe4 = op.inTriggerButton("4"), + exe5 = op.inTriggerButton("5"), + exe6 = op.inTriggerButton("6"), + exe7 = op.inTriggerButton("7"), + number = op.outNumber("number"); + +number.changeAlways = true; +const outTrig = op.outTrigger("Triggered"); + +exe0.onTriggered = function () { number.set(0); outTrig.trigger(); }; +exe1.onTriggered = function () { number.set(1); outTrig.trigger(); }; +exe2.onTriggered = function () { number.set(2); outTrig.trigger(); }; +exe3.onTriggered = function () { number.set(3); outTrig.trigger(); }; +exe4.onTriggered = function () { number.set(4); outTrig.trigger(); }; +exe5.onTriggered = function () { number.set(5); outTrig.trigger(); }; +exe6.onTriggered = function () { number.set(6); outTrig.trigger(); }; +exe7.onTriggered = function () { number.set(7); outTrig.trigger(); }; + + +}; + +Ops.Trigger.TriggerNumber.prototype = new CABLES.Op(); +CABLES.OPS["43ed1123-1312-4383-b843-27b8ec540c09"]={f:Ops.Trigger.TriggerNumber,objName:"Ops.Trigger.TriggerNumber"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerOnChangeArray +// +// ************************************************************** + +Ops.Trigger.TriggerOnChangeArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inval=op.inArray("Array"), + next=op.outTrigger("Changed"), + outArr=op.outArray("Result"); + +inval.onChange=function() +{ + outArr.set(inval.get()); + next.trigger(); +}; + +}; + +Ops.Trigger.TriggerOnChangeArray.prototype = new CABLES.Op(); +CABLES.OPS["e4ddec93-4dee-422b-a402-6a0f6e469ce6"]={f:Ops.Trigger.TriggerOnChangeArray,objName:"Ops.Trigger.TriggerOnChangeArray"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerOnChangeObject +// +// ************************************************************** + +Ops.Trigger.TriggerOnChangeObject = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inval = op.inObject("Object"), + next = op.outTrigger("Changed"), + outArr = op.outObject("Result"); + +inval.onChange = function () +{ + outArr.set(inval.get()); + next.trigger(); +}; + + +}; + +Ops.Trigger.TriggerOnChangeObject.prototype = new CABLES.Op(); +CABLES.OPS["c7e3fa27-21e8-44ef-b176-e0e596837abb"]={f:Ops.Trigger.TriggerOnChangeObject,objName:"Ops.Trigger.TriggerOnChangeObject"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerOnChangeString +// +// ************************************************************** + +Ops.Trigger.TriggerOnChangeString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inval = op.inString("String"), + next = op.outTrigger("Changed"), + outStr = op.outString("Result"); + +inval.onChange = function () +{ + outStr.set(inval.get()); + next.trigger(); +}; + + +}; + +Ops.Trigger.TriggerOnChangeString.prototype = new CABLES.Op(); +CABLES.OPS["319d07e0-5cbe-4bc1-89fb-a934fd41b0c4"]={f:Ops.Trigger.TriggerOnChangeString,objName:"Ops.Trigger.TriggerOnChangeString"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerOnChangeTexture +// +// ************************************************************** + +Ops.Trigger.TriggerOnChangeTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inval = op.inTexture("Texture"), + inFilter = op.inBool("Ignore empty/default Texture", false), + next = op.outTrigger("Changed"), + outTex = op.outTexture("Result", CGL.Texture.getEmptyTexture(op.patch.cgl)); + +inval.onLinkChanged = +inval.onChange = function () +{ + const v = inval.get(); + if (inFilter.get() && (v == CGL.Texture.getEmptyTexture(op.patch.cgl) || v == null)) return; + + outTex.set(v || CGL.Texture.getEmptyTexture(op.patch.cgl)); + next.trigger(); +}; + + +}; + +Ops.Trigger.TriggerOnChangeTexture.prototype = new CABLES.Op(); +CABLES.OPS["d7260ecb-d862-496a-8a26-f8165ab49dd2"]={f:Ops.Trigger.TriggerOnChangeTexture,objName:"Ops.Trigger.TriggerOnChangeTexture"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerOnce +// +// ************************************************************** + +Ops.Trigger.TriggerOnce = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTriggerButton("Exec"), + reset = op.inTriggerButton("Reset"), + next = op.outTrigger("Next"), + outTriggered = op.outBoolNum("Was Triggered"); + +let triggered = false; + +op.toWorkPortsNeedToBeLinked(exe); + +reset.onTriggered = function () +{ + triggered = false; + outTriggered.set(triggered); +}; + +exe.onTriggered = function () +{ + if (triggered) return; + + triggered = true; + next.trigger(); + outTriggered.set(triggered); +}; + + +}; + +Ops.Trigger.TriggerOnce.prototype = new CABLES.Op(); +CABLES.OPS["cf3544e4-e392-432b-89fd-fcfb5c974388"]={f:Ops.Trigger.TriggerOnce,objName:"Ops.Trigger.TriggerOnce"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerReceive +// +// ************************************************************** + +Ops.Trigger.TriggerReceive = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const next = op.outTrigger("Triggered"); +op.varName = op.inValueSelect("Named Trigger", [], "", true); + +updateVarNamesDropdown(); +op.patch.addEventListener("namedTriggersChanged", updateVarNamesDropdown); + +let oldName = null; + +function doTrigger() +{ + next.trigger(); +} + +function updateVarNamesDropdown() +{ + if (CABLES.UI) + { + let varnames = []; + let vars = op.patch.namedTriggers; + // varnames.push('+ create new one'); + for (let i in vars) varnames.push(i); + op.varName.uiAttribs.values = varnames; + } +} + +op.varName.onChange = function () +{ + if (oldName) + { + let oldCbs = op.patch.namedTriggers[oldName]; + let a = oldCbs.indexOf(doTrigger); + if (a != -1) oldCbs.splice(a, 1); + } + + op.setTitle(">" + op.varName.get()); + op.patch.namedTriggers[op.varName.get()] = op.patch.namedTriggers[op.varName.get()] || []; + let cbs = op.patch.namedTriggers[op.varName.get()]; + + cbs.push(doTrigger); + oldName = op.varName.get(); + updateError(); + op.patch.emitEvent("opTriggerNameChanged", op, op.varName.get()); +}; + +op.on("uiParamPanel", updateError); + +function updateError() +{ + if (!op.varName.get()) + { + op.setUiError("unknowntrigger", "unknown trigger"); + } + else op.setUiError("unknowntrigger", null); +} + + +}; + +Ops.Trigger.TriggerReceive.prototype = new CABLES.Op(); +CABLES.OPS["0816c999-f2db-466b-9777-2814573574c5"]={f:Ops.Trigger.TriggerReceive,objName:"Ops.Trigger.TriggerReceive"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerReceiveFilter +// +// ************************************************************** + +Ops.Trigger.TriggerReceiveFilter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const prefixIn = op.inString("Prefix", ""); +const triggerOut = op.outTrigger("Trigger Out"); +const triggerNameOut = op.outString("Trigger Name"); + +const listener = (triggerName) => +{ + const prefix = prefixIn.get(); + if (triggerName) + { + if (prefix) + { + if (triggerName.startsWith(prefix)) + { + triggerNameOut.set(triggerName); + triggerOut.trigger(); + } + } + else + { + triggerNameOut.set(triggerName); + triggerOut.trigger(); + } + } +}; + +prefixIn.onChange = () => +{ + if (prefixIn.get()) + { + op.setUiAttrib({ "extendTitle": prefixIn.get() }); + } +}; + +op.patch.addEventListener("namedTriggerSent", listener); + + +}; + +Ops.Trigger.TriggerReceiveFilter.prototype = new CABLES.Op(); +CABLES.OPS["7bf9e19a-55a8-4eb4-aaeb-5a15a8d2f958"]={f:Ops.Trigger.TriggerReceiveFilter,objName:"Ops.Trigger.TriggerReceiveFilter"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerSend +// +// ************************************************************** + +Ops.Trigger.TriggerSend = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const trigger = op.inTriggerButton("Trigger"); +op.varName = op.inValueSelect("Named Trigger", [], "", true); + +op.varName.onChange = updateName; + +trigger.onTriggered = doTrigger; + +op.patch.addEventListener("namedTriggersChanged", updateVarNamesDropdown); + +updateVarNamesDropdown(); + +function updateVarNamesDropdown() +{ + if (CABLES.UI) + { + const varnames = []; + const vars = op.patch.namedTriggers; + varnames.push("+ create new one"); + for (const i in vars) varnames.push(i); + op.varName.uiAttribs.values = varnames; + } +} + +function updateName() +{ + if (CABLES.UI) + { + if (op.varName.get() == "+ create new one") + { + new CABLES.UI.ModalDialog({ + "prompt": true, + "title": "New Trigger", + "text": "enter a name for the new trigger", + "promptValue": "", + "promptOk": (str) => + { + op.varName.set(str); + op.patch.namedTriggers[str] = op.patch.namedTriggers[str] || []; + updateVarNamesDropdown(); + } + }); + return; + } + + op.refreshParams(); + } + + if (!op.patch.namedTriggers[op.varName.get()]) + { + op.patch.namedTriggers[op.varName.get()] = op.patch.namedTriggers[op.varName.get()] || []; + op.patch.emitEvent("namedTriggersChanged"); + } + + op.setTitle(">" + op.varName.get()); + + op.refreshParams(); + op.patch.emitEvent("opTriggerNameChanged", op, op.varName.get()); +} + +function doTrigger() +{ + const arr = op.patch.namedTriggers[op.varName.get()]; + // fire an event even if noone is receiving this trigger + // this way TriggerReceiveFilter can still handle it + op.patch.emitEvent("namedTriggerSent", op.varName.get()); + + if (!arr) + { + op.setUiError("unknowntrigger", "unknown trigger"); + return; + } + else op.setUiError("unknowntrigger", null); + + for (let i = 0; i < arr.length; i++) + { + arr[i](); + } +} + + +}; + +Ops.Trigger.TriggerSend.prototype = new CABLES.Op(); +CABLES.OPS["ce1eaf2b-943b-4dc0-ab5e-ee11b63c9ed0"]={f:Ops.Trigger.TriggerSend,objName:"Ops.Trigger.TriggerSend"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerSendNamed +// +// ************************************************************** + +Ops.Trigger.TriggerSendNamed = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const trigger = op.inTriggerButton("Trigger"); +const varname = op.inString("Named Trigger", ""); + +trigger.onTriggered = doTrigger; + +varname.onChange = () => +{ + op.setUiAttrib({ "extendTitle": varname.get() }); +}; + +function doTrigger() +{ + const arr = op.patch.namedTriggers[varname.get()]; + // fire an event even if noone is receiving this trigger + // this way TriggerReceiveFilter can still handle it + op.patch.emitEvent("namedTriggerSent", varname.get()); + + if (!arr) + { + return; + } + + for (let i = 0; i < arr.length; i++) + { + arr[i](); + } +} + + +}; + +Ops.Trigger.TriggerSendNamed.prototype = new CABLES.Op(); +CABLES.OPS["45d07cd7-bdfb-48b7-bc76-7606de5afd76"]={f:Ops.Trigger.TriggerSendNamed,objName:"Ops.Trigger.TriggerSendNamed"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggerString +// +// ************************************************************** + +Ops.Trigger.TriggerString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTriggerButton("Trigger"), + inString = op.inString("String", ""), + next = op.outTrigger("Next"), + outString = op.outString("Result"); + +outString.changeAlways = true; +exec.onTriggered = function () +{ + outString.set(inString.get()); + next.trigger(); +}; + + +}; + +Ops.Trigger.TriggerString.prototype = new CABLES.Op(); +CABLES.OPS["217482b8-2ee6-4609-b7ad-4550e6aaa371"]={f:Ops.Trigger.TriggerString,objName:"Ops.Trigger.TriggerString"}; + + + + +// ************************************************************** +// +// Ops.Trigger.TriggersPerSecond +// +// ************************************************************** + +Ops.Trigger.TriggersPerSecond = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + cps = op.outNumber("cps"); + +let timeStart = 0; +let cpsCount = 0; + +exe.onTriggered = function () +{ + if (timeStart === 0)timeStart = CABLES.now(); + let now = CABLES.now(); + + if (now - timeStart > 1000) + { + timeStart = CABLES.now(); + op.setUiAttrib({ "extendTitle": cpsCount }); + cps.set(cpsCount); + cpsCount = 0; + } + + cpsCount++; +}; + + +}; + +Ops.Trigger.TriggersPerSecond.prototype = new CABLES.Op(); +CABLES.OPS["ece2f153-eb31-4268-b0e5-8143ad2fdd81"]={f:Ops.Trigger.TriggersPerSecond,objName:"Ops.Trigger.TriggersPerSecond"}; + + + + +// ************************************************************** +// +// Ops.Trigger.ValueBecameZeroTrigger +// +// ************************************************************** + +Ops.Trigger.ValueBecameZeroTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let valueBefore = -1; + +// input +let valuePort = op.inValue("Value", -1); + +// output +let outTrigger = op.outTrigger("Became Zero Trigger"); + +valuePort.onChange = function () +{ + let value = valuePort.get(); + if (valueBefore != 0 & value == 0) + { + outTrigger.trigger(); + } + valueBefore = value; +}; + + +}; + +Ops.Trigger.ValueBecameZeroTrigger.prototype = new CABLES.Op(); +CABLES.OPS["822f925c-33e8-4b8a-8a89-be5d2e358ed7"]={f:Ops.Trigger.ValueBecameZeroTrigger,objName:"Ops.Trigger.ValueBecameZeroTrigger"}; + + + + +// ************************************************************** +// +// Ops.Ui.Area +// +// ************************************************************** + +Ops.Ui.Area = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inTitle = op.inString("Title", ""); + +inTitle.setUiAttribs({ "hidePort": true }); + +op.setUiAttrib({ "hasArea": true }); + +// exe.onTriggered=function() +// { +// op.patch.instancing.pushLoop(inNum.get()); + +// for(let i=0;i +{ + const mySubPatchOp = getPatchOp(); + + if (!dyn.links.length) return; + + const otherPort = dyn.links[0].getOtherPort(dyn); + dyn.removeLinks(); + + const newPortName = mySubPatchOp.addNewInPort(otherPort); + + const l = gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newPortName); + + mySubPatchOp.saveData(); +}; + + +}; + +Ops.Ui.PatchInput.prototype = new CABLES.Op(); +CABLES.OPS["e3f68bc3-892a-4c78-9974-aca25c27025d"]={f:Ops.Ui.PatchInput,objName:"Ops.Ui.PatchInput"}; + + + + +// ************************************************************** +// +// Ops.Ui.PatchOutput +// +// ************************************************************** + +Ops.Ui.PatchOutput = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const dyn = op.addInPort(new CABLES.Port(op, "create port", CABLES.OP_PORT_TYPE_DYNAMIC)); + +function getPatchOp() +{ + for (let i in op.patch.ops) + { + if (op.patch.ops[i].patchId) + { + if (op.patch.ops[i].patchId.get() == op.uiAttribs.subPatch) + { + return op.patch.ops[i]; + } + } + } +} + +dyn.onLinkChanged = () => +{ + const mySubPatchOp = getPatchOp(); + + if (!dyn.links.length) return; + + const otherPort = dyn.links[0].getOtherPort(dyn); + dyn.removeLinks(); + + const newPortName = mySubPatchOp.addNewOutPort(otherPort); + + const l = gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newPortName); + + mySubPatchOp.saveData(); +}; + + +}; + +Ops.Ui.PatchOutput.prototype = new CABLES.Op(); +CABLES.OPS["851b44cb-5667-4140-9800-5aeb7031f1d7"]={f:Ops.Ui.PatchOutput,objName:"Ops.Ui.PatchOutput"}; + + + + +// ************************************************************** +// +// Ops.Ui.SubPatch +// +// ************************************************************** + +Ops.Ui.SubPatch = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +op.dyn = op.addInPort(new CABLES.Port(op, "create port", CABLES.OP_PORT_TYPE_DYNAMIC)); +op.dynOut = op.addOutPort(new CABLES.Port(op, "create port out", CABLES.OP_PORT_TYPE_DYNAMIC)); + +const dataStr = op.addInPort(new CABLES.Port(op, "dataStr", CABLES.OP_PORT_TYPE_VALUE, { "display": "readonly" })); +op.patchId = op.addInPort(new CABLES.Port(op, "patchId", CABLES.OP_PORT_TYPE_VALUE, { "display": "readonly" })); + +if (CABLES.UI && CABLES.sandbox.isDevEnv()) +{ + const inMakeBp = op.inTriggerButton("Create Blueprint"); + inMakeBp.setUiAttribs({ "hidePort": true }); + + inMakeBp.onTriggered = makeBlueprint; +} + +dataStr.setUiAttribs({ "hideParam": true }); +op.patchId.setUiAttribs({ "hideParam": true }); + +let data = { "ports": [], "portsOut": [] }; +let oldPatchId = CABLES.generateUUID(); +op.patchId.set(oldPatchId); +getSubPatchInputOp(); +getSubPatchOutputOp(); + +let dataLoaded = false; + +op.saveData = saveData; + +op.patchId.onChange = function () +{ + const oldPatchOps = op.patch.getSubPatchOps(oldPatchId); + + if (oldPatchOps.length == 2) + { + for (let i = 0; i < oldPatchOps.length; i++) + { + op.patch.deleteOp(oldPatchOps[i].id); + } + } + else + { + } +}; + +op.onLoaded = function () +{ +}; + +op.onLoadedValueSet = function () +{ + data = JSON.parse(dataStr.get()); + if (!data) + { + data = { "ports": [], "portsOut": [] }; + } + setupPorts(); +}; + +function loadData() +{ +} + +dataStr.onChange = function () +{ + if (dataLoaded) return; + + if (!dataStr.get()) return; + try + { + loadData(); + } + catch (e) + { + op.logError("cannot load subpatch data..."); + op.logError(e); + } +}; + +function saveData() +{ + dataStr.set(JSON.stringify(data)); +} + +op.addPortListener = addPortListener; +function addPortListener(newPort, newPortInPatch) +{ + if (!newPort.hasSubpatchLstener) + { + newPort.hasSubpatchLstener = true; + newPort.addEventListener("onUiAttrChange", function (attribs) + { + if (attribs.title) + { + let i = 0; + for (i = 0; i < data.portsOut.length; i++) + if (data.portsOut[i].name == newPort.name) + data.portsOut[i].title = attribs.title; + + for (i = 0; i < data.ports.length; i++) + if (data.ports[i].name == newPort.name) + data.ports[i].title = attribs.title; + + saveData(); + } + }); + } + + if (newPort.direction == CABLES.PORT_DIR_IN) + { + if (newPort.type == CABLES.OP_PORT_TYPE_FUNCTION) + { + newPort.onTriggered = function () + { + if (newPortInPatch.isLinked()) + newPortInPatch.trigger(); + }; + } + else + { + newPort.onChange = function () + { + newPortInPatch.set(newPort.get()); + if (!newPort.isLinked()) + { + for (let i = 0; i < data.ports.length; i++) + { + if (data.ports[i].name === newPort.name) + { + data.ports[i].value = newPort.get(); + } + } + saveData(); + } + }; + } + } +} + +op.setupPorts = setupPorts; +function setupPorts() +{ + if (!op.patchId.get()) return; + const ports = data.ports || []; + const portsOut = data.portsOut || []; + let i = 0; + + for (i = 0; i < ports.length; i++) + { + if (!op.getPortByName(ports[i].name)) + { + const newPort = op.addInPort(new CABLES.Port(op, ports[i].name, ports[i].type)); + + const patchInputOp = getSubPatchInputOp(); + const newPortInPatch = patchInputOp.addOutPort(new CABLES.Port(patchInputOp, ports[i].name, ports[i].type)); + + newPort.ignoreValueSerialize = true; + newPort.setUiAttribs({ "editableTitle": true }); + if (ports[i].title) + { + newPort.setUiAttribs({ "title": ports[i].title }); + newPortInPatch.setUiAttribs({ "title": ports[i].title }); + } + if (ports[i].objType) + { + newPort.setUiAttribs({ "objType": ports[i].objType }); + newPortInPatch.setUiAttribs({ "objType": ports[i].objType }); + } + if (ports[i].value) + { + newPort.set(ports[i].value); + newPortInPatch.set(ports[i].value); + } + addPortListener(newPort, newPortInPatch); + } + } + + for (i = 0; i < portsOut.length; i++) + { + if (!op.getPortByName(portsOut[i].name)) + { + const newPortOut = op.addOutPort(new CABLES.Port(op, portsOut[i].name, portsOut[i].type)); + const patchOutputOp = getSubPatchOutputOp(); + const newPortOutPatch = patchOutputOp.addInPort(new CABLES.Port(patchOutputOp, portsOut[i].name, portsOut[i].type)); + + newPortOut.ignoreValueSerialize = true; + newPortOut.setUiAttribs({ "editableTitle": true }); + + if (portsOut[i].title) + { + newPortOut.setUiAttribs({ "title": portsOut[i].title }); + newPortOutPatch.setUiAttribs({ "title": portsOut[i].title }); + } + if (portsOut[i].objType) + { + newPortOut.setUiAttribs({ "objType": portsOut[i].objType }); + newPortOutPatch.setUiAttribs({ "objType": portsOut[i].objType }); + } + + // addPortListener(newPortOut,newPortOutPatch); + addPortListener(newPortOutPatch, newPortOut); + } + } + + dataLoaded = true; +} + +op.addNewInPort = function (otherPort, type, objType) +{ + const newName = "in" + data.ports.length + " " + otherPort.parent.name + " " + otherPort.name; + + const o = { "name": newName, "type": otherPort.type }; + if (otherPort.uiAttribs.objType)o.objType = otherPort.uiAttribs.objType; + + data.ports.push(o); + setupPorts(); + return newName; +}; + +op.dyn.onLinkChanged = function () +{ + if (op.dyn.isLinked()) + { + const otherPort = op.dyn.links[0].getOtherPort(op.dyn); + op.dyn.removeLinks(); + otherPort.removeLinkTo(op.dyn); + + op.log("dyn link changed!!!"); + + // const newName = "in" + data.ports.length + " " + otherPort.parent.name + " " + otherPort.name; + + // const o = { "name": newName, "type": otherPort.type }; + // if (otherPort.uiAttribs.objType)o.objType = otherPort.uiAttribs.objType; + // data.ports.push(o); + + // setupPorts(); + + const newName = op.addNewInPort(otherPort); + + const l = gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newName + ); + + dataLoaded = true; + saveData(); + } + else + { + setTimeout(function () + { + op.dyn.removeLinks(); + }, 100); + } +}; + +op.addNewOutPort = function (otherPort, type, objType) +{ + const newName = "out" + data.portsOut.length + " " + otherPort.parent.name + " " + otherPort.name; + + const o = { "name": newName, "type": otherPort.type }; + if (otherPort.uiAttribs.objType)o.objType = otherPort.uiAttribs.objType; + + data.portsOut.push(o); + setupPorts(); + return newName; +}; + +op.dynOut.onLinkChanged = function () +{ + if (op.dynOut.isLinked()) + { + const otherPort = op.dynOut.links[0].getOtherPort(op.dynOut); + op.dynOut.removeLinks(); + otherPort.removeLinkTo(op.dynOut); + + const newName = op.addNewOutPort(otherPort); + + gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newName + ); + + dataLoaded = true; + saveData(); + } + else + { + setTimeout(function () + { + op.dynOut.removeLinks(); + }, 100); + + op.log("dynOut unlinked..."); + } +}; + +function getSubPatchOutputOp() +{ + let patchOutputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchOutput"); + + if (!patchOutputOP) + { + op.patch.addOp("Ops.Ui.PatchOutput", { "subPatch": op.patchId.get(), "translate": { "x": 0, "y": 0 } }); + patchOutputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchOutput"); + if (!patchOutputOP) op.warn("no patchoutput!"); + } + return patchOutputOP; +} + +function getSubPatchInputOp() +{ + let patchInputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchInput"); + + if (!patchInputOP) + { + op.patch.addOp("Ops.Ui.PatchInput", { "subPatch": op.patchId.get(), "translate": { "x": 0, "y": 0 } }); + patchInputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchInput"); + if (!patchInputOP) op.warn("no patchinput2!"); + } + + return patchInputOP; +} + +op.addSubLink = function (p, p2) +{ + const num = data.ports.length; + const sublPortname = "in" + (num - 1) + " " + p2.parent.name + " " + p2.name; + + if (p.direction == CABLES.PORT_DIR_IN) + { + gui.scene().link( + p.parent, + p.getName(), + getSubPatchInputOp(), + sublPortname + ); + } + else + { + const numOut = data.portsOut.length; + gui.scene().link( + p.parent, + p.getName(), + getSubPatchOutputOp(), + "out" + (numOut - 1) + " " + p2.parent.name + " " + p2.name + ); + } + + const bounds = gui.patchView.getSubPatchBounds(op.patchId.get()); + + getSubPatchInputOp().uiAttr( + { + "translate": + { + "x": bounds.minx, + "y": bounds.miny - 100 + } + }); + + getSubPatchOutputOp().uiAttr( + { + "translate": + { + "x": bounds.minx, + "y": bounds.maxy + 100 + } + }); + saveData(); + return sublPortname; +}; + +op.onDelete = function () +{ + for (let i = op.patch.ops.length - 1; i >= 0; i--) + { + if (op.patch.ops[i] && op.patch.ops[i].uiAttribs && op.patch.ops[i].uiAttribs.subPatch == op.patchId.get()) + { + op.patch.deleteOp(op.patch.ops[i].id); + } + } +}; + +function makeBlueprint() +{ + const bpOp = op.patch.addOp(CABLES.UI.DEFAULTOPNAMES.blueprint); + + bpOp.getPortByName("externalPatchId").set(gui.patchId); + bpOp.getPortByName("subPatchId").set(op.patchId.get()); + bpOp.getPortByName("active").set(true); + + bpOp.uiAttr( + { + "translate": + { + "x": op.uiAttribs.translate.x - 150, + "y": op.uiAttribs.translate.y + } + }); +} + +op.rebuildListeners = () => +{ + op.log("rebuild listeners..."); + + const outop = getSubPatchOutputOp(); + for (let i = 0; i < outop.portsIn.length; i++) + { + if (outop.portsIn[i].isLinked()) + { + addPortListener(outop.portsIn[i], this.portsOut[i]); + } + } +}; + + +}; + +Ops.Ui.SubPatch.prototype = new CABLES.Op(); +CABLES.OPS["84d9a6f0-ed7a-466d-b386-225ed9e89c60"]={f:Ops.Ui.SubPatch,objName:"Ops.Ui.SubPatch"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizArrayChart +// +// ************************************************************** + +Ops.Ui.VizArrayChart = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array Numbers"), + inArrTitles = op.inArray("Titles"); + // inStyle = op.inSwitch("Style", ["Pie", "Graph"], "Pie"); + +op.setUiAttrib({ "height": 100, "width": 200, "resizable": true }); + +let max = -Number.MAX_VALUE; +let min = Number.MAX_VALUE; +const padding = 10; + +op.renderVizLayer = (ctx, layer) => +{ + ctx.fillStyle = "#222"; + ctx.fillRect( + layer.x, layer.y, + layer.width, layer.height); + + const arr = inArr.get(); + const arrNames = inArrTitles.get(); + if (!arr) + { + return; + } + + const colors = ["#7AC4E0", "#D183BF", "#9091D6", "#FFC395", "#F0D165", "#63A8E8", "#CF5D9D", "#66C984", "#D66AA6", "#515151"]; + + let size = layer.height / 2; + if (size != size || size < 0) return; + let currentAngle = 0; + let total = 0; + for (let i = 0; i < arr.length; i++) + { + total += parseFloat(Math.abs(arr[i])); + } + + let style = 0; + // if (inStyle.get() === "Graph")style = 1; + + if (style == 0) // PIE + { + const fontSize = size / 10; + ctx.font = "normal " + fontSize + "px sourceCodePro"; + + ctx.fillStyle = "#fff"; + ctx.fillText("total sum:" + Math.round(total * 100) / 100, layer.x + (size * 2) + padding * 2, layer.y + fontSize); + + for (let i = 0; i < arr.length; i++) + { + let name = ""; + if (arrNames && arrNames[i])name = arrNames[i]; + let perc = (Math.abs(arr[i]) / total); + let portionAngle = perc * 2 * Math.PI; + + ctx.beginPath(); + ctx.arc(layer.x + size, layer.y + size, Math.max(0, size - (padding * 2)), currentAngle, currentAngle + portionAngle); + currentAngle += portionAngle; + ctx.lineTo(layer.x + size, layer.y + size); + + ctx.fillStyle = colors[i % (colors.length - 1)]; + ctx.fill(); + + ctx.fillText(i + ": " + name + " " + Math.round(perc * 1000) / 10 + "%", layer.x + (size * 2) + padding * 2, layer.y + fontSize * (i + 1 + 1)); + } + + ctx.beginPath(); + ctx.fillStyle = "#222"; + ctx.arc(layer.x + size, layer.y + size, size / 5, 0, 2 * Math.PI); + ctx.fill(); + } + else if (style == 1) + { + let posy = 0; + for (let i = 0; i < arr.length; i++) + { + const perc = (Math.abs(arr[i]) / total); + const h = perc * layer.height; + + ctx.fillStyle = colors[i % (colors.length - 1)]; + ctx.fillRect( + layer.x + 21, layer.y + posy, + 12, h); + posy += h; + } + } +}; + + +}; + +Ops.Ui.VizArrayChart.prototype = new CABLES.Op(); +CABLES.OPS["04ff8596-d35a-4ad5-86d6-db6a8c2ce8f0"]={f:Ops.Ui.VizArrayChart,objName:"Ops.Ui.VizArrayChart"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizArrayTable +// +// ************************************************************** + +Ops.Ui.VizArrayTable = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inArr = op.inArray("Array"), + inOffset = op.inInt("Start Row", 0); + +op.setUiAttrib({ "height": 200, "width": 400, "resizable": true }); + +op.renderVizLayer = (ctx, layer) => +{ + ctx.fillStyle = "#222"; + ctx.fillRect(layer.x, layer.y, layer.width, layer.height); + + ctx.save(); + ctx.scale(layer.scale, layer.scale); + + ctx.font = "normal 10px sourceCodePro"; + ctx.fillStyle = "#ccc"; + + const arr = inArr.get() || []; + let stride = 1; + + if (!arr) + { + op.setUiAttrib({ "extendTitle": "" }); + return; + } + + op.setUiAttrib({ "extendTitle": "length: " + arr.length }); + + if (inArr.links.length > 0 && inArr.links[0].getOtherPort(inArr)) + stride = inArr.links[0].getOtherPort(inArr).uiAttribs.stride || 1; + + let lines = Math.floor(layer.height / layer.scale / 10 - 1); + let padding = 4; + let offset = inOffset.get() * stride; + + for (let i = offset; i < offset + lines * stride; i += stride) + { + if (i < 0) continue; + if (i + stride > arr.length) continue; + + ctx.fillStyle = "#666"; + + const lineNum = (i) / stride; + + if (lineNum >= 0) + ctx.fillText(lineNum, + layer.x / layer.scale + padding, + layer.y / layer.scale + 10 + (i - offset) / stride * 10 + padding); + + for (let s = 0; s < stride; s++) + { + let str = ""; + const v = arr[i + s]; + + ctx.fillStyle = "#ccc"; + + if (typeof v == "string") str = "\"" + v + "\""; + else if (CABLES.UTILS.isNumeric(v)) str = String(Math.round(v * 10000) / 10000); + else if (Array.isArray(v)) + { + let preview = "..."; + if (v.length == 0) preview = ""; + str = "[" + preview + "] (" + v.length + ")"; + } + else if (typeof v == "object") + { + try + { + str = JSON.stringify(v, true, 1); + } + catch (e) + { + str = "{???}"; + } + } + else if (v != v || v === undefined) + { + ctx.fillStyle = "#f00"; + str += String(v); + } + else + { + str += String(v); + } + + ctx.fillText(str, + layer.x / layer.scale + s * 100 + 50, + layer.y / layer.scale + 10 + (i - offset) / stride * 10 + padding); + } + } + + const gradHeight = 30; + + if (offset > 0) + { + const radGrad = ctx.createLinearGradient(0, layer.y / layer.scale + 5, 0, layer.y / layer.scale + gradHeight); + radGrad.addColorStop(0, "#222"); + radGrad.addColorStop(1, "rgba(34,34,34,0.0)"); + ctx.fillStyle = radGrad; + ctx.fillRect(layer.x / layer.scale, layer.y / layer.scale, 200000, gradHeight); + } + + if (offset + lines * stride < arr.length) + { + const radGrad = ctx.createLinearGradient(0, layer.y / layer.scale + layer.height / layer.scale - gradHeight + 5, 0, layer.y / layer.scale + layer.height / layer.scale - gradHeight + gradHeight); + radGrad.addColorStop(1, "#222"); + radGrad.addColorStop(0, "rgba(34,34,34,0.0)"); + ctx.fillStyle = radGrad; + ctx.fillRect(layer.x / layer.scale, layer.y / layer.scale + layer.height / layer.scale - gradHeight, 200000, gradHeight); + } + + ctx.restore(); +}; + + +}; + +Ops.Ui.VizArrayTable.prototype = new CABLES.Op(); +CABLES.OPS["af2eeaaf-ff86-4bfb-9a27-42f05160a5d8"]={f:Ops.Ui.VizArrayTable,objName:"Ops.Ui.VizArrayTable"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizBool +// +// ************************************************************** + +Ops.Ui.VizBool = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inNum = op.inBool("Boolean", 0), + outBool = op.outBoolNum("Bool"); + +op.setUiAttrib({ "height": 100, "width": 100, "resizable": true }); + +inNum.onChange = () => +{ + outBool.set(inNum.get()); +}; + +op.renderVizLayer = (ctx, layer) => +{ + ctx.fillStyle = "#222"; + ctx.fillRect( + layer.x, layer.y, + layer.width, layer.height); + + let isTrue = !!inNum.get(); + + let circle = new Path2D(); + let radius = Math.min(layer.height, layer.width) / 2.4 * 0.8; + if (radius < 0)radius = 0; + circle.arc(layer.x + layer.width / 2, layer.y + layer.height / 2, radius, 0, 2 * Math.PI, false); + + ctx.strokeStyle = "#555"; + ctx.lineWidth = 7 * layer.scale; + ctx.stroke(circle); + + if (isTrue) + { + if (op.uiAttribs.color)ctx.fillStyle = op.uiAttribs.color; + else ctx.fillStyle = "#ccc"; + + let circle = new Path2D(); + circle.arc(layer.x + layer.width / 2, layer.y + layer.height / 2, radius - (ctx.lineWidth / 2), 0, 2 * Math.PI, false); + ctx.fill(circle); + } +}; + + +}; + +Ops.Ui.VizBool.prototype = new CABLES.Op(); +CABLES.OPS["cf194306-175b-416a-b90e-31ff2192a190"]={f:Ops.Ui.VizBool,objName:"Ops.Ui.VizBool"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizGraph +// +// ************************************************************** + +Ops.Ui.VizGraph = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inNum1 = op.inFloat("Number 1"), + inNum2 = op.inFloat("Number 2"), + inNum3 = op.inFloat("Number 3"), + inNum4 = op.inFloat("Number 4"), + inNum5 = op.inFloat("Number 5"), + inNum6 = op.inFloat("Number 6"), + inNum7 = op.inFloat("Number 7"), + inNum8 = op.inFloat("Number 8"), + inReset = op.inTriggerButton("Reset"); + +op.setUiAttrib({ "height": 150, "resizable": true }); + +let buff = []; + +let max = -Number.MAX_VALUE; +let min = Number.MAX_VALUE; + +inNum1.onLinkChanged = + inNum2.onLinkChanged = + inNum3.onLinkChanged = + inNum4.onLinkChanged = + inNum5.onLinkChanged = + inNum6.onLinkChanged = + inNum7.onLinkChanged = + inNum8.onLinkChanged = + inReset.onTriggered = () => + { + max = -Number.MAX_VALUE; + min = Number.MAX_VALUE; + buff = []; + }; + +op.renderVizLayer = (ctx, layer) => +{ + const perf = CABLES.UI.uiProfiler.start("previewlayer graph"); + + const colors = [ + "#00ffff", + "#ffff00", + "#ff00ff", + "#0000ff", + "#00ff00", + "#ff0000", + "#ffffff", + "#888888", + ]; + + ctx.fillStyle = "#222"; + ctx.fillRect(layer.x, layer.y, layer.width, layer.height); + + for (let p = 0; p < op.portsIn.length; p++) + { + if (!op.portsIn[p].isLinked()) continue; + const newVal = op.portsIn[p].get(); + + max = Math.max(op.portsIn[p].get(), max); + min = Math.min(op.portsIn[p].get(), min); + + if (!buff[p]) buff[p] = []; + buff[p].push(newVal); + if (buff[p].length > 60) buff[p].shift(); + + const texSlot = 5; + const mulX = layer.width / 60; + + ctx.lineWidth = 2; + ctx.strokeStyle = "#555555"; + + ctx.beginPath(); + ctx.moveTo(layer.x, CABLES.map(0, min, max, layer.height, 0) + layer.y); + ctx.lineTo(layer.x + layer.width, CABLES.map(0, min, max, layer.height, 0) + layer.y); + ctx.stroke(); + + ctx.strokeStyle = colors[p]; + + ctx.beginPath(); + + for (let i = 0; i < buff[p].length; i++) + { + let y = buff[p][i]; + + y = CABLES.map(y, min, max, layer.height, 0); + y += layer.y; + if (i === 0)ctx.moveTo(layer.x, y); + else ctx.lineTo(layer.x + i * mulX, y); + } + + ctx.stroke(); + } + + ctx.fillStyle = "#888"; + ctx.fillText("max:" + Math.round(max * 100) / 100, layer.x + 10, layer.y + layer.height - 10); + ctx.fillText("min:" + Math.round(min * 100) / 100, layer.x + 10, layer.y + layer.height - 30); + + perf.finish(); +}; + + +}; + +Ops.Ui.VizGraph.prototype = new CABLES.Op(); +CABLES.OPS["13c54eb4-60ef-4b9c-8425-d52a431f5c87"]={f:Ops.Ui.VizGraph,objName:"Ops.Ui.VizGraph"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizLogger +// +// ************************************************************** + +Ops.Ui.VizLogger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inNum = op.inFloat("Number", 0); +const inString = op.inString("String", ""); +const inClear = op.inTriggerButton("Clear"); + +let lines = 10; +const arr = []; + +inClear.onTriggered = () => +{ + arr.length = 0; +}; + +inString.onChange = () => +{ + arr.push("" + inString.get()); +}; + +inNum.onChange = () => +{ + arr.push("" + inNum.get()); +}; + +op.setUiAttrib({ "height": 200, "width": 400, "resizable": true }); + +op.renderVizLayer = (ctx, layer) => +{ + ctx.fillStyle = "#222"; + ctx.fillRect( + layer.x, layer.y, + layer.width, layer.height); + + ctx.save(); + ctx.scale(layer.scale, layer.scale); + + ctx.font = "normal 10px sourceCodePro"; + ctx.fillStyle = "#ccc"; + + if (lines > 0) while (arr.length - 1 > lines) arr.shift(); + + lines = Math.floor(layer.height / layer.scale / 10 - 1); + let padding = 4; + + ctx.fillStyle = "#ccc"; + + for (let i = Math.min(lines, arr.length - 1); i > 0; i--) + { + ctx.fillText(arr[i], layer.x / layer.scale + padding, layer.y / layer.scale + 10 * i + padding); + } + + ctx.restore(); +}; + + +}; + +Ops.Ui.VizLogger.prototype = new CABLES.Op(); +CABLES.OPS["22b731fe-bc97-42fe-a550-92cf0cedb133"]={f:Ops.Ui.VizLogger,objName:"Ops.Ui.VizLogger"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizNumber +// +// ************************************************************** + +Ops.Ui.VizNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inNum = op.inFloat("Number", 0); +const outNum = op.outNumber("Result"); + +op.setUiAttrib({ "widthOnlyGrow": true }); + +inNum.onChange = () => +{ + let n = inNum.get(); + if (op.patch.isEditorMode()) + { + let str = ""; + if (n === null)str = "null"; + else if (n === undefined)str = "undefined"; + else + { + str = "" + Math.round(n * 10000) / 10000; + + if (str[0] != "-")str = " " + str; + } + + op.setUiAttribs({ "extendTitle": str }); + } + + outNum.set(n); +}; + + +}; + +Ops.Ui.VizNumber.prototype = new CABLES.Op(); +CABLES.OPS["2b60d12d-2884-4ad0-bda4-0caeb6882f5c"]={f:Ops.Ui.VizNumber,objName:"Ops.Ui.VizNumber"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizNumberBar +// +// ************************************************************** + +Ops.Ui.VizNumberBar = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inNum = op.inFloat("Number", 0); + // inDrawBar=op.inBool("Draw Bar",true), + // inDrawNUm=op.inBool("Draw Number",true); + +op.setUiAttrib({ "height": 100, "width": 200, "resizable": true }); + +let max = -Number.MAX_VALUE; +let min = Number.MAX_VALUE; + +inNum.onLinkChanged = () => +{ + max = -Number.MAX_VALUE; + min = Number.MAX_VALUE; +}; + +op.renderVizLayer = (ctx, layer) => +{ + ctx.fillStyle = "#222"; + ctx.fillRect( + layer.x, layer.y, + layer.width, layer.height); + + // if(inDrawBar.get()) + { + max = Math.max(max, inNum.get()); + min = Math.min(min, inNum.get()); + + if (op.uiAttribs.color)ctx.fillStyle = op.uiAttribs.color; + else ctx.fillStyle = "#555"; + + let a = CABLES.map(0, min, max, 0, layer.width); + let b = CABLES.map(inNum.get(), min, max, 0, layer.width); + + let xMin = Math.min(a, b); + let xMax = Math.max(a, b); + + ctx.fillRect( + xMin + layer.x, layer.y, + xMax - xMin, layer.height); + } + + // if(inDrawNUm.get()) + { + const padding = 10; + if (op.uiAttribs.color)ctx.fillStyle = "#fff"; + else ctx.fillStyle = "#ccc"; + + const fontSize = layer.height * 0.7; + ctx.font = "normal " + fontSize + "px sourceCodePro"; + ctx.fillText(Math.round(inNum.get() * 10000) / 10000, layer.x + padding, layer.y + fontSize); + } +}; + + +}; + +Ops.Ui.VizNumberBar.prototype = new CABLES.Op(); +CABLES.OPS["37575d2e-4ba6-4d2b-b00c-c503666867c5"]={f:Ops.Ui.VizNumberBar,objName:"Ops.Ui.VizNumberBar"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizObject +// +// ************************************************************** + +Ops.Ui.VizObject = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inObj = op.inObject("Object"), + inConsole = op.inTriggerButton("console log"); + +inConsole.setUiAttribs({ "hidePort": true }); + +op.setUiAttrib({ "height": 200, "width": 400, "resizable": true }); + +inObj.onLinkChanged = () => +{ + if (inObj.isLinked()) + { + const p = inObj.links[0].getOtherPort(inObj); + + op.setUiAttrib({ "extendTitle": p.uiAttribs.objType }); + } +}; + +inConsole.onTriggered = () => +{ + console.log(inObj.get()); +}; + +op.renderVizLayer = (ctx, layer) => +{ + ctx.fillStyle = "#222"; + ctx.fillRect(layer.x, layer.y, layer.width, layer.height); + + ctx.save(); + ctx.scale(layer.scale, layer.scale); + + ctx.font = "normal 10px sourceCodePro"; + ctx.fillStyle = "#ccc"; + const padding = 10; + + let obj = inObj.get(); + + let str = "???"; + + if (obj && obj.getInfo) + { + obj = obj.getInfo(); + } + + if (obj instanceof Element) + { + const o = {}; + + o.id = obj.getAttribute("id"); + o.classes = obj.classList.value; + o.innerText = obj.innerText; + o.tagName = obj.tagName; + + obj = o; + } + + if (obj && obj.constructor && obj.constructor.name != "Object") + { + // str = + "()\n" + str; + op.setUiAttribs({ "extendTitle": obj.constructor.name }); + } + + try + { + str = JSON.stringify(obj, false, 4); + + if (str == "{}" && obj && obj.constructor && obj.constructor.name != "Object") + { + str = "could not stringify object: " + obj.constructor.name + "\n"; + + if (obj) for (let i in obj) + { + str += "\n" + i + " (" + typeof obj[i] + ")"; + // console.log(i) + } + } + } + catch (e) + { + str = "object can not be displayed as string"; + } + + if (str === undefined)str = "undefined"; + if (str === null)str = "null"; + str = String(str); + let lines = str.split("\n"); + + for (let j = 0; j < lines.length; j++) + ctx.fillText(lines[j], layer.x / layer.scale + padding, layer.y / layer.scale + ((j + 1) * 12)); + + ctx.restore(); +}; + + +}; + +Ops.Ui.VizObject.prototype = new CABLES.Op(); +CABLES.OPS["d09bc53e-9f52-4872-94c7-4ef777512222"]={f:Ops.Ui.VizObject,objName:"Ops.Ui.VizObject"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizString +// +// ************************************************************** + +Ops.Ui.VizString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inStr = op.inString("String", ""); +const outNum = op.outString("Result"); + +op.setUiAttrib({ "widthOnlyGrow": true }); + +inStr.onChange = () => +{ + let str = inStr.get(); + if (op.patch.isEditorMode()) + { + if (str === null)str = "null"; + else if (str === undefined)str = "undefined"; + else str = "\"" + (String(str) || "") + "\""; + op.setTitle(str); + } + + outNum.set(inStr.get()); +}; + + +}; + +Ops.Ui.VizString.prototype = new CABLES.Op(); +CABLES.OPS["b04ff547-f557-4a54-a3ad-8a668fe1303d"]={f:Ops.Ui.VizString,objName:"Ops.Ui.VizString"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizStringLong +// +// ************************************************************** + +Ops.Ui.VizStringLong = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inStringEditor("String"), + inPos = op.inFloatSlider("Scroll", 0); + +op.setUiAttrib({ "height": 200, "width": 400, "resizable": true }); +inStr.ignoreValueSerialize = true; + +let lines = []; + +inStr.onLinkChanged = () => +{ + if (!inStr.isLinked()) + { + lines = []; + inStr.set(null); + } +}; + +inStr.onChange = () => +{ + if (inStr.get()) lines = inStr.get().split("\n"); + else lines = []; +}; + +op.renderVizLayer = (ctx, layer) => +{ + ctx.fillStyle = "#222"; + ctx.fillRect(layer.x, layer.y, layer.width, layer.height); + + if (!inStr.get()) return; + + ctx.save(); + ctx.scale(layer.scale, layer.scale); + + ctx.font = "normal 10px sourceCodePro"; + ctx.fillStyle = "#ccc"; + + let padding = 4; + + const lineHeight = 10; + + const numLines = Math.floor(layer.height / layer.scale / lineHeight); + + let offset = Math.floor(inPos.get() * lines.length); + + offset = Math.max(offset, 0); + offset = Math.min(offset, lines.length - numLines); + if (lines.length < numLines)offset = 0; + + const offsetLeft = ((offset + numLines + " ").length - 1) * 9.5; + + let indent = ""; + for (let i = 0; i < (offset + numLines + " ").length; i++) indent += " "; + + for (let i = offset; i < offset + numLines; i += 1) + { + if (i >= lines.length || i < 0) continue; + + ctx.fillStyle = "#888"; + + ctx.fillText(i, + layer.x / layer.scale + padding, + layer.y / layer.scale + lineHeight + (i - offset) * lineHeight + padding); + + ctx.fillStyle = "#ccc"; + + ctx.fillText(indent + lines[i], + layer.x / layer.scale + padding, + layer.y / layer.scale + lineHeight + (i - offset) * lineHeight + padding); + } + + const gradHeight = 30; + + if (offset > 0) + { + const radGrad = ctx.createLinearGradient(0, layer.y / layer.scale + 5, 0, layer.y / layer.scale + gradHeight); + radGrad.addColorStop(0, "#222"); + radGrad.addColorStop(1, "rgba(34,34,34,0.0)"); + ctx.fillStyle = radGrad; + ctx.fillRect(layer.x / layer.scale, layer.y / layer.scale, 200000, gradHeight); + } + + if (offset + numLines < lines.length) + { + const radGrad = ctx.createLinearGradient(0, layer.y / layer.scale + layer.height / layer.scale - gradHeight + 5, 0, layer.y / layer.scale + layer.height / layer.scale - gradHeight + gradHeight); + radGrad.addColorStop(1, "#222"); + radGrad.addColorStop(0, "rgba(34,34,34,0.0)"); + ctx.fillStyle = radGrad; + ctx.fillRect(layer.x / layer.scale, layer.y / layer.scale + layer.height / layer.scale - gradHeight, 200000, gradHeight); + } + + ctx.restore(); +}; + + +}; + +Ops.Ui.VizStringLong.prototype = new CABLES.Op(); +CABLES.OPS["b4c93fde-85c6-4c7e-9962-a6463a84838b"]={f:Ops.Ui.VizStringLong,objName:"Ops.Ui.VizStringLong"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizTexture +// +// ************************************************************** + +Ops.Ui.VizTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"viztex_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI samplerCube cubeMap;\nUNI float width;\nUNI float height;\nUNI float type;\nUNI float time;\n\nfloat LinearizeDepth(float d,float zNear,float zFar)\n{\n float z_n = 2.0 * d - 1.0;\n return 2.0 * zNear / (zFar + zNear - z_n * (zFar - zNear));\n}\n\nvoid main()\n{\n vec4 col=vec4(vec3(0.),0.0);\n\n vec4 colTex=texture(tex,texCoord);\n\n\n\n if(type==1.0)\n {\n vec4 depth=vec4(0.);\n vec2 localST=texCoord;\n localST.y = 1. - localST.y;\n\n localST.t = mod(localST.t*3.,1.);\n localST.s = mod(localST.s*4.,1.);\n\n #ifdef WEBGL2\n #define texCube texture\n #endif\n #ifdef WEBGL1\n #define texCube textureCube\n #endif\n\n// //Due to the way my depth-cubeMap is rendered, objects to the -x,y,z side is projected to the positive x,y,z side\n// //Inside where top/bottom is to be drawn?\n if (texCoord.s*4.> 1. && texCoord.s*4.<2.)\n {\n //Bottom (-y) quad\n if (texCoord.t*3. < 1.)\n {\n vec3 dir=vec3(localST.s*2.-1.,-1.,-localST.t*2.+1.);//Due to the (arbitrary) way I choose as up in my depth-viewmatrix, i her emultiply the latter coordinate with -1\n depth = texCube(cubeMap, dir);\n }\n //top (+y) quad\n else if (texCoord.t*3. > 2.)\n {\n vec3 dir=vec3(localST.s*2.-1.,1.,localST.t*2.-1.);//Get lower y texture, which is projected to the +y part of my cubeMap\n depth = texCube(cubeMap, dir);\n }\n else//Front (-z) quad\n {\n vec3 dir=vec3(localST.s*2.-1.,-localST.t*2.+1.,1.);\n depth = texCube(cubeMap, dir);\n }\n }\n// //If not, only these ranges should be drawn\n else if (texCoord.t*3. > 1. && texCoord.t*3. < 2.)\n {\n if (texCoord.x*4. < 1.)//left (-x) quad\n {\n vec3 dir=vec3(-1.,-localST.t*2.+1.,localST.s*2.-1.);\n depth = texCube(cubeMap, dir);\n }\n else if (texCoord.x*4. < 3.)//right (+x) quad (front was done above)\n {\n vec3 dir=vec3(1,-localST.t*2.+1.,-localST.s*2.+1.);\n depth = texCube(cubeMap, dir);\n }\n else //back (+z) quad\n {\n vec3 dir=vec3(-localST.s*2.+1.,-localST.t*2.+1.,-1.);\n depth = texCube(cubeMap, dir);\n }\n }\n // colTex = vec4(vec3(depth),1.);\n colTex = vec4(depth);\n }\n\n if(type==2.0)\n {\n float near = 0.1;\n float far = 50.;\n float depth = LinearizeDepth(colTex.r, near, far);\n colTex.rgb = vec3(depth);\n }\n\n if(colTex.r>1.0 || colTex.r<0.0)\n {\n float r=mod( time+colTex.r,1.0)*0.5+0.5;\n colTex.r=r;\n }\n if(colTex.g>1.0 || colTex.g<0.0)\n {\n float r=mod( time+colTex.g,1.0)*0.5+0.5;\n colTex.g=r;\n }\n if(colTex.b>1.0 || colTex.b<0.0)\n {\n float r=mod( time+colTex.b,1.0)*0.5+0.5;\n colTex.b=r;\n }\n\n\n outColor = mix(col,colTex,colTex.a);\n}\n\n","viztex_vert":"IN vec3 vPosition;\nIN vec2 attrTexCoord;\nOUT vec2 texCoord;\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\nvoid main()\n{\n texCoord=vec2(attrTexCoord.x,1.0-attrTexCoord.y);\n vec4 pos = vec4( vPosition, 1. );\n mat4 mvMatrix=viewMatrix * modelMatrix;\n gl_Position = projMatrix * mvMatrix * pos;\n}",}; +const + inTex = op.inTexture("Texture In"), + inShowInfo = op.inBool("Show Info", false), + outTex = op.outTexture("Texture Out"), + outInfo = op.outString("Info"); + +op.setUiAttrib({ "height": 150, "resizable": true }); + +const timer = new CABLES.Timer(); +timer.play(); + +inTex.onChange = () => +{ + const t = inTex.get(); + + outTex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + outTex.set(t); +}; + +op.renderVizLayer = (ctx, layer) => +{ + const port = inTex; + const texSlot = 5; + const texSlotCubemap = texSlot + 1; + + const perf = CABLES.UI.uiProfiler.start("previewlayer texture"); + const cgl = port.parent.patch.cgl; + + if (!this._emptyCubemap) this._emptyCubemap = CGL.Texture.getEmptyCubemapTexture(cgl); + port.parent.patch.cgl.profileData.profileTexPreviews++; + + const portTex = port.get() || CGL.Texture.getEmptyTexture(cgl); + + if (!this._mesh) + { + const geom = new CGL.Geometry("preview op rect"); + geom.vertices = [1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0]; + geom.texCoords = [ + 1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 0.0, 0.0]; + geom.verticesIndices = [0, 1, 2, 3, 1, 2]; + this._mesh = new CGL.Mesh(cgl, geom); + } + if (!this._shader) + { + this._shader = new CGL.Shader(cgl, "glpreviewtex"); + this._shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); + this._shader.setSource(attachments.viztex_vert, attachments.viztex_frag); + this._shaderTexUniform = new CGL.Uniform(this._shader, "t", "tex", texSlot); + this._shaderTexCubemapUniform = new CGL.Uniform(this._shader, "tc", "cubeMap", texSlotCubemap); + + this._shaderTexUniformW = new CGL.Uniform(this._shader, "f", "width", portTex.width); + this._shaderTexUniformH = new CGL.Uniform(this._shader, "f", "height", portTex.height); + this._shaderTypeUniform = new CGL.Uniform(this._shader, "f", "type", 0); + this._shaderTimeUniform = new CGL.Uniform(this._shader, "f", "time", 0); + } + + cgl.pushPMatrix(); + const sizeTex = [portTex.width, portTex.height]; + const small = port.parent.patch.cgl.canvasWidth > sizeTex[0] && port.parent.patch.cgl.canvasHeight > sizeTex[1]; + + if (small) + { + mat4.ortho(cgl.pMatrix, 0, port.parent.patch.cgl.canvasWidth, port.parent.patch.cgl.canvasHeight, 0, 0.001, 11); + } + else mat4.ortho(cgl.pMatrix, -1, 1, 1, -1, 0.001, 11); + + const oldTex = cgl.getTexture(texSlot); + const oldTexCubemap = cgl.getTexture(texSlotCubemap); + + let texType = 0; + if (!portTex) return; + if (portTex.cubemap) texType = 1; + if (portTex.textureType == CGL.Texture.TYPE_DEPTH) texType = 2; + + if (texType == 0 || texType == 2) + { + cgl.setTexture(texSlot, portTex.tex); + cgl.setTexture(texSlotCubemap, this._emptyCubemap.cubemap, cgl.gl.TEXTURE_CUBE_MAP); + } + else if (texType == 1) + { + cgl.setTexture(texSlotCubemap, portTex.cubemap, cgl.gl.TEXTURE_CUBE_MAP); + } + + timer.update(); + this._shaderTimeUniform.setValue(timer.get()); + + this._shaderTypeUniform.setValue(texType); + let s = [port.parent.patch.cgl.canvasWidth, port.parent.patch.cgl.canvasHeight]; + + cgl.gl.clearColor(0, 0, 0, 0); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + + cgl.pushModelMatrix(); + if (small) + { + s = sizeTex; + mat4.translate(cgl.mMatrix, cgl.mMatrix, [sizeTex[0] / 2, sizeTex[1] / 2, 0]); + mat4.scale(cgl.mMatrix, cgl.mMatrix, [sizeTex[0] / 2, sizeTex[1] / 2, 0]); + } + this._mesh.render(this._shader); + cgl.popModelMatrix(); + + if (texType == 0) cgl.setTexture(texSlot, oldTex); + if (texType == 1) cgl.setTexture(texSlotCubemap, oldTexCubemap); + + cgl.popPMatrix(); + cgl.resetViewPort(); + + const sizeImg = [layer.width, layer.height]; + + const stretch = false; + if (!stretch) + { + if (portTex.width > portTex.height) sizeImg[1] = layer.width * sizeTex[1] / sizeTex[0]; + else + { + sizeImg[1] = layer.width * (sizeTex[1] / sizeTex[0]); + + if (sizeImg[1] > layer.height) + { + const r = layer.height / sizeImg[1]; + sizeImg[0] *= r; + sizeImg[1] *= r; + } + } + } + + const scaledDown = sizeImg[0] > sizeTex[0] && sizeImg[1] > sizeTex[1]; + + ctx.imageSmoothingEnabled = !small || !scaledDown; + + if (!ctx.imageSmoothingEnabled) + { + ctx.fillStyle = "#ffffff"; + ctx.fillRect(layer.x, layer.y - 10, 10, 10); + ctx.fillStyle = "#000000"; + ctx.fillRect(layer.x, layer.y - 10, 5, 5); + ctx.fillRect(layer.x + 5, layer.y - 10 + 5, 5, 5); + } + + let numX = (10 * layer.width / layer.height); + let stepY = (layer.height / 10); + let stepX = (layer.width / numX); + for (let x = 0; x < numX; x++) + for (let y = 0; y < 10; y++) + { + if ((x + y) % 2 == 0)ctx.fillStyle = "#333333"; + else ctx.fillStyle = "#393939"; + ctx.fillRect(layer.x + stepX * x, layer.y + stepY * y, stepX, stepY); + } + + ctx.fillStyle = "#222"; + const borderLeft = (layer.width - sizeImg[0]) / 2; + const borderTop = (layer.height - sizeImg[1]) / 2; + ctx.fillRect( + layer.x, layer.y, + borderLeft, (layer.height) + ); + ctx.fillRect( + layer.x + sizeImg[0] + borderLeft, layer.y, + borderLeft, (layer.height) + ); + ctx.fillRect( + layer.x, layer.y, + layer.width, borderTop + ); + ctx.fillRect( + layer.x, layer.y + sizeImg[1] + borderTop, + layer.width, borderTop + ); + + if (sizeTex[1] == 1) + ctx.drawImage(cgl.canvas, + 0, 0, + s[0], s[1], + layer.x, layer.y, + layer.width, layer.height * 5);// workaround filtering problems + if (sizeTex[0] == 1) + ctx.drawImage(cgl.canvas, + 0, 0, + s[0], s[1], + layer.x, layer.y, + layer.width * 5, layer.height); // workaround filtering problems + else + ctx.drawImage(cgl.canvas, + 0, 0, + s[0], s[1], + layer.x + (layer.width - sizeImg[0]) / 2, layer.y + (layer.height - sizeImg[1]) / 2, + sizeImg[0], sizeImg[1]); + + let info = "unknown"; + + if (port.get() && port.get().getInfoOneLine) info = port.get().getInfoOneLine(); + + if (inShowInfo.get()) + { + ctx.save(); + ctx.scale(layer.scale, layer.scale); + ctx.font = "normal 10px sourceCodePro"; + ctx.fillStyle = "#000"; + ctx.fillText(info, layer.x / layer.scale + 5 + 0.75, (layer.y + layer.height) / layer.scale - 5 + 0.75); + ctx.fillStyle = "#fff"; + ctx.fillText(info, layer.x / layer.scale + 5, (layer.y + layer.height) / layer.scale - 5); + ctx.restore(); + } + + outInfo.set(info); + + cgl.gl.clearColor(0, 0, 0, 0); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + + perf.finish(); +}; + + +}; + +Ops.Ui.VizTexture.prototype = new CABLES.Op(); +CABLES.OPS["4ea2d7b0-ca74-45db-962b-4d1965ac20c0"]={f:Ops.Ui.VizTexture,objName:"Ops.Ui.VizTexture"}; + + + + +// ************************************************************** +// +// Ops.Ui.VizTextureTable +// +// ************************************************************** + +Ops.Ui.VizTextureTable = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inTex = op.inTexture("Texture"); + +op.setUiAttrib({ "height": 200, "width": 380, "resizable": true }); + +let pixelData = null; +let lastWidth; +let lastHeight; +let fb; +let lastFloatingPoint; +let lastRead = 0; +const arr = []; + +inTex.onLinkChanged = () => +{ + op.setUiAttrib({ "extendTitle": "" }); +}; + +op.renderVizLayer = (ctx, layer) => +{ + const + realTexture = inTex.get(), + gl = op.patch.cgl.gl; + + ctx.fillStyle = "#222"; + ctx.fillRect(layer.x, layer.y, layer.width, layer.height); + + if (!realTexture) return; + + let lines = Math.floor(layer.height / layer.scale / 10 - 1); + + ctx.save(); + ctx.scale(layer.scale, layer.scale); + + ctx.font = "normal 10px sourceCodePro"; + ctx.fillStyle = "#ccc"; + + if (!fb) fb = gl.createFramebuffer(); + + let channels = gl.RGBA; + let numChannels = 4; + + let texChanged = true; + let channelType = gl.UNSIGNED_BYTE; + + if (texChanged) + { + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, realTexture.tex, 0 + ); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + let isFloatingPoint = realTexture.isFloatingPoint(); + if (isFloatingPoint) channelType = gl.FLOAT; + + if ( + lastFloatingPoint != isFloatingPoint || + lastWidth != realTexture.width || + lastHeight != realTexture.height) + { + const size = realTexture.width * realTexture.height * numChannels; + if (isFloatingPoint) pixelData = new Float32Array(size); + else pixelData = new Uint8Array(size); + + lastFloatingPoint = isFloatingPoint; + lastWidth = realTexture.width; + lastHeight = realTexture.height; + } + + texChanged = false; + } + + let texRows = Math.max(1, Math.ceil(lines / realTexture.width)); + texRows = Math.min(texRows, realTexture.height); + let readW = realTexture.width; + if (lines / realTexture.width < 1)readW = realTexture.width * lines / realTexture.width; + const readH = texRows; + let readPixels = false; + + if (performance.now() - lastRead > 100)readPixels = true; + + if (readPixels) + { + lastRead = performance.now(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + gl.readPixels( + 0, + realTexture.height - texRows, + readW, + readH, + channels, + channelType, + pixelData + ); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + } + + arr.length = pixelData.length; + let stride = 4; + let padding = 4; + let lineHeight = 10; + let isFp = realTexture.isFloatingPoint(); + + for (let x = 0; x < readW; x++) + for (let y = 0; y < readH; y++) + for (let s = 0; s < stride; s++) + arr[(x + (y * readW)) * stride + s] = pixelData[((x) + ((readH - y - 1) * readW)) * stride + s]; + + if (realTexture && realTexture.getInfoOneLine) + op.setUiAttrib({ "extendTitle": realTexture.getInfoOneLine() }); + + for (let y = 0; y < readH; y++) + for (let x = 0; x < readW; x++) + { + const count = x + y * readW; + + const i = x * stride + (y * readW * stride); + + ctx.fillStyle = "#666"; + + ctx.fillText(i / stride, + layer.x / layer.scale + padding, + layer.y / layer.scale + lineHeight + i / stride * lineHeight + padding); + + const idx = count * stride; + + if (inTex.get().isFloatingPoint()) ctx.fillStyle = "rgba(" + arr[idx + 0] * 255 + "," + arr[idx + 1] * 255 + "," + arr[idx + 2] * 255 + "," + arr[idx + 3] * 255 + ")"; + else ctx.fillStyle = "rgba(" + arr[idx + 0] + "," + arr[idx + 1] + "," + arr[idx + 2] + "," + arr[idx + 3] + ")"; + + ctx.fillRect( + layer.x / layer.scale + padding + 25, + layer.y / layer.scale + lineHeight + count * lineHeight + padding - 7, + 15, 8); + + ctx.fillStyle = "#ccc"; + + for (let s = 0; s < stride; s++) + { + let v = arr[i + s]; + let str = "" + v; + + if (!isFp)v /= 255; + str = String(Math.round(v * 10000) / 10000); + + ctx.fillText(str, layer.x / layer.scale + s * 60 + 70, layer.y / layer.scale + 10 + (i / stride) * 10 + padding); + } + } + + const gradHeight = 30; + + if (lines < readH * readW) + { + const radGrad = ctx.createLinearGradient(0, layer.y / layer.scale + layer.height / layer.scale - gradHeight + 5, 0, layer.y / layer.scale + layer.height / layer.scale - gradHeight + gradHeight); + radGrad.addColorStop(1, "#222"); + radGrad.addColorStop(0, "rgba(34,34,34,0.0)"); + ctx.fillStyle = radGrad; + ctx.fillRect(layer.x / layer.scale, layer.y / layer.scale + layer.height / layer.scale - gradHeight, 200000, gradHeight); + } + + ctx.restore(); +}; + + +}; + +Ops.Ui.VizTextureTable.prototype = new CABLES.Op(); +CABLES.OPS["0e5cde34-35cd-440d-b8cd-f78e1b5b7722"]={f:Ops.Ui.VizTextureTable,objName:"Ops.Ui.VizTextureTable"}; + + + + +// ************************************************************** +// +// Ops.Value.Boolean +// +// ************************************************************** + +Ops.Value.Boolean = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v = op.inValueBool("value", false), + result = op.outBoolNum("result"); + +result.set(false); +v.onChange = exec; + +function exec() +{ + if (result.get() != v.get()) result.set(v.get()); +} + + +}; + +Ops.Value.Boolean.prototype = new CABLES.Op(); +CABLES.OPS["83e2d74c-9741-41aa-a4d7-1bda4ef55fb3"]={f:Ops.Value.Boolean,objName:"Ops.Value.Boolean"}; + + + + +// ************************************************************** +// +// Ops.Value.ColorValue +// +// ************************************************************** + +Ops.Value.ColorValue = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const r = op.inValueSlider("r", Math.random()); +const g = op.inValueSlider("g", Math.random()); +const b = op.inValueSlider("b", Math.random()); +r.setUiAttribs({ "colorPick": true }); +const a = op.inValueSlider("a"); + +const outR = op.outNumber("outr"); +const outG = op.outNumber("outg"); +const outB = op.outNumber("outb"); +const outA = op.outNumber("outa"); +const outHex = op.outNumber("Hex", "000000"); +const arrOut = op.outArray("Array"); + +r.onChange = g.onChange = b.onChange = a.onChange = exec; + +/** + * Float [0..1] -> Hex String [00..FF] + */ +function floatToHex(f) +{ + let s = Math.round(f * 255).toString(16); + if (s.length === 1) + { + s = "0" + s; + } + return s.toUpperCase(); +} + +function exec() +{ + outR.set(r.get()); + outG.set(g.get()); + outB.set(b.get()); + outA.set(a.get()); + + let hex = floatToHex(r.get()) + floatToHex(g.get()) + floatToHex(b.get()); + outHex.set(hex); + + arrOut.set([r.get(), g.get(), b.get()]); +} + +exec(); + + +}; + +Ops.Value.ColorValue.prototype = new CABLES.Op(); +CABLES.OPS["7caa37c8-f2a7-49f2-a29c-96af362abca0"]={f:Ops.Value.ColorValue,objName:"Ops.Value.ColorValue"}; + + + + +// ************************************************************** +// +// Ops.Value.DelayBooleanSimple +// +// ************************************************************** + +Ops.Value.DelayBooleanSimple = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val = op.inFloat("Value"), + delTrue = op.inFloat("Delay True", 1), + delFalse = op.inFloat("Delay False", 1), + outVal = op.outBoolNum("Out Value"); + +let timeout = -1; + +val.onChange = + delFalse.onChange = + delTrue.onChange = update; + +function update() +{ + clearTimeout(timeout); + let v = val.get(); + + let delay = 1; + if (v) delay = delTrue.get() * 1000; + else delay = delFalse.get() * 1000; + + timeout = setTimeout(function () + { + outVal.set(v); + }, delay); +} + + +}; + +Ops.Value.DelayBooleanSimple.prototype = new CABLES.Op(); +CABLES.OPS["4516be54-9077-490f-a094-83696b9011ba"]={f:Ops.Value.DelayBooleanSimple,objName:"Ops.Value.DelayBooleanSimple"}; + + + + +// ************************************************************** +// +// Ops.Value.DelayNumberSimple +// +// ************************************************************** + +Ops.Value.DelayNumberSimple = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val = op.inValue("Value"), + de = op.inValue("Delay", 1), + outVal = op.outNumber("Out Value"); + +let timeout = -1; + +val.onChange = update; +de.onChange = update; + +function update() +{ + clearTimeout(timeout); + let v = val.get(); + timeout = setTimeout(function () + { + outVal.set(v); + }, de.get() * 1000); +} + + +}; + +Ops.Value.DelayNumberSimple.prototype = new CABLES.Op(); +CABLES.OPS["89ea7e9c-0dfb-4e1e-8e61-4e79112ee533"]={f:Ops.Value.DelayNumberSimple,objName:"Ops.Value.DelayNumberSimple"}; + + + + +// ************************************************************** +// +// Ops.Value.DelayStringSimple +// +// ************************************************************** + +Ops.Value.DelayStringSimple = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val=op.inString("Value"), + de=op.inValue("Delay",1), + outVal=op.outString("Out Value"); + +let timeout=-1; + +val.onChange= + de.onChange=update; + +function update() +{ + clearTimeout(timeout); + var v=val.get(); + timeout=setTimeout(function() + { + outVal.set(v); + },de.get()*1000); + +} + + +}; + +Ops.Value.DelayStringSimple.prototype = new CABLES.Op(); +CABLES.OPS["0e3c0986-071e-4c98-be84-7f17306490c6"]={f:Ops.Value.DelayStringSimple,objName:"Ops.Value.DelayStringSimple"}; + + + + +// ************************************************************** +// +// Ops.Value.DelayedValue +// +// ************************************************************** + +Ops.Value.DelayedValue = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("Update"), + v = op.inValue("Value", 0), + delay = op.inValue("Delay", 0.5), + result = op.outNumber("Result", 0), + clear = op.inValueBool("Clear on Change", false); + +const anim = new CABLES.Anim(); +anim.createPort(op, "easing", function () {}).set("absolute"); + +exe.onTriggered = function () +{ + result.set(anim.getValue(op.patch.freeTimer.get()) || 0); +}; + +v.onChange = function () +{ + const current = anim.getValue(op.patch.freeTimer.get()); + const t = op.patch.freeTimer.get(); + + if (clear.get()) anim.clear(t); + + anim.setValue(t + delay.get(), v.get()); + + let lastKey = 0; + for (let i = 0; i < anim.keys.length; i++) + { + if (anim.keys[i].time < t)lastKey = i; + } + if (lastKey > 2) anim.keys.splice(0, lastKey); +}; + + +}; + +Ops.Value.DelayedValue.prototype = new CABLES.Op(); +CABLES.OPS["8e7741e0-0b1b-40f3-a62c-ac8a8828dffb"]={f:Ops.Value.DelayedValue,objName:"Ops.Value.DelayedValue"}; + + + + +// ************************************************************** +// +// Ops.Value.FileInput_v2 +// +// ************************************************************** + +Ops.Value.FileInput_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inFile = op.inUrl("File"); +const outPath = op.outString("URL"); + +inFile.onChange = function () +{ + const url = op.patch.getFilePath(String(inFile.get())); + outPath.set(url); +}; + + +}; + +Ops.Value.FileInput_v2.prototype = new CABLES.Op(); +CABLES.OPS["3f20a79f-a35f-4a4d-b2f3-c46973a4531f"]={f:Ops.Value.FileInput_v2,objName:"Ops.Value.FileInput_v2"}; + + + + +// ************************************************************** +// +// Ops.Value.FilterValidNumber +// +// ************************************************************** + +Ops.Value.FilterValidNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inNumber=op.inFloat("Number",0), + inZero=op.inBool("Invalid when 0",false), + inSmaller=op.inBool("Invalid when <0",false), + + outNum=op.outNumber("Last Valid Number"), + outValid=op.outBool("Is Valid"); + +inZero.onChange= +inSmaller.onChange= +inNumber.onChange= + update; + + +function update() +{ + const num=inNumber.get(); + + var r=true; + + if(num===null || num===undefined || num!=num) r=false; + if(inZero.get() && num===0) r=false; + if(inSmaller.get() && num<0) r=false; + + if(r) outNum.set(num); + + outValid.set(r); +} + +}; + +Ops.Value.FilterValidNumber.prototype = new CABLES.Op(); +CABLES.OPS["5a4db4ef-33d2-4131-9825-aa926f1f5a98"]={f:Ops.Value.FilterValidNumber,objName:"Ops.Value.FilterValidNumber"}; + + + + +// ************************************************************** +// +// Ops.Value.GateNumber +// +// ************************************************************** + +Ops.Value.GateNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const valueInPort = op.inValue("Value In", 0); +const passThroughPort = op.inValueBool("Pass Through"); +const valueOutPort = op.outNumber("Value Out"); + +valueInPort.onChange = update; +passThroughPort.onChange = update; + +valueInPort.changeAlways = true; +valueOutPort.changeAlways = true; + +function update() +{ + if (passThroughPort.get()) + { + valueOutPort.set(valueInPort.get()); + } +} + + +}; + +Ops.Value.GateNumber.prototype = new CABLES.Op(); +CABLES.OPS["594105c8-1fdb-4f3c-bbd5-29b9ad6b33e0"]={f:Ops.Value.GateNumber,objName:"Ops.Value.GateNumber"}; + + + + +// ************************************************************** +// +// Ops.Value.Integer +// +// ************************************************************** + +Ops.Value.Integer = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + input = op.inInt("Integer",0), + output = op.outNumber("Number out"); + +input.onChange=function() +{ + output.set(Math.floor(input.get())); +} + +}; + +Ops.Value.Integer.prototype = new CABLES.Op(); +CABLES.OPS["17bc01d7-04ad-4aab-b88b-bb09744c4a69"]={f:Ops.Value.Integer,objName:"Ops.Value.Integer"}; + + + + +// ************************************************************** +// +// Ops.Value.MaximumSafeInteger +// +// ************************************************************** + +Ops.Value.MaximumSafeInteger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +op.outNumber("Max Int", Number.MAX_SAFE_INTEGER); + + +}; + +Ops.Value.MaximumSafeInteger.prototype = new CABLES.Op(); +CABLES.OPS["0efefbb7-461c-4a34-b7fd-28b89b0ceb3f"]={f:Ops.Value.MaximumSafeInteger,objName:"Ops.Value.MaximumSafeInteger"}; + + + + +// ************************************************************** +// +// Ops.Value.MinimumSafeInteger +// +// ************************************************************** + +Ops.Value.MinimumSafeInteger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +op.outNumber("Min Int", Number.MIN_SAFE_INTEGER); + + +}; + +Ops.Value.MinimumSafeInteger.prototype = new CABLES.Op(); +CABLES.OPS["d74e5528-8049-4cbc-984b-1221532a2fd4"]={f:Ops.Value.MinimumSafeInteger,objName:"Ops.Value.MinimumSafeInteger"}; + + + + +// ************************************************************** +// +// Ops.Value.Number +// +// ************************************************************** + +Ops.Value.Number = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v = op.inValueFloat("value"), + result = op.outNumber("result"); + +v.onChange = exec; + +function exec() +{ + result.set(Number(v.get())); +} + + +}; + +Ops.Value.Number.prototype = new CABLES.Op(); +CABLES.OPS["8fb2bb5d-665a-4d0a-8079-12710ae453be"]={f:Ops.Value.Number,objName:"Ops.Value.Number"}; + + + + +// ************************************************************** +// +// Ops.Value.NumberSequence +// +// ************************************************************** + +Ops.Value.NumberSequence = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const NUM_PORTS = 4; +const inPort = op.inValue("In Value"); +const outTrigger = op.outTrigger("Value Changed"); + +const outPorts = []; +for (let i = 0; i < NUM_PORTS; i++) +{ + outPorts.push(op.outNumber("Out Value " + i)); +} + +// change listener +inPort.onChange = function () +{ + const inValue = inPort.get(); + for (let i = 0; i < NUM_PORTS; i++) + { + outPorts[i].set(inValue); + } + outTrigger.trigger(); +}; + + +}; + +Ops.Value.NumberSequence.prototype = new CABLES.Op(); +CABLES.OPS["33b08c9a-639a-4edc-8908-fa4df58a4b51"]={f:Ops.Value.NumberSequence,objName:"Ops.Value.NumberSequence"}; + + + + +// ************************************************************** +// +// Ops.Value.NumberSwitchBoolean +// +// ************************************************************** + +Ops.Value.NumberSwitchBoolean = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inBool = op.inValueBool("Boolean"), + valFalse = op.inValue("Value false", 0), + valTrue = op.inValue("Value true", 1), + outVal = op.outNumber("Result"); + +inBool.onChange = + valTrue.onChange = + valFalse.onChange = update; + +op.setPortGroup("Output Values", [valTrue, valFalse]); + +function update() +{ + if (inBool.get()) outVal.set(valTrue.get()); + else outVal.set(valFalse.get()); +} + + +}; + +Ops.Value.NumberSwitchBoolean.prototype = new CABLES.Op(); +CABLES.OPS["637c5fa8-840d-4535-96ab-3d27b458a8ba"]={f:Ops.Value.NumberSwitchBoolean,objName:"Ops.Value.NumberSwitchBoolean"}; + + + + +// ************************************************************** +// +// Ops.Value.Preset +// +// ************************************************************** + +Ops.Value.Preset = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + dataPort = op.inString("data", ""), + setsPort = op.inString("sets", ""), + id = op.inString("presetid", CABLES.shortId()), + + inInterPolate = op.inSwitch("Interpolation", ["None", "xfade", "a-b"], "None"), // "a..b","a..c" + + presetA = op.inFloat("Preset A", "0"), + presetB = op.inFloat("Preset B", "1"), + presetFade = op.inFloatSlider("Fade", 0.0), + + presetNames = op.inDropDown("Preset", []), + presetCreate = op.inTriggerButton("Create new"), + presetUpdate = op.inTriggerButton("Update"), + move = op.inUiTriggerButtons("move", ["↑", "↓"]), + + presetDelete = op.inTriggerButton("Delete"), + presetRename = op.inTriggerButton("Rename"), + + addPort = op.addOutPort(new CABLES.Port(op, "Create Variable", CABLES.OP_PORT_TYPE_DYNAMIC)), + outNum = op.outNumber("Num Presets", 0), + outNumCurrentPreset = op.outNumber("current Preset", 0), + outDbgData = op.outArray("dbg_data"), + outDbgSets = op.outArray("dbg_sets"); +let data = []; +let presets = []; +const valuePorts = []; +let interpolate = 0; + +presetB.changeAlways = true; +presetA.changeAlways = true; + +op.setPortGroup("Manage Presets", [presetCreate, presetUpdate, presetDelete, presetNames, move, presetRename]); +dataPort.setUiAttribs({ "hideParam": true, "hidePort": true }); +id.setUiAttribs({ "hideParam": true, "hidePort": true }); +setsPort.setUiAttribs({ "hideParam": true, "hidePort": true }); +presetCreate.setUiAttribs({ "hidePort": true }); +presetUpdate.setUiAttribs({ "hidePort": true }); +presetDelete.setUiAttribs({ "hidePort": true }); +presetRename.setUiAttribs({ "hidePort": true }); +presetNames.setUiAttribs({ "showIndex": true }); +presetCreate.setUiAttribs({ "buttonTitle": "Create New Preset" }); +presetDelete.setUiAttribs({ "buttonTitleClass": "button-small" }); +presetRename.setUiAttribs({ "buttonTitleClass": "button-small" }); + +presetNames.onChange = updatePreset; +inInterPolate.onChange = updateInterpolation; +presetA.onChange = + presetB.onChange = + presetFade.onChange = updateFade; + +updateInterpolation(); +updateDropdown(); +updatePreset(); +updateButtons(); + +function movePreset(from, to) +{ + const f = presets.splice(from, 1)[0]; + presets.splice(to, 0, f); +} + +move.onTriggered = function (which) +{ + const current = presetNames.get(); + const idx = presetNames.uiAttribs.values.indexOf(current); + + if (which == "↓") movePreset(idx, idx + 1); + if (which == "↑") movePreset(idx, Math.max(0, idx - 1)); + + updateDropdown(); + updatePreset(); +}; + +op.init = function () +{ + if (presets.length > 0 && data.length == 0) + { + op.logError("it happened again!!"); + + // this happened only once for now, find out how to reproduce it!!! + const keys = Object.keys(presets[0].values); + + for (let i = 0; i < keys.length; i++) + { + data.push( + { + "varname": keys[i], + "type": 0, + "title": keys[i] + + }); + } + saveData(); + } +}; + +function updateInterpolation() +{ + const ip = inInterPolate.get(); + if (ip === "None") + { + interpolate = 0; + presetA.setUiAttribs({ "greyout": true }); + presetB.setUiAttribs({ "greyout": true }); + presetFade.setUiAttribs({ "greyout": true }); + } + else if (ip === "xfade") + { + interpolate = 1; + presetA.setUiAttribs({ "greyout": false }); + presetB.setUiAttribs({ "greyout": false }); + presetFade.setUiAttribs({ "greyout": false }); + } + else if (ip === "a-b") + { + interpolate = 2; + presetA.setUiAttribs({ "greyout": false }); + presetB.setUiAttribs({ "greyout": true }); + presetFade.setUiAttribs({ "greyout": true }); + } + + op.setUiAttrib({ "extendTitle": ip }); + + if (interpolate !== 0) updateFade(); + else updatePreset(); +} + +function updateFade() +{ + if (interpolate === 0) return; + + let fade = 0; + let idxa = 0; + let idxb = 0; + + if (interpolate === 2) // a-b + { + const pr = presetA.get(); + idxa = Math.floor(pr); + idxb = Math.ceil(pr); + fade = pr % 1; + + if (idxa >= presets.length) idxa = presets.length - 1; + if (idxb >= presets.length) idxb = presets.length - 1; + } + else if (interpolate === 1) // xfade + { + fade = presetFade.get(); + idxa = Math.floor(presetA.get()); + idxb = Math.floor(presetB.get()); + } + + const a = presets[idxa]; + const b = presets[idxb]; + + if (!a || !b) + { + op.warn("preset not found"); + return; + } + + // todo: cache variable, so no string lookup needed every time... + + for (const i in a.values) + { + const ip = a.values[i] + (b.values[i] - a.values[i]) * fade; + op.patch.setVarValue(i, ip); + } +} + +function saveData() +{ + savePresets(); +} + +function savePresets() +{ + dataPort.set(JSON.stringify(data)); + + setsPort.set(JSON.stringify(presets)); + outNum.set(presets.length); + setDebugOutput(); +} + +function setPresetValues(preset) +{ + preset.values = preset.values || {}; + + for (let i = 0; i < valuePorts.length; i++) + preset.values[valuePorts[i].name] = valuePorts[i].value; + + return preset; +} + +function updateButtons() +{ + presetDelete.setUiAttribs({ "greyout": presetNames.uiAttribs.values.length == 0 }); + presetUpdate.setUiAttribs({ "greyout": presetNames.uiAttribs.values.length == 0 }); + presetRename.setUiAttribs({ "greyout": presetNames.uiAttribs.values.length == 0 }); + + move.setUiAttribs({ "greyout": presetNames.uiAttribs.values.length == 0 }); + + const preset = getPreset(presetNames.get()); + if (preset) + { + presetDelete.setUiAttribs({ "buttonTitle": "Delete " + preset.name }); + presetUpdate.setUiAttribs({ "buttonTitle": "Update " + preset.name }); + presetRename.setUiAttribs({ "buttonTitle": "Rename " + preset.name }); + } +} + +function updateDropdown() +{ + presetNames.uiAttribs.values.length = 0; + for (let i = 0; i < presets.length; i++) + presetNames.uiAttribs.values.push(presets[i].name); + + updateButtons(); + savePresets(); + setDebugOutput(); +} + +function getPreset(name) +{ + for (let i = 0; i < presets.length; i++) + if (presets[i] && presets[i].name == name) + return presets[i]; +} + +setsPort.onChange = function () +{ + presets = JSON.parse(setsPort.get()); + outNum.set(presets.length); + updateDropdown(); + setsPort.onChange = null; +}; + +function updatePreset() +{ + const preset = getPreset(presetNames.get()); + + if (!preset) return; + + const varnames = Object.keys(preset.values); + + for (let i = 0; i < varnames.length; i++) + { + const p = op.getPort(varnames[i]); + if (p) + { + p.set(preset.values[varnames[i]]); + if (interpolate === 0)p.forceChange(); + } + } + + if (interpolate !== 0) updateFade(); + + updateButtons(); + op.refreshParams(); +} + +presetUpdate.onTriggered = function () +{ + let preset = getPreset(presetNames.get()); + preset = setPresetValues(preset); + savePresets(); +}; + +presetCreate.onTriggered = function () +{ + if (!op.patch.isEditorMode()) return; + + + new CABLES.UI.ModalDialog({ + "prompt": true, + "title": "New Preset", + "text": "Enter a new preset name", + "promptValue": "", + "promptOk": (str) => + { + op.refreshParams(); + presetNames.set(str); + let preset = { "name": str }; + preset = setPresetValues(preset); + presets.push(preset); + updateDropdown(); + savePresets(); + } }); +}; + +presetDelete.onTriggered = function () +{ + if (!CABLES.UI) return; + const current = presetNames.get(); + const idx = presetNames.uiAttribs.values.indexOf(current); + presets.splice(idx, 1); + saveData(); + + if (presets.length > 0) + presetNames.set(presets[0].name); + + op.refreshParams(); + updateDropdown(); + updateButtons(); +}; + +presetRename.onTriggered = function () +{ + if (!CABLES.UI) return; + + new CABLES.UI.ModalDialog({ + "prompt": true, + "title": "New Preset", + "text": "Enter a new preset name", + "promptValue": "", + "promptOk": (str) => + { + if (!str) return; + const current = presetNames.get(); + const idx = presetNames.uiAttribs.values.indexOf(current); + presets[idx].name = str; + presetNames.set(str); + saveData(); + updateDropdown(); + op.refreshParams(); + } + }); +}; + +dataPort.onChange = function () +{ + data = JSON.parse(dataPort.get()); + + for (let i = 0; i < data.length; i++) + { + const portObject = data[i]; + + const varname = portObject.varname; + + if (!op.getPort(varname)) + { + if (portObject.type == CABLES.OP_PORT_TYPE_VALUE) + { + const val = op.patch.getVarValue(varname); + const port = op.inFloat(varname, val); + + port.setUiAttribs({ + "editableTitle": true, + "title": portObject.title }); + + listenPortChange(port, varname); + + port.set(val); + port.forceChange(); + } + } + } + + setDebugOutput(); + // dataPort.onChange=null; +}; + +function listenPortChange(port, varname) +{ + valuePorts.push(port); + port.onChange = function () + { + op.patch.setVarValue(varname, port.get()); + }; + + port.addEventListener("onUiAttrChange", (attribs) => + { + if (attribs.title) + { + const thePort = data.find((p) => { return p.varname === varname; }); + if (thePort) + { + thePort.title = attribs.title; + saveData(); + } + } + }); +} + +op.patch.addEventListener("onOpDelete", (optodelete) => +{ + if (optodelete.objName.indexOf("VarGet") == -1) return; + + const newData = []; + for (let i = 0; i < data.length; i++) + { + let found = false; + + for (let oi = 0; oi < op.patch.ops.length; oi++) + { + const opt = op.patch.ops[oi]; + + if (opt != optodelete && + opt.objName.indexOf("VarGet" > -1) && + opt.varName && + opt.varName.get && + opt.varName.get() == data[i].varname) + { + found = true; + break; + } + } + + if (found) + { + newData.push(data[i]); + } + else + { + op.removePort(op.getPort(data[i].varname)); + } + } + + data = newData; + saveData(); + + op.refreshParams(); + setTimeout(op.refreshParams.bind(this), 1000); +}); + +function setDebugOutput() +{ + outDbgData.set(data); + outDbgSets.set(presets); +} + +addPort.onLinkChanged = function () +{ + if (addPort.links.length === 0) + { + op.log("no links!"); + return; + } + + const link = addPort.links[0]; + const otherPort = link.getOtherPort(addPort); + + const varname = ".preset_" + otherPort.name + "_" + id.get() + "_" + CABLES.shortId(); + + op.log("pilength", op.portsIn.length); + + data.push( + { + "varname": varname, + "title": otherPort.parent.name + " " + otherPort.name, + "type": otherPort.type + }); + + const oldValue = otherPort.get(); + + op.patch.setVarValue(varname, oldValue); + op.patch.getVar(varname).type = "preset"; + + addPort.removeLinks(); + saveData(); + op.refreshParams(); + + otherPort.setVariable(varname); +}; + +op.onDelete = (reloading) => +{ + if (reloading) return; + for (let i = 0; i < data.length; i++) + op.patch.deleteVar(data[i].varname); +}; + + +}; + +Ops.Value.Preset.prototype = new CABLES.Op(); +CABLES.OPS["ffe981a5-67df-4da5-a6a9-7fcb910fc982"]={f:Ops.Value.Preset,objName:"Ops.Value.Preset"}; + + + + +// ************************************************************** +// +// Ops.Value.PreviousValueStore +// +// ************************************************************** + +Ops.Value.PreviousValueStore = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val = op.inValueFloat("Value"), + outCurrent = op.outNumber("Current Value"), + outOldVal = op.outNumber("Previous Value"); + +let oldValue = 0; + +val.onChange = function () +{ + outOldVal.set(oldValue); + oldValue = val.get(); + outCurrent.set(val.get()); +}; + + +}; + +Ops.Value.PreviousValueStore.prototype = new CABLES.Op(); +CABLES.OPS["01716872-67bd-4b31-a4a2-e0ccadf48411"]={f:Ops.Value.PreviousValueStore,objName:"Ops.Value.PreviousValueStore"}; + + + + +// ************************************************************** +// +// Ops.Value.RouteNumber +// +// ************************************************************** + +Ops.Value.RouteNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// constants and variables +const NUM_PORTS = 10; +const DEFAULT_VALUE_DEFAULT = 0; +let lastIdx = null; + +// input +const indexPort = op.inValue("Index", 0); +const valuePort = op.inValue("Value", 0); +const defaultValuePort = op.inValue("Default Value", DEFAULT_VALUE_DEFAULT); +const onlyOnePort = op.inBool("Set inactive to default", false); + +// output +const valuePorts = createOutPorts(DEFAULT_VALUE_DEFAULT); + +// change listeners +indexPort.onChange = update; +valuePort.onChange = update; // TODO: Maybe only one update needed!? +defaultValuePort.onChange = setDefaultValues; +onlyOnePort.onChange = onlyOnePortChange; + +setDefaultValues(); + +// functions + +/** + * creates the output-port array + */ +function createOutPorts() +{ + let arr = []; + for (let i = 0; i < NUM_PORTS; i++) + { + let port = op.outNumber("Index " + i + " Value"); + arr.push(port); + } + return arr; +} + +/** + * Sets all value ports to the default value + */ +function setDefaultValues() +{ + const defaultValue = defaultValuePort.get(); + valuePorts.forEach((valuePort) => + { + valuePort.set(defaultValue); + }); +} + +/** + * Update + */ +function update() +{ + let index = indexPort.get(); + index = Math.round(index); + index = clamp(index, 0, NUM_PORTS - 1); + + if (onlyOnePort.get() && lastIdx !== null && lastIdx != index) + { + valuePorts[lastIdx].set(defaultValuePort.get()); + } + + const value = valuePort.get(); + valuePorts[index].set(value); + + lastIdx = index; +} + +/** + * Returns a number whose value is limited to the given range. + */ +function clamp(value, min, max) +{ + return Math.min(Math.max(value, min), max); +} + +/** + * Reset all ports to default value and set current index + * or let have several ports with old value + */ +function onlyOnePortChange() +{ + if (onlyOnePort.get()) + { + setDefaultValues(); + update(); + } +} + + +}; + +Ops.Value.RouteNumber.prototype = new CABLES.Op(); +CABLES.OPS["e3b1fc2d-a813-4d9b-8cb0-595fc95af4e2"]={f:Ops.Value.RouteNumber,objName:"Ops.Value.RouteNumber"}; + + + + +// ************************************************************** +// +// Ops.Value.SwitchFile_v2 +// +// ************************************************************** + +Ops.Value.SwitchFile_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let idx = op.inValueInt("Index"); +let valuePorts = []; +let result = op.outString("Result"); + +idx.onChange = update; + +for (let i = 0; i < 10; i++) +{ + let p = op.inUrl("File " + i); + valuePorts.push(p); + p.onChange = update; +} + +function update() +{ + const index = idx.get(); + if (index >= 0 && valuePorts[index]) + { + result.set(valuePorts[index].get()); + } +} + + +}; + +Ops.Value.SwitchFile_v2.prototype = new CABLES.Op(); +CABLES.OPS["250c8d79-2b83-419f-8c23-910d95936f2c"]={f:Ops.Value.SwitchFile_v2,objName:"Ops.Value.SwitchFile_v2"}; + + + + +// ************************************************************** +// +// Ops.Value.SwitchNumber +// +// ************************************************************** + +Ops.Value.SwitchNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const idx = op.inValueInt("Index"); +const valuePorts = []; +const result = op.outNumber("Result"); + +idx.onChange = update; + +for (let i = 0; i < 16; i++) +{ + let p = op.inValue("Value " + i); + valuePorts.push(p); + p.onChange = update; +} + +function update() +{ + if (idx.get() >= 0 && valuePorts[idx.get()]) + { + result.set(valuePorts[idx.get()].get()); + } +} + + +}; + +Ops.Value.SwitchNumber.prototype = new CABLES.Op(); +CABLES.OPS["fbb89f72-f2e3-4d34-ad01-7d884a1bcdc0"]={f:Ops.Value.SwitchNumber,objName:"Ops.Value.SwitchNumber"}; + + + + +// ************************************************************** +// +// Ops.Value.SwitchNumberOnTrigger +// +// ************************************************************** + +Ops.Value.SwitchNumberOnTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + currentVal = op.outNumber("Value"), + oldVal = op.outNumber("Last Value"), + triggered = op.outTrigger("Triggered"); + +let triggers = []; +let inVals = []; +let inExes = []; + +function onTrigger() +{ + oldVal.set(currentVal.get()); + currentVal.set(inVals[this.slot].get()); + triggered.trigger(); +} + +let num = 8; +for (let i = 0; i < num; i++) +{ + let newExe = op.addInPort(new CABLES.Port(op, "Trigger " + i, CABLES.OP_PORT_TYPE_FUNCTION)); + newExe.slot = i; + newExe.onTriggered = onTrigger.bind(newExe); + let newVal = op.addInPort(new CABLES.Port(op, "Value " + i, CABLES.OP_PORT_TYPE_VALUE)); + inVals.push(newVal); +} + +let defaultVal = op.inValueString("Default Value"); + +currentVal.set(defaultVal.get()); +oldVal.set(defaultVal.get()); + +defaultVal.onChange = function () +{ + oldVal.set(currentVal.get()); + currentVal.set(defaultVal.get()); +}; + + +}; + +Ops.Value.SwitchNumberOnTrigger.prototype = new CABLES.Op(); +CABLES.OPS["338032c5-bf47-454b-8ae1-cd91f17e5c5b"]={f:Ops.Value.SwitchNumberOnTrigger,objName:"Ops.Value.SwitchNumberOnTrigger"}; + + + + +// ************************************************************** +// +// Ops.Value.ToggleNumber +// +// ************************************************************** + +Ops.Value.ToggleNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + useValue1Port = op.inValueBool("Use Value 1", false), + value0port = op.inValue("Value 0", 0), + value1port = op.inValue("Value 1", 1), + outValuePort = op.outNumber("Out Value", 0); + +value0port.onChange = + value1port.onChange = + useValue1Port.onChange = setOutput; + +function setOutput() +{ + const useValue1 = useValue1Port.get(); + if (useValue1) + { + outValuePort.set(value1port.get()); + } + else + { + outValuePort.set(value0port.get()); + } +} + + +}; + +Ops.Value.ToggleNumber.prototype = new CABLES.Op(); +CABLES.OPS["400eea7d-5a68-4dda-a94d-2bb2ee7c2331"]={f:Ops.Value.ToggleNumber,objName:"Ops.Value.ToggleNumber"}; + + + + +// ************************************************************** +// +// Ops.Value.Trigger3Numbers +// +// ************************************************************** + +Ops.Value.Trigger3Numbers = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + x = op.inValueFloat("value x"), + y = op.inValueFloat("value y"), + z = op.inValueFloat("value z"), + resultX = op.outNumber("result x"), + resultY = op.outNumber("result y"), + resultZ = op.outNumber("result z"); + +exe.onTriggered = + x.onChange = + y.onChange = + z.onChange = exec; + +function frame(time) +{ + exec(); +} + +function exec() +{ + if (resultX.get() != x.get()) resultX.set(x.get()); + if (resultY.get() != y.get()) resultY.set(y.get()); + if (resultZ.get() != z.get()) resultZ.set(z.get()); +} + + +}; + +Ops.Value.Trigger3Numbers.prototype = new CABLES.Op(); +CABLES.OPS["d56326a2-6351-4261-898a-635ca0636dd0"]={f:Ops.Value.Trigger3Numbers,objName:"Ops.Value.Trigger3Numbers"}; + + + + +// ************************************************************** +// +// Ops.Value.TriggerOnChangeNumber +// +// ************************************************************** + +Ops.Value.TriggerOnChangeNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inval = op.inFloat("Value"), + next = op.outTrigger("Next"), + number = op.outNumber("Number"); + +inval.onChange = function () +{ + number.set(inval.get()); + next.trigger(); +}; + + +}; + +Ops.Value.TriggerOnChangeNumber.prototype = new CABLES.Op(); +CABLES.OPS["f5c8c433-ce13-49c4-9a33-74e98f110ed0"]={f:Ops.Value.TriggerOnChangeNumber,objName:"Ops.Value.TriggerOnChangeNumber"}; + + + + +// ************************************************************** +// +// Ops.Value.TypeOf_v2 +// +// ************************************************************** + +Ops.Value.TypeOf_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const input = op.inValue("input"); +const result = op.outString("result"); + +input.onChange = update; + +function update() +{ + result.set(typeof (input.get())); +} + + +}; + +Ops.Value.TypeOf_v2.prototype = new CABLES.Op(); +CABLES.OPS["50fd0008-7bcb-4ab3-8a8c-82fc55a3e7a9"]={f:Ops.Value.TypeOf_v2,objName:"Ops.Value.TypeOf_v2"}; + + + + +// ************************************************************** +// +// Ops.Value.Value2d +// +// ************************************************************** + +Ops.Value.Value2d = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + x = op.inValueFloat("value x"), + y = op.inValueFloat("value y"), + resultX = op.outNumber("result x"), + resultY = op.outNumber("result y"); + +exe.onTriggered = exec; + +x.onChange = exec; +y.onChange = exec; + +function frame(time) +{ + updateAnims(); + exec(); +} + +function exec() +{ + if (resultX.get() != x.get()) resultX.set(x.get()); + if (resultY.get() != y.get()) resultY.set(y.get()); +} + + +}; + +Ops.Value.Value2d.prototype = new CABLES.Op(); +CABLES.OPS["ef4fa87b-a075-4f29-9628-b4f26ce41533"]={f:Ops.Value.Value2d,objName:"Ops.Value.Value2d"}; + + + + +// ************************************************************** +// +// Ops.Value.ValueChangeCounter +// +// ************************************************************** + +Ops.Value.ValueChangeCounter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inVal = op.inValue("Value"), + inReset = op.inTriggerButton("Reset"), + outResult = op.outNumber("Result"); + +let count = 0; + +inReset.onTriggered = function () +{ + count = 0; +}; + +inVal.onChange = function () +{ + count++; + outResult.set(count); +}; + + +}; + +Ops.Value.ValueChangeCounter.prototype = new CABLES.Op(); +CABLES.OPS["cbd50d32-bfc1-45c8-ba02-a6afa1fdd0e1"]={f:Ops.Value.ValueChangeCounter,objName:"Ops.Value.ValueChangeCounter"}; + + + + +// ************************************************************** +// +// Ops.Value.ValueChangedTrigger +// +// ************************************************************** + +Ops.Value.ValueChangedTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + val = op.inFloat("Value", 0), + exe = op.inTrigger("Execute"), + trigger = op.outTrigger("trigger"); + +let changed = false; + +exe.onTriggered = function () +{ + if (changed) + { + changed = false; + trigger.trigger(); + } +}; + +val.onChange = function () +{ + changed = true; +}; + + +}; + +Ops.Value.ValueChangedTrigger.prototype = new CABLES.Op(); +CABLES.OPS["9f353fcc-da0b-4af8-ae5c-4edd256fc9e3"]={f:Ops.Value.ValueChangedTrigger,objName:"Ops.Value.ValueChangedTrigger"}; + + + + +// ************************************************************** +// +// Ops.Values.FreezeArray +// +// ************************************************************** + +Ops.Values.FreezeArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inArray("Number"), + inFreeze = op.inTriggerButton("Button"), + inHidden = op.inString("storedJson"), + outArr = op.outArray("Frozen Array"); + +inFreeze.onTriggered = +inHidden.onTriggered = update; + +inHidden.setUiAttribs({ "hideParam": true, "hidePort": true, "ignoreBigPort": true }); + +function update() +{ + inHidden.set(JSON.stringify(inStr.get())); +} + +outArr.onLinkChanged = () => +{ + outArr.set(JSON.parse(inHidden.get())); +}; + +inHidden.onChange = () => +{ + outArr.set(null); + try + { + outArr.set(JSON.parse(inHidden.get())); + } + catch (e) {} +}; + + +}; + +Ops.Values.FreezeArray.prototype = new CABLES.Op(); +CABLES.OPS["340fb9e2-27ab-47f0-b3cc-2ef001f485c5"]={f:Ops.Values.FreezeArray,objName:"Ops.Values.FreezeArray"}; + + + + +// ************************************************************** +// +// Ops.Values.FreezeNumber +// +// ************************************************************** + +Ops.Values.FreezeNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inFloat("Number", 0), + inFreeze = op.inTriggerButton("Button"), + inHidden = op.inFloat("StoredNumber"), + outNum = op.outNumber("Frozen Number"); + +inFreeze.onTriggered = +inHidden.onTriggered = update; + +inHidden.setUiAttribs({ "hideParam": true, "hidePort": true }); + +outNum.onLinkChanged = () => +{ + outNum.set(inHidden.get()); +}; + +function update() +{ + inHidden.set(inStr.get()); + outNum.set(inHidden.get()); +} + + +}; + +Ops.Values.FreezeNumber.prototype = new CABLES.Op(); +CABLES.OPS["97dea923-f00f-44cf-bef9-808a2556105b"]={f:Ops.Values.FreezeNumber,objName:"Ops.Values.FreezeNumber"}; + + + + +// ************************************************************** +// +// Ops.Values.FreezeString +// +// ************************************************************** + +Ops.Values.FreezeString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inStr = op.inString("String", "default"), + inFreeze = op.inTriggerButton("Button"), + inHidden = op.inString("StoredString"), + outString = op.outString("Frozen String"); + +inFreeze.onTriggered = +inHidden.onTriggered = update; + +inHidden.setUiAttribs({ "hideParam": true, "hidePort": true, "ignoreBigPort": true }); + +outString.onLinkChanged = () => +{ + outString.set(inHidden.get()); +}; + +function update() +{ + inHidden.set(inStr.get()); + outString.set(inHidden.get()); +} + + +}; + +Ops.Values.FreezeString.prototype = new CABLES.Op(); +CABLES.OPS["9ae2598f-8b5a-4749-aff3-a507c9957225"]={f:Ops.Values.FreezeString,objName:"Ops.Values.FreezeString"}; + + + + +// ************************************************************** +// +// Ops.Values.SequenceNumbers +// +// ************************************************************** + +Ops.Values.SequenceNumbers = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +const outputs=[]; +const inputs=[]; + +for(let i=0;i<16;i++) +{ + const inp=op.inFloat("Number "+i,0); + const out=op.outNumber("Output "+i); + + inp.changeAlways=true; + + outputs.push(out); + inputs.push(inp); +}; + + +for(let i=0;i= 0.5) x = 1.0 - texCoord.x;\n\n x *= (1. - width) * 2.0;\n\n col = texture(tex, vec2(x, texCoord.y));\n\n outColor = col;\n}",}; +const cgl = op.patch.cgl; + +const inRefresh = op.inTrigger("Refresh"); +const inFFTArray = op.inArray("FFT Array"); + +const inMirrorActive = op.inBool("Mirror Active", false); +const inMirrorWidth = op.inFloatSlider("Mirror Width", 0.5); + +const inTextureSize = op.inSwitch("Texture Size", [64, 128, 256, 512, 1024, 2048], 512); + +op.setPortGroup("Texture Options", [inTextureSize]); +op.setPortGroup("Mirror Options", [inMirrorActive, inMirrorWidth]); + +const outTexture = op.outTexture("Texture Out", null, "texture"); +const outPosition = op.outNumber("Position"); + +let updateTextureSize = false; +inTextureSize.onChange = () => +{ + updateTextureSize = true; +}; + +inMirrorActive.onChange = () => +{ + inMirrorWidth.setUiAttribs({ "greyout": !inMirrorActive.get() }); +}; + +const texFFT = new CGL.Texture(cgl, + { + "name": "AnalyzerTexture - FFT Texture " + op.id, + "wrap": CGL.Texture.CLAMP_TO_EDGE, + "filter": CGL.Texture.FILTER_LINEAR, + }); + +const texDefault = new CGL.Texture(cgl, + { + "name": "AnalyzerTexture - Render Texture " + op.id, + "wrap": CGL.Texture.CLAMP_TO_EDGE, + "filter": CGL.Texture.FILTER_LINEAR, + "width": Number(inTextureSize.get()), + "height": Number(inTextureSize.get()), + }); + +let data = []; + +let line = 0; +let height = 256; + +let buffer = new Uint8Array(); + +function updateFFT() +{ + const arr = inFFTArray.get(); + if (!arr) return; + + const width = arr.length; + // height = width; + if (!width) return; + + if (data.length === 0 || data.length !== width * 4) + { + data.length = width * 4; + buffer = new Uint8Array(width * height * 4); + } + + line++; + + if (line >= height) line = 0; + + outPosition.set(line / height); + + for (let i = 0; i < width; i++) + { + data[i * 4 + 0] = arr[i]; + data[i * 4 + 1] = arr[i]; + data[i * 4 + 2] = arr[i]; + data[i * 4 + 3] = 255; + } + + buffer.set(data, line * width * 4); + + if (texFFT.width != width || texFFT.height != height) + { + texFFT.setSize( + width, height + ); + // effect.setSourceTexture(texFFT); + } + + texFFT.initFromData( + buffer, + width, height, + // Number(inTextureSize.get()), + // Number(inTextureSize.get()), + CGL.Texture.FILTER_LINEAR, + CGL.Texture.WRAP_CLAMP_TO_EDGE + ); +} + +let effect = new CGL.TextureEffect(cgl, + { + // "isFloatingPointTexture": true, + "filter": CGL.Texture.FILTER_LINEAR, + "wrap": CGL.Texture.WRAP_CLAMP_TO_EDGE, + "width": Number(inTextureSize.get()), + "height": Number(inTextureSize.get()), + } +); + +let prevViewPort = [0, 0, 0, 0]; + +// ------------------ + +const shaderDefault = new CGL.Shader(cgl, "AnalyzerTexture - defaultShader"); +shaderDefault.setSource(shaderDefault.getDefaultVertexShader(), attachments.default_frag); +const texUniformDefault = new CGL.Uniform(shaderDefault, "t", "texFFT", 1); + +const shaderMirror = new CGL.Shader(cgl, "AnalyzerTexture mirror"); + +shaderMirror.setSource(shaderMirror.getDefaultVertexShader(), attachments.mirror_frag); + +const textureUniformMirror = new CGL.Uniform(shaderMirror, "t", "tex", 0); +const uniWidthMirror = new CGL.Uniform(shaderMirror, "f", "width", inMirrorWidth); + +function mirrorTexture() +{ + cgl.pushShader(shaderMirror); + + effect.startEffect(); + effect.setSourceTexture(effect.getCurrentSourceTexture()); + + effect.bind(); + + cgl.setTexture(0, texFFT.tex); + + effect.finish(); + effect.endEffect(); + + cgl.popShader(); +} + +inRefresh.onTriggered = () => +{ + if (!inFFTArray.get()) return; + + updateFFT(); + + if (updateTextureSize) + { + texDefault.setSize( + Number(inTextureSize.get()), + Number(inTextureSize.get()), + ); + + effect.setSourceTexture(texDefault); + updateTextureSize = false; + } + + if (texFFT) + { + cgl.pushShader(shaderDefault); + effect.startEffect(); + effect.setSourceTexture(texDefault); + effect.bind(); + + cgl.setTexture(1, texFFT.tex); + + effect.finish(); + effect.endEffect(); + + cgl.popShader(); + } + + if (inMirrorActive.get()) mirrorTexture(); + + outTexture.set(null); + outTexture.set(effect.getCurrentSourceTexture()); +}; + + +}; + +Ops.WebAudio.AnalyzerTexture_v2.prototype = new CABLES.Op(); +CABLES.OPS["decba955-636b-41c5-bfb8-c2506cbcabd2"]={f:Ops.WebAudio.AnalyzerTexture_v2,objName:"Ops.WebAudio.AnalyzerTexture_v2"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.AudioAnalyzer_v2 +// +// ************************************************************** + +Ops.WebAudio.AudioAnalyzer_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const clamp = (val, min, max) => Math.min(Math.max(val, min), max); +const MAX_DBFS_RANGE_24_BIT = -144; +const MAX_DBFS_RANGE_26_BIT = -96; + +let audioCtx = CABLES.WEBAUDIO.createAudioContext(op); + +const inTrigger = op.inTrigger("Trigger In"); + +const analyser = audioCtx.createAnalyser(); +analyser.smoothingTimeConstant = 0.3; +analyser.fftSize = 256; + +const FFT_BUFFER_SIZES = [32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768]; + +const audioIn = op.inObject("Audio In", null, "audioNode"); +const inFFTSize = op.inDropDown("FFT size", FFT_BUFFER_SIZES, 256); +const inSmoothing = op.inFloatSlider("Smoothing", 0.3); + +const inRangeMin = op.inFloat("Min", -90); +const inRangeMax = op.inFloat("Max", 0); + +op.setPortGroup("Inputs", [inTrigger, audioIn]); +op.setPortGroup("FFT Options", [inFFTSize, inSmoothing]); +op.setPortGroup("Range (in dBFS)", [inRangeMin, inRangeMax]); +const outTrigger = op.outTrigger("Trigger Out"); +const audioOut = op.outObject("Audio Out", null, "audioNode"); +const fftOut = op.outArray("FFT Array"); +const ampOut = op.outArray("Waveform Array"); +const frequencyOut = op.outArray("Frequencies by Index Array"); +const fftLength = op.outNumber("Array Length"); +const avgVolumePeak = op.outNumber("Average Volume"); +const avgVolumeAmp = op.outNumber("Average Volume Time-Domain"); +const avgVolumeRMS = op.outNumber("RMS Volume"); +let updating = false; + +let fftBufferLength = analyser.frequencyBinCount; +let fftDataArray = new Uint8Array(fftBufferLength); +let ampDataArray = new Uint8Array(fftBufferLength); +let frequencyArray = []; +frequencyArray.length = fftBufferLength; +let oldAudioIn = null; + +audioIn.onChange = () => +{ + if (audioIn.get()) + { + const audioNode = audioIn.get(); + if (audioNode.connect) + { + audioNode.connect(analyser); + audioOut.set(analyser); + } + } + else + { + if (oldAudioIn) + { + if (oldAudioIn.disconnect) oldAudioIn.disconnect(analyser); + audioOut.set(null); + } + } + + oldAudioIn = audioIn.get(); +}; + +function updateAnalyser() +{ + try + { + const fftSize = Number(inFFTSize.get()); + analyser.smoothingTimeConstant = clamp(inSmoothing.get(), 0.0, 1.0); + analyser.fftSize = fftSize; + const minDecibels = clamp(inRangeMin.get(), MAX_DBFS_RANGE_24_BIT, -0.0001); + const maxDecibels = Math.max(inRangeMax.get(), analyser.minDecibels + 0.0001); + analyser.minDecibels = minDecibels; + analyser.maxDecibels = maxDecibels; + + if (minDecibels < MAX_DBFS_RANGE_24_BIT) + { + op.setUiError("maxDbRangeMin", + "Your minimum is below the lowest possible dBFS value: " + + MAX_DBFS_RANGE_24_BIT + + "dBFS. To make sure your analyser data is correct, try increasing the minimum.", + 1 + ); + } + else + { + op.setUiError("maxDbRangeMin", null); + } + + if (maxDecibels > 0) + { + op.setUiError("maxDbRangeMax", "Your maximum is above 0 dBFS. As digital signals only go to 0 dBFS and not above, you should use 0 as your maximum.", 1); + } + else + { + op.setUiError("maxDbRangeMax", null); + } + + if (FFT_BUFFER_SIZES.indexOf(fftSize) >= 6) + { + op.setUiError("highFftSize", "Please be careful with high FFT sizes as they can slow down rendering. Check the profiler to see if performance is impacted.", 1); + } + else + { + op.setUiError("highFftSize", null); + } + } + catch (e) + { + op.log(e); + } +} + +inFFTSize.onChange = inSmoothing.onChange += inRangeMin.onChange = inRangeMax.onChange = () => + { + if (inTrigger.isLinked()) updating = true; + else updateAnalyser(); + }; + +inTrigger.onTriggered = function () +{ + if (updating) + { + updateAnalyser(); + updating = false; + } + + if (fftBufferLength != analyser.frequencyBinCount) + { + fftBufferLength = analyser.frequencyBinCount; + fftDataArray = new Uint8Array(fftBufferLength); + ampDataArray = new Uint8Array(fftBufferLength); + + frequencyArray = []; + frequencyArray.length = fftBufferLength; + + for (let index = 0; index < fftBufferLength; index += 1) + { + frequencyArray[index] = Math.round(index * audioCtx.sampleRate / (analyser.fftSize * 2)); + } + + frequencyOut.set(null); + frequencyOut.set(frequencyArray); + } + + if (!fftDataArray) return; + if (!ampDataArray) return; + + const fftSize = Number(inFFTSize.get()); + + try + { + analyser.getByteFrequencyData(fftDataArray); + analyser.getByteTimeDomainData(ampDataArray); + + let values = 0; + let peakValues = 0; + let ampPeakValues = 0; + for (let i = 0; i < analyser.frequencyBinCount; i++) + { + values += ampDataArray[i] * ampDataArray[i]; + peakValues += fftDataArray[i]; + ampPeakValues += ampDataArray[i]; + } + + const peakAverage = peakValues / analyser.frequencyBinCount; + const peakAmpAverage = ampPeakValues / analyser.frequencyBinCount; + + avgVolumePeak.set(peakAverage / 128); + avgVolumeAmp.set(peakAmpAverage / 256); + + let rms = Math.sqrt(values / analyser.frequencyBinCount); + rms = Math.max(rms, rms * inSmoothing.get()); + avgVolumeRMS.set(rms / 256); + } + catch (e) { op.log(e); } + + fftOut.set(null); + fftOut.set(fftDataArray); + + ampOut.set(null); + ampOut.set(ampDataArray); + + fftLength.set(fftDataArray.length); + outTrigger.trigger(); +}; + + +}; + +Ops.WebAudio.AudioAnalyzer_v2.prototype = new CABLES.Op(); +CABLES.OPS["ff9bf46c-676f-4aa1-95bf-5595a6813ed7"]={f:Ops.WebAudio.AudioAnalyzer_v2,objName:"Ops.WebAudio.AudioAnalyzer_v2"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.AudioBufferPlayer_v2 +// +// ************************************************************** + +Ops.WebAudio.AudioBufferPlayer_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// input ports +const audioBufferPort = op.inObject("Audio Buffer", null, "audioBuffer"); +const playPort = op.inBool("Start / Stop", false); + +const loopPort = op.inBool("Loop", false); +const inResetStart = op.inTriggerButton("Restart"); +const offsetPort = op.inFloat("Offset", 0); +const playbackRatePort = op.inFloat("Playback Rate", 1); +const detunePort = op.inFloat("Detune", 0); + +op.setPortGroup("Playback Controls", [playPort, loopPort, inResetStart]); +op.setPortGroup("Time Controls", [offsetPort]); +op.setPortGroup("Miscellaneous", [playbackRatePort, detunePort]); + +// output ports +const audioOutPort = op.outObject("Audio Out", null, "audioNode"); +const outPlaying = op.outBool("Is Playing", false); +const outLoading = op.outBool("Loading", false); + +// vars +let source = null; +let isPlaying = false; +let hasEnded = false; +let pausedAt = null; +let startedAt = null; +let isLoading = false; + +const audioCtx = CABLES.WEBAUDIO.createAudioContext(op); + +const gainNode = audioCtx.createGain(); + +if (!audioBufferPort.isLinked()) +{ + op.setUiError("inputNotConnected", "To be able to play back sound, you need to connect an AudioBuffer to this op.", 0); +} +else +{ + op.setUiError("inputNotConnected", null); +} + +audioBufferPort.onLinkChanged = () => +{ + if (!audioBufferPort.isLinked()) + { + op.setUiError("inputNotConnected", "To be able to play back sound, you need to connect an AudioBuffer to this op.", 0); + } + else + { + op.setUiError("inputNotConnected", null); + } +}; + +if (!audioOutPort.isLinked()) +{ + op.setUiError("outputNotConnected", "To be able to hear sound playing, you need to connect this op to an Output op.", 0); +} +else +{ + op.setUiError("outputNotConnected", null); +} + +audioOutPort.onLinkChanged = () => +{ + if (!audioOutPort.isLinked()) + { + op.setUiError("outputNotConnected", "To be able to hear sound playing, you need to connect this op to an Output op.", 0); + } + else + { + op.setUiError("outputNotConnected", null); + } +}; + +// change listeners +audioBufferPort.onChange = function () +{ + if (audioBufferPort.get()) createAudioBufferSource(); + else + { + if (isLoading) + { + isLoading = false; + outLoading.set(isLoading); + } + + if (isPlaying) + { + stop(0); + source.buffer = null; + source = null; + } + } +}; + +playPort.onChange = function () +{ + if (!audioBufferPort.get()) return; + + if (!source) + { + if (!isLoading) createAudioBufferSource(); + } + + if (playPort.get()) + { + const startTime = 0; + start(startTime); + } + else + { + const stopTime = 0; + stop(stopTime); + } +}; + +loopPort.onChange = function () +{ + if (source) + { + source.loop = !!loopPort.get(); + } +}; + +detunePort.onChange = setDetune; + +function setDetune() +{ + if (!source) return; + + const detune = detunePort.get() || 0; + if (source.detune) + { + source.detune.setValueAtTime( + detune, + audioCtx.currentTime + ); + } +} + +playbackRatePort.onChange = setPlaybackRate; + +function setPlaybackRate() +{ + if (!source) return; + + const playbackRate = playbackRatePort.get() || 0; + if (playbackRate >= source.playbackRate.minValue && playbackRate <= source.playbackRate.maxValue) + { + source.playbackRate.setValueAtTime( + playbackRate, + audioCtx.currentTime + ); + } +} + +let resetTriggered = false; +inResetStart.onTriggered = function () +{ + if (!source) return; + if (!audioBufferPort.get()) return; + else + { + if (!(audioBufferPort.get() instanceof AudioBuffer)) return; + } + + if (playPort.get()) + { + if (isPlaying) + { + resetTriggered = true; + stop(0); + } + else + { + start(0); + } + } +}; + +// functions +function createAudioBufferSource(dontStart = false) +{ + if (isLoading) return; + if (!(audioBufferPort.get() instanceof AudioBuffer)) return; + + isLoading = true; + outLoading.set(isLoading); + + if (source) + { + source.onended = null; + + if (source.buffer) + { + stop(0); + source.disconnect(gainNode); + source.buffer = null; + } + + source = null; + } + + source = audioCtx.createBufferSource(); + + const buffer = audioBufferPort.get(); + + if (!buffer) + { + isLoading = false; + outLoading.set(isLoading); + return; + } + + source.buffer = buffer; + source.onended = onPlaybackEnded; + source.loop = loopPort.get(); + + source.connect(gainNode); + setPlaybackRate(); + setDetune(); + audioOutPort.set(gainNode); + + isLoading = false; + outLoading.set(isLoading); + + if (resetTriggered) + { + start(0); + resetTriggered = false; + return; + } + + if (playPort.get() && !dontStart) + { + // if (!isPlaying) + start(0); + } +} + +let timeOuting = false; +let timerId = null; + +offsetPort.onChange = () => +{ + if (offsetPort.get() >= 0) op.setUiError("offsetNegative", null); + else + { + op.setUiError("offsetNegative", "Offset cannot be negative. Setting to 0.", 1); + } + + if (source) + { + if (source.buffer) + { + if (offsetPort.get() > source.buffer.duration) + { + op.setUiError("offsetTooLong", "Your offset value is higher than the total time of your audio file. Please decrease the duration to be able to hear sound when playing back your buffer.", 1); + } + else + { + op.setUiError("offsetTooLong", null); + } + } + } +}; + +function start(time) +{ + try + { + if (source) + { + let offset = Math.max(0, offsetPort.get()); + source.start(time, offset); // 0 = now + + isPlaying = true; + hasEnded = false; + outPlaying.set(true); + } + else + { + op.log("start() but no src..."); + } + } + catch (e) + { + op.log("Error on start: ", e.message); + outPlaying.set(false); + + isPlaying = false; + } +} + +function recreateBuffer() +{ + let dontStart = !loopPort.get(); + createAudioBufferSource(dontStart); +} + +function stop(time) +{ + try + { + if (source) + { + source.stop(); + if (!resetTriggered) recreateBuffer(); + } + + isPlaying = false; + outPlaying.set(false); + } + catch (e) + { + op.setUiError(e); + outPlaying.set(false); + } +} + +function onPlaybackEnded() +{ + if (loopPort.get()) + { + isPlaying = true; + hasEnded = false; + } + else + { + isPlaying = false; + hasEnded = true; + } + outPlaying.set(isPlaying); + + recreateBuffer(); +} + + +}; + +Ops.WebAudio.AudioBufferPlayer_v2.prototype = new CABLES.Op(); +CABLES.OPS["3abd0dbb-eeee-4c65-ae31-b8bc2345e2d5"]={f:Ops.WebAudio.AudioBufferPlayer_v2,objName:"Ops.WebAudio.AudioBufferPlayer_v2"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.AudioBufferToSplineArray +// +// ************************************************************** + +Ops.WebAudio.AudioBufferToSplineArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// currently only uses mono, if we want to extract stereo data some changes in extractPeaks are needed + +// constants +const SAMPLES_PER_PIXEL_MIN = 100; // might crash when this is too low + +function findMinMax(array) +{ + let min = Infinity; + let max = -Infinity; + let i = 0; + let len = array.length; + let curr; + + for (; i < len; i++) + { + curr = array[i]; + if (min > curr) + { + min = curr; + } + if (max < curr) + { + max = curr; + } + } + + return { + "min": min, + "max": max + }; +} + +const cgl = op.patch.cgl; + +// input +const renderPort = op.inTrigger("Render"); +const audioBufferPort = op.inObject("Audio Buffer", null, "audioBuffer"); +const inWidth = op.inFloat("Width", 1); +const inHeight = op.inFloat("Height", 0.5); +const samplesPerPixelPort = op.inInt("Samples Per Pixel", 10000); + +op.setPortGroup("Waveform Settings", [inWidth, inHeight, samplesPerPixelPort]); +// output +const nextPort = op.outTrigger("Next"); +const outArray = op.outArray("Array Out"); + +// change listeners +let updating = true; +audioBufferPort.onChange = samplesPerPixelPort.onChange += inWidth.onChange = inHeight.onChange = () => + { + if (audioBufferPort.get()) + { + if (!renderPort.isLinked()) + { + const audioBuffer = audioBufferPort.get(); + if (!(audioBuffer instanceof AudioBuffer)) return; + } + } + + updating = true; + }; + +renderPort.onTriggered = () => +{ + if (updating) + { + extractPeaks(); + updating = false; + } + nextPort.trigger(); +}; + +function extractPeaks() +{ + const audioBuffer = audioBufferPort.get(); + if (audioBuffer) + { + op.setUiError("noBuffer", null); + + if (!(audioBuffer instanceof AudioBuffer)) return; + } + else + { + op.setUiError("noBuffer", "You need to connect the \"Audio Buffer\" input for this op to work!", 0); + } + + if (audioBuffer) + { + let samplesPerPixel = samplesPerPixelPort.get(); + if (samplesPerPixel < SAMPLES_PER_PIXEL_MIN) + { + op.setUiError("minSamples", "The value for \"Samples Per Pixel\" is lower than the minimum value " + SAMPLES_PER_PIXEL_MIN + ". Therefore the value has been set to " + SAMPLES_PER_PIXEL_MIN + ".", 1); + samplesPerPixel = SAMPLES_PER_PIXEL_MIN; + } + else + { + op.setUiError("minSamples", null); + } + + let makeMono = audioBuffer.numberOfChannels < 2; // TODO: If we make this a parameter, we have to check if the audio actually is stereo + + const peaks = webaudioPeaks(audioBuffer, samplesPerPixel, makeMono); + + // because we extract mono peaks we just access [0] here + const typedArr = peaks.data[0]; + const regularArr = Array.prototype.slice.call(typedArr); + const minMax = findMinMax(regularArr); + const normalizedArray = []; + for (let i = 0; i < regularArr.length; i += 1) + { + normalizedArray.push( + CABLES.map(i, 0, regularArr.length - 1, -inWidth.get(), inWidth.get()) + ); + normalizedArray.push( + CABLES.map(regularArr[i], minMax.min, minMax.max, -inHeight.get(), inHeight.get()) + ); + normalizedArray.push(0); + } + + outArray.set(null); + outArray.set(normalizedArray); + } + else + { + outArray.set(null); + } +} + + +}; + +Ops.WebAudio.AudioBufferToSplineArray.prototype = new CABLES.Op(); +CABLES.OPS["0a573407-f1af-4580-ba37-1604108151bd"]={f:Ops.WebAudio.AudioBufferToSplineArray,objName:"Ops.WebAudio.AudioBufferToSplineArray"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.AudioBuffer_v2 +// +// ************************************************************** + +Ops.WebAudio.AudioBuffer_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +const + audioCtx = CABLES.WEBAUDIO.createAudioContext(op), + inUrlPort = op.inUrl("URL", "audio"), + inLoadingTask = op.inBool("Create Loading Task", true), + audioBufferPort = op.outObject("Audio Buffer", null, "audioBuffer"), + finishedLoadingPort = op.outBoolNum("Finished Loading", false), + sampleRatePort = op.outNumber("Sample Rate", 0), + lengthPort = op.outNumber("Length", 0), + durationPort = op.outNumber("Duration", 0), + numberOfChannelsPort = op.outNumber("Number of Channels", 0), + outLoading = op.outBool("isLoading", 0); + +let currentBuffer = null; +let isLoading = false; +let currentFileUrl = null; +let urlToLoadNext = null; +let clearAfterLoad = false; +let fromData = false; +let fromDataNew = false; +let fileReader = new FileReader(); +let arrayBuffer = null; +let loadingIdDataURL = 0; + +if (!audioBufferPort.isLinked()) +{ + op.setUiError("notConnected", "To play back sound, connect this op to a playback operator such as SamplePlayer or AudioBufferPlayer.", 0); +} +else +{ + op.setUiError("notConnected", null); +} + +audioBufferPort.onLinkChanged = () => +{ + if (audioBufferPort.isLinked()) + { + op.setUiError("notConnected", null); + } + else + { + op.setUiError("notConnected", "To play back sound, connect this op to a playback operator such as SamplePlayer or AudioBufferPlayer.", 0); + } +}; + +function loadAudioFile(url, loadFromData) +{ + currentFileUrl = url; + isLoading = true; + outLoading.set(isLoading); + + if (!loadFromData) + { + const ext = url.substr(url.lastIndexOf(".") + 1); + if (ext === "wav") + { + op.setUiError("wavFormat", "You are using a .wav file. Make sure the .wav file is 16 bit to be supported by all browsers. Safari does not support 24 bit .wav files.", 1); + } + else + { + op.setUiError("wavFormat", null); + } + + CABLES.WEBAUDIO.loadAudioFile(op.patch, url, onLoadFinished, onLoadFailed, inLoadingTask.get()); + } + else + { + let fileBlob = dataURItoBlob(url); + + if (fileBlob.type === "audio/wav") + { + op.setUiError("wavFormat", "You are using a .wav file. Make sure the .wav file is 16 bit to be supported by all browsers. Safari does not support 24 bit .wav files.", 1); + } + else + { + op.setUiError("wavFormat", null); + } + + if (inLoadingTask.get()) + { + loadingIdDataURL = cgl.patch.loading.start("audiobuffer from data-url " + op.id, ""); + if (cgl.patch.isEditorMode()) gui.jobs().start({ "id": "loadaudio" + loadingIdDataURL, "title": " loading audio data url (" + op.id + ")" }); + } + + fileReader.readAsArrayBuffer(fileBlob); + } +} + +function dataURItoBlob(dataURI) +{ + // convert base64 to raw binary data held in a string + // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this + let byteString = atob(dataURI.split(",")[1]); + + // separate out the mime component + let mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0]; + + // write the bytes of the string to an ArrayBuffer + let ab = new ArrayBuffer(byteString.length); + + // create a view into the buffer + let ia = new Uint8Array(ab); + + // set the bytes of the buffer to the correct values + for (let i = 0; i < byteString.length; i++) + { + ia[i] = byteString.charCodeAt(i); + } + + // write the ArrayBuffer to a blob, and you're done + let blob = new Blob([ab], { "type": mimeString }); + return blob; +} + +// change listeners +inUrlPort.onChange = function () +{ + if (inUrlPort.get()) + { + fromData = String(inUrlPort.get()).indexOf("data:") == 0; + + if (isLoading) + { + fromDataNew = String(inUrlPort.get()).indexOf("data:") == 0; + const newUrl = fromDataNew ? inUrlPort.get() : op.patch.getFilePath(inUrlPort.get()); + if (newUrl !== currentFileUrl) + { + urlToLoadNext = newUrl; + } + else + { + urlToLoadNext = null; + } + + clearAfterLoad = false; + return; + } + + invalidateOutPorts(); + const url = fromData ? inUrlPort.get() : op.patch.getFilePath(inUrlPort.get()); + + loadAudioFile(url, fromData); + } + else + { + if (isLoading) + { + clearAfterLoad = true; + return; + } + invalidateOutPorts(); + op.setUiError("wavFormat", null); + op.setUiError("failedLoading", null); + } +}; + +fileReader.onloadend = () => +{ + arrayBuffer = fileReader.result; + cgl.patch.loading.finished(loadingIdDataURL); + if (cgl.patch.isEditorMode()) gui.jobs().finish("loadaudio" + loadingIdDataURL); + loadFromDataURL(); +}; + +function loadFromDataURL() +{ + if (arrayBuffer) audioCtx.decodeAudioData(arrayBuffer, onLoadFinished, onLoadFailed); +} + +function onLoadFinished(buffer) +{ + isLoading = false; + outLoading.set(isLoading); + + if (clearAfterLoad) + { + invalidateOutPorts(); + clearAfterLoad = false; + return; + } + + if (urlToLoadNext) + { + loadAudioFile(urlToLoadNext, fromDataNew); + urlToLoadNext = null; + } + else + { + currentBuffer = buffer; + lengthPort.set(buffer.length); + durationPort.set(buffer.duration); + numberOfChannelsPort.set(buffer.numberOfChannels); + sampleRatePort.set(buffer.sampleRate); + audioBufferPort.set(buffer); + op.setUiError("failedLoading", null); + finishedLoadingPort.set(true); + fromData = false; + fromDataNew = false; + } +} + +function onLoadFailed(e) +{ + op.logError("Error: Loading audio file failed: ", e); + op.setUiError("failedLoading", "The audio file could not be loaded. Make sure the right file URL is used.", 2); + isLoading = false; + invalidateOutPorts(); + outLoading.set(isLoading); + currentBuffer = null; + + if (urlToLoadNext) + { + loadAudioFile(urlToLoadNext, fromDataNew); + urlToLoadNext = null; + } +} + +function invalidateOutPorts() +{ + lengthPort.set(0); + durationPort.set(0); + numberOfChannelsPort.set(0); + sampleRatePort.set(0); + + audioBufferPort.set(null); + + finishedLoadingPort.set(false); +} + + +}; + +Ops.WebAudio.AudioBuffer_v2.prototype = new CABLES.Op(); +CABLES.OPS["5f1d6a2f-1c04-4744-b0fb-910825beceee"]={f:Ops.WebAudio.AudioBuffer_v2,objName:"Ops.WebAudio.AudioBuffer_v2"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.AudioPanner +// +// ************************************************************** + +Ops.WebAudio.AudioPanner = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +function clamp(val, min, max) +{ + return Math.min(Math.max(val, min), max); +} + +const audioIn = op.inObject("audio in", null, "audioNode"); +const pan = op.inFloat("Pan", 0); +pan.onChange = updateGain; +const audioOut = op.outObject("audio out", null, "audioNode"); + +let audioContext = CABLES.WEBAUDIO.createAudioContext(op); + +let isIOS = false; +let panNode = null; +if (audioContext.createStereoPanner) +{ + panNode = audioContext.createStereoPanner(); +} +else +{ + panNode = audioContext.createPanner(); + panNode.panningModel = "equalpower"; + isIOS = true; +} + +function updateGain() +{ + const panning = clamp(pan.get(), -1, 1); + + if (!isIOS) panNode.pan.setValueAtTime(panning, audioContext.currentTime); + else + { + panNode.setPosition(panning, 0, 1 - Math.abs(panning)); + } +} + +let oldAudioIn = null; + +audioIn.onChange = function () +{ + if (!audioIn.get()) + { + if (oldAudioIn) + { + try + { + if (oldAudioIn.disconnect) + { + oldAudioIn.disconnect(panNode); + } + } + catch (e) + { + op.log(e); + } + } + + audioOut.set(null); + } + else + { + if (audioIn.get().connect) + { + audioIn.get().connect(panNode); + audioOut.set(panNode); + } + } + oldAudioIn = audioIn.get(); +}; + + +}; + +Ops.WebAudio.AudioPanner.prototype = new CABLES.Op(); +CABLES.OPS["313ebceb-fa16-4400-aa2b-20859614c7c6"]={f:Ops.WebAudio.AudioPanner,objName:"Ops.WebAudio.AudioPanner"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.AudioRecorder +// +// ************************************************************** + +Ops.WebAudio.AudioRecorder = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"inc_mediarecorder_js":"\nclass IOSMediaRecorder {\n /**\n * @param {MediaStream} stream The audio stream to record.\n */\n constructor (stream, config = null) {\n /**\n * The `MediaStream` passed into the constructor.\n * @type {MediaStream}\n */\n this.stream = stream\n this.config = config\n /**\n * The current state of recording process.\n * @type {\"inactive\"|\"recording\"|\"paused\"}\n */\n this.state = 'inactive'\n\n this.em = document.createDocumentFragment()\n const blob = new Blob([attachments.waveencoder_js], { \"type\": \"text/javascript\" });\n const fileURL = URL.createObjectURL(blob);\n this.blob = blob;\n this.fileURL = fileURL;\n this.encoder = new Worker(this.fileURL, { \"name\": \"AudioRecorder Encoder with op-id: \" + op.id });\n this.encoder.onmessage = e => {\n let event = new Event('dataavailable');\n\n event.data = new Blob([e.data], { type: this.mimeType });\n this.em.dispatchEvent(event);\n\n // clean up worker after recording\n this.encoder.terminate();\n this.encoder = null;\n\n if (this.state === 'inactive') {\n this.em.dispatchEvent(new Event('stop'))\n }\n };\n\n }\n\n error(method) {\n let event = new Event('error')\n event.data = new Error('Wrong state for ' + method)\n return event\n }\n\n terminateWorker() {\n if (this.encoder) {\n this.encoder.terminate();\n this.encoder = null;\n }\n }\n /**\n * Begins recording media.\n *\n * @param {number} [timeslice] The milliseconds to record into each `Blob`.\n * If this parameter isn’t included, single `Blob`\n * will be recorded.\n *\n * @return {undefined}\n *\n * @example\n * recordButton.addEventListener('click', () => {\n * recorder.start()\n * })\n */\n start (timeslice) {\n\n\n if (!this.encoder) {\n this.encoder = new Worker(this.fileURL, { \"name\": \"AudioRecorder Encoder with op-id: \" + op.id });\n this.encoder.onmessage = e => {\n let event = new Event('dataavailable');\n\n event.data = new Blob([e.data], { type: this.mimeType });\n this.em.dispatchEvent(event);\n\n // clean up worker after recording\n this.encoder.terminate();\n this.encoder = null;\n if (this.state === 'inactive') {\n this.em.dispatchEvent(new Event('stop'))\n }\n };\n }\n\n if (this.state !== 'inactive') {\n return this.em.dispatchEvent(this.error('start'))\n }\n\n this.state = 'recording'\n\n\n\n this.clone = this.stream.clone()\n this.input = audioCtx.createMediaStreamSource(this.clone)\n\n this.numberOfChannels = this.input.channelCount;\n\n this.processor = audioCtx.createScriptProcessor(2048, this.numberOfChannels, this.numberOfChannels);\n\n this.encoder.postMessage(['init', audioCtx.sampleRate])\n\n this.processor.onaudioprocess = e => {\n if (this.state === 'recording') {\n this.encoder.postMessage([\n 'encode',\n [\n [e.inputBuffer.getChannelData(0), e.inputBuffer.getChannelData(1)],\n this.numberOfChannels\n ],\n ]);\n }\n }\n\n this.input.connect(this.processor)\n this.processor.connect(audioCtx.destination)\n\n this.em.dispatchEvent(new Event('start'))\n\n if (timeslice) {\n this.slicing = setInterval(() => {\n if (this.state === 'recording') this.requestData()\n }, timeslice)\n }\n return undefined\n }\n\n /**\n * Stop media capture and raise `dataavailable` event with recorded data.\n *\n * @return {undefined}\n *\n * @example\n * finishButton.addEventListener('click', () => {\n * recorder.stop()\n * })\n */\n stop () {\n if (this.state === 'inactive') {\n return this.em.dispatchEvent(this.error('stop'))\n }\n\n this.requestData()\n this.state = 'inactive'\n this.clone.getTracks().forEach(track => {\n track.stop()\n })\n this.processor.disconnect()\n this.input.disconnect()\n return clearInterval(this.slicing)\n }\n\n /**\n * Pauses recording of media streams.\n *\n * @return {undefined}\n *\n * @example\n * pauseButton.addEventListener('click', () => {\n * recorder.pause()\n * })\n */\n pause () {\n if (this.state !== 'recording') {\n return this.em.dispatchEvent(this.error('pause'))\n }\n\n this.state = 'paused'\n return this.em.dispatchEvent(new Event('pause'))\n }\n\n /**\n * Resumes media recording when it has been previously paused.\n *\n * @return {undefined}\n *\n * @example\n * resumeButton.addEventListener('click', () => {\n * recorder.resume()\n * })\n */\n resume () {\n if (this.state !== 'paused') {\n return this.em.dispatchEvent(this.error('resume'))\n }\n\n this.state = 'recording'\n return this.em.dispatchEvent(new Event('resume'))\n }\n\n /**\n * Raise a `dataavailable` event containing the captured media.\n *\n * @return {undefined}\n *\n * @example\n * this.on('nextData', () => {\n * recorder.requestData()\n * })\n */\n requestData () {\n if (this.state === 'inactive') {\n return this.em.dispatchEvent(this.error('requestData'))\n }\n\n return this.encoder.postMessage([\n 'dump',\n [audioCtx.sampleRate, this.numberOfChannels],\n ]);\n }\n\n /**\n * Add listener for specified event type.\n *\n * @param {\"start\"|\"stop\"|\"pause\"|\"resume\"|\"dataavailable\"|\"error\"}\n * type Event type.\n * @param {function} listener The listener function.\n *\n * @return {undefined}\n *\n * @example\n * recorder.addEventListener('dataavailable', e => {\n * audio.src = URL.createObjectURL(e.data)\n * })\n */\n addEventListener (...args) {\n this.em.addEventListener(...args)\n }\n\n /**\n * Remove event listener.\n *\n * @param {\"start\"|\"stop\"|\"pause\"|\"resume\"|\"dataavailable\"|\"error\"}\n * type Event type.\n * @param {function} listener The same function used in `addEventListener`.\n *\n * @return {undefined}\n */\n removeEventListener (...args) {\n this.em.removeEventListener(...args)\n }\n\n /**\n * Calls each of the listeners registered for a given event.\n *\n * @param {Event} event The event object.\n *\n * @return {boolean} Is event was no canceled by any listener.\n */\n dispatchEvent (...args) {\n this.em.dispatchEvent(...args)\n }\n}\n\n/**\n * The MIME type that is being used for recording.\n * @type {string}\n */\nIOSMediaRecorder.prototype.mimeType = 'audio/wav'\n\n/**\n * Returns `true` if the MIME type specified is one the polyfill can record.\n *\n * This polyfill supports `audio/wav` and `audio/mpeg`.\n *\n * @param {string} mimeType The mimeType to check.\n *\n * @return {boolean} `true` on `audio/wav` and `audio/mpeg` MIME type.\n */\nIOSMediaRecorder.isTypeSupported = mimeType => {\n return IOSMediaRecorder.prototype.mimeType === mimeType\n}\n\n/**\n * `true` if MediaRecorder can not be polyfilled in the current browser.\n * @type {boolean}\n *\n * @example\n * if (IOSMediaRecorder.notSupported) {\n * showWarning('Audio recording is not supported in this browser')\n * }\n */\nIOSMediaRecorder.notSupported = !navigator.mediaDevices || !AudioContext\n\n/**\n * Converts RAW audio buffer to compressed audio files.\n * It will be loaded to Web Worker.\n * By default, WAVE encoder will be used.\n * @type {function}\n *\n * @example\n * IOSMediaRecorder.prototype.mimeType = 'audio/ogg'\n * IOSMediaRecorder.encoder = oggEncoder\n */","waveencoder_js":"function clamp(val, min, max)\n{\n return Math.min(Math.max(val, min), max);\n}\n\nlet BYTES_PER_SAMPLE = 2\n\n let recorded = []\n\n function encode ([buffers, numberOfChannels]) {\n let length = buffers[0].length\n let data = new Uint8Array(length * numberOfChannels * BYTES_PER_SAMPLE)\n // Interleave\n for (let i = 0; i < length; i++ ) {\n\n for (let channel = 0; channel < numberOfChannels; channel++ ) {\n const outputIndex = ( i * numberOfChannels + channel ) * BYTES_PER_SAMPLE;\n\n // clip the signal if it exceeds [-1, 1]\n let sample = clamp(buffers[channel][i], -1, 1);\n sample = sample * 32767.5 - 0.5;\n data[ outputIndex] = sample;\n data[ outputIndex + 1 ] = sample >> 8;\n }\n }\n recorded.push(data)\n }\n\n function dump ([sampleRate, numberOfChannels]) {\n let bufferLength = recorded.length ? recorded[0].length : 0\n let length = recorded.length * bufferLength\n let wav = new Uint8Array(44 + length)\n let view = new DataView(wav.buffer)\n\n // RIFF identifier 'RIFF'\n view.setUint32(0, 1380533830, false)\n // file length minus RIFF identifier length and file description length\n view.setUint32(4, 36 + length, true)\n // RIFF type 'WAVE'\n view.setUint32(8, 1463899717, false)\n // format chunk identifier 'fmt '\n view.setUint32(12, 1718449184, false)\n // format chunk length\n view.setUint32(16, 16, true)\n // sample format (raw)\n view.setUint16(20, 1, true)\n // channel count\n view.setUint16(22, numberOfChannels, true)\n // sample rate\n view.setUint32(24, sampleRate, true)\n // byte rate (sample rate * block align)\n view.setUint32(28, sampleRate * BYTES_PER_SAMPLE * numberOfChannels, true)\n // block align (channel count * bytes per sample)\n view.setUint16(32, BYTES_PER_SAMPLE * numberOfChannels, true)\n // bits per sample\n view.setUint16(34, 8 * BYTES_PER_SAMPLE, true)\n // data chunk identifier 'data'\n view.setUint32(36, 1684108385, false)\n // data chunk length\n view.setUint32(40, length, true)\n\n for (let i = 0; i < recorded.length; i++) {\n wav.set(recorded[i], i * bufferLength + 44)\n }\n\n recorded = []\n postMessage(wav.buffer, [wav.buffer])\n }\n\n onmessage = e => {\n if (e.data[0] === 'encode') {\n encode(e.data[1])\n } else if (e.data[0] === 'dump') {\n dump(e.data[1])\n }\n }",}; + +class IOSMediaRecorder { + /** + * @param {MediaStream} stream The audio stream to record. + */ + constructor (stream, config = null) { + /** + * The `MediaStream` passed into the constructor. + * @type {MediaStream} + */ + this.stream = stream + this.config = config + /** + * The current state of recording process. + * @type {"inactive"|"recording"|"paused"} + */ + this.state = 'inactive' + + this.em = document.createDocumentFragment() + const blob = new Blob([attachments.waveencoder_js], { "type": "text/javascript" }); + const fileURL = URL.createObjectURL(blob); + this.blob = blob; + this.fileURL = fileURL; + this.encoder = new Worker(this.fileURL, { "name": "AudioRecorder Encoder with op-id: " + op.id }); + this.encoder.onmessage = e => { + let event = new Event('dataavailable'); + + event.data = new Blob([e.data], { type: this.mimeType }); + this.em.dispatchEvent(event); + + // clean up worker after recording + this.encoder.terminate(); + this.encoder = null; + + if (this.state === 'inactive') { + this.em.dispatchEvent(new Event('stop')) + } + }; + + } + + error(method) { + let event = new Event('error') + event.data = new Error('Wrong state for ' + method) + return event + } + + terminateWorker() { + if (this.encoder) { + this.encoder.terminate(); + this.encoder = null; + } + } + /** + * Begins recording media. + * + * @param {number} [timeslice] The milliseconds to record into each `Blob`. + * If this parameter isn’t included, single `Blob` + * will be recorded. + * + * @return {undefined} + * + * @example + * recordButton.addEventListener('click', () => { + * recorder.start() + * }) + */ + start (timeslice) { + + + if (!this.encoder) { + this.encoder = new Worker(this.fileURL, { "name": "AudioRecorder Encoder with op-id: " + op.id }); + this.encoder.onmessage = e => { + let event = new Event('dataavailable'); + + event.data = new Blob([e.data], { type: this.mimeType }); + this.em.dispatchEvent(event); + + // clean up worker after recording + this.encoder.terminate(); + this.encoder = null; + if (this.state === 'inactive') { + this.em.dispatchEvent(new Event('stop')) + } + }; + } + + if (this.state !== 'inactive') { + return this.em.dispatchEvent(this.error('start')) + } + + this.state = 'recording' + + + + this.clone = this.stream.clone() + this.input = audioCtx.createMediaStreamSource(this.clone) + + this.numberOfChannels = this.input.channelCount; + + this.processor = audioCtx.createScriptProcessor(2048, this.numberOfChannels, this.numberOfChannels); + + this.encoder.postMessage(['init', audioCtx.sampleRate]) + + this.processor.onaudioprocess = e => { + if (this.state === 'recording') { + this.encoder.postMessage([ + 'encode', + [ + [e.inputBuffer.getChannelData(0), e.inputBuffer.getChannelData(1)], + this.numberOfChannels + ], + ]); + } + } + + this.input.connect(this.processor) + this.processor.connect(audioCtx.destination) + + this.em.dispatchEvent(new Event('start')) + + if (timeslice) { + this.slicing = setInterval(() => { + if (this.state === 'recording') this.requestData() + }, timeslice) + } + return undefined + } + + /** + * Stop media capture and raise `dataavailable` event with recorded data. + * + * @return {undefined} + * + * @example + * finishButton.addEventListener('click', () => { + * recorder.stop() + * }) + */ + stop () { + if (this.state === 'inactive') { + return this.em.dispatchEvent(this.error('stop')) + } + + this.requestData() + this.state = 'inactive' + this.clone.getTracks().forEach(track => { + track.stop() + }) + this.processor.disconnect() + this.input.disconnect() + return clearInterval(this.slicing) + } + + /** + * Pauses recording of media streams. + * + * @return {undefined} + * + * @example + * pauseButton.addEventListener('click', () => { + * recorder.pause() + * }) + */ + pause () { + if (this.state !== 'recording') { + return this.em.dispatchEvent(this.error('pause')) + } + + this.state = 'paused' + return this.em.dispatchEvent(new Event('pause')) + } + + /** + * Resumes media recording when it has been previously paused. + * + * @return {undefined} + * + * @example + * resumeButton.addEventListener('click', () => { + * recorder.resume() + * }) + */ + resume () { + if (this.state !== 'paused') { + return this.em.dispatchEvent(this.error('resume')) + } + + this.state = 'recording' + return this.em.dispatchEvent(new Event('resume')) + } + + /** + * Raise a `dataavailable` event containing the captured media. + * + * @return {undefined} + * + * @example + * this.on('nextData', () => { + * recorder.requestData() + * }) + */ + requestData () { + if (this.state === 'inactive') { + return this.em.dispatchEvent(this.error('requestData')) + } + + return this.encoder.postMessage([ + 'dump', + [audioCtx.sampleRate, this.numberOfChannels], + ]); + } + + /** + * Add listener for specified event type. + * + * @param {"start"|"stop"|"pause"|"resume"|"dataavailable"|"error"} + * type Event type. + * @param {function} listener The listener function. + * + * @return {undefined} + * + * @example + * recorder.addEventListener('dataavailable', e => { + * audio.src = URL.createObjectURL(e.data) + * }) + */ + addEventListener (...args) { + this.em.addEventListener(...args) + } + + /** + * Remove event listener. + * + * @param {"start"|"stop"|"pause"|"resume"|"dataavailable"|"error"} + * type Event type. + * @param {function} listener The same function used in `addEventListener`. + * + * @return {undefined} + */ + removeEventListener (...args) { + this.em.removeEventListener(...args) + } + + /** + * Calls each of the listeners registered for a given event. + * + * @param {Event} event The event object. + * + * @return {boolean} Is event was no canceled by any listener. + */ + dispatchEvent (...args) { + this.em.dispatchEvent(...args) + } +} + +/** + * The MIME type that is being used for recording. + * @type {string} + */ +IOSMediaRecorder.prototype.mimeType = 'audio/wav' + +/** + * Returns `true` if the MIME type specified is one the polyfill can record. + * + * This polyfill supports `audio/wav` and `audio/mpeg`. + * + * @param {string} mimeType The mimeType to check. + * + * @return {boolean} `true` on `audio/wav` and `audio/mpeg` MIME type. + */ +IOSMediaRecorder.isTypeSupported = mimeType => { + return IOSMediaRecorder.prototype.mimeType === mimeType +} + +/** + * `true` if MediaRecorder can not be polyfilled in the current browser. + * @type {boolean} + * + * @example + * if (IOSMediaRecorder.notSupported) { + * showWarning('Audio recording is not supported in this browser') + * } + */ +IOSMediaRecorder.notSupported = !navigator.mediaDevices || !AudioContext + +/** + * Converts RAW audio buffer to compressed audio files. + * It will be loaded to Web Worker. + * By default, WAVE encoder will be used. + * @type {function} + * + * @example + * IOSMediaRecorder.prototype.mimeType = 'audio/ogg' + * IOSMediaRecorder.encoder = oggEncoder + */// inspired by: https://github.com/kaliatech/web-audio-recording-tests/blob/master/src/shared/RecorderService.js + +const audioCtx = CABLES.WEBAUDIO.createAudioContext(op); + +const inAudio = op.inObject("Audio In", null, "audioNode"); +const inStartRecording = op.inTriggerButton("Start Recording"); +const inStopRecording = op.inTriggerButton("Stop Recording"); +const inRecordGain = op.inFloatSlider("Input Gain", 1); +op.setPortGroup("Recording", [inStartRecording, inStopRecording, inRecordGain]); + +const inStartPlayback = op.inTriggerButton("Start Playback"); +const inStopPlayback = op.inTriggerButton("Stop Playback"); +const inClearBuffer = op.inTriggerButton("Clear Buffer"); +const inPlaybackGain = op.inFloatSlider("Playback Gain", 1); +const inLoop = op.inBool("Loop Playback", false); +op.setPortGroup("Playback", [inStartPlayback, inStopPlayback, inLoop, inClearBuffer, inPlaybackGain]); +const inDownloadButton = op.inTriggerButton("Download .wav File"); + +const outOriginal = op.outObject("Audio Out", null, "audioNode"); +const outRecorded = op.outObject("Recorded Audio Out", null, "audioNode"); +const outIsRecording = op.outBool("Is Recording"); +const outIsPlayingBack = op.outBool("Is Playing Back"); +const outState = op.outString("State"); +const outBuffer = op.outObject("AudioBuffer Out", null, "audioBuffer"); +const outDataUrl = op.outString("Data URL"); + +inDownloadButton.setUiAttribs({ "greyout": true }); +outDataUrl.ignoreValueSerialize = true; + +const inputGain = audioCtx.createGain(); +const outputGain = audioCtx.createGain(); + +let isIOS = !navigator.hasOwnProperty("MediaRecorder"); + +const STATES = { + "RECORDING": "recording", + "PROCESSING": "processing", + "READY": "ready", + "PLAYING": "playing", + "IDLING": "idling" +}; + +let state = STATES.IDLING; +outState.set(state); + +const inputStream = audioCtx.createMediaStreamDestination(); + +let mediaRecorder = null; +let fileReader = null; +let blob = null; + +if (isIOS) +{ + mediaRecorder = new IOSMediaRecorder(inputStream.stream); + fileReader = new FileReader(); +} +else +{ + mediaRecorder = new MediaRecorder(inputStream.stream); +} + +mediaRecorder.addEventListener("dataavailable", (e) => +{ + blob = e.data; + if (blob) + { + inDownloadButton.setUiAttribs({ "greyout": !blob }); + let reader = new FileReader(); + reader.onload = function (e) + { + outDataUrl.set(e.target.result); + }; + reader.readAsDataURL(blob); + } + + if (!isIOS) + { + e.data.arrayBuffer() // its a blob + .then((buffer) => + { + arrayBuffer = buffer; + audioCtx.decodeAudioData(arrayBuffer, (buffer) => + { + audioBuffer = buffer; + outBuffer.set(audioBuffer); + createAudioBufferSource(); + }); + }) + .catch((e) => op.log(e)); + } + else + { + fileReader.onload = () => + { + arrayBuffer = fileReader.result; + audioCtx.decodeAudioData(arrayBuffer, (buffer) => + { + audioBuffer = buffer; + outBuffer.set(audioBuffer); + createAudioBufferSource(); + }); + }; + fileReader.readAsArrayBuffer(e.data); + } +}); + +let oldAudioIn = null; + +let isRecording = false; +let isPlayingBack = false; +let bufferReady = false; + +inStartRecording.onTriggered = () => +{ + switch (state) + { + case STATES.RECORDING: + return; + case STATES.PROCESSING: + return; + case STATES.PLAYING: + return; + case STATES.READY: + break; + case STATES.IDLING: + break; + } + + if (!inAudio.get()) + { + op.setUiError("noAudioInput", "No audio input is connected. Recording aborted.", 2); + state = STATES.IDLING; + return; + } + op.setUiError("noAudioInput", null); + op.setUiError("recording", "Recoding audio...", 0); + + mediaRecorder.start(); + state = STATES.RECORDING; + + isRecording = true; + outIsRecording.set(isRecording); + outState.set(state); +}; + +inStopRecording.onTriggered = () => +{ + switch (state) + { + case STATES.RECORDING: + break; + case STATES.PROCESSING: + return; + case STATES.PLAYING: + return; + case STATES.READY: + return; + case STATES.IDLING: + return; + } + + op.setUiError("recording", null); + state = STATES.PROCESSING; + isRecording = false; + outState.set(state); + + mediaRecorder.stop(); + outIsRecording.set(isRecording); + op.setUiError("stopRecording", "Recording stopped. Preparing...", 0); +}; + +inStartPlayback.onTriggered = () => +{ + switch (state) + { + case STATES.RECORDING: + return; + case STATES.PROCESSING: + return; + case STATES.PLAYING: + return; + case STATES.READY: + break; + case STATES.IDLING: + // if (loopSource) break; + return; + } + + op.setUiError("readyPlayback", null); + loopSource.start(); + isPlayingBack = true; + state = STATES.PLAYING; + outState.set(state); + outIsPlayingBack.set(isPlayingBack); + op.setUiError("playingLoop", "Loop is playing...", 0); +}; + +inStopPlayback.onTriggered = () => +{ + switch (state) + { + case STATES.RECORDING: + return; + case STATES.PROCESSING: + return; + case STATES.PLAYING: + break; + case STATES.READY: + return; + case STATES.IDLING: + return; + } + + op.setUiError("playingLoop", null); + loopSource.stop(); + isPlayingBack = false; + outIsPlayingBack.set(isPlayingBack); + state = STATES.IDLING; + outState.set(state); + createAudioBufferSource(); +}; + +inPlaybackGain.onChange = () => +{ + outputGain.gain.linearRampToValueAtTime(inPlaybackGain.get(), audioCtx.currentTime + 0.01); +}; + +let arrayBuffer = null; +let audioBuffer = null; +let loopSource = null; + +inLoop.onChange = () => +{ + if (loopSource) loopSource.loop = inLoop.get(); +}; + +function createAudioBufferSource() +{ + if (loopSource) + { + if (state === STATES.PLAYING) + { + loopSource.stop(); + isPlayingBack = false; + outIsPlayingBack.set(isPlayingBack); + loopSource.disconnect(outputGain); + } + } + + if (!audioBuffer) return; + + loopSource = audioCtx.createBufferSource(); + loopSource.buffer = audioBuffer; + + loopSource.onended = () => + { + if (!state !== STATES.IDLING) createAudioBufferSource(); + }; + loopSource.loop = inLoop.get(); + + loopSource.connect(outputGain); + outRecorded.set(outputGain); + bufferReady = true; + state = STATES.READY; + outState.set(state); + op.setUiError("stopRecording", null); +} + +inClearBuffer.onTriggered = () => +{ + switch (state) + { + case STATES.RECORDING: + return; + case STATES.PROCESSING: + return; + case STATES.PLAYING: + op.setUiError("playingLoop", null); + break; + case STATES.READY: + break; + case STATES.IDLING: + break; + } + + if (!audioBuffer) return; + + state = STATES.IDLING; + outState.set(state); + + if (isPlayingBack) + { + loopSource.stop(); + isPlayingBack = false; + outIsPlayingBack.set(isPlayingBack); + } + + audioBuffer = null; + outBuffer.set(audioBuffer); + blob = null; + inDownloadButton.setUiAttribs({ "greyout": true }); + outDataUrl.set(null); +}; + +inDownloadButton.onTriggered = () => +{ + if (!blob) return; + + const anchor = document.createElement("a"); + + anchor.download = "AudioRecorder " + op.id + ".wav"; + anchor.href = URL.createObjectURL(blob); + + setTimeout(function () + { + anchor.click(); + }, 100); +}; + +inAudio.onLinkChanged = () => +{ + if (!inAudio.isLinked()) + { + switch (state) + { + case STATES.RECORDING: + break; + case STATES.PROCESSING: + return; + case STATES.PLAYING: + return; + case STATES.READY: + return; + case STATES.IDLING: + return; + } + + mediaRecorder.stop(); + if (isIOS) mediaRecorder.terminateWorker(); + state = STATES.IDLING; + outState.set(state); + } +}; + +op.onDelete = () => +{ + switch (state) + { + case STATES.RECORDING: + mediaRecorder.stop(); + break; + case STATES.PROCESSING: + break; + case STATES.PLAYING: + break; + case STATES.READY: + break; + case STATES.IDLING: + break; + } + if (isIOS) mediaRecorder.terminateWorker(); +}; + +inAudio.onChange = () => +{ + if (!inAudio.get()) + { + if (oldAudioIn) + { + try + { + if (oldAudioIn.disconnect) + { + oldAudioIn.disconnect(inputGain); + oldAudioIn.disconnect(inputStream); + } + } + catch (e) + { + op.log(e); + } + } + + outOriginal.set(null); + } + else + { + if (inAudio.get().connect) + { + inAudio.get().connect(inputGain); + inAudio.get().connect(inputStream); + } + + outOriginal.set(inputGain); + } + + oldAudioIn = inAudio.get(); +}; + +op.onDelete = () => +{ + if (loopSource) + { + loopSource.disconnect(); + } +}; + + +}; + +Ops.WebAudio.AudioRecorder.prototype = new CABLES.Op(); +CABLES.OPS["b4d46521-a8f8-4d54-abdb-98f1bfb80edd"]={f:Ops.WebAudio.AudioRecorder,objName:"Ops.WebAudio.AudioRecorder"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.BiquadFilter_v2 +// +// ************************************************************** + +Ops.WebAudio.BiquadFilter_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +function clamp(val, min, max) +{ + return Math.min(Math.max(val, min), max); +} + +const audioContext = CABLES.WEBAUDIO.createAudioContext(op); + +// default values + min and max +const FREQUENCY_MIN = 10; +const FREQUENCY_MAX = audioContext.sampleRate / 2; // Nyquist frequency. +const Q_MIN = 0.0001; +const Q_MAX = 1000; +const GAIN_MIN = -40; +const GAIN_MAX = 40; + +const filterNode = audioContext.createBiquadFilter(); + +const inAudio = op.inObject("Audio In", null, "audioNode"); +const inFilterType = op.inDropDown("Type", ["peaking", "lowpass", "highpass", "bandpass", "lowshelf", "highshelf", "notch", "allpass"], "peaking"); + +const inFrequency = op.inFloat("Frequency", 2000); +const inQ = op.inFloat("Q", 0.0001); +const inGain = op.inFloat("Gain", 0); + +const inDetune = op.inInt("Detune", 0); +const inFrequencyArray = op.inArray("Frequency Array"); + +op.setPortGroup("Filter Settings", [inFilterType, inFrequency, inQ, inGain]); +op.setPortGroup("Detune (in cents)", [inDetune]); +op.setPortGroup("Filter Response Input", [inFrequencyArray]); +const outAudio = op.outObject("Audio Out", null, "audioNode"); +const outMagnitudeResponseArray = op.outArray("Magnitude Response Array"); +const outPhaseResponseArray = op.outArray("Phase Response Array"); +const responseArraysLength = op.outNumber("Response Arrays Length"); + +let oldAudioIn = null; +filterNode.type = inFilterType.get(); + +let oldLength = 0; +let frequencyArray = null; +let phaseResponseArray = null; +let magnitudeResponseArray = null; + +function updateFrequencyResponse() +{ + const frequencies = inFrequencyArray.get(); + + if (!frequencies) return; + + if (inAudio.get()) + { + if (oldLength !== frequencies.length) + { + frequencyArray = new Float32Array(frequencies); + phaseResponseArray = new Float32Array(frequencies.length); + magnitudeResponseArray = new Float32Array(frequencies.length); + oldLength = frequencies.length; + } + + if (oldLength) + { + filterNode.getFrequencyResponse(frequencyArray, phaseResponseArray, magnitudeResponseArray); + + outMagnitudeResponseArray.set(null); + outMagnitudeResponseArray.set(magnitudeResponseArray); + + outPhaseResponseArray.set(null); + outPhaseResponseArray.set(phaseResponseArray); + + responseArraysLength.set(frequencies.length); + } + else + { + outMagnitudeResponseArray.set(null); + outPhaseResponseArray.set(null); + responseArraysLength.set(0); + } + } +} + +inFrequencyArray.onChange = () => updateFrequencyResponse(); +inAudio.onChange = function () +{ + if (!inAudio.get()) + { + if (oldAudioIn) + { + try + { + if (oldAudioIn.disconnect) oldAudioIn.disconnect(filterNode); + } + catch (e) + { + op.log(e); + } + } + + outAudio.set(null); + } + else + { + if (inAudio.get().connect) inAudio.get().connect(filterNode); + } + oldAudioIn = inAudio.get(); + outAudio.set(filterNode); +}; + +inFilterType.onChange = () => +{ + const type = inFilterType.get(); + inGain.setUiAttribs({ + "greyout": ["lowpass", "highpass", "bandpass", "notch", "allpass"].includes(type) + }); + + inQ.setUiAttribs({ + "greyout": ["lowshelf", "highshelf"].includes(type) + }); + + filterNode.type = type; + updateFrequencyResponse(); +}; + +inFrequency.onChange = () => +{ + const freq = inFrequency.get(); + if (freq) + { + if (freq >= FREQUENCY_MIN && freq <= FREQUENCY_MAX) + { + filterNode.frequency.setValueAtTime(clamp(freq, FREQUENCY_MIN, FREQUENCY_MAX), audioContext.currentTime); + op.setUiError("freqRange", null); + } + if (freq < FREQUENCY_MIN) + { + op.setUiError("freqRange", "The frequency you selected is lower than the possible frequency of " + FREQUENCY_MIN + " Hz.", 1); + } + else if (freq > FREQUENCY_MAX) + { + op.setUiError("freqRange", "The frequency you selected is higher than the possible frequency of " + FREQUENCY_MAX + " Hz.", 1); + } + } + updateFrequencyResponse(); +}; + +inDetune.onChange = () => +{ + filterNode.detune.setValueAtTime(inDetune.get(), audioContext.currentTime); + updateFrequencyResponse(); +}; + +inQ.onChange = () => +{ + const q = inQ.get(); + filterNode.Q.setValueAtTime(clamp(q, Q_MIN, Q_MAX), audioContext.currentTime); + + if (q < Q_MIN) op.setUiError("qRange", "Your Q value is below the minimum possible value of " + Q_MIN + ".", 1); + else if (q > Q_MAX) op.setUiError("qRange", "Your Q value is above the maximum possible value of " + Q_MAX + ".", 1); + else + { + op.setUiError("qRange", null); + } + updateFrequencyResponse(); +}; + +inGain.onChange = () => +{ + const gain = inGain.get(); + filterNode.gain.setValueAtTime(clamp(gain, GAIN_MIN, GAIN_MAX), audioContext.currentTime); + if (gain < GAIN_MIN) op.setUiError("gainRange", "Your gain value is below the minimum possible value of " + GAIN_MIN + " dB.", 1); + else if (gain > GAIN_MAX) op.setUiError("gainRange", "Your gain value is above the maximum possible value of " + GAIN_MAX + " dB.", 1); + else + { + op.setUiError("gainRange", null); + } + updateFrequencyResponse(); +}; + + +}; + +Ops.WebAudio.BiquadFilter_v2.prototype = new CABLES.Op(); +CABLES.OPS["a7b30545-6db3-47b5-8ffc-f659accd0eb6"]={f:Ops.WebAudio.BiquadFilter_v2,objName:"Ops.WebAudio.BiquadFilter_v2"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.ClockSequencer +// +// ************************************************************** + +Ops.WebAudio.ClockSequencer = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"worker_js":"let timerIDs = [];\nlet timerID = null;\nconst LOOKAHEAD_IN_MS = 25.;\nonmessage = (e) => {\n if (e.data === \"start\") {\n timerID = setInterval(() => postMessage(\"tick\"), LOOKAHEAD_IN_MS);\n return;\n }\n\n if (e.data === \"stop\") {\n clearInterval(timerID);\n timerID = null;\n console.log(\"stopped\");\n }\n};\n\nonerror = (e) => {\n console.log(\"worker error\", e);\n}\n",}; +const audioCtx = CABLES.WEBAUDIO.createAudioContext(op); + +const inBPM = op.inInt("BPM", 100); +const inStart = op.inTriggerButton("Start"); +const inStop = op.inTriggerButton("Stop"); +const inReset = op.inTriggerButton("Reset"); + +const outTriggers = []; +for (let i = 0; i < 7 * 3; i += 1) +{ + const noteValue = Math.pow(2, i % 7); + let string = "1/" + noteValue; + if (i > 6 && i < 14) + { + string = "1/" + noteValue + " Triplet"; + } + else if (i > 13) + { + string = "1/" + noteValue + " Dotted"; + } + outTriggers[i] = op.outTrigger(string + " Note Trigger"); +} + +const outRunning = op.outBool("Sequencer Running"); +const outBPM = op.outNumber("BPM Out"); +const outStart = op.outTrigger("Start Out"); +const outStop = op.outTrigger("Stop Out"); +const outReset = op.outTrigger("Reset Out"); + +const MIN_BPM = 20; + +const NOTE_QUEUE = []; +const LOOKAHEAD_IN_MS = 25.0; +const SCHEDULEAHEAD_IN_S = 0.1; + +const MULTIPLIERS = [ + 4, 2, 1, 1 / 2, 1 / 4, 1 / 8, 1 / 16, + 8 / 3, 4 / 3, 2 / 3, 1 / 3, 1 / 6, 1 / 12, 1 / 24, // triplet + 6, 3, 3 / 2, 3 / 4, 3 / 8, 3 / 16, 3 / 32 // dotted +]; + +const MODULO_PER_NOTE = MULTIPLIERS.map((val) => Math.floor(val * 48 / 2)); +const TICK_INDEX = 7 * 2 - 1; // 1/64 triplet fastest note +const MAX_ENUMERATOR = 288; +let NOTES_IN_S = []; +let QUARTER_NOTE_S = 60 / inBPM.get(); +NOTES_IN_S = MULTIPLIERS.map((multiplier) => multiplier * QUARTER_NOTE_S); +let TICK_S = NOTES_IN_S[TICK_INDEX] / 2; + +outBPM.set(inBPM.get()); + +let resetTickCount = false; +let changeWhileRunning = false; +inBPM.onChange = updateBpm; + +let worker = null; +let isPlaying = false; +let currentNote = 0; +let nextNoteTime = null; +let tickCount = 0; +let workerRunning = false; +let waitForSchedule = false; +updateBpm(); + +function updateBpm() +{ + outBPM.set(inBPM.get()); + + if (workerRunning) + { + changeWhileRunning = true; + return; + } + QUARTER_NOTE_S = 60 / inBPM.get(); + NOTES_IN_S = MULTIPLIERS.map((multiplier) => multiplier * QUARTER_NOTE_S); + TICK_S = NOTES_IN_S[TICK_INDEX]; +} + +function nextNote() +{ + nextNoteTime += TICK_S; + tickCount = (tickCount + 1) % MAX_ENUMERATOR; +} + +function scheduleNote() +{ + if (waitForSchedule) + { // code block to swallow initial hickup when starting the sequencer + let compareValue = 8; + if (inBPM.get() > 140) compareValue = 12; + if (inBPM.get() > 160) compareValue = 20; + if (tickCount === compareValue) + { // half of highest value + resetTickCount = true; + waitForSchedule = false; + } + else + { + return; + } + } + + if (resetTickCount) + { + tickCount = 0; + resetTickCount = false; + } + for (let i = 0, len = MODULO_PER_NOTE.length; i < len; i += 1) + { + if (tickCount % MODULO_PER_NOTE[i] === 0) outTriggers[i].trigger(); + } +} +function startScheduling() +{ + if (changeWhileRunning) + { + QUARTER_NOTE_S = 60 / inBPM.get(); + NOTES_IN_S = MULTIPLIERS.map((multiplier) => multiplier * QUARTER_NOTE_S); + TICK_S = NOTES_IN_S[TICK_INDEX]; + changeWhileRunning = false; + } + while (nextNoteTime < audioCtx.currentTime + SCHEDULEAHEAD_IN_S) + { + scheduleNote(); + nextNote(); + } +} + +inStart.onTriggered = () => +{ + if (workerRunning) return; + + if (!worker) + { + const blob = new Blob([attachments.worker_js], { "type": "text/javascript" }); + const fileURL = URL.createObjectURL(blob); + + worker = new Worker(fileURL, { "name": "ClockSequencer with op-id: " + op.id }); + worker.addEventListener("message", (e) => + { + if (e.data === "tick") startScheduling(); + if (e.data === "stopped") workerRunning = false; + }, + false); + + nextNoteTime = audioCtx.currentTime; + /* dummy buffer source for time */ + const audioBuffer = audioCtx.createBufferSource(); + audioBuffer.start(0); + workerRunning = true; + tickCount = 0; + worker.postMessage("start"); + waitForSchedule = true; + outRunning.set(workerRunning); + } + + outStart.trigger(); +}; + +inStop.onTriggered = () => +{ + if (worker) + { + worker.postMessage("stop"); + worker.terminate(); + worker = null; + workerRunning = false; + outRunning.set(workerRunning); + } + + outStop.trigger(); +}; + +inReset.onTriggered = () => +{ + resetTickCount = true; + outReset.trigger(); +}; + +op.onDelete = () => +{ + if (!inStart.isLinked()) + { + if (worker) + { + worker.postMessage("stop"); + worker.terminate(); + worker = null; + workerRunning = false; + } + } +}; +inStart.onLinkChanged = () => +{ + if (!inStart.isLinked()) + { + if (worker) + { + worker.postMessage("stop"); + worker.terminate(); + worker = null; + workerRunning = false; + outRunning.set(workerRunning); + } + } +}; + + +}; + +Ops.WebAudio.ClockSequencer.prototype = new CABLES.Op(); +CABLES.OPS["7994c33f-d4ca-455b-af72-83dcbf5ae83f"]={f:Ops.WebAudio.ClockSequencer,objName:"Ops.WebAudio.ClockSequencer"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.ClockSequencerPattern +// +// ************************************************************** + +Ops.WebAudio.ClockSequencerPattern = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const STEPS = Array(32).fill().map((_, i) => 1 + i); + +const inTrigger = op.inTrigger("Clock Trigger Input"); +const inSequenceArray = op.inArray("Sequence Array"); +const inSteps = op.inDropDown("Steps", STEPS, "16"); +const inReset = op.inTriggerButton("Reset"); + +const outTrigger = op.outTrigger("Sequence Trigger Output"); +const outValue = op.outNumber("Sequenced Value"); +const outTickCount = op.outNumber("Current Step"); + +let updateParameters = false; +let arrayChanged = false; +let hasArray = false; + +let COUNT_MODULO = Number(inSteps.get()); +let tickCount = 0; +inSteps.onChange = () => +{ + if (inTrigger.isLinked()) + { + updateParameters = true; + return; + } + COUNT_MODULO = Number(inSteps.get()); +}; + +inSequenceArray.onChange = () => arrayChanged = true; +let resetCount = false; +inReset.onTriggered = () => resetCount = true; + +inTrigger.onTriggered = () => +{ + if (updateParameters) + { + COUNT_MODULO = Number(inSteps.get()); + updateParameters = false; + } + + if (resetCount) + { + tickCount = 0; + resetCount = false; + } + const arr = inSequenceArray.get(); + + if (arrayChanged) + { + if (!arr) + { + op.setUiError("noArr", "No array connected. Passing through clock.", 1); + hasArray = false; + } + else + { + op.setUiError("noArr", null); + hasArray = true; + } + arrayChanged = false; + } + + if (hasArray) + { + if (arr[tickCount]) + { + outTrigger.trigger(); + outValue.set(arr[tickCount]); + } + else + { + outValue.set(0); + } + } + else + { + outTrigger.trigger(); + } + outTickCount.set(tickCount); + tickCount = (tickCount + 1) % COUNT_MODULO; +}; + + +}; + +Ops.WebAudio.ClockSequencerPattern.prototype = new CABLES.Op(); +CABLES.OPS["3b9b9ff5-4c25-42e3-9897-262b11c9cb94"]={f:Ops.WebAudio.ClockSequencerPattern,objName:"Ops.WebAudio.ClockSequencerPattern"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.Convolver_v2 +// +// ************************************************************** + +Ops.WebAudio.Convolver_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +function clamp(val, min, max) +{ + return Math.min(Math.max(val, min), max); +} + +const MAX_DELAY_TIME_IN_SECONDS = 179.999; + +const cgl = op.patch.cgl; +const audioContext = CABLES.WEBAUDIO.createAudioContext(op); + +const audioIn = op.inObject("Audio In", null, "audioNode"); + +const impulseResponse = op.inUrl("Impulse Response"); +const normalize = op.inBool("Normalize", true); +const inConvolverGain = op.inFloatSlider("IR Gain", 0.7); +const inPreDelayMS = op.inFloat("Predelay (MS)", 0); + +const inDryWet = op.inFloatSlider("Dry/Wet", 0.5); +const inOutputGain = op.inFloatSlider("Output Gain", 1); + +const audioOut = op.outObject("Audio Out", null, "audioNode"); +const wetOut = op.outObject("Wet Out", null, "audioNode"); + +op.setPortGroup("IR Options", [impulseResponse, inConvolverGain, normalize, inPreDelayMS]); +op.setPortGroup("Output", [inDryWet, inOutputGain]); + +const convolver = audioContext.createConvolver(); +const convolverGain = audioContext.createGain(); +const predelayNode = audioContext.createDelay(MAX_DELAY_TIME_IN_SECONDS); +const dryNode = audioContext.createGain(); +const wetNode = audioContext.createGain(); +const inputNode = audioContext.createGain(); +const outputNode = audioContext.createGain(); + +wetNode.gain.value = parseFloat(inDryWet.get()); +dryNode.gain.value = 1.0 - parseFloat(inDryWet.get()); + +wetNode.connect(outputNode); +dryNode.connect(outputNode); +inputNode.connect(dryNode); + +inputNode.connect(predelayNode); +predelayNode.connect(convolverGain); +convolverGain.connect(convolver); +convolver.connect(wetNode); + +let oldAudioIn = null; +let myImpulseBuffer = null; +let impulseResponseLoaded = false; +let scheduleConnection = false; +let loadingId = null; + +impulseResponse.onChange = () => +{ + loadingId = cgl.patch.loading.start("IR convolver", ""); + const impulseUrl = impulseResponse.get(); + + const ajaxRequest = new XMLHttpRequest(); + + if (impulseUrl) + { + const url = op.patch.getFilePath(impulseUrl); + const ext = url.substr(url.lastIndexOf(".") + 1); + + if (ext === "wav") + { + op.setUiError("wavExt", "Even though impulse responses are .wav files most of the time, if you plan on using WebAudio in Safari, make sure you use a .wav file that is 16bit or use an .mp3 file instead.", 1); + } + else + { + op.setUiError("wavExt", null); + } + + impulseResponseLoaded = false; + ajaxRequest.open("GET", url, true); + ajaxRequest.responseType = "arraybuffer"; + ajaxRequest.onload = function () + { + const impulseData = ajaxRequest.response; + + audioContext.decodeAudioData(impulseData, function (buffer) + { + if (buffer.sampleRate != audioContext.sampleRate) + { + op.log("[impulse response] Sample rate of the impulse response does not match! Should be " + audioContext.sampleRate); + op.setUiError("wrongSampleRate", "Sample rate of the impulse response does not match! Should be " + audioContext.sampleRate, 2); + return; + } + else + { + op.setUiError("wrongSampleRate", null); + } + myImpulseBuffer = buffer; + convolver.buffer = myImpulseBuffer; + convolver.loop = false; + convolver.normalize = normalize.get(); + convolverGain.gain.value = inConvolverGain.get(); + + audioOut.set(null); + + try + { + if (audioIn.get()) + { + audioIn.get().connect(inputNode); + audioOut.set(outputNode); + wetOut.set(convolver); + } + else + { + scheduleConnection = true; + } + } + catch (e) + { + op.log("[audio in] Could not connect audio in to convolver" + e); + } + + op.log("[impulse response] Impulse Response (" + impulseUrl + ") loaded"); + + impulseResponseLoaded = true; + cgl.patch.loading.finished(loadingId); + op.setUiError("noIR", null); + }, function (e) + { + op.log("[impulse response] Error decoding audio data" + e.err); + impulseResponseLoaded = false; + cgl.patch.loading.finished(loadingId); + }); + }; + + ajaxRequest.send(); + } + else + { + impulseResponseLoaded = false; + op.setUiError("noIR", "No impulse response loaded. Original audio will be passed through the Audio Out output.", 1); + op.setUiError("wavExt", null); + + convolver.buffer = null; + audioOut.set(outputNode); + } +}; + +inConvolverGain.onChange = () => +{ + convolverGain.gain.linearRampToValueAtTime(Number(inConvolverGain.get()) || 0, audioContext.currentTime + 0.01); +}; + +inDryWet.onChange = () => +{ + wetNode.gain.linearRampToValueAtTime(Number(inDryWet.get()), audioContext.currentTime + 0.01); + dryNode.gain.linearRampToValueAtTime(1 - Number(inDryWet.get()), audioContext.currentTime + 0.01); +}; + +inOutputGain.onChange = () => +{ + outputNode.gain.linearRampToValueAtTime(inOutputGain.get(), audioContext.currentTime + 0.01); +}; + +inPreDelayMS.onChange = () => +{ + if (inPreDelayMS.get() < 0) op.setUiError("delayTime", "Pre-Delay should be between 0 ms and " + MAX_DELAY_TIME_IN_SECONDS * 1000 + " ms. Setting to 0.", 1); + else if (inPreDelayMS.get() > MAX_DELAY_TIME_IN_SECONDS * 1000) op.setUiError("delayTime", "Pre-Delay should be between 0 ms and " + MAX_DELAY_TIME_IN_SECONDS * 1000 + " ms. Setting to " + MAX_DELAY_TIME_IN_SECONDS * 1000 + ".", 1); + else op.setUiError("delayTime", null); + + const predelayMS = clamp(inPreDelayMS.get(), 0.0, MAX_DELAY_TIME_IN_SECONDS) / 1000; + predelayNode.delayTime.linearRampToValueAtTime(predelayMS, audioContext.currentTime + 0.05); +}; + +audioIn.onChange = function () +{ + if (audioIn.get()) + { + if (!audioIn.get().connect) + { + oldAudioIn = null; + return; + } + + op.log("[audio in] connected"); + + try + { + audioIn.get().connect(inputNode); + + oldAudioIn = audioIn.get(); + } + catch (e) + { + op.log("[audio in] Could not connect" + e); + } + + if (!impulseResponseLoaded) + { + op.setUiError("noIR", "No impulse response loaded. Original audio will be passed through the Audio Out output.", 1); + audioOut.set(outputNode); + } + else + { + op.setUiError("noIR", null); + audioOut.set(outputNode); + wetOut.set(convolver); + } + } + else + { + if (impulseResponseLoaded) + { + op.setUiError("noIR", null); + } + + if (oldAudioIn) + { + oldAudioIn.disconnect(inputNode); + } + audioOut.set(null); + wetOut.set(null); + + oldAudioIn = null; + } +}; + +normalize.onChange = function () +{ + convolver.normalize = normalize.get(); +}; + +op.onDelete = () => +{ + wetNode.disconnect(); + dryNode.disconnect(); + inputNode.disconnect(); + predelayNode.disconnect(); + convolverGain.disconnect(); + convolver.disconnect(); +}; + + +}; + +Ops.WebAudio.Convolver_v2.prototype = new CABLES.Op(); +CABLES.OPS["63c619be-2256-4bbe-a83f-36b2bb6faa52"]={f:Ops.WebAudio.Convolver_v2,objName:"Ops.WebAudio.Convolver_v2"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.CutFilter +// +// ************************************************************** + +Ops.WebAudio.CutFilter = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +function clamp(val, min, max) +{ + return Math.min(Math.max(val, min), max); +} + +let audioContext = CABLES.WEBAUDIO.createAudioContext(op); + +// default values + min and max +const FREQUENCY_MIN = 10; +const FREQUENCY_MAX = audioContext.sampleRate / 2; // Nyquist frequency. +const Q_MIN = 0.0001; +const Q_MAX = 1000; +const GAIN_MIN = -40; +const GAIN_MAX = 40; +const SLOPES = [12, 24, 36, 48]; +const inAudio = op.inObject("Audio In", null, "audioNode"); + +const inLowActive = op.inBool("Highpass active ", true); +const inLowSlope = op.inSwitch("Highpass Slope (in dB)", SLOPES, 12); +const inLowFrequency = op.inFloat("Low Frequency", 250); +const inLowQ = op.inFloat("Low Q", 0.0001); +op.setPortGroup("Highpass / Lowcut", [inLowActive, inLowSlope, inLowFrequency, inLowQ]); + +const inHighActive = op.inBool("Lowpass active ", true); +const inHighSlope = op.inSwitch("Lowpass Slope (in dB)", SLOPES, 12); +const inHighFrequency = op.inFloat("High Frequency", 5000); +const inHighQ = op.inFloat("High Q", 0.0001); +op.setPortGroup("Lowpass / Highcut", [inHighActive, inHighSlope, inHighFrequency, inHighQ]); + +const lowFilterNodes = SLOPES.map(entry => audioContext.createBiquadFilter()); +const highFilterNodes = SLOPES.map(() => audioContext.createBiquadFilter()); + +/* instantiation */ +lowFilterNodes.forEach((node, index) => +{ + if (index === 0) node.type = "highpass"; + else node.type = "peaking"; + + const freq = inLowFrequency.get(); + node.frequency.setValueAtTime(clamp(freq, FREQUENCY_MIN, FREQUENCY_MAX), audioContext.currentTime); + node.gain.setValueAtTime(clamp(0, GAIN_MIN, GAIN_MAX), audioContext.currentTime); + node.Q.setValueAtTime(clamp(Number(inLowQ.get()), Q_MIN, Q_MAX), audioContext.currentTime); + + if (index < SLOPES.length - 1) node.connect(lowFilterNodes[index + 1]); + else node.connect(highFilterNodes[0]); +}); + +highFilterNodes.forEach((node, index) => +{ + if (index === 0) node.type = "lowpass"; + else node.type = "peaking"; + + const freq = inHighFrequency.get(); + node.frequency.setValueAtTime(clamp(freq, FREQUENCY_MIN, FREQUENCY_MAX), audioContext.currentTime); + node.gain.setValueAtTime(clamp(0, GAIN_MIN, GAIN_MAX), audioContext.currentTime); + node.Q.setValueAtTime(clamp(Number(inHighQ.get()), Q_MIN, Q_MAX), audioContext.currentTime); + + if (index < SLOPES.length - 1) node.connect(highFilterNodes[index + 1]); +}); + +/* onChange handlers */ + +let lastHighState = highFilterNodes.map(node => node.type); + +inHighActive.onChange = () => +{ + inHighSlope.setUiAttribs({ "greyout": !inHighActive.get() }); + inHighFrequency.setUiAttribs({ "greyout": !inHighActive.get() }); + inHighQ.setUiAttribs({ "greyout": !inHighActive.get() }); + + if (inHighActive.get()) + { + for (let i = 0; i < SLOPES.length; i += 1) + { + const node = highFilterNodes[i]; + node.type = lastHighState[i]; + } + } + else + { + lastHighState = highFilterNodes.map(node => node.type); + + for (let i = 0; i < SLOPES.length; i += 1) + { + const node = highFilterNodes[i]; + node.type = "peaking"; + } + } +}; + +let lastLowState = lowFilterNodes.map(node => node.type); + +inLowActive.onChange = () => +{ + inLowSlope.setUiAttribs({ "greyout": !inLowActive.get() }); + inLowFrequency.setUiAttribs({ "greyout": !inLowActive.get() }); + inLowQ.setUiAttribs({ "greyout": !inLowActive.get() }); + + if (inLowActive.get()) + { + for (let i = 0; i < SLOPES.length; i += 1) + { + const node = lowFilterNodes[i]; + node.type = lastLowState[i]; + } + } + else + { + lastLowState = lowFilterNodes.map(node => node.type); + + for (let i = 0; i < SLOPES.length; i += 1) + { + const node = lowFilterNodes[i]; + node.type = "peaking"; + } + } +}; + +inHighFrequency.onChange = () => +{ + if (inHighActive.get()) + { + const freq = inHighFrequency.get(); + + if (freq >= FREQUENCY_MIN && freq <= FREQUENCY_MAX) op.setUiError("freqRangeHigh", null); + + if (freq < FREQUENCY_MIN) + { + op.setUiError("freqRangeHigh", "The frequency you selected for the lowpass filter is lower than the possible frequency of " + FREQUENCY_MIN + " Hz.", 1); + } + else if (freq > FREQUENCY_MAX) + { + op.setUiError("freqRangeHigh", "The frequency you selected for the lowpass filter is higher than the possible frequency of " + FREQUENCY_MAX + " Hz.", 1); + } + + for (let i = 0; i < SLOPES.length; i += 1) + { + const node = highFilterNodes[i]; + node.frequency.setValueAtTime(clamp(freq, FREQUENCY_MIN, FREQUENCY_MAX), audioContext.currentTime); + } + } +}; + +inLowFrequency.onChange = () => +{ + if (inLowActive.get()) + { + const freq = inLowFrequency.get(); + + if (freq >= FREQUENCY_MIN && freq <= FREQUENCY_MAX) op.setUiError("freqRangeLow", null); + + if (freq < FREQUENCY_MIN) + { + op.setUiError("freqRangeLow", "The frequency you selected for the highpass filter is lower than the possible frequency of " + FREQUENCY_MIN + " Hz.", 1); + } + else if (freq > FREQUENCY_MAX) + { + op.setUiError("freqRangeLow", "The frequency you selected for the highpass filter is higher than the possible frequency of " + FREQUENCY_MAX + " Hz.", 1); + } + + for (let i = 0; i < SLOPES.length; i += 1) + { + const node = lowFilterNodes[i]; + node.frequency.setValueAtTime(clamp(freq, FREQUENCY_MIN, FREQUENCY_MAX), audioContext.currentTime); + } + } +}; + +inHighSlope.onChange = () => +{ + if (inHighActive.get()) + { + const cascadeAmount = SLOPES.indexOf(Number(inHighSlope.get())); + if (cascadeAmount < 0) return; + + for (let i = 0; i < SLOPES.length; i += 1) + { + const node = highFilterNodes[i]; + if (i <= cascadeAmount) node.type = "lowpass"; + else node.type = "peaking"; + } + } +}; + +inLowSlope.onChange = () => +{ + if (inLowActive.get()) + { + const cascadeAmount = SLOPES.indexOf(Number(inLowSlope.get())); + + if (cascadeAmount < 0) return; + + for (let i = 0; i < SLOPES.length; i += 1) + { + const node = lowFilterNodes[i]; + if (i <= cascadeAmount) node.type = "highpass"; + else node.type = "peaking"; + } + } +}; + +inHighQ.onChange = () => +{ + if (inHighActive.get()) + { + const q = inHighQ.get(); + + if (q < Q_MIN) op.setUiError("qRangeHigh", "Your Q value for the lowpass filter is below the minimum possible value of " + Q_MIN + ".", 1); + else if (q > Q_MAX) op.setUiError("qRangeHigh", "Your Q value for lowpass filter is above the maximum possible value of " + Q_MAX + ".", 1); + else op.setUiError("qRangeHigh", null); + + for (let i = 0; i < SLOPES.length; i += 1) + { + const node = highFilterNodes[i]; + node.Q.setValueAtTime(clamp(q, Q_MIN, Q_MAX), audioContext.currentTime); + } + } +}; + +inLowQ.onChange = () => +{ + if (inLowActive.get()) + { + const q = inLowQ.get(); + + if (q < Q_MIN) op.setUiError("qRangeLow", "Your Q value for the highpass filter is below the minimum possible value of " + Q_MIN + ".", 1); + else if (q > Q_MAX) op.setUiError("qRangeLow", "Your Q value for highpass filter is above the maximum possible value of " + Q_MAX + ".", 1); + else op.setUiError("qRangeLow", null); + + for (let i = 0; i < SLOPES.length; i += 1) + { + const node = lowFilterNodes[i]; + node.Q.setValueAtTime(clamp(q, Q_MIN, Q_MAX), audioContext.currentTime); + } + } +}; + +const outAudio = op.outObject("Audio Out", null, "audioNode"); + +let oldAudioIn = null; + +inAudio.onChange = function () +{ + if (!inAudio.get()) + { + if (oldAudioIn) + { + try + { + if (oldAudioIn.disconnect) + { + oldAudioIn.disconnect(lowFilterNodes[0]); + } + } + catch (e) + { + op.log(e); + } + } + outAudio.set(null); + } + else + { + if (inAudio.get().connect) inAudio.get().connect(lowFilterNodes[0]); + } + + oldAudioIn = inAudio.get(); + outAudio.set(highFilterNodes[SLOPES.length - 1]); +}; + +op.onDelete = () => +{ + lowFilterNodes.forEach((node, index) => + { + node.disconnect(); + }); + + highFilterNodes.forEach((node, index) => + { + if (index < SLOPES.length - 1) node.disconnect(); + }); +}; + + +}; + +Ops.WebAudio.CutFilter.prototype = new CABLES.Op(); +CABLES.OPS["de1f1d82-49b3-47e3-840e-2ab75ffc0045"]={f:Ops.WebAudio.CutFilter,objName:"Ops.WebAudio.CutFilter"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.Delay +// +// ************************************************************** + +Ops.WebAudio.Delay = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +function clamp(val, min, max) +{ + return Math.min(Math.max(val, min), max); +} + +const audioCtx = CABLES.WEBAUDIO.createAudioContext(op); +const NOTE_NAMES = []; +const MAX_DELAY_TIME = 179.999; + +for (let i = 0; i < 7 * 3; i += 1) +{ + const noteValue = Math.pow(2, i % 7); + let string = "1/" + noteValue; + if (i > 6 && i < 14) + { + string = "1/" + noteValue + " Triplet"; + } + else if (i > 13) + { + string = "1/" + noteValue + " Dotted"; + } + NOTE_NAMES.push(string + " Note"); +} + +const audioIn = op.inObject("Audio In", null, "audioNode"); +const inDryWet = op.inFloatSlider("Dry/Wet", 0.4); +const inFeedback = op.inFloatSlider("Feedback", 0.4); +const inDelayMS = op.inFloat("Delay Time (MS)", 100); +const inDelayShift = op.inFloat("Delay Shift (MS)", 0); +const inBpmSync = op.inBool("BPM based delay time", false); +const inBPM = op.inFloat("BPM", 120); +const inDelayNotes = op.inDropDown("Delay Time (note value) ", NOTE_NAMES, "1/8 Dotted Note"); +const inUseModulation = op.inBool("Use Filter & Modulation", false); +const inHighpassFrequency = op.inFloat("Highpass Frequency", 300); +const inHighpassQ = op.inFloat("Highpass Q", 2); +const inLowpassFrequency = op.inFloat("Lowpass Frequency", 9000); +const inLowpassQ = op.inFloat("Lowpass Q", 2); +const inLfoSpeed = op.inFloat("LFO Speed (Hz) ", 3.14); +const inLfoIntensity = op.inFloatSlider("LFO Intensity", 0.4); +const inLfoWave = op.inDropDown("LFO Waveform", ["sine", "triangle"], "sine"); +inLowpassFrequency.setUiAttribs({ "greyout": !inUseModulation.get() }); +inLowpassQ.setUiAttribs({ "greyout": !inUseModulation.get() }); +inHighpassFrequency.setUiAttribs({ "greyout": !inUseModulation.get() }); +inHighpassQ.setUiAttribs({ "greyout": !inUseModulation.get() }); +inLfoSpeed.setUiAttribs({ "greyout": !inUseModulation.get() }); +inLfoIntensity.setUiAttribs({ "greyout": !inUseModulation.get() }); +inLfoWave.setUiAttribs({ "greyout": !inUseModulation.get() }); +op.setPortGroup("Filters & Modulation", [inLowpassFrequency, inLowpassQ, inHighpassFrequency, inHighpassQ, inLfoSpeed, inLfoIntensity, inLfoWave]); + +const audioOut = op.outObject("Mix Out", null, "audioNode"); +const delayOut = op.outObject("Wet Out", null, "audioNode"); + +const MULTIPLIERS = [ + 4, 2, 1, 1 / 2, 1 / 4, 1 / 8, 1 / 16, + 8 / 3, 4 / 3, 2 / 3, 1 / 3, 1 / 6, 1 / 12, 1 / 24, + 6, 3, 3 / 2, 3 / 4, 3 / 8, 3 / 16, 3 / 32 // dotted +]; + +const MIN_BPM = 20; +let QUARTER_NOTE_S = 60 / Math.max(20, inBPM.get()); +let NOTES_IN_S = MULTIPLIERS.map((multiplier) => multiplier * QUARTER_NOTE_S); + +const calculateDelayTime = () => +{ + if (inBpmSync.get()) + { + const timeIndex = NOTE_NAMES.indexOf(inDelayNotes.get()); + + const timeValue = NOTES_IN_S[timeIndex] + (parseFloat(inDelayShift.get()) / 1000); + clamp(timeValue, 0, MAX_DELAY_TIME); + return timeValue; + } + + const unsyncedTime = (parseFloat(inDelayMS.get()) + parseFloat(inDelayShift.get())) / 1000.0; + + if (unsyncedTime < 0) op.setUiError("minDelayTime", "The delay time is lower than 0 ms. Setting to 0.", 1); + else op.setUiError("minDelayTime", null); + + return Math.max(0, unsyncedTime); +}; + +op.setPortGroup("Main Controls", [inDryWet, inFeedback, inDelayMS, inDelayShift]); +op.setPortGroup("Synced Controls", [inBPM, inDelayNotes]); + +inBPM.setUiAttribs({ "greyout": !inBpmSync.get() }); +inDelayNotes.setUiAttribs({ "greyout": !inBpmSync.get() }); +inDelayMS.setUiAttribs({ "greyout": inBpmSync.get() }); + +inBpmSync.onChange = () => +{ + inBPM.setUiAttribs({ "greyout": !inBpmSync.get() }); + inDelayNotes.setUiAttribs({ "greyout": !inBpmSync.get() }); + inDelayMS.setUiAttribs({ "greyout": inBpmSync.get() }); + + delayNode.delayTime.linearRampToValueAtTime(calculateDelayTime(), audioCtx.currentTime + 0.2); + + if (inBPM.get() < MIN_BPM) op.setUiError("minBPM", "The minimum BPM value is " + MIN_BPM + " BPM.", 1); + else op.setUiError("minBPM", null); +}; + +const inputNode = audioCtx.createGain(); // we create an input node so we can change the delay without requiring the input signal +const delayNode = audioCtx.createDelay(MAX_DELAY_TIME); +const dryNode = audioCtx.createGain(); +const wetNode = audioCtx.createGain(); + +const feedbackNode = audioCtx.createGain(); +const outputNode = audioCtx.createGain(); + +const filterHighpassNode = audioCtx.createBiquadFilter(); +const filterLowpassNode = audioCtx.createBiquadFilter(); +filterLowpassNode.type = "lowpass"; +filterLowpassNode.frequency.value = inLowpassFrequency.get(); +filterLowpassNode.Q.value = inLowpassQ.get(); + +filterHighpassNode.connect(filterLowpassNode); +filterHighpassNode.type = "highpass"; +filterHighpassNode.frequency.value = inHighpassFrequency.get(); +filterHighpassNode.Q.value = inHighpassQ.get(); + +const lfoNode = audioCtx.createOscillator(); +lfoNode.frequency.value = inLfoSpeed.get(); // Freq. in Hz +const lfoGainNode = audioCtx.createGain(); +lfoGainNode.gain.value = inLfoIntensity.get() / 800; +lfoNode.connect(lfoGainNode); +lfoNode.start(); + +delayNode.delayTime.value = calculateDelayTime(); +feedbackNode.gain.value = parseFloat(inFeedback.get()); +wetNode.gain.value = parseFloat(inDryWet.get()); +dryNode.gain.value = 1.0 - parseFloat(inDryWet.get()); + +delayNode.connect(feedbackNode); +feedbackNode.connect(delayNode); + +inputNode.connect(dryNode); +inputNode.connect(delayNode); + +dryNode.connect(outputNode); +delayNode.connect(wetNode); + +wetNode.connect(outputNode); + +inUseModulation.onChange = () => +{ + inLowpassFrequency.setUiAttribs({ "greyout": !inUseModulation.get() }); + inLowpassQ.setUiAttribs({ "greyout": !inUseModulation.get() }); + inHighpassFrequency.setUiAttribs({ "greyout": !inUseModulation.get() }); + inHighpassQ.setUiAttribs({ "greyout": !inUseModulation.get() }); + inLfoSpeed.setUiAttribs({ "greyout": !inUseModulation.get() }); + inLfoIntensity.setUiAttribs({ "greyout": !inUseModulation.get() }); + inLfoWave.setUiAttribs({ "greyout": !inUseModulation.get() }); + + if (inUseModulation.get()) + { + lfoGainNode.connect(delayNode.delayTime); + + inputNode.disconnect(delayNode); + + inputNode.connect(filterHighpassNode); + filterLowpassNode.connect(delayNode); + } + else + { + lfoGainNode.disconnect(delayNode.delayTime); + + inputNode.disconnect(filterHighpassNode); + filterLowpassNode.disconnect(delayNode); + + inputNode.connect(delayNode); + } +}; + +inLfoIntensity.onChange = () => +{ + lfoGainNode.gain.linearRampToValueAtTime(inLfoIntensity.get() / 800, audioCtx.currentTime + 0.1); +}; + +inLfoSpeed.onChange = () => +{ + lfoNode.frequency.linearRampToValueAtTime(inLfoSpeed.get(), audioCtx.currentTime + 0.1); +}; + +inLfoWave.onChange = () => +{ + lfoNode.type = inLfoWave.get(); +}; +inDelayMS.onChange = inDelayShift.onChange = inDelayNotes.onChange = () => +{ + if (inBPM.get() < MIN_BPM) op.setUiError("minBPM", "The minimum BPM value is " + MIN_BPM + " BPM.", 1); + else op.setUiError("minBPM", null); + + delayNode.delayTime.linearRampToValueAtTime(calculateDelayTime(), audioCtx.currentTime + 0.2); +}; + +inBPM.onChange = () => +{ + QUARTER_NOTE_S = 60 / Math.max(MIN_BPM, inBPM.get()); + NOTES_IN_S = MULTIPLIERS.map((multiplier) => multiplier * QUARTER_NOTE_S); + + delayNode.delayTime.linearRampToValueAtTime(calculateDelayTime(), audioCtx.currentTime + 0.1); + + if (inBPM.get() < MIN_BPM) op.setUiError("minBPM", "The minimum BPM value is " + MIN_BPM + " BPM.", 1); + else op.setUiError("minBPM", null); +}; + +inDryWet.onChange = () => +{ + wetNode.gain.linearRampToValueAtTime(Number(inDryWet.get()), audioCtx.currentTime + 0.01); + dryNode.gain.linearRampToValueAtTime((1 - Number(inDryWet.get())), audioCtx.currentTime + 0.01); +}; + +inFeedback.onChange = () => +{ + feedbackNode.gain.linearRampToValueAtTime(Number(inFeedback.get()), audioCtx.currentTime + 0.01); +}; + +const FREQUENCY_MIN = 10; +const FREQUENCY_MAX = audioCtx.sampleRate / 2; // Nyquist frequency. +const Q_MIN = 0.0001; +const Q_MAX = 1000; + +inHighpassFrequency.onChange = () => +{ + const freq = inHighpassFrequency.get(); + if (freq) + { + if (freq >= FREQUENCY_MIN && freq <= FREQUENCY_MAX) + { + filterHighpassNode.frequency.linearRampToValueAtTime(clamp(freq, FREQUENCY_MIN, FREQUENCY_MAX), audioContext.currentTime, +0.01); + op.setUiError("freqRangeHighpass", null); + } + if (freq < FREQUENCY_MIN) + { + op.setUiError("freqRangeHighpass", "The highpass frequency you selected is lower than the possible frequency of " + FREQUENCY_MIN + " Hz.", 1); + } + else if (freq > FREQUENCY_MAX) + { + op.setUiError("freqRangeHighpass", "The highpass frequency you selected is higher than the possible frequency of " + FREQUENCY_MAX + " Hz.", 1); + } + } +}; + +inHighpassQ.onChange = () => +{ + const q = inHighpassQ.get(); + filterHighpassNode.Q.linearRampToValueAtTime(clamp(q, Q_MIN, Q_MAX), audioContext.currentTime + 0.01); + + if (q < Q_MIN) op.setUiError("qRangeHighpass", "Your highpass Q value is below the minimum possible value of " + Q_MIN + ".", 1); + else if (q > Q_MAX) op.setUiError("qRangeHighpass", "Your highpass Q value is above the maximum possible value of " + Q_MAX + ".", 1); + else + { + op.setUiError("qRangeHighpass", null); + } +}; + +inLowpassFrequency.onChange = () => +{ + const freq = inLowpassFrequency.get(); + if (freq) + { + if (freq >= FREQUENCY_MIN && freq <= FREQUENCY_MAX) + { + filterLowpassNode.frequency.linearRampToValueAtTime(clamp(freq, FREQUENCY_MIN, FREQUENCY_MAX), audioContext.currentTime, +0.01); + op.setUiError("freqRangeLowpass", null); + } + if (freq < FREQUENCY_MIN) + { + op.setUiError("freqRangeLowpass", "The lowpass frequency you selected is lower than the possible frequency of " + FREQUENCY_MIN + " Hz.", 1); + } + else if (freq > FREQUENCY_MAX) + { + op.setUiError("freqRangeLowpass", "The lowpass frequency you selected is higher than the possible frequency of " + FREQUENCY_MAX + " Hz.", 1); + } + } +}; + +inLowpassQ.onChange = () => +{ + const q = inLowpassQ.get(); + filterLowpassNode.Q.linearRampToValueAtTime(clamp(q, Q_MIN, Q_MAX), audioContext.currentTime + 0.01); + + if (q < Q_MIN) op.setUiError("qRangeLowpass", "Your lowpass Q value is below the minimum possible value of " + Q_MIN + ".", 1); + else if (q > Q_MAX) op.setUiError("qRangeLowpass", "Your lowpass Q value is above the maximum possible value of " + Q_MAX + ".", 1); + else + { + op.setUiError("qRangeLowpass", null); + } +}; + +let oldAudioIn = null; +audioIn.onChange = function () +{ + if (!audioIn.get()) + { + if (oldAudioIn) + { + try + { + if (oldAudioIn.disconnect) + { + oldAudioIn.disconnect(inputNode); + } + } + catch (e) + { + op.log(e); + } + } + audioOut.set(null); + } + else + { + if (audioIn.get().connect) audioIn.get().connect(inputNode); + } + + oldAudioIn = audioIn.get(); + audioOut.set(outputNode); + delayOut.set(delayNode); +}; + +op.onDelete = () => +{ + lfoNode.disconnect(); + delayNode.disconnect(); + feedbackNode.disconnect(); + + inputNode.disconnect(); + + dryNode.disconnect(); + delayNode.disconnect(); + + wetNode.disconnect(); + filterHighpassNode.disconnect(); + + if (inUseModulation.get()) + { + lfoGainNode.disconnect(); + filterLowpassNode.disconnect(); + } +}; + + +}; + +Ops.WebAudio.Delay.prototype = new CABLES.Op(); +CABLES.OPS["60a557be-109a-494d-aba7-fcc61bbad033"]={f:Ops.WebAudio.Delay,objName:"Ops.WebAudio.Delay"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.FFTAreaAverage_v2 +// +// ************************************************************** + +Ops.WebAudio.FFTAreaAverage_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; +const TEX_SIZES = [128, 256, 512, 1024, 2048]; +const + refresh = op.inTriggerButton("Refresh"), + fftArr = op.inArray("FFT Array"), + x = op.inValueSlider("X Position"), + y = op.inValueSlider("Y Position"), + w = op.inValueSlider("Width", 0.2), + h = op.inValueSlider("Height", 0.2), + drawTex = op.inValueBool("Create Texture", true), + inCanvasSize = op.inSwitch("Texture Size", TEX_SIZES, 128), + texOut = op.outTexture("Texture Out", null, "texture"), + value = op.outNumber("Area Average Volume"); + +op.setPortGroup("Area Settings", [x, y, w, h]); +op.setPortGroup("Texture Settings", [drawTex, inCanvasSize]); + +let updateTexture = false; +inCanvasSize.onChange = () => +{ + updateTexture = true; +}; +const data = []; +const line = 0; +let size = Number(inCanvasSize.get()); + +const canvas = document.createElement("canvas"); +canvas.id = "fft_" + CABLES.uuid(); +canvas.width = canvas.height = size; +canvas.style.display = "none"; +const body = document.getElementsByTagName("body")[0]; +body.appendChild(canvas); +const ctx = canvas.getContext("2d"); + +const MULTIPLIERS = [0.5, 1, 2, 4, 8]; +let multiplier = 1; + +let areaX = 0; +let areaY = 0; +let areaW = 20; +let areaH = 20; +let amount = 0; + +refresh.onTriggered = function () +{ + const arr = fftArr.get(); + if (!arr) + { + return; + } + + const width = arr.length; + + const draw = drawTex.get(); + + if (updateTexture) + { + size = Number(inCanvasSize.get()); + canvas.width = canvas.height = size; + + const indexOfSize = TEX_SIZES.indexOf(Number(inCanvasSize.get())); + multiplier = MULTIPLIERS[indexOfSize]; + + updateTexture = false; + } + + if (draw) + { + ctx.beginPath(); + ctx.fillStyle = "#000"; + ctx.strokeStyle = "#ff0"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + ctx.fillStyle = "#888"; + for (let i = 0; i < arr.length; i++) + ctx.fillRect(i, size - arr[i] * multiplier, 1, arr[i] * multiplier); + } + + areaX = x.get() * canvas.width; + areaY = y.get() * canvas.height; + + areaW = w.get() * size / 2; + areaH = h.get() * size / 2; + + if (draw)ctx.rect(areaX, areaY, areaW, areaH); + if (draw) + { + ctx.lineWidth = 2 * multiplier; + ctx.stroke(); + } + + const val = 0; + let count = 0; + for (let xc = areaX; xc < areaX + areaW; xc++) + for (let yc = areaY; yc < areaY + areaH; yc++) + if (arr[Math.round(xc)] * multiplier > size - yc)count++; + + if (amount != amount)amount = 0; + amount += count / (areaW * areaH); + amount /= 2; + value.set(amount); + + if (draw) + { + ctx.fillStyle = "#ff0"; + ctx.fillRect(0, 0, amount * canvas.width, 6 * multiplier); + + if (texOut.get()) texOut.get().initTexture(canvas, CGL.Texture.FILTER_NEAREST); + else + { + texOut.set(new CGL.Texture.createFromImage(cgl, canvas, { "filter": CGL.Texture.FILTER_NEAREST })); + } + } +}; + + +}; + +Ops.WebAudio.FFTAreaAverage_v2.prototype = new CABLES.Op(); +CABLES.OPS["8dd14fde-57d1-408c-a3ef-441a65bfe53a"]={f:Ops.WebAudio.FFTAreaAverage_v2,objName:"Ops.WebAudio.FFTAreaAverage_v2"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.Gain +// +// ************************************************************** + +Ops.WebAudio.Gain = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const audioIn = op.inObject("audio in", null, "audioNode"); +const gain = op.inFloatSlider("gain", 1); +const inMute = op.inBool("Mute", false); +const audioOut = op.outObject("audio out", null, "audioNode"); + +const audioCtx = CABLES.WEBAUDIO.createAudioContext(op); +const gainNode = audioContext.createGain(); + +gain.onChange = () => +{ + if (inMute.get()) return; + + gainNode.gain.setValueAtTime(Number(gain.get()) || 0, audioCtx.currentTime); +}; + +inMute.onChange = () => +{ + if (inMute.get()) + { + gainNode.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.01); + } + else + { + gainNode.gain.linearRampToValueAtTime(Number(gain.get()) || 0, audioCtx.currentTime + 0.01); + } +}; +let oldAudioIn = null; + +audioIn.onChange = function () +{ + if (!audioIn.get()) + { + if (oldAudioIn) + { + try + { + if (oldAudioIn.disconnect) oldAudioIn.disconnect(gainNode); + } + catch (e) + { + op.log(e); + } + } + audioOut.set(null); + } + else + { + if (audioIn.get().connect) audioIn.get().connect(gainNode); + } + oldAudioIn = audioIn.get(); + audioOut.set(gainNode); +}; + + +}; + +Ops.WebAudio.Gain.prototype = new CABLES.Op(); +CABLES.OPS["29402bba-48ee-4f28-94f0-bdc345e6bb67"]={f:Ops.WebAudio.Gain,objName:"Ops.WebAudio.Gain"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.KeyPiano +// +// ************************************************************** + +Ops.WebAudio.KeyPiano = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let c_on = this.addInPort(new CABLES.Port(this, "c note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let c_off = this.addInPort(new CABLES.Port(this, "c note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let cis_on = this.addInPort(new CABLES.Port(this, "cis note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let cis_off = this.addInPort(new CABLES.Port(this, "cis note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let d_on = this.addInPort(new CABLES.Port(this, "d note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let d_off = this.addInPort(new CABLES.Port(this, "d note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let dis_on = this.addInPort(new CABLES.Port(this, "dis note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let dis_off = this.addInPort(new CABLES.Port(this, "dis note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let e_on = this.addInPort(new CABLES.Port(this, "e note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let e_off = this.addInPort(new CABLES.Port(this, "e note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let f_on = this.addInPort(new CABLES.Port(this, "f note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let f_off = this.addInPort(new CABLES.Port(this, "f note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let fis_on = this.addInPort(new CABLES.Port(this, "fis note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let fis_off = this.addInPort(new CABLES.Port(this, "fis note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let g_on = this.addInPort(new CABLES.Port(this, "g note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let g_off = this.addInPort(new CABLES.Port(this, "g note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let gis_on = this.addInPort(new CABLES.Port(this, "gis note ons", CABLES.OP_PORT_TYPE_FUNCTION)); +let gis_off = this.addInPort(new CABLES.Port(this, "gis note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let a_on = this.addInPort(new CABLES.Port(this, "a note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let a_off = this.addInPort(new CABLES.Port(this, "a note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let ais_on = this.addInPort(new CABLES.Port(this, "ais note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let ais_off = this.addInPort(new CABLES.Port(this, "ais note off", CABLES.OP_PORT_TYPE_FUNCTION)); +let b_on = this.addInPort(new CABLES.Port(this, "b note on", CABLES.OP_PORT_TYPE_FUNCTION)); +let b_off = this.addInPort(new CABLES.Port(this, "b note off", CABLES.OP_PORT_TYPE_FUNCTION)); + +let frequency = this.addOutPort(new CABLES.Port(this, "frequency", CABLES.OP_PORT_TYPE_VALUE)); +let isPressed = this.addOutPort(new CABLES.Port(this, "is pressed", CABLES.OP_PORT_TYPE_VALUE)); + +let OCTAVE_MIN = 1; +let OCTAVE_MAX = 7; + +let toneFreqMap = []; + +for (let i = OCTAVE_MIN; i <= OCTAVE_MAX; i++) +{ + toneFreqMap[i] = {}; +} + +// octave 1 +toneFreqMap[1].c = 32.7032; +toneFreqMap[1].cis = 34.6478; +toneFreqMap[1].d = 36.7081; +toneFreqMap[1].dis = 38.8909; +toneFreqMap[1].e = 41.2034; +toneFreqMap[1].f = 43.6535; +toneFreqMap[1].fis = 46.2493; +toneFreqMap[1].g = 48.9994; +toneFreqMap[1].gis = 51.9131; +toneFreqMap[1].a = 55.0000; +toneFreqMap[1].ais = 58.2705; +toneFreqMap[1].b = 61.7354; +// octave 2 +toneFreqMap[2].c = 65.4064; +toneFreqMap[2].cis = 69.2957; +toneFreqMap[2].d = 73.4162; +toneFreqMap[2].dis = 77.7817; +toneFreqMap[2].e = 82.4069; +toneFreqMap[2].f = 87.3071; +toneFreqMap[2].fis = 92.4986; +toneFreqMap[2].g = 97.9989; +toneFreqMap[2].gis = 103.826; +toneFreqMap[2].a = 110.000; +toneFreqMap[2].ais = 116.541; +toneFreqMap[2].b = 123.471; +// octave 3 +toneFreqMap[3].c = 130.813; +toneFreqMap[3].cis = 138.591; +toneFreqMap[3].d = 146.832; +toneFreqMap[3].dis = 155.563; +toneFreqMap[3].e = 164.814; +toneFreqMap[3].f = 174.614; +toneFreqMap[3].fis = 184.997; +toneFreqMap[3].g = 195.998; +toneFreqMap[3].gis = 207.652; +toneFreqMap[3].a = 220.000; +toneFreqMap[3].ais = 233.082; +toneFreqMap[3].b = 246.942; +// octave 4 +toneFreqMap[4].c = 261.626; +toneFreqMap[4].cis = 277.183; +toneFreqMap[4].d = 293.665; +toneFreqMap[4].dis = 311.127; +toneFreqMap[4].e = 329.628; +toneFreqMap[4].f = 349.228; +toneFreqMap[4].fis = 369.994; +toneFreqMap[4].g = 391.995; +toneFreqMap[4].gis = 415.305; +toneFreqMap[4].a = 440.000; +toneFreqMap[4].ais = 466.164; +toneFreqMap[4].b = 493.883; +// octave 5 +toneFreqMap[5].c = 523.251; +toneFreqMap[5].cis = 554.365; +toneFreqMap[5].d = 587.330; +toneFreqMap[5].dis = 622.254; +toneFreqMap[5].e = 659.255; +toneFreqMap[5].f = 698.456; +toneFreqMap[5].fis = 739.989; +toneFreqMap[5].g = 783.991; +toneFreqMap[5].gis = 830.609; +toneFreqMap[5].a = 880.000; +toneFreqMap[5].ais = 932.328; +toneFreqMap[5].b = 987.767; +// octave 6 +toneFreqMap[6].c = 1046.50; +toneFreqMap[6].cis = 1108.73; +toneFreqMap[6].d = 1174.66; +toneFreqMap[6].dis = 1244.51; +toneFreqMap[6].e = 1318.51; +toneFreqMap[6].f = 1396.91; +toneFreqMap[6].fis = 1479.98; +toneFreqMap[6].g = 1567.98; +toneFreqMap[6].gis = 1661.22; +toneFreqMap[6].a = 1760.00; +toneFreqMap[6].ais = 1864.66; +toneFreqMap[6].b = 1975.53; +// octave 7 +toneFreqMap[7].c = 2093.00; +toneFreqMap[7].cis = 2217.46; +toneFreqMap[7].d = 2349.32; +toneFreqMap[7].dis = 2489.02; +toneFreqMap[7].e = 2637.02; +toneFreqMap[7].f = 2793.83; +toneFreqMap[7].fis = 2959.96; +toneFreqMap[7].g = 3135.96; +toneFreqMap[7].gis = 3322.44; +toneFreqMap[7].a = 3520.00; +toneFreqMap[7].ais = 3729.31; +toneFreqMap[7].b = 3951.07; + +function octaveInRange(oct) +{ + return oct >= OCTAVE_MIN && oct <= OCTAVE_MAX; +} + +function handleNoteOn(tone) +{ + let oct = parseInt(octave.get()); + if (octaveInRange(oct)) + { + let freq = (toneFreqMap[oct][tone]); + frequency.set(freq); + isPressed.set(1.0); + op.log("[note on] " + tone + oct + " (" + freq + "Hz)"); + } +} + +c_on.onTriggered = function () { handleNoteOn("c"); }; +cis_on.onTriggered = function () { handleNoteOn("cis"); }; +d_on.onTriggered = function () { handleNoteOn("d"); }; +dis_on.onTriggered = function () { handleNoteOn("dis"); }; +e_on.onTriggered = function () { handleNoteOn("e"); }; +f_on.onTriggered = function () { handleNoteOn("f"); }; +fis_on.onTriggered = function () { handleNoteOn("fis"); }; +g_on.onTriggered = function () { handleNoteOn("g"); }; +gis_on.onTriggered = function () { handleNoteOn("gis"); }; +a_on.onTriggered = function () { handleNoteOn("a"); }; +ais_on.onTriggered = function () { handleNoteOn("ais"); }; +b_on.onTriggered = function () { handleNoteOn("b"); }; + +function handleNoteOff(tone) +{ + let oct = parseInt(octave.get()); + if (octaveInRange(oct)) + { + let freq = (toneFreqMap[oct][tone]); + frequency.set(freq); + isPressed.set(0.0); + op.log("[note on] " + tone + oct + " (" + freq + "Hz)"); + } +} + +c_off.onTriggered = function () { handleNoteOff("c"); }; +cis_off.onTriggered = function () { handleNoteOff("cis"); }; +d_off.onTriggered = function () { handleNoteOff("d"); }; +dis_off.onTriggered = function () { handleNoteOff("dis"); }; +e_off.onTriggered = function () { handleNoteOff("e"); }; +f_off.onTriggered = function () { handleNoteOff("f"); }; +fis_off.onTriggered = function () { handleNoteOff("fis"); }; +g_off.onTriggered = function () { handleNoteOff("g"); }; +gis_off.onTriggered = function () { handleNoteOff("gis"); }; +a_off.onTriggered = function () { handleNoteOff("a"); }; +ais_off.onTriggered = function () { handleNoteOff("ais"); }; +b_off.onTriggered = function () { handleNoteOff("b"); }; + +let octave = this.addInPort(new CABLES.Port(this, "octave", CABLES.OP_PORT_TYPE_)); + +octave.set(4); +frequency.set(0); + + +}; + +Ops.WebAudio.KeyPiano.prototype = new CABLES.Op(); +CABLES.OPS["9f7d2411-ed87-45be-95a7-e6759d51582d"]={f:Ops.WebAudio.KeyPiano,objName:"Ops.WebAudio.KeyPiano"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.MicrophoneIn_v2 +// +// ************************************************************** + +Ops.WebAudio.MicrophoneIn_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +let microphone = null; +const audioCtx = CABLES.WEBAUDIO.createAudioContext(op); + +const inInit = op.inTriggerButton("Start"); +const inInputDevices = op.inDropDown("Audio Input", ["None"]); +const inGain = op.inFloatSlider("Volume", 1); +const inMute = op.inBool("Mute", false); +const audioOut = op.outObject("Audio Out", null, "audioNode"); +const recording = op.outBool("Listening", false); +const outDevices = op.outArray("List of Input Devices"); + +op.setPortGroup("Volume Settings", [inGain, inMute]); +let audioInputsLoaded = false; +let loadingId = null; + +const gainNode = audioCtx.createGain(); + +function streamAudio(stream) +{ + microphone = audioCtx.createMediaStreamSource(stream); + microphone.connect(gainNode); + audioOut.set(gainNode); + op.log("[microphoneIn] streaming mic audio!", stream, microphone); + recording.set(true); +} + +inGain.onChange = () => +{ + if (inMute.get()) return; + gainNode.gain.setValueAtTime(Number(inGain.get()) || 0, audioCtx.currentTime); +}; + +inMute.onChange = () => +{ + if (inMute.get()) + { + gainNode.gain.setValueAtTime(0, audioCtx.currentTime); + } + else + { + gainNode.gain.setValueAtTime(Number(inGain.get()) || 0, audioCtx.currentTime); + } +}; + +inInit.onTriggered = function () +{ + if (!audioCtx) + { + op.log("[microphoneIn] no audiocontext!"); + return; + } + + if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) + { + op.log("[microphoneIn] new micro"); + + if (audioInputsLoaded) + { + op.setUiError("noAudioInputs", null); + + const device = inInputDevices.get(); + + if (device === "None") + { + op.setUiError("noDeviceSelected", "No audio device selected!", 1); + return; + } + else + { + op.setUiError("noDeviceSelected", null); + } + const constraints = { + "audio": { "deviceId": device }, + }; + + navigator.mediaDevices.getUserMedia(constraints) + .then((stream) => + { + microphone = audioCtx.createMediaStreamSource(stream); + microphone.connect(gainNode); + audioOut.set(gainNode); + op.log("streaming mic audio!", stream, microphone, gainNode); + recording.set(true); + op.setUiError("devicesLoaded", null); + }) + .catch((e) => + { + op.log("ERROR STREAMNG", e); + }); + } + else + { + op.setUiError("noAudioInputs", "There are no audio inputs to use the MicrophoneIn op with.", 2); + } + } + else + { + // old method + navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia); + + if (navigator.getUserMedia) + { + navigator.getUserMedia( + { "audio": true }, + streamAudio, + function (e) + { + op.log("[microphoneIn]No live audio input " + e); + recording.set(false); + } + ); + } + else + { + op.log("[op microphone] could not get usermedia"); + recording.set(false); + } + } +}; + +/* INIT FUNCTION */ +loadingId = cgl.patch.loading.start("MIC inputs", ""); +navigator.mediaDevices.getUserMedia({ "audio": true }) + .then((res) => + navigator.mediaDevices.enumerateDevices()) + .then((devices) => + { + const audioInputDevices = devices + .filter((device) => device.kind === "audioinput") + .map((deviceInfo, index) => deviceInfo.label || `microphone ${index + 1}`); + + inInputDevices.uiAttribs.values = audioInputDevices; + op.setUiError("devicesLoaded", "Input devices have been loaded. Please choose a device from the dropdown menu and click the \"Start\" button to activate the microphone input.", 0); + cgl.patch.loading.finished(loadingId); + audioInputsLoaded = true; + outDevices.set(null); + outDevices.set(audioInputDevices); + }) + .catch((e) => + { + op.log("error", e); + cgl.patch.loading.finished(loadingId); + audioInputsLoaded = false; + }); + + +}; + +Ops.WebAudio.MicrophoneIn_v2.prototype = new CABLES.Op(); +CABLES.OPS["cbfbbffd-a5a8-4b21-bcb5-5d031cc5e11a"]={f:Ops.WebAudio.MicrophoneIn_v2,objName:"Ops.WebAudio.MicrophoneIn_v2"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.MidiValueToFrequency +// +// ************************************************************** + +Ops.WebAudio.MidiValueToFrequency = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + midiValuePort = op.inValue("MIDI Value"), + tuningPort = op.inValue("Tuning", 440), + frequencyPort = op.outNumber("Frequency"); + +midiValuePort.onChange = + tuningPort.onChange = setFrequency; + +function setFrequency() +{ + let frequency = freq(tuningPort.get(), midiValuePort.get()); + frequencyPort.set(frequency); +} + +// Taken from danigb - https://github.com/danigb/midi-freq +function freq(tuning, midi) +{ + tuning = tuning || 440; + if (arguments.length > 1) return freq(tuning)(midi); + + return function (m) + { + return m === 0 || (m > 0 && m < 128) ? Math.pow(2, (m - 69) / 12) * tuning : null; + }; +} + + +}; + +Ops.WebAudio.MidiValueToFrequency.prototype = new CABLES.Op(); +CABLES.OPS["751c0067-e015-4f73-90b1-2d265b6e6d72"]={f:Ops.WebAudio.MidiValueToFrequency,objName:"Ops.WebAudio.MidiValueToFrequency"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.Mixer +// +// ************************************************************** + +Ops.WebAudio.Mixer = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +function clamp(val, min, max) +{ + return Math.min(Math.max(val, min), max); +} + +const audioCtx = CABLES.WEBAUDIO.createAudioContext(op); + +const inAudio0 = op.inObject("Audio In 0", null, "audioNode"); +const inAudio1 = op.inObject("Audio In 1", null, "audioNode"); +const inAudio2 = op.inObject("Audio In 2", null, "audioNode"); +const inAudio3 = op.inObject("Audio In 3", null, "audioNode"); +const inAudio4 = op.inObject("Audio In 4", null, "audioNode"); +const inAudio5 = op.inObject("Audio In 5", null, "audioNode"); +const inAudio6 = op.inObject("Audio In 6", null, "audioNode"); +const inAudio7 = op.inObject("Audio In 7", null, "audioNode"); + +const inAudio0Gain = op.inFloatSlider("In 0 Gain", 1); +const inAudio1Gain = op.inFloatSlider("In 1 Gain", 1); +const inAudio2Gain = op.inFloatSlider("In 2 Gain", 1); +const inAudio3Gain = op.inFloatSlider("In 3 Gain", 1); +const inAudio4Gain = op.inFloatSlider("In 4 Gain", 1); +const inAudio5Gain = op.inFloatSlider("In 5 Gain", 1); +const inAudio6Gain = op.inFloatSlider("In 6 Gain", 1); +const inAudio7Gain = op.inFloatSlider("In 7 Gain", 1); + +const inAudio0Pan = op.inFloat("In 0 Pan", 0); +const inAudio1Pan = op.inFloat("In 1 Pan", 0); +const inAudio2Pan = op.inFloat("In 2 Pan", 0); +const inAudio3Pan = op.inFloat("In 3 Pan", 0); +const inAudio4Pan = op.inFloat("In 4 Pan", 0); +const inAudio5Pan = op.inFloat("In 5 Pan", 0); +const inAudio6Pan = op.inFloat("In 6 Pan", 0); +const inAudio7Pan = op.inFloat("In 7 Pan", 0); + +const inMasterGain = op.inFloatSlider("Output Gain", 1); + +let isIOS = false; +let panNode = null; + +let createPanner = () => {}; +if (audioCtx.createStereoPanner) +{ + isIOS = false; +} +else +{ + isIOS = true; +} + +const audioOut = op.outObject("Audio Out", null, "audioNode"); + +const gain = audioCtx.createGain(); +audioOut.set(gain); + +const N_PORTS = 8; + +const audioIns = [inAudio0, inAudio1, inAudio2, inAudio3, inAudio4, inAudio5, inAudio6, inAudio7]; +const audioInGains = [inAudio0Gain, inAudio1Gain, inAudio2Gain, inAudio3Gain, inAudio4Gain, inAudio5Gain, inAudio6Gain, inAudio7Gain]; +const audioInPans = [inAudio0Pan, inAudio1Pan, inAudio2Pan, inAudio3Pan, inAudio4Pan, inAudio5Pan, inAudio6Pan, inAudio7Pan]; +op.setPortGroup("Audio Inputs", audioIns); +op.setPortGroup("Input", audioInGains); +op.setPortGroup("Panning", audioInPans); +op.setPortGroup("Output ", [inMasterGain]); +const oldAudioIns = audioIns.map(() => ({ "node": null, "isConnected": false })); + +audioInGains.forEach((port, index) => +{ + port.gainNode = audioCtx.createGain(); + port.onChange = () => port.gainNode.gain.linearRampToValueAtTime((audioInGains[index].get() || 0), audioCtx.currentTime + 0.01); +}); + +audioInPans.forEach((port, index) => +{ + if (isIOS) + { + port.panNode = audioCtx.createPanner(); + port.panNode.panningModel = "equalpower"; + } + else + { + port.panNode = audioCtx.createStereoPanner(); + } + + port.panNode.connect(audioInGains[index].gainNode); + + port.onChange = () => + { + const panning = clamp(audioInPans[index].get(), -1, 1); + if (!isIOS) port.panNode.pan.linearRampToValueAtTime(panning, audioCtx.currentTime + 0.01); + else port.panNode.setPosition(panning, 0, 1 - Math.abs(panning)); + }; +}); + +audioIns.forEach((port, index) => +{ + port.onChange = () => + { + const audioNode = audioIns[index].get(); + try + { + if (audioNode) + { + if (audioNode.connect) + { + const bufferedNode = oldAudioIns[index]; + bufferedNode.node = audioNode; + const gainNodePort = audioInGains[index].gainNode; + const panNodePort = audioInPans[index].panNode; + + if (!bufferedNode.isConnected) + { + bufferedNode.node.connect(panNodePort); + gainNodePort.connect(gain); + bufferedNode.isConnected = true; + } + } + } + else + { + const bufferedNode = oldAudioIns[index]; + const gainNodePort = audioInGains[index].gainNode; + const panNodePort = audioInPans[index].panNode; + + if (bufferedNode.isConnected) + { + bufferedNode.node.disconnect(panNodePort); + gainNodePort.disconnect(gain); + bufferedNode.isConnected = false; + } + } + } + catch (e) + { + op.log(e); + } + }; + + port.audioInPortNr = index; +}); + +inMasterGain.onChange = () => gain.gain.linearRampToValueAtTime((inMasterGain.get() || 0), audioCtx.currentTime + 0.01); + +op.onDelete = () => +{ + for (let i = 0; i < audioInPans.length; i += 1) + { + audioInPans[i].panNode.disconnect(); + } +}; + + +}; + +Ops.WebAudio.Mixer.prototype = new CABLES.Op(); +CABLES.OPS["363980dc-39b8-4690-8b8d-bff954f65d49"]={f:Ops.WebAudio.Mixer,objName:"Ops.WebAudio.Mixer"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.MusicalScales +// +// ************************************************************** + +Ops.WebAudio.MusicalScales = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +function clamp(val, min, max) +{ + return Math.min(Math.max(val, min), max); +} + +// constants +const NOTE_NUMBERS = [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; + +const BASE_TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; +const BASE_TONE_DEFAULT = "C"; +const SCALE_TYPE_DEFAULT = "Minor"; +const APPEND_OCTAVE_DEFAULT = true; +const OCTAVE_DEFAULT = 4; +const OCTAVE_MIN = -1; +const OCTAVE_MAX = 9; +const INCLUDE_HIGH_BASE_TONE_DEFAULT = false; + +// scale types taken from: https://github.com/stagas/scales/blob/master/index.js +const SCALE_TYPES = { + "Major": [0, 2, 4, 5, 7, 9, 11], + "Minor": [0, 2, 3, 5, 7, 8, 10], + "Ionian": [0, 2, 4, 5, 7, 9, 11], + "Aeolian": [0, 2, 3, 5, 7, 8, 10], + "Dorian": [0, 2, 3, 5, 7, 9, 10], + "Mixolydian": [0, 2, 4, 5, 7, 9, 10], + "Lydian": [0, 2, 4, 6, 7, 9, 11], + "Phrygian": [0, 1, 3, 5, 7, 8, 10], + "Locrian": [0, 1, 3, 5, 6, 8, 10], + "Diminished": [0, 1, 3, 4, 6, 7, 9, 10], + "Whole Half": [0, 2, 3, 5, 6, 8, 9, 11], + "Whole Tone": [0, 2, 4, 6, 8, 10], + "Minor Blues": [0, 3, 5, 6, 7, 10], + "Minor Pentatonic": [0, 3, 5, 7, 10], + "Major Pentatonic": [0, 2, 4, 7, 9], + "Harmonic Minor": [0, 2, 3, 5, 7, 8, 11], + "Melodic Minor": [0, 2, 3, 5, 7, 9, 11], + "Super Locrian": [0, 1, 3, 4, 6, 8, 10], + "Bhairav": [0, 1, 4, 5, 7, 8, 11], + "Hungarian Minor": [0, 2, 3, 6, 7, 8, 11], + "Minor Gypsy": [0, 1, 4, 5, 7, 8, 10], + "Hirojoshi": [0, 2, 3, 7, 8], + "In Sen": [0, 1, 5, 7, 10], + "Iwato": [0, 1, 5, 6, 10], + "Kumoi": [0, 2, 3, 7, 9], + "Pelog": [0, 1, 3, 4, 7, 8], + "Spanish": [0, 1, 3, 4, 5, 6, 8, 10], + "Ion Aeol": [0, 2, 3, 4, 5, 7, 8, 9, 10, 11] +}; + +// input +const baseTonePort = op.inDropDown("Root Note", BASE_TONES, BASE_TONE_DEFAULT); +const scaleTypePort = op.inDropDown("Scale Type", Object.keys(SCALE_TYPES), SCALE_TYPE_DEFAULT); +const includeHighBaseTonePort = op.inBool("Include Upper Root Note", INCLUDE_HIGH_BASE_TONE_DEFAULT); +const octavePort = op.inInt("Octave", OCTAVE_DEFAULT); +const appendOctavePort = op.inBool("Append Octave To Names", APPEND_OCTAVE_DEFAULT); + +op.setPortGroup("Scale Settings", [baseTonePort, scaleTypePort, includeHighBaseTonePort, octavePort, appendOctavePort]); + +// output +const outNoteNames = op.outArray("Note Names Array"); +const outNoteSteps = op.outArray("Note Step Number Array"); +const outMidiNotes = op.outArray("Midi Note Array"); +const outCurrentScale = op.outString("Current Scale"); +// change listeners +baseTonePort.onChange = scaleTypePort.onChange = octavePort.onChange += appendOctavePort.onChange = includeHighBaseTonePort.onChange = setOutput; + +// functions +function getToneAt(index, offset) +{ + let i = index + offset; + if (i >= BASE_TONES.length) + { + i -= BASE_TONES.length; + } + return BASE_TONES[i]; +} + +function getToneIndex(tone) +{ + for (let i = 0; i < BASE_TONES.length; i++) + { + if (BASE_TONES[i] === tone) + { + return i; + } + } + return -1; +} + +function setOutput() +{ + const baseTone = baseTonePort.get(); + const baseToneIndex = getToneIndex(baseTone); + const scaleType = scaleTypePort.get(); + + if (SCALE_TYPES.hasOwnProperty(scaleType) && BASE_TONES.indexOf(baseTone) > -1) + { + const scale = SCALE_TYPES[scaleType]; + const noteNamesArray = []; + const noteStepsArray = []; + const appendOctave = appendOctavePort.get(); + let octave = octavePort.get(); + + if (octave > OCTAVE_MAX) op.setUiError("octave", "Octave value higher than " + OCTAVE_MAX + ". Setting to " + OCTAVE_MAX + ". Highest midi note is 127.", 1); + else if (octave < OCTAVE_MIN) op.setUiError("octave", "Octave value lower than " + OCTAVE_MIN + ". Setting to " + OCTAVE_MIN + ". Lowest midi note is 0.", 1); + else op.setUiError("octave", null); + + octave = clamp(Math.round(octave), OCTAVE_MIN, OCTAVE_MAX); + + for (let i = 0; i < scale.length; i++) + { + noteNamesArray.push(getToneAt(scale[i], baseToneIndex) + (appendOctave ? octave : "")); + noteStepsArray.push(scale[i]); + } + + // append the base tone in the next octave + if (includeHighBaseTonePort.get()) + { + noteStepsArray.push(scale[0] + 12); + noteNamesArray.push(getToneAt(scale[0], baseToneIndex) + (appendOctave ? octave + 1 : "")); + } + + const midiNotes = NOTE_NUMBERS + .filter((nr) => noteStepsArray.includes(nr - 12)) // only get scale notes + .map((nr) => nr + BASE_TONES.indexOf(baseTone) + 12 * octave) // map to midi note number + .filter((midiNote) => midiNote <= 127); // only use values <= 127 (highest possible midi note) + + outNoteNames.set(null); + outNoteNames.set(noteNamesArray); + + outNoteSteps.set(null); + outNoteSteps.set(noteStepsArray); + + outMidiNotes.set(null); + outMidiNotes.set(midiNotes); + + outCurrentScale.set(scaleTypePort.get()); + } +} + +setOutput(); + + +}; + +Ops.WebAudio.MusicalScales.prototype = new CABLES.Op(); +CABLES.OPS["9e8c08d9-8c9a-4cff-8e5b-05c72d1f8522"]={f:Ops.WebAudio.MusicalScales,objName:"Ops.WebAudio.MusicalScales"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.Output_v2 +// +// ************************************************************** + +Ops.WebAudio.Output_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inAudio = op.inObject("Audio In", null, "audioNode"), + inGain = op.inFloatSlider("Volume", 1), + inMute = op.inBool("Mute", false), + inShowSusp = op.inBool("Show Audio Suspended Button", true), + outVol = op.outNumber("Current Volume", 0), + outState = op.outString("Context State", "unknown"); + +op.setPortGroup("Volume Settings", [inMute, inGain]); + +let isSuspended = false; +let audioCtx = CABLES.WEBAUDIO.createAudioContext(op); +let gainNode = audioCtx.createGain(); +const destinationNode = audioCtx.destination; +let oldAudioIn = null; +let connectedToOut = false; +// let fsElement = null; + +inMute.onChange = () => +{ + mute(inMute.get()); +}; + +inGain.onChange = setVolume; +op.onMasterVolumeChanged = setVolume; + +let pauseId = op.patch.on("pause", setVolume); +let resumeId = op.patch.on("resume", setVolume); + +audioCtx.addEventListener("statechange", updateStateError); +inShowSusp.onChange = updateAudioStateButton; + +updateStateError(); +updateAudioStateButton(); + +op.onDelete = () => +{ + if (gainNode) gainNode.disconnect(); + gainNode = null; + CABLES.interActionNeededButton.remove("audiosuspended"); + if (pauseId) op.patch.off(pauseId); + if (resumeId) op.patch.off(resumeId); +}; + +inAudio.onChange = function () +{ + if (!inAudio.get()) + { + if (oldAudioIn) + { + try + { + if (oldAudioIn.disconnect) + { + oldAudioIn.disconnect(gainNode); + } + } + catch (e) + { + op.logError(e); + } + } + + op.setUiError("multipleInputs", null); + + if (connectedToOut) + { + if (gainNode)gainNode.disconnect(destinationNode); + connectedToOut = false; + } + } + else + { + if (inAudio.links.length > 1) op.setUiError("multipleInputs", "You have connected multiple inputs. It is possible that you experience unexpected behaviour. Please use a Mixer op to connect multiple audio streams.", 1); + else op.setUiError("multipleInputs", null); + + if (inAudio.get().connect) inAudio.get().connect(gainNode); + } + + oldAudioIn = inAudio.get(); + + if (!connectedToOut) + { + if (gainNode)gainNode.connect(destinationNode); + connectedToOut = true; + } + + setVolume(); +}; + +function setVolume(fromMute) +{ + const masterVolume = op.patch.config.masterVolume || 0; + + let volume = inGain.get() * masterVolume; + + if (op.patch._paused || inMute.get()) volume = 0; + + let addTime = 0.05; + if (fromMute) addTime = 0.2; + + volume = CABLES.clamp(volume, 0, 1); + + if (!gainNode) + op.logError("gainNode undefined"); + + if (gainNode) gainNode.gain.linearRampToValueAtTime(volume, audioCtx.currentTime + addTime); + + outVol.set(volume); +} + +function mute(b) +{ + if (b) + { + if (audioCtx.state === "suspended") + { // make sure that when audio context is suspended node will also be muted + // this prevents the initial short sound burst being heard when context is suspended + // and started from user interaction + // also note, we have to cancle the already scheduled values as we have no influence over + // the order in which onchange handlers are executed + + if (gainNode) + { + gainNode.gain.cancelScheduledValues(audioCtx.currentTime); + gainNode.gain.value = 0; + gainNode.gain.setValueAtTime(0, audioCtx.currentTime); + } + + outVol.set(0); + + return; + } + } + + setVolume(true); +} + +function updateStateError() +{ + outState.set(audioCtx.state); + op.logVerbose("audioCtx.state change", audioCtx.state); + + if (audioCtx.state == "suspended") op.setUiError("ctxSusp", "Your Browser suspended audio context, use playButton op to play audio after a user interaction"); + else op.setUiError("ctxSusp", null); + + updateAudioStateButton(); +} + +function updateAudioStateButton() +{ + if (audioCtx.state == "suspended") + { + mute(true); + if (inShowSusp.get()) + { + isSuspended = true; + + CABLES.interActionNeededButton.add(op.patch, "audiosuspended", () => + { + if (audioCtx && audioCtx.state == "suspended") + { + audioCtx.resume(); + CABLES.interActionNeededButton.remove("audiosuspended"); + } + }); + } + else + { + CABLES.interActionNeededButton.remove("audiosuspended"); + } + } + else + { + CABLES.interActionNeededButton.remove("audiosuspended"); + + if (isSuspended) + { + op.log("was suspended - set vol"); + setVolume(true); + } + } +} + + +}; + +Ops.WebAudio.Output_v2.prototype = new CABLES.Op(); +CABLES.OPS["90b95403-b0c4-4980-ab3b-b6c354771c81"]={f:Ops.WebAudio.Output_v2,objName:"Ops.WebAudio.Output_v2"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.SamplePlayer +// +// ************************************************************** + +Ops.WebAudio.SamplePlayer = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +function clamp(value, min, max) +{ + return Math.min(Math.max(value, min), max); +} + +const audioCtx = CABLES.WEBAUDIO.createAudioContext(op); + +// input ports +const audioBufferPort = op.inObject("Audio Buffer", null, "audioBuffer"); +const inTrigger = op.inTriggerButton("Play Sample"); +const inTriggerStop = op.inTriggerButton("Stop Playback"); +const offsetPort = op.inValue("Offset", 0); +const maxSamples = op.inInt("Buffer Size", 32); +const playbackRatePort = op.inValue("Playback Rate", 1); +const detunePort = op.inValue("Detune", 0); + +op.setPortGroup("Time Controls", [offsetPort]); +op.setPortGroup("Miscellaneous", [playbackRatePort, detunePort]); +// output ports +const audioOutPort = op.outObject("Audio Out", null, "audioNode"); +const outPlaying = op.outBool("Is Playing", false); + +if (!audioBufferPort.isLinked()) +{ + op.setUiError("inputNotConnected", "To be able to play back sound, you need to connect an AudioBuffer to this op.", 0); +} +else +{ + op.setUiError("inputNotConnected", null); +} + +audioBufferPort.onLinkChanged = () => +{ + if (!audioBufferPort.isLinked()) + { + op.setUiError("inputNotConnected", "To be able to play back sound, you need to connect an AudioBuffer to this op.", 0); + } + else + { + op.setUiError("inputNotConnected", null); + } +}; + +if (!audioOutPort.isLinked()) +{ + op.setUiError("outputNotConnected", "To be able to hear sound playing, you need to connect this op to an Output op.", 0); +} +else +{ + op.setUiError("outputNotConnected", null); +} + +audioOutPort.onLinkChanged = () => +{ + if (!audioOutPort.isLinked()) + { + op.setUiError("outputNotConnected", "To be able to hear sound playing, you need to connect this op to an Output op.", 0); + } + else + { + op.setUiError("outputNotConnected", null); + } +}; + +// vars +let isPlaying = false; + +const gainNode = audioCtx.createGain(); + +let sourceSize = maxSamples.get() || 32; +let SOURCES = new Array(sourceSize).fill(0).map(() => + ({ + "bufferSource": audioCtx.createBufferSource(), + "isPlaying": false, + "gainNode": audioCtx.createGain(), + "isGainNodeConnected": false + })); +let SOURCES_LENGTH = SOURCES.length; + +maxSamples.onChange = () => +{ + op.setUiError("maxSamples", null); + if (!maxSamples.get() || maxSamples.get < 1 || maxSamples.get() > 32) + { + op.setUiError("maxSamples", "Buffer Size needs to be a number (1-32)"); + } + sourceSize = maxSamples.get() || 32; + SOURCES = new Array(sourceSize).fill(0).map(() => + ({ + "bufferSource": audioCtx.createBufferSource(), + "isPlaying": false, + "gainNode": audioCtx.createGain(), + "isGainNodeConnected": false + })); + SOURCES_LENGTH = SOURCES.length; + currentSample = 0; + createAudioBufferSources(); +}; + +detunePort.onChange = playbackRatePort.onChange = () => +{ + op.setUiError("playbackRate", null); + if (playbackRatePort.get() < 0) + { + op.setUiError("playbackRate", "Playback Rate needs to be a positive number"); + } + try + { + SOURCES.forEach((src) => + { + /* playback rate */ + const playbackRate = clamp(playbackRatePort.get(), 0.01, 4); + if ( + playbackRate >= src.bufferSource.playbackRate.minValue + && playbackRate <= src.bufferSource.playbackRate.maxValue + ) + { + src.bufferSource.playbackRate.setValueAtTime( + playbackRate, + audioCtx.currentTime + 0.01 + ); + } + + /* detune */ + const detune = detunePort.get(); + if (src.bufferSource.detune) + { + src.bufferSource.detune.setValueAtTime( + detune, + audioCtx.currentTime + 0.01 + ); + } + }); + } + catch (e) + { + op.log("err in param change", e); + } +}; + +function createAudioBufferSources() +{ + const buffer = audioBufferPort.get(); + if (!buffer) return; + + for (let i = 0; i < SOURCES_LENGTH; i += 1) + { + const src = SOURCES[i]; + createSingleSource(src); + } + + audioOutPort.set(gainNode); +} + +function createSingleSource(src) +{ + const buffer = audioBufferPort.get(); + if (!buffer) return; + if (src.isPlaying) return; + + if (src.isGainNodeConnected) src.bufferSource.disconnect(src.gainNode); + + src.bufferSource = audioCtx.createBufferSource(); + + /* end callback */ + src.bufferSource.onended = () => + { + src.isPlaying = false; + outPlaying.set(SOURCES.some((src) => src.isPlaying === true)); + createSingleSource(src); + }; + + src.bufferSource.buffer = buffer; + src.bufferSource.loop = false; + + /* playback rate */ + const playbackRate = clamp(playbackRatePort.get(), 0.01, 4); + if ( + playbackRate >= src.bufferSource.playbackRate.minValue + && playbackRate <= src.bufferSource.playbackRate.maxValue + ) + { + src.bufferSource.playbackRate.setValueAtTime( + playbackRate, + audioCtx.currentTime + ); + } + + /* detune */ + const detune = detunePort.get(); + if (src.bufferSource.detune) + { + src.bufferSource.detune.setValueAtTime( + detune, + audioCtx.currentTime + ); + } + + src.bufferSource.connect(src.gainNode); + src.gainNode.gain.setValueAtTime(0, audioCtx.currentTime); + src.gainNode.connect(gainNode); + src.isGainNodeConnected = true; +} + +let currentSample = 0; +inTrigger.onTriggered = () => +{ + if (!maxSamples.get() || maxSamples.get < 1 || maxSamples.get() > 32) return; + if (playbackRatePort.get() < 0) return; + + if (!audioBufferPort.get() || !(audioBufferPort.get() instanceof AudioBuffer)) return; + try + { + const time = 0; + const offset = Number(offsetPort.get()); + + if (!SOURCES[currentSample].isPlaying) + { + SOURCES[currentSample].bufferSource.start(Math.max(0, time), Math.max(0, offset)); + SOURCES[currentSample].gainNode.gain.linearRampToValueAtTime(1.0, audioCtx.currentTime + 0.01); + SOURCES[currentSample].isPlaying = true; + outPlaying.set(true); + } + + currentSample += 1; + currentSample %= SOURCES_LENGTH; + } + catch (e) + { + op.setUiError(e); + op.log("Error: ", e); + outPlaying.set(false); + } +}; + +inTriggerStop.onTriggered = () => +{ + SOURCES.forEach((src, index) => + { + if (src.isPlaying && src.bufferSource) src.bufferSource.stop(); + src.isPlaying = false; + }); + outPlaying.set(false); +}; + +inTrigger.onLinkChanged = () => +{ + if (!inTrigger.isLinked()) + { + SOURCES.forEach((src, index) => + { + if (src.isPlaying && src.bufferSource) src.bufferSource.stop(); + src.isPlaying = false; + }); + outPlaying.set(false); + } +}; +// change listeners +audioBufferPort.onChange = function () +{ + if (audioBufferPort.get()) + { + if ((audioBufferPort.get() instanceof AudioBuffer)) + { + createAudioBufferSources(); + } + } + else + { + SOURCES.forEach((src) => + { + if (src.bufferSource && src.isGainNodeConnected) + { + src.bufferSource.disconnect(src.gainNode); + src.gainNode.disconnect(gainNode); + src.isGainNodeConnected = false; + } + src.bufferSource = null; + }); + + outPlaying.set(false); + } +}; + + +}; + +Ops.WebAudio.SamplePlayer.prototype = new CABLES.Op(); +CABLES.OPS["47796760-f211-47f5-9d6b-cc9cd6944897"]={f:Ops.WebAudio.SamplePlayer,objName:"Ops.WebAudio.SamplePlayer"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.TextToSpeech.Say_v2 +// +// ************************************************************** + +Ops.WebAudio.TextToSpeech.Say_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +// default / min /max values +let PITCH_DEFAULT = 1; +let PITCH_MIN = 0; +let PITCH_MAX = 2; +let RATE_DEFAULT = 1; +let RATE_MIN = 0.1; +let RATE_MAX = 10; +let VOLUME_DEFAULT = 1; +let VOLUME_MIN = 0; +let VOLUME_MAX = 1; + +// vars +let synth = window.speechSynthesis; +let voiceMap = getVoiceMap(synth.getVoices()); +let voiceMapKeys = Object.keys(voiceMap); + +// inputs +let updateStatePort = op.inTrigger("Update State"); +let textPort = op.inString("Text", "Wazzup"); +let triggerPort = op.inTriggerButton("Say"); +let voicePort = op.inDropDown("Voice", voiceMapKeys); +let pitchPort = op.inFloatSlider("Pitch", PITCH_DEFAULT, PITCH_MIN, PITCH_MAX); +let ratePort = op.inFloatSlider("Rate", RATE_DEFAULT, RATE_MIN, RATE_MAX); +let volumePort = op.inFloatSlider("Volume", VOLUME_DEFAULT, VOLUME_MIN, VOLUME_MAX); + +let sayOnTextChangePort = op.inBool("Say on Text Change", false); +let pausePort = op.inTriggerButton("Pause"); +let resumePort = op.inTriggerButton("Resume"); +let cancelPort = op.inTriggerButton("Cancel"); + +// outputs +let nextPort = op.outTrigger("Next"); +let speakingPort = op.outBoolNum("Speaking", false); +let pendingPort = op.outBoolNum("Pending", false); +let pausedPort = op.outBoolNum("Paused", false); + +// change listeners +updateStatePort.onTriggered = updateState; +triggerPort.onTriggered = say; +sayOnTextChangePort.onChange = function () +{ + if (sayOnTextChangePort.get()) + { + textPort.onChange = say; + } + else + { + textPort.onChange = function () {}; // don't do anything + } +}; +pausePort.onTriggered = function () +{ + synth.pause(); +}; +resumePort.onTriggered = function () +{ + synth.resume(); +}; +cancelPort.onTriggered = function () +{ + synth.cancel(); +}; + +// voices loaded callback (async) +window.speechSynthesis.onvoiceschanged = function () +{ + voiceMap = getVoiceMap(synth.getVoices()); + voiceMapKeys = Object.keys(voiceMap); + if (CABLES.UI) + { + voicePort.uiAttribs.values = voiceMapKeys; // update dropdown values + op.refreshParams(); // update visible dropdown menu + } +}; + +/** + * Updates the state output ports + */ +function updateState() +{ + speakingPort.set(synth.speaking); + pendingPort.set(synth.pending); + pausedPort.set(synth.paused); + nextPort.trigger(); +} + +/** + * says the text from text-port using voice voice + */ +function say() +{ + let text = textPort.get(); + let voice; + let voiceDisplayName = voicePort.get(); + if (voiceDisplayName && voiceMap.hasOwnProperty(voiceDisplayName)) + { // voices are loaded async, at start it may not be there + voice = voiceMap[voiceDisplayName]; + } + let utterance = new SpeechSynthesisUtterance(text); + if (voice) + { + utterance.voice = voice; + } + let pitch = pitchPort.get(); + if (pitch < PITCH_MIN) { pitch = PITCH_MIN; } + else if (pitch > PITCH_MAX) { pitch = PITCH_MAX; } + utterance.pitch = pitch; + let rate = ratePort.get(); + if (rate < RATE_MIN) { rate = RATE_MIN; } + else if (rate > RATE_MAX) { rate = RATE_MAX; } + utterance.rate = rate; + let volume = volumePort.get(); + if (volume < VOLUME_MIN) { volume = VOLUME_MIN; } + else if (volume > VOLUME_MAX) { volume = VOLUME_MAX; } + utterance.volume = volume; + synth.speak(utterance); +} + +/** + * Returns a map of voices + * e.g. { "Alex (de-DE)": { voice object }, ...} + */ +function getVoiceMap(voices) +{ + let ret = {}; + if (!voices || voices.length === 0) { return ret; } + + voices.forEach(function (voice) + { + let key = voice.name + " (" + voice.lang + ")"; + ret[key] = voice; + }); + return ret; +} + + +}; + +Ops.WebAudio.TextToSpeech.Say_v2.prototype = new CABLES.Op(); +CABLES.OPS["eb71ad36-9756-449b-89e6-54bf5c944d81"]={f:Ops.WebAudio.TextToSpeech.Say_v2,objName:"Ops.WebAudio.TextToSpeech.Say_v2"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.ThreeBandEqualizer +// +// ************************************************************** + +Ops.WebAudio.ThreeBandEqualizer = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +function clamp(val, min, max) +{ + return Math.min(Math.max(val, min), max); +} + +let audioContext = CABLES.WEBAUDIO.createAudioContext(op); + +// default values + min and max +const FREQUENCY_MIN = 10; +const FREQUENCY_MAX = audioContext.sampleRate / 2; // Nyquist frequency. +const Q_MIN = 0.0001; +const Q_MAX = 1000; +const GAIN_MIN = -40; +const GAIN_MAX = 40; + +const inAudio = op.inObject("Audio In", null, "audioNode"); + +const inLowFilterType = op.inDropDown("Low Filter Type", ["peaking", "lowshelf"], "lowshelf"); +const inLowFrequency = op.inFloat("Low Frequency", 250); +const inLowQ = op.inFloat("Low Q", 0.0001); +const inLowGain = op.inFloat("Low Gain", 0); +op.setPortGroup("Low", [inLowFilterType, inLowFrequency, inLowQ, inLowGain]); + +const inMidFilterType = op.inDropDown("Mid Filter Type", ["peaking", "notch"], "peaking"); +const inMidFrequency = op.inFloat("Mid Frequency", 1000); +const inMidQ = op.inFloat("Mid Q", 0.0001); +const inMidGain = op.inFloat("Mid Gain", 0); +op.setPortGroup("Mid", [inMidFilterType, inMidFrequency, inMidQ, inMidGain]); + +const inHighFilterType = op.inDropDown("High Filter Type", ["peaking", "highshelf"], "highshelf"); +const inHighFrequency = op.inFloat("High Frequency", 5000); +const inHighQ = op.inFloat("High Q", 0.0001); +const inHighGain = op.inFloat("High Gain", 0); +op.setPortGroup("High", [inHighFilterType, inHighFrequency, inHighQ, inHighGain]); + +const lowFilterNode = audioContext.createBiquadFilter(); +const midFilterNode = audioContext.createBiquadFilter(); +const highFilterNode = audioContext.createBiquadFilter(); +lowFilterNode.connect(midFilterNode); +midFilterNode.connect(highFilterNode); + +lowFilterNode.type = inLowFilterType.get(); +midFilterNode.type = inMidFilterType.get(); +highFilterNode.type = inHighFilterType.get(); +const FILTER_TYPES = [ + { "node": lowFilterNode, "port": inLowFilterType }, + { "node": midFilterNode, "port": inMidFilterType }, + { "node": highFilterNode, "port": inHighFilterType } +]; + +const FILTER_FREQUENCIES = [ + { "node": lowFilterNode, "port": inLowFrequency, "name": "low" }, + { "node": midFilterNode, "port": inMidFrequency, "name": "mid" }, + { "node": highFilterNode, "port": inHighFrequency, "name": "high" } +]; + +const FILTER_QS = [ + { "node": lowFilterNode, "port": inLowQ, "name": "low" }, + { "node": midFilterNode, "port": inMidQ, "name": "mid" }, + { "node": highFilterNode, "port": inHighQ, "name": "high" } +]; + +const FILTER_GAINS = [ + { "node": lowFilterNode, "port": inLowGain, "name": "low" }, + { "node": midFilterNode, "port": inMidGain, "name": "mid" }, + { "node": highFilterNode, "port": inHighGain, "name": "high" } +]; + +FILTER_TYPES.forEach((obj, index) => +{ + /* initial greyout-ing */ + const type = obj.port.get(); + + FILTER_GAINS[index].port.setUiAttribs({ + "greyout": ["lowpass", "highpass", "bandpass", "notch", "allpass"].includes(type) + }); + + FILTER_QS[index].port.setUiAttribs({ + "greyout": ["lowshelf", "highshelf"].includes(type) + }); + + /* onChange handler */ + obj.port.onChange = () => + { + const type = obj.port.get(); + FILTER_GAINS[index].port.setUiAttribs({ + "greyout": ["lowpass", "highpass", "bandpass", "notch", "allpass"].includes(type) + }); + + FILTER_QS[index].port.setUiAttribs({ + "greyout": ["lowshelf", "highshelf"].includes(type) + }); + + obj.node.type = type; + }; +}); + +FILTER_FREQUENCIES.forEach((obj, index) => +{ + obj.port.onChange = () => + { + const freq = obj.port.get(); + if (freq) + { + if (freq >= FREQUENCY_MIN && freq <= FREQUENCY_MAX) + { + obj.node.frequency.setValueAtTime(clamp(freq, FREQUENCY_MIN, FREQUENCY_MAX), audioContext.currentTime); + op.setUiError("freqRange", null); + } + if (freq < FREQUENCY_MIN) + { + op.setUiError("freqRange", "The frequency you selected for the " + obj.name + " band is lower than the possible frequency of " + FREQUENCY_MIN + " Hz.", 1); + } + else if (freq > FREQUENCY_MAX) + { + op.setUiError("freqRange", "The frequency you selected for the " + obj.name + " band is higher than the possible frequency of " + FREQUENCY_MAX + " Hz.", 1); + } + } + }; +}); + +FILTER_QS.forEach((obj, index) => +{ + obj.port.onChange = () => + { + const q = obj.port.get(); + obj.node.Q.setValueAtTime(clamp(q, Q_MIN, Q_MAX), audioContext.currentTime); + + if (q < Q_MIN) op.setUiError(obj.name + "_qRange", "Your Q value for the " + obj.name + " band is below the minimum possible value of " + Q_MIN + ".", 1); + else if (q > Q_MAX) op.setUiError(obj.name + "_qRange", "Your Q value for the " + obj.name + " band is above the maximum possible value of " + Q_MAX + ".", 1); + else + { + op.setUiError(obj.name + "_qRange", null); + } + }; +}); + +FILTER_GAINS.forEach((obj, index) => +{ + obj.port.onChange = () => + { + const gain = obj.port.get(); + + obj.node.gain.setValueAtTime(clamp(gain, GAIN_MIN, GAIN_MAX), audioContext.currentTime); + + if (gain < GAIN_MIN) op.setUiError(obj.name + "GainRange", "Your gain value for the " + obj.name + " band is below the minimum possible value of " + GAIN_MIN + " dB.", 1); + else if (gain > GAIN_MAX) op.setUiError(obj.name + "GainRange", "Your gain value for the " + obj.name + " band is above the maximum possible value of " + GAIN_MAX + " dB.", 1); + else + { + op.setUiError(obj.name + "GainRange", null); + } + }; +}); + +const outAudio = op.outObject("Audio Out", null, "audioNode"); + +let oldAudioIn = null; + +inAudio.onChange = function () +{ + if (!inAudio.get()) + { + if (oldAudioIn) + { + try + { + if (oldAudioIn.disconnect) + { + oldAudioIn.disconnect(lowFilterNode); + } + } + catch (e) + { + op.log(e); + } + } + outAudio.set(null); + } + else + { + if (inAudio.get().connect) inAudio.get().connect(lowFilterNode); + } + oldAudioIn = inAudio.get(); + outAudio.set(highFilterNode); +}; + +op.onDelete = () => +{ + lowFilterNode.disconnect(); + midFilterNode.disconnect(); +}; + + +}; + +Ops.WebAudio.ThreeBandEqualizer.prototype = new CABLES.Op(); +CABLES.OPS["eb875ed3-335b-4709-82ab-3d6514ae2ae6"]={f:Ops.WebAudio.ThreeBandEqualizer,objName:"Ops.WebAudio.ThreeBandEqualizer"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.WaveformMesh +// +// ************************************************************** + +Ops.WebAudio.WaveformMesh = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +// currently only uses mono, if we want to extract stereo data some changes in extractPeaks are needed + +// constants +const SAMPLES_PER_PIXEL_MIN = 100; // might crash when this is too low + +function findMinMax(array) +{ + let min = Infinity; + let max = -Infinity; + let i = 0; + let len = array.length; + let curr; + + for (; i < len; i++) + { + curr = array[i]; + if (min > curr) + { + min = curr; + } + if (max < curr) + { + max = curr; + } + } + + return { + "min": min, + "max": max + }; +} + +// vars +const geom = new CGL.Geometry("Waveform"); +let mesh = null; +const cgl = op.patch.cgl; + +// input +const renderPort = op.inTrigger("Render"); +const audioBufferPort = op.inObject("Audio Buffer", null, "audioBuffer"); +const renderActivePort = op.inBool("Render Active", true); +const showBottomHalfPort = op.inBool("Show bottom half", true); +const centerPort = op.inBool("Center Origin", true); +const widthPort = op.inValue("Width", 30); +const samplesPerPixelPort = op.inInt("Samples Per Pixel", 10000); +const inCalculateUV = op.inBool("Calculate Tex Coords", true); + +op.setPortGroup("Render Options", [renderActivePort, showBottomHalfPort, centerPort]); +op.setPortGroup("Waveform Settings", [widthPort, samplesPerPixelPort]); +op.setPortGroup("Mesh Options", [inCalculateUV]); +// output +const nextPort = op.outTrigger("Next"); +const geometryPort = op.outObject("Geometry", null, "geometry"); + +// change listeners +let updating = true; +audioBufferPort.onChange = samplesPerPixelPort.onChange += showBottomHalfPort.onChange = centerPort.onChange += widthPort.onChange = inCalculateUV.onChange = () => + { + updating = true; + }; + +renderPort.onTriggered = () => +{ + if (updating) + { + extractPeaks(); + updating = false; + } + if (mesh && renderActivePort.get()) + { + mesh.render(cgl.getShader()); + } + nextPort.trigger(); +}; + +// functions +function calculateUV(meshPoints) +{ + const texCoordsNew = []; + const xTex = []; + const yTex = []; + + for (let i = 0; i < meshPoints.length; i += 3) + { + xTex.push(meshPoints[i + 0]); + yTex.push(meshPoints[i + 1]); + } + + const minMaxX = findMinMax(xTex); + const minMaxY = findMinMax(yTex); + + const normalizedTexX = xTex.map((val) => mapRange(val, minMaxX.min, minMaxX.max, 0, 1)); + const normalizedTexY = yTex.map((val) => mapRange(val, minMaxY.min, minMaxY.max, 0, 1)); + + const finalTexCoords = []; + + for (let i = 0; i < normalizedTexX.length; i += 1) + { + finalTexCoords.push(normalizedTexX[i], 1.0 - normalizedTexY[i]); + } + + return finalTexCoords; +} + +function createMesh(meshPoints) +{ + geom.clear(); + geom.vertices = meshPoints; + + if (inCalculateUV.get()) + { + const texCoords = calculateUV(meshPoints); + geom.setTexCoords(texCoords); + } + else + { + geom.setTexCoords([]); + } + + geom.calculateNormals(); + + for (let i = 0; i < geom.vertexNormals.length; i += 3) + { + geom.vertexNormals[i + 2] *= -1; + } + + mesh = new CGL.Mesh(cgl, geom); + geometryPort.set(null); + geometryPort.set(geom); +} + +/* + * Add to triangles to the mesh + * z-coordinate is assumed to be 0. + * @param meshPoints The mesh to add triangles to, [x0, y0, z0, x1, y1, z1, ...] + * @param splinePoints The source for the traingle coordinates, [x0, y0, z0, x1, y1, z1, ...] + @ param i The index of the first x-coordinate in splinePoints array + */ +function addTrianglesToMesh(meshPoints, splinePoints, i) +{ + // first triangle + meshPoints.push(splinePoints[i], splinePoints[i + 1], 0); // point a + meshPoints.push(splinePoints[i + 3], splinePoints[i + 4], 0); // point b + meshPoints.push(splinePoints[i + 3], 0, 0); // point b (y=0) + // second triangle + meshPoints.push(splinePoints[i], splinePoints[i + 1], 0); // point a + meshPoints.push(splinePoints[i + 3], 0, 0); // point b (y=0) + meshPoints.push(splinePoints[i], 0, 0); // point a (y=0) +} + +/** + * Creates triangles in the form [ax, ay, az, bx, by, bz, cx, cy, cz, ...] + * based on all points in splinePoints + */ +function createMeshPoints(splinePoints) +{ + // TODO: calc indices ? optimize mesh poitns + if (!splinePoints) { return []; } + let meshPoints = []; + // if we only draw one half, we can just go over all points + if (!showBottomHalfPort.get()) + { + for (var i = 0; i < splinePoints.length - 5; i += 3) + { + addTrianglesToMesh(meshPoints, splinePoints, i); + } + } + // if both sides are drawn, we need to handle the end point differently + else + { + // add top half + for (var i = 0; i < (splinePoints.length / 2) - 5; i += 3) + { + addTrianglesToMesh(meshPoints, splinePoints, i); + } + // add bottom half + for (var i = (splinePoints.length / 2); i < splinePoints.length - 5; i += 3) + { + addTrianglesToMesh(meshPoints, splinePoints, i); + } + } + return meshPoints; +} + +function extractPeaks() +{ + const audioBuffer = audioBufferPort.get(); + if (audioBuffer) + { + op.setUiError("noBuffer", null); + + if (!(audioBuffer instanceof AudioBuffer)) return; + } + else op.setUiError("noBuffer", "You need to connect the \"Audio Buffer\" input for this op to work!", 0); + + if (audioBuffer) + { + let samplesPerPixel = samplesPerPixelPort.get(); + if (samplesPerPixel < SAMPLES_PER_PIXEL_MIN) + { + op.setUiError("minSamples", "The value for \"Samples Per Pixel\" is lower than the minimum value " + SAMPLES_PER_PIXEL_MIN + ". Therefore the value has been set to " + SAMPLES_PER_PIXEL_MIN + ".", 1); + samplesPerPixel = SAMPLES_PER_PIXEL_MIN; + } + else + { + op.setUiError("minSamples", null); + } + + let makeMono = audioBuffer.numberOfChannels < 2; // TODO: If we make this a parameter, we have to check if the audio actually is stereo + + const peaks = webaudioPeaks(audioBuffer, samplesPerPixel, makeMono); + + // because we extract mono peaks we just access [0] here + const typedArr = peaks.data[0]; + const regularArr = Array.prototype.slice.call(typedArr); + + const normalizedArray = []; + normalizedArray.length = regularArr.length; + + const minMax = findMinMax(regularArr); + + for (let i = 0; i < regularArr.length; i += 1) + { + normalizedArray[i] = mapRange(regularArr[i], minMax.min, minMax.max, -1, 1); + } + + // currently the array contains values like this: [y1top, y1bottom, y2top, ...] + // we want it to be: [y1top, y2top, ..., y2bottom, y1bottom] + + const resortedArr = []; + + // to center the waveform around the corrdinate origin, we need to offset its position + + let offset = 0; + let width = widthPort.get(); + + if (centerPort.get()) offset = 1; + + for (var i = 1; i < normalizedArray.length; i += 2) + { + const xCoord = i / normalizedArray.length; + const yCoord = normalizedArray[i]; + + resortedArr.push(xCoord - offset, yCoord, 0); + } + + const minX = resortedArr[0]; + const maxX = resortedArr[resortedArr.length - 3]; + + if (showBottomHalfPort.get()) + { + for (var i = normalizedArray.length - 2; i >= 0; i -= 2) + { + const xCoord = i / regularArr.length; + const yCoord = normalizedArray[i]; + resortedArr.push(xCoord - offset, yCoord, 0); + } + } + + // re-map range of x-coordinates + let toMin = 0; + let toMax = width; + + if (centerPort.get()) + { + toMin = -width / 2; + toMax = width / 2; + } + + for (let i = 0; i < resortedArr.length; i += 3) + { + resortedArr[i] = mapRange(resortedArr[i], minX, maxX, toMin, toMax); + } + + // resortedArr now looks like: [yTop0, yTop1, ..., yBottom1, yBottom0] + const meshPoints = createMeshPoints(resortedArr); + createMesh(meshPoints); + } + else + { + geometryPort.set(null); + if (geom) geom.clear(); + if (mesh) + { + mesh.dispose(); + mesh = null; + } + } +} + +function mapRange(value, inMin, inMax, outMin, outMax) +{ + return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; +} + + +}; + +Ops.WebAudio.WaveformMesh.prototype = new CABLES.Op(); +CABLES.OPS["9f61a958-90b1-46df-a893-de48a5844583"]={f:Ops.WebAudio.WaveformMesh,objName:"Ops.WebAudio.WaveformMesh"}; + + + + +// ************************************************************** +// +// Ops.WebAudio.Waveshaper +// +// ************************************************************** + +Ops.WebAudio.Waveshaper = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +let audioContext = CABLES.WEBAUDIO.createAudioContext(op); + +const inAudio = op.inObject("audio in", null, "audioNode"); +const inDryWet = op.inFloatSlider("Dry/Wet", 1); +const inOversampling = op.inSwitch("Oversampling", ["none", "2x", "4x"], "4x"); +const inDistortionAmount = op.inInt("Distortion Amount", 15); + +const inWaveshapeArray = op.inArray("Waveshape Array In"); +const inOutputGain = op.inFloatSlider("Output Gain", 0.5); + +op.setPortGroup("Waveshape Settings", [inOversampling, inDistortionAmount, inWaveshapeArray]); +const audioOut = op.outObject("audio out", null, "audioNode"); +const outCurve = op.outArray("Curve Out"); +const outCurveLength = op.outNumber("Curve Length"); + +// * webaudio nodes * // +const inputNode = audioContext.createGain(); +const dryNode = audioContext.createGain(); +const wetNode = audioContext.createGain(); +const outputNode = audioContext.createGain(); +wetNode.gain.value = parseFloat(inDryWet.get()); +dryNode.gain.value = 1.0 - parseFloat(inDryWet.get()); + +const waveshaperNode = audioContext.createWaveShaper(); + +function makeDefaultDistortionCurve(amount) +{ + let n_samples = 256, curve = new Float32Array(n_samples); + for (let i = 0; i < n_samples; ++i) + { + // normalize [-1,1]; + let x = i * 2 / n_samples - 1; + // transfer function + curve[i] = (Math.PI + amount) * x / (Math.PI + amount * Math.abs(x)); + } + return curve; +} + +waveshaperNode.curve = makeDefaultDistortionCurve(400); +waveshaperNode.oversample = inOversampling.get(); +outCurve.set(waveshaperNode.curve); +outCurve.set(waveshaperNode.curve.length); +inputNode.connect(dryNode); +inputNode.connect(waveshaperNode); + +dryNode.connect(outputNode); +waveshaperNode.connect(wetNode); + +wetNode.connect(outputNode); + +changeDistortion(); + +function changeDistortion() +{ + if (inWaveshapeArray.get()) + { + inDistortionAmount.setUiAttribs({ "greyout": true }); + waveshaperNode.curve = Float32Array.from(inWaveshapeArray.get()); + } + else + { + inDistortionAmount.setUiAttribs({ "greyout": false }); + if (inDistortionAmount.get() < 0) + { + op.setUiError("distAmountOutOfRange", "The minimum amount of possible distortion is 0. Choosing values lower than 0 will set them to 0.", 1); + } + else + { + op.setUiError("distAmountOutOfRange", null); + } + waveshaperNode.curve = makeDefaultDistortionCurve(Math.max(0, inDistortionAmount.get())); + } + + outCurve.set(Array.from(waveshaperNode.curve)); + outCurveLength.set(waveshaperNode.curve.length); +} + +// * onChange handlers * // +inDryWet.onChange = () => +{ + wetNode.gain.linearRampToValueAtTime(Number(inDryWet.get()), audioContext.currentTime + 0.01); + dryNode.gain.linearRampToValueAtTime((1 - Number(inDryWet.get())), audioContext.currentTime + 0.01); +}; + +inWaveshapeArray.onChange = inDistortionAmount.onChange = changeDistortion; + +inOversampling.onChange = updateOversampling; + +function updateOversampling() +{ + waveshaperNode.oversample = inOversampling.get(); +} + +inOutputGain.onChange = updateGain; + +function updateGain() +{ + outputNode.gain.linearRampToValueAtTime(Number(inOutputGain.get()), audioContext.currentTime + 0.01); +} + +let oldAudioIn = null; +inAudio.onChange = function () +{ + if (!inAudio.get()) + { + if (oldAudioIn) + { + try + { + if (oldAudioIn.disconnect) + { + oldAudioIn.disconnect(inputNode); + } + } + catch (e) + { + op.log(e); + } + } + + audioOut.set(null); + } + else + { + if (inAudio.get().connect) + { + inAudio.get().connect(inputNode); + audioOut.set(outputNode); + updateGain(); + updateOversampling(); + } + } + oldAudioIn = inAudio.get(); +}; + + +}; + +Ops.WebAudio.Waveshaper.prototype = new CABLES.Op(); +CABLES.OPS["c7c957a7-4531-4909-8721-172e568ee076"]={f:Ops.WebAudio.Waveshaper,objName:"Ops.WebAudio.Waveshaper"}; + + + + +// ************************************************************** +// +// Ops.WebXr.Vr.Vr +// +// ************************************************************** + +Ops.WebXr.Vr.Vr = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"icon_svg":"%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20245.82%20141.73%22%3E%3Cdefs%3E%3Cstyle%3E.a%7Bfill%3A%23fff%3Bfill-rule%3Aevenodd%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Emask%3C%2Ftitle%3E%3Cpath%20class%3D%22a%22%20d%3D%22M175.56%2C111.37c-22.52%2C0-40.77-18.84-40.77-42.07S153%2C27.24%2C175.56%2C27.24s40.77%2C18.84%2C40.77%2C42.07S198.08%2C111.37%2C175.56%2C111.37ZM26.84%2C69.31c0-23.23%2C18.25-42.07%2C40.77-42.07s40.77%2C18.84%2C40.77%2C42.07-18.26%2C42.07-40.77%2C42.07S26.84%2C92.54%2C26.84%2C69.31ZM27.27%2C0C11.54%2C0%2C0%2C12.34%2C0%2C28.58V110.9c0%2C16.24%2C11.54%2C30.83%2C27.27%2C30.83H99.57c2.17%2C0%2C4.19-1.83%2C5.4-3.7L116.47%2C118a8%2C8%2C0%2C0%2C1%2C12.52-.18l11.51%2C20.34c1.2%2C1.86%2C3.22%2C3.61%2C5.39%2C3.61h72.29c15.74%2C0%2C27.63-14.6%2C27.63-30.83V28.58C245.82%2C12.34%2C233.93%2C0%2C218.19%2C0H27.27Z%22%2F%3E%3C%2Fsvg%3E","present_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\n\nvoid main()\n{\n outColor= texture(tex,texCoord);\n // outColor=vec4(texCoord.x,texCoord.y,0.0,1.0);\n}\n\n","present_vert":"{{MODULES_HEAD}}\nIN vec3 vPosition;\nIN vec2 attrTexCoord;\n\nIN float attrVertIndex;\n\nOUT vec2 texCoord;\nOUT vec3 norm;\nUNI mat4 projMatrix;\nUNI mat4 viewMatrix;\nUNI mat4 modelMatrix;\n\nvoid main()\n{\n texCoord=vec2(attrTexCoord.x,1.0-attrTexCoord.y);\n vec4 pos=vec4(vPosition, 1.0);\n mat4 mMatrix=modelMatrix;\n {{MODULE_VERTEX_POSITION}}\n gl_Position = projMatrix * (viewMatrix*mMatrix) * pos;\n}\n",}; +/* +https://web.dev/vr-comes-to-the-web-pt-ii/ +*/ + +const + inMainloop = op.inTrigger("Mainloop"), + inStop = op.inTriggerButton("Stop"), + inShowButton = op.inBool("Show Button", true), + inButtonStyle = op.inStringEditor("Button Style", "padding:10px;\nposition:absolute;\nleft:50%;\ntop:50%;\ntransform: translate(-50%,-50%);\nwidth:50px;\nheight:50px;\ncursor:pointer;\nborder-radius:40px;\nbackground:#444;\nbackground-repeat:no-repeat;\nbackground-size:70%;\nbackground-position:center center;\nz-index:9999;\nbackground-image:url(data:image/svg+xml," + attachments.icon_svg + ");", "inline-css"), + inRender2Tex = op.inBool("Render to texture", false), + inShader = op.inObject("Shader", null, "shader"), + msaa = op.inSwitch("MSAA", ["none", "2x", "4x", "8x"], "none"), + next = op.outTrigger("Next"), + nextPre = op.outTrigger("Render After Eyes"), + outPose = op.outObject("Viewer Pose"), + outEyeIndex = op.outNumber("Eye Index"), + outVr = op.outBoolNum("VR Support"), + outMat = op.outArray("Matrix"), + outElement = op.outObject("DOM Overlay Ele", null, "element"), + outSession = op.outBoolNum("In Session"), + outMs = op.outArray("Ms per eye"), + outTex = op.outTexture("Texture"), + outDepth = op.outTexture("Texture Depth"); + +const cgl = op.patch.cgl; +const canvas = op.patch.cgl.canvas.parentElement; + +op.setPortGroup("Startbutton", [inButtonStyle, inShowButton]); +op.setPortGroup("Texture", [inRender2Tex, msaa]); + +let msEyes = [0, 0]; +let xr = navigator.xr; +let fb = null; + +let hadError = false; +let buttonEle = null; +let glLayer = null; +let xrSession = null; +let webGLRenContext = null; +let xrReferenceSpace = null; +let vmat = mat4.create(); +let xrViewerPose = null; + +inStop.onTriggered = stopVr; +inButtonStyle.onChange = () => { if (buttonEle)buttonEle.style = inButtonStyle.get(); }; + +if (xr) xr.isSessionSupported("immersive-vr").then( + (r) => + { + outVr.set(true); + + if (r) initButton(); + else removeButton(); + }); +else removeButton(); + +op.onDelete = () => +{ + removeButton(); +}; + +inShowButton.onChange = () => +{ + if (!inShowButton.get())removeButton(); + else initButton(); +}; + +function stopVr() +{ + if (xrSession)xrSession.end(); + xrSession = null; + outSession.set(false); + + cgl.frameStore.xrSession = null; + cgl.frameStore.xrFrame = null; + cgl.frameStore.xrViewerPose = null; + cgl.frameStore.xrReferenceSpace = null; +} + +function startVr() +{ + if (xrSession) + { + stopVr(); + return; + } + + xr.requestSession("immersive-vr", {}) + .then( + async (session) => + { + xrSession = session; + outSession.set(true); + + xrSession.requestReferenceSpace("local").then( + (refSpace) => + { + xrReferenceSpace = refSpace; + }); + + if (xrSession) + { + await cgl.gl.makeXRCompatible(); + + let canvas = cgl.canvas; + webGLRenContext = canvas.getContext("webgl2", { "xrCompatible": true }); + + xrSession.updateRenderState({ "baseLayer": new XRWebGLLayer(xrSession, webGLRenContext) }); + xrSession.requestAnimationFrame(onXRFrame); + } + }, + (err) => + { + // error.... + op.error(err); + }); +} + +function onXRFrame(hrTime, xrFrame) +{ + if (hadError) return; + + let xrSession = xrFrame.session; + xrSession.requestAnimationFrame(onXRFrame); + + try + { + xrViewerPose = xrFrame.getViewerPose(xrReferenceSpace); + + cgl.frameStore.xrSession = xrSession; + cgl.frameStore.xrFrame = xrFrame; + cgl.frameStore.xrViewerPose = xrViewerPose; + cgl.frameStore.xrReferenceSpace = xrReferenceSpace; + + outMat.set(xrViewerPose.transform.matrix); + + outPose.set(null); + outPose.set(xrViewerPose); + + if (xrViewerPose) + { + glLayer = xrSession.renderState.baseLayer; + webGLRenContext.bindFramebuffer(webGLRenContext.FRAMEBUFFER, glLayer.framebuffer); + } + + CABLES.patch.emitOnAnimFrameEvent(); + + cgl.renderStart(cgl); + + if (inRender2Tex.get()) r2texStart(); + + cgl.gl.clearColor(0, 0, 0, 1); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + + op.patch.cg = cgl; + + for (let i = 0; i < xrViewerPose.views.length; i++) + { + let start = performance.now(); + outEyeIndex.set(i); + renderPre(); + renderEye(xrViewerPose.views[i]); + + msEyes[i] = performance.now() - start; + renderPost(); + + if (CGL.MESH.lastMesh)CGL.MESH.lastMesh.unBind(); + } + + if (CGL.MESH.lastMesh)CGL.MESH.lastMesh.unBind(); + + if (inRender2Tex.get()) r2texEnd(); + + cgl.gl.bindFramebuffer(cgl.gl.FRAMEBUFFER, glLayer.framebuffer); // gllayer has a default framebuffer.... interferes with cables fb stack... + + nextPre.trigger(); + + if (inRender2Tex.get()) + { + cgl.gl.clearColor(0, 0, 1, 1); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + + cgl.pushPMatrix(); + cgl.pushViewMatrix(); + + mat4.ortho(cgl.pMatrix, 0, glLayer.framebufferWidth, glLayer.framebufferHeight, 0, -10, 10); + cgl.gl.viewport(0, 0, glLayer.framebufferWidth, glLayer.framebufferHeight); + + if (!mesh)rebuildRectangle(); + + cgl.setTexture(0, outTex.get().tex); + + if (inShader.isLinked())mesh.render(inShader.get()); + else mesh.render(shader); + + cgl.popPMatrix(); + cgl.popViewMatrix(); + } + + cgl.renderEnd(cgl); + + outMs.set(msEyes); + + CGL.MESH.lastShader = null; + CGL.MESH.lastMesh = null; + op.patch.cg = null; + } + catch (e) + { + op.error(e); + hadError = true; + } +} + +inMainloop.onTriggered = () => +{ + if (!xrSession) + { + next.trigger(); + } +}; + +function renderPre() +{ + cgl.pushDepthTest(true); + cgl.pushDepthWrite(true); + cgl.pushDepthFunc(cgl.gl.LEQUAL); + + CGL.MESH.lastShader = null; + CGL.MESH.lastMesh = null; +} + +function renderPost() +{ + cgl.popDepthTest(); + cgl.popDepthWrite(); + cgl.popDepthFunc(); +} + +function renderEye(view) +{ + cgl.pushBlend(true); + cgl.gl.blendEquationSeparate(cgl.gl.FUNC_ADD, cgl.gl.FUNC_ADD); + cgl.gl.blendFuncSeparate(cgl.gl.SRC_ALPHA, cgl.gl.ONE_MINUS_SRC_ALPHA, cgl.gl.ONE, cgl.gl.ONE_MINUS_SRC_ALPHA); + + cgl.pushPMatrix(); + + cgl.pMatrix = view.projectionMatrix; + + cgl.pushViewMatrix(); + + mat4.invert(cgl.vMatrix, xrViewerPose.transform.matrix); + + let viewport = glLayer.getViewport(view); + cgl.gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height); + + next.trigger(); + + cgl.popViewMatrix(); + cgl.popPMatrix(); + cgl.popBlend(); +} + +function initButton() +{ + if (buttonEle) + { + removeButton(); + buttonEle = null; + } + + buttonEle = document.createElement("div"); + let container = op.patch.cgl.canvas.parentElement; + if (container)container.appendChild(buttonEle); + buttonEle.addEventListener("click", startVr); + buttonEle.addEventListener("touchstart", startVr); + buttonEle.style = inButtonStyle.get(); +} + +function removeButton() +{ + if (buttonEle)buttonEle.remove(); +} + +msaa.onChange = () => +{ + if (fb) fb.delete(); + fb = null; +}; + +function r2texStart() +{ + const w = glLayer.framebufferWidth; + const h = glLayer.framebufferHeight; + + if (!fb) + { + if (fb) fb.delete(); + + let selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + let selectFilter = CGL.Texture.FILTER_NEAREST; + + let ms = true; + let msSamples = 4; + + if (msaa.get() == "none") + { + msSamples = 0; + ms = false; + } + if (msaa.get() == "2x") msSamples = 2; + if (msaa.get() == "4x") msSamples = 4; + if (msaa.get() == "8x") msSamples = 8; + + fb = new CGL.Framebuffer2(cgl, w, h, + { + "name": "render2texture " + op.id, + "isFloatingPointTexture": false, + "multisampling": ms, + "wrap": selectedWrap, + "filter": selectFilter, + "depth": true, + "multisamplingSamples": msSamples, + "clear": true + }); + + outDepth.set(fb.getTextureDepth()); + } + + if (fb.getWidth() != Math.ceil(w) || fb.getHeight() != Math.ceil(h)) fb.setSize(w, h); + + fb.renderStart(cgl); +} + +function r2texEnd() +{ + fb.renderEnd(cgl); + + outTex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + outTex.set(fb.getTextureColor()); +} + +let geom = new CGL.Geometry("webxr final texture draw rectangle"); +let mesh = null; +const shader = new CGL.Shader(cgl, "fullscreenrectangle"); +shader.fullscreenRectUniform = new CGL.Uniform(shader, "t", "tex", 0); +shader.setSource(attachments.present_vert, attachments.present_frag); + +function rebuildRectangle() +{ + // const currentViewPort = cgl.getViewPort(); + + // if (currentViewPort[2] == w && currentViewPort[3] == h && mesh) return; + + let xx = 0, xy = 0; + + const w = glLayer.framebufferWidth; + const h = glLayer.framebufferHeight; + + geom.vertices = new Float32Array([ + xx + w, xy + h, 0, + xx, xy + h, 0, + xx + w, xy, 0, + xx, xy, 0 + ]); + + let tc = null; + + // if (flipY.get()) + // tc = new Float32Array([ + // 1.0, 0.0, + // 0.0, 0.0, + // 1.0, 1.0, + // 0.0, 1.0 + // ]); + // else + tc = new Float32Array([ + 1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 0.0, 0.0 + ]); + + // if (flipX.get()) + // { + // tc[0] = 0.0; + // tc[2] = 1.0; + // tc[4] = 0.0; + // tc[6] = 1.0; + // } + + geom.setTexCoords(tc); + + geom.verticesIndices = new Uint16Array([2, 1, 0, 3, 1, 2]); + geom.vertexNormals = new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,]); + geom.tangents = new Float32Array([-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0]); + geom.biTangents == new Float32Array([0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0]); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); +} + + +}; + +Ops.WebXr.Vr.Vr.prototype = new CABLES.Op(); +CABLES.OPS["884b3def-9bb3-422a-8e1c-c85dcc346f5e"]={f:Ops.WebXr.Vr.Vr,objName:"Ops.WebXr.Vr.Vr"}; + + + + +// ************************************************************** +// +// Ops.WebXr.Vr.VrController +// +// ************************************************************** + +Ops.WebXr.Vr.VrController = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUpdate = op.inTrigger("Update"), + inHand = op.inSwitch("Handedness", ["left", "right"], "right"), + next = op.outTrigger("Next"), + + outAxis1 = op.outNumber("Axis 1"), + outAxis2 = op.outNumber("Axis 2"), + outAxis3 = op.outNumber("Axis 3"), + outAxis4 = op.outNumber("Axis 4"), + outButton1 = op.outBoolNum("Button 1 Pressed"), + outButton2 = op.outBoolNum("Button 2 Pressed"), + outButton3 = op.outBoolNum("Button 3 Pressed"), + outButton4 = op.outBoolNum("Button 4 Pressed"), + outButton5 = op.outBoolNum("Button 5 Pressed"), + outButton6 = op.outBoolNum("Button 6 Pressed"), + outButton7 = op.outBoolNum("Button 7 Pressed"), + outButton1Touch = op.outBoolNum("Button 1 Touched"), + outButton2Touch = op.outBoolNum("Button 2 Touched"), + outButton3Touch = op.outBoolNum("Button 3 Touched"), + outButton4Touch = op.outBoolNum("Button 4 Touched"), + outButton5Touch = op.outBoolNum("Button 5 Touched"), + outButton6Touch = op.outBoolNum("Button 6 Touched"), + outButton7Touch = op.outBoolNum("Button 7 Touched"), + + outX = op.outNumber("Position X"), + outY = op.outNumber("Position Y"), + outZ = op.outNumber("Position Z"), + + outGp = op.outObject("Gamepad Values"), + + outTransformed = op.outTrigger("Transformed Position"), + + outFound = op.outBoolNum("Found"); + +const cgl = op.patch.cgl; + +op.setPortGroup("Gamepad", [ + outButton1, outButton2, outButton3, outButton4, outButton5, outButton6, outButton7, + outButton1Touch, outButton2Touch, outButton3Touch, outButton4Touch, outButton5Touch, outButton6Touch, outButton7Touch, + outAxis1, outAxis2, outAxis3, outAxis4]); + +inUpdate.onTriggered = () => +{ + outGp.set(null); + + if (op.patch.cgl.frameStore.xrSession) + { + let found = false; + let xrSession = op.patch.cgl.frameStore.xrSession; + + const inputSources = xrSession.inputSources; + + for (let i = 0; i < inputSources.length; i++) + { + if (inputSources[i].handedness === inHand.get()) + { + found = true; + + if (inputSources[i].gamepad)setGamepadValues(inputSources[i].gamepad); + + let controlPose = cgl.frameStore.xrFrame.getPose(inputSources[i].gripSpace, cgl.frameStore.xrReferenceSpace); + if (controlPose && controlPose.transform) + { + cgl.pushModelMatrix(); + + mat4.multiply(cgl.mMatrix, cgl.mMatrix, controlPose.transform.matrix); + outX.set(controlPose.transform.position.x); + outY.set(controlPose.transform.position.y); + outZ.set(controlPose.transform.position.z); + + outTransformed.trigger(); + + cgl.popModelMatrix(); + } + else op.log("vr controller: no controlpose transform?!"); + + break; + } + } + + outFound.set(found); + } + + next.trigger(); +}; + +function setGamepadValues(gp) +{ + outAxis1.set(gp.axes[0]); + outAxis2.set(gp.axes[1]); + + outButton1.set(gp.buttons[0].pressed); + outButton2.set(gp.buttons[1].pressed); + outButton3.set(gp.buttons[2].pressed); + outButton4.set(gp.buttons[3].pressed); + outButton5.set(gp.buttons[4].pressed); + outButton6.set(gp.buttons[5].pressed); + outButton7.set(gp.buttons[6].pressed); + + outButton1Touch.set(gp.buttons[0].touched); + outButton2Touch.set(gp.buttons[1].touched); + outButton3Touch.set(gp.buttons[2].touched); + outButton4Touch.set(gp.buttons[3].touched); + outButton5Touch.set(gp.buttons[4].touched); + outButton6Touch.set(gp.buttons[5].touched); + outButton7Touch.set(gp.buttons[6].touched); + + const g = { "buttons": gp.buttons, + "axes": gp.axes, + "connected": gp.connected, + "mapping": gp.mapping, + "axes": gp.axes + }; + + outGp.set(g); +} + + +}; + +Ops.WebXr.Vr.VrController.prototype = new CABLES.Op(); +CABLES.OPS["6959b43a-8c64-4aa1-a0f0-d4d5b7e2b1c3"]={f:Ops.WebXr.Vr.VrController,objName:"Ops.WebXr.Vr.VrController"}; + + + + +// ************************************************************** +// +// Ops.Website.Cookie +// +// ************************************************************** + +Ops.Website.Cookie = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + outCookie = op.outObject("Cookie"), + outString = op.outString("Cookie String"); + +update(); + +function str_obj(str) +{ + str = str.split(";"); + const result = {}; + + for (let i = 0; i < str.length; i++) + { + const cur = str[i].split("="); + if (cur.length > 1) result[cur[0].trim()] = cur[1].trim(); + } + return result; +} + +function update() +{ + outCookie.set(str_obj(document.cookie)); + outString.set(document.cookie); +} + + +}; + +Ops.Website.Cookie.prototype = new CABLES.Op(); +CABLES.OPS["a02b3bbe-eace-4a2e-b02b-1bf1e398d78a"]={f:Ops.Website.Cookie,objName:"Ops.Website.Cookie"}; + + + + +// ************************************************************** +// +// Ops.Website.FilenameInfo +// +// ************************************************************** + +Ops.Website.FilenameInfo = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inUrl=op.inString("URL",""), + outProtocol=op.outString("Protocol"), + outHost=op.outString("Host"), + outFullPath=op.outString("Full Path"), + outFilename=op.outString("Filename"), + outIsURL=op.outString("Is URL"), + outExt=op.outString("Suffix"); + +function isValidUrl(string) +{ + try { new URL(string); } + catch (_) { return false; } + + return true; +} + +inUrl.onChange=()=> +{ + const url=inUrl.get(); + const isUrl=isValidUrl(url); + outIsURL.set(isUrl); + + if(url.indexOf(":")>-1) + { + const pathArray = url.split( ':' ); + outProtocol.set(pathArray[0]); + } + + if(url.indexOf("/")>-1) + { + const hostArr = url.split( '/' ); + outHost.set(hostArr[2]); + } + + if(url.indexOf(".")>-1) + { + const fnArray=url.split("."); + outExt.set(fnArray[fnArray.length-1]); + } + else outExt.set(""); + + if(url.indexOf("/")>-1 ) + { + const hostArr = url.split( '/' ); + outFilename.set(hostArr[hostArr.length-1]); + + hostArr.length=hostArr.length-1; + const fullPath=hostArr.join("/"); + outFullPath.set(fullPath); + } + else + { + if(!isUrl)outFilename.set(url); + } +}; + + +}; + +Ops.Website.FilenameInfo.prototype = new CABLES.Op(); +CABLES.OPS["0a349745-20c8-44c4-8a70-214c7ccf87e9"]={f:Ops.Website.FilenameInfo,objName:"Ops.Website.FilenameInfo"}; + + + + +// ************************************************************** +// +// Ops.Website.ForceHttps +// +// ************************************************************** + +Ops.Website.ForceHttps = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; + +if (location.href.indexOf("http://localhost")) + if (location.protocol !== "https:") location.protocol = "https:"; + + +}; + +Ops.Website.ForceHttps.prototype = new CABLES.Op(); +CABLES.OPS["63eb8aad-4700-4760-ac0b-ae712ac01fd7"]={f:Ops.Website.ForceHttps,objName:"Ops.Website.ForceHttps"}; + + + + +// ************************************************************** +// +// Ops.Website.InfoURL +// +// ************************************************************** + +Ops.Website.InfoURL = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + outUrl = op.outString("URL"), + outHost = op.outString("Host"), + outHash = op.outString("Hash"), + outPathname = op.outString("Pathname"), + outProtocol = op.outString("Protocol"), + outPort = op.outString("Port"), + outChangeHash = op.outTrigger("Hash Changed"); + +op.onDelete = () => +{ + window.removeEventListener("hashchange", hashChange); +}; + +window.addEventListener("hashchange", hashChange); + +const l = document.location; +outUrl.set(l.href); +outHost.set(l.host); +outHash.set(l.hash); +outPathname.set(l.pathname); +outProtocol.set(l.protocol); +outPort.set(l.port); + +function hashChange() +{ + const l = document.location; + outHash.set(l.hash); + + outChangeHash.trigger(); +} + + +}; + +Ops.Website.InfoURL.prototype = new CABLES.Op(); +CABLES.OPS["e72fbd80-f9c7-4572-8b6d-a485ef474b74"]={f:Ops.Website.InfoURL,objName:"Ops.Website.InfoURL"}; + + + + +// ************************************************************** +// +// Ops.Website.LocalStorageNumber +// +// ************************************************************** + +Ops.Website.LocalStorageNumber = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inKey = op.inString("Key"), + inValue = op.inFloat("Number"), + inStore = op.inTriggerButton("Store"), + outValue = op.outNumber("Stored Number"), + outSupported = op.outBoolNum("Storage Support", true); + +inKey.onChange = updateOutput; +inStore.onTriggered = storeValue; + +const localStorageSupport = !!window.localStorage; +if (!localStorageSupport) +{ + op.logError("your browser does not support or blocks access to localStorage, output will be inValue!"); + outSupported.set(false); +} + +updateOutput(); +op.onLoaded = updateOutput; + +function parse(val) +{ + if (val === "true" || val === true)val = 1; + if (val === "false" || val === false)val = 0; + val = parseFloat(val); + if (val != val)val = 0; + return val; +} + +op.patch.on("localstorageStored", (key, val) => +{ + if (key == inKey.get()) outValue.set(parse(val)); +}); + +function getKey() +{ + return (op.patch.namespace || "") + inKey.get(); +} + +function updateOutput() +{ + if (localStorageSupport) + { + outValue.set(parse(window.localStorage.getItem(getKey()))); + } + else + { + outValue.set(inValue.get()); + } +} + +function storeValue() +{ + let val = parse(inValue.get()); + + if (localStorageSupport) + { + window.localStorage.setItem(getKey(), val); + } + else + { + op.warn("not storing to localstorage, missing browsersupport!"); + } + outValue.set(val); + op.patch.emitEvent("localstorageStored", inKey.get(), val); +} + + +}; + +Ops.Website.LocalStorageNumber.prototype = new CABLES.Op(); +CABLES.OPS["2894cca0-0c06-4034-9d38-04b72afad277"]={f:Ops.Website.LocalStorageNumber,objName:"Ops.Website.LocalStorageNumber"}; + + + + +// ************************************************************** +// +// Ops.Website.LocalStorageString +// +// ************************************************************** + +Ops.Website.LocalStorageString = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inKey = op.inString("Key"), + inValue = op.inString("String", ""), + inStore = op.inTriggerButton("Store"), + outValue = op.outString("Stored String"), + outSupported = op.outBool("Storage Support", true); + +const localStorageSupport = !!window.localStorage; +if (!localStorageSupport) +{ + op.logError("your browser does not support or blocks access to localStorage, output will be inValue!"); + outSupported.set(false); +} + +updateOutput(); +inKey.onChange = updateOutput; +inStore.onTriggered = storeValue; + +function getKey() +{ + return (op.patch.namespace || "") + inKey.get(); +} + +function updateOutput() +{ + if (localStorageSupport) + { + outValue.set(window.localStorage.getItem(getKey())); + } + else + { + outValue.set(inValue.get()); + } +} + +function storeValue() +{ + const val = inValue.get(); + if (localStorageSupport) + { + window.localStorage.setItem(getKey(), val); + } + else + { + op.warn("not storing to localstorage, missing browsersupport!"); + } + outValue.set(val); +} + + +}; + +Ops.Website.LocalStorageString.prototype = new CABLES.Op(); +CABLES.OPS["f908fc2e-70b6-4ca4-8afd-4302b35ff570"]={f:Ops.Website.LocalStorageString,objName:"Ops.Website.LocalStorageString"}; + + + + +// ************************************************************** +// +// Ops.Website.LocationHashRoute +// +// ************************************************************** + +Ops.Website.LocationHashRoute = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const routeIn = op.inString("Route"); +const parsedOut = op.outObject("Values", {}); +const changedOut = op.outTrigger("Changed"); +const outMatching = op.outBool("Matching"); + +let router = null; +let lastHref = null; +let hashChangeListener = null; + +op.onLoaded, op.onCreate = init; + +function init() +{ + if ("onhashchange" in window) + { + router = new Navigo("/", { "hash": true, "noMatchWarning": true }); + const eventWrapper = (event) => + { + event.internal = true; + hashChange(event); + }; + + if (hashChangeListener) + { + op.patch.removeEventListener(hashChangeListener); + hashChangeListener = null; + } + hashChangeListener = op.patch.addEventListener("LocationHashChange", eventWrapper); + window.removeEventListener("hashchange", hashChangeFromBrowser); + window.addEventListener("hashchange", hashChangeFromBrowser); + hashChange({ "newURL": window.location.href }); + } + else + { + op.setUiError("unsupported", "Your browser does not support listening to hashchanges!"); + } +} + +function hashChangeFromBrowser(event) +{ + hashChange(event); +} + +op.onDelete = function () +{ + if (hashChangeListener) + { + op.patch.removeEventListener(hashChangeListener); + hashChangeListener = null; + } + window.removeEventListener("hashchange", hashChangeFromBrowser); +}; + +routeIn.onChange = function () +{ + if (router) + { + hashChange({ "newURL": window.location.href }, true); + } +}; + +function hashChange(event, forceUpdate) +{ + let hash = ""; + if (!forceUpdate && (event.newURL === lastHref)) + { + return; + } + lastHref = event.newURL; + op.setUiError("unsupported", null); + let values = {}; + const fields = event.newURL.split("#"); + let hasMatch = false; + if (routeIn.get()) + { + if (router && fields.length > 1) + { + hasMatch = false; + for (let i = 1; i < fields.length; i++) + { + let route = routeIn.get(); + let match = fields[i]; + hash += "#" + fields[i]; + const matched = router.matchLocation(route, match); + if (matched) + { + if (matched.data) + { + const keys = Object.keys(matched.data); + keys.forEach((key) => + { + matched.data[key] = getTypedValue(matched.data[key]); + }); + values = Object.assign(values, matched.data); + } + if (matched.params) + { + const keys = Object.keys(matched.params); + keys.forEach((key) => + { + matched.params[key] = getTypedValue(matched.params[key]); + }); + values = Object.assign(values, matched.params); + } + hasMatch = true; + } + } + } + } + else + { + const all = event.newURL.split("#", 2); + hash = all[1] || ""; + hasMatch = true; + } + + if (hasMatch) + { + let paramStr = hash.split("?", 2); + let params = parseQuery(paramStr[1]); + let keys = Object.keys(params); + keys.forEach((key) => + { + if (!values.hasOwnProperty(key)) values[key] = params[key]; + }); + } + + outMatching.set(hasMatch); + + if (!(parsedOut.get().length === 0 && values.length === 0)) + { + parsedOut.set(values); + } + + if (hasMatch && !event.silent) + { + changedOut.trigger(); + } +} + +function getTypedValue(val) +{ + let value = decodeURIComponent(val || ""); + if (value !== "") + { + switch (value) + { + case "true": + value = true; + break; + + case "false": + value = false; + break; + + default: + if (!isNaN(value)) + { + value = Number(value); + } + } + } + return value; +} + +function parseQuery(str) +{ + if (typeof str != "string" || str.length == 0) return {}; + let s = str.split("&"); + let s_length = s.length; + let bit, query = {}, first, second; + for (let i = 0; i < s_length; i++) + { + bit = s[i].split("="); + first = decodeURIComponent(bit[0]); + if (first.length == 0) continue; + second = decodeURIComponent(bit[1]); + if (typeof query[first] == "undefined") query[first] = second; + else if (query[first] instanceof Array) query[first].push(second); + else query[first] = [query[first], second]; + } + return query; +} + + +}; + +Ops.Website.LocationHashRoute.prototype = new CABLES.Op(); +CABLES.OPS["1e76f92b-1eed-4575-96e1-fcff6ed08c04"]={f:Ops.Website.LocationHashRoute,objName:"Ops.Website.LocationHashRoute"}; + + + + +// ************************************************************** +// +// Ops.Website.SetLocationHash +// +// ************************************************************** + +Ops.Website.SetLocationHash = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + hashIn = op.inString("Hash", ""), + inUpdate = op.inTriggerButton("Update"), + activeIn = op.inBool("Active", false), + silentIn = op.inBool("Silent", true), + reloadIn = op.inBool("Allow Empty", false); + +inUpdate.onTriggered = update; + +function update() +{ + if (!activeIn.get()) return; + + let hash = ""; + if (hashIn.get()) + { + hash = "#" + hashIn.get(); + } + + if (window.location.hash == hash) + { + return; + } + + try + { + op.setUiError("overload", null); + const event = new Event("hashchange"); + event.oldURL = window.location.href; + if (silentIn.get()) event.silent = true; + + if (hash) + { + history.replaceState(null, null, window.location.pathname + hash); + } + else if (reloadIn.get()) + { + history.replaceState(null, null, window.location.pathname); + } + event.newURL = window.location.href; + op.patch.emitEvent("LocationHashChange", event); + window.dispatchEvent(event); + } + catch (e) + { + op.setUiError("overload", "too many changes to the location hash, throttle down"); + op.log(e.message); + } +} + + +}; + +Ops.Website.SetLocationHash.prototype = new CABLES.Op(); +CABLES.OPS["82492357-c11d-4b76-bd57-b296d3b79b83"]={f:Ops.Website.SetLocationHash,objName:"Ops.Website.SetLocationHash"}; + + + + +// ************************************************************** +// +// Ops.Website.UrlQueryParams_v2 +// +// ************************************************************** + +Ops.Website.UrlQueryParams_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + paramName = op.inString("parameter"), + def = op.inString("Default"), + result = op.outString("result"); + +def.onChange = update; +paramName.onChange = updateParam; + +const query = {}; +const a = window.location.search.substr(1).split("&"); + +update(); + +for (let i = 0; i < a.length; i++) +{ + const b = a[i].split("="); + query[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || ""); +} + +function updateParam() +{ + op.setUiAttrib({ "extendTitle": paramName.get() }); + update(); +} + +function update() +{ + if (!query.hasOwnProperty(paramName.get())) + { + const value = def.get() || null; + result.set(value); + } + else + { + let v = query[paramName.get()]; + if (v === "true")v = true; + else if (v === "false")v = false; + + result.set(v); + } +} + + +}; + +Ops.Website.UrlQueryParams_v2.prototype = new CABLES.Op(); +CABLES.OPS["2e1b645c-c463-465d-abec-bf06ee4b970c"]={f:Ops.Website.UrlQueryParams_v2,objName:"Ops.Website.UrlQueryParams_v2"}; + + diff --git a/survey_dashboard/hmc_layout/static/en_files/lang.js b/survey_dashboard/hmc_layout/static/en_files/lang.js new file mode 100644 index 0000000..abf243a --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/lang.js @@ -0,0 +1 @@ +window.i18n = {en:{"app":{"read-more":"read more","glossary":"Glossary","to-offer":"to offer","job-offers":"Job Offers","expires-at":"Expires at","contacts":"contacts","link-to-tool":"Link to Tool","detailsLinkText":"details","overviewLinkText":"back to overview","no-hits":"No results were found for your input.","search":"Search for keywords","categories":{"hmc":"Open Positions at HMC","hmc-projects":"Open Positions at HMC projects","incubator":"Open positions at other incubator platforms","fdm":"Open positions in the field of RDM"}},"auth":{"failed":"These credentials do not match our records.","throttle":"Too many login attempts. Please try again in :seconds seconds."},"pagination":{"previous":"« Previous","next":"Next »"},"passwords":{"reset":"Your password has been reset!","sent":"We have emailed your password reset link!","throttled":"Please wait before retrying.","token":"This password reset token is invalid.","user":"We can't find a user with that email address."},"routes":{"pages":"pages","hmc-office":"hmc-office","aeronautics":"aeronautics","ap2":"ap2","earth-and-environment":"earth-and-environment","energy":"energy","health":"health","information":"information","matter":"matter","news":"news","events":"events","communities":"communities","glossary":"glossary","jobs":"job-offers","contact":"contact","tools":"tools","inf-projects":"inf-projects","training-materials":"training-materials","use-cases":"use-cases"},"validation":{"accepted":"The :attribute must be accepted.","active_url":"The :attribute is not a valid URL.","after":"The :attribute must be a date after :date.","after_or_equal":"The :attribute must be a date after or equal to :date.","alpha":"The :attribute may only contain letters.","alpha_dash":"The :attribute may only contain letters, numbers, dashes and underscores.","alpha_num":"The :attribute may only contain letters and numbers.","array":"The :attribute must be an array.","before":"The :attribute must be a date before :date.","before_or_equal":"The :attribute must be a date before or equal to :date.","between":{"numeric":"The :attribute must be between :min and :max.","file":"The :attribute must be between :min and :max kilobytes.","string":"The :attribute must be between :min and :max characters.","array":"The :attribute must have between :min and :max items."},"boolean":"The :attribute field must be true or false.","confirmed":"The :attribute confirmation does not match.","date":"The :attribute is not a valid date.","date_equals":"The :attribute must be a date equal to :date.","date_format":"The :attribute does not match the format :format.","different":"The :attribute and :other must be different.","digits":"The :attribute must be :digits digits.","digits_between":"The :attribute must be between :min and :max digits.","dimensions":"The :attribute has invalid image dimensions.","distinct":"The :attribute field has a duplicate value.","email":"The :attribute must be a valid email address.","ends_with":"The :attribute must end with one of the following: :values.","exists":"The selected :attribute is invalid.","file":"The :attribute must be a file.","filled":"The :attribute field must have a value.","gt":{"numeric":"The :attribute must be greater than :value.","file":"The :attribute must be greater than :value kilobytes.","string":"The :attribute must be greater than :value characters.","array":"The :attribute must have more than :value items."},"gte":{"numeric":"The :attribute must be greater than or equal :value.","file":"The :attribute must be greater than or equal :value kilobytes.","string":"The :attribute must be greater than or equal :value characters.","array":"The :attribute must have :value items or more."},"image":"The :attribute must be an image.","in":"The selected :attribute is invalid.","in_array":"The :attribute field does not exist in :other.","integer":"The :attribute must be an integer.","ip":"The :attribute must be a valid IP address.","ipv4":"The :attribute must be a valid IPv4 address.","ipv6":"The :attribute must be a valid IPv6 address.","json":"The :attribute must be a valid JSON string.","lt":{"numeric":"The :attribute must be less than :value.","file":"The :attribute must be less than :value kilobytes.","string":"The :attribute must be less than :value characters.","array":"The :attribute must have less than :value items."},"lte":{"numeric":"The :attribute must be less than or equal :value.","file":"The :attribute must be less than or equal :value kilobytes.","string":"The :attribute must be less than or equal :value characters.","array":"The :attribute must not have more than :value items."},"max":{"numeric":"The :attribute may not be greater than :max.","file":"The :attribute may not be greater than :max kilobytes.","string":"The :attribute may not be greater than :max characters.","array":"The :attribute may not have more than :max items."},"mimes":"The :attribute must be a file of type: :values.","mimetypes":"The :attribute must be a file of type: :values.","min":{"numeric":"The :attribute must be at least :min.","file":"The :attribute must be at least :min kilobytes.","string":"The :attribute must be at least :min characters.","array":"The :attribute must have at least :min items."},"not_in":"The selected :attribute is invalid.","not_regex":"The :attribute format is invalid.","numeric":"The :attribute must be a number.","password":"The password is incorrect.","present":"The :attribute field must be present.","regex":"The :attribute format is invalid.","required":"The :attribute field is required.","required_if":"The :attribute field is required when :other is :value.","required_unless":"The :attribute field is required unless :other is in :values.","required_with":"The :attribute field is required when :values is present.","required_with_all":"The :attribute field is required when :values are present.","required_without":"The :attribute field is required when :values is not present.","required_without_all":"The :attribute field is required when none of :values are present.","same":"The :attribute and :other must match.","size":{"numeric":"The :attribute must be :size.","file":"The :attribute must be :size kilobytes.","string":"The :attribute must be :size characters.","array":"The :attribute must contain :size items."},"starts_with":"The :attribute must start with one of the following: :values.","string":"The :attribute must be a string.","timezone":"The :attribute must be a valid zone.","unique":"The :attribute has already been taken.","uploaded":"The :attribute failed to upload.","url":"The :attribute format is invalid.","uuid":"The :attribute must be a valid UUID.","custom":{"attribute-name":{"rule-name":"custom-message"}},"attributes":[]}}}; \ No newline at end of file diff --git a/survey_dashboard/hmc_layout/static/en_files/libs(1).js b/survey_dashboard/hmc_layout/static/en_files/libs(1).js new file mode 100644 index 0000000..e69de29 diff --git a/survey_dashboard/hmc_layout/static/en_files/libs.core.min(1).js b/survey_dashboard/hmc_layout/static/en_files/libs.core.min(1).js new file mode 100644 index 0000000..af46eda --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/libs.core.min(1).js @@ -0,0 +1,44 @@ +/*! +@fileoverview gl-matrix - High performance matrix and vector operations +@author Brandon Jones +@author Colin MacKenzie IV +@version 3.1.0 + +Copyright (c) 2015-2019, Brandon Jones, Colin MacKenzie IV. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t=t||self).glMatrix={})}(this,function(t){"use strict";var n=1e-6,a="undefined"!=typeof Float32Array?Float32Array:Array,r=Math.random;var u=Math.PI/180;Math.hypot||(Math.hypot=function(){for(var t=0,n=arguments.length;n--;)t+=arguments[n]*arguments[n];return Math.sqrt(t)});var e=Object.freeze({EPSILON:n,get ARRAY_TYPE(){return a},RANDOM:r,setMatrixArrayType:function(t){a=t},toRadian:function(t){return t*u},equals:function(t,a){return Math.abs(t-a)<=n*Math.max(1,Math.abs(t),Math.abs(a))}});function o(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=a[0],c=a[1],h=a[2],s=a[3];return t[0]=r*i+e*c,t[1]=u*i+o*c,t[2]=r*h+e*s,t[3]=u*h+o*s,t}function i(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t[3]=n[3]-a[3],t}var c=o,h=i,s=Object.freeze({create:function(){var t=new a(4);return a!=Float32Array&&(t[1]=0,t[2]=0),t[0]=1,t[3]=1,t},clone:function(t){var n=new a(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},copy:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},identity:function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},fromValues:function(t,n,r,u){var e=new a(4);return e[0]=t,e[1]=n,e[2]=r,e[3]=u,e},set:function(t,n,a,r,u){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t},transpose:function(t,n){if(t===n){var a=n[1];t[1]=n[2],t[2]=a}else t[0]=n[0],t[1]=n[2],t[2]=n[1],t[3]=n[3];return t},invert:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=a*e-u*r;return o?(o=1/o,t[0]=e*o,t[1]=-r*o,t[2]=-u*o,t[3]=a*o,t):null},adjoint:function(t,n){var a=n[0];return t[0]=n[3],t[1]=-n[1],t[2]=-n[2],t[3]=a,t},determinant:function(t){return t[0]*t[3]-t[2]*t[1]},multiply:o,rotate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=Math.sin(a),c=Math.cos(a);return t[0]=r*c+e*i,t[1]=u*c+o*i,t[2]=r*-i+e*c,t[3]=u*-i+o*c,t},scale:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=a[0],c=a[1];return t[0]=r*i,t[1]=u*i,t[2]=e*c,t[3]=o*c,t},fromRotation:function(t,n){var a=Math.sin(n),r=Math.cos(n);return t[0]=r,t[1]=a,t[2]=-a,t[3]=r,t},fromScaling:function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t},str:function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},frob:function(t){return Math.hypot(t[0],t[1],t[2],t[3])},LDU:function(t,n,a,r){return t[2]=r[2]/r[0],a[0]=r[0],a[1]=r[1],a[3]=r[3]-t[2]*a[1],[t,n,a]},add:function(t,n,a){return t[0]=n[0]+a[0],t[1]=n[1]+a[1],t[2]=n[2]+a[2],t[3]=n[3]+a[3],t},subtract:i,exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=a[0],c=a[1],h=a[2],s=a[3];return Math.abs(r-i)<=n*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(u-c)<=n*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(e-h)<=n*Math.max(1,Math.abs(e),Math.abs(h))&&Math.abs(o-s)<=n*Math.max(1,Math.abs(o),Math.abs(s))},multiplyScalar:function(t,n,a){return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t},multiplyScalarAndAdd:function(t,n,a,r){return t[0]=n[0]+a[0]*r,t[1]=n[1]+a[1]*r,t[2]=n[2]+a[2]*r,t[3]=n[3]+a[3]*r,t},mul:c,sub:h});function M(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=a[0],s=a[1],M=a[2],f=a[3],l=a[4],v=a[5];return t[0]=r*h+e*s,t[1]=u*h+o*s,t[2]=r*M+e*f,t[3]=u*M+o*f,t[4]=r*l+e*v+i,t[5]=u*l+o*v+c,t}function f(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t[3]=n[3]-a[3],t[4]=n[4]-a[4],t[5]=n[5]-a[5],t}var l=M,v=f,b=Object.freeze({create:function(){var t=new a(6);return a!=Float32Array&&(t[1]=0,t[2]=0,t[4]=0,t[5]=0),t[0]=1,t[3]=1,t},clone:function(t){var n=new a(6);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n},copy:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t},identity:function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},fromValues:function(t,n,r,u,e,o){var i=new a(6);return i[0]=t,i[1]=n,i[2]=r,i[3]=u,i[4]=e,i[5]=o,i},set:function(t,n,a,r,u,e,o){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t[4]=e,t[5]=o,t},invert:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=a*e-r*u;return c?(c=1/c,t[0]=e*c,t[1]=-r*c,t[2]=-u*c,t[3]=a*c,t[4]=(u*i-e*o)*c,t[5]=(r*o-a*i)*c,t):null},determinant:function(t){return t[0]*t[3]-t[1]*t[2]},multiply:M,rotate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=Math.sin(a),s=Math.cos(a);return t[0]=r*s+e*h,t[1]=u*s+o*h,t[2]=r*-h+e*s,t[3]=u*-h+o*s,t[4]=i,t[5]=c,t},scale:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=a[0],s=a[1];return t[0]=r*h,t[1]=u*h,t[2]=e*s,t[3]=o*s,t[4]=i,t[5]=c,t},translate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=a[0],s=a[1];return t[0]=r,t[1]=u,t[2]=e,t[3]=o,t[4]=r*h+e*s+i,t[5]=u*h+o*s+c,t},fromRotation:function(t,n){var a=Math.sin(n),r=Math.cos(n);return t[0]=r,t[1]=a,t[2]=-a,t[3]=r,t[4]=0,t[5]=0,t},fromScaling:function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t[4]=0,t[5]=0,t},fromTranslation:function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=n[0],t[5]=n[1],t},str:function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},frob:function(t){return Math.hypot(t[0],t[1],t[2],t[3],t[4],t[5],1)},add:function(t,n,a){return t[0]=n[0]+a[0],t[1]=n[1]+a[1],t[2]=n[2]+a[2],t[3]=n[3]+a[3],t[4]=n[4]+a[4],t[5]=n[5]+a[5],t},subtract:f,multiplyScalar:function(t,n,a){return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*a,t[5]=n[5]*a,t},multiplyScalarAndAdd:function(t,n,a,r){return t[0]=n[0]+a[0]*r,t[1]=n[1]+a[1]*r,t[2]=n[2]+a[2]*r,t[3]=n[3]+a[3]*r,t[4]=n[4]+a[4]*r,t[5]=n[5]+a[5]*r,t},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=t[4],c=t[5],h=a[0],s=a[1],M=a[2],f=a[3],l=a[4],v=a[5];return Math.abs(r-h)<=n*Math.max(1,Math.abs(r),Math.abs(h))&&Math.abs(u-s)<=n*Math.max(1,Math.abs(u),Math.abs(s))&&Math.abs(e-M)<=n*Math.max(1,Math.abs(e),Math.abs(M))&&Math.abs(o-f)<=n*Math.max(1,Math.abs(o),Math.abs(f))&&Math.abs(i-l)<=n*Math.max(1,Math.abs(i),Math.abs(l))&&Math.abs(c-v)<=n*Math.max(1,Math.abs(c),Math.abs(v))},mul:l,sub:v});function m(){var t=new a(9);return a!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[5]=0,t[6]=0,t[7]=0),t[0]=1,t[4]=1,t[8]=1,t}function d(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=n[8],f=a[0],l=a[1],v=a[2],b=a[3],m=a[4],d=a[5],x=a[6],p=a[7],y=a[8];return t[0]=f*r+l*o+v*h,t[1]=f*u+l*i+v*s,t[2]=f*e+l*c+v*M,t[3]=b*r+m*o+d*h,t[4]=b*u+m*i+d*s,t[5]=b*e+m*c+d*M,t[6]=x*r+p*o+y*h,t[7]=x*u+p*i+y*s,t[8]=x*e+p*c+y*M,t}function x(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t[3]=n[3]-a[3],t[4]=n[4]-a[4],t[5]=n[5]-a[5],t[6]=n[6]-a[6],t[7]=n[7]-a[7],t[8]=n[8]-a[8],t}var p=d,y=x,q=Object.freeze({create:m,fromMat4:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t},clone:function(t){var n=new a(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n},copy:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},fromValues:function(t,n,r,u,e,o,i,c,h){var s=new a(9);return s[0]=t,s[1]=n,s[2]=r,s[3]=u,s[4]=e,s[5]=o,s[6]=i,s[7]=c,s[8]=h,s},set:function(t,n,a,r,u,e,o,i,c,h){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t[4]=e,t[5]=o,t[6]=i,t[7]=c,t[8]=h,t},identity:function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},transpose:function(t,n){if(t===n){var a=n[1],r=n[2],u=n[5];t[1]=n[3],t[2]=n[6],t[3]=a,t[5]=n[7],t[6]=r,t[7]=u}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t},invert:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=n[6],h=n[7],s=n[8],M=s*o-i*h,f=-s*e+i*c,l=h*e-o*c,v=a*M+r*f+u*l;return v?(v=1/v,t[0]=M*v,t[1]=(-s*r+u*h)*v,t[2]=(i*r-u*o)*v,t[3]=f*v,t[4]=(s*a-u*c)*v,t[5]=(-i*a+u*e)*v,t[6]=l*v,t[7]=(-h*a+r*c)*v,t[8]=(o*a-r*e)*v,t):null},adjoint:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=n[6],h=n[7],s=n[8];return t[0]=o*s-i*h,t[1]=u*h-r*s,t[2]=r*i-u*o,t[3]=i*c-e*s,t[4]=a*s-u*c,t[5]=u*e-a*i,t[6]=e*h-o*c,t[7]=r*c-a*h,t[8]=a*o-r*e,t},determinant:function(t){var n=t[0],a=t[1],r=t[2],u=t[3],e=t[4],o=t[5],i=t[6],c=t[7],h=t[8];return n*(h*e-o*c)+a*(-h*u+o*i)+r*(c*u-e*i)},multiply:d,translate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=n[8],f=a[0],l=a[1];return t[0]=r,t[1]=u,t[2]=e,t[3]=o,t[4]=i,t[5]=c,t[6]=f*r+l*o+h,t[7]=f*u+l*i+s,t[8]=f*e+l*c+M,t},rotate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=n[8],f=Math.sin(a),l=Math.cos(a);return t[0]=l*r+f*o,t[1]=l*u+f*i,t[2]=l*e+f*c,t[3]=l*o-f*r,t[4]=l*i-f*u,t[5]=l*c-f*e,t[6]=h,t[7]=s,t[8]=M,t},scale:function(t,n,a){var r=a[0],u=a[1];return t[0]=r*n[0],t[1]=r*n[1],t[2]=r*n[2],t[3]=u*n[3],t[4]=u*n[4],t[5]=u*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},fromTranslation:function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t},fromRotation:function(t,n){var a=Math.sin(n),r=Math.cos(n);return t[0]=r,t[1]=a,t[2]=0,t[3]=-a,t[4]=r,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},fromScaling:function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},fromMat2d:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t},fromQuat:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=a+a,i=r+r,c=u+u,h=a*o,s=r*o,M=r*i,f=u*o,l=u*i,v=u*c,b=e*o,m=e*i,d=e*c;return t[0]=1-M-v,t[3]=s-d,t[6]=f+m,t[1]=s+d,t[4]=1-h-v,t[7]=l-b,t[2]=f-m,t[5]=l+b,t[8]=1-h-M,t},normalFromMat4:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=n[6],h=n[7],s=n[8],M=n[9],f=n[10],l=n[11],v=n[12],b=n[13],m=n[14],d=n[15],x=a*i-r*o,p=a*c-u*o,y=a*h-e*o,q=r*c-u*i,g=r*h-e*i,A=u*h-e*c,w=s*b-M*v,R=s*m-f*v,z=s*d-l*v,P=M*m-f*b,j=M*d-l*b,I=f*d-l*m,S=x*I-p*j+y*P+q*z-g*R+A*w;return S?(S=1/S,t[0]=(i*I-c*j+h*P)*S,t[1]=(c*z-o*I-h*R)*S,t[2]=(o*j-i*z+h*w)*S,t[3]=(u*j-r*I-e*P)*S,t[4]=(a*I-u*z+e*R)*S,t[5]=(r*z-a*j-e*w)*S,t[6]=(b*A-m*g+d*q)*S,t[7]=(m*y-v*A-d*p)*S,t[8]=(v*g-b*y+d*x)*S,t):null},projection:function(t,n,a){return t[0]=2/n,t[1]=0,t[2]=0,t[3]=0,t[4]=-2/a,t[5]=0,t[6]=-1,t[7]=1,t[8]=1,t},str:function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},frob:function(t){return Math.hypot(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8])},add:function(t,n,a){return t[0]=n[0]+a[0],t[1]=n[1]+a[1],t[2]=n[2]+a[2],t[3]=n[3]+a[3],t[4]=n[4]+a[4],t[5]=n[5]+a[5],t[6]=n[6]+a[6],t[7]=n[7]+a[7],t[8]=n[8]+a[8],t},subtract:x,multiplyScalar:function(t,n,a){return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*a,t[5]=n[5]*a,t[6]=n[6]*a,t[7]=n[7]*a,t[8]=n[8]*a,t},multiplyScalarAndAdd:function(t,n,a,r){return t[0]=n[0]+a[0]*r,t[1]=n[1]+a[1]*r,t[2]=n[2]+a[2]*r,t[3]=n[3]+a[3]*r,t[4]=n[4]+a[4]*r,t[5]=n[5]+a[5]*r,t[6]=n[6]+a[6]*r,t[7]=n[7]+a[7]*r,t[8]=n[8]+a[8]*r,t},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=t[4],c=t[5],h=t[6],s=t[7],M=t[8],f=a[0],l=a[1],v=a[2],b=a[3],m=a[4],d=a[5],x=a[6],p=a[7],y=a[8];return Math.abs(r-f)<=n*Math.max(1,Math.abs(r),Math.abs(f))&&Math.abs(u-l)<=n*Math.max(1,Math.abs(u),Math.abs(l))&&Math.abs(e-v)<=n*Math.max(1,Math.abs(e),Math.abs(v))&&Math.abs(o-b)<=n*Math.max(1,Math.abs(o),Math.abs(b))&&Math.abs(i-m)<=n*Math.max(1,Math.abs(i),Math.abs(m))&&Math.abs(c-d)<=n*Math.max(1,Math.abs(c),Math.abs(d))&&Math.abs(h-x)<=n*Math.max(1,Math.abs(h),Math.abs(x))&&Math.abs(s-p)<=n*Math.max(1,Math.abs(s),Math.abs(p))&&Math.abs(M-y)<=n*Math.max(1,Math.abs(M),Math.abs(y))},mul:p,sub:y});function g(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function A(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=n[8],f=n[9],l=n[10],v=n[11],b=n[12],m=n[13],d=n[14],x=n[15],p=a[0],y=a[1],q=a[2],g=a[3];return t[0]=p*r+y*i+q*M+g*b,t[1]=p*u+y*c+q*f+g*m,t[2]=p*e+y*h+q*l+g*d,t[3]=p*o+y*s+q*v+g*x,p=a[4],y=a[5],q=a[6],g=a[7],t[4]=p*r+y*i+q*M+g*b,t[5]=p*u+y*c+q*f+g*m,t[6]=p*e+y*h+q*l+g*d,t[7]=p*o+y*s+q*v+g*x,p=a[8],y=a[9],q=a[10],g=a[11],t[8]=p*r+y*i+q*M+g*b,t[9]=p*u+y*c+q*f+g*m,t[10]=p*e+y*h+q*l+g*d,t[11]=p*o+y*s+q*v+g*x,p=a[12],y=a[13],q=a[14],g=a[15],t[12]=p*r+y*i+q*M+g*b,t[13]=p*u+y*c+q*f+g*m,t[14]=p*e+y*h+q*l+g*d,t[15]=p*o+y*s+q*v+g*x,t}function w(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=r+r,c=u+u,h=e+e,s=r*i,M=r*c,f=r*h,l=u*c,v=u*h,b=e*h,m=o*i,d=o*c,x=o*h;return t[0]=1-(l+b),t[1]=M+x,t[2]=f-d,t[3]=0,t[4]=M-x,t[5]=1-(s+b),t[6]=v+m,t[7]=0,t[8]=f+d,t[9]=v-m,t[10]=1-(s+l),t[11]=0,t[12]=a[0],t[13]=a[1],t[14]=a[2],t[15]=1,t}function R(t,n){return t[0]=n[12],t[1]=n[13],t[2]=n[14],t}function z(t,n){var a=n[0],r=n[1],u=n[2],e=n[4],o=n[5],i=n[6],c=n[8],h=n[9],s=n[10];return t[0]=Math.hypot(a,r,u),t[1]=Math.hypot(e,o,i),t[2]=Math.hypot(c,h,s),t}function P(t,n){var r=new a(3);z(r,n);var u=1/r[0],e=1/r[1],o=1/r[2],i=n[0]*u,c=n[1]*e,h=n[2]*o,s=n[4]*u,M=n[5]*e,f=n[6]*o,l=n[8]*u,v=n[9]*e,b=n[10]*o,m=i+M+b,d=0;return m>0?(d=2*Math.sqrt(m+1),t[3]=.25*d,t[0]=(f-v)/d,t[1]=(l-h)/d,t[2]=(c-s)/d):i>M&&i>b?(d=2*Math.sqrt(1+i-M-b),t[3]=(f-v)/d,t[0]=.25*d,t[1]=(c+s)/d,t[2]=(l+h)/d):M>b?(d=2*Math.sqrt(1+M-i-b),t[3]=(l-h)/d,t[0]=(c+s)/d,t[1]=.25*d,t[2]=(f+v)/d):(d=2*Math.sqrt(1+b-i-M),t[3]=(c-s)/d,t[0]=(l+h)/d,t[1]=(f+v)/d,t[2]=.25*d),t}function j(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t[3]=n[3]-a[3],t[4]=n[4]-a[4],t[5]=n[5]-a[5],t[6]=n[6]-a[6],t[7]=n[7]-a[7],t[8]=n[8]-a[8],t[9]=n[9]-a[9],t[10]=n[10]-a[10],t[11]=n[11]-a[11],t[12]=n[12]-a[12],t[13]=n[13]-a[13],t[14]=n[14]-a[14],t[15]=n[15]-a[15],t}var I=A,S=j,E=Object.freeze({create:function(){var t=new a(16);return a!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0),t[0]=1,t[5]=1,t[10]=1,t[15]=1,t},clone:function(t){var n=new a(16);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n[9]=t[9],n[10]=t[10],n[11]=t[11],n[12]=t[12],n[13]=t[13],n[14]=t[14],n[15]=t[15],n},copy:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},fromValues:function(t,n,r,u,e,o,i,c,h,s,M,f,l,v,b,m){var d=new a(16);return d[0]=t,d[1]=n,d[2]=r,d[3]=u,d[4]=e,d[5]=o,d[6]=i,d[7]=c,d[8]=h,d[9]=s,d[10]=M,d[11]=f,d[12]=l,d[13]=v,d[14]=b,d[15]=m,d},set:function(t,n,a,r,u,e,o,i,c,h,s,M,f,l,v,b,m){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t[4]=e,t[5]=o,t[6]=i,t[7]=c,t[8]=h,t[9]=s,t[10]=M,t[11]=f,t[12]=l,t[13]=v,t[14]=b,t[15]=m,t},identity:g,transpose:function(t,n){if(t===n){var a=n[1],r=n[2],u=n[3],e=n[6],o=n[7],i=n[11];t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=a,t[6]=n[9],t[7]=n[13],t[8]=r,t[9]=e,t[11]=n[14],t[12]=u,t[13]=o,t[14]=i}else t[0]=n[0],t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=n[1],t[5]=n[5],t[6]=n[9],t[7]=n[13],t[8]=n[2],t[9]=n[6],t[10]=n[10],t[11]=n[14],t[12]=n[3],t[13]=n[7],t[14]=n[11],t[15]=n[15];return t},invert:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=n[6],h=n[7],s=n[8],M=n[9],f=n[10],l=n[11],v=n[12],b=n[13],m=n[14],d=n[15],x=a*i-r*o,p=a*c-u*o,y=a*h-e*o,q=r*c-u*i,g=r*h-e*i,A=u*h-e*c,w=s*b-M*v,R=s*m-f*v,z=s*d-l*v,P=M*m-f*b,j=M*d-l*b,I=f*d-l*m,S=x*I-p*j+y*P+q*z-g*R+A*w;return S?(S=1/S,t[0]=(i*I-c*j+h*P)*S,t[1]=(u*j-r*I-e*P)*S,t[2]=(b*A-m*g+d*q)*S,t[3]=(f*g-M*A-l*q)*S,t[4]=(c*z-o*I-h*R)*S,t[5]=(a*I-u*z+e*R)*S,t[6]=(m*y-v*A-d*p)*S,t[7]=(s*A-f*y+l*p)*S,t[8]=(o*j-i*z+h*w)*S,t[9]=(r*z-a*j-e*w)*S,t[10]=(v*g-b*y+d*x)*S,t[11]=(M*y-s*g-l*x)*S,t[12]=(i*R-o*P-c*w)*S,t[13]=(a*P-r*R+u*w)*S,t[14]=(b*p-v*q-m*x)*S,t[15]=(s*q-M*p+f*x)*S,t):null},adjoint:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=n[6],h=n[7],s=n[8],M=n[9],f=n[10],l=n[11],v=n[12],b=n[13],m=n[14],d=n[15];return t[0]=i*(f*d-l*m)-M*(c*d-h*m)+b*(c*l-h*f),t[1]=-(r*(f*d-l*m)-M*(u*d-e*m)+b*(u*l-e*f)),t[2]=r*(c*d-h*m)-i*(u*d-e*m)+b*(u*h-e*c),t[3]=-(r*(c*l-h*f)-i*(u*l-e*f)+M*(u*h-e*c)),t[4]=-(o*(f*d-l*m)-s*(c*d-h*m)+v*(c*l-h*f)),t[5]=a*(f*d-l*m)-s*(u*d-e*m)+v*(u*l-e*f),t[6]=-(a*(c*d-h*m)-o*(u*d-e*m)+v*(u*h-e*c)),t[7]=a*(c*l-h*f)-o*(u*l-e*f)+s*(u*h-e*c),t[8]=o*(M*d-l*b)-s*(i*d-h*b)+v*(i*l-h*M),t[9]=-(a*(M*d-l*b)-s*(r*d-e*b)+v*(r*l-e*M)),t[10]=a*(i*d-h*b)-o*(r*d-e*b)+v*(r*h-e*i),t[11]=-(a*(i*l-h*M)-o*(r*l-e*M)+s*(r*h-e*i)),t[12]=-(o*(M*m-f*b)-s*(i*m-c*b)+v*(i*f-c*M)),t[13]=a*(M*m-f*b)-s*(r*m-u*b)+v*(r*f-u*M),t[14]=-(a*(i*m-c*b)-o*(r*m-u*b)+v*(r*c-u*i)),t[15]=a*(i*f-c*M)-o*(r*f-u*M)+s*(r*c-u*i),t},determinant:function(t){var n=t[0],a=t[1],r=t[2],u=t[3],e=t[4],o=t[5],i=t[6],c=t[7],h=t[8],s=t[9],M=t[10],f=t[11],l=t[12],v=t[13],b=t[14],m=t[15];return(n*o-a*e)*(M*m-f*b)-(n*i-r*e)*(s*m-f*v)+(n*c-u*e)*(s*b-M*v)+(a*i-r*o)*(h*m-f*l)-(a*c-u*o)*(h*b-M*l)+(r*c-u*i)*(h*v-s*l)},multiply:A,translate:function(t,n,a){var r,u,e,o,i,c,h,s,M,f,l,v,b=a[0],m=a[1],d=a[2];return n===t?(t[12]=n[0]*b+n[4]*m+n[8]*d+n[12],t[13]=n[1]*b+n[5]*m+n[9]*d+n[13],t[14]=n[2]*b+n[6]*m+n[10]*d+n[14],t[15]=n[3]*b+n[7]*m+n[11]*d+n[15]):(r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=n[8],f=n[9],l=n[10],v=n[11],t[0]=r,t[1]=u,t[2]=e,t[3]=o,t[4]=i,t[5]=c,t[6]=h,t[7]=s,t[8]=M,t[9]=f,t[10]=l,t[11]=v,t[12]=r*b+i*m+M*d+n[12],t[13]=u*b+c*m+f*d+n[13],t[14]=e*b+h*m+l*d+n[14],t[15]=o*b+s*m+v*d+n[15]),t},scale:function(t,n,a){var r=a[0],u=a[1],e=a[2];return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*u,t[5]=n[5]*u,t[6]=n[6]*u,t[7]=n[7]*u,t[8]=n[8]*e,t[9]=n[9]*e,t[10]=n[10]*e,t[11]=n[11]*e,t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},rotate:function(t,a,r,u){var e,o,i,c,h,s,M,f,l,v,b,m,d,x,p,y,q,g,A,w,R,z,P,j,I=u[0],S=u[1],E=u[2],O=Math.hypot(I,S,E);return O0?(r[0]=2*(c*i+M*u+h*o-s*e)/f,r[1]=2*(h*i+M*e+s*u-c*o)/f,r[2]=2*(s*i+M*o+c*e-h*u)/f):(r[0]=2*(c*i+M*u+h*o-s*e),r[1]=2*(h*i+M*e+s*u-c*o),r[2]=2*(s*i+M*o+c*e-h*u)),w(t,n,r),t},getTranslation:R,getScaling:z,getRotation:P,fromRotationTranslationScale:function(t,n,a,r){var u=n[0],e=n[1],o=n[2],i=n[3],c=u+u,h=e+e,s=o+o,M=u*c,f=u*h,l=u*s,v=e*h,b=e*s,m=o*s,d=i*c,x=i*h,p=i*s,y=r[0],q=r[1],g=r[2];return t[0]=(1-(v+m))*y,t[1]=(f+p)*y,t[2]=(l-x)*y,t[3]=0,t[4]=(f-p)*q,t[5]=(1-(M+m))*q,t[6]=(b+d)*q,t[7]=0,t[8]=(l+x)*g,t[9]=(b-d)*g,t[10]=(1-(M+v))*g,t[11]=0,t[12]=a[0],t[13]=a[1],t[14]=a[2],t[15]=1,t},fromRotationTranslationScaleOrigin:function(t,n,a,r,u){var e=n[0],o=n[1],i=n[2],c=n[3],h=e+e,s=o+o,M=i+i,f=e*h,l=e*s,v=e*M,b=o*s,m=o*M,d=i*M,x=c*h,p=c*s,y=c*M,q=r[0],g=r[1],A=r[2],w=u[0],R=u[1],z=u[2],P=(1-(b+d))*q,j=(l+y)*q,I=(v-p)*q,S=(l-y)*g,E=(1-(f+d))*g,O=(m+x)*g,T=(v+p)*A,D=(m-x)*A,F=(1-(f+b))*A;return t[0]=P,t[1]=j,t[2]=I,t[3]=0,t[4]=S,t[5]=E,t[6]=O,t[7]=0,t[8]=T,t[9]=D,t[10]=F,t[11]=0,t[12]=a[0]+w-(P*w+S*R+T*z),t[13]=a[1]+R-(j*w+E*R+D*z),t[14]=a[2]+z-(I*w+O*R+F*z),t[15]=1,t},fromQuat:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=a+a,i=r+r,c=u+u,h=a*o,s=r*o,M=r*i,f=u*o,l=u*i,v=u*c,b=e*o,m=e*i,d=e*c;return t[0]=1-M-v,t[1]=s+d,t[2]=f-m,t[3]=0,t[4]=s-d,t[5]=1-h-v,t[6]=l+b,t[7]=0,t[8]=f+m,t[9]=l-b,t[10]=1-h-M,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},frustum:function(t,n,a,r,u,e,o){var i=1/(a-n),c=1/(u-r),h=1/(e-o);return t[0]=2*e*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*e*c,t[6]=0,t[7]=0,t[8]=(a+n)*i,t[9]=(u+r)*c,t[10]=(o+e)*h,t[11]=-1,t[12]=0,t[13]=0,t[14]=o*e*2*h,t[15]=0,t},perspective:function(t,n,a,r,u){var e,o=1/Math.tan(n/2);return t[0]=o/a,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=o,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=-1,t[12]=0,t[13]=0,t[15]=0,null!=u&&u!==1/0?(e=1/(r-u),t[10]=(u+r)*e,t[14]=2*u*r*e):(t[10]=-1,t[14]=-2*r),t},perspectiveFromFieldOfView:function(t,n,a,r){var u=Math.tan(n.upDegrees*Math.PI/180),e=Math.tan(n.downDegrees*Math.PI/180),o=Math.tan(n.leftDegrees*Math.PI/180),i=Math.tan(n.rightDegrees*Math.PI/180),c=2/(o+i),h=2/(u+e);return t[0]=c,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=h,t[6]=0,t[7]=0,t[8]=-(o-i)*c*.5,t[9]=(u-e)*h*.5,t[10]=r/(a-r),t[11]=-1,t[12]=0,t[13]=0,t[14]=r*a/(a-r),t[15]=0,t},ortho:function(t,n,a,r,u,e,o){var i=1/(n-a),c=1/(r-u),h=1/(e-o);return t[0]=-2*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*c,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*h,t[11]=0,t[12]=(n+a)*i,t[13]=(u+r)*c,t[14]=(o+e)*h,t[15]=1,t},lookAt:function(t,a,r,u){var e,o,i,c,h,s,M,f,l,v,b=a[0],m=a[1],d=a[2],x=u[0],p=u[1],y=u[2],q=r[0],A=r[1],w=r[2];return Math.abs(b-q)0&&(s*=l=1/Math.sqrt(l),M*=l,f*=l);var v=c*f-h*M,b=h*s-i*f,m=i*M-c*s;return(l=v*v+b*b+m*m)>0&&(v*=l=1/Math.sqrt(l),b*=l,m*=l),t[0]=v,t[1]=b,t[2]=m,t[3]=0,t[4]=M*m-f*b,t[5]=f*v-s*m,t[6]=s*b-M*v,t[7]=0,t[8]=s,t[9]=M,t[10]=f,t[11]=0,t[12]=u,t[13]=e,t[14]=o,t[15]=1,t},str:function(t){return"mat4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+", "+t[9]+", "+t[10]+", "+t[11]+", "+t[12]+", "+t[13]+", "+t[14]+", "+t[15]+")"},frob:function(t){return Math.hypot(t[0],t[1],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15])},add:function(t,n,a){return t[0]=n[0]+a[0],t[1]=n[1]+a[1],t[2]=n[2]+a[2],t[3]=n[3]+a[3],t[4]=n[4]+a[4],t[5]=n[5]+a[5],t[6]=n[6]+a[6],t[7]=n[7]+a[7],t[8]=n[8]+a[8],t[9]=n[9]+a[9],t[10]=n[10]+a[10],t[11]=n[11]+a[11],t[12]=n[12]+a[12],t[13]=n[13]+a[13],t[14]=n[14]+a[14],t[15]=n[15]+a[15],t},subtract:j,multiplyScalar:function(t,n,a){return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*a,t[5]=n[5]*a,t[6]=n[6]*a,t[7]=n[7]*a,t[8]=n[8]*a,t[9]=n[9]*a,t[10]=n[10]*a,t[11]=n[11]*a,t[12]=n[12]*a,t[13]=n[13]*a,t[14]=n[14]*a,t[15]=n[15]*a,t},multiplyScalarAndAdd:function(t,n,a,r){return t[0]=n[0]+a[0]*r,t[1]=n[1]+a[1]*r,t[2]=n[2]+a[2]*r,t[3]=n[3]+a[3]*r,t[4]=n[4]+a[4]*r,t[5]=n[5]+a[5]*r,t[6]=n[6]+a[6]*r,t[7]=n[7]+a[7]*r,t[8]=n[8]+a[8]*r,t[9]=n[9]+a[9]*r,t[10]=n[10]+a[10]*r,t[11]=n[11]+a[11]*r,t[12]=n[12]+a[12]*r,t[13]=n[13]+a[13]*r,t[14]=n[14]+a[14]*r,t[15]=n[15]+a[15]*r,t},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]&&t[9]===n[9]&&t[10]===n[10]&&t[11]===n[11]&&t[12]===n[12]&&t[13]===n[13]&&t[14]===n[14]&&t[15]===n[15]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=t[4],c=t[5],h=t[6],s=t[7],M=t[8],f=t[9],l=t[10],v=t[11],b=t[12],m=t[13],d=t[14],x=t[15],p=a[0],y=a[1],q=a[2],g=a[3],A=a[4],w=a[5],R=a[6],z=a[7],P=a[8],j=a[9],I=a[10],S=a[11],E=a[12],O=a[13],T=a[14],D=a[15];return Math.abs(r-p)<=n*Math.max(1,Math.abs(r),Math.abs(p))&&Math.abs(u-y)<=n*Math.max(1,Math.abs(u),Math.abs(y))&&Math.abs(e-q)<=n*Math.max(1,Math.abs(e),Math.abs(q))&&Math.abs(o-g)<=n*Math.max(1,Math.abs(o),Math.abs(g))&&Math.abs(i-A)<=n*Math.max(1,Math.abs(i),Math.abs(A))&&Math.abs(c-w)<=n*Math.max(1,Math.abs(c),Math.abs(w))&&Math.abs(h-R)<=n*Math.max(1,Math.abs(h),Math.abs(R))&&Math.abs(s-z)<=n*Math.max(1,Math.abs(s),Math.abs(z))&&Math.abs(M-P)<=n*Math.max(1,Math.abs(M),Math.abs(P))&&Math.abs(f-j)<=n*Math.max(1,Math.abs(f),Math.abs(j))&&Math.abs(l-I)<=n*Math.max(1,Math.abs(l),Math.abs(I))&&Math.abs(v-S)<=n*Math.max(1,Math.abs(v),Math.abs(S))&&Math.abs(b-E)<=n*Math.max(1,Math.abs(b),Math.abs(E))&&Math.abs(m-O)<=n*Math.max(1,Math.abs(m),Math.abs(O))&&Math.abs(d-T)<=n*Math.max(1,Math.abs(d),Math.abs(T))&&Math.abs(x-D)<=n*Math.max(1,Math.abs(x),Math.abs(D))},mul:I,sub:S});function O(){var t=new a(3);return a!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t}function T(t){var n=t[0],a=t[1],r=t[2];return Math.hypot(n,a,r)}function D(t,n,r){var u=new a(3);return u[0]=t,u[1]=n,u[2]=r,u}function F(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t}function L(t,n,a){return t[0]=n[0]*a[0],t[1]=n[1]*a[1],t[2]=n[2]*a[2],t}function V(t,n,a){return t[0]=n[0]/a[0],t[1]=n[1]/a[1],t[2]=n[2]/a[2],t}function Q(t,n){var a=n[0]-t[0],r=n[1]-t[1],u=n[2]-t[2];return Math.hypot(a,r,u)}function Y(t,n){var a=n[0]-t[0],r=n[1]-t[1],u=n[2]-t[2];return a*a+r*r+u*u}function X(t){var n=t[0],a=t[1],r=t[2];return n*n+a*a+r*r}function Z(t,n){var a=n[0],r=n[1],u=n[2],e=a*a+r*r+u*u;return e>0&&(e=1/Math.sqrt(e)),t[0]=n[0]*e,t[1]=n[1]*e,t[2]=n[2]*e,t}function _(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function B(t,n,a){var r=n[0],u=n[1],e=n[2],o=a[0],i=a[1],c=a[2];return t[0]=u*c-e*i,t[1]=e*o-r*c,t[2]=r*i-u*o,t}var N,k=F,U=L,W=V,C=Q,G=Y,H=T,J=X,K=(N=O(),function(t,n,a,r,u,e){var o,i;for(n||(n=3),a||(a=0),i=r?Math.min(r*n+a,t.length):t.length,o=a;o1?0:u<-1?Math.PI:Math.acos(u)},zero:function(t){return t[0]=0,t[1]=0,t[2]=0,t},str:function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=a[0],i=a[1],c=a[2];return Math.abs(r-o)<=n*Math.max(1,Math.abs(r),Math.abs(o))&&Math.abs(u-i)<=n*Math.max(1,Math.abs(u),Math.abs(i))&&Math.abs(e-c)<=n*Math.max(1,Math.abs(e),Math.abs(c))},sub:k,mul:U,div:W,dist:C,sqrDist:G,len:H,sqrLen:J,forEach:K});function tt(){var t=new a(4);return a!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[3]=0),t}function nt(t){var n=new a(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n}function at(t,n,r,u){var e=new a(4);return e[0]=t,e[1]=n,e[2]=r,e[3]=u,e}function rt(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t}function ut(t,n,a,r,u){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t}function et(t,n,a){return t[0]=n[0]+a[0],t[1]=n[1]+a[1],t[2]=n[2]+a[2],t[3]=n[3]+a[3],t}function ot(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t[3]=n[3]-a[3],t}function it(t,n,a){return t[0]=n[0]*a[0],t[1]=n[1]*a[1],t[2]=n[2]*a[2],t[3]=n[3]*a[3],t}function ct(t,n,a){return t[0]=n[0]/a[0],t[1]=n[1]/a[1],t[2]=n[2]/a[2],t[3]=n[3]/a[3],t}function ht(t,n,a){return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t}function st(t,n){var a=n[0]-t[0],r=n[1]-t[1],u=n[2]-t[2],e=n[3]-t[3];return Math.hypot(a,r,u,e)}function Mt(t,n){var a=n[0]-t[0],r=n[1]-t[1],u=n[2]-t[2],e=n[3]-t[3];return a*a+r*r+u*u+e*e}function ft(t){var n=t[0],a=t[1],r=t[2],u=t[3];return Math.hypot(n,a,r,u)}function lt(t){var n=t[0],a=t[1],r=t[2],u=t[3];return n*n+a*a+r*r+u*u}function vt(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=a*a+r*r+u*u+e*e;return o>0&&(o=1/Math.sqrt(o)),t[0]=a*o,t[1]=r*o,t[2]=u*o,t[3]=e*o,t}function bt(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]}function mt(t,n,a,r){var u=n[0],e=n[1],o=n[2],i=n[3];return t[0]=u+r*(a[0]-u),t[1]=e+r*(a[1]-e),t[2]=o+r*(a[2]-o),t[3]=i+r*(a[3]-i),t}function dt(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]}function xt(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=a[0],c=a[1],h=a[2],s=a[3];return Math.abs(r-i)<=n*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(u-c)<=n*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(e-h)<=n*Math.max(1,Math.abs(e),Math.abs(h))&&Math.abs(o-s)<=n*Math.max(1,Math.abs(o),Math.abs(s))}var pt=ot,yt=it,qt=ct,gt=st,At=Mt,wt=ft,Rt=lt,zt=function(){var t=tt();return function(n,a,r,u,e,o){var i,c;for(a||(a=4),r||(r=0),c=u?Math.min(u*a+r,n.length):n.length,i=r;i=1);do{c=(e=2*r()-1)*e+(o=2*r()-1)*o}while(c>=1);var h=Math.sqrt((1-i)/c);return t[0]=n*a,t[1]=n*u,t[2]=n*e*h,t[3]=n*o*h,t},transformMat4:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3];return t[0]=a[0]*r+a[4]*u+a[8]*e+a[12]*o,t[1]=a[1]*r+a[5]*u+a[9]*e+a[13]*o,t[2]=a[2]*r+a[6]*u+a[10]*e+a[14]*o,t[3]=a[3]*r+a[7]*u+a[11]*e+a[15]*o,t},transformQuat:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=a[0],i=a[1],c=a[2],h=a[3],s=h*r+i*e-c*u,M=h*u+c*r-o*e,f=h*e+o*u-i*r,l=-o*r-i*u-c*e;return t[0]=s*h+l*-o+M*-c-f*-i,t[1]=M*h+l*-i+f*-o-s*-c,t[2]=f*h+l*-c+s*-i-M*-o,t[3]=n[3],t},zero:function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},str:function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},exactEquals:dt,equals:xt,sub:pt,mul:yt,div:qt,dist:gt,sqrDist:At,len:wt,sqrLen:Rt,forEach:zt});function jt(){var t=new a(4);return a!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t[3]=1,t}function It(t,n,a){a*=.5;var r=Math.sin(a);return t[0]=r*n[0],t[1]=r*n[1],t[2]=r*n[2],t[3]=Math.cos(a),t}function St(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=a[0],c=a[1],h=a[2],s=a[3];return t[0]=r*s+o*i+u*h-e*c,t[1]=u*s+o*c+e*i-r*h,t[2]=e*s+o*h+r*c-u*i,t[3]=o*s-r*i-u*c-e*h,t}function Et(t,n,a){a*=.5;var r=n[0],u=n[1],e=n[2],o=n[3],i=Math.sin(a),c=Math.cos(a);return t[0]=r*c+o*i,t[1]=u*c+e*i,t[2]=e*c-u*i,t[3]=o*c-r*i,t}function Ot(t,n,a){a*=.5;var r=n[0],u=n[1],e=n[2],o=n[3],i=Math.sin(a),c=Math.cos(a);return t[0]=r*c-e*i,t[1]=u*c+o*i,t[2]=e*c+r*i,t[3]=o*c-u*i,t}function Tt(t,n,a){a*=.5;var r=n[0],u=n[1],e=n[2],o=n[3],i=Math.sin(a),c=Math.cos(a);return t[0]=r*c+u*i,t[1]=u*c-r*i,t[2]=e*c+o*i,t[3]=o*c-e*i,t}function Dt(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=Math.sqrt(a*a+r*r+u*u),i=Math.exp(e),c=o>0?i*Math.sin(o)/o:0;return t[0]=a*c,t[1]=r*c,t[2]=u*c,t[3]=i*Math.cos(o),t}function Ft(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=Math.sqrt(a*a+r*r+u*u),i=o>0?Math.atan2(o,e)/o:0;return t[0]=a*i,t[1]=r*i,t[2]=u*i,t[3]=.5*Math.log(a*a+r*r+u*u+e*e),t}function Lt(t,a,r,u){var e,o,i,c,h,s=a[0],M=a[1],f=a[2],l=a[3],v=r[0],b=r[1],m=r[2],d=r[3];return(o=s*v+M*b+f*m+l*d)<0&&(o=-o,v=-v,b=-b,m=-m,d=-d),1-o>n?(e=Math.acos(o),i=Math.sin(e),c=Math.sin((1-u)*e)/i,h=Math.sin(u*e)/i):(c=1-u,h=u),t[0]=c*s+h*v,t[1]=c*M+h*b,t[2]=c*f+h*m,t[3]=c*l+h*d,t}function Vt(t,n){var a,r=n[0]+n[4]+n[8];if(r>0)a=Math.sqrt(r+1),t[3]=.5*a,a=.5/a,t[0]=(n[5]-n[7])*a,t[1]=(n[6]-n[2])*a,t[2]=(n[1]-n[3])*a;else{var u=0;n[4]>n[0]&&(u=1),n[8]>n[3*u+u]&&(u=2);var e=(u+1)%3,o=(u+2)%3;a=Math.sqrt(n[3*u+u]-n[3*e+e]-n[3*o+o]+1),t[u]=.5*a,a=.5/a,t[3]=(n[3*e+o]-n[3*o+e])*a,t[e]=(n[3*e+u]+n[3*u+e])*a,t[o]=(n[3*o+u]+n[3*u+o])*a}return t}var Qt,Yt,Xt,Zt,_t,Bt,Nt=nt,kt=at,Ut=rt,Wt=ut,Ct=et,Gt=St,Ht=ht,Jt=bt,Kt=mt,$t=ft,tn=$t,nn=lt,an=nn,rn=vt,un=dt,en=xt,on=(Qt=O(),Yt=D(1,0,0),Xt=D(0,1,0),function(t,n,a){var r=_(n,a);return r<-.999999?(B(Qt,Yt,n),H(Qt)<1e-6&&B(Qt,Xt,n),Z(Qt,Qt),It(t,Qt,Math.PI),t):r>.999999?(t[0]=0,t[1]=0,t[2]=0,t[3]=1,t):(B(Qt,n,a),t[0]=Qt[0],t[1]=Qt[1],t[2]=Qt[2],t[3]=1+r,rn(t,t))}),cn=(Zt=jt(),_t=jt(),function(t,n,a,r,u,e){return Lt(Zt,n,u,e),Lt(_t,a,r,e),Lt(t,Zt,_t,2*e*(1-e)),t}),hn=(Bt=m(),function(t,n,a,r){return Bt[0]=a[0],Bt[3]=a[1],Bt[6]=a[2],Bt[1]=r[0],Bt[4]=r[1],Bt[7]=r[2],Bt[2]=-n[0],Bt[5]=-n[1],Bt[8]=-n[2],rn(t,Vt(t,Bt))}),sn=Object.freeze({create:jt,identity:function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},setAxisAngle:It,getAxisAngle:function(t,a){var r=2*Math.acos(a[3]),u=Math.sin(r/2);return u>n?(t[0]=a[0]/u,t[1]=a[1]/u,t[2]=a[2]/u):(t[0]=1,t[1]=0,t[2]=0),r},getAngle:function(t,n){var a=Jt(t,n);return Math.acos(2*a*a-1)},multiply:St,rotateX:Et,rotateY:Ot,rotateZ:Tt,calculateW:function(t,n){var a=n[0],r=n[1],u=n[2];return t[0]=a,t[1]=r,t[2]=u,t[3]=Math.sqrt(Math.abs(1-a*a-r*r-u*u)),t},exp:Dt,ln:Ft,pow:function(t,n,a){return Ft(t,n),Ht(t,t,a),Dt(t,t),t},slerp:Lt,random:function(t){var n=r(),a=r(),u=r(),e=Math.sqrt(1-n),o=Math.sqrt(n);return t[0]=e*Math.sin(2*Math.PI*a),t[1]=e*Math.cos(2*Math.PI*a),t[2]=o*Math.sin(2*Math.PI*u),t[3]=o*Math.cos(2*Math.PI*u),t},invert:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=a*a+r*r+u*u+e*e,i=o?1/o:0;return t[0]=-a*i,t[1]=-r*i,t[2]=-u*i,t[3]=e*i,t},conjugate:function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t},fromMat3:Vt,fromEuler:function(t,n,a,r){var u=.5*Math.PI/180;n*=u,a*=u,r*=u;var e=Math.sin(n),o=Math.cos(n),i=Math.sin(a),c=Math.cos(a),h=Math.sin(r),s=Math.cos(r);return t[0]=e*c*s-o*i*h,t[1]=o*i*s+e*c*h,t[2]=o*c*h-e*i*s,t[3]=o*c*s+e*i*h,t},str:function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},clone:Nt,fromValues:kt,copy:Ut,set:Wt,add:Ct,mul:Gt,scale:Ht,dot:Jt,lerp:Kt,length:$t,len:tn,squaredLength:nn,sqrLen:an,normalize:rn,exactEquals:un,equals:en,rotationTo:on,sqlerp:cn,setAxes:hn});function Mn(t,n,a){var r=.5*a[0],u=.5*a[1],e=.5*a[2],o=n[0],i=n[1],c=n[2],h=n[3];return t[0]=o,t[1]=i,t[2]=c,t[3]=h,t[4]=r*h+u*c-e*i,t[5]=u*h+e*o-r*c,t[6]=e*h+r*i-u*o,t[7]=-r*o-u*i-e*c,t}function fn(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t}var ln=Ut;var vn=Ut;function bn(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=a[4],c=a[5],h=a[6],s=a[7],M=n[4],f=n[5],l=n[6],v=n[7],b=a[0],m=a[1],d=a[2],x=a[3];return t[0]=r*x+o*b+u*d-e*m,t[1]=u*x+o*m+e*b-r*d,t[2]=e*x+o*d+r*m-u*b,t[3]=o*x-r*b-u*m-e*d,t[4]=r*s+o*i+u*h-e*c+M*x+v*b+f*d-l*m,t[5]=u*s+o*c+e*i-r*h+f*x+v*m+l*b-M*d,t[6]=e*s+o*h+r*c-u*i+l*x+v*d+M*m-f*b,t[7]=o*s-r*i-u*c-e*h+v*x-M*b-f*m-l*d,t}var mn=bn;var dn=Jt;var xn=$t,pn=xn,yn=nn,qn=yn;var gn=Object.freeze({create:function(){var t=new a(8);return a!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[4]=0,t[5]=0,t[6]=0,t[7]=0),t[3]=1,t},clone:function(t){var n=new a(8);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n},fromValues:function(t,n,r,u,e,o,i,c){var h=new a(8);return h[0]=t,h[1]=n,h[2]=r,h[3]=u,h[4]=e,h[5]=o,h[6]=i,h[7]=c,h},fromRotationTranslationValues:function(t,n,r,u,e,o,i){var c=new a(8);c[0]=t,c[1]=n,c[2]=r,c[3]=u;var h=.5*e,s=.5*o,M=.5*i;return c[4]=h*u+s*r-M*n,c[5]=s*u+M*t-h*r,c[6]=M*u+h*n-s*t,c[7]=-h*t-s*n-M*r,c},fromRotationTranslation:Mn,fromTranslation:function(t,n){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t[4]=.5*n[0],t[5]=.5*n[1],t[6]=.5*n[2],t[7]=0,t},fromRotation:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=0,t[5]=0,t[6]=0,t[7]=0,t},fromMat4:function(t,n){var r=jt();P(r,n);var u=new a(3);return R(u,n),Mn(t,r,u),t},copy:fn,identity:function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t[6]=0,t[7]=0,t},set:function(t,n,a,r,u,e,o,i,c){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t[4]=e,t[5]=o,t[6]=i,t[7]=c,t},getReal:ln,getDual:function(t,n){return t[0]=n[4],t[1]=n[5],t[2]=n[6],t[3]=n[7],t},setReal:vn,setDual:function(t,n){return t[4]=n[0],t[5]=n[1],t[6]=n[2],t[7]=n[3],t},getTranslation:function(t,n){var a=n[4],r=n[5],u=n[6],e=n[7],o=-n[0],i=-n[1],c=-n[2],h=n[3];return t[0]=2*(a*h+e*o+r*c-u*i),t[1]=2*(r*h+e*i+u*o-a*c),t[2]=2*(u*h+e*c+a*i-r*o),t},translate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=.5*a[0],c=.5*a[1],h=.5*a[2],s=n[4],M=n[5],f=n[6],l=n[7];return t[0]=r,t[1]=u,t[2]=e,t[3]=o,t[4]=o*i+u*h-e*c+s,t[5]=o*c+e*i-r*h+M,t[6]=o*h+r*c-u*i+f,t[7]=-r*i-u*c-e*h+l,t},rotateX:function(t,n,a){var r=-n[0],u=-n[1],e=-n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=i*o+s*r+c*e-h*u,f=c*o+s*u+h*r-i*e,l=h*o+s*e+i*u-c*r,v=s*o-i*r-c*u-h*e;return Et(t,n,a),r=t[0],u=t[1],e=t[2],o=t[3],t[4]=M*o+v*r+f*e-l*u,t[5]=f*o+v*u+l*r-M*e,t[6]=l*o+v*e+M*u-f*r,t[7]=v*o-M*r-f*u-l*e,t},rotateY:function(t,n,a){var r=-n[0],u=-n[1],e=-n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=i*o+s*r+c*e-h*u,f=c*o+s*u+h*r-i*e,l=h*o+s*e+i*u-c*r,v=s*o-i*r-c*u-h*e;return Ot(t,n,a),r=t[0],u=t[1],e=t[2],o=t[3],t[4]=M*o+v*r+f*e-l*u,t[5]=f*o+v*u+l*r-M*e,t[6]=l*o+v*e+M*u-f*r,t[7]=v*o-M*r-f*u-l*e,t},rotateZ:function(t,n,a){var r=-n[0],u=-n[1],e=-n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=i*o+s*r+c*e-h*u,f=c*o+s*u+h*r-i*e,l=h*o+s*e+i*u-c*r,v=s*o-i*r-c*u-h*e;return Tt(t,n,a),r=t[0],u=t[1],e=t[2],o=t[3],t[4]=M*o+v*r+f*e-l*u,t[5]=f*o+v*u+l*r-M*e,t[6]=l*o+v*e+M*u-f*r,t[7]=v*o-M*r-f*u-l*e,t},rotateByQuatAppend:function(t,n,a){var r=a[0],u=a[1],e=a[2],o=a[3],i=n[0],c=n[1],h=n[2],s=n[3];return t[0]=i*o+s*r+c*e-h*u,t[1]=c*o+s*u+h*r-i*e,t[2]=h*o+s*e+i*u-c*r,t[3]=s*o-i*r-c*u-h*e,i=n[4],c=n[5],h=n[6],s=n[7],t[4]=i*o+s*r+c*e-h*u,t[5]=c*o+s*u+h*r-i*e,t[6]=h*o+s*e+i*u-c*r,t[7]=s*o-i*r-c*u-h*e,t},rotateByQuatPrepend:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=a[0],c=a[1],h=a[2],s=a[3];return t[0]=r*s+o*i+u*h-e*c,t[1]=u*s+o*c+e*i-r*h,t[2]=e*s+o*h+r*c-u*i,t[3]=o*s-r*i-u*c-e*h,i=a[4],c=a[5],h=a[6],s=a[7],t[4]=r*s+o*i+u*h-e*c,t[5]=u*s+o*c+e*i-r*h,t[6]=e*s+o*h+r*c-u*i,t[7]=o*s-r*i-u*c-e*h,t},rotateAroundAxis:function(t,a,r,u){if(Math.abs(u)0){a=Math.sqrt(a);var r=n[0]/a,u=n[1]/a,e=n[2]/a,o=n[3]/a,i=n[4],c=n[5],h=n[6],s=n[7],M=r*i+u*c+e*h+o*s;t[0]=r,t[1]=u,t[2]=e,t[3]=o,t[4]=(i-r*M)/a,t[5]=(c-u*M)/a,t[6]=(h-e*M)/a,t[7]=(s-o*M)/a}return t},str:function(t){return"quat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+")"},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=t[4],c=t[5],h=t[6],s=t[7],M=a[0],f=a[1],l=a[2],v=a[3],b=a[4],m=a[5],d=a[6],x=a[7];return Math.abs(r-M)<=n*Math.max(1,Math.abs(r),Math.abs(M))&&Math.abs(u-f)<=n*Math.max(1,Math.abs(u),Math.abs(f))&&Math.abs(e-l)<=n*Math.max(1,Math.abs(e),Math.abs(l))&&Math.abs(o-v)<=n*Math.max(1,Math.abs(o),Math.abs(v))&&Math.abs(i-b)<=n*Math.max(1,Math.abs(i),Math.abs(b))&&Math.abs(c-m)<=n*Math.max(1,Math.abs(c),Math.abs(m))&&Math.abs(h-d)<=n*Math.max(1,Math.abs(h),Math.abs(d))&&Math.abs(s-x)<=n*Math.max(1,Math.abs(s),Math.abs(x))}});function An(){var t=new a(2);return a!=Float32Array&&(t[0]=0,t[1]=0),t}function wn(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t}function Rn(t,n,a){return t[0]=n[0]*a[0],t[1]=n[1]*a[1],t}function zn(t,n,a){return t[0]=n[0]/a[0],t[1]=n[1]/a[1],t}function Pn(t,n){var a=n[0]-t[0],r=n[1]-t[1];return Math.hypot(a,r)}function jn(t,n){var a=n[0]-t[0],r=n[1]-t[1];return a*a+r*r}function In(t){var n=t[0],a=t[1];return Math.hypot(n,a)}function Sn(t){var n=t[0],a=t[1];return n*n+a*a}var En=In,On=wn,Tn=Rn,Dn=zn,Fn=Pn,Ln=jn,Vn=Sn,Qn=function(){var t=An();return function(n,a,r,u,e,o){var i,c;for(a||(a=2),r||(r=0),c=u?Math.min(u*a+r,n.length):n.length,i=r;i0&&(u=1/Math.sqrt(u)),t[0]=n[0]*u,t[1]=n[1]*u,t},dot:function(t,n){return t[0]*n[0]+t[1]*n[1]},cross:function(t,n,a){var r=n[0]*a[1]-n[1]*a[0];return t[0]=t[1]=0,t[2]=r,t},lerp:function(t,n,a,r){var u=n[0],e=n[1];return t[0]=u+r*(a[0]-u),t[1]=e+r*(a[1]-e),t},random:function(t,n){n=n||1;var a=2*r()*Math.PI;return t[0]=Math.cos(a)*n,t[1]=Math.sin(a)*n,t},transformMat2:function(t,n,a){var r=n[0],u=n[1];return t[0]=a[0]*r+a[2]*u,t[1]=a[1]*r+a[3]*u,t},transformMat2d:function(t,n,a){var r=n[0],u=n[1];return t[0]=a[0]*r+a[2]*u+a[4],t[1]=a[1]*r+a[3]*u+a[5],t},transformMat3:function(t,n,a){var r=n[0],u=n[1];return t[0]=a[0]*r+a[3]*u+a[6],t[1]=a[1]*r+a[4]*u+a[7],t},transformMat4:function(t,n,a){var r=n[0],u=n[1];return t[0]=a[0]*r+a[4]*u+a[12],t[1]=a[1]*r+a[5]*u+a[13],t},rotate:function(t,n,a,r){var u=n[0]-a[0],e=n[1]-a[1],o=Math.sin(r),i=Math.cos(r);return t[0]=u*i-e*o+a[0],t[1]=u*o+e*i+a[1],t},angle:function(t,n){var a=t[0],r=t[1],u=n[0],e=n[1],o=a*a+r*r;o>0&&(o=1/Math.sqrt(o));var i=u*u+e*e;i>0&&(i=1/Math.sqrt(i));var c=(a*u+r*e)*o*i;return c>1?0:c<-1?Math.PI:Math.acos(c)},zero:function(t){return t[0]=0,t[1]=0,t},str:function(t){return"vec2("+t[0]+", "+t[1]+")"},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]},equals:function(t,a){var r=t[0],u=t[1],e=a[0],o=a[1];return Math.abs(r-e)<=n*Math.max(1,Math.abs(r),Math.abs(e))&&Math.abs(u-o)<=n*Math.max(1,Math.abs(u),Math.abs(o))},len:En,sub:On,mul:Tn,div:Dn,dist:Fn,sqrDist:Ln,sqrLen:Vn,forEach:Qn});t.glMatrix=e,t.mat2=s,t.mat2d=b,t.mat3=q,t.mat4=E,t.quat=sn,t.quat2=gn,t.vec2=Yn,t.vec3=$,t.vec4=Pt,Object.defineProperty(t,"__esModule",{value:!0})}); + +// ["glMatrix", "mat2", "mat2d", "mat3", "mat4", "quat", "quat2", "vec2", "vec3", "vec4"] +window.glMatrix = glMatrix; +window.mat2 = glMatrix.mat2; +window.mat2d = glMatrix.mat2d; +window.mat3 = glMatrix.mat3; +window.mat4 = glMatrix.mat4; +window.quat = glMatrix.quat; +window.quat2 = glMatrix.quat2; +window.vec2 = glMatrix.vec2; +window.vec3 = glMatrix.vec3; +window.vec4 = glMatrix.vec4; + + + +var CABLES = CABLES || {}; CABLES.build = {"timestamp":1671442325495,"created":"2022-12-19T09:32:05.495Z","git":{"branch":"master","commit":"9aa5acdd3f463ec28e2abce3963ff31d3eaf3926","date":null,"message":null}}; \ No newline at end of file diff --git a/survey_dashboard/hmc_layout/static/en_files/libs.core.min.js b/survey_dashboard/hmc_layout/static/en_files/libs.core.min.js new file mode 100644 index 0000000..af46eda --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/libs.core.min.js @@ -0,0 +1,44 @@ +/*! +@fileoverview gl-matrix - High performance matrix and vector operations +@author Brandon Jones +@author Colin MacKenzie IV +@version 3.1.0 + +Copyright (c) 2015-2019, Brandon Jones, Colin MacKenzie IV. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t=t||self).glMatrix={})}(this,function(t){"use strict";var n=1e-6,a="undefined"!=typeof Float32Array?Float32Array:Array,r=Math.random;var u=Math.PI/180;Math.hypot||(Math.hypot=function(){for(var t=0,n=arguments.length;n--;)t+=arguments[n]*arguments[n];return Math.sqrt(t)});var e=Object.freeze({EPSILON:n,get ARRAY_TYPE(){return a},RANDOM:r,setMatrixArrayType:function(t){a=t},toRadian:function(t){return t*u},equals:function(t,a){return Math.abs(t-a)<=n*Math.max(1,Math.abs(t),Math.abs(a))}});function o(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=a[0],c=a[1],h=a[2],s=a[3];return t[0]=r*i+e*c,t[1]=u*i+o*c,t[2]=r*h+e*s,t[3]=u*h+o*s,t}function i(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t[3]=n[3]-a[3],t}var c=o,h=i,s=Object.freeze({create:function(){var t=new a(4);return a!=Float32Array&&(t[1]=0,t[2]=0),t[0]=1,t[3]=1,t},clone:function(t){var n=new a(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},copy:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},identity:function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},fromValues:function(t,n,r,u){var e=new a(4);return e[0]=t,e[1]=n,e[2]=r,e[3]=u,e},set:function(t,n,a,r,u){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t},transpose:function(t,n){if(t===n){var a=n[1];t[1]=n[2],t[2]=a}else t[0]=n[0],t[1]=n[2],t[2]=n[1],t[3]=n[3];return t},invert:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=a*e-u*r;return o?(o=1/o,t[0]=e*o,t[1]=-r*o,t[2]=-u*o,t[3]=a*o,t):null},adjoint:function(t,n){var a=n[0];return t[0]=n[3],t[1]=-n[1],t[2]=-n[2],t[3]=a,t},determinant:function(t){return t[0]*t[3]-t[2]*t[1]},multiply:o,rotate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=Math.sin(a),c=Math.cos(a);return t[0]=r*c+e*i,t[1]=u*c+o*i,t[2]=r*-i+e*c,t[3]=u*-i+o*c,t},scale:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=a[0],c=a[1];return t[0]=r*i,t[1]=u*i,t[2]=e*c,t[3]=o*c,t},fromRotation:function(t,n){var a=Math.sin(n),r=Math.cos(n);return t[0]=r,t[1]=a,t[2]=-a,t[3]=r,t},fromScaling:function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t},str:function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},frob:function(t){return Math.hypot(t[0],t[1],t[2],t[3])},LDU:function(t,n,a,r){return t[2]=r[2]/r[0],a[0]=r[0],a[1]=r[1],a[3]=r[3]-t[2]*a[1],[t,n,a]},add:function(t,n,a){return t[0]=n[0]+a[0],t[1]=n[1]+a[1],t[2]=n[2]+a[2],t[3]=n[3]+a[3],t},subtract:i,exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=a[0],c=a[1],h=a[2],s=a[3];return Math.abs(r-i)<=n*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(u-c)<=n*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(e-h)<=n*Math.max(1,Math.abs(e),Math.abs(h))&&Math.abs(o-s)<=n*Math.max(1,Math.abs(o),Math.abs(s))},multiplyScalar:function(t,n,a){return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t},multiplyScalarAndAdd:function(t,n,a,r){return t[0]=n[0]+a[0]*r,t[1]=n[1]+a[1]*r,t[2]=n[2]+a[2]*r,t[3]=n[3]+a[3]*r,t},mul:c,sub:h});function M(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=a[0],s=a[1],M=a[2],f=a[3],l=a[4],v=a[5];return t[0]=r*h+e*s,t[1]=u*h+o*s,t[2]=r*M+e*f,t[3]=u*M+o*f,t[4]=r*l+e*v+i,t[5]=u*l+o*v+c,t}function f(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t[3]=n[3]-a[3],t[4]=n[4]-a[4],t[5]=n[5]-a[5],t}var l=M,v=f,b=Object.freeze({create:function(){var t=new a(6);return a!=Float32Array&&(t[1]=0,t[2]=0,t[4]=0,t[5]=0),t[0]=1,t[3]=1,t},clone:function(t){var n=new a(6);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n},copy:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t},identity:function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},fromValues:function(t,n,r,u,e,o){var i=new a(6);return i[0]=t,i[1]=n,i[2]=r,i[3]=u,i[4]=e,i[5]=o,i},set:function(t,n,a,r,u,e,o){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t[4]=e,t[5]=o,t},invert:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=a*e-r*u;return c?(c=1/c,t[0]=e*c,t[1]=-r*c,t[2]=-u*c,t[3]=a*c,t[4]=(u*i-e*o)*c,t[5]=(r*o-a*i)*c,t):null},determinant:function(t){return t[0]*t[3]-t[1]*t[2]},multiply:M,rotate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=Math.sin(a),s=Math.cos(a);return t[0]=r*s+e*h,t[1]=u*s+o*h,t[2]=r*-h+e*s,t[3]=u*-h+o*s,t[4]=i,t[5]=c,t},scale:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=a[0],s=a[1];return t[0]=r*h,t[1]=u*h,t[2]=e*s,t[3]=o*s,t[4]=i,t[5]=c,t},translate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=a[0],s=a[1];return t[0]=r,t[1]=u,t[2]=e,t[3]=o,t[4]=r*h+e*s+i,t[5]=u*h+o*s+c,t},fromRotation:function(t,n){var a=Math.sin(n),r=Math.cos(n);return t[0]=r,t[1]=a,t[2]=-a,t[3]=r,t[4]=0,t[5]=0,t},fromScaling:function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t[4]=0,t[5]=0,t},fromTranslation:function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=n[0],t[5]=n[1],t},str:function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},frob:function(t){return Math.hypot(t[0],t[1],t[2],t[3],t[4],t[5],1)},add:function(t,n,a){return t[0]=n[0]+a[0],t[1]=n[1]+a[1],t[2]=n[2]+a[2],t[3]=n[3]+a[3],t[4]=n[4]+a[4],t[5]=n[5]+a[5],t},subtract:f,multiplyScalar:function(t,n,a){return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*a,t[5]=n[5]*a,t},multiplyScalarAndAdd:function(t,n,a,r){return t[0]=n[0]+a[0]*r,t[1]=n[1]+a[1]*r,t[2]=n[2]+a[2]*r,t[3]=n[3]+a[3]*r,t[4]=n[4]+a[4]*r,t[5]=n[5]+a[5]*r,t},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=t[4],c=t[5],h=a[0],s=a[1],M=a[2],f=a[3],l=a[4],v=a[5];return Math.abs(r-h)<=n*Math.max(1,Math.abs(r),Math.abs(h))&&Math.abs(u-s)<=n*Math.max(1,Math.abs(u),Math.abs(s))&&Math.abs(e-M)<=n*Math.max(1,Math.abs(e),Math.abs(M))&&Math.abs(o-f)<=n*Math.max(1,Math.abs(o),Math.abs(f))&&Math.abs(i-l)<=n*Math.max(1,Math.abs(i),Math.abs(l))&&Math.abs(c-v)<=n*Math.max(1,Math.abs(c),Math.abs(v))},mul:l,sub:v});function m(){var t=new a(9);return a!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[5]=0,t[6]=0,t[7]=0),t[0]=1,t[4]=1,t[8]=1,t}function d(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=n[8],f=a[0],l=a[1],v=a[2],b=a[3],m=a[4],d=a[5],x=a[6],p=a[7],y=a[8];return t[0]=f*r+l*o+v*h,t[1]=f*u+l*i+v*s,t[2]=f*e+l*c+v*M,t[3]=b*r+m*o+d*h,t[4]=b*u+m*i+d*s,t[5]=b*e+m*c+d*M,t[6]=x*r+p*o+y*h,t[7]=x*u+p*i+y*s,t[8]=x*e+p*c+y*M,t}function x(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t[3]=n[3]-a[3],t[4]=n[4]-a[4],t[5]=n[5]-a[5],t[6]=n[6]-a[6],t[7]=n[7]-a[7],t[8]=n[8]-a[8],t}var p=d,y=x,q=Object.freeze({create:m,fromMat4:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t},clone:function(t){var n=new a(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n},copy:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},fromValues:function(t,n,r,u,e,o,i,c,h){var s=new a(9);return s[0]=t,s[1]=n,s[2]=r,s[3]=u,s[4]=e,s[5]=o,s[6]=i,s[7]=c,s[8]=h,s},set:function(t,n,a,r,u,e,o,i,c,h){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t[4]=e,t[5]=o,t[6]=i,t[7]=c,t[8]=h,t},identity:function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},transpose:function(t,n){if(t===n){var a=n[1],r=n[2],u=n[5];t[1]=n[3],t[2]=n[6],t[3]=a,t[5]=n[7],t[6]=r,t[7]=u}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t},invert:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=n[6],h=n[7],s=n[8],M=s*o-i*h,f=-s*e+i*c,l=h*e-o*c,v=a*M+r*f+u*l;return v?(v=1/v,t[0]=M*v,t[1]=(-s*r+u*h)*v,t[2]=(i*r-u*o)*v,t[3]=f*v,t[4]=(s*a-u*c)*v,t[5]=(-i*a+u*e)*v,t[6]=l*v,t[7]=(-h*a+r*c)*v,t[8]=(o*a-r*e)*v,t):null},adjoint:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=n[6],h=n[7],s=n[8];return t[0]=o*s-i*h,t[1]=u*h-r*s,t[2]=r*i-u*o,t[3]=i*c-e*s,t[4]=a*s-u*c,t[5]=u*e-a*i,t[6]=e*h-o*c,t[7]=r*c-a*h,t[8]=a*o-r*e,t},determinant:function(t){var n=t[0],a=t[1],r=t[2],u=t[3],e=t[4],o=t[5],i=t[6],c=t[7],h=t[8];return n*(h*e-o*c)+a*(-h*u+o*i)+r*(c*u-e*i)},multiply:d,translate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=n[8],f=a[0],l=a[1];return t[0]=r,t[1]=u,t[2]=e,t[3]=o,t[4]=i,t[5]=c,t[6]=f*r+l*o+h,t[7]=f*u+l*i+s,t[8]=f*e+l*c+M,t},rotate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=n[8],f=Math.sin(a),l=Math.cos(a);return t[0]=l*r+f*o,t[1]=l*u+f*i,t[2]=l*e+f*c,t[3]=l*o-f*r,t[4]=l*i-f*u,t[5]=l*c-f*e,t[6]=h,t[7]=s,t[8]=M,t},scale:function(t,n,a){var r=a[0],u=a[1];return t[0]=r*n[0],t[1]=r*n[1],t[2]=r*n[2],t[3]=u*n[3],t[4]=u*n[4],t[5]=u*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},fromTranslation:function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t},fromRotation:function(t,n){var a=Math.sin(n),r=Math.cos(n);return t[0]=r,t[1]=a,t[2]=0,t[3]=-a,t[4]=r,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},fromScaling:function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},fromMat2d:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t},fromQuat:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=a+a,i=r+r,c=u+u,h=a*o,s=r*o,M=r*i,f=u*o,l=u*i,v=u*c,b=e*o,m=e*i,d=e*c;return t[0]=1-M-v,t[3]=s-d,t[6]=f+m,t[1]=s+d,t[4]=1-h-v,t[7]=l-b,t[2]=f-m,t[5]=l+b,t[8]=1-h-M,t},normalFromMat4:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=n[6],h=n[7],s=n[8],M=n[9],f=n[10],l=n[11],v=n[12],b=n[13],m=n[14],d=n[15],x=a*i-r*o,p=a*c-u*o,y=a*h-e*o,q=r*c-u*i,g=r*h-e*i,A=u*h-e*c,w=s*b-M*v,R=s*m-f*v,z=s*d-l*v,P=M*m-f*b,j=M*d-l*b,I=f*d-l*m,S=x*I-p*j+y*P+q*z-g*R+A*w;return S?(S=1/S,t[0]=(i*I-c*j+h*P)*S,t[1]=(c*z-o*I-h*R)*S,t[2]=(o*j-i*z+h*w)*S,t[3]=(u*j-r*I-e*P)*S,t[4]=(a*I-u*z+e*R)*S,t[5]=(r*z-a*j-e*w)*S,t[6]=(b*A-m*g+d*q)*S,t[7]=(m*y-v*A-d*p)*S,t[8]=(v*g-b*y+d*x)*S,t):null},projection:function(t,n,a){return t[0]=2/n,t[1]=0,t[2]=0,t[3]=0,t[4]=-2/a,t[5]=0,t[6]=-1,t[7]=1,t[8]=1,t},str:function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},frob:function(t){return Math.hypot(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8])},add:function(t,n,a){return t[0]=n[0]+a[0],t[1]=n[1]+a[1],t[2]=n[2]+a[2],t[3]=n[3]+a[3],t[4]=n[4]+a[4],t[5]=n[5]+a[5],t[6]=n[6]+a[6],t[7]=n[7]+a[7],t[8]=n[8]+a[8],t},subtract:x,multiplyScalar:function(t,n,a){return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*a,t[5]=n[5]*a,t[6]=n[6]*a,t[7]=n[7]*a,t[8]=n[8]*a,t},multiplyScalarAndAdd:function(t,n,a,r){return t[0]=n[0]+a[0]*r,t[1]=n[1]+a[1]*r,t[2]=n[2]+a[2]*r,t[3]=n[3]+a[3]*r,t[4]=n[4]+a[4]*r,t[5]=n[5]+a[5]*r,t[6]=n[6]+a[6]*r,t[7]=n[7]+a[7]*r,t[8]=n[8]+a[8]*r,t},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=t[4],c=t[5],h=t[6],s=t[7],M=t[8],f=a[0],l=a[1],v=a[2],b=a[3],m=a[4],d=a[5],x=a[6],p=a[7],y=a[8];return Math.abs(r-f)<=n*Math.max(1,Math.abs(r),Math.abs(f))&&Math.abs(u-l)<=n*Math.max(1,Math.abs(u),Math.abs(l))&&Math.abs(e-v)<=n*Math.max(1,Math.abs(e),Math.abs(v))&&Math.abs(o-b)<=n*Math.max(1,Math.abs(o),Math.abs(b))&&Math.abs(i-m)<=n*Math.max(1,Math.abs(i),Math.abs(m))&&Math.abs(c-d)<=n*Math.max(1,Math.abs(c),Math.abs(d))&&Math.abs(h-x)<=n*Math.max(1,Math.abs(h),Math.abs(x))&&Math.abs(s-p)<=n*Math.max(1,Math.abs(s),Math.abs(p))&&Math.abs(M-y)<=n*Math.max(1,Math.abs(M),Math.abs(y))},mul:p,sub:y});function g(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function A(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=n[8],f=n[9],l=n[10],v=n[11],b=n[12],m=n[13],d=n[14],x=n[15],p=a[0],y=a[1],q=a[2],g=a[3];return t[0]=p*r+y*i+q*M+g*b,t[1]=p*u+y*c+q*f+g*m,t[2]=p*e+y*h+q*l+g*d,t[3]=p*o+y*s+q*v+g*x,p=a[4],y=a[5],q=a[6],g=a[7],t[4]=p*r+y*i+q*M+g*b,t[5]=p*u+y*c+q*f+g*m,t[6]=p*e+y*h+q*l+g*d,t[7]=p*o+y*s+q*v+g*x,p=a[8],y=a[9],q=a[10],g=a[11],t[8]=p*r+y*i+q*M+g*b,t[9]=p*u+y*c+q*f+g*m,t[10]=p*e+y*h+q*l+g*d,t[11]=p*o+y*s+q*v+g*x,p=a[12],y=a[13],q=a[14],g=a[15],t[12]=p*r+y*i+q*M+g*b,t[13]=p*u+y*c+q*f+g*m,t[14]=p*e+y*h+q*l+g*d,t[15]=p*o+y*s+q*v+g*x,t}function w(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=r+r,c=u+u,h=e+e,s=r*i,M=r*c,f=r*h,l=u*c,v=u*h,b=e*h,m=o*i,d=o*c,x=o*h;return t[0]=1-(l+b),t[1]=M+x,t[2]=f-d,t[3]=0,t[4]=M-x,t[5]=1-(s+b),t[6]=v+m,t[7]=0,t[8]=f+d,t[9]=v-m,t[10]=1-(s+l),t[11]=0,t[12]=a[0],t[13]=a[1],t[14]=a[2],t[15]=1,t}function R(t,n){return t[0]=n[12],t[1]=n[13],t[2]=n[14],t}function z(t,n){var a=n[0],r=n[1],u=n[2],e=n[4],o=n[5],i=n[6],c=n[8],h=n[9],s=n[10];return t[0]=Math.hypot(a,r,u),t[1]=Math.hypot(e,o,i),t[2]=Math.hypot(c,h,s),t}function P(t,n){var r=new a(3);z(r,n);var u=1/r[0],e=1/r[1],o=1/r[2],i=n[0]*u,c=n[1]*e,h=n[2]*o,s=n[4]*u,M=n[5]*e,f=n[6]*o,l=n[8]*u,v=n[9]*e,b=n[10]*o,m=i+M+b,d=0;return m>0?(d=2*Math.sqrt(m+1),t[3]=.25*d,t[0]=(f-v)/d,t[1]=(l-h)/d,t[2]=(c-s)/d):i>M&&i>b?(d=2*Math.sqrt(1+i-M-b),t[3]=(f-v)/d,t[0]=.25*d,t[1]=(c+s)/d,t[2]=(l+h)/d):M>b?(d=2*Math.sqrt(1+M-i-b),t[3]=(l-h)/d,t[0]=(c+s)/d,t[1]=.25*d,t[2]=(f+v)/d):(d=2*Math.sqrt(1+b-i-M),t[3]=(c-s)/d,t[0]=(l+h)/d,t[1]=(f+v)/d,t[2]=.25*d),t}function j(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t[3]=n[3]-a[3],t[4]=n[4]-a[4],t[5]=n[5]-a[5],t[6]=n[6]-a[6],t[7]=n[7]-a[7],t[8]=n[8]-a[8],t[9]=n[9]-a[9],t[10]=n[10]-a[10],t[11]=n[11]-a[11],t[12]=n[12]-a[12],t[13]=n[13]-a[13],t[14]=n[14]-a[14],t[15]=n[15]-a[15],t}var I=A,S=j,E=Object.freeze({create:function(){var t=new a(16);return a!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0),t[0]=1,t[5]=1,t[10]=1,t[15]=1,t},clone:function(t){var n=new a(16);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n[9]=t[9],n[10]=t[10],n[11]=t[11],n[12]=t[12],n[13]=t[13],n[14]=t[14],n[15]=t[15],n},copy:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},fromValues:function(t,n,r,u,e,o,i,c,h,s,M,f,l,v,b,m){var d=new a(16);return d[0]=t,d[1]=n,d[2]=r,d[3]=u,d[4]=e,d[5]=o,d[6]=i,d[7]=c,d[8]=h,d[9]=s,d[10]=M,d[11]=f,d[12]=l,d[13]=v,d[14]=b,d[15]=m,d},set:function(t,n,a,r,u,e,o,i,c,h,s,M,f,l,v,b,m){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t[4]=e,t[5]=o,t[6]=i,t[7]=c,t[8]=h,t[9]=s,t[10]=M,t[11]=f,t[12]=l,t[13]=v,t[14]=b,t[15]=m,t},identity:g,transpose:function(t,n){if(t===n){var a=n[1],r=n[2],u=n[3],e=n[6],o=n[7],i=n[11];t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=a,t[6]=n[9],t[7]=n[13],t[8]=r,t[9]=e,t[11]=n[14],t[12]=u,t[13]=o,t[14]=i}else t[0]=n[0],t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=n[1],t[5]=n[5],t[6]=n[9],t[7]=n[13],t[8]=n[2],t[9]=n[6],t[10]=n[10],t[11]=n[14],t[12]=n[3],t[13]=n[7],t[14]=n[11],t[15]=n[15];return t},invert:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=n[6],h=n[7],s=n[8],M=n[9],f=n[10],l=n[11],v=n[12],b=n[13],m=n[14],d=n[15],x=a*i-r*o,p=a*c-u*o,y=a*h-e*o,q=r*c-u*i,g=r*h-e*i,A=u*h-e*c,w=s*b-M*v,R=s*m-f*v,z=s*d-l*v,P=M*m-f*b,j=M*d-l*b,I=f*d-l*m,S=x*I-p*j+y*P+q*z-g*R+A*w;return S?(S=1/S,t[0]=(i*I-c*j+h*P)*S,t[1]=(u*j-r*I-e*P)*S,t[2]=(b*A-m*g+d*q)*S,t[3]=(f*g-M*A-l*q)*S,t[4]=(c*z-o*I-h*R)*S,t[5]=(a*I-u*z+e*R)*S,t[6]=(m*y-v*A-d*p)*S,t[7]=(s*A-f*y+l*p)*S,t[8]=(o*j-i*z+h*w)*S,t[9]=(r*z-a*j-e*w)*S,t[10]=(v*g-b*y+d*x)*S,t[11]=(M*y-s*g-l*x)*S,t[12]=(i*R-o*P-c*w)*S,t[13]=(a*P-r*R+u*w)*S,t[14]=(b*p-v*q-m*x)*S,t[15]=(s*q-M*p+f*x)*S,t):null},adjoint:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=n[4],i=n[5],c=n[6],h=n[7],s=n[8],M=n[9],f=n[10],l=n[11],v=n[12],b=n[13],m=n[14],d=n[15];return t[0]=i*(f*d-l*m)-M*(c*d-h*m)+b*(c*l-h*f),t[1]=-(r*(f*d-l*m)-M*(u*d-e*m)+b*(u*l-e*f)),t[2]=r*(c*d-h*m)-i*(u*d-e*m)+b*(u*h-e*c),t[3]=-(r*(c*l-h*f)-i*(u*l-e*f)+M*(u*h-e*c)),t[4]=-(o*(f*d-l*m)-s*(c*d-h*m)+v*(c*l-h*f)),t[5]=a*(f*d-l*m)-s*(u*d-e*m)+v*(u*l-e*f),t[6]=-(a*(c*d-h*m)-o*(u*d-e*m)+v*(u*h-e*c)),t[7]=a*(c*l-h*f)-o*(u*l-e*f)+s*(u*h-e*c),t[8]=o*(M*d-l*b)-s*(i*d-h*b)+v*(i*l-h*M),t[9]=-(a*(M*d-l*b)-s*(r*d-e*b)+v*(r*l-e*M)),t[10]=a*(i*d-h*b)-o*(r*d-e*b)+v*(r*h-e*i),t[11]=-(a*(i*l-h*M)-o*(r*l-e*M)+s*(r*h-e*i)),t[12]=-(o*(M*m-f*b)-s*(i*m-c*b)+v*(i*f-c*M)),t[13]=a*(M*m-f*b)-s*(r*m-u*b)+v*(r*f-u*M),t[14]=-(a*(i*m-c*b)-o*(r*m-u*b)+v*(r*c-u*i)),t[15]=a*(i*f-c*M)-o*(r*f-u*M)+s*(r*c-u*i),t},determinant:function(t){var n=t[0],a=t[1],r=t[2],u=t[3],e=t[4],o=t[5],i=t[6],c=t[7],h=t[8],s=t[9],M=t[10],f=t[11],l=t[12],v=t[13],b=t[14],m=t[15];return(n*o-a*e)*(M*m-f*b)-(n*i-r*e)*(s*m-f*v)+(n*c-u*e)*(s*b-M*v)+(a*i-r*o)*(h*m-f*l)-(a*c-u*o)*(h*b-M*l)+(r*c-u*i)*(h*v-s*l)},multiply:A,translate:function(t,n,a){var r,u,e,o,i,c,h,s,M,f,l,v,b=a[0],m=a[1],d=a[2];return n===t?(t[12]=n[0]*b+n[4]*m+n[8]*d+n[12],t[13]=n[1]*b+n[5]*m+n[9]*d+n[13],t[14]=n[2]*b+n[6]*m+n[10]*d+n[14],t[15]=n[3]*b+n[7]*m+n[11]*d+n[15]):(r=n[0],u=n[1],e=n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=n[8],f=n[9],l=n[10],v=n[11],t[0]=r,t[1]=u,t[2]=e,t[3]=o,t[4]=i,t[5]=c,t[6]=h,t[7]=s,t[8]=M,t[9]=f,t[10]=l,t[11]=v,t[12]=r*b+i*m+M*d+n[12],t[13]=u*b+c*m+f*d+n[13],t[14]=e*b+h*m+l*d+n[14],t[15]=o*b+s*m+v*d+n[15]),t},scale:function(t,n,a){var r=a[0],u=a[1],e=a[2];return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*u,t[5]=n[5]*u,t[6]=n[6]*u,t[7]=n[7]*u,t[8]=n[8]*e,t[9]=n[9]*e,t[10]=n[10]*e,t[11]=n[11]*e,t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},rotate:function(t,a,r,u){var e,o,i,c,h,s,M,f,l,v,b,m,d,x,p,y,q,g,A,w,R,z,P,j,I=u[0],S=u[1],E=u[2],O=Math.hypot(I,S,E);return O0?(r[0]=2*(c*i+M*u+h*o-s*e)/f,r[1]=2*(h*i+M*e+s*u-c*o)/f,r[2]=2*(s*i+M*o+c*e-h*u)/f):(r[0]=2*(c*i+M*u+h*o-s*e),r[1]=2*(h*i+M*e+s*u-c*o),r[2]=2*(s*i+M*o+c*e-h*u)),w(t,n,r),t},getTranslation:R,getScaling:z,getRotation:P,fromRotationTranslationScale:function(t,n,a,r){var u=n[0],e=n[1],o=n[2],i=n[3],c=u+u,h=e+e,s=o+o,M=u*c,f=u*h,l=u*s,v=e*h,b=e*s,m=o*s,d=i*c,x=i*h,p=i*s,y=r[0],q=r[1],g=r[2];return t[0]=(1-(v+m))*y,t[1]=(f+p)*y,t[2]=(l-x)*y,t[3]=0,t[4]=(f-p)*q,t[5]=(1-(M+m))*q,t[6]=(b+d)*q,t[7]=0,t[8]=(l+x)*g,t[9]=(b-d)*g,t[10]=(1-(M+v))*g,t[11]=0,t[12]=a[0],t[13]=a[1],t[14]=a[2],t[15]=1,t},fromRotationTranslationScaleOrigin:function(t,n,a,r,u){var e=n[0],o=n[1],i=n[2],c=n[3],h=e+e,s=o+o,M=i+i,f=e*h,l=e*s,v=e*M,b=o*s,m=o*M,d=i*M,x=c*h,p=c*s,y=c*M,q=r[0],g=r[1],A=r[2],w=u[0],R=u[1],z=u[2],P=(1-(b+d))*q,j=(l+y)*q,I=(v-p)*q,S=(l-y)*g,E=(1-(f+d))*g,O=(m+x)*g,T=(v+p)*A,D=(m-x)*A,F=(1-(f+b))*A;return t[0]=P,t[1]=j,t[2]=I,t[3]=0,t[4]=S,t[5]=E,t[6]=O,t[7]=0,t[8]=T,t[9]=D,t[10]=F,t[11]=0,t[12]=a[0]+w-(P*w+S*R+T*z),t[13]=a[1]+R-(j*w+E*R+D*z),t[14]=a[2]+z-(I*w+O*R+F*z),t[15]=1,t},fromQuat:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=a+a,i=r+r,c=u+u,h=a*o,s=r*o,M=r*i,f=u*o,l=u*i,v=u*c,b=e*o,m=e*i,d=e*c;return t[0]=1-M-v,t[1]=s+d,t[2]=f-m,t[3]=0,t[4]=s-d,t[5]=1-h-v,t[6]=l+b,t[7]=0,t[8]=f+m,t[9]=l-b,t[10]=1-h-M,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},frustum:function(t,n,a,r,u,e,o){var i=1/(a-n),c=1/(u-r),h=1/(e-o);return t[0]=2*e*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*e*c,t[6]=0,t[7]=0,t[8]=(a+n)*i,t[9]=(u+r)*c,t[10]=(o+e)*h,t[11]=-1,t[12]=0,t[13]=0,t[14]=o*e*2*h,t[15]=0,t},perspective:function(t,n,a,r,u){var e,o=1/Math.tan(n/2);return t[0]=o/a,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=o,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=-1,t[12]=0,t[13]=0,t[15]=0,null!=u&&u!==1/0?(e=1/(r-u),t[10]=(u+r)*e,t[14]=2*u*r*e):(t[10]=-1,t[14]=-2*r),t},perspectiveFromFieldOfView:function(t,n,a,r){var u=Math.tan(n.upDegrees*Math.PI/180),e=Math.tan(n.downDegrees*Math.PI/180),o=Math.tan(n.leftDegrees*Math.PI/180),i=Math.tan(n.rightDegrees*Math.PI/180),c=2/(o+i),h=2/(u+e);return t[0]=c,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=h,t[6]=0,t[7]=0,t[8]=-(o-i)*c*.5,t[9]=(u-e)*h*.5,t[10]=r/(a-r),t[11]=-1,t[12]=0,t[13]=0,t[14]=r*a/(a-r),t[15]=0,t},ortho:function(t,n,a,r,u,e,o){var i=1/(n-a),c=1/(r-u),h=1/(e-o);return t[0]=-2*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*c,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*h,t[11]=0,t[12]=(n+a)*i,t[13]=(u+r)*c,t[14]=(o+e)*h,t[15]=1,t},lookAt:function(t,a,r,u){var e,o,i,c,h,s,M,f,l,v,b=a[0],m=a[1],d=a[2],x=u[0],p=u[1],y=u[2],q=r[0],A=r[1],w=r[2];return Math.abs(b-q)0&&(s*=l=1/Math.sqrt(l),M*=l,f*=l);var v=c*f-h*M,b=h*s-i*f,m=i*M-c*s;return(l=v*v+b*b+m*m)>0&&(v*=l=1/Math.sqrt(l),b*=l,m*=l),t[0]=v,t[1]=b,t[2]=m,t[3]=0,t[4]=M*m-f*b,t[5]=f*v-s*m,t[6]=s*b-M*v,t[7]=0,t[8]=s,t[9]=M,t[10]=f,t[11]=0,t[12]=u,t[13]=e,t[14]=o,t[15]=1,t},str:function(t){return"mat4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+", "+t[9]+", "+t[10]+", "+t[11]+", "+t[12]+", "+t[13]+", "+t[14]+", "+t[15]+")"},frob:function(t){return Math.hypot(t[0],t[1],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15])},add:function(t,n,a){return t[0]=n[0]+a[0],t[1]=n[1]+a[1],t[2]=n[2]+a[2],t[3]=n[3]+a[3],t[4]=n[4]+a[4],t[5]=n[5]+a[5],t[6]=n[6]+a[6],t[7]=n[7]+a[7],t[8]=n[8]+a[8],t[9]=n[9]+a[9],t[10]=n[10]+a[10],t[11]=n[11]+a[11],t[12]=n[12]+a[12],t[13]=n[13]+a[13],t[14]=n[14]+a[14],t[15]=n[15]+a[15],t},subtract:j,multiplyScalar:function(t,n,a){return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*a,t[5]=n[5]*a,t[6]=n[6]*a,t[7]=n[7]*a,t[8]=n[8]*a,t[9]=n[9]*a,t[10]=n[10]*a,t[11]=n[11]*a,t[12]=n[12]*a,t[13]=n[13]*a,t[14]=n[14]*a,t[15]=n[15]*a,t},multiplyScalarAndAdd:function(t,n,a,r){return t[0]=n[0]+a[0]*r,t[1]=n[1]+a[1]*r,t[2]=n[2]+a[2]*r,t[3]=n[3]+a[3]*r,t[4]=n[4]+a[4]*r,t[5]=n[5]+a[5]*r,t[6]=n[6]+a[6]*r,t[7]=n[7]+a[7]*r,t[8]=n[8]+a[8]*r,t[9]=n[9]+a[9]*r,t[10]=n[10]+a[10]*r,t[11]=n[11]+a[11]*r,t[12]=n[12]+a[12]*r,t[13]=n[13]+a[13]*r,t[14]=n[14]+a[14]*r,t[15]=n[15]+a[15]*r,t},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]&&t[9]===n[9]&&t[10]===n[10]&&t[11]===n[11]&&t[12]===n[12]&&t[13]===n[13]&&t[14]===n[14]&&t[15]===n[15]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=t[4],c=t[5],h=t[6],s=t[7],M=t[8],f=t[9],l=t[10],v=t[11],b=t[12],m=t[13],d=t[14],x=t[15],p=a[0],y=a[1],q=a[2],g=a[3],A=a[4],w=a[5],R=a[6],z=a[7],P=a[8],j=a[9],I=a[10],S=a[11],E=a[12],O=a[13],T=a[14],D=a[15];return Math.abs(r-p)<=n*Math.max(1,Math.abs(r),Math.abs(p))&&Math.abs(u-y)<=n*Math.max(1,Math.abs(u),Math.abs(y))&&Math.abs(e-q)<=n*Math.max(1,Math.abs(e),Math.abs(q))&&Math.abs(o-g)<=n*Math.max(1,Math.abs(o),Math.abs(g))&&Math.abs(i-A)<=n*Math.max(1,Math.abs(i),Math.abs(A))&&Math.abs(c-w)<=n*Math.max(1,Math.abs(c),Math.abs(w))&&Math.abs(h-R)<=n*Math.max(1,Math.abs(h),Math.abs(R))&&Math.abs(s-z)<=n*Math.max(1,Math.abs(s),Math.abs(z))&&Math.abs(M-P)<=n*Math.max(1,Math.abs(M),Math.abs(P))&&Math.abs(f-j)<=n*Math.max(1,Math.abs(f),Math.abs(j))&&Math.abs(l-I)<=n*Math.max(1,Math.abs(l),Math.abs(I))&&Math.abs(v-S)<=n*Math.max(1,Math.abs(v),Math.abs(S))&&Math.abs(b-E)<=n*Math.max(1,Math.abs(b),Math.abs(E))&&Math.abs(m-O)<=n*Math.max(1,Math.abs(m),Math.abs(O))&&Math.abs(d-T)<=n*Math.max(1,Math.abs(d),Math.abs(T))&&Math.abs(x-D)<=n*Math.max(1,Math.abs(x),Math.abs(D))},mul:I,sub:S});function O(){var t=new a(3);return a!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t}function T(t){var n=t[0],a=t[1],r=t[2];return Math.hypot(n,a,r)}function D(t,n,r){var u=new a(3);return u[0]=t,u[1]=n,u[2]=r,u}function F(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t}function L(t,n,a){return t[0]=n[0]*a[0],t[1]=n[1]*a[1],t[2]=n[2]*a[2],t}function V(t,n,a){return t[0]=n[0]/a[0],t[1]=n[1]/a[1],t[2]=n[2]/a[2],t}function Q(t,n){var a=n[0]-t[0],r=n[1]-t[1],u=n[2]-t[2];return Math.hypot(a,r,u)}function Y(t,n){var a=n[0]-t[0],r=n[1]-t[1],u=n[2]-t[2];return a*a+r*r+u*u}function X(t){var n=t[0],a=t[1],r=t[2];return n*n+a*a+r*r}function Z(t,n){var a=n[0],r=n[1],u=n[2],e=a*a+r*r+u*u;return e>0&&(e=1/Math.sqrt(e)),t[0]=n[0]*e,t[1]=n[1]*e,t[2]=n[2]*e,t}function _(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function B(t,n,a){var r=n[0],u=n[1],e=n[2],o=a[0],i=a[1],c=a[2];return t[0]=u*c-e*i,t[1]=e*o-r*c,t[2]=r*i-u*o,t}var N,k=F,U=L,W=V,C=Q,G=Y,H=T,J=X,K=(N=O(),function(t,n,a,r,u,e){var o,i;for(n||(n=3),a||(a=0),i=r?Math.min(r*n+a,t.length):t.length,o=a;o1?0:u<-1?Math.PI:Math.acos(u)},zero:function(t){return t[0]=0,t[1]=0,t[2]=0,t},str:function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=a[0],i=a[1],c=a[2];return Math.abs(r-o)<=n*Math.max(1,Math.abs(r),Math.abs(o))&&Math.abs(u-i)<=n*Math.max(1,Math.abs(u),Math.abs(i))&&Math.abs(e-c)<=n*Math.max(1,Math.abs(e),Math.abs(c))},sub:k,mul:U,div:W,dist:C,sqrDist:G,len:H,sqrLen:J,forEach:K});function tt(){var t=new a(4);return a!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[3]=0),t}function nt(t){var n=new a(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n}function at(t,n,r,u){var e=new a(4);return e[0]=t,e[1]=n,e[2]=r,e[3]=u,e}function rt(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t}function ut(t,n,a,r,u){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t}function et(t,n,a){return t[0]=n[0]+a[0],t[1]=n[1]+a[1],t[2]=n[2]+a[2],t[3]=n[3]+a[3],t}function ot(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t[2]=n[2]-a[2],t[3]=n[3]-a[3],t}function it(t,n,a){return t[0]=n[0]*a[0],t[1]=n[1]*a[1],t[2]=n[2]*a[2],t[3]=n[3]*a[3],t}function ct(t,n,a){return t[0]=n[0]/a[0],t[1]=n[1]/a[1],t[2]=n[2]/a[2],t[3]=n[3]/a[3],t}function ht(t,n,a){return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t}function st(t,n){var a=n[0]-t[0],r=n[1]-t[1],u=n[2]-t[2],e=n[3]-t[3];return Math.hypot(a,r,u,e)}function Mt(t,n){var a=n[0]-t[0],r=n[1]-t[1],u=n[2]-t[2],e=n[3]-t[3];return a*a+r*r+u*u+e*e}function ft(t){var n=t[0],a=t[1],r=t[2],u=t[3];return Math.hypot(n,a,r,u)}function lt(t){var n=t[0],a=t[1],r=t[2],u=t[3];return n*n+a*a+r*r+u*u}function vt(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=a*a+r*r+u*u+e*e;return o>0&&(o=1/Math.sqrt(o)),t[0]=a*o,t[1]=r*o,t[2]=u*o,t[3]=e*o,t}function bt(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]}function mt(t,n,a,r){var u=n[0],e=n[1],o=n[2],i=n[3];return t[0]=u+r*(a[0]-u),t[1]=e+r*(a[1]-e),t[2]=o+r*(a[2]-o),t[3]=i+r*(a[3]-i),t}function dt(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]}function xt(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=a[0],c=a[1],h=a[2],s=a[3];return Math.abs(r-i)<=n*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(u-c)<=n*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(e-h)<=n*Math.max(1,Math.abs(e),Math.abs(h))&&Math.abs(o-s)<=n*Math.max(1,Math.abs(o),Math.abs(s))}var pt=ot,yt=it,qt=ct,gt=st,At=Mt,wt=ft,Rt=lt,zt=function(){var t=tt();return function(n,a,r,u,e,o){var i,c;for(a||(a=4),r||(r=0),c=u?Math.min(u*a+r,n.length):n.length,i=r;i=1);do{c=(e=2*r()-1)*e+(o=2*r()-1)*o}while(c>=1);var h=Math.sqrt((1-i)/c);return t[0]=n*a,t[1]=n*u,t[2]=n*e*h,t[3]=n*o*h,t},transformMat4:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3];return t[0]=a[0]*r+a[4]*u+a[8]*e+a[12]*o,t[1]=a[1]*r+a[5]*u+a[9]*e+a[13]*o,t[2]=a[2]*r+a[6]*u+a[10]*e+a[14]*o,t[3]=a[3]*r+a[7]*u+a[11]*e+a[15]*o,t},transformQuat:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=a[0],i=a[1],c=a[2],h=a[3],s=h*r+i*e-c*u,M=h*u+c*r-o*e,f=h*e+o*u-i*r,l=-o*r-i*u-c*e;return t[0]=s*h+l*-o+M*-c-f*-i,t[1]=M*h+l*-i+f*-o-s*-c,t[2]=f*h+l*-c+s*-i-M*-o,t[3]=n[3],t},zero:function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},str:function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},exactEquals:dt,equals:xt,sub:pt,mul:yt,div:qt,dist:gt,sqrDist:At,len:wt,sqrLen:Rt,forEach:zt});function jt(){var t=new a(4);return a!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t[3]=1,t}function It(t,n,a){a*=.5;var r=Math.sin(a);return t[0]=r*n[0],t[1]=r*n[1],t[2]=r*n[2],t[3]=Math.cos(a),t}function St(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=a[0],c=a[1],h=a[2],s=a[3];return t[0]=r*s+o*i+u*h-e*c,t[1]=u*s+o*c+e*i-r*h,t[2]=e*s+o*h+r*c-u*i,t[3]=o*s-r*i-u*c-e*h,t}function Et(t,n,a){a*=.5;var r=n[0],u=n[1],e=n[2],o=n[3],i=Math.sin(a),c=Math.cos(a);return t[0]=r*c+o*i,t[1]=u*c+e*i,t[2]=e*c-u*i,t[3]=o*c-r*i,t}function Ot(t,n,a){a*=.5;var r=n[0],u=n[1],e=n[2],o=n[3],i=Math.sin(a),c=Math.cos(a);return t[0]=r*c-e*i,t[1]=u*c+o*i,t[2]=e*c+r*i,t[3]=o*c-u*i,t}function Tt(t,n,a){a*=.5;var r=n[0],u=n[1],e=n[2],o=n[3],i=Math.sin(a),c=Math.cos(a);return t[0]=r*c+u*i,t[1]=u*c-r*i,t[2]=e*c+o*i,t[3]=o*c-e*i,t}function Dt(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=Math.sqrt(a*a+r*r+u*u),i=Math.exp(e),c=o>0?i*Math.sin(o)/o:0;return t[0]=a*c,t[1]=r*c,t[2]=u*c,t[3]=i*Math.cos(o),t}function Ft(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=Math.sqrt(a*a+r*r+u*u),i=o>0?Math.atan2(o,e)/o:0;return t[0]=a*i,t[1]=r*i,t[2]=u*i,t[3]=.5*Math.log(a*a+r*r+u*u+e*e),t}function Lt(t,a,r,u){var e,o,i,c,h,s=a[0],M=a[1],f=a[2],l=a[3],v=r[0],b=r[1],m=r[2],d=r[3];return(o=s*v+M*b+f*m+l*d)<0&&(o=-o,v=-v,b=-b,m=-m,d=-d),1-o>n?(e=Math.acos(o),i=Math.sin(e),c=Math.sin((1-u)*e)/i,h=Math.sin(u*e)/i):(c=1-u,h=u),t[0]=c*s+h*v,t[1]=c*M+h*b,t[2]=c*f+h*m,t[3]=c*l+h*d,t}function Vt(t,n){var a,r=n[0]+n[4]+n[8];if(r>0)a=Math.sqrt(r+1),t[3]=.5*a,a=.5/a,t[0]=(n[5]-n[7])*a,t[1]=(n[6]-n[2])*a,t[2]=(n[1]-n[3])*a;else{var u=0;n[4]>n[0]&&(u=1),n[8]>n[3*u+u]&&(u=2);var e=(u+1)%3,o=(u+2)%3;a=Math.sqrt(n[3*u+u]-n[3*e+e]-n[3*o+o]+1),t[u]=.5*a,a=.5/a,t[3]=(n[3*e+o]-n[3*o+e])*a,t[e]=(n[3*e+u]+n[3*u+e])*a,t[o]=(n[3*o+u]+n[3*u+o])*a}return t}var Qt,Yt,Xt,Zt,_t,Bt,Nt=nt,kt=at,Ut=rt,Wt=ut,Ct=et,Gt=St,Ht=ht,Jt=bt,Kt=mt,$t=ft,tn=$t,nn=lt,an=nn,rn=vt,un=dt,en=xt,on=(Qt=O(),Yt=D(1,0,0),Xt=D(0,1,0),function(t,n,a){var r=_(n,a);return r<-.999999?(B(Qt,Yt,n),H(Qt)<1e-6&&B(Qt,Xt,n),Z(Qt,Qt),It(t,Qt,Math.PI),t):r>.999999?(t[0]=0,t[1]=0,t[2]=0,t[3]=1,t):(B(Qt,n,a),t[0]=Qt[0],t[1]=Qt[1],t[2]=Qt[2],t[3]=1+r,rn(t,t))}),cn=(Zt=jt(),_t=jt(),function(t,n,a,r,u,e){return Lt(Zt,n,u,e),Lt(_t,a,r,e),Lt(t,Zt,_t,2*e*(1-e)),t}),hn=(Bt=m(),function(t,n,a,r){return Bt[0]=a[0],Bt[3]=a[1],Bt[6]=a[2],Bt[1]=r[0],Bt[4]=r[1],Bt[7]=r[2],Bt[2]=-n[0],Bt[5]=-n[1],Bt[8]=-n[2],rn(t,Vt(t,Bt))}),sn=Object.freeze({create:jt,identity:function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},setAxisAngle:It,getAxisAngle:function(t,a){var r=2*Math.acos(a[3]),u=Math.sin(r/2);return u>n?(t[0]=a[0]/u,t[1]=a[1]/u,t[2]=a[2]/u):(t[0]=1,t[1]=0,t[2]=0),r},getAngle:function(t,n){var a=Jt(t,n);return Math.acos(2*a*a-1)},multiply:St,rotateX:Et,rotateY:Ot,rotateZ:Tt,calculateW:function(t,n){var a=n[0],r=n[1],u=n[2];return t[0]=a,t[1]=r,t[2]=u,t[3]=Math.sqrt(Math.abs(1-a*a-r*r-u*u)),t},exp:Dt,ln:Ft,pow:function(t,n,a){return Ft(t,n),Ht(t,t,a),Dt(t,t),t},slerp:Lt,random:function(t){var n=r(),a=r(),u=r(),e=Math.sqrt(1-n),o=Math.sqrt(n);return t[0]=e*Math.sin(2*Math.PI*a),t[1]=e*Math.cos(2*Math.PI*a),t[2]=o*Math.sin(2*Math.PI*u),t[3]=o*Math.cos(2*Math.PI*u),t},invert:function(t,n){var a=n[0],r=n[1],u=n[2],e=n[3],o=a*a+r*r+u*u+e*e,i=o?1/o:0;return t[0]=-a*i,t[1]=-r*i,t[2]=-u*i,t[3]=e*i,t},conjugate:function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t},fromMat3:Vt,fromEuler:function(t,n,a,r){var u=.5*Math.PI/180;n*=u,a*=u,r*=u;var e=Math.sin(n),o=Math.cos(n),i=Math.sin(a),c=Math.cos(a),h=Math.sin(r),s=Math.cos(r);return t[0]=e*c*s-o*i*h,t[1]=o*i*s+e*c*h,t[2]=o*c*h-e*i*s,t[3]=o*c*s+e*i*h,t},str:function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},clone:Nt,fromValues:kt,copy:Ut,set:Wt,add:Ct,mul:Gt,scale:Ht,dot:Jt,lerp:Kt,length:$t,len:tn,squaredLength:nn,sqrLen:an,normalize:rn,exactEquals:un,equals:en,rotationTo:on,sqlerp:cn,setAxes:hn});function Mn(t,n,a){var r=.5*a[0],u=.5*a[1],e=.5*a[2],o=n[0],i=n[1],c=n[2],h=n[3];return t[0]=o,t[1]=i,t[2]=c,t[3]=h,t[4]=r*h+u*c-e*i,t[5]=u*h+e*o-r*c,t[6]=e*h+r*i-u*o,t[7]=-r*o-u*i-e*c,t}function fn(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t}var ln=Ut;var vn=Ut;function bn(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=a[4],c=a[5],h=a[6],s=a[7],M=n[4],f=n[5],l=n[6],v=n[7],b=a[0],m=a[1],d=a[2],x=a[3];return t[0]=r*x+o*b+u*d-e*m,t[1]=u*x+o*m+e*b-r*d,t[2]=e*x+o*d+r*m-u*b,t[3]=o*x-r*b-u*m-e*d,t[4]=r*s+o*i+u*h-e*c+M*x+v*b+f*d-l*m,t[5]=u*s+o*c+e*i-r*h+f*x+v*m+l*b-M*d,t[6]=e*s+o*h+r*c-u*i+l*x+v*d+M*m-f*b,t[7]=o*s-r*i-u*c-e*h+v*x-M*b-f*m-l*d,t}var mn=bn;var dn=Jt;var xn=$t,pn=xn,yn=nn,qn=yn;var gn=Object.freeze({create:function(){var t=new a(8);return a!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[4]=0,t[5]=0,t[6]=0,t[7]=0),t[3]=1,t},clone:function(t){var n=new a(8);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n},fromValues:function(t,n,r,u,e,o,i,c){var h=new a(8);return h[0]=t,h[1]=n,h[2]=r,h[3]=u,h[4]=e,h[5]=o,h[6]=i,h[7]=c,h},fromRotationTranslationValues:function(t,n,r,u,e,o,i){var c=new a(8);c[0]=t,c[1]=n,c[2]=r,c[3]=u;var h=.5*e,s=.5*o,M=.5*i;return c[4]=h*u+s*r-M*n,c[5]=s*u+M*t-h*r,c[6]=M*u+h*n-s*t,c[7]=-h*t-s*n-M*r,c},fromRotationTranslation:Mn,fromTranslation:function(t,n){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t[4]=.5*n[0],t[5]=.5*n[1],t[6]=.5*n[2],t[7]=0,t},fromRotation:function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=0,t[5]=0,t[6]=0,t[7]=0,t},fromMat4:function(t,n){var r=jt();P(r,n);var u=new a(3);return R(u,n),Mn(t,r,u),t},copy:fn,identity:function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t[6]=0,t[7]=0,t},set:function(t,n,a,r,u,e,o,i,c){return t[0]=n,t[1]=a,t[2]=r,t[3]=u,t[4]=e,t[5]=o,t[6]=i,t[7]=c,t},getReal:ln,getDual:function(t,n){return t[0]=n[4],t[1]=n[5],t[2]=n[6],t[3]=n[7],t},setReal:vn,setDual:function(t,n){return t[4]=n[0],t[5]=n[1],t[6]=n[2],t[7]=n[3],t},getTranslation:function(t,n){var a=n[4],r=n[5],u=n[6],e=n[7],o=-n[0],i=-n[1],c=-n[2],h=n[3];return t[0]=2*(a*h+e*o+r*c-u*i),t[1]=2*(r*h+e*i+u*o-a*c),t[2]=2*(u*h+e*c+a*i-r*o),t},translate:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=.5*a[0],c=.5*a[1],h=.5*a[2],s=n[4],M=n[5],f=n[6],l=n[7];return t[0]=r,t[1]=u,t[2]=e,t[3]=o,t[4]=o*i+u*h-e*c+s,t[5]=o*c+e*i-r*h+M,t[6]=o*h+r*c-u*i+f,t[7]=-r*i-u*c-e*h+l,t},rotateX:function(t,n,a){var r=-n[0],u=-n[1],e=-n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=i*o+s*r+c*e-h*u,f=c*o+s*u+h*r-i*e,l=h*o+s*e+i*u-c*r,v=s*o-i*r-c*u-h*e;return Et(t,n,a),r=t[0],u=t[1],e=t[2],o=t[3],t[4]=M*o+v*r+f*e-l*u,t[5]=f*o+v*u+l*r-M*e,t[6]=l*o+v*e+M*u-f*r,t[7]=v*o-M*r-f*u-l*e,t},rotateY:function(t,n,a){var r=-n[0],u=-n[1],e=-n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=i*o+s*r+c*e-h*u,f=c*o+s*u+h*r-i*e,l=h*o+s*e+i*u-c*r,v=s*o-i*r-c*u-h*e;return Ot(t,n,a),r=t[0],u=t[1],e=t[2],o=t[3],t[4]=M*o+v*r+f*e-l*u,t[5]=f*o+v*u+l*r-M*e,t[6]=l*o+v*e+M*u-f*r,t[7]=v*o-M*r-f*u-l*e,t},rotateZ:function(t,n,a){var r=-n[0],u=-n[1],e=-n[2],o=n[3],i=n[4],c=n[5],h=n[6],s=n[7],M=i*o+s*r+c*e-h*u,f=c*o+s*u+h*r-i*e,l=h*o+s*e+i*u-c*r,v=s*o-i*r-c*u-h*e;return Tt(t,n,a),r=t[0],u=t[1],e=t[2],o=t[3],t[4]=M*o+v*r+f*e-l*u,t[5]=f*o+v*u+l*r-M*e,t[6]=l*o+v*e+M*u-f*r,t[7]=v*o-M*r-f*u-l*e,t},rotateByQuatAppend:function(t,n,a){var r=a[0],u=a[1],e=a[2],o=a[3],i=n[0],c=n[1],h=n[2],s=n[3];return t[0]=i*o+s*r+c*e-h*u,t[1]=c*o+s*u+h*r-i*e,t[2]=h*o+s*e+i*u-c*r,t[3]=s*o-i*r-c*u-h*e,i=n[4],c=n[5],h=n[6],s=n[7],t[4]=i*o+s*r+c*e-h*u,t[5]=c*o+s*u+h*r-i*e,t[6]=h*o+s*e+i*u-c*r,t[7]=s*o-i*r-c*u-h*e,t},rotateByQuatPrepend:function(t,n,a){var r=n[0],u=n[1],e=n[2],o=n[3],i=a[0],c=a[1],h=a[2],s=a[3];return t[0]=r*s+o*i+u*h-e*c,t[1]=u*s+o*c+e*i-r*h,t[2]=e*s+o*h+r*c-u*i,t[3]=o*s-r*i-u*c-e*h,i=a[4],c=a[5],h=a[6],s=a[7],t[4]=r*s+o*i+u*h-e*c,t[5]=u*s+o*c+e*i-r*h,t[6]=e*s+o*h+r*c-u*i,t[7]=o*s-r*i-u*c-e*h,t},rotateAroundAxis:function(t,a,r,u){if(Math.abs(u)0){a=Math.sqrt(a);var r=n[0]/a,u=n[1]/a,e=n[2]/a,o=n[3]/a,i=n[4],c=n[5],h=n[6],s=n[7],M=r*i+u*c+e*h+o*s;t[0]=r,t[1]=u,t[2]=e,t[3]=o,t[4]=(i-r*M)/a,t[5]=(c-u*M)/a,t[6]=(h-e*M)/a,t[7]=(s-o*M)/a}return t},str:function(t){return"quat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+")"},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]},equals:function(t,a){var r=t[0],u=t[1],e=t[2],o=t[3],i=t[4],c=t[5],h=t[6],s=t[7],M=a[0],f=a[1],l=a[2],v=a[3],b=a[4],m=a[5],d=a[6],x=a[7];return Math.abs(r-M)<=n*Math.max(1,Math.abs(r),Math.abs(M))&&Math.abs(u-f)<=n*Math.max(1,Math.abs(u),Math.abs(f))&&Math.abs(e-l)<=n*Math.max(1,Math.abs(e),Math.abs(l))&&Math.abs(o-v)<=n*Math.max(1,Math.abs(o),Math.abs(v))&&Math.abs(i-b)<=n*Math.max(1,Math.abs(i),Math.abs(b))&&Math.abs(c-m)<=n*Math.max(1,Math.abs(c),Math.abs(m))&&Math.abs(h-d)<=n*Math.max(1,Math.abs(h),Math.abs(d))&&Math.abs(s-x)<=n*Math.max(1,Math.abs(s),Math.abs(x))}});function An(){var t=new a(2);return a!=Float32Array&&(t[0]=0,t[1]=0),t}function wn(t,n,a){return t[0]=n[0]-a[0],t[1]=n[1]-a[1],t}function Rn(t,n,a){return t[0]=n[0]*a[0],t[1]=n[1]*a[1],t}function zn(t,n,a){return t[0]=n[0]/a[0],t[1]=n[1]/a[1],t}function Pn(t,n){var a=n[0]-t[0],r=n[1]-t[1];return Math.hypot(a,r)}function jn(t,n){var a=n[0]-t[0],r=n[1]-t[1];return a*a+r*r}function In(t){var n=t[0],a=t[1];return Math.hypot(n,a)}function Sn(t){var n=t[0],a=t[1];return n*n+a*a}var En=In,On=wn,Tn=Rn,Dn=zn,Fn=Pn,Ln=jn,Vn=Sn,Qn=function(){var t=An();return function(n,a,r,u,e,o){var i,c;for(a||(a=2),r||(r=0),c=u?Math.min(u*a+r,n.length):n.length,i=r;i0&&(u=1/Math.sqrt(u)),t[0]=n[0]*u,t[1]=n[1]*u,t},dot:function(t,n){return t[0]*n[0]+t[1]*n[1]},cross:function(t,n,a){var r=n[0]*a[1]-n[1]*a[0];return t[0]=t[1]=0,t[2]=r,t},lerp:function(t,n,a,r){var u=n[0],e=n[1];return t[0]=u+r*(a[0]-u),t[1]=e+r*(a[1]-e),t},random:function(t,n){n=n||1;var a=2*r()*Math.PI;return t[0]=Math.cos(a)*n,t[1]=Math.sin(a)*n,t},transformMat2:function(t,n,a){var r=n[0],u=n[1];return t[0]=a[0]*r+a[2]*u,t[1]=a[1]*r+a[3]*u,t},transformMat2d:function(t,n,a){var r=n[0],u=n[1];return t[0]=a[0]*r+a[2]*u+a[4],t[1]=a[1]*r+a[3]*u+a[5],t},transformMat3:function(t,n,a){var r=n[0],u=n[1];return t[0]=a[0]*r+a[3]*u+a[6],t[1]=a[1]*r+a[4]*u+a[7],t},transformMat4:function(t,n,a){var r=n[0],u=n[1];return t[0]=a[0]*r+a[4]*u+a[12],t[1]=a[1]*r+a[5]*u+a[13],t},rotate:function(t,n,a,r){var u=n[0]-a[0],e=n[1]-a[1],o=Math.sin(r),i=Math.cos(r);return t[0]=u*i-e*o+a[0],t[1]=u*o+e*i+a[1],t},angle:function(t,n){var a=t[0],r=t[1],u=n[0],e=n[1],o=a*a+r*r;o>0&&(o=1/Math.sqrt(o));var i=u*u+e*e;i>0&&(i=1/Math.sqrt(i));var c=(a*u+r*e)*o*i;return c>1?0:c<-1?Math.PI:Math.acos(c)},zero:function(t){return t[0]=0,t[1]=0,t},str:function(t){return"vec2("+t[0]+", "+t[1]+")"},exactEquals:function(t,n){return t[0]===n[0]&&t[1]===n[1]},equals:function(t,a){var r=t[0],u=t[1],e=a[0],o=a[1];return Math.abs(r-e)<=n*Math.max(1,Math.abs(r),Math.abs(e))&&Math.abs(u-o)<=n*Math.max(1,Math.abs(u),Math.abs(o))},len:En,sub:On,mul:Tn,div:Dn,dist:Fn,sqrDist:Ln,sqrLen:Vn,forEach:Qn});t.glMatrix=e,t.mat2=s,t.mat2d=b,t.mat3=q,t.mat4=E,t.quat=sn,t.quat2=gn,t.vec2=Yn,t.vec3=$,t.vec4=Pt,Object.defineProperty(t,"__esModule",{value:!0})}); + +// ["glMatrix", "mat2", "mat2d", "mat3", "mat4", "quat", "quat2", "vec2", "vec3", "vec4"] +window.glMatrix = glMatrix; +window.mat2 = glMatrix.mat2; +window.mat2d = glMatrix.mat2d; +window.mat3 = glMatrix.mat3; +window.mat4 = glMatrix.mat4; +window.quat = glMatrix.quat; +window.quat2 = glMatrix.quat2; +window.vec2 = glMatrix.vec2; +window.vec3 = glMatrix.vec3; +window.vec4 = glMatrix.vec4; + + + +var CABLES = CABLES || {}; CABLES.build = {"timestamp":1671442325495,"created":"2022-12-19T09:32:05.495Z","git":{"branch":"master","commit":"9aa5acdd3f463ec28e2abce3963ff31d3eaf3926","date":null,"message":null}}; \ No newline at end of file diff --git a/survey_dashboard/hmc_layout/static/en_files/libs.js b/survey_dashboard/hmc_layout/static/en_files/libs.js new file mode 100644 index 0000000..e69de29 diff --git a/survey_dashboard/hmc_layout/static/en_files/libs.min.js b/survey_dashboard/hmc_layout/static/en_files/libs.min.js new file mode 100644 index 0000000..e604f8e --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/libs.min.js @@ -0,0 +1,36 @@ +/*! For license information please see colorrick.js.LICENSE.txt */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ColorRick=e():t.ColorRick=e()}(self,(function(){return t={10:(t,e,n)=>{const r=n(792);t.exports=class{constructor(t){if(this._elements=[],this.options=t,this._areaWidth=256,this._areaHeight=150,this._elContainer=document.createElement("div"),this._elContainer.classList.add("colorRick_dialog"),document.body.appendChild(this._elContainer),this._elements.push(this._elContainer),this._elContainer.style.setProperty("--width",this._areaWidth+"px"),this._elContainer.style.setProperty("--height",this._areaHeight+"px"),this._elArea=document.createElement("div"),this._elArea.classList.add("colorRick_area"),this._elContainer.appendChild(this._elArea),this._elements.push(this._elArea),this._elAreaCursor=document.createElement("div"),this._elAreaCursor.classList.add("colorRick_cursor"),this._elArea.appendChild(this._elAreaCursor),this._elements.push(this._elAreaCursor),this._elBrightness=document.createElement("div"),this._elBrightness.classList.add("colorRick_brightness"),this._elArea.appendChild(this._elBrightness),this._elements.push(this._elBrightness),this._elHue=document.createElement("div"),this._elHue.classList.add("colorRick_hue"),this._elContainer.appendChild(this._elHue),this._elements.push(this._elHue),this._elHueCursor=document.createElement("div"),this._elHueCursor.classList.add("colorRick_cursor_hue"),this._elHue.appendChild(this._elHueCursor),this._elements.push(this._elHueCursor),this._elColorBox=document.createElement("div"),this._elColorBox.classList.add("colorRick_preview"),this._elContainer.appendChild(this._elColorBox),this._elements.push(this._elColorBox),this._elColorBoxOrig=document.createElement("div"),this._elColorBoxOrig.classList.add("colorRick_preview"),this._elColorBoxOrig.classList.add("colorRick_preview_orig"),this._elContainer.appendChild(this._elColorBoxOrig),this._elements.push(this._elColorBoxOrig),this._elInputContainer=document.createElement("div"),this._elInputContainer.classList.add("colorRick_inputcontainer"),this._elContainer.appendChild(this._elInputContainer),this._elements.push(this._elInputContainer),this._elInputContainer.innerHTML='
    HEX
    RGB
    HSV
    ',this._inputHex=document.getElementById("colorRick_input_hex"),this._inputR=document.getElementById("colorRick_input_r"),this._inputG=document.getElementById("colorRick_input_g"),this._inputB=document.getElementById("colorRick_input_b"),this._inputH=document.getElementById("colorRick_input_h"),this._inputS=document.getElementById("colorRick_input_s"),this._inputV=document.getElementById("colorRick_input_v"),this._elements.push(this._inputHex),this._elements.push(this._inputR,this._inputG,this._inputB),this._elements.push(this._inputH,this._inputS,this._inputV),this._color=r("white"),this.options.color&&this.setColor(this.options.color),this._elColorBoxOrig.style.backgroundColor=this._color.hex(),this.updateColorField(),this._inputR.addEventListener("input",t=>{this._setColorFromRgbInputs()}),this._inputG.addEventListener("input",t=>{this._setColorFromRgbInputs()}),this._inputB.addEventListener("input",t=>{this._setColorFromRgbInputs()}),this._inputH.addEventListener("input",t=>{this._setColorFromHsvInputs()}),this._inputS.addEventListener("input",t=>{this._setColorFromHsvInputs()}),this._inputV.addEventListener("input",t=>{this._setColorFromHsvInputs()}),this._inputHex.addEventListener("input",t=>{this.validateHexInput()&&this.setColor(this.validateHexInput())}),this._inputHex.addEventListener("blur",t=>{this.validateHexInput()||(this._inputHex.classList.remove("colorRick_invalid"),this._inputHex.value=this._color.hex())}),this._elHue.addEventListener("pointerdown",this._onHueMouse.bind(this)),this._elHue.addEventListener("pointermove",this._onHueMouse.bind(this)),this._elHue.addEventListener("wheel",t=>{t.deltaY>0?this._hue-=.2:this._hue+=.2,this.updateColorField(),t.preventDefault()}),this.options.ele){const t=this.options.ele.getBoundingClientRect(),e=30,n=this._elContainer.getBoundingClientRect(),r=t.x+e>window.innerWidth-n.width;this._elContainer.style.left=r?t.x-n.width+"px":t.x+e+"px",t.y+n.height>window.innerHeight?this._elContainer.style.top=window.innerHeight-n.height-10+"px":this._elContainer.style.top=t.y+"px"}this._elHue.addEventListener("pointerdown",t=>{this._elHue.setPointerCapture(t.pointerId)}),this._elHue.addEventListener("pointerup",t=>{this._elHue.releasePointerCapture(t.pointerId)}),this._elArea.addEventListener("pointerdown",this._onAreaMouse.bind(this)),this._elArea.addEventListener("pointermove",this._onAreaMouse.bind(this)),this._elArea.addEventListener("wheel",t=>{t.deltaY>0?this._hueV-=.001:this._hueV+=.001,this.updateColorField(),t.preventDefault()}),this._elArea.addEventListener("pointerdown",t=>{this._elArea.setPointerCapture(t.pointerId)}),this._elArea.addEventListener("pointerup",t=>{this._elArea.releasePointerCapture(t.pointerId)}),document.addEventListener("pointerdown",this._clickOutside.bind(this)),this._elColorBoxOrig.addEventListener("click",()=>{this.setColor(this._elColorBoxOrig.style.backgroundColor)})}_clickOutside(t){if(this._elContainer.contains(t.target))return!0;this.close()}validateHexInput(){let t=this._inputHex.value;if(-1==t.indexOf("#")&&(t="#"+t),7==t.length)return this._inputHex.classList.remove("colorRick_invalid"),t;this._inputHex.classList.add("colorRick_invalid")}_setColorFromHsvInputs(){this._color=r(this._inputH.value,this._inputS.value,this._inputV.value,"hsv"),this._hue=this._inputH.value,this._hueS=this._inputS.value,this._hueV=this._inputV.value,this.updateColorField()}_setColorFromRgbInputs(){this.setColor([parseInt(this._inputR.value),parseInt(this._inputG.value),parseInt(this._inputB.value)])}setColor(t){this._color=r(t),this._hue=this._color.hsv()[0],this._hueS=this._color.hsv()[1],this._hueV=this._color.hsv()[2],this._inputH.value=this._color.hsv()[0],this._inputS.value=this._color.hsv()[1],this._inputV.value=this._color.hsv()[2],this.updateColorField()}updateCursors(){this._color.luminance()>.55?this._elAreaCursor.style.backgroundColor="black":this._elAreaCursor.style.backgroundColor="white",this._elHueCursor.style.top=this._areaHeight-this._hue/360*this._areaHeight+"px",this._elAreaCursor.style.marginLeft=this._color.hsv()[1]*this._areaWidth-3+"px",this._elAreaCursor.style.marginTop=this._areaHeight-this._color.hsv()[2]*this._areaHeight-3+"px"}updateColorField(){this._hue!=this._hue&&(this._hue=0),this._hue<0&&(this._hue=0),this._hue>360&&(this._hue=360),this._hueV!=this._hueV&&(this._hueV=1e-4),this._hueV<0&&(this._hueV=1e-4),this._hueV>1&&(this._hueV=1),this._hueS!=this._hueS&&(this._hueS=1e-4),this._hueS<0&&(this._hueS=1e-4),this._hueS>1&&(this._hueS=1),this._color=r(this._hue,this._hueS,this._hueV,"hsv"),this.options.onChange&&this._currentHex!=this._color.hex()&&this.options.onChange(this._color),this._currentHex=this._color.hex();const t=r(this._hue,1,1,"hsv").rgb();this._elArea.style.background="linear-gradient(to right, rgb(255, 255, 255), rgb("+t[0]+", "+t[1]+", "+t[2]+"))",this._elColorBox.style.backgroundColor=this._color.hex(),this._inputHex.value=this._color.hex(),this._inputR.value=this._color.rgb()[0],this._inputG.value=this._color.rgb()[1],this._inputB.value=this._color.rgb()[2],this.updateCursors()}close(){document.removeEventListener("pointerdown",this._clickOutside.bind(this));for(let t=0;tn?n:t},e={},n=0,r=["Boolean","Number","String","Function","Array","Date","RegExp","Undefined","Null"];n255)&&(e._clipped=!0),e[n]=t(e[n],0,255)):3===n&&(e[n]=t(e[n],0,1));return e},limit:t,type:o,unpack:function(t,e){return void 0===e&&(e=null),t.length>=3?Array.prototype.slice.call(t):"object"==o(t[0])&&e?e.split("").filter((function(e){return void 0!==t[0][e]})).map((function(e){return t[0][e]})):t[0]},last:function(t){if(t.length<2)return null;var e=t.length-1;return"string"==o(t[e])?t[e].toLowerCase():null},PI:s,TWOPI:2*s,PITHIRD:s/3,DEG2RAD:s/180,RAD2DEG:180/s},l={format:{},autodetect:[]},u=a.last,h=a.clip_rgb,c=a.type,f=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=this;if("object"===c(t[0])&&t[0].constructor&&t[0].constructor===this.constructor)return t[0];var r=u(t),i=!1;if(!r){i=!0,l.sorted||(l.autodetect=l.autodetect.sort((function(t,e){return e.p-t.p})),l.sorted=!0);for(var o=0,s=l.autodetect;o4?t[4]:1;return 1===o?[0,0,0,s]:[n>=1?0:255*(1-n)*(1-o),r>=1?0:255*(1-r)*(1-o),i>=1?0:255*(1-i)*(1-o),s]},l.autodetect.push({p:2,test:function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(t=y(t,"cmyk"),"array"===_(t)&&4===t.length)return"cmyk"}});var k=a.unpack,w=a.last,S=function(t){return Math.round(100*t)/100},x=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=k(t,"hsla"),r=w(t)||"lsa";return n[0]=S(n[0]||0),n[1]=S(100*n[1])+"%",n[2]=S(100*n[2])+"%","hsla"===r||n.length>3&&n[3]<1?(n[3]=n.length>3?n[3]:1,r="hsla"):n.length=3,r+"("+n.join(",")+")"},M=a.unpack,C=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=(t=M(t,"rgba"))[0],r=t[1],i=t[2];n/=255,r/=255,i/=255;var o,s,a=Math.min(n,r,i),l=Math.max(n,r,i),u=(l+a)/2;return l===a?(o=0,s=Number.NaN):o=u<.5?(l-a)/(l+a):(l-a)/(2-l-a),n==l?s=(r-i)/(l-a):r==l?s=2+(i-n)/(l-a):i==l&&(s=4+(n-r)/(l-a)),(s*=60)<0&&(s+=360),t.length>3&&void 0!==t[3]?[s,o,u,t[3]]:[s,o,u]},E=a.unpack,P=a.last,O=Math.round,D=a.unpack,I=Math.round,L=function(){for(var t,e=[],n=arguments.length;n--;)e[n]=arguments[n];var r,i,o,s=(e=D(e,"hsl"))[0],a=e[1],l=e[2];if(0===a)r=i=o=255*l;else{var u=[0,0,0],h=[0,0,0],c=l<.5?l*(1+a):l+a-l*a,f=2*l-c,p=s/360;u[0]=p+1/3,u[1]=p,u[2]=p-1/3;for(var d=0;d<3;d++)u[d]<0&&(u[d]+=1),u[d]>1&&(u[d]-=1),6*u[d]<1?h[d]=f+6*(c-f)*u[d]:2*u[d]<1?h[d]=c:3*u[d]<2?h[d]=f+(c-f)*(2/3-u[d])*6:h[d]=f;r=(t=[I(255*h[0]),I(255*h[1]),I(255*h[2])])[0],i=t[1],o=t[2]}return e.length>3?[r,i,o,e[3]]:[r,i,o,1]},T=/^rgb\(\s*(-?\d+),\s*(-?\d+)\s*,\s*(-?\d+)\s*\)$/,A=/^rgba\(\s*(-?\d+),\s*(-?\d+)\s*,\s*(-?\d+)\s*,\s*([01]|[01]?\.\d+)\)$/,B=/^rgb\(\s*(-?\d+(?:\.\d+)?)%,\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*\)$/,R=/^rgba\(\s*(-?\d+(?:\.\d+)?)%,\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*,\s*([01]|[01]?\.\d+)\)$/,N=/^hsl\(\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*\)$/,H=/^hsla\(\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*,\s*([01]|[01]?\.\d+)\)$/,F=Math.round,Y=function(t){var e;if(t=t.toLowerCase().trim(),l.format.named)try{return l.format.named(t)}catch(t){}if(e=t.match(T)){for(var n=e.slice(1,4),r=0;r<3;r++)n[r]=+n[r];return n[3]=1,n}if(e=t.match(A)){for(var i=e.slice(1,5),o=0;o<4;o++)i[o]=+i[o];return i}if(e=t.match(B)){for(var s=e.slice(1,4),a=0;a<3;a++)s[a]=F(2.55*s[a]);return s[3]=1,s}if(e=t.match(R)){for(var u=e.slice(1,5),h=0;h<3;h++)u[h]=F(2.55*u[h]);return u[3]=+u[3],u}if(e=t.match(N)){var c=e.slice(1,4);c[1]*=.01,c[2]*=.01;var f=L(c);return f[3]=1,f}if(e=t.match(H)){var p=e.slice(1,4);p[1]*=.01,p[2]*=.01;var d=L(p);return d[3]=+e[4],d}};Y.test=function(t){return T.test(t)||A.test(t)||B.test(t)||R.test(t)||N.test(t)||H.test(t)};var W=Y,$=a.type;p.prototype.css=function(t){return function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=E(t,"rgba"),r=P(t)||"rgb";return"hsl"==r.substr(0,3)?x(C(n),r):(n[0]=O(n[0]),n[1]=O(n[1]),n[2]=O(n[2]),("rgba"===r||n.length>3&&n[3]<1)&&(n[3]=n.length>3?n[3]:1,r="rgba"),r+"("+n.slice(0,"rgb"===r?3:4).join(",")+")")}(this._rgb,t)},g.css=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["css"])))},l.format.css=W,l.autodetect.push({p:5,test:function(t){for(var e=[],n=arguments.length-1;n-- >0;)e[n]=arguments[n+1];if(!e.length&&"string"===$(t)&&W.test(t))return"css"}});var j=a.unpack;l.format.gl=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=j(t,"rgba");return n[0]*=255,n[1]*=255,n[2]*=255,n},g.gl=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["gl"])))},p.prototype.gl=function(){var t=this._rgb;return[t[0]/255,t[1]/255,t[2]/255,t[3]]};var z=a.unpack,G=a.unpack,U=Math.floor,q=a.unpack,V=a.type;p.prototype.hcg=function(){return function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n,r=z(t,"rgb"),i=r[0],o=r[1],s=r[2],a=Math.min(i,o,s),l=Math.max(i,o,s),u=l-a,h=100*u/255,c=a/(255-u)*100;return 0===u?n=Number.NaN:(i===l&&(n=(o-s)/u),o===l&&(n=2+(s-i)/u),s===l&&(n=4+(i-o)/u),(n*=60)<0&&(n+=360)),[n,h,c]}(this._rgb)},g.hcg=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["hcg"])))},l.format.hcg=function(){for(var t,e,n,r,i,o,s=[],a=arguments.length;a--;)s[a]=arguments[a];var l,u,h,c=(s=G(s,"hcg"))[0],f=s[1],p=s[2];p*=255;var d=255*f;if(0===f)l=u=h=p;else{360===c&&(c=0),c>360&&(c-=360),c<0&&(c+=360);var g=U(c/=60),m=c-g,v=p*(1-f),b=v+d*(1-m),y=v+d*m,_=v+d;switch(g){case 0:l=(t=[_,y,v])[0],u=t[1],h=t[2];break;case 1:l=(e=[b,_,v])[0],u=e[1],h=e[2];break;case 2:l=(n=[v,_,y])[0],u=n[1],h=n[2];break;case 3:l=(r=[v,b,_])[0],u=r[1],h=r[2];break;case 4:l=(i=[y,v,_])[0],u=i[1],h=i[2];break;case 5:l=(o=[_,v,b])[0],u=o[1],h=o[2]}}return[l,u,h,s.length>3?s[3]:1]},l.autodetect.push({p:1,test:function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(t=q(t,"hcg"),"array"===V(t)&&3===t.length)return"hcg"}});var K=a.unpack,X=a.last,Q=Math.round,J=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=K(t,"rgba"),r=n[0],i=n[1],o=n[2],s=n[3],a=X(t)||"auto";void 0===s&&(s=1),"auto"===a&&(a=s<1?"rgba":"rgb");var l="000000"+((r=Q(r))<<16|(i=Q(i))<<8|(o=Q(o))).toString(16);l=l.substr(l.length-6);var u="0"+Q(255*s).toString(16);switch(u=u.substr(u.length-2),a.toLowerCase()){case"rgba":return"#"+l+u;case"argb":return"#"+u+l;default:return"#"+l}},Z=/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,tt=/^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/,et=function(t){if(t.match(Z)){4!==t.length&&7!==t.length||(t=t.substr(1)),3===t.length&&(t=(t=t.split(""))[0]+t[0]+t[1]+t[1]+t[2]+t[2]);var e=parseInt(t,16);return[e>>16,e>>8&255,255&e,1]}if(t.match(tt)){5!==t.length&&9!==t.length||(t=t.substr(1)),4===t.length&&(t=(t=t.split(""))[0]+t[0]+t[1]+t[1]+t[2]+t[2]+t[3]+t[3]);var n=parseInt(t,16);return[n>>24&255,n>>16&255,n>>8&255,Math.round((255&n)/255*100)/100]}throw new Error("unknown hex color: "+t)},nt=a.type;p.prototype.hex=function(t){return J(this._rgb,t)},g.hex=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["hex"])))},l.format.hex=et,l.autodetect.push({p:4,test:function(t){for(var e=[],n=arguments.length-1;n-- >0;)e[n]=arguments[n+1];if(!e.length&&"string"===nt(t)&&[3,4,5,6,7,8,9].indexOf(t.length)>=0)return"hex"}});var rt=a.unpack,it=a.TWOPI,ot=Math.min,st=Math.sqrt,at=Math.acos,lt=a.unpack,ut=a.limit,ht=a.TWOPI,ct=a.PITHIRD,ft=Math.cos,pt=a.unpack,dt=a.type;p.prototype.hsi=function(){return function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n,r=rt(t,"rgb"),i=r[0],o=r[1],s=r[2],a=ot(i/=255,o/=255,s/=255),l=(i+o+s)/3,u=l>0?1-a/l:0;return 0===u?n=NaN:(n=(i-o+(i-s))/2,n/=st((i-o)*(i-o)+(i-s)*(o-s)),n=at(n),s>o&&(n=it-n),n/=it),[360*n,u,l]}(this._rgb)},g.hsi=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["hsi"])))},l.format.hsi=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n,r,i,o=(t=lt(t,"hsi"))[0],s=t[1],a=t[2];return isNaN(o)&&(o=0),isNaN(s)&&(s=0),o>360&&(o-=360),o<0&&(o+=360),(o/=360)<1/3?r=1-((i=(1-s)/3)+(n=(1+s*ft(ht*o)/ft(ct-ht*o))/3)):o<2/3?i=1-((n=(1-s)/3)+(r=(1+s*ft(ht*(o-=1/3))/ft(ct-ht*o))/3)):n=1-((r=(1-s)/3)+(i=(1+s*ft(ht*(o-=2/3))/ft(ct-ht*o))/3)),[255*(n=ut(a*n*3)),255*(r=ut(a*r*3)),255*(i=ut(a*i*3)),t.length>3?t[3]:1]},l.autodetect.push({p:2,test:function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(t=pt(t,"hsi"),"array"===dt(t)&&3===t.length)return"hsi"}});var gt=a.unpack,mt=a.type;p.prototype.hsl=function(){return C(this._rgb)},g.hsl=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["hsl"])))},l.format.hsl=L,l.autodetect.push({p:2,test:function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(t=gt(t,"hsl"),"array"===mt(t)&&3===t.length)return"hsl"}});var vt=a.unpack,bt=Math.min,yt=Math.max,_t=a.unpack,kt=Math.floor,wt=a.unpack,St=a.type;p.prototype.hsv=function(){return function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n,r,i,o=(t=vt(t,"rgb"))[0],s=t[1],a=t[2],l=bt(o,s,a),u=yt(o,s,a),h=u-l;return i=u/255,0===u?(n=Number.NaN,r=0):(r=h/u,o===u&&(n=(s-a)/h),s===u&&(n=2+(a-o)/h),a===u&&(n=4+(o-s)/h),(n*=60)<0&&(n+=360)),[n,r,i]}(this._rgb)},g.hsv=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["hsv"])))},l.format.hsv=function(){for(var t,e,n,r,i,o,s=[],a=arguments.length;a--;)s[a]=arguments[a];var l,u,h,c=(s=_t(s,"hsv"))[0],f=s[1],p=s[2];if(p*=255,0===f)l=u=h=p;else{360===c&&(c=0),c>360&&(c-=360),c<0&&(c+=360);var d=kt(c/=60),g=c-d,m=p*(1-f),v=p*(1-f*g),b=p*(1-f*(1-g));switch(d){case 0:l=(t=[p,b,m])[0],u=t[1],h=t[2];break;case 1:l=(e=[v,p,m])[0],u=e[1],h=e[2];break;case 2:l=(n=[m,p,b])[0],u=n[1],h=n[2];break;case 3:l=(r=[m,v,p])[0],u=r[1],h=r[2];break;case 4:l=(i=[b,m,p])[0],u=i[1],h=i[2];break;case 5:l=(o=[p,m,v])[0],u=o[1],h=o[2]}}return[l,u,h,s.length>3?s[3]:1]},l.autodetect.push({p:2,test:function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(t=wt(t,"hsv"),"array"===St(t)&&3===t.length)return"hsv"}});var xt=.95047,Mt=1.08883,Ct=.137931034,Et=.12841855,Pt=a.unpack,Ot=Math.pow,Dt=function(t){return(t/=255)<=.04045?t/12.92:Ot((t+.055)/1.055,2.4)},It=function(t){return t>.008856452?Ot(t,1/3):t/Et+Ct},Lt=function(t,e,n){return t=Dt(t),e=Dt(e),n=Dt(n),[It((.4124564*t+.3575761*e+.1804375*n)/xt),It((.2126729*t+.7151522*e+.072175*n)/1),It((.0193339*t+.119192*e+.9503041*n)/Mt)]},Tt=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=Pt(t,"rgb"),r=n[0],i=n[1],o=n[2],s=Lt(r,i,o),a=s[0],l=s[1],u=116*l-16;return[u<0?0:u,500*(a-l),200*(l-s[2])]},At=a.unpack,Bt=Math.pow,Rt=function(t){return 255*(t<=.00304?12.92*t:1.055*Bt(t,1/2.4)-.055)},Nt=function(t){return t>.206896552?t*t*t:Et*(t-Ct)},Ht=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n,r,i,o=(t=At(t,"lab"))[0],s=t[1],a=t[2];return r=(o+16)/116,n=isNaN(s)?r:r+s/500,i=isNaN(a)?r:r-a/200,r=1*Nt(r),n=xt*Nt(n),i=Mt*Nt(i),[Rt(3.2404542*n-1.5371385*r-.4985314*i),Rt(-.969266*n+1.8760108*r+.041556*i),Rt(.0556434*n-.2040259*r+1.0572252*i),t.length>3?t[3]:1]},Ft=a.unpack,Yt=a.type;p.prototype.lab=function(){return Tt(this._rgb)},g.lab=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["lab"])))},l.format.lab=Ht,l.autodetect.push({p:2,test:function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(t=Ft(t,"lab"),"array"===Yt(t)&&3===t.length)return"lab"}});var Wt=a.unpack,$t=a.RAD2DEG,jt=Math.sqrt,zt=Math.atan2,Gt=Math.round,Ut=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=Wt(t,"lab"),r=n[0],i=n[1],o=n[2],s=jt(i*i+o*o),a=(zt(o,i)*$t+360)%360;return 0===Gt(1e4*s)&&(a=Number.NaN),[r,s,a]},qt=a.unpack,Vt=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=qt(t,"rgb"),r=n[0],i=n[1],o=n[2],s=Tt(r,i,o),a=s[0],l=s[1],u=s[2];return Ut(a,l,u)},Kt=a.unpack,Xt=a.DEG2RAD,Qt=Math.sin,Jt=Math.cos,Zt=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=Kt(t,"lch"),r=n[0],i=n[1],o=n[2];return isNaN(o)&&(o=0),[r,Jt(o*=Xt)*i,Qt(o)*i]},te=a.unpack,ee=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=(t=te(t,"lch"))[0],r=t[1],i=t[2],o=Zt(n,r,i),s=o[0],a=o[1],l=o[2],u=Ht(s,a,l);return[u[0],u[1],u[2],t.length>3?t[3]:1]},ne=a.unpack,re=a.unpack,ie=a.type;p.prototype.lch=function(){return Vt(this._rgb)},p.prototype.hcl=function(){return Vt(this._rgb).reverse()},g.lch=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["lch"])))},g.hcl=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["hcl"])))},l.format.lch=ee,l.format.hcl=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=ne(t,"hcl").reverse();return ee.apply(void 0,n)},["lch","hcl"].forEach((function(t){return l.autodetect.push({p:2,test:function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];if(e=re(e,t),"array"===ie(e)&&3===e.length)return t}})}));var oe={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflower:"#6495ed",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",laserlemon:"#ffff54",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrod:"#fafad2",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",maroon2:"#7f0000",maroon3:"#b03060",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",purple2:"#7f007f",purple3:"#a020f0",rebeccapurple:"#663399",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},se=a.type;p.prototype.name=function(){for(var t=J(this._rgb,"rgb"),e=0,n=Object.keys(oe);e0;)e[n]=arguments[n+1];if(!e.length&&"string"===se(t)&&oe[t.toLowerCase()])return"named"}});var ae=a.unpack,le=a.type,ue=a.type;p.prototype.num=function(){return function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=ae(t,"rgb");return(n[0]<<16)+(n[1]<<8)+n[2]}(this._rgb)},g.num=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["num"])))},l.format.num=function(t){if("number"==le(t)&&t>=0&&t<=16777215)return[t>>16,t>>8&255,255&t,1];throw new Error("unknown num color: "+t)},l.autodetect.push({p:5,test:function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(1===t.length&&"number"===ue(t[0])&&t[0]>=0&&t[0]<=16777215)return"num"}});var he=a.unpack,ce=a.type,fe=Math.round;p.prototype.rgb=function(t){return void 0===t&&(t=!0),!1===t?this._rgb.slice(0,3):this._rgb.slice(0,3).map(fe)},p.prototype.rgba=function(t){return void 0===t&&(t=!0),this._rgb.slice(0,4).map((function(e,n){return n<3?!1===t?e:fe(e):e}))},g.rgb=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["rgb"])))},l.format.rgb=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n=he(t,"rgba");return void 0===n[3]&&(n[3]=1),n},l.autodetect.push({p:3,test:function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(t=he(t,"rgba"),"array"===ce(t)&&(3===t.length||4===t.length&&"number"==ce(t[3])&&t[3]>=0&&t[3]<=1))return"rgb"}});var pe=Math.log,de=function(t){var e,n,r,i=t/100;return i<66?(e=255,n=-155.25485562709179-.44596950469579133*(n=i-2)+104.49216199393888*pe(n),r=i<20?0:.8274096064007395*(r=i-10)-254.76935184120902+115.67994401066147*pe(r)):(e=351.97690566805693+.114206453784165*(e=i-55)-40.25366309332127*pe(e),n=325.4494125711974+.07943456536662342*(n=i-50)-28.0852963507957*pe(n),r=255),[e,n,r,1]},ge=a.unpack,me=Math.round;p.prototype.temp=p.prototype.kelvin=p.prototype.temperature=function(){return function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];for(var n,r=ge(t,"rgb"),i=r[0],o=r[2],s=1e3,a=4e4,l=.4;a-s>l;){var u=de(n=.5*(a+s));u[2]/u[0]>=o/i?a=n:s=n}return me(n)}(this._rgb)},g.temp=g.kelvin=g.temperature=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return new(Function.prototype.bind.apply(p,[null].concat(t,["temp"])))},l.format.temp=l.format.kelvin=l.format.temperature=de;var ve=a.type;p.prototype.alpha=function(t,e){return void 0===e&&(e=!1),void 0!==t&&"number"===ve(t)?e?(this._rgb[3]=t,this):new p([this._rgb[0],this._rgb[1],this._rgb[2],t],"rgb"):this._rgb[3]},p.prototype.clipped=function(){return this._rgb._clipped||!1},p.prototype.darken=function(t){void 0===t&&(t=1);var e=this.lab();return e[0]-=18*t,new p(e,"lab").alpha(this.alpha(),!0)},p.prototype.brighten=function(t){return void 0===t&&(t=1),this.darken(-t)},p.prototype.darker=p.prototype.darken,p.prototype.brighter=p.prototype.brighten,p.prototype.get=function(t){var e=t.split("."),n=e[0],r=e[1],i=this[n]();if(r){var o=n.indexOf(r);if(o>-1)return i[o];throw new Error("unknown channel "+r+" in mode "+n)}return i};var be=a.type,ye=Math.pow;p.prototype.luminance=function(t){if(void 0!==t&&"number"===be(t)){if(0===t)return new p([0,0,0,this._rgb[3]],"rgb");if(1===t)return new p([255,255,255,this._rgb[3]],"rgb");var e=this.luminance(),n=20,r=function(e,i){var o=e.interpolate(i,.5,"rgb"),s=o.luminance();return Math.abs(t-s)<1e-7||!n--?o:s>t?r(e,o):r(o,i)},i=(e>t?r(new p([0,0,0]),this):r(this,new p([255,255,255]))).rgb();return new p(i.concat([this._rgb[3]]))}return _e.apply(void 0,this._rgb.slice(0,3))};var _e=function(t,e,n){return.2126*(t=ke(t))+.7152*(e=ke(e))+.0722*ke(n)},ke=function(t){return(t/=255)<=.03928?t/12.92:ye((t+.055)/1.055,2.4)},we={},Se=a.type,xe=function(t,e,n){void 0===n&&(n=.5);for(var r=[],i=arguments.length-3;i-- >0;)r[i]=arguments[i+3];var o=r[0]||"lrgb";if(we[o]||r.length||(o=Object.keys(we)[0]),!we[o])throw new Error("interpolation mode "+o+" is not defined");return"object"!==Se(t)&&(t=new p(t)),"object"!==Se(e)&&(e=new p(e)),we[o](t,e,n).alpha(t.alpha()+n*(e.alpha()-t.alpha()))};p.prototype.mix=p.prototype.interpolate=function(t,e){void 0===e&&(e=.5);for(var n=[],r=arguments.length-2;r-- >0;)n[r]=arguments[r+2];return xe.apply(void 0,[this,t,e].concat(n))},p.prototype.premultiply=function(t){void 0===t&&(t=!1);var e=this._rgb,n=e[3];return t?(this._rgb=[e[0]*n,e[1]*n,e[2]*n,n],this):new p([e[0]*n,e[1]*n,e[2]*n,n],"rgb")},p.prototype.saturate=function(t){void 0===t&&(t=1);var e=this.lch();return e[1]+=18*t,e[1]<0&&(e[1]=0),new p(e,"lch").alpha(this.alpha(),!0)},p.prototype.desaturate=function(t){return void 0===t&&(t=1),this.saturate(-t)};var Me=a.type;p.prototype.set=function(t,e,n){void 0===n&&(n=!1);var r=t.split("."),i=r[0],o=r[1],s=this[i]();if(o){var a=i.indexOf(o);if(a>-1){if("string"==Me(e))switch(e.charAt(0)){case"+":case"-":s[a]+=+e;break;case"*":s[a]*=+e.substr(1);break;case"/":s[a]/=+e.substr(1);break;default:s[a]=+e}else{if("number"!==Me(e))throw new Error("unsupported value for Color.set");s[a]=e}var l=new p(s,i);return n?(this._rgb=l._rgb,this):l}throw new Error("unknown channel "+o+" in mode "+i)}return s},we.rgb=function(t,e,n){var r=t._rgb,i=e._rgb;return new p(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),"rgb")};var Ce=Math.sqrt,Ee=Math.pow;we.lrgb=function(t,e,n){var r=t._rgb,i=r[0],o=r[1],s=r[2],a=e._rgb,l=a[0],u=a[1],h=a[2];return new p(Ce(Ee(i,2)*(1-n)+Ee(l,2)*n),Ce(Ee(o,2)*(1-n)+Ee(u,2)*n),Ce(Ee(s,2)*(1-n)+Ee(h,2)*n),"rgb")},we.lab=function(t,e,n){var r=t.lab(),i=e.lab();return new p(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),"lab")};var Pe=function(t,e,n,r){var i,o,s,a,l,u,h,c,f,d,g,m;return"hsl"===r?(s=t.hsl(),a=e.hsl()):"hsv"===r?(s=t.hsv(),a=e.hsv()):"hcg"===r?(s=t.hcg(),a=e.hcg()):"hsi"===r?(s=t.hsi(),a=e.hsi()):"lch"!==r&&"hcl"!==r||(r="hcl",s=t.hcl(),a=e.hcl()),"h"===r.substr(0,1)&&(l=(i=s)[0],h=i[1],f=i[2],u=(o=a)[0],c=o[1],d=o[2]),isNaN(l)||isNaN(u)?isNaN(l)?isNaN(u)?m=Number.NaN:(m=u,1!=f&&0!=f||"hsv"==r||(g=c)):(m=l,1!=d&&0!=d||"hsv"==r||(g=h)):m=l+n*(u>l&&u-l>180?u-(l+360):u180?u+360-l:u-l),void 0===g&&(g=h+n*(c-h)),new p([m,g,f+n*(d-f)],r)},Oe=function(t,e,n){return Pe(t,e,n,"lch")};we.lch=Oe,we.hcl=Oe,we.num=function(t,e,n){var r=t.num(),i=e.num();return new p(r+n*(i-r),"num")},we.hcg=function(t,e,n){return Pe(t,e,n,"hcg")},we.hsi=function(t,e,n){return Pe(t,e,n,"hsi")},we.hsl=function(t,e,n){return Pe(t,e,n,"hsl")},we.hsv=function(t,e,n){return Pe(t,e,n,"hsv")};var De=a.clip_rgb,Ie=Math.pow,Le=Math.sqrt,Te=Math.PI,Ae=Math.cos,Be=Math.sin,Re=Math.atan2,Ne=a.type,He=Math.pow,Fe=function(t){var e="rgb",n=g("#ccc"),r=0,i=[0,1],o=[],s=[0,0],a=!1,l=[],u=!1,h=0,c=1,f=!1,p={},d=!0,m=1,v=function(t){if((t=t||["#fff","#000"])&&"string"===Ne(t)&&g.brewer&&g.brewer[t.toLowerCase()]&&(t=g.brewer[t.toLowerCase()]),"array"===Ne(t)){1===t.length&&(t=[t[0],t[0]]),t=t.slice(0);for(var e=0;e2?function(t){if(null!=a){for(var e=a.length-1,n=0;n=a[n];)n++;return n-1}return 0}(t)/(a.length-2):c!==h?(t-h)/(c-h):1,u=y(u),r||(u=b(u)),1!==m&&(u=He(u,m)),u=s[0]+u*(1-s[0]-s[1]),u=Math.min(1,Math.max(0,u));var f=Math.floor(1e4*u);if(d&&p[f])i=p[f];else{if("array"===Ne(l))for(var v=0;v=_&&v===o.length-1){i=l[v];break}if(u>_&&u2){var u=t.map((function(e,n){return n/(t.length-1)})),f=t.map((function(t){return(t-h)/(c-h)}));f.every((function(t,e){return u[e]===t}))||(y=function(t){if(t<=0||t>=1)return t;for(var e=0;t>=f[e+1];)e++;var n=(t-f[e])/(f[e+1]-f[e]);return u[e]+n*(u[e+1]-u[e])})}}return i=[h,c],w},w.mode=function(t){return arguments.length?(e=t,k(),w):e},w.range=function(t,e){return v(t),w},w.out=function(t){return u=t,w},w.spread=function(t){return arguments.length?(r=t,w):r},w.correctLightness=function(t){return null==t&&(t=!0),f=t,k(),b=f?function(t){for(var e=_(0,!0).lab()[0],n=_(1,!0).lab()[0],r=e>n,i=_(t,!0).lab()[0],o=e+(n-e)*t,s=i-o,a=0,l=1,u=20;Math.abs(s)>.01&&u-- >0;)r&&(s*=-1),s<0?(a=t,t+=.5*(l-t)):(l=t,t+=.5*(a-t)),s=(i=_(t,!0).lab()[0])-o;return t}:function(t){return t},w},w.padding=function(t){return null!=t?("number"===Ne(t)&&(t=[t,t]),s=t,w):s},w.colors=function(e,n){arguments.length<2&&(n="hex");var r=[];if(0===arguments.length)r=l.slice(0);else if(1===e)r=[w(.5)];else if(e>1){var o=i[0],s=i[1]-o;r=Ye(0,e,!1).map((function(t){return w(o+t/(e-1)*s)}))}else{t=[];var u=[];if(a&&a.length>2)for(var h=1,c=a.length,f=1<=c;f?hc;f?h++:h--)u.push(.5*(a[h-1]+a[h]));else u=i;r=u.map((function(t){return w(t)}))}return g[n]&&(r=r.map((function(t){return t[n]()}))),r},w.cache=function(t){return null!=t?(d=t,w):d},w.gamma=function(t){return null!=t?(m=t,w):m},w.nodata=function(t){return null!=t?(n=g(t),w):n},w};function Ye(t,e,n){for(var r=[],i=to;i?s++:s--)r.push(s);return r}var We=function(t){var e,n,r,i,o,s,a;if(2===(t=t.map((function(t){return new p(t)}))).length)e=t.map((function(t){return t.lab()})),o=e[0],s=e[1],i=function(t){var e=[0,1,2].map((function(e){return o[e]+t*(s[e]-o[e])}));return new p(e,"lab")};else if(3===t.length)n=t.map((function(t){return t.lab()})),o=n[0],s=n[1],a=n[2],i=function(t){var e=[0,1,2].map((function(e){return(1-t)*(1-t)*o[e]+2*(1-t)*t*s[e]+t*t*a[e]}));return new p(e,"lab")};else if(4===t.length){var l;r=t.map((function(t){return t.lab()})),o=r[0],s=r[1],a=r[2],l=r[3],i=function(t){var e=[0,1,2].map((function(e){return(1-t)*(1-t)*(1-t)*o[e]+3*(1-t)*(1-t)*t*s[e]+3*(1-t)*t*t*a[e]+t*t*t*l[e]}));return new p(e,"lab")}}else if(5===t.length){var u=We(t.slice(0,3)),h=We(t.slice(2,5));i=function(t){return t<.5?u(2*t):h(2*(t-.5))}}return i},$e=function(t,e,n){if(!$e[n])throw new Error("unknown blend mode "+n);return $e[n](t,e)},je=function(t){return function(e,n){var r=g(n).rgb(),i=g(e).rgb();return g.rgb(t(r,i))}},ze=function(t){return function(e,n){var r=[];return r[0]=t(e[0],n[0]),r[1]=t(e[1],n[1]),r[2]=t(e[2],n[2]),r}};$e.normal=je(ze((function(t){return t}))),$e.multiply=je(ze((function(t,e){return t*e/255}))),$e.screen=je(ze((function(t,e){return 255*(1-(1-t/255)*(1-e/255))}))),$e.overlay=je(ze((function(t,e){return e<128?2*t*e/255:255*(1-2*(1-t/255)*(1-e/255))}))),$e.darken=je(ze((function(t,e){return t>e?e:t}))),$e.lighten=je(ze((function(t,e){return t>e?t:e}))),$e.dodge=je(ze((function(t,e){return 255===t||(t=e/255*255/(1-t/255))>255?255:t}))),$e.burn=je(ze((function(t,e){return 255*(1-(1-e/255)/(t/255))})));for(var Ge=$e,Ue=a.type,qe=a.clip_rgb,Ve=a.TWOPI,Ke=Math.pow,Xe=Math.sin,Qe=Math.cos,Je=Math.floor,Ze=Math.random,tn=Math.log,en=Math.pow,nn=Math.floor,rn=Math.abs,on=function(t,e){void 0===e&&(e=null);var n={min:Number.MAX_VALUE,max:-1*Number.MAX_VALUE,sum:0,values:[],count:0};return"object"===o(t)&&(t=Object.values(t)),t.forEach((function(t){e&&"object"===o(t)&&(t=t[e]),null==t||isNaN(t)||(n.values.push(t),n.sum+=t,tn.max&&(n.max=t),n.count+=1)})),n.domain=[n.min,n.max],n.limits=function(t,e){return sn(n,t,e)},n},sn=function(t,e,n){void 0===e&&(e="equal"),void 0===n&&(n=7),"array"==o(t)&&(t=on(t));var r=t.min,i=t.max,s=t.values.sort((function(t,e){return t-e}));if(1===n)return[r,i];var a=[];if("c"===e.substr(0,1)&&(a.push(r),a.push(i)),"e"===e.substr(0,1)){a.push(r);for(var l=1;l 0");var u=Math.LOG10E*tn(r),h=Math.LOG10E*tn(i);a.push(r);for(var c=1;c200&&(_=!1)}for(var R={},N=0;N.9999999&&(r[3]=1),new p(De(r))}(t,n);for(var o=t.shift(),s=o.get(e),a=[],l=0,u=0,h=0;h=360;)g-=360;s[d]=g}else s[d]=s[d]/a[d];return f/=r,new p(s,e).alpha(f>.99999?1:f,!0)},g.bezier=function(t){var e=We(t);return e.scale=function(){return Fe(e)},e},g.blend=Ge,g.cubehelix=function(t,e,n,r,i){void 0===t&&(t=300),void 0===e&&(e=-1.5),void 0===n&&(n=1),void 0===r&&(r=1),void 0===i&&(i=[0,1]);var o,s=0;"array"===Ue(i)?o=i[1]-i[0]:(o=0,i=[i,i]);var a=function(a){var l=Ve*((t+120)/360+e*a),u=Ke(i[0]+o*a,r),h=(0!==s?n[0]+a*s:n)*u*(1-u)/2,c=Qe(l),f=Xe(l);return g(qe([255*(u+h*(-.14861*c+1.78277*f)),255*(u+h*(-.29227*c-.90649*f)),255*(u+h*(1.97294*c)),1]))};return a.start=function(e){return null==e?t:(t=e,a)},a.rotations=function(t){return null==t?e:(e=t,a)},a.gamma=function(t){return null==t?r:(r=t,a)},a.hue=function(t){return null==t?n:("array"===Ue(n=t)?0==(s=n[1]-n[0])&&(n=n[1]):s=0,a)},a.lightness=function(t){return null==t?i:("array"===Ue(t)?(i=t,o=t[1]-t[0]):(i=[t,t],o=0),a)},a.scale=function(){return g.scale(a)},a.hue(n),a},g.mix=g.interpolate=xe,g.random=function(){for(var t="#",e=0;e<6;e++)t+="0123456789abcdef".charAt(Je(16*Ze()));return new p(t,"hex")},g.scale=Fe,g.analyze=an.analyze,g.contrast=function(t,e){t=new p(t),e=new p(e);var n=t.luminance(),r=e.luminance();return n>r?(n+.05)/(r+.05):(r+.05)/(n+.05)},g.deltaE=function(t,e,n,r){void 0===n&&(n=1),void 0===r&&(r=1),t=new p(t),e=new p(e);for(var i=Array.from(t.lab()),o=i[0],s=i[1],a=i[2],l=Array.from(e.lab()),u=l[0],h=l[1],c=l[2],f=ln(s*s+a*a),d=ln(h*h+c*c),g=o<16?.511:.040975*o/(1+.01765*o),m=.0638*f/(1+.0131*f)+.638,v=f<1e-6?0:180*un(a,s)/fn;v<0;)v+=360;for(;v>=360;)v-=360;var b=v>=164&&v<=345?.56+hn(.2*cn(fn*(v+168)/180)):.36+hn(.4*cn(fn*(v+35)/180)),y=f*f*f*f,_=ln(y/(y+1900)),k=m*(_*b+1-_),w=f-d,S=s-h,x=a-c,M=(o-u)/(n*g),C=w/(r*m);return ln(M*M+C*C+(S*S+x*x-w*w)/(k*k))},g.distance=function(t,e,n){void 0===n&&(n="lab"),t=new p(t),e=new p(e);var r=t.get(n),i=e.get(n),o=0;for(var s in r){var a=(r[s]||0)-(i[s]||0);o+=a*a}return Math.sqrt(o)},g.limits=an.limits,g.valid=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];try{return new(Function.prototype.bind.apply(p,[null].concat(t))),!0}catch(t){return!1}},g.scales=pn,g.colors=oe,g.brewer=bn,g}()}},e={},function n(r){var i=e[r];if(void 0!==i)return i.exports;var o=e[r]={exports:{}};return t[r].call(o.exports,o,o.exports,n),o.exports}(10);var t,e})), +/**! + + @license + handlebars v4.5.3 + +Copyright (C) 2011-2017 by Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Handlebars=e():t.Handlebars=e()}(this,(function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={exports:{},id:r,loaded:!1};return t[r].call(i.exports,i,i.exports,n),i.loaded=!0,i.exports}return n.m=t,n.c=e,n.p="",n(0)}([function(t,e,n){"use strict";var r=n(1).default;e.__esModule=!0;var i=r(n(2)),o=r(n(40)),s=n(41),a=n(46),l=r(n(49)),u=r(n(44)),h=r(n(39)),c=i.default.create;function f(){var t=c();return t.compile=function(e,n){return a.compile(e,n,t)},t.precompile=function(e,n){return a.precompile(e,n,t)},t.AST=o.default,t.Compiler=a.Compiler,t.JavaScriptCompiler=l.default,t.Parser=s.parser,t.parse=s.parse,t.parseWithoutProcessing=s.parseWithoutProcessing,t}var p=f();p.create=f,h.default(p),p.Visitor=u.default,p.default=p,e.default=p,t.exports=e.default},function(t,e){"use strict";e.default=function(t){return t&&t.__esModule?t:{default:t}},e.__esModule=!0},function(t,e,n){"use strict";var r=n(3).default,i=n(1).default;e.__esModule=!0;var o=r(n(4)),s=i(n(33)),a=i(n(6)),l=r(n(5)),u=r(n(34)),h=i(n(39));function c(){var t=new o.HandlebarsEnvironment;return l.extend(t,o),t.SafeString=s.default,t.Exception=a.default,t.Utils=l,t.escapeExpression=l.escapeExpression,t.VM=u,t.template=function(e){return u.template(e,t)},t}var f=c();f.create=c,h.default(f),f.default=f,e.default=f,t.exports=e.default},function(t,e){"use strict";e.default=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e.default=t,e},e.__esModule=!0},function(t,e,n){"use strict";var r=n(1).default;e.__esModule=!0,e.HandlebarsEnvironment=u;var i=n(5),o=r(n(6)),s=n(10),a=n(30),l=r(n(32));e.VERSION="4.5.3";e.COMPILER_REVISION=8;e.LAST_COMPATIBLE_COMPILER_REVISION=7;e.REVISION_CHANGES={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1",7:">= 4.0.0 <4.3.0",8:">= 4.3.0"};function u(t,e,n){this.helpers=t||{},this.partials=e||{},this.decorators=n||{},s.registerDefaultHelpers(this),a.registerDefaultDecorators(this)}u.prototype={constructor:u,logger:l.default,log:l.default.log,registerHelper:function(t,e){if("[object Object]"===i.toString.call(t)){if(e)throw new o.default("Arg not supported with multiple helpers");i.extend(this.helpers,t)}else this.helpers[t]=e},unregisterHelper:function(t){delete this.helpers[t]},registerPartial:function(t,e){if("[object Object]"===i.toString.call(t))i.extend(this.partials,t);else{if(void 0===e)throw new o.default('Attempting to register a partial called "'+t+'" as undefined');this.partials[t]=e}},unregisterPartial:function(t){delete this.partials[t]},registerDecorator:function(t,e){if("[object Object]"===i.toString.call(t)){if(e)throw new o.default("Arg not supported with multiple decorators");i.extend(this.decorators,t)}else this.decorators[t]=e},unregisterDecorator:function(t){delete this.decorators[t]}};var h=l.default.log;e.log=h,e.createFrame=i.createFrame,e.logger=l.default},function(t,e){"use strict";e.__esModule=!0,e.extend=s,e.indexOf=function(t,e){for(var n=0,r=t.length;n":">",'"':""","'":"'","`":"`","=":"="},r=/[&<>"'`=]/g,i=/[&<>"'`=]/;function o(t){return n[t]}function s(t){for(var e=1;e0?(n.ids&&(n.ids=[n.name]),t.helpers.each(e,n)):i(this);if(n.data&&n.ids){var s=r.createFrame(n.data);s.contextPath=r.appendContextPath(n.data.contextPath,n.name),n={data:s}}return o(e,n)}))},t.exports=e.default},function(t,e,n){(function(r){"use strict";var i=n(13).default,o=n(1).default;e.__esModule=!0;var s=n(5),a=o(n(6));e.default=function(t){t.registerHelper("each",(function(t,e){if(!e)throw new a.default("Must pass iterator to #each");var n,o=e.fn,l=e.inverse,u=0,h="",c=void 0,f=void 0;function p(e,n,r){c&&(c.key=e,c.index=n,c.first=0===n,c.last=!!r,f&&(c.contextPath=f+e)),h+=o(t[e],{data:c,blockParams:s.blockParams([t[e],e],[f+e,null])})}if(e.data&&e.ids&&(f=s.appendContextPath(e.data.contextPath,e.ids[0])+"."),s.isFunction(t)&&(t=t.call(this)),e.data&&(c=s.createFrame(e.data)),t&&"object"==typeof t)if(s.isArray(t))for(var d=t.length;u=0?e:parseInt(t,10)}return t},log:function(t){if(t=i.lookupLevel(t),"undefined"!=typeof console&&i.lookupLevel(i.level)<=t){var e=i.methodMap[t];console[e]||(e="log");for(var n=arguments.length,r=Array(n>1?n-1:0),o=1;o=l.LAST_COMPATIBLE_COMPILER_REVISION&&e<=l.COMPILER_REVISION)return;if(e2&&_.push("'"+this.terminals_[v]+"'");S=this.lexer.showPosition?"Parse error on line "+(a+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+_.join(", ")+", got '"+(this.terminals_[f]||f)+"'":"Parse error on line "+(a+1)+": Unexpected "+(1==f?"end of input":"'"+(this.terminals_[f]||f)+"'"),this.parseError(S,{text:this.lexer.match,token:this.terminals_[f]||f,line:this.lexer.yylineno,loc:h,expected:_})}}if(g[0]instanceof Array&&g.length>1)throw new Error("Parse Error: multiple actions possible at state: "+d+", token: "+f);switch(g[0]){case 1:n.push(f),r.push(this.lexer.yytext),i.push(this.lexer.yylloc),n.push(g[1]),f=null,p?(f=p,p=null):(l=this.lexer.yyleng,s=this.lexer.yytext,a=this.lexer.yylineno,h=this.lexer.yylloc,u>0&&u--);break;case 2:if(b=this.productions_[g[1]][1],w.$=r[r.length-b],w._$={first_line:i[i.length-(b||1)].first_line,last_line:i[i.length-1].last_line,first_column:i[i.length-(b||1)].first_column,last_column:i[i.length-1].last_column},c&&(w._$.range=[i[i.length-(b||1)].range[0],i[i.length-1].range[1]]),void 0!==(m=this.performAction.call(w,s,l,a,this.yy,g[1],r,i)))return m;b&&(n=n.slice(0,-1*b*2),r=r.slice(0,-1*b),i=i.slice(0,-1*b)),n.push(this.productions_[g[1]][0]),r.push(w.$),i.push(w._$),y=o[n[n.length-2]][n[n.length-1]],n.push(y);break;case 3:return!0}}return!0}},e=function(){var t={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t){return this._input=t,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e-1),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this},more:function(){return this._more=!0,this},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},next:function(){if(this.done)return this.EOF;var t,e,n,r,i;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var o=this._currentRules(),s=0;se[0].length)||(e=n,r=s,this.options.flex));s++);return e?((i=e[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=i.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:i?i[i.length-1].length-i[i.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+e[0].length},this.yytext+=e[0],this.match+=e[0],this.matches=e,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(e[0].length),this.matched+=e[0],t=this.performAction.call(this,this.yy,this,o[r],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),t||void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return void 0!==t?t:this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(t){this.begin(t)},options:{},performAction:function(t,e,n,r){function i(t,n){return e.yytext=e.yytext.substring(t,e.yyleng-n+t)}switch(n){case 0:if("\\\\"===e.yytext.slice(-2)?(i(0,1),this.begin("mu")):"\\"===e.yytext.slice(-1)?(i(0,1),this.begin("emu")):this.begin("mu"),e.yytext)return 15;break;case 1:return 15;case 2:return this.popState(),15;case 3:return this.begin("raw"),15;case 4:return this.popState(),"raw"===this.conditionStack[this.conditionStack.length-1]?15:(i(5,9),"END_RAW_BLOCK");case 5:return 15;case 6:return this.popState(),14;case 7:return 65;case 8:return 68;case 9:return 19;case 10:return this.popState(),this.begin("raw"),23;case 11:return 55;case 12:return 60;case 13:return 29;case 14:return 47;case 15:case 16:return this.popState(),44;case 17:return 34;case 18:return 39;case 19:return 51;case 20:return 48;case 21:this.unput(e.yytext),this.popState(),this.begin("com");break;case 22:return this.popState(),14;case 23:return 48;case 24:return 73;case 25:case 26:return 72;case 27:return 87;case 28:break;case 29:return this.popState(),54;case 30:return this.popState(),33;case 31:return e.yytext=i(1,2).replace(/\\"/g,'"'),80;case 32:return e.yytext=i(1,2).replace(/\\'/g,"'"),80;case 33:return 85;case 34:case 35:return 82;case 36:return 83;case 37:return 84;case 38:return 81;case 39:return 75;case 40:return 77;case 41:return 72;case 42:return e.yytext=e.yytext.replace(/\\([\\\]])/g,"$1"),72;case 43:return"INVALID";case 44:return 5}},rules:[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{(?=[^\/]))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]+?(?=(\{\{\{\{)))/,/^(?:[\s\S]*?--(~)?\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#>)/,/^(?:\{\{(~)?#\*?)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{(~)?!--)/,/^(?:\{\{(~)?![\s\S]*?\}\})/,/^(?:\{\{(~)?\*?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)|])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:undefined(?=([~}\s)])))/,/^(?:null(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:as\s+\|)/,/^(?:\|)/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/,/^(?:\[(\\\]|[^\]])*\])/,/^(?:.)/,/^(?:$)/],conditions:{mu:{rules:[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[6],inclusive:!1},raw:{rules:[3,4,5],inclusive:!1},INITIAL:{rules:[0,1,44],inclusive:!0}}};return t}();function n(){this.yy={}}return t.lexer=e,n.prototype=t,t.Parser=n,new n}();e.default=n,t.exports=e.default},function(t,e,n){"use strict";var r=n(1).default;e.__esModule=!0;var i=r(n(44));function o(){var t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];this.options=t}function s(t,e,n){void 0===e&&(e=t.length);var r=t[e-1],i=t[e-2];return r?"ContentStatement"===r.type?(i||!n?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(r.original):void 0:n}function a(t,e,n){void 0===e&&(e=-1);var r=t[e+1],i=t[e+2];return r?"ContentStatement"===r.type?(i||!n?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(r.original):void 0:n}function l(t,e,n){var r=t[null==e?0:e+1];if(r&&"ContentStatement"===r.type&&(n||!r.rightStripped)){var i=r.value;r.value=r.value.replace(n?/^\s+/:/^[ \t]*\r?\n?/,""),r.rightStripped=r.value!==i}}function u(t,e,n){var r=t[null==e?t.length-1:e-1];if(r&&"ContentStatement"===r.type&&(n||!r.leftStripped)){var i=r.value;return r.value=r.value.replace(n?/\s+$/:/[ \t]+$/,""),r.leftStripped=r.value!==i,r.leftStripped}}o.prototype=new i.default,o.prototype.Program=function(t){var e=!this.options.ignoreStandalone,n=!this.isRootSeen;this.isRootSeen=!0;for(var r=t.body,i=0,o=r.length;i0)throw new i.default("Invalid path: "+r,{loc:n});".."===u&&s++}}return{type:"PathExpression",data:t,depth:s,parts:o,original:r,loc:n}},e.prepareMustache=function(t,e,n,r,i,o){var s=r.charAt(3)||r.charAt(2),a="{"!==s&&"&"!==s;return{type:/\*/.test(r)?"Decorator":"MustacheStatement",path:t,params:e,hash:n,escaped:a,strip:i,loc:this.locInfo(o)}},e.prepareRawBlock=function(t,e,n,r){o(t,n),r=this.locInfo(r);var i={type:"Program",body:e,strip:{},loc:r};return{type:"BlockStatement",path:t.path,params:t.params,hash:t.hash,program:i,openStrip:{},inverseStrip:{},closeStrip:{},loc:r}},e.prepareBlock=function(t,e,n,r,s,a){r&&r.path&&o(t,r);var l=/\*/.test(t.open);e.blockParams=t.blockParams;var u=void 0,h=void 0;if(n){if(l)throw new i.default("Unexpected inverse block on decorator",n);n.chain&&(n.program.body[0].closeStrip=r.strip),h=n.strip,u=n.program}s&&(s=u,u=e,e=s);return{type:l?"DecoratorBlock":"BlockStatement",path:t.path,params:t.params,hash:t.hash,program:e,inverse:u,openStrip:t.strip,inverseStrip:h,closeStrip:r&&r.strip,loc:this.locInfo(a)}},e.prepareProgram=function(t,e){if(!e&&t.length){var n=t[0].loc,r=t[t.length-1].loc;n&&r&&(e={source:n.source,start:{line:n.start.line,column:n.start.column},end:{line:r.end.line,column:r.end.column}})}return{type:"Program",body:t,strip:{},loc:e}},e.preparePartialBlock=function(t,e,n,r){return o(t,n),{type:"PartialBlockStatement",name:t.path,params:t.params,hash:t.hash,program:e,openStrip:t.strip,closeStrip:n&&n.strip,loc:this.locInfo(r)}};var i=r(n(6));function o(t,e){if(e=e.path?e.path.original:e,t.path.original!==e){var n={loc:t.path.loc};throw new i.default(t.path.original+" doesn't match "+e,n)}}},function(t,e,n){"use strict";var r=n(47).default,i=n(1).default;e.__esModule=!0,e.Compiler=u,e.precompile=function(t,e,n){if(null==t||"string"!=typeof t&&"Program"!==t.type)throw new o.default("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+t);"data"in(e=e||{})||(e.data=!0);e.compat&&(e.useDepths=!0);var r=n.parse(t,e),i=(new n.Compiler).compile(r,e);return(new n.JavaScriptCompiler).compile(i,e)},e.compile=function(t,e,n){void 0===e&&(e={});if(null==t||"string"!=typeof t&&"Program"!==t.type)throw new o.default("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+t);"data"in(e=s.extend({},e))||(e.data=!0);e.compat&&(e.useDepths=!0);var r=void 0;function i(){var r=n.parse(t,e),i=(new n.Compiler).compile(r,e),o=(new n.JavaScriptCompiler).compile(i,e,void 0,!0);return n.template(o)}function a(t,e){return r||(r=i()),r.call(this,t,e)}return a._setup=function(t){return r||(r=i()),r._setup(t)},a._child=function(t,e,n,o){return r||(r=i()),r._child(t,e,n,o)},a};var o=i(n(6)),s=n(5),a=i(n(40)),l=[].slice;function u(){}function h(t,e){if(t===e)return!0;if(s.isArray(t)&&s.isArray(e)&&t.length===e.length){for(var n=0;n1)throw new o.default("Unsupported number of partial arguments: "+n.length,t);n.length||(this.options.explicitPartialContext?this.opcode("pushLiteral","undefined"):n.push({type:"PathExpression",parts:[],depth:0}));var r=t.name.original,i="SubExpression"===t.name.type;i&&this.accept(t.name),this.setupFullMustacheParams(t,e,void 0,!0);var s=t.indent||"";this.options.preventIndent&&s&&(this.opcode("appendContent",s),s=""),this.opcode("invokePartial",i,r,s),this.opcode("append")},PartialBlockStatement:function(t){this.PartialStatement(t)},MustacheStatement:function(t){this.SubExpression(t),t.escaped&&!this.options.noEscape?this.opcode("appendEscaped"):this.opcode("append")},Decorator:function(t){this.DecoratorBlock(t)},ContentStatement:function(t){t.value&&this.opcode("appendContent",t.value)},CommentStatement:function(){},SubExpression:function(t){c(t);var e=this.classifySexpr(t);"simple"===e?this.simpleSexpr(t):"helper"===e?this.helperSexpr(t):this.ambiguousSexpr(t)},ambiguousSexpr:function(t,e,n){var r=t.path,i=r.parts[0],o=null!=e||null!=n;this.opcode("getContext",r.depth),this.opcode("pushProgram",e),this.opcode("pushProgram",n),r.strict=!0,this.accept(r),this.opcode("invokeAmbiguous",i,o)},simpleSexpr:function(t){var e=t.path;e.strict=!0,this.accept(e),this.opcode("resolvePossibleLambda")},helperSexpr:function(t,e,n){var r=this.setupFullMustacheParams(t,e,n),i=t.path,s=i.parts[0];if(this.options.knownHelpers[s])this.opcode("invokeKnownHelper",r.length,s);else{if(this.options.knownHelpersOnly)throw new o.default("You specified knownHelpersOnly, but used the unknown helper "+s,t);i.strict=!0,i.falsy=!0,this.accept(i),this.opcode("invokeHelper",r.length,i.original,a.default.helpers.simpleId(i))}},PathExpression:function(t){this.addDepth(t.depth),this.opcode("getContext",t.depth);var e=t.parts[0],n=a.default.helpers.scopedId(t),r=!t.depth&&!n&&this.blockParamIndex(e);r?this.opcode("lookupBlockParam",r,t.parts):e?t.data?(this.options.data=!0,this.opcode("lookupData",t.depth,t.parts,t.strict)):this.opcode("lookupOnContext",t.parts,t.falsy,t.strict,n):this.opcode("pushContext")},StringLiteral:function(t){this.opcode("pushString",t.value)},NumberLiteral:function(t){this.opcode("pushLiteral",t.value)},BooleanLiteral:function(t){this.opcode("pushLiteral",t.value)},UndefinedLiteral:function(){this.opcode("pushLiteral","undefined")},NullLiteral:function(){this.opcode("pushLiteral","null")},Hash:function(t){var e=t.pairs,n=0,r=e.length;for(this.opcode("pushHash");n=0)return[e,i]}}}},function(t,e,n){t.exports={default:n(48),__esModule:!0}},function(t,e,n){var r=n(9);t.exports=function(t,e){return r.create(t,e)}},function(t,e,n){"use strict";var r=n(13).default,i=n(1).default;e.__esModule=!0;var o=n(4),s=i(n(6)),a=n(5),l=i(n(50)),u=n(28);function h(t){this.value=t}function c(){}c.prototype={nameLookup:function(t,e){return u.dangerousPropertyRegex.test(e)?["(",[this.aliasable("container.propertyIsEnumerable"),".call(",t,",",JSON.stringify(e),")"],"?",n()," : undefined)"]:n();function n(){return c.isValidJavaScriptVariableName(e)?[t,".",e]:[t,"[",JSON.stringify(e),"]"]}},depthedLookup:function(t){return[this.aliasable("container.lookup"),'(depths, "',t,'")']},compilerInfo:function(){var t=o.COMPILER_REVISION;return[t,o.REVISION_CHANGES[t]]},appendToBuffer:function(t,e,n){return a.isArray(t)||(t=[t]),t=this.source.wrap(t,e),this.environment.isSimple?["return ",t,";"]:n?["buffer += ",t,";"]:(t.appendToBuffer=!0,t)},initializeBuffer:function(){return this.quotedString("")},compile:function(t,e,n,r){this.environment=t,this.options=e,this.stringParams=this.options.stringParams,this.trackIds=this.options.trackIds,this.precompile=!r,this.name=this.environment.name,this.isChild=!!n,this.context=n||{decorators:[],programs:[],environments:[]},this.preamble(),this.stackSlot=0,this.stackVars=[],this.aliases={},this.registers={list:[]},this.hashes=[],this.compileStack=[],this.inlineStack=[],this.blockParams=[],this.compileChildren(t,e),this.useDepths=this.useDepths||t.useDepths||t.useDecorators||this.options.compat,this.useBlockParams=this.useBlockParams||t.useBlockParams;var i=t.opcodes,o=void 0,a=void 0,l=void 0,u=void 0;for(l=0,u=i.length;l0&&(n+=", "+i.join(", "));var o=0;r(this.aliases).forEach((function(t){var r=e.aliases[t];r.children&&r.referenceCount>1&&(n+=", alias"+ ++o+"="+t,r.children[0]="alias"+o)}));var s=["container","depth0","helpers","partials","data"];(this.useBlockParams||this.useDepths)&&s.push("blockParams"),this.useDepths&&s.push("depths");var a=this.mergeSource(n);return t?(s.push(a),Function.apply(this,s)):this.source.wrap(["function(",s.join(","),") {\n ",a,"}"])},mergeSource:function(t){var e=this.environment.isSimple,n=!this.forceBuffer,r=void 0,i=void 0,o=void 0,s=void 0;return this.source.each((function(t){t.appendToBuffer?(o?t.prepend(" + "):o=t,s=t):(o&&(i?o.prepend("buffer += "):r=!0,s.add(";"),o=s=void 0),i=!0,e||(n=!1))})),n?o?(o.prepend("return "),s.add(";")):i||this.source.push('return "";'):(t+=", buffer = "+(r?"":this.initializeBuffer()),o?(o.prepend("return buffer + "),s.add(";")):this.source.push("return buffer;")),t&&this.source.prepend("var "+t.substring(2)+(r?"":";\n")),this.source.merge()},blockValue:function(t){var e=this.aliasable("container.hooks.blockHelperMissing"),n=[this.contextName(0)];this.setupHelperArgs(t,0,n);var r=this.popStack();n.splice(1,0,r),this.push(this.source.functionCall(e,"call",n))},ambiguousBlockValue:function(){var t=this.aliasable("container.hooks.blockHelperMissing"),e=[this.contextName(0)];this.setupHelperArgs("",0,e,!0),this.flushInline();var n=this.topStack();e.splice(1,0,n),this.pushSource(["if (!",this.lastHelper,") { ",n," = ",this.source.functionCall(t,"call",e),"}"])},appendContent:function(t){this.pendingContent?t=this.pendingContent+t:this.pendingLocation=this.source.currentLocation,this.pendingContent=t},append:function(){if(this.isInline())this.replaceStack((function(t){return[" != null ? ",t,' : ""']})),this.pushSource(this.appendToBuffer(this.popStack()));else{var t=this.popStack();this.pushSource(["if (",t," != null) { ",this.appendToBuffer(t,void 0,!0)," }"]),this.environment.isSimple&&this.pushSource(["else { ",this.appendToBuffer("''",void 0,!0)," }"])}},appendEscaped:function(){this.pushSource(this.appendToBuffer([this.aliasable("container.escapeExpression"),"(",this.popStack(),")"]))},getContext:function(t){this.lastContext=t},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(t,e,n,r){var i=0;r||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(t[i++])),this.resolvePath("context",t,i,e,n)},lookupBlockParam:function(t,e){this.useBlockParams=!0,this.push(["blockParams[",t[0],"][",t[1],"]"]),this.resolvePath("context",e,1)},lookupData:function(t,e,n){t?this.pushStackLiteral("container.data(data, "+t+")"):this.pushStackLiteral("data"),this.resolvePath("data",e,0,!0,n)},resolvePath:function(t,e,n,r,i){var o=this;if(this.options.strict||this.options.assumeObjects)this.push(function(t,e,n,r){var i=e.popStack(),o=0,s=n.length;t&&s--;for(;othis.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var t=this.inlineStack;this.inlineStack=[];for(var e=0,n=t.length;e[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:l,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};function e(e){this.tokens=[],this.tokens.links={},this.options=e||h.defaults,this.rules=t.normal,this.options.gfm&&(this.options.tables?this.rules=t.tables:this.rules=t.gfm)}t.bullet=/(?:[*+-]|\d+\.)/,t.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/,t.item=a(t.item,"gm")(/bull/g,t.bullet)(),t.list=a(t.list)(/bull/g,t.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+t.def.source+")")(),t.blockquote=a(t.blockquote)("def",t.def)(),t._tag="(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b",t.html=a(t.html)("comment",//)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,t._tag)(),t.paragraph=a(t.paragraph)("hr",t.hr)("heading",t.heading)("lheading",t.lheading)("blockquote",t.blockquote)("tag","<"+t._tag)("def",t.def)(),t.normal=u({},t),t.gfm=u({},t.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),t.gfm.paragraph=a(t.paragraph)("(?!","(?!"+t.gfm.fences.source.replace("\\1","\\2")+"|"+t.list.source.replace("\\1","\\3")+"|")(),t.tables=u({},t.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/}),e.rules=t,e.lex=function(t,n){return new e(n).lex(t)},e.prototype.lex=function(t){return t=t.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(t,!0)},e.prototype.token=function(e,n,r){var i,o,s,a,l,u,h,c,f;for(e=e.replace(/^ +$/gm,"");e;)if((s=this.rules.newline.exec(e))&&(e=e.substring(s[0].length),s[0].length>1&&this.tokens.push({type:"space"})),s=this.rules.code.exec(e))e=e.substring(s[0].length),s=s[0].replace(/^ {4}/gm,""),this.tokens.push({type:"code",text:this.options.pedantic?s:s.replace(/\n+$/,"")});else if(s=this.rules.fences.exec(e))e=e.substring(s[0].length),this.tokens.push({type:"code",lang:s[2],text:s[3]||""});else if(s=this.rules.heading.exec(e))e=e.substring(s[0].length),this.tokens.push({type:"heading",depth:s[1].length,text:s[2]});else if(n&&(s=this.rules.nptable.exec(e))){for(e=e.substring(s[0].length),u={type:"table",header:s[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:s[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:s[3].replace(/\n$/,"").split("\n")},c=0;c ?/gm,""),this.token(s,n,!0),this.tokens.push({type:"blockquote_end"});else if(s=this.rules.list.exec(e)){for(e=e.substring(s[0].length),a=s[2],this.tokens.push({type:"list_start",ordered:a.length>1}),i=!1,f=(s=s[0].match(this.rules.item)).length,c=0;c1&&l.length>1||(e=s.slice(c+1).join("\n")+e,c=f-1)),o=i||/\n\n(?!\s*$)/.test(u),c!==f-1&&(i="\n"===u.charAt(u.length-1),o||(o=i)),this.tokens.push({type:o?"loose_item_start":"list_item_start"}),this.token(u,!1,r),this.tokens.push({type:"list_item_end"});this.tokens.push({type:"list_end"})}else if(s=this.rules.html.exec(e))e=e.substring(s[0].length),this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&("pre"===s[1]||"script"===s[1]||"style"===s[1]),text:s[0]});else if(!r&&n&&(s=this.rules.def.exec(e)))e=e.substring(s[0].length),this.tokens.links[s[1].toLowerCase()]={href:s[2],title:s[3]};else if(n&&(s=this.rules.table.exec(e))){for(e=e.substring(s[0].length),u={type:"table",header:s[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:s[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:s[3].replace(/(?: *\| *)?\n$/,"").split("\n")},c=0;c])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:l,tag:/^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:l,text:/^[\s\S]+?(?=[\\/g,">").replace(/"/g,""").replace(/'/g,"'")}function a(t,e){return t=t.source,e=e||"",function n(r,i){return r?(i=(i=i.source||i).replace(/(^|[^\[])\^/g,"$1"),t=t.replace(r,i),n):new RegExp(t,e)}}function l(){}function u(t){for(var e,n,r=1;rAn error occured:

    "+s(t.message+"",!0)+"
    ";throw t}}n._inside=/(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/,n._href=/\s*?(?:\s+['"]([\s\S]*?)['"])?\s*/,n.link=a(n.link)("inside",n._inside)("href",n._href)(),n.reflink=a(n.reflink)("inside",n._inside)(),n.normal=u({},n),n.pedantic=u({},n.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/}),n.gfm=u({},n.normal,{escape:a(n.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:a(n.text)("]|","~]|")("|","|https?://|")()}),n.breaks=u({},n.gfm,{br:a(n.br)("{2,}","*")(),text:a(n.gfm.text)("{2,}","*")()}),r.rules=n,r.output=function(t,e,n){return new r(e,n).output(t)},r.prototype.output=function(t){for(var e,n,r,i,o="";t;)if(i=this.rules.escape.exec(t))t=t.substring(i[0].length),o+=i[1];else if(i=this.rules.autolink.exec(t))t=t.substring(i[0].length),"@"===i[2]?(n=":"===i[1].charAt(6)?this.mangle(i[1].substring(7)):this.mangle(i[1]),r=this.mangle("mailto:")+n):r=n=s(i[1]),o+=this.renderer.link(r,null,n);else if(this.inLink||!(i=this.rules.url.exec(t))){if(i=this.rules.tag.exec(t))!this.inLink&&/^/i.test(i[0])&&(this.inLink=!1),t=t.substring(i[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(i[0]):s(i[0]):i[0];else if(i=this.rules.link.exec(t))t=t.substring(i[0].length),this.inLink=!0,o+=this.outputLink(i,{href:i[2],title:i[3]}),this.inLink=!1;else if((i=this.rules.reflink.exec(t))||(i=this.rules.nolink.exec(t))){if(t=t.substring(i[0].length),e=(i[2]||i[1]).replace(/\s+/g," "),!(e=this.links[e.toLowerCase()])||!e.href){o+=i[0].charAt(0),t=i[0].substring(1)+t;continue}this.inLink=!0,o+=this.outputLink(i,e),this.inLink=!1}else if(i=this.rules.strong.exec(t))t=t.substring(i[0].length),o+=this.renderer.strong(this.output(i[2]||i[1]));else if(i=this.rules.em.exec(t))t=t.substring(i[0].length),o+=this.renderer.em(this.output(i[2]||i[1]));else if(i=this.rules.code.exec(t))t=t.substring(i[0].length),o+=this.renderer.codespan(s(i[2],!0));else if(i=this.rules.br.exec(t))t=t.substring(i[0].length),o+=this.renderer.br();else if(i=this.rules.del.exec(t))t=t.substring(i[0].length),o+=this.renderer.del(this.output(i[1]));else if(i=this.rules.text.exec(t))t=t.substring(i[0].length),o+=this.renderer.text(s(this.smartypants(i[0])));else if(t)throw new Error("Infinite loop on byte: "+t.charCodeAt(0))}else t=t.substring(i[0].length),r=n=s(i[1]),o+=this.renderer.link(r,null,n);return o},r.prototype.outputLink=function(t,e){var n=s(e.href),r=e.title?s(e.title):null;return"!"!==t[0].charAt(0)?this.renderer.link(n,r,this.output(t[1])):this.renderer.image(n,r,s(t[1]))},r.prototype.smartypants=function(t){return this.options.smartypants?t.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):t},r.prototype.mangle=function(t){if(!this.options.mangle)return t;for(var e,n="",r=t.length,i=0;i.5&&(e="x"+e.toString(16)),n+="&#"+e+";";return n},i.prototype.code=function(t,e,n){if(this.options.highlight){var r=this.options.highlight(t,e);null!=r&&r!==t&&(n=!0,t=r)}return e?'
    '+(n?t:s(t,!0))+"\n
    \n":"
    "+(n?t:s(t,!0))+"\n
    "},i.prototype.blockquote=function(t){return"
    \n"+t+"
    \n"},i.prototype.html=function(t){return t},i.prototype.heading=function(t,e,n){return"'+t+"\n"},i.prototype.hr=function(){return this.options.xhtml?"
    \n":"
    \n"},i.prototype.list=function(t,e){var n=e?"ol":"ul";return"<"+n+">\n"+t+"\n"},i.prototype.listitem=function(t){return"
  • "+t+"
  • \n"},i.prototype.paragraph=function(t){return"

    "+t+"

    \n"},i.prototype.table=function(t,e){return"\n\n"+t+"\n\n"+e+"\n
    \n"},i.prototype.tablerow=function(t){return"\n"+t+"\n"},i.prototype.tablecell=function(t,e){var n=e.header?"th":"td";return(e.align?"<"+n+' style="text-align:'+e.align+'">':"<"+n+">")+t+"\n"},i.prototype.strong=function(t){return""+t+""},i.prototype.em=function(t){return""+t+""},i.prototype.codespan=function(t){return""+t+""},i.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},i.prototype.del=function(t){return""+t+""},i.prototype.link=function(t,e,n){if(this.options.sanitize){try{var r=decodeURIComponent((i=t,i.replace(/&([#\w]+);/g,(function(t,e){return"colon"===(e=e.toLowerCase())?":":"#"===e.charAt(0)?"x"===e.charAt(1)?String.fromCharCode(parseInt(e.substring(2),16)):String.fromCharCode(+e.substring(1)):""})))).replace(/[^\w:]/g,"").toLowerCase()}catch(t){return""}if(0===r.indexOf("javascript:")||0===r.indexOf("vbscript:"))return""}var i,o='
    "},i.prototype.image=function(t,e,n){var r=''+n+'":">"},i.prototype.text=function(t){return t},o.parse=function(t,e,n){return new o(e,n).parse(t)},o.prototype.parse=function(t){this.inline=new r(t.links,this.options,this.renderer),this.tokens=t.reverse();for(var e="";this.next();)e+=this.tok();return e},o.prototype.next=function(){return this.token=this.tokens.pop()},o.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},o.prototype.parseText=function(){for(var t=this.token.text;"text"===this.peek().type;)t+="\n"+this.next().text;return this.inline.output(t)},o.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var t,e,n,r,i="",o="";for(n="",t=0;t'))}(e),!t)return!0;for(var o in t)r(o);return!0},{init:function(e){var o=Object.assign({},{openTrigger:"data-micromodal-trigger"},e),n=t(document.querySelectorAll("[".concat(o.openTrigger,"]"))),r=function(e,t){var o=[];return e.forEach((function(e){var n=e.attributes[t].value;void 0===o[n]&&(o[n]=[]),o[n].push(e)})),o}(n,o.openTrigger);if(!0!==o.debugMode||!1!==s(n,r))for(var l in r){var c=r[l];o.targetModal=l,o.triggers=t(c),a=new i(o)}},show:function(e,t){var o=t||{};o.targetModal=e,!0===o.debugMode&&!1===r(e)||(a&&a.removeEventListeners(),(a=new i(o)).showModal())},close:function(e){e?a.closeModalById(e):a.closeModal()}});return"undefined"!=typeof window&&(window.MicroModal=l),l})); diff --git a/survey_dashboard/hmc_layout/static/en_files/projectops(1).js b/survey_dashboard/hmc_layout/static/en_files/projectops(1).js new file mode 100644 index 0000000..6b81b1b --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/projectops(1).js @@ -0,0 +1,4269 @@ +"use strict"; + +var CABLES=CABLES||{}; +CABLES.OPS=CABLES.OPS||{}; + +var Ops=Ops || {}; +Ops.Gl=Ops.Gl || {}; +Ops.Ui=Ops.Ui || {}; +Ops.Anim=Ops.Anim || {}; +Ops.Math=Ops.Math || {}; +Ops.Color=Ops.Color || {}; +Ops.Value=Ops.Value || {}; +Ops.String=Ops.String || {}; +Ops.Trigger=Ops.Trigger || {}; +Ops.Gl.Matrix=Ops.Gl.Matrix || {}; +Ops.Gl.Meshes=Ops.Gl.Meshes || {}; +Ops.Gl.Shader=Ops.Gl.Shader || {}; +Ops.Deprecated=Ops.Deprecated || {}; +Ops.Math.Compare=Ops.Math.Compare || {}; +Ops.Deprecated.Exp=Ops.Deprecated.Exp || {}; +Ops.Deprecated.Anim=Ops.Deprecated.Anim || {}; +Ops.Deprecated.Exp.Gl=Ops.Deprecated.Exp.Gl || {}; +Ops.Gl.TextureEffects=Ops.Gl.TextureEffects || {}; +Ops.Gl.TextureEffects.Noise=Ops.Gl.TextureEffects.Noise || {}; + + + +// ************************************************************** +// +// Ops.Gl.MainLoop +// +// ************************************************************** + +Ops.Gl.MainLoop = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + fpsLimit = op.inValue("FPS Limit", 0), + trigger = op.outTrigger("trigger"), + width = op.outNumber("width"), + height = op.outNumber("height"), + reduceFocusFPS = op.inValueBool("Reduce FPS not focussed", true), + reduceLoadingFPS = op.inValueBool("Reduce FPS loading"), + clear = op.inValueBool("Clear", true), + clearAlpha = op.inValueBool("ClearAlpha", true), + fullscreen = op.inValueBool("Fullscreen Button", false), + active = op.inValueBool("Active", true), + hdpi = op.inValueBool("Hires Displays", false), + inUnit = op.inSwitch("Pixel Unit", ["Display", "CSS"], "Display"); + +op.onAnimFrame = render; +hdpi.onChange = function () +{ + if (hdpi.get()) op.patch.cgl.pixelDensity = window.devicePixelRatio; + else op.patch.cgl.pixelDensity = 1; + + op.patch.cgl.updateSize(); + if (CABLES.UI) gui.setLayout(); + + // inUnit.setUiAttribs({ "greyout": !hdpi.get() }); + + // if (!hdpi.get())inUnit.set("CSS"); + // else inUnit.set("Display"); +}; + +active.onChange = function () +{ + op.patch.removeOnAnimFrame(op); + + if (active.get()) + { + op.setUiAttrib({ "extendTitle": "" }); + op.onAnimFrame = render; + op.patch.addOnAnimFrame(op); + op.log("adding again!"); + } + else + { + op.setUiAttrib({ "extendTitle": "Inactive" }); + } +}; + +const cgl = op.patch.cgl; +let rframes = 0; +let rframeStart = 0; + +if (!op.patch.cgl) op.uiAttr({ "error": "No webgl cgl context" }); + +const identTranslate = vec3.create(); +vec3.set(identTranslate, 0, 0, 0); +const identTranslateView = vec3.create(); +vec3.set(identTranslateView, 0, 0, -2); + +fullscreen.onChange = updateFullscreenButton; +setTimeout(updateFullscreenButton, 100); +let fsElement = null; + +let winhasFocus = true; +let winVisible = true; + +window.addEventListener("blur", () => { winhasFocus = false; }); +window.addEventListener("focus", () => { winhasFocus = true; }); +document.addEventListener("visibilitychange", () => { winVisible = !document.hidden; }); +testMultiMainloop(); + +inUnit.onChange = () => +{ + width.set(0); + height.set(0); +}; + +function getFpsLimit() +{ + if (reduceLoadingFPS.get() && op.patch.loading.getProgress() < 1.0) return 5; + + if (reduceFocusFPS.get()) + { + if (!winVisible) return 10; + if (!winhasFocus) return 30; + } + + return fpsLimit.get(); +} + +function updateFullscreenButton() +{ + function onMouseEnter() + { + if (fsElement)fsElement.style.display = "block"; + } + + function onMouseLeave() + { + if (fsElement)fsElement.style.display = "none"; + } + + op.patch.cgl.canvas.addEventListener("mouseleave", onMouseLeave); + op.patch.cgl.canvas.addEventListener("mouseenter", onMouseEnter); + + if (fullscreen.get()) + { + if (!fsElement) + { + fsElement = document.createElement("div"); + + const container = op.patch.cgl.canvas.parentElement; + if (container)container.appendChild(fsElement); + + fsElement.addEventListener("mouseenter", onMouseEnter); + fsElement.addEventListener("click", function (e) + { + if (CABLES.UI && !e.shiftKey) gui.cycleFullscreen(); + else cgl.fullScreen(); + }); + } + + fsElement.style.padding = "10px"; + fsElement.style.position = "absolute"; + fsElement.style.right = "5px"; + fsElement.style.top = "5px"; + fsElement.style.width = "20px"; + fsElement.style.height = "20px"; + fsElement.style.cursor = "pointer"; + fsElement.style["border-radius"] = "40px"; + fsElement.style.background = "#444"; + fsElement.style["z-index"] = "9999"; + fsElement.style.display = "none"; + fsElement.innerHTML = ""; + } + else + { + if (fsElement) + { + fsElement.style.display = "none"; + fsElement.remove(); + fsElement = null; + } + } +} + +op.onDelete = function () +{ + cgl.gl.clearColor(0, 0, 0, 0); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); +}; + +function render(time) +{ + if (!active.get()) return; + if (cgl.aborted || cgl.canvas.clientWidth === 0 || cgl.canvas.clientHeight === 0) return; + + op.patch.cg = cgl; + + const startTime = performance.now(); + + op.patch.config.fpsLimit = getFpsLimit(); + + if (cgl.canvasWidth == -1) + { + cgl.setCanvas(op.patch.config.glCanvasId); + return; + } + + if (cgl.canvasWidth != width.get() || cgl.canvasHeight != height.get()) + { + let div = 1; + if (inUnit.get() == "CSS")div = op.patch.cgl.pixelDensity; + + width.set(cgl.canvasWidth / div); + height.set(cgl.canvasHeight / div); + } + + if (CABLES.now() - rframeStart > 1000) + { + CGL.fpsReport = CGL.fpsReport || []; + if (op.patch.loading.getProgress() >= 1.0 && rframeStart !== 0)CGL.fpsReport.push(rframes); + rframes = 0; + rframeStart = CABLES.now(); + } + CGL.MESH.lastShader = null; + CGL.MESH.lastMesh = null; + + cgl.renderStart(cgl, identTranslate, identTranslateView); + + if (clear.get()) + { + cgl.gl.clearColor(0, 0, 0, 1); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + } + + trigger.trigger(); + + if (CGL.MESH.lastMesh)CGL.MESH.lastMesh.unBind(); + + if (CGL.Texture.previewTexture) + { + if (!CGL.Texture.texturePreviewer) CGL.Texture.texturePreviewer = new CGL.Texture.texturePreview(cgl); + CGL.Texture.texturePreviewer.render(CGL.Texture.previewTexture); + } + cgl.renderEnd(cgl); + + op.patch.cg = null; + + if (clearAlpha.get()) + { + cgl.gl.clearColor(1, 1, 1, 1); + cgl.gl.colorMask(false, false, false, true); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT); + cgl.gl.colorMask(true, true, true, true); + } + + if (!cgl.frameStore.phong)cgl.frameStore.phong = {}; + rframes++; + + op.patch.cgl.profileData.profileMainloopMs = performance.now() - startTime; +} + +function testMultiMainloop() +{ + setTimeout( + () => + { + if (op.patch.getOpsByObjName(op.name).length > 1) + { + op.setUiError("multimainloop", "there should only be one mainloop op!"); + op.patch.addEventListener("onOpDelete", testMultiMainloop); + } + else op.setUiError("multimainloop", null, 1); + }, 500); +} + + +}; + +Ops.Gl.MainLoop.prototype = new CABLES.Op(); +CABLES.OPS["b0472a1d-db16-4ba6-8787-f300fbdc77bb"]={f:Ops.Gl.MainLoop,objName:"Ops.Gl.MainLoop"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.Transform +// +// ************************************************************** + +Ops.Gl.Matrix.Transform = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + posX = op.inValue("posX", 0), + posY = op.inValue("posY", 0), + posZ = op.inValue("posZ", 0), + scale = op.inValue("scale", 1), + rotX = op.inValue("rotX", 0), + rotY = op.inValue("rotY", 0), + rotZ = op.inValue("rotZ", 0), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Rotation", [rotX, rotY, rotZ]); +op.setPortGroup("Position", [posX, posY, posZ]); +op.setPortGroup("Scale", [scale]); +op.setUiAxisPorts(posX, posY, posZ); + +const vPos = vec3.create(); +const vScale = vec3.create(); +const transMatrix = mat4.create(); +mat4.identity(transMatrix); + +let + doScale = false, + doTranslate = false, + translationChanged = true, + scaleChanged = true, + rotChanged = true; + +rotX.onChange = rotY.onChange = rotZ.onChange = setRotChanged; +posX.onChange = posY.onChange = posZ.onChange = setTranslateChanged; +scale.onChange = setScaleChanged; + +render.onTriggered = function () +{ + // if(!CGL.TextureEffect.checkOpNotInTextureEffect(op)) return; + + let updateMatrix = false; + if (translationChanged) + { + updateTranslation(); + updateMatrix = true; + } + if (scaleChanged) + { + updateScale(); + updateMatrix = true; + } + if (rotChanged) updateMatrix = true; + + if (updateMatrix) doUpdateMatrix(); + + const cg = op.patch.cgl; + cg.pushModelMatrix(); + mat4.multiply(cg.mMatrix, cg.mMatrix, transMatrix); + + trigger.trigger(); + cg.popModelMatrix(); + + if (CABLES.UI && CABLES.UI.showCanvasTransforms) gui.setTransform(op.id, posX.get(), posY.get(), posZ.get()); + + if (op.isCurrentUiOp()) + gui.setTransformGizmo( + { + "posX": posX, + "posY": posY, + "posZ": posZ, + }); +}; + +op.transform3d = function () +{ + return { "pos": [posX, posY, posZ] }; +}; + +function doUpdateMatrix() +{ + mat4.identity(transMatrix); + if (doTranslate)mat4.translate(transMatrix, transMatrix, vPos); + + if (rotX.get() !== 0)mat4.rotateX(transMatrix, transMatrix, rotX.get() * CGL.DEG2RAD); + if (rotY.get() !== 0)mat4.rotateY(transMatrix, transMatrix, rotY.get() * CGL.DEG2RAD); + if (rotZ.get() !== 0)mat4.rotateZ(transMatrix, transMatrix, rotZ.get() * CGL.DEG2RAD); + + if (doScale)mat4.scale(transMatrix, transMatrix, vScale); + rotChanged = false; +} + +function updateTranslation() +{ + doTranslate = false; + if (posX.get() !== 0.0 || posY.get() !== 0.0 || posZ.get() !== 0.0) doTranslate = true; + vec3.set(vPos, posX.get(), posY.get(), posZ.get()); + translationChanged = false; +} + +function updateScale() +{ + // doScale=false; + // if(scale.get()!==0.0) + doScale = true; + vec3.set(vScale, scale.get(), scale.get(), scale.get()); + scaleChanged = false; +} + +function setTranslateChanged() +{ + translationChanged = true; +} + +function setScaleChanged() +{ + scaleChanged = true; +} + +function setRotChanged() +{ + rotChanged = true; +} + +doUpdateMatrix(); + + +}; + +Ops.Gl.Matrix.Transform.prototype = new CABLES.Op(); +CABLES.OPS["650baeb1-db2d-4781-9af6-ab4e9d4277be"]={f:Ops.Gl.Matrix.Transform,objName:"Ops.Gl.Matrix.Transform"}; + + + + +// ************************************************************** +// +// Ops.Math.Multiply +// +// ************************************************************** + +Ops.Math.Multiply = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValueFloat("number1", 1), + number2 = op.inValueFloat("number2", 2), + result = op.outNumber("result"); + +op.setTitle("*"); + +number1.onChange = number2.onChange = update; +update(); + +function update() +{ + const n1 = number1.get(); + const n2 = number2.get(); + + result.set(n1 * n2); +} + + +}; + +Ops.Math.Multiply.prototype = new CABLES.Op(); +CABLES.OPS["1bbdae06-fbb2-489b-9bcc-36c9d65bd441"]={f:Ops.Math.Multiply,objName:"Ops.Math.Multiply"}; + + + + +// ************************************************************** +// +// Ops.Deprecated.Exp.Gl.VectorFieldArray +// +// ************************************************************** + +Ops.Deprecated.Exp.Gl.VectorFieldArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +let exe = op.inTrigger("exe"); +let geom = op.inObject("geom"); +geom.ignoreValueSerialize = true; + +let tex = op.inTexture("Texture"); + +let numColumns = op.inValue("Columns", 100); +let numRows = op.inValue("Rows", 100); +let spacingColumns = op.inValue("Spacing Columns", 1); +let spacingRows = op.inValue("Spacing Rows", 1); +let doCenter = op.inValueBool("Center", true); + +let transRotate = op.inValueBool("Rotate", true); +let transScale = op.inValueBool("Scale", false); +let transTransZ = op.inValueBool("Translate Z", false); + +transTransZ.onChange = updateTransforms; +transRotate.onChange = updateTransforms; +transScale.onChange = updateTransforms; + +function updateTransforms() +{ + if (!shader) return; + if (transRotate.get())shader.define("TRANS_ROTATE"); + else shader.removeDefine("TRANS_ROTATE"); + + if (transScale.get())shader.define("TRANS_SCALE"); + else shader.removeDefine("TRANS_SCALE"); + + if (transTransZ.get())shader.define("TRANS_TRANS_Z"); + else shader.removeDefine("TRANS_TRANS_Z"); +} + +let transformations = []; +let mod = null; +let mesh = null; +var shader = null; +let uniDoInstancing = null; +let uniSpaceX = null; +let uniSpaceY = null; +let recalc = true; + +numRows.onChange = reset; +numColumns.onChange = reset; +doCenter.onChange = reset; +spacingColumns.onChange = reset; +spacingRows.onChange = reset; + +geom.onChange = reset; +exe.onTriggered = doRender; +exe.onLinkChanged = removeModule; + +let srcHeadVert = "" + .endl() + "UNI float do_instancing;" + .endl() + "UNI sampler2D {{mod}}field;" + + .endl() + "UNI float {{mod}}spaceX;" + .endl() + "UNI float {{mod}}spaceY;" + .endl() + "UNI float {{mod}}rows;" + .endl() + "UNI float {{mod}}cols;" + + .endl() + "#ifdef INSTANCING" + .endl() + " IN mat4 instMat;" + .endl() + " OUT mat4 instModelMat;" + .endl() + "#endif" + + .endl() + "mat4 rotationMatrix(vec3 axis, float angle)" + .endl() + "{" + .endl() + " axis = normalize(axis);" + .endl() + " float s = sin(angle);" + .endl() + " float c = cos(angle);" + .endl() + " float oc = 1.0 - c;" + + .endl() + " return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0," + .endl() + " oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0," + .endl() + " oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0," + .endl() + " 0.0, 0.0, 0.0, 1.0);" + .endl() + "}"; + +let srcBodyVert = "" + .endl() + "#ifdef INSTANCING" + // .endl()+' if( do_instancing==1.0 )' + .endl() + " {" + .endl() + " instModelMat=instMat;" + .endl() + " float tx=(instModelMat[3][0]) / {{mod}}cols;" + .endl() + " float ty=(instModelMat[3][1]) / {{mod}}rows;" + .endl() + " instModelMat[3][0]*={{mod}}spaceX;" + .endl() + " instModelMat[3][1]*={{mod}}spaceY;" + .endl() + " vec4 instCol = texture2D( {{mod}}field, vec2(tx,ty) );" + + .endl() + " #ifdef TRANS_ROTATE" + .endl() + " instModelMat*=rotationMatrix(vec3(0.0,0.0,1.0),instCol.r*3.1415926535897932384626433832795*2.0);" + .endl() + " #endif" + + .endl() + " #ifdef TRANS_SCALE" + .endl() + " pos.rgb*=instCol.r;" + .endl() + " #endif" + + .endl() + " #ifdef TRANS_TRANS_Z" + .endl() + " pos.z+=instCol.r;" + .endl() + " #endif" + +// .endl()+' pos*=instCol.r;' + .endl() + " mMatrix=mMatrix * instModelMat;" + .endl() + " }" + .endl() + "#endif" + .endl(); + +function reset() +{ + recalc = true; +} + +function prepare() +{ + if (geom.get()) + { + calc(); + + let num = transformations.length; + let arrs = [].concat.apply([], transformations); + + let matrices = new Float32Array(arrs); + + mesh = new CGL.Mesh(cgl, geom.get()); + mesh.numInstances = num; + + mesh.addAttribute("instMat", matrices, 16); + + recalc = false; + } +} + +function removeModule() +{ + if (shader && mod) + { + shader.removeModule(mod); + shader = null; + } +} + +function doRender() +{ + if (recalc)prepare(); + if (mesh) + { + if (cgl.getShader() && cgl.getShader() != shader) + { + if (shader && mod) + { + shader.removeModule(mod); + shader = null; + } + + shader = cgl.getShader(); + if (!shader.hasDefine("INSTANCING")) + { + mod = shader.addModule( + { + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert + }); + + shader.define("INSTANCING"); + op.uniDoInstancing = new CGL.Uniform(shader, "f", "do_instancing", 1); + op.uniSpaceX = new CGL.Uniform(shader, "f", mod.prefix + "spaceX", spacingColumns); + op.uniSpaceY = new CGL.Uniform(shader, "f", mod.prefix + "spaceY", spacingRows); + op.uniTexture = new CGL.Uniform(shader, "t", mod.prefix + "field", 5); + op.uniCols = new CGL.Uniform(shader, "f", mod.prefix + "cols", numColumns); + op.uniRows = new CGL.Uniform(shader, "f", mod.prefix + "rows", numRows); + + updateTransforms(); + } + else + { + op.uniDoInstancing = shader.getUniform("do_instancing"); + } + } + + // if(uniSpaceX) + // { + // uniSpaceY.setValue(spacingRows.get()); + // uniSpaceX.setValue(spacingColumns.get()); + + // uniCols.setValue(numColumns.get()); + // uniRows.setValue(numRows.get()); + + // } + + if (tex.get()) + cgl.setTexture(5, tex.get().tex); + + op.uniDoInstancing.setValue(1); + mesh.render(shader); + op.uniDoInstancing.setValue(0); + } + else + { + prepare(); + } +} + +function calc() +{ + let m = mat4.create(); + let cols = Math.round(numColumns.get()); + let rows = Math.round(numRows.get()); + if (cols <= 0)cols = 1; + if (rows <= 0)rows = 1; + + let distX = spacingColumns.get(); + let distY = spacingRows.get(); + + let centerX = 0; + let centerY = 0; + if (doCenter.get()) + { + centerX = cols * (spacingColumns.get() / 2); + centerY = rows * (spacingRows.get() / 2); + } + + transformations.length = cols * rows; + + for (let x = 0; x < cols; x++) + { + for (let y = 0; y < rows; y++) + { + mat4.identity(m); + mat4.translate(m, m, [x - centerX, y - centerY, 0]); + transformations[x + y * cols] = Array.prototype.slice.call(m); + } + } + + op.log("reset", transformations.length, cols, rows); +} + + +}; + +Ops.Deprecated.Exp.Gl.VectorFieldArray.prototype = new CABLES.Op(); +CABLES.OPS["fba86dad-f37f-482e-a8fb-2abc76723ecf"]={f:Ops.Deprecated.Exp.Gl.VectorFieldArray,objName:"Ops.Deprecated.Exp.Gl.VectorFieldArray"}; + + + + +// ************************************************************** +// +// Ops.Sequence +// +// ************************************************************** + +Ops.Sequence = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + cleanup = op.inTriggerButton("Clean up connections"); + +const + exes = [], + triggers = [], + num = 16; + +let updateTimeout = null; + +exe.onTriggered = triggerAll; +cleanup.onTriggered = clean; +cleanup.setUiAttribs({ "hidePort": true }); +cleanup.setUiAttribs({ "hideParam": true }); + +for (let i = 0; i < num; i++) +{ + const p = op.outTrigger("trigger " + i); + triggers.push(p); + p.onLinkChanged = updateButton; + + if (i < num - 1) + { + let newExe = op.inTrigger("exe " + i); + newExe.onTriggered = triggerAll; + exes.push(newExe); + } +} + +function updateButton() +{ + clearTimeout(updateTimeout); + updateTimeout = setTimeout(() => + { + let show = false; + for (let i = 0; i < triggers.length; i++) + if (triggers[i].links.length > 1) show = true; + + cleanup.setUiAttribs({ "hideParam": !show }); + + if (op.isCurrentUiOp()) op.refreshParams(); + }, 60); +} + +function triggerAll() +{ + for (let i = 0; i < triggers.length; i++) triggers[i].trigger(); +} + +function clean() +{ + let count = 0; + for (let i = 0; i < triggers.length; i++) + { + let removeLinks = []; + + if (triggers[i].links.length > 1) + for (let j = 1; j < triggers[i].links.length; j++) + { + while (triggers[count].links.length > 0) count++; + + removeLinks.push(triggers[i].links[j]); + const otherPort = triggers[i].links[j].getOtherPort(triggers[i]); + op.patch.link(op, "trigger " + count, otherPort.parent, otherPort.name); + count++; + } + + for (let j = 0; j < removeLinks.length; j++) removeLinks[j].remove(); + } + updateButton(); +} + + +}; + +Ops.Sequence.prototype = new CABLES.Op(); +CABLES.OPS["a466bc1f-06e9-4595-8849-bffb9fe22f99"]={f:Ops.Sequence,objName:"Ops.Sequence"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ImageCompose_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.ImageCompose_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"imgcomp_frag":"UNI float a;\nvoid main()\n{\n outColor= vec4(0.0,0.0,0.0,a);\n}\n",}; +const + render = op.inTrigger("Render"), + useVPSize = op.inBool("Use viewport size", true), + width = op.inValueInt("Width", 640), + height = op.inValueInt("Height", 480), + tfilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inValueSelect("Wrap", ["clamp to edge", "repeat", "mirrored repeat"], "repeat"), + fpTexture = op.inValueBool("HDR"), + inTransp = op.inValueBool("Transparent", false), + + trigger = op.outTrigger("Next"), + texOut = op.outTexture("texture_out"), + outRatio = op.outValue("Aspect Ratio"); + +const cgl = op.patch.cgl; +op.setPortGroup("Texture Size", [useVPSize, width, height]); +op.setPortGroup("Texture Settings", [twrap, tfilter, fpTexture, inTransp]); + +texOut.set(CGL.Texture.getEmptyTexture(cgl, fpTexture.get())); +let effect = null; +let tex = null; +let w = 8, h = 8; + +const prevViewPort = [0, 0, 0, 0]; +let reInitEffect = true; + +// const bgShader = new CGL.Shader(cgl, "imgcompose bg"); +// bgShader.setSource(bgShader.getDefaultVertexShader(), attachments.imgcomp_frag); + +// const uniAlpha = new CGL.Uniform(bgShader, "f", "a", !inTransp.get()); + +let selectedFilter = CGL.Texture.FILTER_LINEAR; +let selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + +const fps = 0; +const fpsStart = 0; + +twrap.onChange = onWrapChange; +tfilter.onChange = onFilterChange; + +render.onTriggered = op.preRender = doRender; + +onFilterChange(); +onWrapChange(); +updateSizePorts(); + +// inTransp.onChange = () => +// { +// uniAlpha.setValue(!inTransp.get()); +// }; + +function initEffect() +{ + if (effect)effect.delete(); + if (tex)tex.delete(); + + if (fpTexture.get() && tfilter.get() == "mipmap") op.setUiError("fpmipmap", "Don't use mipmap and HDR at the same time, many systems do not support this."); + else op.setUiError("fpmipmap", null); + + effect = new CGL.TextureEffect(cgl, { "isFloatingPointTexture": fpTexture.get() }); + + tex = new CGL.Texture(cgl, + { + "name": "image_compose_v2_" + op.id, + "isFloatingPointTexture": fpTexture.get(), + "filter": selectedFilter, + "wrap": selectedWrap, + "width": Math.ceil(width.get()), + "height": Math.ceil(height.get()), + }); + + effect.setSourceTexture(tex); + texOut.set(CGL.Texture.getEmptyTexture(cgl, fpTexture.get())); + + reInitEffect = false; +} + +fpTexture.onChange = function () +{ + reInitEffect = true; +}; + +function updateResolution() +{ + if (!effect)initEffect(); + + if (useVPSize.get()) + { + w = cgl.getViewPort()[2]; + h = cgl.getViewPort()[3]; + } + else + { + w = Math.ceil(width.get()); + h = Math.ceil(height.get()); + } + + outRatio.set(w / h); + + if ((w != tex.width || h != tex.height) && (w !== 0 && h !== 0)) + { + // height.set(h); + // width.set(w); + tex.setSize(w, h); + + effect.setSourceTexture(tex); + texOut.set(CGL.Texture.getEmptyTexture(cgl, fpTexture.get())); + texOut.set(tex); + } + + // if (texOut.get() && selectedFilter != CGL.Texture.FILTER_NEAREST) + // { + // if (!texOut.get().isPowerOfTwo()) op.setUiError("hintnpot", "texture dimensions not power of two! - texture filtering when scaling will not work on ios devices.", 0); + // else op.setUiError("hintnpot", null, 0); + // } + // else op.setUiError("hintnpot", null, 0); +} + +function updateSizePorts() +{ + width.setUiAttribs({ "greyout": useVPSize.get() }); + height.setUiAttribs({ "greyout": useVPSize.get() }); +} + +useVPSize.onChange = function () +{ + updateSizePorts(); +}; + +op.preRender = function () +{ + doRender(); + // bgShader.bind(); +}; + +function doRender() +{ + if (!effect || reInitEffect) initEffect(); + + const vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + cgl.pushBlend(false); + + updateResolution(); + + const oldEffect = cgl.currentTextureEffect; + cgl.currentTextureEffect = effect; + cgl.currentTextureEffect.width = width.get(); + cgl.currentTextureEffect.height = height.get(); + effect.setSourceTexture(tex); + + let bgTex = CGL.Texture.getBlackTexture(cgl); + if (inTransp.get())bgTex = CGL.Texture.getEmptyTexture(cgl, fpTexture.get()); + + effect.startEffect(bgTex); + + // cgl.pushShader(bgShader); + // cgl.currentTextureEffect.bind(); + // cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + // cgl.currentTextureEffect.finish(); + // cgl.popShader(); + + trigger.trigger(); + + texOut.set(effect.getCurrentSourceTexture()); + + effect.endEffect(); + + cgl.setViewPort(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); + + cgl.popBlend(false); + cgl.currentTextureEffect = oldEffect; +} + +function onWrapChange() +{ + if (twrap.get() == "repeat") selectedWrap = CGL.Texture.WRAP_REPEAT; + if (twrap.get() == "mirrored repeat") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (twrap.get() == "clamp to edge") selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + reInitEffect = true; + // updateResolution(); +} + +function onFilterChange() +{ + if (tfilter.get() == "nearest") selectedFilter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "linear") selectedFilter = CGL.Texture.FILTER_LINEAR; + if (tfilter.get() == "mipmap") selectedFilter = CGL.Texture.FILTER_MIPMAP; + + reInitEffect = true; + // updateResolution(); +} + + +}; + +Ops.Gl.TextureEffects.ImageCompose_v2.prototype = new CABLES.Op(); +CABLES.OPS["a5b43d4c-a9ea-4eaf-9ed0-f257d222659d"]={f:Ops.Gl.TextureEffects.ImageCompose_v2,objName:"Ops.Gl.TextureEffects.ImageCompose_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.BasicMaterial_v3 +// +// ************************************************************** + +Ops.Gl.Shader.BasicMaterial_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"basicmaterial_frag":"{{MODULES_HEAD}}\n\nIN vec2 texCoord;\n\n#ifdef VERTEX_COLORS\nIN vec4 vertCol;\n#endif\n\n#ifdef HAS_TEXTURES\n IN vec2 texCoordOrig;\n #ifdef HAS_TEXTURE_DIFFUSE\n UNI sampler2D tex;\n #endif\n #ifdef HAS_TEXTURE_OPACITY\n UNI sampler2D texOpacity;\n #endif\n#endif\n\nvoid main()\n{\n {{MODULE_BEGIN_FRAG}}\n vec4 col=color;\n\n\n #ifdef HAS_TEXTURES\n vec2 uv=texCoord;\n\n #ifdef CROP_TEXCOORDS\n if(uv.x<0.0 || uv.x>1.0 || uv.y<0.0 || uv.y>1.0) discard;\n #endif\n\n #ifdef HAS_TEXTURE_DIFFUSE\n col=texture(tex,uv);\n\n #ifdef COLORIZE_TEXTURE\n col.r*=color.r;\n col.g*=color.g;\n col.b*=color.b;\n #endif\n #endif\n col.a*=color.a;\n #ifdef HAS_TEXTURE_OPACITY\n #ifdef TRANSFORMALPHATEXCOORDS\n uv=texCoordOrig;\n #endif\n #ifdef ALPHA_MASK_IALPHA\n col.a*=1.0-texture(texOpacity,uv).a;\n #endif\n #ifdef ALPHA_MASK_ALPHA\n col.a*=texture(texOpacity,uv).a;\n #endif\n #ifdef ALPHA_MASK_LUMI\n col.a*=dot(vec3(0.2126,0.7152,0.0722), texture(texOpacity,uv).rgb);\n #endif\n #ifdef ALPHA_MASK_R\n col.a*=texture(texOpacity,uv).r;\n #endif\n #ifdef ALPHA_MASK_G\n col.a*=texture(texOpacity,uv).g;\n #endif\n #ifdef ALPHA_MASK_B\n col.a*=texture(texOpacity,uv).b;\n #endif\n // #endif\n #endif\n #endif\n\n {{MODULE_COLOR}}\n\n #ifdef DISCARDTRANS\n if(col.a<0.2) discard;\n #endif\n\n #ifdef VERTEX_COLORS\n col*=vertCol;\n #endif\n\n outColor = col;\n}\n","basicmaterial_vert":"\n{{MODULES_HEAD}}\n\n// OUT vec3 norm;\nOUT vec2 texCoord;\nOUT vec2 texCoordOrig;\n\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\n#ifdef HAS_TEXTURES\n UNI float diffuseRepeatX;\n UNI float diffuseRepeatY;\n UNI float texOffsetX;\n UNI float texOffsetY;\n#endif\n\n#ifdef VERTEX_COLORS\n in vec4 attrVertColor;\n out vec4 vertCol;\n\n#endif\n\n\nvoid main()\n{\n mat4 mMatrix=modelMatrix;\n mat4 mvMatrix;\n\n norm=attrVertNormal;\n texCoordOrig=attrTexCoord;\n texCoord=attrTexCoord;\n #ifdef HAS_TEXTURES\n texCoord.x=texCoord.x*diffuseRepeatX+texOffsetX;\n texCoord.y=(1.0-texCoord.y)*diffuseRepeatY+texOffsetY;\n #endif\n\n #ifdef VERTEX_COLORS\n vertCol=attrVertColor;\n #endif\n\n vec4 pos = vec4(vPosition, 1.0);\n\n #ifdef BILLBOARD\n vec3 position=vPosition;\n mvMatrix=viewMatrix*modelMatrix;\n\n gl_Position = projMatrix * mvMatrix * vec4((\n position.x * vec3(\n mvMatrix[0][0],\n mvMatrix[1][0],\n mvMatrix[2][0] ) +\n position.y * vec3(\n mvMatrix[0][1],\n mvMatrix[1][1],\n mvMatrix[2][1]) ), 1.0);\n #endif\n\n {{MODULE_VERTEX_POSITION}}\n\n #ifndef BILLBOARD\n mvMatrix=viewMatrix * mMatrix;\n #endif\n\n\n #ifndef BILLBOARD\n // gl_Position = projMatrix * viewMatrix * modelMatrix * pos;\n gl_Position = projMatrix * mvMatrix * pos;\n #endif\n}\n",}; +const render = op.inTrigger("render"); + +const trigger = op.outTrigger("trigger"); +const shaderOut = op.outObject("shader", null, "shader"); + +shaderOut.ignoreValueSerialize = true; + +op.toWorkPortsNeedToBeLinked(render); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "basicmaterialnew"); +shader.addAttribute({ "type": "vec3", "name": "vPosition" }); +shader.addAttribute({ "type": "vec2", "name": "attrTexCoord" }); +shader.addAttribute({ "type": "vec3", "name": "attrVertNormal", "nameFrag": "norm" }); +shader.addAttribute({ "type": "float", "name": "attrVertIndex" }); + +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); + +shader.setSource(attachments.basicmaterial_vert, attachments.basicmaterial_frag); + +shaderOut.set(shader); + +render.onTriggered = doRender; + +// rgba colors +const r = op.inValueSlider("r", Math.random()); +const g = op.inValueSlider("g", Math.random()); +const b = op.inValueSlider("b", Math.random()); +const a = op.inValueSlider("a", 1); +r.setUiAttribs({ "colorPick": true }); + +// const uniColor=new CGL.Uniform(shader,'4f','color',r,g,b,a); +const colUni = shader.addUniformFrag("4f", "color", r, g, b, a); + +shader.uniformColorDiffuse = colUni; + +// diffuse outTexture + +const diffuseTexture = op.inTexture("texture"); +let diffuseTextureUniform = null; +diffuseTexture.onChange = updateDiffuseTexture; + +const colorizeTexture = op.inValueBool("colorizeTexture", false); +const vertexColors = op.inValueBool("Vertex Colors", false); + +// opacity texture +const textureOpacity = op.inTexture("textureOpacity"); +let textureOpacityUniform = null; + +const alphaMaskSource = op.inSwitch("Alpha Mask Source", ["Luminance", "R", "G", "B", "A", "1-A"], "Luminance"); +alphaMaskSource.setUiAttribs({ "greyout": true }); +textureOpacity.onChange = updateOpacity; + +const texCoordAlpha = op.inValueBool("Opacity TexCoords Transform", false); +const discardTransPxl = op.inValueBool("Discard Transparent Pixels"); + +// texture coords +const + diffuseRepeatX = op.inValue("diffuseRepeatX", 1), + diffuseRepeatY = op.inValue("diffuseRepeatY", 1), + diffuseOffsetX = op.inValue("Tex Offset X", 0), + diffuseOffsetY = op.inValue("Tex Offset Y", 0), + cropRepeat = op.inBool("Crop TexCoords", false); + +shader.addUniformFrag("f", "diffuseRepeatX", diffuseRepeatX); +shader.addUniformFrag("f", "diffuseRepeatY", diffuseRepeatY); +shader.addUniformFrag("f", "texOffsetX", diffuseOffsetX); +shader.addUniformFrag("f", "texOffsetY", diffuseOffsetY); + +const doBillboard = op.inValueBool("billboard", false); + +alphaMaskSource.onChange = + doBillboard.onChange = + discardTransPxl.onChange = + texCoordAlpha.onChange = + cropRepeat.onChange = + vertexColors.onChange = + colorizeTexture.onChange = updateDefines; + +op.setPortGroup("Color", [r, g, b, a]); +op.setPortGroup("Color Texture", [diffuseTexture, vertexColors, colorizeTexture]); +op.setPortGroup("Opacity", [textureOpacity, alphaMaskSource, discardTransPxl, texCoordAlpha]); +op.setPortGroup("Texture Transform", [diffuseRepeatX, diffuseRepeatY, diffuseOffsetX, diffuseOffsetY, cropRepeat]); + +updateOpacity(); +updateDiffuseTexture(); + +op.preRender = function () +{ + shader.bind(); + doRender(); +}; + +function doRender() +{ + if (!shader) return; + + cgl.pushShader(shader); + shader.popTextures(); + + if (diffuseTextureUniform && diffuseTexture.get()) shader.pushTexture(diffuseTextureUniform, diffuseTexture.get()); + if (textureOpacityUniform && textureOpacity.get()) shader.pushTexture(textureOpacityUniform, textureOpacity.get()); + + trigger.trigger(); + + cgl.popShader(); +} + +function updateOpacity() +{ + if (textureOpacity.get()) + { + if (textureOpacityUniform !== null) return; + shader.removeUniform("texOpacity"); + shader.define("HAS_TEXTURE_OPACITY"); + if (!textureOpacityUniform)textureOpacityUniform = new CGL.Uniform(shader, "t", "texOpacity"); + + alphaMaskSource.setUiAttribs({ "greyout": false }); + texCoordAlpha.setUiAttribs({ "greyout": false }); + } + else + { + shader.removeUniform("texOpacity"); + shader.removeDefine("HAS_TEXTURE_OPACITY"); + textureOpacityUniform = null; + + alphaMaskSource.setUiAttribs({ "greyout": true }); + texCoordAlpha.setUiAttribs({ "greyout": true }); + } + + updateDefines(); +} + +function updateDiffuseTexture() +{ + if (diffuseTexture.get()) + { + if (!shader.hasDefine("HAS_TEXTURE_DIFFUSE"))shader.define("HAS_TEXTURE_DIFFUSE"); + if (!diffuseTextureUniform)diffuseTextureUniform = new CGL.Uniform(shader, "t", "texDiffuse"); + + diffuseRepeatX.setUiAttribs({ "greyout": false }); + diffuseRepeatY.setUiAttribs({ "greyout": false }); + diffuseOffsetX.setUiAttribs({ "greyout": false }); + diffuseOffsetY.setUiAttribs({ "greyout": false }); + colorizeTexture.setUiAttribs({ "greyout": false }); + } + else + { + shader.removeUniform("texDiffuse"); + shader.removeDefine("HAS_TEXTURE_DIFFUSE"); + diffuseTextureUniform = null; + + diffuseRepeatX.setUiAttribs({ "greyout": true }); + diffuseRepeatY.setUiAttribs({ "greyout": true }); + diffuseOffsetX.setUiAttribs({ "greyout": true }); + diffuseOffsetY.setUiAttribs({ "greyout": true }); + colorizeTexture.setUiAttribs({ "greyout": true }); + } +} + +function updateDefines() +{ + shader.toggleDefine("VERTEX_COLORS", vertexColors.get()); + shader.toggleDefine("CROP_TEXCOORDS", cropRepeat.get()); + shader.toggleDefine("COLORIZE_TEXTURE", colorizeTexture.get()); + shader.toggleDefine("TRANSFORMALPHATEXCOORDS", texCoordAlpha.get()); + shader.toggleDefine("DISCARDTRANS", discardTransPxl.get()); + shader.toggleDefine("BILLBOARD", doBillboard.get()); + + shader.toggleDefine("ALPHA_MASK_ALPHA", alphaMaskSource.get() == "A"); + shader.toggleDefine("ALPHA_MASK_IALPHA", alphaMaskSource.get() == "1-A"); + shader.toggleDefine("ALPHA_MASK_LUMI", alphaMaskSource.get() == "Luminance"); + shader.toggleDefine("ALPHA_MASK_R", alphaMaskSource.get() == "R"); + shader.toggleDefine("ALPHA_MASK_G", alphaMaskSource.get() == "G"); + shader.toggleDefine("ALPHA_MASK_B", alphaMaskSource.get() == "B"); +} + + +}; + +Ops.Gl.Shader.BasicMaterial_v3.prototype = new CABLES.Op(); +CABLES.OPS["ec55d252-3843-41b1-b731-0482dbd9e72b"]={f:Ops.Gl.Shader.BasicMaterial_v3,objName:"Ops.Gl.Shader.BasicMaterial_v3"}; + + + + +// ************************************************************** +// +// Ops.Deprecated.Anim.RelativeTime +// +// ************************************************************** + +Ops.Deprecated.Anim.RelativeTime = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + mul = op.inValue("Multiply", 1), + outTrigger = op.outTrigger("Trigger out"), + result = op.outNumber("result"); + +exe.onTriggered = update; +update(); + +function update() +{ + result.set(op.patch.freeTimer.get() * mul.get()); + outTrigger.trigger(); +} + + +}; + +Ops.Deprecated.Anim.RelativeTime.prototype = new CABLES.Op(); +CABLES.OPS["917df27b-7cc3-465f-986d-bcf5a7e125a7"]={f:Ops.Deprecated.Anim.RelativeTime,objName:"Ops.Deprecated.Anim.RelativeTime"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.FBMNoise +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.FBMNoise = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"fbmnoise_frag":"UNI sampler2D tex;\nUNI float anim;\n\nUNI float scale;\nUNI float repeat;\n\nUNI float scrollX;\nUNI float scrollY;\n\nUNI float amount;\n\nUNI bool layer1;\nUNI bool layer2;\nUNI bool layer3;\nUNI bool layer4;\nUNI vec3 color;\nUNI float aspect;\n\nIN vec2 texCoord;\n\n\n{{CGL.BLENDMODES}}\n\n// csdcsdcds\n// adapted from warp shader by inigo quilez/iq\n// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.\n\n// See here for a tutorial on how to make this: http://www.iquilezles.org/www/articles/warp/warp.htm\n\nconst mat2 m = mat2( 0.80, 0.60, -0.60, 0.80 );\n\nfloat noise( in vec2 x )\n{\n\treturn sin(1.5*x.x)*sin(1.5*x.y);\n}\n\nfloat fbm4( vec2 p )\n{\n float f = 0.0;\n f += 0.5000*noise( p ); p = m*p*2.02;\n f += 0.2500*noise( p ); p = m*p*2.03;\n f += 0.1250*noise( p ); p = m*p*2.01;\n f += 0.0625*noise( p );\n return f/0.9375;\n}\n\nfloat fbm6( vec2 p )\n{\n float f = 0.0;\n f += 0.500000*(0.5+0.5*noise( p )); p = m*p*2.02;\n f += 0.250000*(0.5+0.5*noise( p )); p = m*p*2.03;\n f += 0.125000*(0.5+0.5*noise( p )); p = m*p*2.01;\n f += 0.062500*(0.5+0.5*noise( p )); p = m*p*2.04;\n f += 0.031250*(0.5+0.5*noise( p )); p = m*p*2.01;\n f += 0.015625*(0.5+0.5*noise( p ));\n return f/0.96875;\n}\n\nvoid main()\n{\n // vec4 col=texture(tex,texCoord+2.0*fbm4(texCoord+2.0*fbm6(texCoord+anim)));\n\n vec2 tc=texCoord;\n\t#ifdef DO_TILEABLE\n\t tc=abs(texCoord-0.5);\n\t#endif\n\n\n vec2 p=(tc-0.5)*scale;\n\n\n p.y/=aspect;\n vec2 q = vec2( fbm4( p + vec2(0.3+scrollX,0.20+scrollY) ),\n fbm4( p + vec2(3.1+scrollX,1.3+scrollY) ) );\n\n vec2 q2 = vec2( fbm4( p + vec2(2.0+scrollX,1.0+scrollY) ),\n fbm4( p + vec2(3.1+scrollX,1.3+scrollY) ) );\n\n vec2 q3 = vec2( fbm4( p + vec2(9.0+scrollX,4.0+scrollY) ),\n fbm4( p + vec2(3.1+scrollX,4.3+scrollY) ) );\n\n\n\n float v= fbm4( ( p + 4.0*q +anim*0.1)*repeat);\n float v2= fbm4( (p + 4.0*q2 +anim*0.1)*repeat );\n\n float v3= fbm6( (p + 4.0*q3 +anim*0.1)*repeat );\n float v4= fbm6( (p + 4.0*q2 +anim*0.1)*repeat );\n\n\n\n\n vec4 base=texture(tex,texCoord);\n\n vec4 finalColor;\n float colVal=0.0;\n float numLayers=0.0;\n\n if(layer1)\n {\n colVal+=v;\n numLayers++;\n }\n\n if(layer2)\n {\n colVal+=v2;\n numLayers++;\n }\n\n if(layer3)\n {\n colVal+=v3;\n numLayers++;\n }\n\n if(layer4)\n {\n colVal+=v4;\n numLayers++;\n }\n\n finalColor=vec4( color*vec3(colVal/numLayers),1.0);\n\n outColor = cgl_blend(base,finalColor,amount);;\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + r = op.inValueSlider("r", 1.0), + g = op.inValueSlider("g", 1.0), + b = op.inValueSlider("b", 1.0), + trigger = op.outTrigger("trigger"); + +r.setUiAttribs({ "colorPick": true }); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "fbmnoise"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.fbmnoise_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +const uniScale = new CGL.Uniform(shader, "f", "scale", op.inValue("scale", 2)); +const uniAnim = new CGL.Uniform(shader, "f", "anim", op.inValue("anim", 0)); +const uniScrollX = new CGL.Uniform(shader, "f", "scrollX", op.inValue("scrollX", 9)); +const uniScrollY = new CGL.Uniform(shader, "f", "scrollY", op.inValue("scrollY", 0)); +const uniRepeat = new CGL.Uniform(shader, "f", "repeat", op.inValue("repeat", 1)); +const uniAspect = new CGL.Uniform(shader, "f", "aspect", op.inValue("aspect", 1)); + +const uniLayer1 = new CGL.Uniform(shader, "b", "layer1", op.inValueBool("Layer 1", true)); +const uniLayer2 = new CGL.Uniform(shader, "b", "layer2", op.inValueBool("Layer 2", true)); +const uniLayer3 = new CGL.Uniform(shader, "b", "layer3", op.inValueBool("Layer 3", true)); +const uniLayer4 = new CGL.Uniform(shader, "b", "layer4", op.inValueBool("Layer 4", true)); + +const uniColor = new CGL.Uniform(shader, "3f", "color", r, g, b); + +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +const tile = op.inValueBool("Tileable", false); +tile.onChange = updateTileable; +function updateTileable() +{ + if (tile.get())shader.define("DO_TILEABLE"); + else shader.removeDefine("DO_TILEABLE"); +} + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + uniAspect.set(cgl.currentTextureEffect.getCurrentSourceTexture().width / cgl.currentTextureEffect.getCurrentSourceTexture().height); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.FBMNoise.prototype = new CABLES.Op(); +CABLES.OPS["7073186c-b776-48c2-a01e-041df88ad88a"]={f:Ops.Gl.TextureEffects.Noise.FBMNoise,objName:"Ops.Gl.TextureEffects.Noise.FBMNoise"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ColorMap +// +// ************************************************************** + +Ops.Gl.TextureEffects.ColorMap = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colormap_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D gradient;\nUNI float pos;\nUNI float amount;\nUNI float vmin;\nUNI float vmax;\n\n{{CGL.BLENDMODES}}\n\n\nfloat lumi(vec3 color)\n{\n return vec3(dot(vec3(0.2126,0.7152,0.0722), color)).r;\n}\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n\n base=clamp(base,vmin,vmax);\n\n #ifdef METH_LUMI\n vec4 color=texture(gradient,vec2(lumi(base.rgb),pos));\n #endif\n\n #ifdef METH_CHANNELS\n vec4 color=vec4(1.0);\n color.r=texture(gradient,vec2(base.r,pos)).r;\n color.g=texture(gradient,vec2(base.g,pos)).g;\n color.b=texture(gradient,vec2(base.b,pos)).b;\n #endif\n\n// outColor= vec4(color);\n outColor=cgl_blend(base,color,amount);\n\n}\n",}; +let render = op.inTrigger("render"); +let trigger = op.outTrigger("trigger"); + +const blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"); +const amount = op.inValueSlider("Amount", 1); + +let inGradient = op.inTexture("Gradient"); +let inMethod = op.inSwitch("Method", ["Luminance", "Channels"], "Luminance"); + +let inMin = op.inFloatSlider("Min", 0); +let inMax = op.inFloatSlider("Max", 1); + +let inPos = op.inValueSlider("Position", 0.5); + +op.setPortGroup("Vertical Position", [inMin, inMax, inPos]); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); +shader.define("METH_LUMI"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.colormap_frag); +var textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +var textureUniform = new CGL.Uniform(shader, "t", "gradient", 1); +let uniPos = new CGL.Uniform(shader, "f", "pos", inPos); + +let uniMin = new CGL.Uniform(shader, "f", "vmin", inMin); +let uniMax = new CGL.Uniform(shader, "f", "vmax", inMax); +let uniAmount = new CGL.Uniform(shader, "f", "amount", amount); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +inMethod.onChange = () => +{ + shader.toggleDefine("METH_LUMI", inMethod.get() == "Luminance"); + shader.toggleDefine("METH_CHANNELS", inMethod.get() == "Channels"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + if (!inGradient.get()) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.setTexture(1, inGradient.get().tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, inGradient.get().tex ); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.ColorMap.prototype = new CABLES.Op(); +CABLES.OPS["58e302d7-4b84-4077-aa13-4f3cf0885205"]={f:Ops.Gl.TextureEffects.ColorMap,objName:"Ops.Gl.TextureEffects.ColorMap"}; + + + + +// ************************************************************** +// +// Ops.Gl.Render2Texture +// +// ************************************************************** + +Ops.Gl.Render2Texture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +const + render = op.inTrigger("render"), + useVPSize = op.inValueBool("use viewport size", true), + width = op.inValueInt("texture width", 512), + height = op.inValueInt("texture height", 512), + aspect = op.inBool("Auto Aspect", false), + tfilter = op.inSwitch("filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inSwitch("Wrap", ["Clamp", "Repeat", "Mirror"], "Repeat"), + msaa = op.inSwitch("MSAA", ["none", "2x", "4x", "8x"], "none"), + trigger = op.outTrigger("trigger"), + tex = op.outTexture("texture"), + texDepth = op.outTexture("textureDepth"), + fpTexture = op.inValueBool("HDR"), + depth = op.inValueBool("Depth", true), + clear = op.inValueBool("Clear", true); + +let fb = null; +let reInitFb = true; +tex.set(CGL.Texture.getEmptyTexture(cgl)); + +op.setPortGroup("Size", [useVPSize, width, height, aspect]); + +const prevViewPort = [0, 0, 0, 0]; + +fpTexture.setUiAttribs({ "title": "Pixelformat Float 32bit" }); + +fpTexture.onChange = + depth.onChange = + clear.onChange = + tfilter.onChange = + twrap.onChange = + msaa.onChange = initFbLater; + +useVPSize.onChange = updateVpSize; + +render.onTriggered = + op.preRender = doRender; + +updateVpSize(); + +function updateVpSize() +{ + width.setUiAttribs({ "greyout": useVPSize.get() }); + height.setUiAttribs({ "greyout": useVPSize.get() }); + aspect.setUiAttribs({ "greyout": useVPSize.get() }); +} + +function initFbLater() +{ + reInitFb = true; +} + +function doRender() +{ + const vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + if (!fb || reInitFb) + { + if (fb) fb.delete(); + + let selectedWrap = CGL.Texture.WRAP_REPEAT; + if (twrap.get() == "Clamp") selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + else if (twrap.get() == "Mirror") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + + let selectFilter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "nearest") selectFilter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") selectFilter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") selectFilter = CGL.Texture.FILTER_MIPMAP; + + if (fpTexture.get() && tfilter.get() == "mipmap") op.setUiError("fpmipmap", "Don't use mipmap and HDR at the same time, many systems do not support this."); + else op.setUiError("fpmipmap", null); + + if (cgl.glVersion >= 2) + { + let ms = true; + let msSamples = 4; + + if (msaa.get() == "none") + { + msSamples = 0; + ms = false; + } + if (msaa.get() == "2x") msSamples = 2; + if (msaa.get() == "4x") msSamples = 4; + if (msaa.get() == "8x") msSamples = 8; + + fb = new CGL.Framebuffer2(cgl, 8, 8, + { + "name": "render2texture " + op.id, + "isFloatingPointTexture": fpTexture.get(), + "multisampling": ms, + "wrap": selectedWrap, + "filter": selectFilter, + "depth": depth.get(), + "multisamplingSamples": msSamples, + "clear": clear.get() + }); + } + else + { + fb = new CGL.Framebuffer(cgl, 8, 8, { "isFloatingPointTexture": fpTexture.get(), "clear": clear.get() }); + } + + texDepth.set(fb.getTextureDepth()); + reInitFb = false; + } + + if (useVPSize.get()) + { + width.set(cgl.getViewPort()[2]); + height.set(cgl.getViewPort()[3]); + } + + if (fb.getWidth() != Math.ceil(width.get()) || fb.getHeight() != Math.ceil(height.get())) + { + fb.setSize( + Math.max(1, Math.ceil(width.get())), + Math.max(1, Math.ceil(height.get()))); + } + + fb.renderStart(cgl); + + if (aspect.get()) mat4.perspective(cgl.pMatrix, 45, width.get() / height.get(), 0.1, 1000.0); + + trigger.trigger(); + fb.renderEnd(cgl); + + // cgl.resetViewPort(); + cgl.setViewPort(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); + + tex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + tex.set(fb.getTextureColor()); +} + + +}; + +Ops.Gl.Render2Texture.prototype = new CABLES.Op(); +CABLES.OPS["d01fa820-396c-4cb5-9d78-6b14762852af"]={f:Ops.Gl.Render2Texture,objName:"Ops.Gl.Render2Texture"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.DrawImage_v3 +// +// ************************************************************** + +Ops.Gl.TextureEffects.DrawImage_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"drawimage_frag":"#ifdef HAS_TEXTURES\n IN vec2 texCoord;\n UNI sampler2D tex;\n UNI sampler2D image;\n#endif\n\n#ifdef TEX_TRANSFORM\n IN mat3 transform;\n#endif\n// UNI float rotate;\n\n{{CGL.BLENDMODES}}\n\n#ifdef HAS_TEXTUREALPHA\n UNI sampler2D imageAlpha;\n#endif\n\nUNI float amount;\n\n#ifdef ASPECT_RATIO\n UNI float aspectTex;\n UNI float aspectPos;\n#endif\n\nvoid main()\n{\n vec4 blendRGBA=vec4(0.0,0.0,0.0,1.0);\n\n #ifdef HAS_TEXTURES\n vec2 tc=texCoord;\n\n #ifdef TEX_FLIP_X\n tc.x=1.0-tc.x;\n #endif\n #ifdef TEX_FLIP_Y\n tc.y=1.0-tc.y;\n #endif\n\n #ifdef ASPECT_RATIO\n #ifdef ASPECT_AXIS_X\n tc.y=(1.0-aspectPos)-(((1.0-aspectPos)-tc.y)*aspectTex);\n #endif\n #ifdef ASPECT_AXIS_Y\n tc.x=(1.0-aspectPos)-(((1.0-aspectPos)-tc.x)/aspectTex);\n #endif\n #endif\n\n #ifdef TEX_TRANSFORM\n vec3 coordinates=vec3(tc.x, tc.y,1.0);\n tc=(transform * coordinates ).xy;\n #endif\n\n blendRGBA=texture(image,tc);\n\n vec3 blend=blendRGBA.rgb;\n vec4 baseRGBA=texture(tex,texCoord);\n vec3 base=baseRGBA.rgb;\n\n\n #ifdef PREMUL\n blend.rgb = (blend.rgb) + (base.rgb * (1.0 - blendRGBA.a));\n #endif\n\n vec3 colNew=_blend(base,blend);\n\n\n\n\n #ifdef REMOVE_ALPHA_SRC\n blendRGBA.a=1.0;\n #endif\n\n #ifdef HAS_TEXTUREALPHA\n vec4 colImgAlpha=texture(imageAlpha,tc);\n float colImgAlphaAlpha=colImgAlpha.a;\n\n #ifdef ALPHA_FROM_LUMINANCE\n vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), colImgAlpha.rgb ));\n colImgAlphaAlpha=(gray.r+gray.g+gray.b)/3.0;\n #endif\n\n #ifdef ALPHA_FROM_INV_UMINANCE\n vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), colImgAlpha.rgb ));\n colImgAlphaAlpha=1.0-(gray.r+gray.g+gray.b)/3.0;\n #endif\n\n #ifdef INVERT_ALPHA\n colImgAlphaAlpha=clamp(colImgAlphaAlpha,0.0,1.0);\n colImgAlphaAlpha=1.0-colImgAlphaAlpha;\n #endif\n\n blendRGBA.a=colImgAlphaAlpha*blendRGBA.a;\n #endif\n #endif\n\n float am=amount;\n\n #ifdef CLIP_REPEAT\n if(tc.y>1.0 || tc.y<0.0 || tc.x>1.0 || tc.x<0.0)\n {\n // colNew.rgb=vec3(0.0);\n am=0.0;\n }\n #endif\n\n #ifdef ASPECT_RATIO\n #ifdef ASPECT_CROP\n if(tc.y>1.0 || tc.y<0.0 || tc.x>1.0 || tc.x<0.0)\n {\n colNew.rgb=base.rgb;\n am=0.0;\n }\n\n #endif\n #endif\n\n\n\n #ifndef PREMUL\n blendRGBA.rgb=mix(colNew,base,1.0-(am*blendRGBA.a));\n blendRGBA.a=clamp(baseRGBA.a+(blendRGBA.a*am),0.,1.);\n #endif\n\n #ifdef PREMUL\n // premultiply\n // blendRGBA.rgb = (blendRGBA.rgb) + (baseRGBA.rgb * (1.0 - blendRGBA.a));\n blendRGBA=vec4(\n mix(colNew.rgb,base,1.0-(am*blendRGBA.a)),\n blendRGBA.a*am+baseRGBA.a\n );\n #endif\n\n #ifdef ALPHA_MASK\n blendRGBA.a=baseRGBA.a;\n #endif\n\n outColor=blendRGBA;\n}\n\n\n\n\n\n\n\n","drawimage_vert":"IN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\n\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\n\nOUT vec2 texCoord;\n// OUT vec3 norm;\n\n#ifdef TEX_TRANSFORM\n UNI float posX;\n UNI float posY;\n UNI float scaleX;\n UNI float scaleY;\n UNI float rotate;\n OUT mat3 transform;\n#endif\n\nvoid main()\n{\n texCoord=attrTexCoord;\n// norm=attrVertNormal;\n\n #ifdef TEX_TRANSFORM\n vec3 coordinates=vec3(attrTexCoord.x, attrTexCoord.y,1.0);\n float angle = radians( rotate );\n vec2 scale= vec2(scaleX,scaleY);\n vec2 translate= vec2(posX,posY);\n\n transform = mat3( scale.x * cos( angle ), scale.x * sin( angle ), 0.0,\n - scale.y * sin( angle ), scale.y * cos( angle ), 0.0,\n - 0.5 * scale.x * cos( angle ) + 0.5 * scale.y * sin( angle ) - 0.5 * translate.x*2.0 + 0.5, - 0.5 * scale.x * sin( angle ) - 0.5 * scale.y * cos( angle ) - 0.5 * translate.y*2.0 + 0.5, 1.0);\n #endif\n\n gl_Position = projMatrix * mvMatrix * vec4(vPosition, 1.0);\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "blendMode"), + amount = op.inValueSlider("amount", 1), + + image = op.inTexture("Image"), + inAlphaPremul = op.inValueBool("Premultiplied", false), + inAlphaMask = op.inValueBool("Alpha Mask", false), + removeAlphaSrc = op.inValueBool("removeAlphaSrc", false), + + imageAlpha = op.inTexture("Mask"), + alphaSrc = op.inValueSelect("Mask Src", ["alpha channel", "luminance", "luminance inv"], "luminance"), + invAlphaChannel = op.inValueBool("Invert alpha channel"), + + inAspect = op.inValueBool("Aspect Ratio", false), + inAspectAxis = op.inValueSelect("Stretch Axis", ["X", "Y"], "X"), + inAspectPos = op.inValueSlider("Position", 0.0), + inAspectCrop = op.inValueBool("Crop", false), + + trigger = op.outTrigger("trigger"); + +blendMode.set("normal"); +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "drawimage"); + +imageAlpha.onLinkChanged = updateAlphaPorts; + +op.setPortGroup("Mask", [imageAlpha, alphaSrc, invAlphaChannel]); +op.setPortGroup("Aspect Ratio", [inAspect, inAspectPos, inAspectCrop, inAspectAxis]); + +function updateAlphaPorts() +{ + if (imageAlpha.isLinked()) + { + removeAlphaSrc.setUiAttribs({ "greyout": true }); + alphaSrc.setUiAttribs({ "greyout": false }); + invAlphaChannel.setUiAttribs({ "greyout": false }); + } + else + { + removeAlphaSrc.setUiAttribs({ "greyout": false }); + alphaSrc.setUiAttribs({ "greyout": true }); + invAlphaChannel.setUiAttribs({ "greyout": true }); + } +} + +op.toWorkPortsNeedToBeLinked(image); + +shader.setSource(attachments.drawimage_vert, attachments.drawimage_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureImaghe = new CGL.Uniform(shader, "t", "image", 1), + textureAlpha = new CGL.Uniform(shader, "t", "imageAlpha", 2), + uniTexAspect = new CGL.Uniform(shader, "f", "aspectTex", 1), + uniAspectPos = new CGL.Uniform(shader, "f", "aspectPos", inAspectPos); + +inAspect.onChange = + inAspectCrop.onChange = + inAspectAxis.onChange = updateAspectRatio; + +function updateAspectRatio() +{ + shader.removeDefine("ASPECT_AXIS_X"); + shader.removeDefine("ASPECT_AXIS_Y"); + shader.removeDefine("ASPECT_CROP"); + + inAspectPos.setUiAttribs({ "greyout": !inAspect.get() }); + inAspectCrop.setUiAttribs({ "greyout": !inAspect.get() }); + inAspectAxis.setUiAttribs({ "greyout": !inAspect.get() }); + + if (inAspect.get()) + { + shader.define("ASPECT_RATIO"); + + if (inAspectCrop.get()) shader.define("ASPECT_CROP"); + + if (inAspectAxis.get() == "X") shader.define("ASPECT_AXIS_X"); + if (inAspectAxis.get() == "Y") shader.define("ASPECT_AXIS_Y"); + } + else + { + shader.removeDefine("ASPECT_RATIO"); + if (inAspectCrop.get()) shader.define("ASPECT_CROP"); + + if (inAspectAxis.get() == "X") shader.define("ASPECT_AXIS_X"); + if (inAspectAxis.get() == "Y") shader.define("ASPECT_AXIS_Y"); + } +} + +alphaSrc.set("alpha channel"); + +// +// texture flip +// +const flipX = op.inValueBool("flip x"); +const flipY = op.inValueBool("flip y"); + +// +// texture transform +// + +let doTransform = op.inValueBool("Transform"); + +let scaleX = op.inValueSlider("Scale X", 1); +let scaleY = op.inValueSlider("Scale Y", 1); + +let posX = op.inValue("Position X", 0); +let posY = op.inValue("Position Y", 0); + +let rotate = op.inValue("Rotation", 0); + +const inClipRepeat = op.inValueBool("Clip Repeat", false); + +const uniScaleX = new CGL.Uniform(shader, "f", "scaleX", scaleX); +const uniScaleY = new CGL.Uniform(shader, "f", "scaleY", scaleY); +const uniPosX = new CGL.Uniform(shader, "f", "posX", posX); +const uniPosY = new CGL.Uniform(shader, "f", "posY", posY); +const uniRotate = new CGL.Uniform(shader, "f", "rotate", rotate); + +doTransform.onChange = updateTransformPorts; + +function updateTransformPorts() +{ + shader.toggleDefine("TEX_TRANSFORM", doTransform.get()); + + scaleX.setUiAttribs({ "greyout": !doTransform.get() }); + scaleY.setUiAttribs({ "greyout": !doTransform.get() }); + posX.setUiAttribs({ "greyout": !doTransform.get() }); + posY.setUiAttribs({ "greyout": !doTransform.get() }); + rotate.setUiAttribs({ "greyout": !doTransform.get() }); +} + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +render.onTriggered = doRender; + +inClipRepeat.onChange = + imageAlpha.onChange = + inAlphaPremul.onChange = + inAlphaMask.onChange = + invAlphaChannel.onChange = + flipY.onChange = + flipX.onChange = + removeAlphaSrc.onChange = + alphaSrc.onChange = updateDefines; + +updateTransformPorts(); +updateAlphaPorts(); +updateAspectRatio(); +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("REMOVE_ALPHA_SRC", removeAlphaSrc.get()); + shader.toggleDefine("ALPHA_MASK", inAlphaMask.get()); + + shader.toggleDefine("CLIP_REPEAT", inClipRepeat.get()); + + shader.toggleDefine("HAS_TEXTUREALPHA", imageAlpha.get() && imageAlpha.get().tex); + + shader.toggleDefine("TEX_FLIP_X", flipX.get()); + shader.toggleDefine("TEX_FLIP_Y", flipY.get()); + + shader.toggleDefine("INVERT_ALPHA", invAlphaChannel.get()); + + shader.toggleDefine("ALPHA_FROM_LUMINANCE", alphaSrc.get() == "luminance"); + shader.toggleDefine("ALPHA_FROM_INV_UMINANCE", alphaSrc.get() == "luminance_inv"); + shader.toggleDefine("PREMUL", inAlphaPremul.get()); +} + +function doRender() +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + const tex = image.get(); + if (tex && tex.tex && amount.get() > 0.0) + { + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + const imgTex = cgl.currentTextureEffect.getCurrentSourceTexture(); + cgl.setTexture(0, imgTex.tex); + + if (imgTex && tex) + { + if (tex.textureType != imgTex.textureType && (tex.textureType != CGL.Texture.TYPE_FLOAT || imgTex.textureType != CGL.Texture.TYPE_FLOAT)) + op.setUiError("textypediff", "Drawing 32bit texture into an 8 bit can result in data/precision loss", 1); + else + op.setUiError("textypediff", null); + } + + const asp = 1 / (cgl.currentTextureEffect.getWidth() / cgl.currentTextureEffect.getHeight()) * (tex.width / tex.height); + // uniTexAspect.setValue(1 / (tex.height / tex.width * imgTex.width / imgTex.height)); + + uniTexAspect.setValue(asp); + + cgl.setTexture(1, tex.tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, image.get().tex ); + + if (imageAlpha.get() && imageAlpha.get().tex) + { + cgl.setTexture(2, imageAlpha.get().tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, imageAlpha.get().tex ); + } + + // cgl.pushBlend(false); + + cgl.pushBlendMode(CGL.BLEND_NONE, true); + + cgl.currentTextureEffect.finish(); + cgl.popBlendMode(); + + // cgl.popBlend(); + + cgl.popShader(); + } + + trigger.trigger(); +} + + +}; + +Ops.Gl.TextureEffects.DrawImage_v3.prototype = new CABLES.Op(); +CABLES.OPS["8f6b2f15-fcb0-4597-90c0-e5173f2969fe"]={f:Ops.Gl.TextureEffects.DrawImage_v3,objName:"Ops.Gl.TextureEffects.DrawImage_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.GradientTexture +// +// ************************************************************** + +Ops.Gl.GradientTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inGrad = op.inGradient("Gradient"), + inDir = op.inValueSelect("Direction", ["X", "Y", "Radial"], "X"), + inSmoothstep = op.inValueBool("Smoothstep", false), + inStep = op.inBool("Step", false), + inFlip = op.inBool("Flip", false), + inSRGB = op.inBool("sRGB", false), + inOklab = op.inBool("Oklab", false), + inSize = op.inValueInt("Size", 256), + tfilter = op.inSwitch("filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inValueSelect("wrap", ["clamp to edge", "repeat", "mirrored repeat"], "clamp to edge"), + inGradArray = op.inArray("Gradient Array"), + inRandom = op.inTriggerButton("Randomize Colors"), + outTex = op.outTexture("Texture"), + outColors = op.outArray("Colors", null, 3), + outColorPos = op.outArray("Colors Pos", null, 1); + +const cgl = op.patch.cgl; + +twrap.onChange = + tfilter.onChange = + inStep.onChange = + inFlip.onChange = + inSRGB.onChange = + inOklab.onChange = + inSize.onChange = inGrad.onChange = inSmoothstep.onChange = inDir.onChange = inGradArray.onChange = update; + +inGrad.set("{\"keys\" : [{\"pos\":0,\"r\":0,\"g\":0,\"b\":0},{\"pos\":0.25,\"r\":0,\"g\":0,\"b\":0},{\"pos\":0.75,\"r\":1,\"g\":1,\"b\":1},{\"pos\":1,\"r\":1,\"g\":1,\"b\":1}]}"); + +op.onLoaded = update; + +inRandom.onTriggered = () => +{ + const keys = parseKeys(); + if (keys) + { + keys.forEach((key) => + { + key.r = Math.random(); + key.g = Math.random(); + key.b = Math.random(); + }); + const newKeys = JSON.stringify({ "keys": keys }); + inGrad.set(newKeys); + } +}; + +function rgbToOklab(r, g, b) +{ + let l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b; + let m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b; + let s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b; + l = Math.cbrt(l); m = Math.cbrt(m); s = Math.cbrt(s); + return [ + l * +0.2104542553 + m * +0.7936177850 + s * -0.0040720468, + l * +1.9779984951 + m * -2.4285922050 + s * +0.4505937099, + l * +0.0259040371 + m * +0.7827717662 + s * -0.8086757660 + ]; +} + +function clamp(value, min, max) +{ + return Math.max(Math.min(value, max), min); +} + +function oklabToRGB(L, a, b) +{ + let l = L + a * +0.3963377774 + b * +0.2158037573; + let m = L + a * -0.1055613458 + b * -0.0638541728; + let s = L + a * -0.0894841775 + b * -1.2914855480; + l **= 3; m **= 3; s **= 3; + let r = l * +4.0767416621 + m * -3.3077115913 + s * +0.2309699292; + let g = l * -1.2684380046 + m * +2.6097574011 + s * -0.3413193965; + var b = l * -0.0041960863 + m * -0.7034186147 + s * +1.7076147010; + r = clamp(r, 0, 1); g = clamp(g, 0, 1); b = clamp(b, 0, 1); + return [r, g, b]; +} + +function lin2srgb(r, g, b) +{ + r /= 255; + + const thr = 0.0031308; + + let c_loR = 12.92 * r; + + let c_hiR = 1.055 * Math.pow(r, 0.41666) - 0.055; + return ((r < thr) ? c_loR : c_hiR) * 255; +} + +function update() +{ + const keys = parseKeys(); + if (keys) updateGradient(keys); +} + +function parseKeys() +{ + let keys = null; + op.setUiError("nodata", null); + op.setUiError("parse", null); + + if (Array.isArray(inGradArray.get())) + { + keys = inGradArray.get(); + } + else + { + let grad = null; + if (!inGrad.get() || inGrad.get() === "") + { + op.setUiError("nodata", "gradient no data"); + return null; + } + + try + { + grad = JSON.parse(inGrad.get()); + } + catch (e) + { + op.setUiError("parse", "could not parse gradient data"); + } + + if (!grad || !grad.keys) + { + op.setUiError("nodata", "gradient no data"); + return null; + } + keys = grad.keys; + } + return keys; +} + +function updateGradient(keys) +{ + let width = Math.round(inSize.get()); + if (width < 4) width = 4; + + let selectedWrap = 0; + let selectedFilter = 0; + if (twrap.get() == "repeat") selectedWrap = CGL.Texture.WRAP_REPEAT; + else if (twrap.get() == "mirrored repeat") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + else if (twrap.get() == "clamp to edge") selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + if (tfilter.get() == "nearest") selectedFilter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") selectedFilter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") selectedFilter = CGL.Texture.FILTER_MIPMAP; + + const tex = new CGL.Texture(cgl); + + if (inDir.get() == "X" || inDir.get() == "Y") + { + const pixels = new Uint8Array(width * 4); + + for (let i = 0; i < keys.length - 1; i++) + { + const keyA = keys[i]; + const keyB = keys[i + 1]; + + for (let x = keyA.pos * width; x < keyB.pos * width; x++) + { + let p = CABLES.map(x, keyA.pos * width, keyB.pos * width, 0, 1); + if (inStep.get())p = Math.round(p); + if (inSmoothstep.get()) p = CABLES.smoothStep(p); + x = Math.round(x); + + let xx = x; + if (inFlip.get())xx = width - x - 1; + + if (inOklab.get()) + { + const klabA = rgbToOklab(keyA.r, keyA.g, keyA.b); + const labA_r = klabA[0]; + const labA_g = klabA[1]; + const labA_b = klabA[2]; + + const klabB = rgbToOklab(keyB.r, keyB.g, keyB.b); + const labB_r = klabB[0]; + const labB_g = klabB[1]; + const labB_b = klabB[2]; + + const l = ((p * labB_r + (1.0 - p) * labA_r)); + const a = ((p * labB_g + (1.0 - p) * labA_g)); + const b = ((p * labB_b + (1.0 - p) * labA_b)); + + const pixCol = oklabToRGB(l, a, b); + pixels[xx * 4 + 0] = Math.round(pixCol[0] * 255); + pixels[xx * 4 + 1] = Math.round(pixCol[1] * 255); + pixels[xx * 4 + 2] = Math.round(pixCol[2] * 255); + } + else + { + pixels[xx * 4 + 0] = Math.round((p * keyB.r + (1.0 - p) * keyA.r) * 255); + pixels[xx * 4 + 1] = Math.round((p * keyB.g + (1.0 - p) * keyA.g) * 255); + pixels[xx * 4 + 2] = Math.round((p * keyB.b + (1.0 - p) * keyA.b) * 255); + } + + if (typeof keyA.a !== "undefined" && typeof keyB.a !== "undefined") + { + const alpha = Math.round((p * keyB.a + (1.0 - p) * keyA.a) * 255); + pixels[xx * 4 + 3] = alpha; + } + else + { + pixels[xx * 4 + 3] = Math.round(255); + } + } + } + + if (inSRGB.get()) + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] = lin2srgb(pixels[i + 0]); + pixels[i + 1] = lin2srgb(pixels[i + 1]); + pixels[i + 2] = lin2srgb(pixels[i + 2]); + } + + if (inDir.get() == "X") tex.initFromData(pixels, width, 1, selectedFilter, selectedWrap); + if (inDir.get() == "Y") tex.initFromData(pixels, 1, width, selectedFilter, selectedWrap); + } + + if (inDir.get() == "Radial") + { + const pixels = new Uint8Array(width * width * 4); + + const animR = new CABLES.Anim(); + const animG = new CABLES.Anim(); + const animB = new CABLES.Anim(); + + for (let i = 0; i < keys.length - 1; i++) + { + animR.setValue(keys[i].pos, keys[i].r); + animG.setValue(keys[i].pos, keys[i].g); + animB.setValue(keys[i].pos, keys[i].b); + } + + for (let x = 0; x < width; x++) + { + for (let y = 0; y < width; y++) + { + const dx = x - (width - 1) / 2; + const dy = y - (width - 1) / 2; + let pos = Math.sqrt(dx * dx + dy * dy) / (width) * 2; + + if (inSmoothstep.get()) pos = CABLES.smoothStep(pos); + + pixels[(x * 4) + (y * 4 * width) + 0] = animR.getValue(pos) * 255; + pixels[(x * 4) + (y * 4 * width) + 1] = animG.getValue(pos) * 255; + pixels[(x * 4) + (y * 4 * width) + 2] = animB.getValue(pos) * 255; + pixels[(x * 4) + (y * 4 * width) + 3] = Math.round(255); + } + } + + if (inSRGB.get()) + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] = lin2srgb(pixels[i + 0]); + pixels[i + 1] = lin2srgb(pixels[i + 1]); + pixels[i + 2] = lin2srgb(pixels[i + 2]); + } + + tex.initFromData(pixels, width, width, selectedFilter, selectedWrap); + } + + const colorArr = []; + for (let i = 0; i < keys.length - 1; i++) + { + colorArr.push(keys[i].r, keys[i].g, keys[i].b); + } + + const colorPosArr = []; + for (let i = 0; i < keys.length - 1; i++) + { + colorPosArr.push(keys[i].pos); + } + + outColors.set(colorArr); + outColorPos.set(colorPosArr); + + outTex.set(null); + outTex.set(tex); +} + + +}; + +Ops.Gl.GradientTexture.prototype = new CABLES.Op(); +CABLES.OPS["01380a50-2dbb-4465-ae80-86349b0b717a"]={f:Ops.Gl.GradientTexture,objName:"Ops.Gl.GradientTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.OrbitControls +// +// ************************************************************** + +Ops.Gl.Matrix.OrbitControls = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + minDist = op.inValueFloat("min distance"), + maxDist = op.inValueFloat("max distance"), + + minRotY = op.inValue("min rot y", 0), + maxRotY = op.inValue("max rot y", 0), + + initialRadius = op.inValue("initial radius", 0), + initialAxis = op.inValueSlider("initial axis y"), + initialX = op.inValueSlider("initial axis x"), + + mul = op.inValueFloat("mul"), + smoothness = op.inValueSlider("Smoothness", 1.0), + speedX = op.inValue("Speed X", 1), + speedY = op.inValue("Speed Y", 1), + + active = op.inValueBool("Active", true), + + allowPanning = op.inValueBool("Allow Panning", true), + allowZooming = op.inValueBool("Allow Zooming", true), + allowRotation = op.inValueBool("Allow Rotation", true), + restricted = op.inValueBool("restricted", true), + + trigger = op.outTrigger("trigger"), + outRadius = op.outNumber("radius"), + outXDeg = op.outNumber("Rot X"), + outYDeg = op.outNumber("Rot Y"), + + inReset = op.inTriggerButton("Reset"); + +op.setPortGroup("Initial Values", [initialAxis, initialX, initialRadius]); +op.setPortGroup("Interaction", [mul, smoothness, speedX, speedY]); +op.setPortGroup("Boundaries", [minRotY, maxRotY, minDist, maxDist]); + +mul.set(1); +minDist.set(0.05); +maxDist.set(99999); + +inReset.onTriggered = reset; + +let eye = vec3.create(); +const vUp = vec3.create(); +const vCenter = vec3.create(); +const viewMatrix = mat4.create(); +const tempViewMatrix = mat4.create(); +const vOffset = vec3.create(); +const finalEyeAbs = vec3.create(); + +initialAxis.set(0.5); + +let mouseDown = false; +let radius = 5; +outRadius.set(radius); + +let lastMouseX = 0, lastMouseY = 0; +let percX = 0, percY = 0; + +vec3.set(vCenter, 0, 0, 0); +vec3.set(vUp, 0, 1, 0); + +const tempEye = vec3.create(); +const finalEye = vec3.create(); +const tempCenter = vec3.create(); +const finalCenter = vec3.create(); + +let px = 0; +let py = 0; + +let divisor = 1; +let element = null; +updateSmoothness(); + +op.onDelete = unbind; + +const halfCircle = Math.PI; +const fullCircle = Math.PI * 2; + +function reset() +{ + let off = 0; + + if (px % fullCircle < -halfCircle) + { + off = -fullCircle; + px %= -fullCircle; + } + else + if (px % fullCircle > halfCircle) + { + off = fullCircle; + px %= fullCircle; + } + else px %= fullCircle; + + py %= (Math.PI); + + vec3.set(vOffset, 0, 0, 0); + vec3.set(vCenter, 0, 0, 0); + vec3.set(vUp, 0, 1, 0); + + percX = (initialX.get() * Math.PI * 2 + off); + percY = (initialAxis.get() - 0.5); + + radius = initialRadius.get(); + eye = circlePos(percY); +} + +function updateSmoothness() +{ + divisor = smoothness.get() * 10 + 1.0; +} + +smoothness.onChange = updateSmoothness; + +let initializing = true; + +function ip(val, goal) +{ + if (initializing) return goal; + return val + (goal - val) / divisor; +} + +let lastPy = 0; +const lastPx = 0; + +render.onTriggered = function () +{ + const cgl = op.patch.cg; + + if (!element) + { + setElement(cgl.canvas); + bind(); + } + + cgl.pushViewMatrix(); + + px = ip(px, percX); + py = ip(py, percY); + + let degY = (py + 0.5) * 180; + + if (minRotY.get() !== 0 && degY < minRotY.get()) + { + degY = minRotY.get(); + py = lastPy; + } + else if (maxRotY.get() !== 0 && degY > maxRotY.get()) + { + degY = maxRotY.get(); + py = lastPy; + } + else + { + lastPy = py; + } + + const degX = (px) * CGL.RAD2DEG; + + outYDeg.set(degY); + outXDeg.set(degX); + + circlePosi(eye, py); + + vec3.add(tempEye, eye, vOffset); + vec3.add(tempCenter, vCenter, vOffset); + + finalEye[0] = ip(finalEye[0], tempEye[0]); + finalEye[1] = ip(finalEye[1], tempEye[1]); + finalEye[2] = ip(finalEye[2], tempEye[2]); + + finalCenter[0] = ip(finalCenter[0], tempCenter[0]); + finalCenter[1] = ip(finalCenter[1], tempCenter[1]); + finalCenter[2] = ip(finalCenter[2], tempCenter[2]); + + const empty = vec3.create(); + + mat4.lookAt(viewMatrix, finalEye, finalCenter, vUp); + mat4.rotate(viewMatrix, viewMatrix, px, vUp); + + // finaly multiply current scene viewmatrix + mat4.multiply(cgl.vMatrix, cgl.vMatrix, viewMatrix); + + trigger.trigger(); + cgl.popViewMatrix(); + initializing = false; +}; + +function circlePosi(vec, perc) +{ + const mmul = mul.get(); + if (radius < minDist.get() * mmul) radius = minDist.get() * mmul; + if (radius > maxDist.get() * mmul) radius = maxDist.get() * mmul; + + outRadius.set(radius * mmul); + + let i = 0, degInRad = 0; + + degInRad = 360 * perc / 2 * CGL.DEG2RAD; + vec3.set(vec, + Math.cos(degInRad) * radius * mmul, + Math.sin(degInRad) * radius * mmul, + 0); + return vec; +} + +function circlePos(perc) +{ + const mmul = mul.get(); + if (radius < minDist.get() * mmul)radius = minDist.get() * mmul; + if (radius > maxDist.get() * mmul)radius = maxDist.get() * mmul; + + outRadius.set(radius * mmul); + + let i = 0, degInRad = 0; + const vec = vec3.create(); + degInRad = 360 * perc / 2 * CGL.DEG2RAD; + vec3.set(vec, + Math.cos(degInRad) * radius * mmul, + Math.sin(degInRad) * radius * mmul, + 0); + return vec; +} + +function onmousemove(event) +{ + if (!mouseDown) return; + + const x = event.clientX; + const y = event.clientY; + + let movementX = (x - lastMouseX); + let movementY = (y - lastMouseY); + + movementX *= speedX.get(); + movementY *= speedY.get(); + + if (event.buttons == 2 && allowPanning.get()) + { + vOffset[2] += movementX * 0.01 * mul.get(); + vOffset[1] += movementY * 0.01 * mul.get(); + } + else + if (event.buttons == 4 && allowZooming.get()) + { + radius += movementY * 0.05; + eye = circlePos(percY); + } + else + { + if (allowRotation.get()) + { + percX += movementX * 0.003; + percY += movementY * 0.002; + + if (restricted.get()) + { + if (percY > 0.5)percY = 0.5; + if (percY < -0.5)percY = -0.5; + } + } + } + + lastMouseX = x; + lastMouseY = y; +} + +function onMouseDown(event) +{ + lastMouseX = event.clientX; + lastMouseY = event.clientY; + mouseDown = true; + + try { element.setPointerCapture(event.pointerId); } + catch (e) {} +} + +function onMouseUp(e) +{ + mouseDown = false; + // cgl.canvas.style.cursor='url(/ui/img/rotate.png),pointer'; + + try { element.releasePointerCapture(e.pointerId); } + catch (e) {} +} + +function lockChange() +{ + const el = op.patch.cg.canvas; + + if (document.pointerLockElement === el || document.mozPointerLockElement === el || document.webkitPointerLockElement === el) + { + document.addEventListener("mousemove", onmousemove, false); + } +} + +function onMouseEnter(e) +{ + // cgl.canvas.style.cursor='url(/ui/img/rotate.png),pointer'; +} + +initialRadius.onChange = function () +{ + radius = initialRadius.get(); + reset(); +}; + +initialX.onChange = function () +{ + px = percX = (initialX.get() * Math.PI * 2); +}; + +initialAxis.onChange = function () +{ + py = percY = (initialAxis.get() - 0.5); + eye = circlePos(percY); +}; + +const onMouseWheel = function (event) +{ + if (allowZooming.get()) + { + const delta = CGL.getWheelSpeed(event) * 0.06; + radius += (parseFloat(delta)) * 1.2; + + eye = circlePos(percY); + } +}; + +const ontouchstart = function (event) +{ + if (event.touches && event.touches.length > 0) onMouseDown(event.touches[0]); +}; + +const ontouchend = function (event) +{ + onMouseUp(); +}; + +const ontouchmove = function (event) +{ + if (event.touches && event.touches.length > 0) onmousemove(event.touches[0]); +}; + +active.onChange = function () +{ + if (active.get())bind(); + else unbind(); +}; + +function setElement(ele) +{ + unbind(); + element = ele; + bind(); +} + +function bind() +{ + if (!element) return; + + element.addEventListener("pointermove", onmousemove); + element.addEventListener("pointerdown", onMouseDown); + element.addEventListener("pointerup", onMouseUp); + element.addEventListener("pointerleave", onMouseUp); + element.addEventListener("pointerenter", onMouseEnter); + element.addEventListener("contextmenu", function (e) { e.preventDefault(); }); + element.addEventListener("wheel", onMouseWheel, { "passive": true }); +} + +function unbind() +{ + if (!element) return; + + element.removeEventListener("pointermove", onmousemove); + element.removeEventListener("pointerdown", onMouseDown); + element.removeEventListener("pointerup", onMouseUp); + element.removeEventListener("pointerleave", onMouseUp); + element.removeEventListener("pointerenter", onMouseUp); + element.removeEventListener("wheel", onMouseWheel); +} + +eye = circlePos(0); + +initialX.set(0.25); +initialRadius.set(0.05); + + +}; + +Ops.Gl.Matrix.OrbitControls.prototype = new CABLES.Op(); +CABLES.OPS["eaf4f7ce-08a3-4d1b-b9f4-ebc0b7b1cde1"]={f:Ops.Gl.Matrix.OrbitControls,objName:"Ops.Gl.Matrix.OrbitControls"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Vibrance +// +// ************************************************************** + +Ops.Gl.TextureEffects.Vibrance = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"vibrance_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\n\nconst vec4 lumcoeff = vec4(0.299,0.587,0.114, 0.);\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n col=texture(tex,texCoord);\n\n float luma = dot(col, lumcoeff);\n vec4 mask = (col - vec4(luma));\n mask = clamp(mask, 0.0, 1.0);\n float lumaMask = dot(lumcoeff, mask);\n lumaMask = 1.0 - lumaMask;\n vec4 vibrance = mix(vec4(luma), col, 1.0 + amount * lumaMask);\n outColor= vibrance;\n}",}; +const render = op.inTrigger("Render"); +const trigger = op.outTrigger("Trigger"); +const amount = op.inValue("amount", 2); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.vibrance_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Vibrance.prototype = new CABLES.Op(); +CABLES.OPS["9c71c980-e439-4397-9c2b-c2ae085eaed9"]={f:Ops.Gl.TextureEffects.Vibrance,objName:"Ops.Gl.TextureEffects.Vibrance"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.FullscreenRectangle +// +// ************************************************************** + +Ops.Gl.Meshes.FullscreenRectangle = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"shader_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\n\nvoid main()\n{\n outColor= texture(tex,texCoord);\n}\n\n","shader_vert":"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\n\nOUT vec2 texCoord;\nIN vec2 attrTexCoord;\n\nvoid main()\n{\n vec4 pos=vec4(vPosition, 1.0);\n\n texCoord=vec2(attrTexCoord.x,(1.0-attrTexCoord.y));\n\n gl_Position = projMatrix * mvMatrix * pos;\n}\n",}; +const + render = op.inTrigger("render"), + inScale = op.inSwitch("Scale", ["Stretch", "Fit"], "Fit"), + flipY = op.inValueBool("Flip Y"), + flipX = op.inValueBool("Flip X"), + inTexture = op.inTexture("Texture"), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +let mesh = null; +let geom = new CGL.Geometry("fullscreen rectangle"); +let x = 0, y = 0, z = 0, w = 0, h = 0; + +flipX.onChange = rebuildFlip; +flipY.onChange = rebuildFlip; +render.onTriggered = doRender; +inTexture.onLinkChanged = updateUi; +op.toWorkPortsNeedToBeLinked(render); + +const shader = new CGL.Shader(cgl, "fullscreenrectangle"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); + +shader.setSource(attachments.shader_vert, attachments.shader_frag); +shader.fullscreenRectUniform = new CGL.Uniform(shader, "t", "tex", 0); +shader.aspectUni = new CGL.Uniform(shader, "f", "aspectTex", 0); + +let useShader = false; +let updateShaderLater = true; +let fitImageAspect = false; +let oldVp = []; + +updateUi(); + +inTexture.onChange = function () +{ + updateShaderLater = true; +}; + +function updateUi() +{ + if (!CABLES.UI) return; + flipY.setUiAttribs({ "greyout": !inTexture.isLinked() }); + flipX.setUiAttribs({ "greyout": !inTexture.isLinked() }); + inScale.setUiAttribs({ "greyout": !inTexture.isLinked() }); +} + +function updateShader() +{ + let tex = inTexture.get(); + if (tex) useShader = true; + else useShader = false; +} + +op.preRender = function () +{ + updateShader(); + shader.bind(); + if (mesh)mesh.render(shader); + doRender(); +}; + +inScale.onChange = () => +{ + fitImageAspect = inScale.get() == "Fit"; +}; + +function doRender() +{ + if (cgl.getViewPort()[2] != w || cgl.getViewPort()[3] != h || !mesh) rebuild(); + + if (updateShaderLater) updateShader(); + + cgl.pushPMatrix(); + mat4.identity(cgl.pMatrix); + mat4.ortho(cgl.pMatrix, 0, w, h, 0, -10.0, 1000); + + cgl.pushModelMatrix(); + mat4.identity(cgl.mMatrix); + + cgl.pushViewMatrix(); + mat4.identity(cgl.vMatrix); + + if (fitImageAspect && inTexture.get()) + { + const rat = inTexture.get().width / inTexture.get().height; + + let _h = h; + let _w = h * rat; + + if (_w > w) + { + _h = w * 1 / rat; + _w = w; + } + + oldVp[0] = cgl.getViewPort()[0]; + oldVp[1] = cgl.getViewPort()[1]; + oldVp[2] = cgl.getViewPort()[2]; + oldVp[3] = cgl.getViewPort()[3]; + + cgl.setViewPort((w - _w) / 2, (h - _h) / 2, _w, _h); + // cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + } + + if (useShader) + { + if (inTexture.get()) + cgl.setTexture(0, inTexture.get().tex); + + mesh.render(shader); + } + else + { + mesh.render(cgl.getShader()); + } + + cgl.gl.clear(cgl.gl.DEPTH_BUFFER_BIT); + + cgl.popPMatrix(); + cgl.popModelMatrix(); + cgl.popViewMatrix(); + + if (fitImageAspect && inTexture.get()) + cgl.setViewPort(oldVp[0], oldVp[1], oldVp[2], oldVp[3]); + + trigger.trigger(); +} + +function rebuildFlip() +{ + mesh = null; +} + +function rebuild() +{ + const currentViewPort = cgl.getViewPort(); + + if (currentViewPort[2] == w && currentViewPort[3] == h && mesh) return; + + let xx = 0, xy = 0; + + w = currentViewPort[2]; + h = currentViewPort[3]; + + geom.vertices = new Float32Array([ + xx + w, xy + h, 0.0, + xx, xy + h, 0.0, + xx + w, xy, 0.0, + xx, xy, 0.0 + ]); + + let tc = null; + + if (flipY.get()) + tc = new Float32Array([ + 1.0, 0.0, + 0.0, 0.0, + 1.0, 1.0, + 0.0, 1.0 + ]); + else + tc = new Float32Array([ + 1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 0.0, 0.0 + ]); + + if (flipX.get()) + { + tc[0] = 0.0; + tc[2] = 1.0; + tc[4] = 0.0; + tc[6] = 1.0; + } + + geom.setTexCoords(tc); + + geom.verticesIndices = new Uint16Array([ + 2, 1, 0, + 3, 1, 2 + ]); + + geom.vertexNormals = new Float32Array([ + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + ]); + geom.tangents = new Float32Array([ + -1, 0, 0, + -1, 0, 0, + -1, 0, 0, + -1, 0, 0]); + geom.biTangents == new Float32Array([ + 0, -1, 0, + 0, -1, 0, + 0, -1, 0, + 0, -1, 0]); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); +} + + +}; + +Ops.Gl.Meshes.FullscreenRectangle.prototype = new CABLES.Op(); +CABLES.OPS["255bd15b-cc91-4a12-9b4e-53c710cbb282"]={f:Ops.Gl.Meshes.FullscreenRectangle,objName:"Ops.Gl.Meshes.FullscreenRectangle"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.TranslateView +// +// ************************************************************** + +Ops.Gl.Matrix.TranslateView = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render=op.inTrigger('render'), + x=op.inValueFloat("x"), + y=op.inValueFloat("y"), + z=op.inValueFloat("z"), + trigger=op.outTrigger('trigger'); + +const cgl=op.patch.cgl; +const vec=vec3.create(); + +render.onTriggered=function() +{ + vec3.set(vec, x.get(),y.get(),z.get()); + cgl.pushViewMatrix(); + mat4.translate(cgl.vMatrix,cgl.vMatrix, vec); + trigger.trigger(); + cgl.popViewMatrix(); +}; + + +}; + +Ops.Gl.Matrix.TranslateView.prototype = new CABLES.Op(); +CABLES.OPS["b15472e2-b895-4dde-95c3-239fa5e08afc"]={f:Ops.Gl.Matrix.TranslateView,objName:"Ops.Gl.Matrix.TranslateView"}; + + + + +// ************************************************************** +// +// Ops.Gl.Texture_v2 +// +// ************************************************************** + +Ops.Gl.Texture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + filename = op.inUrl("File", [".jpg", ".png", ".webp", ".jpeg", ".avif"]), + tfilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"]), + wrap = op.inValueSelect("Wrap", ["repeat", "mirrored repeat", "clamp to edge"], "clamp to edge"), + aniso = op.inSwitch("Anisotropic", [0, 1, 2, 4, 8, 16], 0), + flip = op.inValueBool("Flip", false), + unpackAlpha = op.inValueBool("Pre Multiplied Alpha", false), + active = op.inValueBool("Active", true), + inFreeMemory = op.inBool("Save Memory", true), + textureOut = op.outTexture("Texture"), + width = op.outNumber("Width"), + height = op.outNumber("Height"), + ratio = op.outNumber("Aspect Ratio"), + loaded = op.outNumber("Loaded", false), + loading = op.outNumber("Loading", false); + +op.setPortGroup("Size", [width, height]); + +unpackAlpha.setUiAttribs({ "hidePort": true }); + +op.toWorkPortsNeedToBeLinked(textureOut); + +const cgl = op.patch.cgl; + +let loadedFilename = null; +let loadingId = null; +let tex = null; +let cgl_filter = CGL.Texture.FILTER_MIPMAP; +let cgl_wrap = CGL.Texture.WRAP_REPEAT; +let cgl_aniso = 0; +let timedLoader = 0; + +filename.onChange = flip.onChange = function () { reloadSoon(); }; +aniso.onChange = tfilter.onChange = onFilterChange; +wrap.onChange = onWrapChange; +unpackAlpha.onChange = function () { reloadSoon(); }; + +tfilter.set("mipmap"); +wrap.set("repeat"); + +textureOut.set(CGL.Texture.getEmptyTexture(cgl)); + +active.onChange = function () +{ + if (active.get()) + { + if (loadedFilename != filename.get() || !tex) reloadSoon(); + else textureOut.set(tex); + } + else + { + textureOut.set(CGL.Texture.getEmptyTexture(cgl)); + width.set(CGL.Texture.getEmptyTexture(cgl).width); + height.set(CGL.Texture.getEmptyTexture(cgl).height); + if (tex)tex.delete(); + tex = null; + } +}; + +const setTempTexture = function () +{ + const t = CGL.Texture.getTempTexture(cgl); + textureOut.set(t); +}; + +function reloadSoon(nocache) +{ + clearTimeout(timedLoader); + timedLoader = setTimeout(function () + { + realReload(nocache); + }, 30); +} + +function realReload(nocache) +{ + if (!active.get()) return; + // if (filename.get() === null) return; + if (!loadingId)loadingId = cgl.patch.loading.start("textureOp", filename.get()); + + let url = op.patch.getFilePath(String(filename.get())); + + if (nocache)url += "?rnd=" + CABLES.uuid(); + + if (String(filename.get()).indexOf("data:") == 0) url = filename.get(); + + let needsRefresh = false; + if (loadedFilename != filename.get()) needsRefresh = true; + loadedFilename = filename.get(); + + if ((filename.get() && filename.get().length > 1)) + { + loaded.set(false); + loading.set(true); + + const fileToLoad = filename.get(); + + op.setUiAttrib({ "extendTitle": CABLES.basename(url) }); + if (needsRefresh) op.refreshParams(); + + cgl.patch.loading.addAssetLoadingTask(() => + { + op.setUiError("urlerror", null); + + CGL.Texture.load(cgl, url, + function (err, newTex) + { + if (filename.get() != fileToLoad) + { + cgl.patch.loading.finished(loadingId); + loadingId = null; + return; + } + + if (err) + { + setTempTexture(); + op.setUiError("urlerror", "could not load texture: \"" + filename.get() + "\"", 2); + cgl.patch.loading.finished(loadingId); + return; + } + + textureOut.set(newTex); + + width.set(newTex.width); + height.set(newTex.height); + ratio.set(newTex.width / newTex.height); + + // if (!newTex.isPowerOfTwo()) op.setUiError("npot", "Texture dimensions not power of two! - Texture filtering will not work in WebGL 1.", 0); + // else op.setUiError("npot", null); + + if (tex)tex.delete(); + tex = newTex; + textureOut.set(null); + textureOut.set(tex); + + loading.set(false); + loaded.set(true); + + if (inFreeMemory.get()) tex.image = null; + + cgl.patch.loading.finished(loadingId); + }, { + "anisotropic": cgl_aniso, + "wrap": cgl_wrap, + "flip": flip.get(), + "unpackAlpha": unpackAlpha.get(), + "filter": cgl_filter + }); + + // textureOut.set(null); + // textureOut.set(tex); + }); + } + else + { + cgl.patch.loading.finished(loadingId); + setTempTexture(); + } +} + +function onFilterChange() +{ + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + else if (tfilter.get() == "Anisotropic") cgl_filter = CGL.Texture.FILTER_ANISOTROPIC; + + cgl_aniso = parseFloat(aniso.get()); + + reloadSoon(); +} + +function onWrapChange() +{ + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + reloadSoon(); +} + +op.onFileChanged = function (fn) +{ + if (filename.get() && filename.get().indexOf(fn) > -1) + { + textureOut.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + textureOut.set(CGL.Texture.getTempTexture(cgl)); + realReload(true); + } +}; + + +}; + +Ops.Gl.Texture_v2.prototype = new CABLES.Op(); +CABLES.OPS["790f3702-9833-464e-8e37-6f0f813f7e16"]={f:Ops.Gl.Texture_v2,objName:"Ops.Gl.Texture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Rectangle_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.Rectangle_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + width = op.inValue("width", 1), + height = op.inValue("height", 1), + pivotX = op.inSwitch("pivot x", ["left", "center", "right"]), + pivotY = op.inSwitch("pivot y", ["top", "center", "bottom"]), + nColumns = op.inValueInt("num columns", 1), + nRows = op.inValueInt("num rows", 1), + axis = op.inSwitch("axis", ["xy", "xz"], "xy"), + active = op.inValueBool("Active", true), + geomOut = op.outObject("geometry", null, "geometry"); + +geomOut.ignoreValueSerialize = true; + +const cgl = op.patch.cgl; +axis.set("xy"); +pivotX.set("center"); +pivotY.set("center"); + +op.setPortGroup("Pivot", [pivotX, pivotY]); +op.setPortGroup("Size", [width, height]); +op.setPortGroup("Structure", [nColumns, nRows]); +op.toWorkPortsNeedToBeLinked(render); + +const geom = new CGL.Geometry("rectangle"); +let mesh = null; +let needsRebuild = false; + +axis.onChange = + pivotX.onChange = + pivotY.onChange = + width.onChange = + height.onChange = + nRows.onChange = + nColumns.onChange = rebuildLater; +rebuild(); + +function rebuildLater() +{ + needsRebuild = true; +} + +op.preRender = +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpNotInTextureEffect(op)) return; + if (needsRebuild)rebuild(); + if (active.get() && mesh) mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +function rebuild() +{ + let w = width.get(); + let h = parseFloat(height.get()); + let x = 0; + let y = 0; + + if (typeof w == "string")w = parseFloat(w); + if (typeof h == "string")h = parseFloat(h); + + if (pivotX.get() == "center") x = 0; + else if (pivotX.get() == "right") x = -w / 2; + else if (pivotX.get() == "left") x = +w / 2; + + if (pivotY.get() == "center") y = 0; + else if (pivotY.get() == "top") y = -h / 2; + else if (pivotY.get() == "bottom") y = +h / 2; + + const verts = []; + const tc = []; + const norms = []; + const tangents = []; + const biTangents = []; + const indices = []; + + const numRows = Math.round(nRows.get()); + const numColumns = Math.round(nColumns.get()); + + const stepColumn = w / numColumns; + const stepRow = h / numRows; + + let c, r, a; + a = axis.get(); + for (r = 0; r <= numRows; r++) + { + for (c = 0; c <= numColumns; c++) + { + verts.push(c * stepColumn - width.get() / 2 + x); + if (a == "xz") verts.push(0.0); + verts.push(r * stepRow - height.get() / 2 + y); + if (a == "xy") verts.push(0.0); + + tc.push(c / numColumns); + tc.push(1.0 - r / numRows); + + if (a == "xz") + { + norms.push(0, 1, 0); + tangents.push(1, 0, 0); + biTangents.push(0, 0, 1); + } + else if (a == "xy") + { + norms.push(0, 0, 1); + tangents.push(-1, 0, 0); + biTangents.push(0, -1, 0); + } + } + } + + for (c = 0; c < numColumns; c++) + { + for (r = 0; r < numRows; r++) + { + const ind = c + (numColumns + 1) * r; + const v1 = ind; + const v2 = ind + 1; + const v3 = ind + numColumns + 1; + const v4 = ind + 1 + numColumns + 1; + + indices.push(v1); + indices.push(v3); + indices.push(v2); + + indices.push(v2); + indices.push(v3); + indices.push(v4); + } + } + + geom.clear(); + geom.vertices = verts; + geom.texCoords = tc; + geom.verticesIndices = indices; + geom.vertexNormals = norms; + geom.tangents = tangents; + geom.biTangents = biTangents; + + if (numColumns * numRows > 64000)geom.unIndex(); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); + + geomOut.set(null); + geomOut.set(geom); + needsRebuild = false; +} + +op.onDelete = function () +{ + if (mesh)mesh.dispose(); +}; + + +}; + +Ops.Gl.Meshes.Rectangle_v2.prototype = new CABLES.Op(); +CABLES.OPS["fc5718d6-11a5-496e-8f16-1c78b1a2824c"]={f:Ops.Gl.Meshes.Rectangle_v2,objName:"Ops.Gl.Meshes.Rectangle_v2"}; + + + + +// ************************************************************** +// +// Ops.Anim.Timer_v2 +// +// ************************************************************** + +Ops.Anim.Timer_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inSpeed = op.inValue("Speed", 1), + playPause = op.inValueBool("Play", true), + reset = op.inTriggerButton("Reset"), + inSyncTimeline = op.inValueBool("Sync to timeline", false), + outTime = op.outNumber("Time"); + +op.setPortGroup("Controls", [playPause, reset, inSpeed]); + +const timer = new CABLES.Timer(); +let lastTime = null; +let time = 0; +let syncTimeline = false; + +playPause.onChange = setState; +setState(); + +function setState() +{ + if (playPause.get()) + { + timer.play(); + op.patch.addOnAnimFrame(op); + } + else + { + timer.pause(); + op.patch.removeOnAnimFrame(op); + } +} + +reset.onTriggered = doReset; + +function doReset() +{ + time = 0; + lastTime = null; + timer.setTime(0); + outTime.set(0); +} + +inSyncTimeline.onChange = function () +{ + syncTimeline = inSyncTimeline.get(); + playPause.setUiAttribs({ "greyout": syncTimeline }); + reset.setUiAttribs({ "greyout": syncTimeline }); +}; + +op.onAnimFrame = function (tt) +{ + if (timer.isPlaying()) + { + if (CABLES.overwriteTime !== undefined) + { + outTime.set(CABLES.overwriteTime * inSpeed.get()); + } + else + + if (syncTimeline) + { + outTime.set(tt * inSpeed.get()); + } + else + { + timer.update(); + const timerVal = timer.get(); + + if (lastTime === null) + { + lastTime = timerVal; + return; + } + + const t = Math.abs(timerVal - lastTime); + lastTime = timerVal; + + time += t * inSpeed.get(); + if (time != time)time = 0; + outTime.set(time); + } + } +}; + + +}; + +Ops.Anim.Timer_v2.prototype = new CABLES.Op(); +CABLES.OPS["aac7f721-208f-411a-adb3-79adae2e471a"]={f:Ops.Anim.Timer_v2,objName:"Ops.Anim.Timer_v2"}; + + + + +// ************************************************************** +// +// Ops.Math.MapRange +// +// ************************************************************** + +Ops.Math.MapRange = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v = op.inValueFloat("value", 0), + old_min = op.inValueFloat("old min", 0), + old_max = op.inValueFloat("old max", 1), + new_min = op.inValueFloat("new min", -1), + new_max = op.inValueFloat("new max", 1), + easing = op.inValueSelect("Easing", ["Linear", "Smoothstep", "Smootherstep"], "Linear"), + result = op.outNumber("result", 0); + +op.setPortGroup("Input Range", [old_min, old_max]); +op.setPortGroup("Output Range", [new_min, new_max]); + +let ease = 0; +let r = 0; + +v.onChange = + old_min.onChange = + old_max.onChange = + new_min.onChange = + new_max.onChange = exec; + +exec(); + +easing.onChange = function () +{ + if (easing.get() == "Smoothstep") ease = 1; + else if (easing.get() == "Smootherstep") ease = 2; + else ease = 0; +}; + +function exec() +{ + const nMin = new_min.get(); + const nMax = new_max.get(); + const oMin = old_min.get(); + const oMax = old_max.get(); + let x = v.get(); + + if (x >= Math.max(oMax, oMin)) + { + result.set(nMax); + return; + } + else + if (x <= Math.min(oMax, oMin)) + { + result.set(nMin); + return; + } + + let reverseInput = false; + const oldMin = Math.min(oMin, oMax); + const oldMax = Math.max(oMin, oMax); + if (oldMin != oMin) reverseInput = true; + + let reverseOutput = false; + const newMin = Math.min(nMin, nMax); + const newMax = Math.max(nMin, nMax); + if (newMin != nMin) reverseOutput = true; + + let portion = 0; + + if (reverseInput) portion = (oldMax - x) * (newMax - newMin) / (oldMax - oldMin); + else portion = (x - oldMin) * (newMax - newMin) / (oldMax - oldMin); + + if (reverseOutput) r = newMax - portion; + else r = portion + newMin; + + if (ease === 0) + { + result.set(r); + } + else + if (ease == 1) + { + x = Math.max(0, Math.min(1, (r - nMin) / (nMax - nMin))); + result.set(nMin + x * x * (3 - 2 * x) * (nMax - nMin)); // smoothstep + } + else + if (ease == 2) + { + x = Math.max(0, Math.min(1, (r - nMin) / (nMax - nMin))); + result.set(nMin + x * x * x * (x * (x * 6 - 15) + 10) * (nMax - nMin)); // smootherstep + } +} + + +}; + +Ops.Math.MapRange.prototype = new CABLES.Op(); +CABLES.OPS["2617b407-60a0-4ff6-b4a7-18136cfa7817"]={f:Ops.Math.MapRange,objName:"Ops.Math.MapRange"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.IfBetweenThen +// +// ************************************************************** + +Ops.Math.Compare.IfBetweenThen = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + number = op.inValue("number", 0), + min = op.inValue("min", 0), + max = op.inValue("max", 1), + triggerThen = op.outTrigger("then"), + triggerElse = op.outTrigger("else"), + outBetween = op.outBoolNum("bs between"); + +exe.onTriggered = function () +{ + if (number.get() >= min.get() && number.get() < max.get()) + { + outBetween.set(true); + triggerThen.trigger(); + } + else + { + outBetween.set(false); + triggerElse.trigger(); + } +}; + + +}; + +Ops.Math.Compare.IfBetweenThen.prototype = new CABLES.Op(); +CABLES.OPS["c80437f0-f0e1-465c-9cea-8a044aa2feaa"]={f:Ops.Math.Compare.IfBetweenThen,objName:"Ops.Math.Compare.IfBetweenThen"}; + + + + +// ************************************************************** +// +// Ops.Gl.ViewPortSize +// +// ************************************************************** + +Ops.Gl.ViewPortSize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Exec"), + next = op.outTrigger("Next"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outW = op.outNumber("Width"), + outH = op.outNumber("Height"); + +exec.onTriggered = function () +{ + const vp = op.patch.cgl.getViewPort(); + + outX.set(vp[0]); + outY.set(vp[1]); + outW.set(vp[2]); + outH.set(vp[3]); + + next.trigger(); +}; + + +}; + +Ops.Gl.ViewPortSize.prototype = new CABLES.Op(); +CABLES.OPS["7cb99d8f-d7ef-478e-902b-54e054e387a0"]={f:Ops.Gl.ViewPortSize,objName:"Ops.Gl.ViewPortSize"}; + + + + +// ************************************************************** +// +// Ops.Trigger.SetNumberOnTrigger +// +// ************************************************************** + +Ops.Trigger.SetNumberOnTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + setValuePort = op.inTriggerButton("Set"), + valuePort = op.inValueFloat("Number"), + outNext = op.outTrigger("Next"), + outValuePort = op.outNumber("Out Value"); + +outValuePort.changeAlways = true; + +setValuePort.onTriggered = function () +{ + outValuePort.set(valuePort.get()); + outNext.trigger(); +}; + + +}; + +Ops.Trigger.SetNumberOnTrigger.prototype = new CABLES.Op(); +CABLES.OPS["9989b1c0-1073-4d5f-bfa0-36dd98b66e27"]={f:Ops.Trigger.SetNumberOnTrigger,objName:"Ops.Trigger.SetNumberOnTrigger"}; + + + + +// ************************************************************** +// +// Ops.Color.HexToRGB_v2 +// +// ************************************************************** + +Ops.Color.HexToRGB_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + hex = op.inString("Hex", "#ff0000"), + asBytes = op.inValueBool("Bytes"), + outR = op.outNumber("R"), + outG = op.outNumber("G"), + outB = op.outNumber("B"); + +function hexToR(h) +{ + return parseInt((cutHex(h)).substring(0, 2), 16) || 0; +} +function hexToG(h) +{ + return parseInt((cutHex(h)).substring(2, 4), 16) || 0; +} +function hexToB(h) +{ + return parseInt((cutHex(h)).substring(4, 6), 16) || 0; +} +function cutHex(h) +{ + return (h.charAt(0) == "#") ? h.substring(1, 7) : h; +} + +hex.onChange = parse; +asBytes.onChange = parse; + +function parse() +{ + let str = hex.get() || ""; + let r = hexToR(str); + let g = hexToG(str); + let b = hexToB(str); + + if (!asBytes.get()) + { + r /= 255; + g /= 255; + b /= 255; + } + + outR.set(r); + outB.set(b); + outG.set(g); +} + + +}; + +Ops.Color.HexToRGB_v2.prototype = new CABLES.Op(); +CABLES.OPS["9877f198-8dac-48e5-9310-244ef1a8dec5"]={f:Ops.Color.HexToRGB_v2,objName:"Ops.Color.HexToRGB_v2"}; + + + + +// ************************************************************** +// +// Ops.String.String_v2 +// +// ************************************************************** + +Ops.String.String_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v=op.inString("value",""), + result=op.outString("String"); + +v.onChange=function() +{ + result.set(v.get()); +}; + + + +}; + +Ops.String.String_v2.prototype = new CABLES.Op(); +CABLES.OPS["d697ff82-74fd-4f31-8f54-295bc64e713d"]={f:Ops.String.String_v2,objName:"Ops.String.String_v2"}; + + + + +// ************************************************************** +// +// Ops.Value.Number +// +// ************************************************************** + +Ops.Value.Number = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v = op.inValueFloat("value"), + result = op.outNumber("result"); + +v.onChange = exec; + +function exec() +{ + result.set(Number(v.get())); +} + + +}; + +Ops.Value.Number.prototype = new CABLES.Op(); +CABLES.OPS["8fb2bb5d-665a-4d0a-8079-12710ae453be"]={f:Ops.Value.Number,objName:"Ops.Value.Number"}; + + + + +// ************************************************************** +// +// Ops.Ui.PatchInput +// +// ************************************************************** + +Ops.Ui.PatchInput = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const dyn = op.addOutPort(new CABLES.Port(op, "create port", CABLES.OP_PORT_TYPE_DYNAMIC)); + +function getPatchOp() +{ + for (let i in op.patch.ops) + { + if (op.patch.ops[i].patchId) + { + if (op.patch.ops[i].patchId.get() == op.uiAttribs.subPatch) + { + return op.patch.ops[i]; + } + } + } +} + +dyn.onLinkChanged = () => +{ + const mySubPatchOp = getPatchOp(); + + if (!dyn.links.length) return; + + const otherPort = dyn.links[0].getOtherPort(dyn); + dyn.removeLinks(); + + const newPortName = mySubPatchOp.addNewInPort(otherPort); + + const l = gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newPortName); + + mySubPatchOp.saveData(); +}; + + +}; + +Ops.Ui.PatchInput.prototype = new CABLES.Op(); +CABLES.OPS["e3f68bc3-892a-4c78-9974-aca25c27025d"]={f:Ops.Ui.PatchInput,objName:"Ops.Ui.PatchInput"}; + + + + +// ************************************************************** +// +// Ops.Ui.PatchOutput +// +// ************************************************************** + +Ops.Ui.PatchOutput = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const dyn = op.addInPort(new CABLES.Port(op, "create port", CABLES.OP_PORT_TYPE_DYNAMIC)); + +function getPatchOp() +{ + for (let i in op.patch.ops) + { + if (op.patch.ops[i].patchId) + { + if (op.patch.ops[i].patchId.get() == op.uiAttribs.subPatch) + { + return op.patch.ops[i]; + } + } + } +} + +dyn.onLinkChanged = () => +{ + const mySubPatchOp = getPatchOp(); + + if (!dyn.links.length) return; + + const otherPort = dyn.links[0].getOtherPort(dyn); + dyn.removeLinks(); + + const newPortName = mySubPatchOp.addNewOutPort(otherPort); + + const l = gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newPortName); + + mySubPatchOp.saveData(); +}; + + +}; + +Ops.Ui.PatchOutput.prototype = new CABLES.Op(); +CABLES.OPS["851b44cb-5667-4140-9800-5aeb7031f1d7"]={f:Ops.Ui.PatchOutput,objName:"Ops.Ui.PatchOutput"}; + + + + +// ************************************************************** +// +// Ops.Ui.SubPatch +// +// ************************************************************** + +Ops.Ui.SubPatch = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +op.dyn = op.addInPort(new CABLES.Port(op, "create port", CABLES.OP_PORT_TYPE_DYNAMIC)); +op.dynOut = op.addOutPort(new CABLES.Port(op, "create port out", CABLES.OP_PORT_TYPE_DYNAMIC)); + +const dataStr = op.addInPort(new CABLES.Port(op, "dataStr", CABLES.OP_PORT_TYPE_VALUE, { "display": "readonly" })); +op.patchId = op.addInPort(new CABLES.Port(op, "patchId", CABLES.OP_PORT_TYPE_VALUE, { "display": "readonly" })); + +if (CABLES.UI && CABLES.sandbox.isDevEnv()) +{ + const inMakeBp = op.inTriggerButton("Create Blueprint"); + inMakeBp.setUiAttribs({ "hidePort": true }); + + inMakeBp.onTriggered = makeBlueprint; +} + +dataStr.setUiAttribs({ "hideParam": true }); +op.patchId.setUiAttribs({ "hideParam": true }); + +let data = { "ports": [], "portsOut": [] }; +let oldPatchId = CABLES.generateUUID(); +op.patchId.set(oldPatchId); +getSubPatchInputOp(); +getSubPatchOutputOp(); + +let dataLoaded = false; + +op.saveData = saveData; + +op.patchId.onChange = function () +{ + const oldPatchOps = op.patch.getSubPatchOps(oldPatchId); + + if (oldPatchOps.length == 2) + { + for (let i = 0; i < oldPatchOps.length; i++) + { + op.patch.deleteOp(oldPatchOps[i].id); + } + } + else + { + } +}; + +op.onLoaded = function () +{ +}; + +op.onLoadedValueSet = function () +{ + data = JSON.parse(dataStr.get()); + if (!data) + { + data = { "ports": [], "portsOut": [] }; + } + setupPorts(); +}; + +function loadData() +{ +} + +dataStr.onChange = function () +{ + if (dataLoaded) return; + + if (!dataStr.get()) return; + try + { + loadData(); + } + catch (e) + { + op.logError("cannot load subpatch data..."); + op.logError(e); + } +}; + +function saveData() +{ + dataStr.set(JSON.stringify(data)); +} + +op.addPortListener = addPortListener; +function addPortListener(newPort, newPortInPatch) +{ + if (!newPort.hasSubpatchLstener) + { + newPort.hasSubpatchLstener = true; + newPort.addEventListener("onUiAttrChange", function (attribs) + { + if (attribs.title) + { + let i = 0; + for (i = 0; i < data.portsOut.length; i++) + if (data.portsOut[i].name == newPort.name) + data.portsOut[i].title = attribs.title; + + for (i = 0; i < data.ports.length; i++) + if (data.ports[i].name == newPort.name) + data.ports[i].title = attribs.title; + + saveData(); + } + }); + } + + if (newPort.direction == CABLES.PORT_DIR_IN) + { + if (newPort.type == CABLES.OP_PORT_TYPE_FUNCTION) + { + newPort.onTriggered = function () + { + if (newPortInPatch.isLinked()) + newPortInPatch.trigger(); + }; + } + else + { + newPort.onChange = function () + { + newPortInPatch.set(newPort.get()); + if (!newPort.isLinked()) + { + for (let i = 0; i < data.ports.length; i++) + { + if (data.ports[i].name === newPort.name) + { + data.ports[i].value = newPort.get(); + } + } + saveData(); + } + }; + } + } +} + +op.setupPorts = setupPorts; +function setupPorts() +{ + if (!op.patchId.get()) return; + const ports = data.ports || []; + const portsOut = data.portsOut || []; + let i = 0; + + for (i = 0; i < ports.length; i++) + { + if (!op.getPortByName(ports[i].name)) + { + const newPort = op.addInPort(new CABLES.Port(op, ports[i].name, ports[i].type)); + + const patchInputOp = getSubPatchInputOp(); + const newPortInPatch = patchInputOp.addOutPort(new CABLES.Port(patchInputOp, ports[i].name, ports[i].type)); + + newPort.ignoreValueSerialize = true; + newPort.setUiAttribs({ "editableTitle": true }); + if (ports[i].title) + { + newPort.setUiAttribs({ "title": ports[i].title }); + newPortInPatch.setUiAttribs({ "title": ports[i].title }); + } + if (ports[i].objType) + { + newPort.setUiAttribs({ "objType": ports[i].objType }); + newPortInPatch.setUiAttribs({ "objType": ports[i].objType }); + } + if (ports[i].value) + { + newPort.set(ports[i].value); + newPortInPatch.set(ports[i].value); + } + addPortListener(newPort, newPortInPatch); + } + } + + for (i = 0; i < portsOut.length; i++) + { + if (!op.getPortByName(portsOut[i].name)) + { + const newPortOut = op.addOutPort(new CABLES.Port(op, portsOut[i].name, portsOut[i].type)); + const patchOutputOp = getSubPatchOutputOp(); + const newPortOutPatch = patchOutputOp.addInPort(new CABLES.Port(patchOutputOp, portsOut[i].name, portsOut[i].type)); + + newPortOut.ignoreValueSerialize = true; + newPortOut.setUiAttribs({ "editableTitle": true }); + + if (portsOut[i].title) + { + newPortOut.setUiAttribs({ "title": portsOut[i].title }); + newPortOutPatch.setUiAttribs({ "title": portsOut[i].title }); + } + if (portsOut[i].objType) + { + newPortOut.setUiAttribs({ "objType": portsOut[i].objType }); + newPortOutPatch.setUiAttribs({ "objType": portsOut[i].objType }); + } + + // addPortListener(newPortOut,newPortOutPatch); + addPortListener(newPortOutPatch, newPortOut); + } + } + + dataLoaded = true; +} + +op.addNewInPort = function (otherPort, type, objType) +{ + const newName = "in" + data.ports.length + " " + otherPort.parent.name + " " + otherPort.name; + + const o = { "name": newName, "type": otherPort.type }; + if (otherPort.uiAttribs.objType)o.objType = otherPort.uiAttribs.objType; + + data.ports.push(o); + setupPorts(); + return newName; +}; + +op.dyn.onLinkChanged = function () +{ + if (op.dyn.isLinked()) + { + const otherPort = op.dyn.links[0].getOtherPort(op.dyn); + op.dyn.removeLinks(); + otherPort.removeLinkTo(op.dyn); + + op.log("dyn link changed!!!"); + + // const newName = "in" + data.ports.length + " " + otherPort.parent.name + " " + otherPort.name; + + // const o = { "name": newName, "type": otherPort.type }; + // if (otherPort.uiAttribs.objType)o.objType = otherPort.uiAttribs.objType; + // data.ports.push(o); + + // setupPorts(); + + const newName = op.addNewInPort(otherPort); + + const l = gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newName + ); + + dataLoaded = true; + saveData(); + } + else + { + setTimeout(function () + { + op.dyn.removeLinks(); + }, 100); + } +}; + +op.addNewOutPort = function (otherPort, type, objType) +{ + const newName = "out" + data.portsOut.length + " " + otherPort.parent.name + " " + otherPort.name; + + const o = { "name": newName, "type": otherPort.type }; + if (otherPort.uiAttribs.objType)o.objType = otherPort.uiAttribs.objType; + + data.portsOut.push(o); + setupPorts(); + return newName; +}; + +op.dynOut.onLinkChanged = function () +{ + if (op.dynOut.isLinked()) + { + const otherPort = op.dynOut.links[0].getOtherPort(op.dynOut); + op.dynOut.removeLinks(); + otherPort.removeLinkTo(op.dynOut); + + const newName = op.addNewOutPort(otherPort); + + gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newName + ); + + dataLoaded = true; + saveData(); + } + else + { + setTimeout(function () + { + op.dynOut.removeLinks(); + }, 100); + + op.log("dynOut unlinked..."); + } +}; + +function getSubPatchOutputOp() +{ + let patchOutputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchOutput"); + + if (!patchOutputOP) + { + op.patch.addOp("Ops.Ui.PatchOutput", { "subPatch": op.patchId.get(), "translate": { "x": 0, "y": 0 } }); + patchOutputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchOutput"); + if (!patchOutputOP) op.warn("no patchoutput!"); + } + return patchOutputOP; +} + +function getSubPatchInputOp() +{ + let patchInputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchInput"); + + if (!patchInputOP) + { + op.patch.addOp("Ops.Ui.PatchInput", { "subPatch": op.patchId.get(), "translate": { "x": 0, "y": 0 } }); + patchInputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchInput"); + if (!patchInputOP) op.warn("no patchinput2!"); + } + + return patchInputOP; +} + +op.addSubLink = function (p, p2) +{ + const num = data.ports.length; + const sublPortname = "in" + (num - 1) + " " + p2.parent.name + " " + p2.name; + + if (p.direction == CABLES.PORT_DIR_IN) + { + gui.scene().link( + p.parent, + p.getName(), + getSubPatchInputOp(), + sublPortname + ); + } + else + { + const numOut = data.portsOut.length; + gui.scene().link( + p.parent, + p.getName(), + getSubPatchOutputOp(), + "out" + (numOut - 1) + " " + p2.parent.name + " " + p2.name + ); + } + + const bounds = gui.patchView.getSubPatchBounds(op.patchId.get()); + + getSubPatchInputOp().uiAttr( + { + "translate": + { + "x": bounds.minx, + "y": bounds.miny - 100 + } + }); + + getSubPatchOutputOp().uiAttr( + { + "translate": + { + "x": bounds.minx, + "y": bounds.maxy + 100 + } + }); + saveData(); + return sublPortname; +}; + +op.onDelete = function () +{ + for (let i = op.patch.ops.length - 1; i >= 0; i--) + { + if (op.patch.ops[i] && op.patch.ops[i].uiAttribs && op.patch.ops[i].uiAttribs.subPatch == op.patchId.get()) + { + op.patch.deleteOp(op.patch.ops[i].id); + } + } +}; + +function makeBlueprint() +{ + const bpOp = op.patch.addOp(CABLES.UI.DEFAULTOPNAMES.blueprint); + + bpOp.getPortByName("externalPatchId").set(gui.patchId); + bpOp.getPortByName("subPatchId").set(op.patchId.get()); + bpOp.getPortByName("active").set(true); + + bpOp.uiAttr( + { + "translate": + { + "x": op.uiAttribs.translate.x - 150, + "y": op.uiAttribs.translate.y + } + }); +} + +op.rebuildListeners = () => +{ + op.log("rebuild listeners..."); + + const outop = getSubPatchOutputOp(); + for (let i = 0; i < outop.portsIn.length; i++) + { + if (outop.portsIn[i].isLinked()) + { + addPortListener(outop.portsIn[i], this.portsOut[i]); + } + } +}; + + +}; + +Ops.Ui.SubPatch.prototype = new CABLES.Op(); +CABLES.OPS["84d9a6f0-ed7a-466d-b386-225ed9e89c60"]={f:Ops.Ui.SubPatch,objName:"Ops.Ui.SubPatch"}; + + + + +// ************************************************************** +// +// Ops.Math.Sum +// +// ************************************************************** + +Ops.Math.Sum = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValueFloat("number1", 1), + number2 = op.inValueFloat("number2", 1), + result = op.outNumber("result"); + +op.setTitle("+"); + +number1.onChange = +number2.onChange = exec; +exec(); + +function exec() +{ + const v = number1.get() + number2.get(); + if (!isNaN(v)) + result.set(v); +} + + +}; + +Ops.Math.Sum.prototype = new CABLES.Op(); +CABLES.OPS["c8fb181e-0b03-4b41-9e55-06b6267bc634"]={f:Ops.Math.Sum,objName:"Ops.Math.Sum"}; + + diff --git a/survey_dashboard/hmc_layout/static/en_files/projectops.js b/survey_dashboard/hmc_layout/static/en_files/projectops.js new file mode 100644 index 0000000..6b81b1b --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/projectops.js @@ -0,0 +1,4269 @@ +"use strict"; + +var CABLES=CABLES||{}; +CABLES.OPS=CABLES.OPS||{}; + +var Ops=Ops || {}; +Ops.Gl=Ops.Gl || {}; +Ops.Ui=Ops.Ui || {}; +Ops.Anim=Ops.Anim || {}; +Ops.Math=Ops.Math || {}; +Ops.Color=Ops.Color || {}; +Ops.Value=Ops.Value || {}; +Ops.String=Ops.String || {}; +Ops.Trigger=Ops.Trigger || {}; +Ops.Gl.Matrix=Ops.Gl.Matrix || {}; +Ops.Gl.Meshes=Ops.Gl.Meshes || {}; +Ops.Gl.Shader=Ops.Gl.Shader || {}; +Ops.Deprecated=Ops.Deprecated || {}; +Ops.Math.Compare=Ops.Math.Compare || {}; +Ops.Deprecated.Exp=Ops.Deprecated.Exp || {}; +Ops.Deprecated.Anim=Ops.Deprecated.Anim || {}; +Ops.Deprecated.Exp.Gl=Ops.Deprecated.Exp.Gl || {}; +Ops.Gl.TextureEffects=Ops.Gl.TextureEffects || {}; +Ops.Gl.TextureEffects.Noise=Ops.Gl.TextureEffects.Noise || {}; + + + +// ************************************************************** +// +// Ops.Gl.MainLoop +// +// ************************************************************** + +Ops.Gl.MainLoop = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + fpsLimit = op.inValue("FPS Limit", 0), + trigger = op.outTrigger("trigger"), + width = op.outNumber("width"), + height = op.outNumber("height"), + reduceFocusFPS = op.inValueBool("Reduce FPS not focussed", true), + reduceLoadingFPS = op.inValueBool("Reduce FPS loading"), + clear = op.inValueBool("Clear", true), + clearAlpha = op.inValueBool("ClearAlpha", true), + fullscreen = op.inValueBool("Fullscreen Button", false), + active = op.inValueBool("Active", true), + hdpi = op.inValueBool("Hires Displays", false), + inUnit = op.inSwitch("Pixel Unit", ["Display", "CSS"], "Display"); + +op.onAnimFrame = render; +hdpi.onChange = function () +{ + if (hdpi.get()) op.patch.cgl.pixelDensity = window.devicePixelRatio; + else op.patch.cgl.pixelDensity = 1; + + op.patch.cgl.updateSize(); + if (CABLES.UI) gui.setLayout(); + + // inUnit.setUiAttribs({ "greyout": !hdpi.get() }); + + // if (!hdpi.get())inUnit.set("CSS"); + // else inUnit.set("Display"); +}; + +active.onChange = function () +{ + op.patch.removeOnAnimFrame(op); + + if (active.get()) + { + op.setUiAttrib({ "extendTitle": "" }); + op.onAnimFrame = render; + op.patch.addOnAnimFrame(op); + op.log("adding again!"); + } + else + { + op.setUiAttrib({ "extendTitle": "Inactive" }); + } +}; + +const cgl = op.patch.cgl; +let rframes = 0; +let rframeStart = 0; + +if (!op.patch.cgl) op.uiAttr({ "error": "No webgl cgl context" }); + +const identTranslate = vec3.create(); +vec3.set(identTranslate, 0, 0, 0); +const identTranslateView = vec3.create(); +vec3.set(identTranslateView, 0, 0, -2); + +fullscreen.onChange = updateFullscreenButton; +setTimeout(updateFullscreenButton, 100); +let fsElement = null; + +let winhasFocus = true; +let winVisible = true; + +window.addEventListener("blur", () => { winhasFocus = false; }); +window.addEventListener("focus", () => { winhasFocus = true; }); +document.addEventListener("visibilitychange", () => { winVisible = !document.hidden; }); +testMultiMainloop(); + +inUnit.onChange = () => +{ + width.set(0); + height.set(0); +}; + +function getFpsLimit() +{ + if (reduceLoadingFPS.get() && op.patch.loading.getProgress() < 1.0) return 5; + + if (reduceFocusFPS.get()) + { + if (!winVisible) return 10; + if (!winhasFocus) return 30; + } + + return fpsLimit.get(); +} + +function updateFullscreenButton() +{ + function onMouseEnter() + { + if (fsElement)fsElement.style.display = "block"; + } + + function onMouseLeave() + { + if (fsElement)fsElement.style.display = "none"; + } + + op.patch.cgl.canvas.addEventListener("mouseleave", onMouseLeave); + op.patch.cgl.canvas.addEventListener("mouseenter", onMouseEnter); + + if (fullscreen.get()) + { + if (!fsElement) + { + fsElement = document.createElement("div"); + + const container = op.patch.cgl.canvas.parentElement; + if (container)container.appendChild(fsElement); + + fsElement.addEventListener("mouseenter", onMouseEnter); + fsElement.addEventListener("click", function (e) + { + if (CABLES.UI && !e.shiftKey) gui.cycleFullscreen(); + else cgl.fullScreen(); + }); + } + + fsElement.style.padding = "10px"; + fsElement.style.position = "absolute"; + fsElement.style.right = "5px"; + fsElement.style.top = "5px"; + fsElement.style.width = "20px"; + fsElement.style.height = "20px"; + fsElement.style.cursor = "pointer"; + fsElement.style["border-radius"] = "40px"; + fsElement.style.background = "#444"; + fsElement.style["z-index"] = "9999"; + fsElement.style.display = "none"; + fsElement.innerHTML = ""; + } + else + { + if (fsElement) + { + fsElement.style.display = "none"; + fsElement.remove(); + fsElement = null; + } + } +} + +op.onDelete = function () +{ + cgl.gl.clearColor(0, 0, 0, 0); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); +}; + +function render(time) +{ + if (!active.get()) return; + if (cgl.aborted || cgl.canvas.clientWidth === 0 || cgl.canvas.clientHeight === 0) return; + + op.patch.cg = cgl; + + const startTime = performance.now(); + + op.patch.config.fpsLimit = getFpsLimit(); + + if (cgl.canvasWidth == -1) + { + cgl.setCanvas(op.patch.config.glCanvasId); + return; + } + + if (cgl.canvasWidth != width.get() || cgl.canvasHeight != height.get()) + { + let div = 1; + if (inUnit.get() == "CSS")div = op.patch.cgl.pixelDensity; + + width.set(cgl.canvasWidth / div); + height.set(cgl.canvasHeight / div); + } + + if (CABLES.now() - rframeStart > 1000) + { + CGL.fpsReport = CGL.fpsReport || []; + if (op.patch.loading.getProgress() >= 1.0 && rframeStart !== 0)CGL.fpsReport.push(rframes); + rframes = 0; + rframeStart = CABLES.now(); + } + CGL.MESH.lastShader = null; + CGL.MESH.lastMesh = null; + + cgl.renderStart(cgl, identTranslate, identTranslateView); + + if (clear.get()) + { + cgl.gl.clearColor(0, 0, 0, 1); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + } + + trigger.trigger(); + + if (CGL.MESH.lastMesh)CGL.MESH.lastMesh.unBind(); + + if (CGL.Texture.previewTexture) + { + if (!CGL.Texture.texturePreviewer) CGL.Texture.texturePreviewer = new CGL.Texture.texturePreview(cgl); + CGL.Texture.texturePreviewer.render(CGL.Texture.previewTexture); + } + cgl.renderEnd(cgl); + + op.patch.cg = null; + + if (clearAlpha.get()) + { + cgl.gl.clearColor(1, 1, 1, 1); + cgl.gl.colorMask(false, false, false, true); + cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT); + cgl.gl.colorMask(true, true, true, true); + } + + if (!cgl.frameStore.phong)cgl.frameStore.phong = {}; + rframes++; + + op.patch.cgl.profileData.profileMainloopMs = performance.now() - startTime; +} + +function testMultiMainloop() +{ + setTimeout( + () => + { + if (op.patch.getOpsByObjName(op.name).length > 1) + { + op.setUiError("multimainloop", "there should only be one mainloop op!"); + op.patch.addEventListener("onOpDelete", testMultiMainloop); + } + else op.setUiError("multimainloop", null, 1); + }, 500); +} + + +}; + +Ops.Gl.MainLoop.prototype = new CABLES.Op(); +CABLES.OPS["b0472a1d-db16-4ba6-8787-f300fbdc77bb"]={f:Ops.Gl.MainLoop,objName:"Ops.Gl.MainLoop"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.Transform +// +// ************************************************************** + +Ops.Gl.Matrix.Transform = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + posX = op.inValue("posX", 0), + posY = op.inValue("posY", 0), + posZ = op.inValue("posZ", 0), + scale = op.inValue("scale", 1), + rotX = op.inValue("rotX", 0), + rotY = op.inValue("rotY", 0), + rotZ = op.inValue("rotZ", 0), + trigger = op.outTrigger("trigger"); + +op.setPortGroup("Rotation", [rotX, rotY, rotZ]); +op.setPortGroup("Position", [posX, posY, posZ]); +op.setPortGroup("Scale", [scale]); +op.setUiAxisPorts(posX, posY, posZ); + +const vPos = vec3.create(); +const vScale = vec3.create(); +const transMatrix = mat4.create(); +mat4.identity(transMatrix); + +let + doScale = false, + doTranslate = false, + translationChanged = true, + scaleChanged = true, + rotChanged = true; + +rotX.onChange = rotY.onChange = rotZ.onChange = setRotChanged; +posX.onChange = posY.onChange = posZ.onChange = setTranslateChanged; +scale.onChange = setScaleChanged; + +render.onTriggered = function () +{ + // if(!CGL.TextureEffect.checkOpNotInTextureEffect(op)) return; + + let updateMatrix = false; + if (translationChanged) + { + updateTranslation(); + updateMatrix = true; + } + if (scaleChanged) + { + updateScale(); + updateMatrix = true; + } + if (rotChanged) updateMatrix = true; + + if (updateMatrix) doUpdateMatrix(); + + const cg = op.patch.cgl; + cg.pushModelMatrix(); + mat4.multiply(cg.mMatrix, cg.mMatrix, transMatrix); + + trigger.trigger(); + cg.popModelMatrix(); + + if (CABLES.UI && CABLES.UI.showCanvasTransforms) gui.setTransform(op.id, posX.get(), posY.get(), posZ.get()); + + if (op.isCurrentUiOp()) + gui.setTransformGizmo( + { + "posX": posX, + "posY": posY, + "posZ": posZ, + }); +}; + +op.transform3d = function () +{ + return { "pos": [posX, posY, posZ] }; +}; + +function doUpdateMatrix() +{ + mat4.identity(transMatrix); + if (doTranslate)mat4.translate(transMatrix, transMatrix, vPos); + + if (rotX.get() !== 0)mat4.rotateX(transMatrix, transMatrix, rotX.get() * CGL.DEG2RAD); + if (rotY.get() !== 0)mat4.rotateY(transMatrix, transMatrix, rotY.get() * CGL.DEG2RAD); + if (rotZ.get() !== 0)mat4.rotateZ(transMatrix, transMatrix, rotZ.get() * CGL.DEG2RAD); + + if (doScale)mat4.scale(transMatrix, transMatrix, vScale); + rotChanged = false; +} + +function updateTranslation() +{ + doTranslate = false; + if (posX.get() !== 0.0 || posY.get() !== 0.0 || posZ.get() !== 0.0) doTranslate = true; + vec3.set(vPos, posX.get(), posY.get(), posZ.get()); + translationChanged = false; +} + +function updateScale() +{ + // doScale=false; + // if(scale.get()!==0.0) + doScale = true; + vec3.set(vScale, scale.get(), scale.get(), scale.get()); + scaleChanged = false; +} + +function setTranslateChanged() +{ + translationChanged = true; +} + +function setScaleChanged() +{ + scaleChanged = true; +} + +function setRotChanged() +{ + rotChanged = true; +} + +doUpdateMatrix(); + + +}; + +Ops.Gl.Matrix.Transform.prototype = new CABLES.Op(); +CABLES.OPS["650baeb1-db2d-4781-9af6-ab4e9d4277be"]={f:Ops.Gl.Matrix.Transform,objName:"Ops.Gl.Matrix.Transform"}; + + + + +// ************************************************************** +// +// Ops.Math.Multiply +// +// ************************************************************** + +Ops.Math.Multiply = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValueFloat("number1", 1), + number2 = op.inValueFloat("number2", 2), + result = op.outNumber("result"); + +op.setTitle("*"); + +number1.onChange = number2.onChange = update; +update(); + +function update() +{ + const n1 = number1.get(); + const n2 = number2.get(); + + result.set(n1 * n2); +} + + +}; + +Ops.Math.Multiply.prototype = new CABLES.Op(); +CABLES.OPS["1bbdae06-fbb2-489b-9bcc-36c9d65bd441"]={f:Ops.Math.Multiply,objName:"Ops.Math.Multiply"}; + + + + +// ************************************************************** +// +// Ops.Deprecated.Exp.Gl.VectorFieldArray +// +// ************************************************************** + +Ops.Deprecated.Exp.Gl.VectorFieldArray = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +let exe = op.inTrigger("exe"); +let geom = op.inObject("geom"); +geom.ignoreValueSerialize = true; + +let tex = op.inTexture("Texture"); + +let numColumns = op.inValue("Columns", 100); +let numRows = op.inValue("Rows", 100); +let spacingColumns = op.inValue("Spacing Columns", 1); +let spacingRows = op.inValue("Spacing Rows", 1); +let doCenter = op.inValueBool("Center", true); + +let transRotate = op.inValueBool("Rotate", true); +let transScale = op.inValueBool("Scale", false); +let transTransZ = op.inValueBool("Translate Z", false); + +transTransZ.onChange = updateTransforms; +transRotate.onChange = updateTransforms; +transScale.onChange = updateTransforms; + +function updateTransforms() +{ + if (!shader) return; + if (transRotate.get())shader.define("TRANS_ROTATE"); + else shader.removeDefine("TRANS_ROTATE"); + + if (transScale.get())shader.define("TRANS_SCALE"); + else shader.removeDefine("TRANS_SCALE"); + + if (transTransZ.get())shader.define("TRANS_TRANS_Z"); + else shader.removeDefine("TRANS_TRANS_Z"); +} + +let transformations = []; +let mod = null; +let mesh = null; +var shader = null; +let uniDoInstancing = null; +let uniSpaceX = null; +let uniSpaceY = null; +let recalc = true; + +numRows.onChange = reset; +numColumns.onChange = reset; +doCenter.onChange = reset; +spacingColumns.onChange = reset; +spacingRows.onChange = reset; + +geom.onChange = reset; +exe.onTriggered = doRender; +exe.onLinkChanged = removeModule; + +let srcHeadVert = "" + .endl() + "UNI float do_instancing;" + .endl() + "UNI sampler2D {{mod}}field;" + + .endl() + "UNI float {{mod}}spaceX;" + .endl() + "UNI float {{mod}}spaceY;" + .endl() + "UNI float {{mod}}rows;" + .endl() + "UNI float {{mod}}cols;" + + .endl() + "#ifdef INSTANCING" + .endl() + " IN mat4 instMat;" + .endl() + " OUT mat4 instModelMat;" + .endl() + "#endif" + + .endl() + "mat4 rotationMatrix(vec3 axis, float angle)" + .endl() + "{" + .endl() + " axis = normalize(axis);" + .endl() + " float s = sin(angle);" + .endl() + " float c = cos(angle);" + .endl() + " float oc = 1.0 - c;" + + .endl() + " return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0," + .endl() + " oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0," + .endl() + " oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0," + .endl() + " 0.0, 0.0, 0.0, 1.0);" + .endl() + "}"; + +let srcBodyVert = "" + .endl() + "#ifdef INSTANCING" + // .endl()+' if( do_instancing==1.0 )' + .endl() + " {" + .endl() + " instModelMat=instMat;" + .endl() + " float tx=(instModelMat[3][0]) / {{mod}}cols;" + .endl() + " float ty=(instModelMat[3][1]) / {{mod}}rows;" + .endl() + " instModelMat[3][0]*={{mod}}spaceX;" + .endl() + " instModelMat[3][1]*={{mod}}spaceY;" + .endl() + " vec4 instCol = texture2D( {{mod}}field, vec2(tx,ty) );" + + .endl() + " #ifdef TRANS_ROTATE" + .endl() + " instModelMat*=rotationMatrix(vec3(0.0,0.0,1.0),instCol.r*3.1415926535897932384626433832795*2.0);" + .endl() + " #endif" + + .endl() + " #ifdef TRANS_SCALE" + .endl() + " pos.rgb*=instCol.r;" + .endl() + " #endif" + + .endl() + " #ifdef TRANS_TRANS_Z" + .endl() + " pos.z+=instCol.r;" + .endl() + " #endif" + +// .endl()+' pos*=instCol.r;' + .endl() + " mMatrix=mMatrix * instModelMat;" + .endl() + " }" + .endl() + "#endif" + .endl(); + +function reset() +{ + recalc = true; +} + +function prepare() +{ + if (geom.get()) + { + calc(); + + let num = transformations.length; + let arrs = [].concat.apply([], transformations); + + let matrices = new Float32Array(arrs); + + mesh = new CGL.Mesh(cgl, geom.get()); + mesh.numInstances = num; + + mesh.addAttribute("instMat", matrices, 16); + + recalc = false; + } +} + +function removeModule() +{ + if (shader && mod) + { + shader.removeModule(mod); + shader = null; + } +} + +function doRender() +{ + if (recalc)prepare(); + if (mesh) + { + if (cgl.getShader() && cgl.getShader() != shader) + { + if (shader && mod) + { + shader.removeModule(mod); + shader = null; + } + + shader = cgl.getShader(); + if (!shader.hasDefine("INSTANCING")) + { + mod = shader.addModule( + { + "name": "MODULE_VERTEX_POSITION", + "srcHeadVert": srcHeadVert, + "srcBodyVert": srcBodyVert + }); + + shader.define("INSTANCING"); + op.uniDoInstancing = new CGL.Uniform(shader, "f", "do_instancing", 1); + op.uniSpaceX = new CGL.Uniform(shader, "f", mod.prefix + "spaceX", spacingColumns); + op.uniSpaceY = new CGL.Uniform(shader, "f", mod.prefix + "spaceY", spacingRows); + op.uniTexture = new CGL.Uniform(shader, "t", mod.prefix + "field", 5); + op.uniCols = new CGL.Uniform(shader, "f", mod.prefix + "cols", numColumns); + op.uniRows = new CGL.Uniform(shader, "f", mod.prefix + "rows", numRows); + + updateTransforms(); + } + else + { + op.uniDoInstancing = shader.getUniform("do_instancing"); + } + } + + // if(uniSpaceX) + // { + // uniSpaceY.setValue(spacingRows.get()); + // uniSpaceX.setValue(spacingColumns.get()); + + // uniCols.setValue(numColumns.get()); + // uniRows.setValue(numRows.get()); + + // } + + if (tex.get()) + cgl.setTexture(5, tex.get().tex); + + op.uniDoInstancing.setValue(1); + mesh.render(shader); + op.uniDoInstancing.setValue(0); + } + else + { + prepare(); + } +} + +function calc() +{ + let m = mat4.create(); + let cols = Math.round(numColumns.get()); + let rows = Math.round(numRows.get()); + if (cols <= 0)cols = 1; + if (rows <= 0)rows = 1; + + let distX = spacingColumns.get(); + let distY = spacingRows.get(); + + let centerX = 0; + let centerY = 0; + if (doCenter.get()) + { + centerX = cols * (spacingColumns.get() / 2); + centerY = rows * (spacingRows.get() / 2); + } + + transformations.length = cols * rows; + + for (let x = 0; x < cols; x++) + { + for (let y = 0; y < rows; y++) + { + mat4.identity(m); + mat4.translate(m, m, [x - centerX, y - centerY, 0]); + transformations[x + y * cols] = Array.prototype.slice.call(m); + } + } + + op.log("reset", transformations.length, cols, rows); +} + + +}; + +Ops.Deprecated.Exp.Gl.VectorFieldArray.prototype = new CABLES.Op(); +CABLES.OPS["fba86dad-f37f-482e-a8fb-2abc76723ecf"]={f:Ops.Deprecated.Exp.Gl.VectorFieldArray,objName:"Ops.Deprecated.Exp.Gl.VectorFieldArray"}; + + + + +// ************************************************************** +// +// Ops.Sequence +// +// ************************************************************** + +Ops.Sequence = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + cleanup = op.inTriggerButton("Clean up connections"); + +const + exes = [], + triggers = [], + num = 16; + +let updateTimeout = null; + +exe.onTriggered = triggerAll; +cleanup.onTriggered = clean; +cleanup.setUiAttribs({ "hidePort": true }); +cleanup.setUiAttribs({ "hideParam": true }); + +for (let i = 0; i < num; i++) +{ + const p = op.outTrigger("trigger " + i); + triggers.push(p); + p.onLinkChanged = updateButton; + + if (i < num - 1) + { + let newExe = op.inTrigger("exe " + i); + newExe.onTriggered = triggerAll; + exes.push(newExe); + } +} + +function updateButton() +{ + clearTimeout(updateTimeout); + updateTimeout = setTimeout(() => + { + let show = false; + for (let i = 0; i < triggers.length; i++) + if (triggers[i].links.length > 1) show = true; + + cleanup.setUiAttribs({ "hideParam": !show }); + + if (op.isCurrentUiOp()) op.refreshParams(); + }, 60); +} + +function triggerAll() +{ + for (let i = 0; i < triggers.length; i++) triggers[i].trigger(); +} + +function clean() +{ + let count = 0; + for (let i = 0; i < triggers.length; i++) + { + let removeLinks = []; + + if (triggers[i].links.length > 1) + for (let j = 1; j < triggers[i].links.length; j++) + { + while (triggers[count].links.length > 0) count++; + + removeLinks.push(triggers[i].links[j]); + const otherPort = triggers[i].links[j].getOtherPort(triggers[i]); + op.patch.link(op, "trigger " + count, otherPort.parent, otherPort.name); + count++; + } + + for (let j = 0; j < removeLinks.length; j++) removeLinks[j].remove(); + } + updateButton(); +} + + +}; + +Ops.Sequence.prototype = new CABLES.Op(); +CABLES.OPS["a466bc1f-06e9-4595-8849-bffb9fe22f99"]={f:Ops.Sequence,objName:"Ops.Sequence"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ImageCompose_v2 +// +// ************************************************************** + +Ops.Gl.TextureEffects.ImageCompose_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"imgcomp_frag":"UNI float a;\nvoid main()\n{\n outColor= vec4(0.0,0.0,0.0,a);\n}\n",}; +const + render = op.inTrigger("Render"), + useVPSize = op.inBool("Use viewport size", true), + width = op.inValueInt("Width", 640), + height = op.inValueInt("Height", 480), + tfilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inValueSelect("Wrap", ["clamp to edge", "repeat", "mirrored repeat"], "repeat"), + fpTexture = op.inValueBool("HDR"), + inTransp = op.inValueBool("Transparent", false), + + trigger = op.outTrigger("Next"), + texOut = op.outTexture("texture_out"), + outRatio = op.outValue("Aspect Ratio"); + +const cgl = op.patch.cgl; +op.setPortGroup("Texture Size", [useVPSize, width, height]); +op.setPortGroup("Texture Settings", [twrap, tfilter, fpTexture, inTransp]); + +texOut.set(CGL.Texture.getEmptyTexture(cgl, fpTexture.get())); +let effect = null; +let tex = null; +let w = 8, h = 8; + +const prevViewPort = [0, 0, 0, 0]; +let reInitEffect = true; + +// const bgShader = new CGL.Shader(cgl, "imgcompose bg"); +// bgShader.setSource(bgShader.getDefaultVertexShader(), attachments.imgcomp_frag); + +// const uniAlpha = new CGL.Uniform(bgShader, "f", "a", !inTransp.get()); + +let selectedFilter = CGL.Texture.FILTER_LINEAR; +let selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + +const fps = 0; +const fpsStart = 0; + +twrap.onChange = onWrapChange; +tfilter.onChange = onFilterChange; + +render.onTriggered = op.preRender = doRender; + +onFilterChange(); +onWrapChange(); +updateSizePorts(); + +// inTransp.onChange = () => +// { +// uniAlpha.setValue(!inTransp.get()); +// }; + +function initEffect() +{ + if (effect)effect.delete(); + if (tex)tex.delete(); + + if (fpTexture.get() && tfilter.get() == "mipmap") op.setUiError("fpmipmap", "Don't use mipmap and HDR at the same time, many systems do not support this."); + else op.setUiError("fpmipmap", null); + + effect = new CGL.TextureEffect(cgl, { "isFloatingPointTexture": fpTexture.get() }); + + tex = new CGL.Texture(cgl, + { + "name": "image_compose_v2_" + op.id, + "isFloatingPointTexture": fpTexture.get(), + "filter": selectedFilter, + "wrap": selectedWrap, + "width": Math.ceil(width.get()), + "height": Math.ceil(height.get()), + }); + + effect.setSourceTexture(tex); + texOut.set(CGL.Texture.getEmptyTexture(cgl, fpTexture.get())); + + reInitEffect = false; +} + +fpTexture.onChange = function () +{ + reInitEffect = true; +}; + +function updateResolution() +{ + if (!effect)initEffect(); + + if (useVPSize.get()) + { + w = cgl.getViewPort()[2]; + h = cgl.getViewPort()[3]; + } + else + { + w = Math.ceil(width.get()); + h = Math.ceil(height.get()); + } + + outRatio.set(w / h); + + if ((w != tex.width || h != tex.height) && (w !== 0 && h !== 0)) + { + // height.set(h); + // width.set(w); + tex.setSize(w, h); + + effect.setSourceTexture(tex); + texOut.set(CGL.Texture.getEmptyTexture(cgl, fpTexture.get())); + texOut.set(tex); + } + + // if (texOut.get() && selectedFilter != CGL.Texture.FILTER_NEAREST) + // { + // if (!texOut.get().isPowerOfTwo()) op.setUiError("hintnpot", "texture dimensions not power of two! - texture filtering when scaling will not work on ios devices.", 0); + // else op.setUiError("hintnpot", null, 0); + // } + // else op.setUiError("hintnpot", null, 0); +} + +function updateSizePorts() +{ + width.setUiAttribs({ "greyout": useVPSize.get() }); + height.setUiAttribs({ "greyout": useVPSize.get() }); +} + +useVPSize.onChange = function () +{ + updateSizePorts(); +}; + +op.preRender = function () +{ + doRender(); + // bgShader.bind(); +}; + +function doRender() +{ + if (!effect || reInitEffect) initEffect(); + + const vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + cgl.pushBlend(false); + + updateResolution(); + + const oldEffect = cgl.currentTextureEffect; + cgl.currentTextureEffect = effect; + cgl.currentTextureEffect.width = width.get(); + cgl.currentTextureEffect.height = height.get(); + effect.setSourceTexture(tex); + + let bgTex = CGL.Texture.getBlackTexture(cgl); + if (inTransp.get())bgTex = CGL.Texture.getEmptyTexture(cgl, fpTexture.get()); + + effect.startEffect(bgTex); + + // cgl.pushShader(bgShader); + // cgl.currentTextureEffect.bind(); + // cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + // cgl.currentTextureEffect.finish(); + // cgl.popShader(); + + trigger.trigger(); + + texOut.set(effect.getCurrentSourceTexture()); + + effect.endEffect(); + + cgl.setViewPort(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); + + cgl.popBlend(false); + cgl.currentTextureEffect = oldEffect; +} + +function onWrapChange() +{ + if (twrap.get() == "repeat") selectedWrap = CGL.Texture.WRAP_REPEAT; + if (twrap.get() == "mirrored repeat") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (twrap.get() == "clamp to edge") selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + reInitEffect = true; + // updateResolution(); +} + +function onFilterChange() +{ + if (tfilter.get() == "nearest") selectedFilter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "linear") selectedFilter = CGL.Texture.FILTER_LINEAR; + if (tfilter.get() == "mipmap") selectedFilter = CGL.Texture.FILTER_MIPMAP; + + reInitEffect = true; + // updateResolution(); +} + + +}; + +Ops.Gl.TextureEffects.ImageCompose_v2.prototype = new CABLES.Op(); +CABLES.OPS["a5b43d4c-a9ea-4eaf-9ed0-f257d222659d"]={f:Ops.Gl.TextureEffects.ImageCompose_v2,objName:"Ops.Gl.TextureEffects.ImageCompose_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Shader.BasicMaterial_v3 +// +// ************************************************************** + +Ops.Gl.Shader.BasicMaterial_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"basicmaterial_frag":"{{MODULES_HEAD}}\n\nIN vec2 texCoord;\n\n#ifdef VERTEX_COLORS\nIN vec4 vertCol;\n#endif\n\n#ifdef HAS_TEXTURES\n IN vec2 texCoordOrig;\n #ifdef HAS_TEXTURE_DIFFUSE\n UNI sampler2D tex;\n #endif\n #ifdef HAS_TEXTURE_OPACITY\n UNI sampler2D texOpacity;\n #endif\n#endif\n\nvoid main()\n{\n {{MODULE_BEGIN_FRAG}}\n vec4 col=color;\n\n\n #ifdef HAS_TEXTURES\n vec2 uv=texCoord;\n\n #ifdef CROP_TEXCOORDS\n if(uv.x<0.0 || uv.x>1.0 || uv.y<0.0 || uv.y>1.0) discard;\n #endif\n\n #ifdef HAS_TEXTURE_DIFFUSE\n col=texture(tex,uv);\n\n #ifdef COLORIZE_TEXTURE\n col.r*=color.r;\n col.g*=color.g;\n col.b*=color.b;\n #endif\n #endif\n col.a*=color.a;\n #ifdef HAS_TEXTURE_OPACITY\n #ifdef TRANSFORMALPHATEXCOORDS\n uv=texCoordOrig;\n #endif\n #ifdef ALPHA_MASK_IALPHA\n col.a*=1.0-texture(texOpacity,uv).a;\n #endif\n #ifdef ALPHA_MASK_ALPHA\n col.a*=texture(texOpacity,uv).a;\n #endif\n #ifdef ALPHA_MASK_LUMI\n col.a*=dot(vec3(0.2126,0.7152,0.0722), texture(texOpacity,uv).rgb);\n #endif\n #ifdef ALPHA_MASK_R\n col.a*=texture(texOpacity,uv).r;\n #endif\n #ifdef ALPHA_MASK_G\n col.a*=texture(texOpacity,uv).g;\n #endif\n #ifdef ALPHA_MASK_B\n col.a*=texture(texOpacity,uv).b;\n #endif\n // #endif\n #endif\n #endif\n\n {{MODULE_COLOR}}\n\n #ifdef DISCARDTRANS\n if(col.a<0.2) discard;\n #endif\n\n #ifdef VERTEX_COLORS\n col*=vertCol;\n #endif\n\n outColor = col;\n}\n","basicmaterial_vert":"\n{{MODULES_HEAD}}\n\n// OUT vec3 norm;\nOUT vec2 texCoord;\nOUT vec2 texCoordOrig;\n\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\n#ifdef HAS_TEXTURES\n UNI float diffuseRepeatX;\n UNI float diffuseRepeatY;\n UNI float texOffsetX;\n UNI float texOffsetY;\n#endif\n\n#ifdef VERTEX_COLORS\n in vec4 attrVertColor;\n out vec4 vertCol;\n\n#endif\n\n\nvoid main()\n{\n mat4 mMatrix=modelMatrix;\n mat4 mvMatrix;\n\n norm=attrVertNormal;\n texCoordOrig=attrTexCoord;\n texCoord=attrTexCoord;\n #ifdef HAS_TEXTURES\n texCoord.x=texCoord.x*diffuseRepeatX+texOffsetX;\n texCoord.y=(1.0-texCoord.y)*diffuseRepeatY+texOffsetY;\n #endif\n\n #ifdef VERTEX_COLORS\n vertCol=attrVertColor;\n #endif\n\n vec4 pos = vec4(vPosition, 1.0);\n\n #ifdef BILLBOARD\n vec3 position=vPosition;\n mvMatrix=viewMatrix*modelMatrix;\n\n gl_Position = projMatrix * mvMatrix * vec4((\n position.x * vec3(\n mvMatrix[0][0],\n mvMatrix[1][0],\n mvMatrix[2][0] ) +\n position.y * vec3(\n mvMatrix[0][1],\n mvMatrix[1][1],\n mvMatrix[2][1]) ), 1.0);\n #endif\n\n {{MODULE_VERTEX_POSITION}}\n\n #ifndef BILLBOARD\n mvMatrix=viewMatrix * mMatrix;\n #endif\n\n\n #ifndef BILLBOARD\n // gl_Position = projMatrix * viewMatrix * modelMatrix * pos;\n gl_Position = projMatrix * mvMatrix * pos;\n #endif\n}\n",}; +const render = op.inTrigger("render"); + +const trigger = op.outTrigger("trigger"); +const shaderOut = op.outObject("shader", null, "shader"); + +shaderOut.ignoreValueSerialize = true; + +op.toWorkPortsNeedToBeLinked(render); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "basicmaterialnew"); +shader.addAttribute({ "type": "vec3", "name": "vPosition" }); +shader.addAttribute({ "type": "vec2", "name": "attrTexCoord" }); +shader.addAttribute({ "type": "vec3", "name": "attrVertNormal", "nameFrag": "norm" }); +shader.addAttribute({ "type": "float", "name": "attrVertIndex" }); + +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); + +shader.setSource(attachments.basicmaterial_vert, attachments.basicmaterial_frag); + +shaderOut.set(shader); + +render.onTriggered = doRender; + +// rgba colors +const r = op.inValueSlider("r", Math.random()); +const g = op.inValueSlider("g", Math.random()); +const b = op.inValueSlider("b", Math.random()); +const a = op.inValueSlider("a", 1); +r.setUiAttribs({ "colorPick": true }); + +// const uniColor=new CGL.Uniform(shader,'4f','color',r,g,b,a); +const colUni = shader.addUniformFrag("4f", "color", r, g, b, a); + +shader.uniformColorDiffuse = colUni; + +// diffuse outTexture + +const diffuseTexture = op.inTexture("texture"); +let diffuseTextureUniform = null; +diffuseTexture.onChange = updateDiffuseTexture; + +const colorizeTexture = op.inValueBool("colorizeTexture", false); +const vertexColors = op.inValueBool("Vertex Colors", false); + +// opacity texture +const textureOpacity = op.inTexture("textureOpacity"); +let textureOpacityUniform = null; + +const alphaMaskSource = op.inSwitch("Alpha Mask Source", ["Luminance", "R", "G", "B", "A", "1-A"], "Luminance"); +alphaMaskSource.setUiAttribs({ "greyout": true }); +textureOpacity.onChange = updateOpacity; + +const texCoordAlpha = op.inValueBool("Opacity TexCoords Transform", false); +const discardTransPxl = op.inValueBool("Discard Transparent Pixels"); + +// texture coords +const + diffuseRepeatX = op.inValue("diffuseRepeatX", 1), + diffuseRepeatY = op.inValue("diffuseRepeatY", 1), + diffuseOffsetX = op.inValue("Tex Offset X", 0), + diffuseOffsetY = op.inValue("Tex Offset Y", 0), + cropRepeat = op.inBool("Crop TexCoords", false); + +shader.addUniformFrag("f", "diffuseRepeatX", diffuseRepeatX); +shader.addUniformFrag("f", "diffuseRepeatY", diffuseRepeatY); +shader.addUniformFrag("f", "texOffsetX", diffuseOffsetX); +shader.addUniformFrag("f", "texOffsetY", diffuseOffsetY); + +const doBillboard = op.inValueBool("billboard", false); + +alphaMaskSource.onChange = + doBillboard.onChange = + discardTransPxl.onChange = + texCoordAlpha.onChange = + cropRepeat.onChange = + vertexColors.onChange = + colorizeTexture.onChange = updateDefines; + +op.setPortGroup("Color", [r, g, b, a]); +op.setPortGroup("Color Texture", [diffuseTexture, vertexColors, colorizeTexture]); +op.setPortGroup("Opacity", [textureOpacity, alphaMaskSource, discardTransPxl, texCoordAlpha]); +op.setPortGroup("Texture Transform", [diffuseRepeatX, diffuseRepeatY, diffuseOffsetX, diffuseOffsetY, cropRepeat]); + +updateOpacity(); +updateDiffuseTexture(); + +op.preRender = function () +{ + shader.bind(); + doRender(); +}; + +function doRender() +{ + if (!shader) return; + + cgl.pushShader(shader); + shader.popTextures(); + + if (diffuseTextureUniform && diffuseTexture.get()) shader.pushTexture(diffuseTextureUniform, diffuseTexture.get()); + if (textureOpacityUniform && textureOpacity.get()) shader.pushTexture(textureOpacityUniform, textureOpacity.get()); + + trigger.trigger(); + + cgl.popShader(); +} + +function updateOpacity() +{ + if (textureOpacity.get()) + { + if (textureOpacityUniform !== null) return; + shader.removeUniform("texOpacity"); + shader.define("HAS_TEXTURE_OPACITY"); + if (!textureOpacityUniform)textureOpacityUniform = new CGL.Uniform(shader, "t", "texOpacity"); + + alphaMaskSource.setUiAttribs({ "greyout": false }); + texCoordAlpha.setUiAttribs({ "greyout": false }); + } + else + { + shader.removeUniform("texOpacity"); + shader.removeDefine("HAS_TEXTURE_OPACITY"); + textureOpacityUniform = null; + + alphaMaskSource.setUiAttribs({ "greyout": true }); + texCoordAlpha.setUiAttribs({ "greyout": true }); + } + + updateDefines(); +} + +function updateDiffuseTexture() +{ + if (diffuseTexture.get()) + { + if (!shader.hasDefine("HAS_TEXTURE_DIFFUSE"))shader.define("HAS_TEXTURE_DIFFUSE"); + if (!diffuseTextureUniform)diffuseTextureUniform = new CGL.Uniform(shader, "t", "texDiffuse"); + + diffuseRepeatX.setUiAttribs({ "greyout": false }); + diffuseRepeatY.setUiAttribs({ "greyout": false }); + diffuseOffsetX.setUiAttribs({ "greyout": false }); + diffuseOffsetY.setUiAttribs({ "greyout": false }); + colorizeTexture.setUiAttribs({ "greyout": false }); + } + else + { + shader.removeUniform("texDiffuse"); + shader.removeDefine("HAS_TEXTURE_DIFFUSE"); + diffuseTextureUniform = null; + + diffuseRepeatX.setUiAttribs({ "greyout": true }); + diffuseRepeatY.setUiAttribs({ "greyout": true }); + diffuseOffsetX.setUiAttribs({ "greyout": true }); + diffuseOffsetY.setUiAttribs({ "greyout": true }); + colorizeTexture.setUiAttribs({ "greyout": true }); + } +} + +function updateDefines() +{ + shader.toggleDefine("VERTEX_COLORS", vertexColors.get()); + shader.toggleDefine("CROP_TEXCOORDS", cropRepeat.get()); + shader.toggleDefine("COLORIZE_TEXTURE", colorizeTexture.get()); + shader.toggleDefine("TRANSFORMALPHATEXCOORDS", texCoordAlpha.get()); + shader.toggleDefine("DISCARDTRANS", discardTransPxl.get()); + shader.toggleDefine("BILLBOARD", doBillboard.get()); + + shader.toggleDefine("ALPHA_MASK_ALPHA", alphaMaskSource.get() == "A"); + shader.toggleDefine("ALPHA_MASK_IALPHA", alphaMaskSource.get() == "1-A"); + shader.toggleDefine("ALPHA_MASK_LUMI", alphaMaskSource.get() == "Luminance"); + shader.toggleDefine("ALPHA_MASK_R", alphaMaskSource.get() == "R"); + shader.toggleDefine("ALPHA_MASK_G", alphaMaskSource.get() == "G"); + shader.toggleDefine("ALPHA_MASK_B", alphaMaskSource.get() == "B"); +} + + +}; + +Ops.Gl.Shader.BasicMaterial_v3.prototype = new CABLES.Op(); +CABLES.OPS["ec55d252-3843-41b1-b731-0482dbd9e72b"]={f:Ops.Gl.Shader.BasicMaterial_v3,objName:"Ops.Gl.Shader.BasicMaterial_v3"}; + + + + +// ************************************************************** +// +// Ops.Deprecated.Anim.RelativeTime +// +// ************************************************************** + +Ops.Deprecated.Anim.RelativeTime = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + mul = op.inValue("Multiply", 1), + outTrigger = op.outTrigger("Trigger out"), + result = op.outNumber("result"); + +exe.onTriggered = update; +update(); + +function update() +{ + result.set(op.patch.freeTimer.get() * mul.get()); + outTrigger.trigger(); +} + + +}; + +Ops.Deprecated.Anim.RelativeTime.prototype = new CABLES.Op(); +CABLES.OPS["917df27b-7cc3-465f-986d-bcf5a7e125a7"]={f:Ops.Deprecated.Anim.RelativeTime,objName:"Ops.Deprecated.Anim.RelativeTime"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Noise.FBMNoise +// +// ************************************************************** + +Ops.Gl.TextureEffects.Noise.FBMNoise = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"fbmnoise_frag":"UNI sampler2D tex;\nUNI float anim;\n\nUNI float scale;\nUNI float repeat;\n\nUNI float scrollX;\nUNI float scrollY;\n\nUNI float amount;\n\nUNI bool layer1;\nUNI bool layer2;\nUNI bool layer3;\nUNI bool layer4;\nUNI vec3 color;\nUNI float aspect;\n\nIN vec2 texCoord;\n\n\n{{CGL.BLENDMODES}}\n\n// csdcsdcds\n// adapted from warp shader by inigo quilez/iq\n// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.\n\n// See here for a tutorial on how to make this: http://www.iquilezles.org/www/articles/warp/warp.htm\n\nconst mat2 m = mat2( 0.80, 0.60, -0.60, 0.80 );\n\nfloat noise( in vec2 x )\n{\n\treturn sin(1.5*x.x)*sin(1.5*x.y);\n}\n\nfloat fbm4( vec2 p )\n{\n float f = 0.0;\n f += 0.5000*noise( p ); p = m*p*2.02;\n f += 0.2500*noise( p ); p = m*p*2.03;\n f += 0.1250*noise( p ); p = m*p*2.01;\n f += 0.0625*noise( p );\n return f/0.9375;\n}\n\nfloat fbm6( vec2 p )\n{\n float f = 0.0;\n f += 0.500000*(0.5+0.5*noise( p )); p = m*p*2.02;\n f += 0.250000*(0.5+0.5*noise( p )); p = m*p*2.03;\n f += 0.125000*(0.5+0.5*noise( p )); p = m*p*2.01;\n f += 0.062500*(0.5+0.5*noise( p )); p = m*p*2.04;\n f += 0.031250*(0.5+0.5*noise( p )); p = m*p*2.01;\n f += 0.015625*(0.5+0.5*noise( p ));\n return f/0.96875;\n}\n\nvoid main()\n{\n // vec4 col=texture(tex,texCoord+2.0*fbm4(texCoord+2.0*fbm6(texCoord+anim)));\n\n vec2 tc=texCoord;\n\t#ifdef DO_TILEABLE\n\t tc=abs(texCoord-0.5);\n\t#endif\n\n\n vec2 p=(tc-0.5)*scale;\n\n\n p.y/=aspect;\n vec2 q = vec2( fbm4( p + vec2(0.3+scrollX,0.20+scrollY) ),\n fbm4( p + vec2(3.1+scrollX,1.3+scrollY) ) );\n\n vec2 q2 = vec2( fbm4( p + vec2(2.0+scrollX,1.0+scrollY) ),\n fbm4( p + vec2(3.1+scrollX,1.3+scrollY) ) );\n\n vec2 q3 = vec2( fbm4( p + vec2(9.0+scrollX,4.0+scrollY) ),\n fbm4( p + vec2(3.1+scrollX,4.3+scrollY) ) );\n\n\n\n float v= fbm4( ( p + 4.0*q +anim*0.1)*repeat);\n float v2= fbm4( (p + 4.0*q2 +anim*0.1)*repeat );\n\n float v3= fbm6( (p + 4.0*q3 +anim*0.1)*repeat );\n float v4= fbm6( (p + 4.0*q2 +anim*0.1)*repeat );\n\n\n\n\n vec4 base=texture(tex,texCoord);\n\n vec4 finalColor;\n float colVal=0.0;\n float numLayers=0.0;\n\n if(layer1)\n {\n colVal+=v;\n numLayers++;\n }\n\n if(layer2)\n {\n colVal+=v2;\n numLayers++;\n }\n\n if(layer3)\n {\n colVal+=v3;\n numLayers++;\n }\n\n if(layer4)\n {\n colVal+=v4;\n numLayers++;\n }\n\n finalColor=vec4( color*vec3(colVal/numLayers),1.0);\n\n outColor = cgl_blend(base,finalColor,amount);;\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"), + amount = op.inValueSlider("Amount", 1), + r = op.inValueSlider("r", 1.0), + g = op.inValueSlider("g", 1.0), + b = op.inValueSlider("b", 1.0), + trigger = op.outTrigger("trigger"); + +r.setUiAttribs({ "colorPick": true }); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "fbmnoise"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.fbmnoise_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +const uniScale = new CGL.Uniform(shader, "f", "scale", op.inValue("scale", 2)); +const uniAnim = new CGL.Uniform(shader, "f", "anim", op.inValue("anim", 0)); +const uniScrollX = new CGL.Uniform(shader, "f", "scrollX", op.inValue("scrollX", 9)); +const uniScrollY = new CGL.Uniform(shader, "f", "scrollY", op.inValue("scrollY", 0)); +const uniRepeat = new CGL.Uniform(shader, "f", "repeat", op.inValue("repeat", 1)); +const uniAspect = new CGL.Uniform(shader, "f", "aspect", op.inValue("aspect", 1)); + +const uniLayer1 = new CGL.Uniform(shader, "b", "layer1", op.inValueBool("Layer 1", true)); +const uniLayer2 = new CGL.Uniform(shader, "b", "layer2", op.inValueBool("Layer 2", true)); +const uniLayer3 = new CGL.Uniform(shader, "b", "layer3", op.inValueBool("Layer 3", true)); +const uniLayer4 = new CGL.Uniform(shader, "b", "layer4", op.inValueBool("Layer 4", true)); + +const uniColor = new CGL.Uniform(shader, "3f", "color", r, g, b); + +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +const tile = op.inValueBool("Tileable", false); +tile.onChange = updateTileable; +function updateTileable() +{ + if (tile.get())shader.define("DO_TILEABLE"); + else shader.removeDefine("DO_TILEABLE"); +} + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + uniAspect.set(cgl.currentTextureEffect.getCurrentSourceTexture().width / cgl.currentTextureEffect.getCurrentSourceTexture().height); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Noise.FBMNoise.prototype = new CABLES.Op(); +CABLES.OPS["7073186c-b776-48c2-a01e-041df88ad88a"]={f:Ops.Gl.TextureEffects.Noise.FBMNoise,objName:"Ops.Gl.TextureEffects.Noise.FBMNoise"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.ColorMap +// +// ************************************************************** + +Ops.Gl.TextureEffects.ColorMap = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"colormap_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI sampler2D gradient;\nUNI float pos;\nUNI float amount;\nUNI float vmin;\nUNI float vmax;\n\n{{CGL.BLENDMODES}}\n\n\nfloat lumi(vec3 color)\n{\n return vec3(dot(vec3(0.2126,0.7152,0.0722), color)).r;\n}\n\nvoid main()\n{\n vec4 base=texture(tex,texCoord);\n\n base=clamp(base,vmin,vmax);\n\n #ifdef METH_LUMI\n vec4 color=texture(gradient,vec2(lumi(base.rgb),pos));\n #endif\n\n #ifdef METH_CHANNELS\n vec4 color=vec4(1.0);\n color.r=texture(gradient,vec2(base.r,pos)).r;\n color.g=texture(gradient,vec2(base.g,pos)).g;\n color.b=texture(gradient,vec2(base.b,pos)).b;\n #endif\n\n// outColor= vec4(color);\n outColor=cgl_blend(base,color,amount);\n\n}\n",}; +let render = op.inTrigger("render"); +let trigger = op.outTrigger("trigger"); + +const blendMode = CGL.TextureEffect.AddBlendSelect(op, "Blend Mode", "normal"); +const amount = op.inValueSlider("Amount", 1); + +let inGradient = op.inTexture("Gradient"); +let inMethod = op.inSwitch("Method", ["Luminance", "Channels"], "Luminance"); + +let inMin = op.inFloatSlider("Min", 0); +let inMax = op.inFloatSlider("Max", 1); + +let inPos = op.inValueSlider("Position", 0.5); + +op.setPortGroup("Vertical Position", [inMin, inMax, inPos]); + +let cgl = op.patch.cgl; +let shader = new CGL.Shader(cgl, op.name); +shader.define("METH_LUMI"); + +shader.setSource(shader.getDefaultVertexShader(), attachments.colormap_frag); +var textureUniform = new CGL.Uniform(shader, "t", "tex", 0); + +var textureUniform = new CGL.Uniform(shader, "t", "gradient", 1); +let uniPos = new CGL.Uniform(shader, "f", "pos", inPos); + +let uniMin = new CGL.Uniform(shader, "f", "vmin", inMin); +let uniMax = new CGL.Uniform(shader, "f", "vmax", inMax); +let uniAmount = new CGL.Uniform(shader, "f", "amount", amount); + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +inMethod.onChange = () => +{ + shader.toggleDefine("METH_LUMI", inMethod.get() == "Luminance"); + shader.toggleDefine("METH_CHANNELS", inMethod.get() == "Channels"); +}; + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + if (!inGradient.get()) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.setTexture(1, inGradient.get().tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, inGradient.get().tex ); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.ColorMap.prototype = new CABLES.Op(); +CABLES.OPS["58e302d7-4b84-4077-aa13-4f3cf0885205"]={f:Ops.Gl.TextureEffects.ColorMap,objName:"Ops.Gl.TextureEffects.ColorMap"}; + + + + +// ************************************************************** +// +// Ops.Gl.Render2Texture +// +// ************************************************************** + +Ops.Gl.Render2Texture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const cgl = op.patch.cgl; + +const + render = op.inTrigger("render"), + useVPSize = op.inValueBool("use viewport size", true), + width = op.inValueInt("texture width", 512), + height = op.inValueInt("texture height", 512), + aspect = op.inBool("Auto Aspect", false), + tfilter = op.inSwitch("filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inSwitch("Wrap", ["Clamp", "Repeat", "Mirror"], "Repeat"), + msaa = op.inSwitch("MSAA", ["none", "2x", "4x", "8x"], "none"), + trigger = op.outTrigger("trigger"), + tex = op.outTexture("texture"), + texDepth = op.outTexture("textureDepth"), + fpTexture = op.inValueBool("HDR"), + depth = op.inValueBool("Depth", true), + clear = op.inValueBool("Clear", true); + +let fb = null; +let reInitFb = true; +tex.set(CGL.Texture.getEmptyTexture(cgl)); + +op.setPortGroup("Size", [useVPSize, width, height, aspect]); + +const prevViewPort = [0, 0, 0, 0]; + +fpTexture.setUiAttribs({ "title": "Pixelformat Float 32bit" }); + +fpTexture.onChange = + depth.onChange = + clear.onChange = + tfilter.onChange = + twrap.onChange = + msaa.onChange = initFbLater; + +useVPSize.onChange = updateVpSize; + +render.onTriggered = + op.preRender = doRender; + +updateVpSize(); + +function updateVpSize() +{ + width.setUiAttribs({ "greyout": useVPSize.get() }); + height.setUiAttribs({ "greyout": useVPSize.get() }); + aspect.setUiAttribs({ "greyout": useVPSize.get() }); +} + +function initFbLater() +{ + reInitFb = true; +} + +function doRender() +{ + const vp = cgl.getViewPort(); + prevViewPort[0] = vp[0]; + prevViewPort[1] = vp[1]; + prevViewPort[2] = vp[2]; + prevViewPort[3] = vp[3]; + + if (!fb || reInitFb) + { + if (fb) fb.delete(); + + let selectedWrap = CGL.Texture.WRAP_REPEAT; + if (twrap.get() == "Clamp") selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + else if (twrap.get() == "Mirror") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + + let selectFilter = CGL.Texture.FILTER_NEAREST; + if (tfilter.get() == "nearest") selectFilter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") selectFilter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") selectFilter = CGL.Texture.FILTER_MIPMAP; + + if (fpTexture.get() && tfilter.get() == "mipmap") op.setUiError("fpmipmap", "Don't use mipmap and HDR at the same time, many systems do not support this."); + else op.setUiError("fpmipmap", null); + + if (cgl.glVersion >= 2) + { + let ms = true; + let msSamples = 4; + + if (msaa.get() == "none") + { + msSamples = 0; + ms = false; + } + if (msaa.get() == "2x") msSamples = 2; + if (msaa.get() == "4x") msSamples = 4; + if (msaa.get() == "8x") msSamples = 8; + + fb = new CGL.Framebuffer2(cgl, 8, 8, + { + "name": "render2texture " + op.id, + "isFloatingPointTexture": fpTexture.get(), + "multisampling": ms, + "wrap": selectedWrap, + "filter": selectFilter, + "depth": depth.get(), + "multisamplingSamples": msSamples, + "clear": clear.get() + }); + } + else + { + fb = new CGL.Framebuffer(cgl, 8, 8, { "isFloatingPointTexture": fpTexture.get(), "clear": clear.get() }); + } + + texDepth.set(fb.getTextureDepth()); + reInitFb = false; + } + + if (useVPSize.get()) + { + width.set(cgl.getViewPort()[2]); + height.set(cgl.getViewPort()[3]); + } + + if (fb.getWidth() != Math.ceil(width.get()) || fb.getHeight() != Math.ceil(height.get())) + { + fb.setSize( + Math.max(1, Math.ceil(width.get())), + Math.max(1, Math.ceil(height.get()))); + } + + fb.renderStart(cgl); + + if (aspect.get()) mat4.perspective(cgl.pMatrix, 45, width.get() / height.get(), 0.1, 1000.0); + + trigger.trigger(); + fb.renderEnd(cgl); + + // cgl.resetViewPort(); + cgl.setViewPort(prevViewPort[0], prevViewPort[1], prevViewPort[2], prevViewPort[3]); + + tex.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + tex.set(fb.getTextureColor()); +} + + +}; + +Ops.Gl.Render2Texture.prototype = new CABLES.Op(); +CABLES.OPS["d01fa820-396c-4cb5-9d78-6b14762852af"]={f:Ops.Gl.Render2Texture,objName:"Ops.Gl.Render2Texture"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.DrawImage_v3 +// +// ************************************************************** + +Ops.Gl.TextureEffects.DrawImage_v3 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"drawimage_frag":"#ifdef HAS_TEXTURES\n IN vec2 texCoord;\n UNI sampler2D tex;\n UNI sampler2D image;\n#endif\n\n#ifdef TEX_TRANSFORM\n IN mat3 transform;\n#endif\n// UNI float rotate;\n\n{{CGL.BLENDMODES}}\n\n#ifdef HAS_TEXTUREALPHA\n UNI sampler2D imageAlpha;\n#endif\n\nUNI float amount;\n\n#ifdef ASPECT_RATIO\n UNI float aspectTex;\n UNI float aspectPos;\n#endif\n\nvoid main()\n{\n vec4 blendRGBA=vec4(0.0,0.0,0.0,1.0);\n\n #ifdef HAS_TEXTURES\n vec2 tc=texCoord;\n\n #ifdef TEX_FLIP_X\n tc.x=1.0-tc.x;\n #endif\n #ifdef TEX_FLIP_Y\n tc.y=1.0-tc.y;\n #endif\n\n #ifdef ASPECT_RATIO\n #ifdef ASPECT_AXIS_X\n tc.y=(1.0-aspectPos)-(((1.0-aspectPos)-tc.y)*aspectTex);\n #endif\n #ifdef ASPECT_AXIS_Y\n tc.x=(1.0-aspectPos)-(((1.0-aspectPos)-tc.x)/aspectTex);\n #endif\n #endif\n\n #ifdef TEX_TRANSFORM\n vec3 coordinates=vec3(tc.x, tc.y,1.0);\n tc=(transform * coordinates ).xy;\n #endif\n\n blendRGBA=texture(image,tc);\n\n vec3 blend=blendRGBA.rgb;\n vec4 baseRGBA=texture(tex,texCoord);\n vec3 base=baseRGBA.rgb;\n\n\n #ifdef PREMUL\n blend.rgb = (blend.rgb) + (base.rgb * (1.0 - blendRGBA.a));\n #endif\n\n vec3 colNew=_blend(base,blend);\n\n\n\n\n #ifdef REMOVE_ALPHA_SRC\n blendRGBA.a=1.0;\n #endif\n\n #ifdef HAS_TEXTUREALPHA\n vec4 colImgAlpha=texture(imageAlpha,tc);\n float colImgAlphaAlpha=colImgAlpha.a;\n\n #ifdef ALPHA_FROM_LUMINANCE\n vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), colImgAlpha.rgb ));\n colImgAlphaAlpha=(gray.r+gray.g+gray.b)/3.0;\n #endif\n\n #ifdef ALPHA_FROM_INV_UMINANCE\n vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), colImgAlpha.rgb ));\n colImgAlphaAlpha=1.0-(gray.r+gray.g+gray.b)/3.0;\n #endif\n\n #ifdef INVERT_ALPHA\n colImgAlphaAlpha=clamp(colImgAlphaAlpha,0.0,1.0);\n colImgAlphaAlpha=1.0-colImgAlphaAlpha;\n #endif\n\n blendRGBA.a=colImgAlphaAlpha*blendRGBA.a;\n #endif\n #endif\n\n float am=amount;\n\n #ifdef CLIP_REPEAT\n if(tc.y>1.0 || tc.y<0.0 || tc.x>1.0 || tc.x<0.0)\n {\n // colNew.rgb=vec3(0.0);\n am=0.0;\n }\n #endif\n\n #ifdef ASPECT_RATIO\n #ifdef ASPECT_CROP\n if(tc.y>1.0 || tc.y<0.0 || tc.x>1.0 || tc.x<0.0)\n {\n colNew.rgb=base.rgb;\n am=0.0;\n }\n\n #endif\n #endif\n\n\n\n #ifndef PREMUL\n blendRGBA.rgb=mix(colNew,base,1.0-(am*blendRGBA.a));\n blendRGBA.a=clamp(baseRGBA.a+(blendRGBA.a*am),0.,1.);\n #endif\n\n #ifdef PREMUL\n // premultiply\n // blendRGBA.rgb = (blendRGBA.rgb) + (baseRGBA.rgb * (1.0 - blendRGBA.a));\n blendRGBA=vec4(\n mix(colNew.rgb,base,1.0-(am*blendRGBA.a)),\n blendRGBA.a*am+baseRGBA.a\n );\n #endif\n\n #ifdef ALPHA_MASK\n blendRGBA.a=baseRGBA.a;\n #endif\n\n outColor=blendRGBA;\n}\n\n\n\n\n\n\n\n","drawimage_vert":"IN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\n\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\n\nOUT vec2 texCoord;\n// OUT vec3 norm;\n\n#ifdef TEX_TRANSFORM\n UNI float posX;\n UNI float posY;\n UNI float scaleX;\n UNI float scaleY;\n UNI float rotate;\n OUT mat3 transform;\n#endif\n\nvoid main()\n{\n texCoord=attrTexCoord;\n// norm=attrVertNormal;\n\n #ifdef TEX_TRANSFORM\n vec3 coordinates=vec3(attrTexCoord.x, attrTexCoord.y,1.0);\n float angle = radians( rotate );\n vec2 scale= vec2(scaleX,scaleY);\n vec2 translate= vec2(posX,posY);\n\n transform = mat3( scale.x * cos( angle ), scale.x * sin( angle ), 0.0,\n - scale.y * sin( angle ), scale.y * cos( angle ), 0.0,\n - 0.5 * scale.x * cos( angle ) + 0.5 * scale.y * sin( angle ) - 0.5 * translate.x*2.0 + 0.5, - 0.5 * scale.x * sin( angle ) - 0.5 * scale.y * cos( angle ) - 0.5 * translate.y*2.0 + 0.5, 1.0);\n #endif\n\n gl_Position = projMatrix * mvMatrix * vec4(vPosition, 1.0);\n}\n",}; +const + render = op.inTrigger("render"), + blendMode = CGL.TextureEffect.AddBlendSelect(op, "blendMode"), + amount = op.inValueSlider("amount", 1), + + image = op.inTexture("Image"), + inAlphaPremul = op.inValueBool("Premultiplied", false), + inAlphaMask = op.inValueBool("Alpha Mask", false), + removeAlphaSrc = op.inValueBool("removeAlphaSrc", false), + + imageAlpha = op.inTexture("Mask"), + alphaSrc = op.inValueSelect("Mask Src", ["alpha channel", "luminance", "luminance inv"], "luminance"), + invAlphaChannel = op.inValueBool("Invert alpha channel"), + + inAspect = op.inValueBool("Aspect Ratio", false), + inAspectAxis = op.inValueSelect("Stretch Axis", ["X", "Y"], "X"), + inAspectPos = op.inValueSlider("Position", 0.0), + inAspectCrop = op.inValueBool("Crop", false), + + trigger = op.outTrigger("trigger"); + +blendMode.set("normal"); +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, "drawimage"); + +imageAlpha.onLinkChanged = updateAlphaPorts; + +op.setPortGroup("Mask", [imageAlpha, alphaSrc, invAlphaChannel]); +op.setPortGroup("Aspect Ratio", [inAspect, inAspectPos, inAspectCrop, inAspectAxis]); + +function updateAlphaPorts() +{ + if (imageAlpha.isLinked()) + { + removeAlphaSrc.setUiAttribs({ "greyout": true }); + alphaSrc.setUiAttribs({ "greyout": false }); + invAlphaChannel.setUiAttribs({ "greyout": false }); + } + else + { + removeAlphaSrc.setUiAttribs({ "greyout": false }); + alphaSrc.setUiAttribs({ "greyout": true }); + invAlphaChannel.setUiAttribs({ "greyout": true }); + } +} + +op.toWorkPortsNeedToBeLinked(image); + +shader.setSource(attachments.drawimage_vert, attachments.drawimage_frag); + +const + textureUniform = new CGL.Uniform(shader, "t", "tex", 0), + textureImaghe = new CGL.Uniform(shader, "t", "image", 1), + textureAlpha = new CGL.Uniform(shader, "t", "imageAlpha", 2), + uniTexAspect = new CGL.Uniform(shader, "f", "aspectTex", 1), + uniAspectPos = new CGL.Uniform(shader, "f", "aspectPos", inAspectPos); + +inAspect.onChange = + inAspectCrop.onChange = + inAspectAxis.onChange = updateAspectRatio; + +function updateAspectRatio() +{ + shader.removeDefine("ASPECT_AXIS_X"); + shader.removeDefine("ASPECT_AXIS_Y"); + shader.removeDefine("ASPECT_CROP"); + + inAspectPos.setUiAttribs({ "greyout": !inAspect.get() }); + inAspectCrop.setUiAttribs({ "greyout": !inAspect.get() }); + inAspectAxis.setUiAttribs({ "greyout": !inAspect.get() }); + + if (inAspect.get()) + { + shader.define("ASPECT_RATIO"); + + if (inAspectCrop.get()) shader.define("ASPECT_CROP"); + + if (inAspectAxis.get() == "X") shader.define("ASPECT_AXIS_X"); + if (inAspectAxis.get() == "Y") shader.define("ASPECT_AXIS_Y"); + } + else + { + shader.removeDefine("ASPECT_RATIO"); + if (inAspectCrop.get()) shader.define("ASPECT_CROP"); + + if (inAspectAxis.get() == "X") shader.define("ASPECT_AXIS_X"); + if (inAspectAxis.get() == "Y") shader.define("ASPECT_AXIS_Y"); + } +} + +alphaSrc.set("alpha channel"); + +// +// texture flip +// +const flipX = op.inValueBool("flip x"); +const flipY = op.inValueBool("flip y"); + +// +// texture transform +// + +let doTransform = op.inValueBool("Transform"); + +let scaleX = op.inValueSlider("Scale X", 1); +let scaleY = op.inValueSlider("Scale Y", 1); + +let posX = op.inValue("Position X", 0); +let posY = op.inValue("Position Y", 0); + +let rotate = op.inValue("Rotation", 0); + +const inClipRepeat = op.inValueBool("Clip Repeat", false); + +const uniScaleX = new CGL.Uniform(shader, "f", "scaleX", scaleX); +const uniScaleY = new CGL.Uniform(shader, "f", "scaleY", scaleY); +const uniPosX = new CGL.Uniform(shader, "f", "posX", posX); +const uniPosY = new CGL.Uniform(shader, "f", "posY", posY); +const uniRotate = new CGL.Uniform(shader, "f", "rotate", rotate); + +doTransform.onChange = updateTransformPorts; + +function updateTransformPorts() +{ + shader.toggleDefine("TEX_TRANSFORM", doTransform.get()); + + scaleX.setUiAttribs({ "greyout": !doTransform.get() }); + scaleY.setUiAttribs({ "greyout": !doTransform.get() }); + posX.setUiAttribs({ "greyout": !doTransform.get() }); + posY.setUiAttribs({ "greyout": !doTransform.get() }); + rotate.setUiAttribs({ "greyout": !doTransform.get() }); +} + +CGL.TextureEffect.setupBlending(op, shader, blendMode, amount); + +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +render.onTriggered = doRender; + +inClipRepeat.onChange = + imageAlpha.onChange = + inAlphaPremul.onChange = + inAlphaMask.onChange = + invAlphaChannel.onChange = + flipY.onChange = + flipX.onChange = + removeAlphaSrc.onChange = + alphaSrc.onChange = updateDefines; + +updateTransformPorts(); +updateAlphaPorts(); +updateAspectRatio(); +updateDefines(); + +function updateDefines() +{ + shader.toggleDefine("REMOVE_ALPHA_SRC", removeAlphaSrc.get()); + shader.toggleDefine("ALPHA_MASK", inAlphaMask.get()); + + shader.toggleDefine("CLIP_REPEAT", inClipRepeat.get()); + + shader.toggleDefine("HAS_TEXTUREALPHA", imageAlpha.get() && imageAlpha.get().tex); + + shader.toggleDefine("TEX_FLIP_X", flipX.get()); + shader.toggleDefine("TEX_FLIP_Y", flipY.get()); + + shader.toggleDefine("INVERT_ALPHA", invAlphaChannel.get()); + + shader.toggleDefine("ALPHA_FROM_LUMINANCE", alphaSrc.get() == "luminance"); + shader.toggleDefine("ALPHA_FROM_INV_UMINANCE", alphaSrc.get() == "luminance_inv"); + shader.toggleDefine("PREMUL", inAlphaPremul.get()); +} + +function doRender() +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + const tex = image.get(); + if (tex && tex.tex && amount.get() > 0.0) + { + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + const imgTex = cgl.currentTextureEffect.getCurrentSourceTexture(); + cgl.setTexture(0, imgTex.tex); + + if (imgTex && tex) + { + if (tex.textureType != imgTex.textureType && (tex.textureType != CGL.Texture.TYPE_FLOAT || imgTex.textureType != CGL.Texture.TYPE_FLOAT)) + op.setUiError("textypediff", "Drawing 32bit texture into an 8 bit can result in data/precision loss", 1); + else + op.setUiError("textypediff", null); + } + + const asp = 1 / (cgl.currentTextureEffect.getWidth() / cgl.currentTextureEffect.getHeight()) * (tex.width / tex.height); + // uniTexAspect.setValue(1 / (tex.height / tex.width * imgTex.width / imgTex.height)); + + uniTexAspect.setValue(asp); + + cgl.setTexture(1, tex.tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, image.get().tex ); + + if (imageAlpha.get() && imageAlpha.get().tex) + { + cgl.setTexture(2, imageAlpha.get().tex); + // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, imageAlpha.get().tex ); + } + + // cgl.pushBlend(false); + + cgl.pushBlendMode(CGL.BLEND_NONE, true); + + cgl.currentTextureEffect.finish(); + cgl.popBlendMode(); + + // cgl.popBlend(); + + cgl.popShader(); + } + + trigger.trigger(); +} + + +}; + +Ops.Gl.TextureEffects.DrawImage_v3.prototype = new CABLES.Op(); +CABLES.OPS["8f6b2f15-fcb0-4597-90c0-e5173f2969fe"]={f:Ops.Gl.TextureEffects.DrawImage_v3,objName:"Ops.Gl.TextureEffects.DrawImage_v3"}; + + + + +// ************************************************************** +// +// Ops.Gl.GradientTexture +// +// ************************************************************** + +Ops.Gl.GradientTexture = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const inGrad = op.inGradient("Gradient"), + inDir = op.inValueSelect("Direction", ["X", "Y", "Radial"], "X"), + inSmoothstep = op.inValueBool("Smoothstep", false), + inStep = op.inBool("Step", false), + inFlip = op.inBool("Flip", false), + inSRGB = op.inBool("sRGB", false), + inOklab = op.inBool("Oklab", false), + inSize = op.inValueInt("Size", 256), + tfilter = op.inSwitch("filter", ["nearest", "linear", "mipmap"], "linear"), + twrap = op.inValueSelect("wrap", ["clamp to edge", "repeat", "mirrored repeat"], "clamp to edge"), + inGradArray = op.inArray("Gradient Array"), + inRandom = op.inTriggerButton("Randomize Colors"), + outTex = op.outTexture("Texture"), + outColors = op.outArray("Colors", null, 3), + outColorPos = op.outArray("Colors Pos", null, 1); + +const cgl = op.patch.cgl; + +twrap.onChange = + tfilter.onChange = + inStep.onChange = + inFlip.onChange = + inSRGB.onChange = + inOklab.onChange = + inSize.onChange = inGrad.onChange = inSmoothstep.onChange = inDir.onChange = inGradArray.onChange = update; + +inGrad.set("{\"keys\" : [{\"pos\":0,\"r\":0,\"g\":0,\"b\":0},{\"pos\":0.25,\"r\":0,\"g\":0,\"b\":0},{\"pos\":0.75,\"r\":1,\"g\":1,\"b\":1},{\"pos\":1,\"r\":1,\"g\":1,\"b\":1}]}"); + +op.onLoaded = update; + +inRandom.onTriggered = () => +{ + const keys = parseKeys(); + if (keys) + { + keys.forEach((key) => + { + key.r = Math.random(); + key.g = Math.random(); + key.b = Math.random(); + }); + const newKeys = JSON.stringify({ "keys": keys }); + inGrad.set(newKeys); + } +}; + +function rgbToOklab(r, g, b) +{ + let l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b; + let m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b; + let s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b; + l = Math.cbrt(l); m = Math.cbrt(m); s = Math.cbrt(s); + return [ + l * +0.2104542553 + m * +0.7936177850 + s * -0.0040720468, + l * +1.9779984951 + m * -2.4285922050 + s * +0.4505937099, + l * +0.0259040371 + m * +0.7827717662 + s * -0.8086757660 + ]; +} + +function clamp(value, min, max) +{ + return Math.max(Math.min(value, max), min); +} + +function oklabToRGB(L, a, b) +{ + let l = L + a * +0.3963377774 + b * +0.2158037573; + let m = L + a * -0.1055613458 + b * -0.0638541728; + let s = L + a * -0.0894841775 + b * -1.2914855480; + l **= 3; m **= 3; s **= 3; + let r = l * +4.0767416621 + m * -3.3077115913 + s * +0.2309699292; + let g = l * -1.2684380046 + m * +2.6097574011 + s * -0.3413193965; + var b = l * -0.0041960863 + m * -0.7034186147 + s * +1.7076147010; + r = clamp(r, 0, 1); g = clamp(g, 0, 1); b = clamp(b, 0, 1); + return [r, g, b]; +} + +function lin2srgb(r, g, b) +{ + r /= 255; + + const thr = 0.0031308; + + let c_loR = 12.92 * r; + + let c_hiR = 1.055 * Math.pow(r, 0.41666) - 0.055; + return ((r < thr) ? c_loR : c_hiR) * 255; +} + +function update() +{ + const keys = parseKeys(); + if (keys) updateGradient(keys); +} + +function parseKeys() +{ + let keys = null; + op.setUiError("nodata", null); + op.setUiError("parse", null); + + if (Array.isArray(inGradArray.get())) + { + keys = inGradArray.get(); + } + else + { + let grad = null; + if (!inGrad.get() || inGrad.get() === "") + { + op.setUiError("nodata", "gradient no data"); + return null; + } + + try + { + grad = JSON.parse(inGrad.get()); + } + catch (e) + { + op.setUiError("parse", "could not parse gradient data"); + } + + if (!grad || !grad.keys) + { + op.setUiError("nodata", "gradient no data"); + return null; + } + keys = grad.keys; + } + return keys; +} + +function updateGradient(keys) +{ + let width = Math.round(inSize.get()); + if (width < 4) width = 4; + + let selectedWrap = 0; + let selectedFilter = 0; + if (twrap.get() == "repeat") selectedWrap = CGL.Texture.WRAP_REPEAT; + else if (twrap.get() == "mirrored repeat") selectedWrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + else if (twrap.get() == "clamp to edge") selectedWrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + if (tfilter.get() == "nearest") selectedFilter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") selectedFilter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") selectedFilter = CGL.Texture.FILTER_MIPMAP; + + const tex = new CGL.Texture(cgl); + + if (inDir.get() == "X" || inDir.get() == "Y") + { + const pixels = new Uint8Array(width * 4); + + for (let i = 0; i < keys.length - 1; i++) + { + const keyA = keys[i]; + const keyB = keys[i + 1]; + + for (let x = keyA.pos * width; x < keyB.pos * width; x++) + { + let p = CABLES.map(x, keyA.pos * width, keyB.pos * width, 0, 1); + if (inStep.get())p = Math.round(p); + if (inSmoothstep.get()) p = CABLES.smoothStep(p); + x = Math.round(x); + + let xx = x; + if (inFlip.get())xx = width - x - 1; + + if (inOklab.get()) + { + const klabA = rgbToOklab(keyA.r, keyA.g, keyA.b); + const labA_r = klabA[0]; + const labA_g = klabA[1]; + const labA_b = klabA[2]; + + const klabB = rgbToOklab(keyB.r, keyB.g, keyB.b); + const labB_r = klabB[0]; + const labB_g = klabB[1]; + const labB_b = klabB[2]; + + const l = ((p * labB_r + (1.0 - p) * labA_r)); + const a = ((p * labB_g + (1.0 - p) * labA_g)); + const b = ((p * labB_b + (1.0 - p) * labA_b)); + + const pixCol = oklabToRGB(l, a, b); + pixels[xx * 4 + 0] = Math.round(pixCol[0] * 255); + pixels[xx * 4 + 1] = Math.round(pixCol[1] * 255); + pixels[xx * 4 + 2] = Math.round(pixCol[2] * 255); + } + else + { + pixels[xx * 4 + 0] = Math.round((p * keyB.r + (1.0 - p) * keyA.r) * 255); + pixels[xx * 4 + 1] = Math.round((p * keyB.g + (1.0 - p) * keyA.g) * 255); + pixels[xx * 4 + 2] = Math.round((p * keyB.b + (1.0 - p) * keyA.b) * 255); + } + + if (typeof keyA.a !== "undefined" && typeof keyB.a !== "undefined") + { + const alpha = Math.round((p * keyB.a + (1.0 - p) * keyA.a) * 255); + pixels[xx * 4 + 3] = alpha; + } + else + { + pixels[xx * 4 + 3] = Math.round(255); + } + } + } + + if (inSRGB.get()) + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] = lin2srgb(pixels[i + 0]); + pixels[i + 1] = lin2srgb(pixels[i + 1]); + pixels[i + 2] = lin2srgb(pixels[i + 2]); + } + + if (inDir.get() == "X") tex.initFromData(pixels, width, 1, selectedFilter, selectedWrap); + if (inDir.get() == "Y") tex.initFromData(pixels, 1, width, selectedFilter, selectedWrap); + } + + if (inDir.get() == "Radial") + { + const pixels = new Uint8Array(width * width * 4); + + const animR = new CABLES.Anim(); + const animG = new CABLES.Anim(); + const animB = new CABLES.Anim(); + + for (let i = 0; i < keys.length - 1; i++) + { + animR.setValue(keys[i].pos, keys[i].r); + animG.setValue(keys[i].pos, keys[i].g); + animB.setValue(keys[i].pos, keys[i].b); + } + + for (let x = 0; x < width; x++) + { + for (let y = 0; y < width; y++) + { + const dx = x - (width - 1) / 2; + const dy = y - (width - 1) / 2; + let pos = Math.sqrt(dx * dx + dy * dy) / (width) * 2; + + if (inSmoothstep.get()) pos = CABLES.smoothStep(pos); + + pixels[(x * 4) + (y * 4 * width) + 0] = animR.getValue(pos) * 255; + pixels[(x * 4) + (y * 4 * width) + 1] = animG.getValue(pos) * 255; + pixels[(x * 4) + (y * 4 * width) + 2] = animB.getValue(pos) * 255; + pixels[(x * 4) + (y * 4 * width) + 3] = Math.round(255); + } + } + + if (inSRGB.get()) + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] = lin2srgb(pixels[i + 0]); + pixels[i + 1] = lin2srgb(pixels[i + 1]); + pixels[i + 2] = lin2srgb(pixels[i + 2]); + } + + tex.initFromData(pixels, width, width, selectedFilter, selectedWrap); + } + + const colorArr = []; + for (let i = 0; i < keys.length - 1; i++) + { + colorArr.push(keys[i].r, keys[i].g, keys[i].b); + } + + const colorPosArr = []; + for (let i = 0; i < keys.length - 1; i++) + { + colorPosArr.push(keys[i].pos); + } + + outColors.set(colorArr); + outColorPos.set(colorPosArr); + + outTex.set(null); + outTex.set(tex); +} + + +}; + +Ops.Gl.GradientTexture.prototype = new CABLES.Op(); +CABLES.OPS["01380a50-2dbb-4465-ae80-86349b0b717a"]={f:Ops.Gl.GradientTexture,objName:"Ops.Gl.GradientTexture"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.OrbitControls +// +// ************************************************************** + +Ops.Gl.Matrix.OrbitControls = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + minDist = op.inValueFloat("min distance"), + maxDist = op.inValueFloat("max distance"), + + minRotY = op.inValue("min rot y", 0), + maxRotY = op.inValue("max rot y", 0), + + initialRadius = op.inValue("initial radius", 0), + initialAxis = op.inValueSlider("initial axis y"), + initialX = op.inValueSlider("initial axis x"), + + mul = op.inValueFloat("mul"), + smoothness = op.inValueSlider("Smoothness", 1.0), + speedX = op.inValue("Speed X", 1), + speedY = op.inValue("Speed Y", 1), + + active = op.inValueBool("Active", true), + + allowPanning = op.inValueBool("Allow Panning", true), + allowZooming = op.inValueBool("Allow Zooming", true), + allowRotation = op.inValueBool("Allow Rotation", true), + restricted = op.inValueBool("restricted", true), + + trigger = op.outTrigger("trigger"), + outRadius = op.outNumber("radius"), + outXDeg = op.outNumber("Rot X"), + outYDeg = op.outNumber("Rot Y"), + + inReset = op.inTriggerButton("Reset"); + +op.setPortGroup("Initial Values", [initialAxis, initialX, initialRadius]); +op.setPortGroup("Interaction", [mul, smoothness, speedX, speedY]); +op.setPortGroup("Boundaries", [minRotY, maxRotY, minDist, maxDist]); + +mul.set(1); +minDist.set(0.05); +maxDist.set(99999); + +inReset.onTriggered = reset; + +let eye = vec3.create(); +const vUp = vec3.create(); +const vCenter = vec3.create(); +const viewMatrix = mat4.create(); +const tempViewMatrix = mat4.create(); +const vOffset = vec3.create(); +const finalEyeAbs = vec3.create(); + +initialAxis.set(0.5); + +let mouseDown = false; +let radius = 5; +outRadius.set(radius); + +let lastMouseX = 0, lastMouseY = 0; +let percX = 0, percY = 0; + +vec3.set(vCenter, 0, 0, 0); +vec3.set(vUp, 0, 1, 0); + +const tempEye = vec3.create(); +const finalEye = vec3.create(); +const tempCenter = vec3.create(); +const finalCenter = vec3.create(); + +let px = 0; +let py = 0; + +let divisor = 1; +let element = null; +updateSmoothness(); + +op.onDelete = unbind; + +const halfCircle = Math.PI; +const fullCircle = Math.PI * 2; + +function reset() +{ + let off = 0; + + if (px % fullCircle < -halfCircle) + { + off = -fullCircle; + px %= -fullCircle; + } + else + if (px % fullCircle > halfCircle) + { + off = fullCircle; + px %= fullCircle; + } + else px %= fullCircle; + + py %= (Math.PI); + + vec3.set(vOffset, 0, 0, 0); + vec3.set(vCenter, 0, 0, 0); + vec3.set(vUp, 0, 1, 0); + + percX = (initialX.get() * Math.PI * 2 + off); + percY = (initialAxis.get() - 0.5); + + radius = initialRadius.get(); + eye = circlePos(percY); +} + +function updateSmoothness() +{ + divisor = smoothness.get() * 10 + 1.0; +} + +smoothness.onChange = updateSmoothness; + +let initializing = true; + +function ip(val, goal) +{ + if (initializing) return goal; + return val + (goal - val) / divisor; +} + +let lastPy = 0; +const lastPx = 0; + +render.onTriggered = function () +{ + const cgl = op.patch.cg; + + if (!element) + { + setElement(cgl.canvas); + bind(); + } + + cgl.pushViewMatrix(); + + px = ip(px, percX); + py = ip(py, percY); + + let degY = (py + 0.5) * 180; + + if (minRotY.get() !== 0 && degY < minRotY.get()) + { + degY = minRotY.get(); + py = lastPy; + } + else if (maxRotY.get() !== 0 && degY > maxRotY.get()) + { + degY = maxRotY.get(); + py = lastPy; + } + else + { + lastPy = py; + } + + const degX = (px) * CGL.RAD2DEG; + + outYDeg.set(degY); + outXDeg.set(degX); + + circlePosi(eye, py); + + vec3.add(tempEye, eye, vOffset); + vec3.add(tempCenter, vCenter, vOffset); + + finalEye[0] = ip(finalEye[0], tempEye[0]); + finalEye[1] = ip(finalEye[1], tempEye[1]); + finalEye[2] = ip(finalEye[2], tempEye[2]); + + finalCenter[0] = ip(finalCenter[0], tempCenter[0]); + finalCenter[1] = ip(finalCenter[1], tempCenter[1]); + finalCenter[2] = ip(finalCenter[2], tempCenter[2]); + + const empty = vec3.create(); + + mat4.lookAt(viewMatrix, finalEye, finalCenter, vUp); + mat4.rotate(viewMatrix, viewMatrix, px, vUp); + + // finaly multiply current scene viewmatrix + mat4.multiply(cgl.vMatrix, cgl.vMatrix, viewMatrix); + + trigger.trigger(); + cgl.popViewMatrix(); + initializing = false; +}; + +function circlePosi(vec, perc) +{ + const mmul = mul.get(); + if (radius < minDist.get() * mmul) radius = minDist.get() * mmul; + if (radius > maxDist.get() * mmul) radius = maxDist.get() * mmul; + + outRadius.set(radius * mmul); + + let i = 0, degInRad = 0; + + degInRad = 360 * perc / 2 * CGL.DEG2RAD; + vec3.set(vec, + Math.cos(degInRad) * radius * mmul, + Math.sin(degInRad) * radius * mmul, + 0); + return vec; +} + +function circlePos(perc) +{ + const mmul = mul.get(); + if (radius < minDist.get() * mmul)radius = minDist.get() * mmul; + if (radius > maxDist.get() * mmul)radius = maxDist.get() * mmul; + + outRadius.set(radius * mmul); + + let i = 0, degInRad = 0; + const vec = vec3.create(); + degInRad = 360 * perc / 2 * CGL.DEG2RAD; + vec3.set(vec, + Math.cos(degInRad) * radius * mmul, + Math.sin(degInRad) * radius * mmul, + 0); + return vec; +} + +function onmousemove(event) +{ + if (!mouseDown) return; + + const x = event.clientX; + const y = event.clientY; + + let movementX = (x - lastMouseX); + let movementY = (y - lastMouseY); + + movementX *= speedX.get(); + movementY *= speedY.get(); + + if (event.buttons == 2 && allowPanning.get()) + { + vOffset[2] += movementX * 0.01 * mul.get(); + vOffset[1] += movementY * 0.01 * mul.get(); + } + else + if (event.buttons == 4 && allowZooming.get()) + { + radius += movementY * 0.05; + eye = circlePos(percY); + } + else + { + if (allowRotation.get()) + { + percX += movementX * 0.003; + percY += movementY * 0.002; + + if (restricted.get()) + { + if (percY > 0.5)percY = 0.5; + if (percY < -0.5)percY = -0.5; + } + } + } + + lastMouseX = x; + lastMouseY = y; +} + +function onMouseDown(event) +{ + lastMouseX = event.clientX; + lastMouseY = event.clientY; + mouseDown = true; + + try { element.setPointerCapture(event.pointerId); } + catch (e) {} +} + +function onMouseUp(e) +{ + mouseDown = false; + // cgl.canvas.style.cursor='url(/ui/img/rotate.png),pointer'; + + try { element.releasePointerCapture(e.pointerId); } + catch (e) {} +} + +function lockChange() +{ + const el = op.patch.cg.canvas; + + if (document.pointerLockElement === el || document.mozPointerLockElement === el || document.webkitPointerLockElement === el) + { + document.addEventListener("mousemove", onmousemove, false); + } +} + +function onMouseEnter(e) +{ + // cgl.canvas.style.cursor='url(/ui/img/rotate.png),pointer'; +} + +initialRadius.onChange = function () +{ + radius = initialRadius.get(); + reset(); +}; + +initialX.onChange = function () +{ + px = percX = (initialX.get() * Math.PI * 2); +}; + +initialAxis.onChange = function () +{ + py = percY = (initialAxis.get() - 0.5); + eye = circlePos(percY); +}; + +const onMouseWheel = function (event) +{ + if (allowZooming.get()) + { + const delta = CGL.getWheelSpeed(event) * 0.06; + radius += (parseFloat(delta)) * 1.2; + + eye = circlePos(percY); + } +}; + +const ontouchstart = function (event) +{ + if (event.touches && event.touches.length > 0) onMouseDown(event.touches[0]); +}; + +const ontouchend = function (event) +{ + onMouseUp(); +}; + +const ontouchmove = function (event) +{ + if (event.touches && event.touches.length > 0) onmousemove(event.touches[0]); +}; + +active.onChange = function () +{ + if (active.get())bind(); + else unbind(); +}; + +function setElement(ele) +{ + unbind(); + element = ele; + bind(); +} + +function bind() +{ + if (!element) return; + + element.addEventListener("pointermove", onmousemove); + element.addEventListener("pointerdown", onMouseDown); + element.addEventListener("pointerup", onMouseUp); + element.addEventListener("pointerleave", onMouseUp); + element.addEventListener("pointerenter", onMouseEnter); + element.addEventListener("contextmenu", function (e) { e.preventDefault(); }); + element.addEventListener("wheel", onMouseWheel, { "passive": true }); +} + +function unbind() +{ + if (!element) return; + + element.removeEventListener("pointermove", onmousemove); + element.removeEventListener("pointerdown", onMouseDown); + element.removeEventListener("pointerup", onMouseUp); + element.removeEventListener("pointerleave", onMouseUp); + element.removeEventListener("pointerenter", onMouseUp); + element.removeEventListener("wheel", onMouseWheel); +} + +eye = circlePos(0); + +initialX.set(0.25); +initialRadius.set(0.05); + + +}; + +Ops.Gl.Matrix.OrbitControls.prototype = new CABLES.Op(); +CABLES.OPS["eaf4f7ce-08a3-4d1b-b9f4-ebc0b7b1cde1"]={f:Ops.Gl.Matrix.OrbitControls,objName:"Ops.Gl.Matrix.OrbitControls"}; + + + + +// ************************************************************** +// +// Ops.Gl.TextureEffects.Vibrance +// +// ************************************************************** + +Ops.Gl.TextureEffects.Vibrance = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"vibrance_frag":"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\n\nconst vec4 lumcoeff = vec4(0.299,0.587,0.114, 0.);\n\nvoid main()\n{\n vec4 col=vec4(1.0,0.0,0.0,1.0);\n col=texture(tex,texCoord);\n\n float luma = dot(col, lumcoeff);\n vec4 mask = (col - vec4(luma));\n mask = clamp(mask, 0.0, 1.0);\n float lumaMask = dot(lumcoeff, mask);\n lumaMask = 1.0 - lumaMask;\n vec4 vibrance = mix(vec4(luma), col, 1.0 + amount * lumaMask);\n outColor= vibrance;\n}",}; +const render = op.inTrigger("Render"); +const trigger = op.outTrigger("Trigger"); +const amount = op.inValue("amount", 2); + +const cgl = op.patch.cgl; +const shader = new CGL.Shader(cgl, op.name); + +shader.setSource(shader.getDefaultVertexShader(), attachments.vibrance_frag); +const textureUniform = new CGL.Uniform(shader, "t", "tex", 0); +const amountUniform = new CGL.Uniform(shader, "f", "amount", amount); + +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpInEffect(op)) return; + + cgl.pushShader(shader); + cgl.currentTextureEffect.bind(); + + cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex); + + cgl.currentTextureEffect.finish(); + cgl.popShader(); + + trigger.trigger(); +}; + + +}; + +Ops.Gl.TextureEffects.Vibrance.prototype = new CABLES.Op(); +CABLES.OPS["9c71c980-e439-4397-9c2b-c2ae085eaed9"]={f:Ops.Gl.TextureEffects.Vibrance,objName:"Ops.Gl.TextureEffects.Vibrance"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.FullscreenRectangle +// +// ************************************************************** + +Ops.Gl.Meshes.FullscreenRectangle = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={"shader_frag":"UNI sampler2D tex;\nIN vec2 texCoord;\n\nvoid main()\n{\n outColor= texture(tex,texCoord);\n}\n\n","shader_vert":"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\n\nOUT vec2 texCoord;\nIN vec2 attrTexCoord;\n\nvoid main()\n{\n vec4 pos=vec4(vPosition, 1.0);\n\n texCoord=vec2(attrTexCoord.x,(1.0-attrTexCoord.y));\n\n gl_Position = projMatrix * mvMatrix * pos;\n}\n",}; +const + render = op.inTrigger("render"), + inScale = op.inSwitch("Scale", ["Stretch", "Fit"], "Fit"), + flipY = op.inValueBool("Flip Y"), + flipX = op.inValueBool("Flip X"), + inTexture = op.inTexture("Texture"), + trigger = op.outTrigger("trigger"); + +const cgl = op.patch.cgl; +let mesh = null; +let geom = new CGL.Geometry("fullscreen rectangle"); +let x = 0, y = 0, z = 0, w = 0, h = 0; + +flipX.onChange = rebuildFlip; +flipY.onChange = rebuildFlip; +render.onTriggered = doRender; +inTexture.onLinkChanged = updateUi; +op.toWorkPortsNeedToBeLinked(render); + +const shader = new CGL.Shader(cgl, "fullscreenrectangle"); +shader.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG"]); + +shader.setSource(attachments.shader_vert, attachments.shader_frag); +shader.fullscreenRectUniform = new CGL.Uniform(shader, "t", "tex", 0); +shader.aspectUni = new CGL.Uniform(shader, "f", "aspectTex", 0); + +let useShader = false; +let updateShaderLater = true; +let fitImageAspect = false; +let oldVp = []; + +updateUi(); + +inTexture.onChange = function () +{ + updateShaderLater = true; +}; + +function updateUi() +{ + if (!CABLES.UI) return; + flipY.setUiAttribs({ "greyout": !inTexture.isLinked() }); + flipX.setUiAttribs({ "greyout": !inTexture.isLinked() }); + inScale.setUiAttribs({ "greyout": !inTexture.isLinked() }); +} + +function updateShader() +{ + let tex = inTexture.get(); + if (tex) useShader = true; + else useShader = false; +} + +op.preRender = function () +{ + updateShader(); + shader.bind(); + if (mesh)mesh.render(shader); + doRender(); +}; + +inScale.onChange = () => +{ + fitImageAspect = inScale.get() == "Fit"; +}; + +function doRender() +{ + if (cgl.getViewPort()[2] != w || cgl.getViewPort()[3] != h || !mesh) rebuild(); + + if (updateShaderLater) updateShader(); + + cgl.pushPMatrix(); + mat4.identity(cgl.pMatrix); + mat4.ortho(cgl.pMatrix, 0, w, h, 0, -10.0, 1000); + + cgl.pushModelMatrix(); + mat4.identity(cgl.mMatrix); + + cgl.pushViewMatrix(); + mat4.identity(cgl.vMatrix); + + if (fitImageAspect && inTexture.get()) + { + const rat = inTexture.get().width / inTexture.get().height; + + let _h = h; + let _w = h * rat; + + if (_w > w) + { + _h = w * 1 / rat; + _w = w; + } + + oldVp[0] = cgl.getViewPort()[0]; + oldVp[1] = cgl.getViewPort()[1]; + oldVp[2] = cgl.getViewPort()[2]; + oldVp[3] = cgl.getViewPort()[3]; + + cgl.setViewPort((w - _w) / 2, (h - _h) / 2, _w, _h); + // cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT); + } + + if (useShader) + { + if (inTexture.get()) + cgl.setTexture(0, inTexture.get().tex); + + mesh.render(shader); + } + else + { + mesh.render(cgl.getShader()); + } + + cgl.gl.clear(cgl.gl.DEPTH_BUFFER_BIT); + + cgl.popPMatrix(); + cgl.popModelMatrix(); + cgl.popViewMatrix(); + + if (fitImageAspect && inTexture.get()) + cgl.setViewPort(oldVp[0], oldVp[1], oldVp[2], oldVp[3]); + + trigger.trigger(); +} + +function rebuildFlip() +{ + mesh = null; +} + +function rebuild() +{ + const currentViewPort = cgl.getViewPort(); + + if (currentViewPort[2] == w && currentViewPort[3] == h && mesh) return; + + let xx = 0, xy = 0; + + w = currentViewPort[2]; + h = currentViewPort[3]; + + geom.vertices = new Float32Array([ + xx + w, xy + h, 0.0, + xx, xy + h, 0.0, + xx + w, xy, 0.0, + xx, xy, 0.0 + ]); + + let tc = null; + + if (flipY.get()) + tc = new Float32Array([ + 1.0, 0.0, + 0.0, 0.0, + 1.0, 1.0, + 0.0, 1.0 + ]); + else + tc = new Float32Array([ + 1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 0.0, 0.0 + ]); + + if (flipX.get()) + { + tc[0] = 0.0; + tc[2] = 1.0; + tc[4] = 0.0; + tc[6] = 1.0; + } + + geom.setTexCoords(tc); + + geom.verticesIndices = new Uint16Array([ + 2, 1, 0, + 3, 1, 2 + ]); + + geom.vertexNormals = new Float32Array([ + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + ]); + geom.tangents = new Float32Array([ + -1, 0, 0, + -1, 0, 0, + -1, 0, 0, + -1, 0, 0]); + geom.biTangents == new Float32Array([ + 0, -1, 0, + 0, -1, 0, + 0, -1, 0, + 0, -1, 0]); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); +} + + +}; + +Ops.Gl.Meshes.FullscreenRectangle.prototype = new CABLES.Op(); +CABLES.OPS["255bd15b-cc91-4a12-9b4e-53c710cbb282"]={f:Ops.Gl.Meshes.FullscreenRectangle,objName:"Ops.Gl.Meshes.FullscreenRectangle"}; + + + + +// ************************************************************** +// +// Ops.Gl.Matrix.TranslateView +// +// ************************************************************** + +Ops.Gl.Matrix.TranslateView = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render=op.inTrigger('render'), + x=op.inValueFloat("x"), + y=op.inValueFloat("y"), + z=op.inValueFloat("z"), + trigger=op.outTrigger('trigger'); + +const cgl=op.patch.cgl; +const vec=vec3.create(); + +render.onTriggered=function() +{ + vec3.set(vec, x.get(),y.get(),z.get()); + cgl.pushViewMatrix(); + mat4.translate(cgl.vMatrix,cgl.vMatrix, vec); + trigger.trigger(); + cgl.popViewMatrix(); +}; + + +}; + +Ops.Gl.Matrix.TranslateView.prototype = new CABLES.Op(); +CABLES.OPS["b15472e2-b895-4dde-95c3-239fa5e08afc"]={f:Ops.Gl.Matrix.TranslateView,objName:"Ops.Gl.Matrix.TranslateView"}; + + + + +// ************************************************************** +// +// Ops.Gl.Texture_v2 +// +// ************************************************************** + +Ops.Gl.Texture_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + filename = op.inUrl("File", [".jpg", ".png", ".webp", ".jpeg", ".avif"]), + tfilter = op.inSwitch("Filter", ["nearest", "linear", "mipmap"]), + wrap = op.inValueSelect("Wrap", ["repeat", "mirrored repeat", "clamp to edge"], "clamp to edge"), + aniso = op.inSwitch("Anisotropic", [0, 1, 2, 4, 8, 16], 0), + flip = op.inValueBool("Flip", false), + unpackAlpha = op.inValueBool("Pre Multiplied Alpha", false), + active = op.inValueBool("Active", true), + inFreeMemory = op.inBool("Save Memory", true), + textureOut = op.outTexture("Texture"), + width = op.outNumber("Width"), + height = op.outNumber("Height"), + ratio = op.outNumber("Aspect Ratio"), + loaded = op.outNumber("Loaded", false), + loading = op.outNumber("Loading", false); + +op.setPortGroup("Size", [width, height]); + +unpackAlpha.setUiAttribs({ "hidePort": true }); + +op.toWorkPortsNeedToBeLinked(textureOut); + +const cgl = op.patch.cgl; + +let loadedFilename = null; +let loadingId = null; +let tex = null; +let cgl_filter = CGL.Texture.FILTER_MIPMAP; +let cgl_wrap = CGL.Texture.WRAP_REPEAT; +let cgl_aniso = 0; +let timedLoader = 0; + +filename.onChange = flip.onChange = function () { reloadSoon(); }; +aniso.onChange = tfilter.onChange = onFilterChange; +wrap.onChange = onWrapChange; +unpackAlpha.onChange = function () { reloadSoon(); }; + +tfilter.set("mipmap"); +wrap.set("repeat"); + +textureOut.set(CGL.Texture.getEmptyTexture(cgl)); + +active.onChange = function () +{ + if (active.get()) + { + if (loadedFilename != filename.get() || !tex) reloadSoon(); + else textureOut.set(tex); + } + else + { + textureOut.set(CGL.Texture.getEmptyTexture(cgl)); + width.set(CGL.Texture.getEmptyTexture(cgl).width); + height.set(CGL.Texture.getEmptyTexture(cgl).height); + if (tex)tex.delete(); + tex = null; + } +}; + +const setTempTexture = function () +{ + const t = CGL.Texture.getTempTexture(cgl); + textureOut.set(t); +}; + +function reloadSoon(nocache) +{ + clearTimeout(timedLoader); + timedLoader = setTimeout(function () + { + realReload(nocache); + }, 30); +} + +function realReload(nocache) +{ + if (!active.get()) return; + // if (filename.get() === null) return; + if (!loadingId)loadingId = cgl.patch.loading.start("textureOp", filename.get()); + + let url = op.patch.getFilePath(String(filename.get())); + + if (nocache)url += "?rnd=" + CABLES.uuid(); + + if (String(filename.get()).indexOf("data:") == 0) url = filename.get(); + + let needsRefresh = false; + if (loadedFilename != filename.get()) needsRefresh = true; + loadedFilename = filename.get(); + + if ((filename.get() && filename.get().length > 1)) + { + loaded.set(false); + loading.set(true); + + const fileToLoad = filename.get(); + + op.setUiAttrib({ "extendTitle": CABLES.basename(url) }); + if (needsRefresh) op.refreshParams(); + + cgl.patch.loading.addAssetLoadingTask(() => + { + op.setUiError("urlerror", null); + + CGL.Texture.load(cgl, url, + function (err, newTex) + { + if (filename.get() != fileToLoad) + { + cgl.patch.loading.finished(loadingId); + loadingId = null; + return; + } + + if (err) + { + setTempTexture(); + op.setUiError("urlerror", "could not load texture: \"" + filename.get() + "\"", 2); + cgl.patch.loading.finished(loadingId); + return; + } + + textureOut.set(newTex); + + width.set(newTex.width); + height.set(newTex.height); + ratio.set(newTex.width / newTex.height); + + // if (!newTex.isPowerOfTwo()) op.setUiError("npot", "Texture dimensions not power of two! - Texture filtering will not work in WebGL 1.", 0); + // else op.setUiError("npot", null); + + if (tex)tex.delete(); + tex = newTex; + textureOut.set(null); + textureOut.set(tex); + + loading.set(false); + loaded.set(true); + + if (inFreeMemory.get()) tex.image = null; + + cgl.patch.loading.finished(loadingId); + }, { + "anisotropic": cgl_aniso, + "wrap": cgl_wrap, + "flip": flip.get(), + "unpackAlpha": unpackAlpha.get(), + "filter": cgl_filter + }); + + // textureOut.set(null); + // textureOut.set(tex); + }); + } + else + { + cgl.patch.loading.finished(loadingId); + setTempTexture(); + } +} + +function onFilterChange() +{ + if (tfilter.get() == "nearest") cgl_filter = CGL.Texture.FILTER_NEAREST; + else if (tfilter.get() == "linear") cgl_filter = CGL.Texture.FILTER_LINEAR; + else if (tfilter.get() == "mipmap") cgl_filter = CGL.Texture.FILTER_MIPMAP; + else if (tfilter.get() == "Anisotropic") cgl_filter = CGL.Texture.FILTER_ANISOTROPIC; + + cgl_aniso = parseFloat(aniso.get()); + + reloadSoon(); +} + +function onWrapChange() +{ + if (wrap.get() == "repeat") cgl_wrap = CGL.Texture.WRAP_REPEAT; + if (wrap.get() == "mirrored repeat") cgl_wrap = CGL.Texture.WRAP_MIRRORED_REPEAT; + if (wrap.get() == "clamp to edge") cgl_wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE; + + reloadSoon(); +} + +op.onFileChanged = function (fn) +{ + if (filename.get() && filename.get().indexOf(fn) > -1) + { + textureOut.set(CGL.Texture.getEmptyTexture(op.patch.cgl)); + textureOut.set(CGL.Texture.getTempTexture(cgl)); + realReload(true); + } +}; + + +}; + +Ops.Gl.Texture_v2.prototype = new CABLES.Op(); +CABLES.OPS["790f3702-9833-464e-8e37-6f0f813f7e16"]={f:Ops.Gl.Texture_v2,objName:"Ops.Gl.Texture_v2"}; + + + + +// ************************************************************** +// +// Ops.Gl.Meshes.Rectangle_v2 +// +// ************************************************************** + +Ops.Gl.Meshes.Rectangle_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + render = op.inTrigger("render"), + trigger = op.outTrigger("trigger"), + width = op.inValue("width", 1), + height = op.inValue("height", 1), + pivotX = op.inSwitch("pivot x", ["left", "center", "right"]), + pivotY = op.inSwitch("pivot y", ["top", "center", "bottom"]), + nColumns = op.inValueInt("num columns", 1), + nRows = op.inValueInt("num rows", 1), + axis = op.inSwitch("axis", ["xy", "xz"], "xy"), + active = op.inValueBool("Active", true), + geomOut = op.outObject("geometry", null, "geometry"); + +geomOut.ignoreValueSerialize = true; + +const cgl = op.patch.cgl; +axis.set("xy"); +pivotX.set("center"); +pivotY.set("center"); + +op.setPortGroup("Pivot", [pivotX, pivotY]); +op.setPortGroup("Size", [width, height]); +op.setPortGroup("Structure", [nColumns, nRows]); +op.toWorkPortsNeedToBeLinked(render); + +const geom = new CGL.Geometry("rectangle"); +let mesh = null; +let needsRebuild = false; + +axis.onChange = + pivotX.onChange = + pivotY.onChange = + width.onChange = + height.onChange = + nRows.onChange = + nColumns.onChange = rebuildLater; +rebuild(); + +function rebuildLater() +{ + needsRebuild = true; +} + +op.preRender = +render.onTriggered = function () +{ + if (!CGL.TextureEffect.checkOpNotInTextureEffect(op)) return; + if (needsRebuild)rebuild(); + if (active.get() && mesh) mesh.render(cgl.getShader()); + trigger.trigger(); +}; + +function rebuild() +{ + let w = width.get(); + let h = parseFloat(height.get()); + let x = 0; + let y = 0; + + if (typeof w == "string")w = parseFloat(w); + if (typeof h == "string")h = parseFloat(h); + + if (pivotX.get() == "center") x = 0; + else if (pivotX.get() == "right") x = -w / 2; + else if (pivotX.get() == "left") x = +w / 2; + + if (pivotY.get() == "center") y = 0; + else if (pivotY.get() == "top") y = -h / 2; + else if (pivotY.get() == "bottom") y = +h / 2; + + const verts = []; + const tc = []; + const norms = []; + const tangents = []; + const biTangents = []; + const indices = []; + + const numRows = Math.round(nRows.get()); + const numColumns = Math.round(nColumns.get()); + + const stepColumn = w / numColumns; + const stepRow = h / numRows; + + let c, r, a; + a = axis.get(); + for (r = 0; r <= numRows; r++) + { + for (c = 0; c <= numColumns; c++) + { + verts.push(c * stepColumn - width.get() / 2 + x); + if (a == "xz") verts.push(0.0); + verts.push(r * stepRow - height.get() / 2 + y); + if (a == "xy") verts.push(0.0); + + tc.push(c / numColumns); + tc.push(1.0 - r / numRows); + + if (a == "xz") + { + norms.push(0, 1, 0); + tangents.push(1, 0, 0); + biTangents.push(0, 0, 1); + } + else if (a == "xy") + { + norms.push(0, 0, 1); + tangents.push(-1, 0, 0); + biTangents.push(0, -1, 0); + } + } + } + + for (c = 0; c < numColumns; c++) + { + for (r = 0; r < numRows; r++) + { + const ind = c + (numColumns + 1) * r; + const v1 = ind; + const v2 = ind + 1; + const v3 = ind + numColumns + 1; + const v4 = ind + 1 + numColumns + 1; + + indices.push(v1); + indices.push(v3); + indices.push(v2); + + indices.push(v2); + indices.push(v3); + indices.push(v4); + } + } + + geom.clear(); + geom.vertices = verts; + geom.texCoords = tc; + geom.verticesIndices = indices; + geom.vertexNormals = norms; + geom.tangents = tangents; + geom.biTangents = biTangents; + + if (numColumns * numRows > 64000)geom.unIndex(); + + if (!mesh) mesh = new CGL.Mesh(cgl, geom); + else mesh.setGeom(geom); + + geomOut.set(null); + geomOut.set(geom); + needsRebuild = false; +} + +op.onDelete = function () +{ + if (mesh)mesh.dispose(); +}; + + +}; + +Ops.Gl.Meshes.Rectangle_v2.prototype = new CABLES.Op(); +CABLES.OPS["fc5718d6-11a5-496e-8f16-1c78b1a2824c"]={f:Ops.Gl.Meshes.Rectangle_v2,objName:"Ops.Gl.Meshes.Rectangle_v2"}; + + + + +// ************************************************************** +// +// Ops.Anim.Timer_v2 +// +// ************************************************************** + +Ops.Anim.Timer_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + inSpeed = op.inValue("Speed", 1), + playPause = op.inValueBool("Play", true), + reset = op.inTriggerButton("Reset"), + inSyncTimeline = op.inValueBool("Sync to timeline", false), + outTime = op.outNumber("Time"); + +op.setPortGroup("Controls", [playPause, reset, inSpeed]); + +const timer = new CABLES.Timer(); +let lastTime = null; +let time = 0; +let syncTimeline = false; + +playPause.onChange = setState; +setState(); + +function setState() +{ + if (playPause.get()) + { + timer.play(); + op.patch.addOnAnimFrame(op); + } + else + { + timer.pause(); + op.patch.removeOnAnimFrame(op); + } +} + +reset.onTriggered = doReset; + +function doReset() +{ + time = 0; + lastTime = null; + timer.setTime(0); + outTime.set(0); +} + +inSyncTimeline.onChange = function () +{ + syncTimeline = inSyncTimeline.get(); + playPause.setUiAttribs({ "greyout": syncTimeline }); + reset.setUiAttribs({ "greyout": syncTimeline }); +}; + +op.onAnimFrame = function (tt) +{ + if (timer.isPlaying()) + { + if (CABLES.overwriteTime !== undefined) + { + outTime.set(CABLES.overwriteTime * inSpeed.get()); + } + else + + if (syncTimeline) + { + outTime.set(tt * inSpeed.get()); + } + else + { + timer.update(); + const timerVal = timer.get(); + + if (lastTime === null) + { + lastTime = timerVal; + return; + } + + const t = Math.abs(timerVal - lastTime); + lastTime = timerVal; + + time += t * inSpeed.get(); + if (time != time)time = 0; + outTime.set(time); + } + } +}; + + +}; + +Ops.Anim.Timer_v2.prototype = new CABLES.Op(); +CABLES.OPS["aac7f721-208f-411a-adb3-79adae2e471a"]={f:Ops.Anim.Timer_v2,objName:"Ops.Anim.Timer_v2"}; + + + + +// ************************************************************** +// +// Ops.Math.MapRange +// +// ************************************************************** + +Ops.Math.MapRange = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v = op.inValueFloat("value", 0), + old_min = op.inValueFloat("old min", 0), + old_max = op.inValueFloat("old max", 1), + new_min = op.inValueFloat("new min", -1), + new_max = op.inValueFloat("new max", 1), + easing = op.inValueSelect("Easing", ["Linear", "Smoothstep", "Smootherstep"], "Linear"), + result = op.outNumber("result", 0); + +op.setPortGroup("Input Range", [old_min, old_max]); +op.setPortGroup("Output Range", [new_min, new_max]); + +let ease = 0; +let r = 0; + +v.onChange = + old_min.onChange = + old_max.onChange = + new_min.onChange = + new_max.onChange = exec; + +exec(); + +easing.onChange = function () +{ + if (easing.get() == "Smoothstep") ease = 1; + else if (easing.get() == "Smootherstep") ease = 2; + else ease = 0; +}; + +function exec() +{ + const nMin = new_min.get(); + const nMax = new_max.get(); + const oMin = old_min.get(); + const oMax = old_max.get(); + let x = v.get(); + + if (x >= Math.max(oMax, oMin)) + { + result.set(nMax); + return; + } + else + if (x <= Math.min(oMax, oMin)) + { + result.set(nMin); + return; + } + + let reverseInput = false; + const oldMin = Math.min(oMin, oMax); + const oldMax = Math.max(oMin, oMax); + if (oldMin != oMin) reverseInput = true; + + let reverseOutput = false; + const newMin = Math.min(nMin, nMax); + const newMax = Math.max(nMin, nMax); + if (newMin != nMin) reverseOutput = true; + + let portion = 0; + + if (reverseInput) portion = (oldMax - x) * (newMax - newMin) / (oldMax - oldMin); + else portion = (x - oldMin) * (newMax - newMin) / (oldMax - oldMin); + + if (reverseOutput) r = newMax - portion; + else r = portion + newMin; + + if (ease === 0) + { + result.set(r); + } + else + if (ease == 1) + { + x = Math.max(0, Math.min(1, (r - nMin) / (nMax - nMin))); + result.set(nMin + x * x * (3 - 2 * x) * (nMax - nMin)); // smoothstep + } + else + if (ease == 2) + { + x = Math.max(0, Math.min(1, (r - nMin) / (nMax - nMin))); + result.set(nMin + x * x * x * (x * (x * 6 - 15) + 10) * (nMax - nMin)); // smootherstep + } +} + + +}; + +Ops.Math.MapRange.prototype = new CABLES.Op(); +CABLES.OPS["2617b407-60a0-4ff6-b4a7-18136cfa7817"]={f:Ops.Math.MapRange,objName:"Ops.Math.MapRange"}; + + + + +// ************************************************************** +// +// Ops.Math.Compare.IfBetweenThen +// +// ************************************************************** + +Ops.Math.Compare.IfBetweenThen = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exe = op.inTrigger("exe"), + number = op.inValue("number", 0), + min = op.inValue("min", 0), + max = op.inValue("max", 1), + triggerThen = op.outTrigger("then"), + triggerElse = op.outTrigger("else"), + outBetween = op.outBoolNum("bs between"); + +exe.onTriggered = function () +{ + if (number.get() >= min.get() && number.get() < max.get()) + { + outBetween.set(true); + triggerThen.trigger(); + } + else + { + outBetween.set(false); + triggerElse.trigger(); + } +}; + + +}; + +Ops.Math.Compare.IfBetweenThen.prototype = new CABLES.Op(); +CABLES.OPS["c80437f0-f0e1-465c-9cea-8a044aa2feaa"]={f:Ops.Math.Compare.IfBetweenThen,objName:"Ops.Math.Compare.IfBetweenThen"}; + + + + +// ************************************************************** +// +// Ops.Gl.ViewPortSize +// +// ************************************************************** + +Ops.Gl.ViewPortSize = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + exec = op.inTrigger("Exec"), + next = op.outTrigger("Next"), + outX = op.outNumber("X"), + outY = op.outNumber("Y"), + outW = op.outNumber("Width"), + outH = op.outNumber("Height"); + +exec.onTriggered = function () +{ + const vp = op.patch.cgl.getViewPort(); + + outX.set(vp[0]); + outY.set(vp[1]); + outW.set(vp[2]); + outH.set(vp[3]); + + next.trigger(); +}; + + +}; + +Ops.Gl.ViewPortSize.prototype = new CABLES.Op(); +CABLES.OPS["7cb99d8f-d7ef-478e-902b-54e054e387a0"]={f:Ops.Gl.ViewPortSize,objName:"Ops.Gl.ViewPortSize"}; + + + + +// ************************************************************** +// +// Ops.Trigger.SetNumberOnTrigger +// +// ************************************************************** + +Ops.Trigger.SetNumberOnTrigger = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + setValuePort = op.inTriggerButton("Set"), + valuePort = op.inValueFloat("Number"), + outNext = op.outTrigger("Next"), + outValuePort = op.outNumber("Out Value"); + +outValuePort.changeAlways = true; + +setValuePort.onTriggered = function () +{ + outValuePort.set(valuePort.get()); + outNext.trigger(); +}; + + +}; + +Ops.Trigger.SetNumberOnTrigger.prototype = new CABLES.Op(); +CABLES.OPS["9989b1c0-1073-4d5f-bfa0-36dd98b66e27"]={f:Ops.Trigger.SetNumberOnTrigger,objName:"Ops.Trigger.SetNumberOnTrigger"}; + + + + +// ************************************************************** +// +// Ops.Color.HexToRGB_v2 +// +// ************************************************************** + +Ops.Color.HexToRGB_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + hex = op.inString("Hex", "#ff0000"), + asBytes = op.inValueBool("Bytes"), + outR = op.outNumber("R"), + outG = op.outNumber("G"), + outB = op.outNumber("B"); + +function hexToR(h) +{ + return parseInt((cutHex(h)).substring(0, 2), 16) || 0; +} +function hexToG(h) +{ + return parseInt((cutHex(h)).substring(2, 4), 16) || 0; +} +function hexToB(h) +{ + return parseInt((cutHex(h)).substring(4, 6), 16) || 0; +} +function cutHex(h) +{ + return (h.charAt(0) == "#") ? h.substring(1, 7) : h; +} + +hex.onChange = parse; +asBytes.onChange = parse; + +function parse() +{ + let str = hex.get() || ""; + let r = hexToR(str); + let g = hexToG(str); + let b = hexToB(str); + + if (!asBytes.get()) + { + r /= 255; + g /= 255; + b /= 255; + } + + outR.set(r); + outB.set(b); + outG.set(g); +} + + +}; + +Ops.Color.HexToRGB_v2.prototype = new CABLES.Op(); +CABLES.OPS["9877f198-8dac-48e5-9310-244ef1a8dec5"]={f:Ops.Color.HexToRGB_v2,objName:"Ops.Color.HexToRGB_v2"}; + + + + +// ************************************************************** +// +// Ops.String.String_v2 +// +// ************************************************************** + +Ops.String.String_v2 = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v=op.inString("value",""), + result=op.outString("String"); + +v.onChange=function() +{ + result.set(v.get()); +}; + + + +}; + +Ops.String.String_v2.prototype = new CABLES.Op(); +CABLES.OPS["d697ff82-74fd-4f31-8f54-295bc64e713d"]={f:Ops.String.String_v2,objName:"Ops.String.String_v2"}; + + + + +// ************************************************************** +// +// Ops.Value.Number +// +// ************************************************************** + +Ops.Value.Number = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + v = op.inValueFloat("value"), + result = op.outNumber("result"); + +v.onChange = exec; + +function exec() +{ + result.set(Number(v.get())); +} + + +}; + +Ops.Value.Number.prototype = new CABLES.Op(); +CABLES.OPS["8fb2bb5d-665a-4d0a-8079-12710ae453be"]={f:Ops.Value.Number,objName:"Ops.Value.Number"}; + + + + +// ************************************************************** +// +// Ops.Ui.PatchInput +// +// ************************************************************** + +Ops.Ui.PatchInput = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const dyn = op.addOutPort(new CABLES.Port(op, "create port", CABLES.OP_PORT_TYPE_DYNAMIC)); + +function getPatchOp() +{ + for (let i in op.patch.ops) + { + if (op.patch.ops[i].patchId) + { + if (op.patch.ops[i].patchId.get() == op.uiAttribs.subPatch) + { + return op.patch.ops[i]; + } + } + } +} + +dyn.onLinkChanged = () => +{ + const mySubPatchOp = getPatchOp(); + + if (!dyn.links.length) return; + + const otherPort = dyn.links[0].getOtherPort(dyn); + dyn.removeLinks(); + + const newPortName = mySubPatchOp.addNewInPort(otherPort); + + const l = gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newPortName); + + mySubPatchOp.saveData(); +}; + + +}; + +Ops.Ui.PatchInput.prototype = new CABLES.Op(); +CABLES.OPS["e3f68bc3-892a-4c78-9974-aca25c27025d"]={f:Ops.Ui.PatchInput,objName:"Ops.Ui.PatchInput"}; + + + + +// ************************************************************** +// +// Ops.Ui.PatchOutput +// +// ************************************************************** + +Ops.Ui.PatchOutput = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const dyn = op.addInPort(new CABLES.Port(op, "create port", CABLES.OP_PORT_TYPE_DYNAMIC)); + +function getPatchOp() +{ + for (let i in op.patch.ops) + { + if (op.patch.ops[i].patchId) + { + if (op.patch.ops[i].patchId.get() == op.uiAttribs.subPatch) + { + return op.patch.ops[i]; + } + } + } +} + +dyn.onLinkChanged = () => +{ + const mySubPatchOp = getPatchOp(); + + if (!dyn.links.length) return; + + const otherPort = dyn.links[0].getOtherPort(dyn); + dyn.removeLinks(); + + const newPortName = mySubPatchOp.addNewOutPort(otherPort); + + const l = gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newPortName); + + mySubPatchOp.saveData(); +}; + + +}; + +Ops.Ui.PatchOutput.prototype = new CABLES.Op(); +CABLES.OPS["851b44cb-5667-4140-9800-5aeb7031f1d7"]={f:Ops.Ui.PatchOutput,objName:"Ops.Ui.PatchOutput"}; + + + + +// ************************************************************** +// +// Ops.Ui.SubPatch +// +// ************************************************************** + +Ops.Ui.SubPatch = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +op.dyn = op.addInPort(new CABLES.Port(op, "create port", CABLES.OP_PORT_TYPE_DYNAMIC)); +op.dynOut = op.addOutPort(new CABLES.Port(op, "create port out", CABLES.OP_PORT_TYPE_DYNAMIC)); + +const dataStr = op.addInPort(new CABLES.Port(op, "dataStr", CABLES.OP_PORT_TYPE_VALUE, { "display": "readonly" })); +op.patchId = op.addInPort(new CABLES.Port(op, "patchId", CABLES.OP_PORT_TYPE_VALUE, { "display": "readonly" })); + +if (CABLES.UI && CABLES.sandbox.isDevEnv()) +{ + const inMakeBp = op.inTriggerButton("Create Blueprint"); + inMakeBp.setUiAttribs({ "hidePort": true }); + + inMakeBp.onTriggered = makeBlueprint; +} + +dataStr.setUiAttribs({ "hideParam": true }); +op.patchId.setUiAttribs({ "hideParam": true }); + +let data = { "ports": [], "portsOut": [] }; +let oldPatchId = CABLES.generateUUID(); +op.patchId.set(oldPatchId); +getSubPatchInputOp(); +getSubPatchOutputOp(); + +let dataLoaded = false; + +op.saveData = saveData; + +op.patchId.onChange = function () +{ + const oldPatchOps = op.patch.getSubPatchOps(oldPatchId); + + if (oldPatchOps.length == 2) + { + for (let i = 0; i < oldPatchOps.length; i++) + { + op.patch.deleteOp(oldPatchOps[i].id); + } + } + else + { + } +}; + +op.onLoaded = function () +{ +}; + +op.onLoadedValueSet = function () +{ + data = JSON.parse(dataStr.get()); + if (!data) + { + data = { "ports": [], "portsOut": [] }; + } + setupPorts(); +}; + +function loadData() +{ +} + +dataStr.onChange = function () +{ + if (dataLoaded) return; + + if (!dataStr.get()) return; + try + { + loadData(); + } + catch (e) + { + op.logError("cannot load subpatch data..."); + op.logError(e); + } +}; + +function saveData() +{ + dataStr.set(JSON.stringify(data)); +} + +op.addPortListener = addPortListener; +function addPortListener(newPort, newPortInPatch) +{ + if (!newPort.hasSubpatchLstener) + { + newPort.hasSubpatchLstener = true; + newPort.addEventListener("onUiAttrChange", function (attribs) + { + if (attribs.title) + { + let i = 0; + for (i = 0; i < data.portsOut.length; i++) + if (data.portsOut[i].name == newPort.name) + data.portsOut[i].title = attribs.title; + + for (i = 0; i < data.ports.length; i++) + if (data.ports[i].name == newPort.name) + data.ports[i].title = attribs.title; + + saveData(); + } + }); + } + + if (newPort.direction == CABLES.PORT_DIR_IN) + { + if (newPort.type == CABLES.OP_PORT_TYPE_FUNCTION) + { + newPort.onTriggered = function () + { + if (newPortInPatch.isLinked()) + newPortInPatch.trigger(); + }; + } + else + { + newPort.onChange = function () + { + newPortInPatch.set(newPort.get()); + if (!newPort.isLinked()) + { + for (let i = 0; i < data.ports.length; i++) + { + if (data.ports[i].name === newPort.name) + { + data.ports[i].value = newPort.get(); + } + } + saveData(); + } + }; + } + } +} + +op.setupPorts = setupPorts; +function setupPorts() +{ + if (!op.patchId.get()) return; + const ports = data.ports || []; + const portsOut = data.portsOut || []; + let i = 0; + + for (i = 0; i < ports.length; i++) + { + if (!op.getPortByName(ports[i].name)) + { + const newPort = op.addInPort(new CABLES.Port(op, ports[i].name, ports[i].type)); + + const patchInputOp = getSubPatchInputOp(); + const newPortInPatch = patchInputOp.addOutPort(new CABLES.Port(patchInputOp, ports[i].name, ports[i].type)); + + newPort.ignoreValueSerialize = true; + newPort.setUiAttribs({ "editableTitle": true }); + if (ports[i].title) + { + newPort.setUiAttribs({ "title": ports[i].title }); + newPortInPatch.setUiAttribs({ "title": ports[i].title }); + } + if (ports[i].objType) + { + newPort.setUiAttribs({ "objType": ports[i].objType }); + newPortInPatch.setUiAttribs({ "objType": ports[i].objType }); + } + if (ports[i].value) + { + newPort.set(ports[i].value); + newPortInPatch.set(ports[i].value); + } + addPortListener(newPort, newPortInPatch); + } + } + + for (i = 0; i < portsOut.length; i++) + { + if (!op.getPortByName(portsOut[i].name)) + { + const newPortOut = op.addOutPort(new CABLES.Port(op, portsOut[i].name, portsOut[i].type)); + const patchOutputOp = getSubPatchOutputOp(); + const newPortOutPatch = patchOutputOp.addInPort(new CABLES.Port(patchOutputOp, portsOut[i].name, portsOut[i].type)); + + newPortOut.ignoreValueSerialize = true; + newPortOut.setUiAttribs({ "editableTitle": true }); + + if (portsOut[i].title) + { + newPortOut.setUiAttribs({ "title": portsOut[i].title }); + newPortOutPatch.setUiAttribs({ "title": portsOut[i].title }); + } + if (portsOut[i].objType) + { + newPortOut.setUiAttribs({ "objType": portsOut[i].objType }); + newPortOutPatch.setUiAttribs({ "objType": portsOut[i].objType }); + } + + // addPortListener(newPortOut,newPortOutPatch); + addPortListener(newPortOutPatch, newPortOut); + } + } + + dataLoaded = true; +} + +op.addNewInPort = function (otherPort, type, objType) +{ + const newName = "in" + data.ports.length + " " + otherPort.parent.name + " " + otherPort.name; + + const o = { "name": newName, "type": otherPort.type }; + if (otherPort.uiAttribs.objType)o.objType = otherPort.uiAttribs.objType; + + data.ports.push(o); + setupPorts(); + return newName; +}; + +op.dyn.onLinkChanged = function () +{ + if (op.dyn.isLinked()) + { + const otherPort = op.dyn.links[0].getOtherPort(op.dyn); + op.dyn.removeLinks(); + otherPort.removeLinkTo(op.dyn); + + op.log("dyn link changed!!!"); + + // const newName = "in" + data.ports.length + " " + otherPort.parent.name + " " + otherPort.name; + + // const o = { "name": newName, "type": otherPort.type }; + // if (otherPort.uiAttribs.objType)o.objType = otherPort.uiAttribs.objType; + // data.ports.push(o); + + // setupPorts(); + + const newName = op.addNewInPort(otherPort); + + const l = gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newName + ); + + dataLoaded = true; + saveData(); + } + else + { + setTimeout(function () + { + op.dyn.removeLinks(); + }, 100); + } +}; + +op.addNewOutPort = function (otherPort, type, objType) +{ + const newName = "out" + data.portsOut.length + " " + otherPort.parent.name + " " + otherPort.name; + + const o = { "name": newName, "type": otherPort.type }; + if (otherPort.uiAttribs.objType)o.objType = otherPort.uiAttribs.objType; + + data.portsOut.push(o); + setupPorts(); + return newName; +}; + +op.dynOut.onLinkChanged = function () +{ + if (op.dynOut.isLinked()) + { + const otherPort = op.dynOut.links[0].getOtherPort(op.dynOut); + op.dynOut.removeLinks(); + otherPort.removeLinkTo(op.dynOut); + + const newName = op.addNewOutPort(otherPort); + + gui.scene().link( + otherPort.parent, + otherPort.getName(), + op, + newName + ); + + dataLoaded = true; + saveData(); + } + else + { + setTimeout(function () + { + op.dynOut.removeLinks(); + }, 100); + + op.log("dynOut unlinked..."); + } +}; + +function getSubPatchOutputOp() +{ + let patchOutputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchOutput"); + + if (!patchOutputOP) + { + op.patch.addOp("Ops.Ui.PatchOutput", { "subPatch": op.patchId.get(), "translate": { "x": 0, "y": 0 } }); + patchOutputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchOutput"); + if (!patchOutputOP) op.warn("no patchoutput!"); + } + return patchOutputOP; +} + +function getSubPatchInputOp() +{ + let patchInputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchInput"); + + if (!patchInputOP) + { + op.patch.addOp("Ops.Ui.PatchInput", { "subPatch": op.patchId.get(), "translate": { "x": 0, "y": 0 } }); + patchInputOP = op.patch.getSubPatchOp(op.patchId.get(), "Ops.Ui.PatchInput"); + if (!patchInputOP) op.warn("no patchinput2!"); + } + + return patchInputOP; +} + +op.addSubLink = function (p, p2) +{ + const num = data.ports.length; + const sublPortname = "in" + (num - 1) + " " + p2.parent.name + " " + p2.name; + + if (p.direction == CABLES.PORT_DIR_IN) + { + gui.scene().link( + p.parent, + p.getName(), + getSubPatchInputOp(), + sublPortname + ); + } + else + { + const numOut = data.portsOut.length; + gui.scene().link( + p.parent, + p.getName(), + getSubPatchOutputOp(), + "out" + (numOut - 1) + " " + p2.parent.name + " " + p2.name + ); + } + + const bounds = gui.patchView.getSubPatchBounds(op.patchId.get()); + + getSubPatchInputOp().uiAttr( + { + "translate": + { + "x": bounds.minx, + "y": bounds.miny - 100 + } + }); + + getSubPatchOutputOp().uiAttr( + { + "translate": + { + "x": bounds.minx, + "y": bounds.maxy + 100 + } + }); + saveData(); + return sublPortname; +}; + +op.onDelete = function () +{ + for (let i = op.patch.ops.length - 1; i >= 0; i--) + { + if (op.patch.ops[i] && op.patch.ops[i].uiAttribs && op.patch.ops[i].uiAttribs.subPatch == op.patchId.get()) + { + op.patch.deleteOp(op.patch.ops[i].id); + } + } +}; + +function makeBlueprint() +{ + const bpOp = op.patch.addOp(CABLES.UI.DEFAULTOPNAMES.blueprint); + + bpOp.getPortByName("externalPatchId").set(gui.patchId); + bpOp.getPortByName("subPatchId").set(op.patchId.get()); + bpOp.getPortByName("active").set(true); + + bpOp.uiAttr( + { + "translate": + { + "x": op.uiAttribs.translate.x - 150, + "y": op.uiAttribs.translate.y + } + }); +} + +op.rebuildListeners = () => +{ + op.log("rebuild listeners..."); + + const outop = getSubPatchOutputOp(); + for (let i = 0; i < outop.portsIn.length; i++) + { + if (outop.portsIn[i].isLinked()) + { + addPortListener(outop.portsIn[i], this.portsOut[i]); + } + } +}; + + +}; + +Ops.Ui.SubPatch.prototype = new CABLES.Op(); +CABLES.OPS["84d9a6f0-ed7a-466d-b386-225ed9e89c60"]={f:Ops.Ui.SubPatch,objName:"Ops.Ui.SubPatch"}; + + + + +// ************************************************************** +// +// Ops.Math.Sum +// +// ************************************************************** + +Ops.Math.Sum = function() +{ +CABLES.Op.apply(this,arguments); +const op=this; +const attachments={}; +const + number1 = op.inValueFloat("number1", 1), + number2 = op.inValueFloat("number2", 1), + result = op.outNumber("result"); + +op.setTitle("+"); + +number1.onChange = +number2.onChange = exec; +exec(); + +function exec() +{ + const v = number1.get() + number2.get(); + if (!isNaN(v)) + result.set(v); +} + + +}; + +Ops.Math.Sum.prototype = new CABLES.Op(); +CABLES.OPS["c8fb181e-0b03-4b41-9e55-06b6267bc634"]={f:Ops.Math.Sum,objName:"Ops.Math.Sum"}; + + diff --git a/survey_dashboard/hmc_layout/static/en_files/script.min.js b/survey_dashboard/hmc_layout/static/en_files/script.min.js new file mode 100644 index 0000000..4f7984f --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/script.min.js @@ -0,0 +1,3 @@ +function getHandleBarHtml(e,t){return cables.templates[e]?cables.templates[e](t):(console.log("template not found: "+e),"")}function uuid(){let e=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const n=(e+16*Math.random())%16|0;return e=Math.floor(e/16),("x"===t?n:3&n|8).toString(16)})}function getQueryVariable(e){const t=window.location.search.substring(1).split("&");for(let n=0;n{if(e){const n=e.querySelector(".row.pagination");n&&n.remove(),e.innerHTML+=getHandleBarHtml("pagination",t)}});const i=e+" .pagination .tab-bar ";document.querySelectorAll(i+".prev").forEach(e=>{if(e){const t=()=>{const e=Math.max(0,o-n);s(n,e)};e.removeEventListener("pointerdown",t),e.addEventListener("pointerdown",t)}});document.querySelectorAll(i+".next").forEach(e=>{if(e){const i=()=>{const e=Math.min(t.count,o+n);s(n,e)};e.removeEventListener("pointerdown",i),e.addEventListener("pointerdown",i)}});document.querySelectorAll(i+".more").forEach(e=>{if(e){const t=()=>{s(0,0)};e.removeEventListener("pointerdown",t),e.addEventListener("pointerdown",t)}});document.querySelectorAll(i+"li.page").forEach((e,t)=>{e.addEventListener("pointerdown",o=>{let i=t;e.dataset.pageIndex&&(i=Number(e.dataset.pageIndex)-1);s(n,i*n)})})}(CABLES=CABLES||{}).EventTarget=function(){this._eventCallbacks={},this.addEventListener=function(e,t){this._eventCallbacks[e]?this._eventCallbacks[e].push(t):this._eventCallbacks[e]=[t]},this.hasEventListener=this.on=function(e,t){if(e&&t){if(this._eventCallbacks[e]){return-1!=this._eventCallbacks[e].indexOf(t)}}else console.log("hasListener: missing parameters")},this.removeEventListener=function(e,t){if(this._eventCallbacks[e]){const n=this._eventCallbacks[e].indexOf(t);-1==n?console.log("eventlistener "+e+" not found..."):this._eventCallbacks[e].splice(n)}},this.emitEvent=function(e,t,n,o,s,i,a){if(this._eventCallbacks[e])for(let r=0;r
    '+(e.title||"")+'
    '+t+"

    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





    Cables Logo


    ",e.html&&(n+='
    ',n+='
    ',n+=e.html,n+="
    ",n+="
    ")}const o=ele.byId("results");return o&&(o.innerHTML+=n),t}function getDetailTitle(e){return"
    "+e+"

    "}function getDetail(e,t){return e+": "+t+"
    "}(CABLES=CABLES||{}).getGPU=function(e){const t={};let n=e;n=n.replace("Mesa DRI ",""),n=n.replace("Mesa ",""),n=n.replace("NVIDIA Corporation, ",""),n=n.replace("Intel Open Source Technology Center, ",""),n=n.replace("(R)",""),n=n.replace("(TM)",""),n=n.replace("Direct3D11 vs_5_0 ps_5_0)",""),n=n.replace("Direct3D11 vs_4_1 ps_4_1)",""),n=n.replace("OpenGL Engine",""),n=n.replace("/PCIe/SSE2",""),n=n.replace("X.Org, ",""),n=n.replace("ATI Radeon","Radeon"),n=n.replace("with Max-Q Design",""),n=n.replace("Series",""),n=n.replace(" "," ");let o=n.split(/[,(/)]/),s=null;for(let e=0;e-1&&(i="Nvidia"),e.toLowerCase().indexOf("geforce")>-1&&(i="Nvidia"),e.toLowerCase().indexOf("radeon")>-1&&(i="AMD"),e.toLowerCase().indexOf("amd ")>-1&&(i="AMD"),e.toLowerCase().indexOf("intel")>-1&&(i="Intel"),e.toLowerCase().indexOf("swiftshader")>-1&&(i="Google"),e.toLowerCase().indexOf("adreno")>-1&&(i="Qualcom"),e.toLowerCase().indexOf("mali-")>-1&&(i="ARM"),e.toLowerCase().indexOf("apple")>-1&&(i="Apple"),t.vendor=i,e.toLowerCase().indexOf("swiftshader")>-1&&(t.software=!0),t},CABLES.Browser={};const tests=[function(){logResult.browser=platform.name,logResult.browser_version=platform.version;showResult({hide:!0,title:"browser",info:platform.name+" "+platform.version,html:""})},function(){let e="";platform.os&&(platform.os.family&&(e+=platform.os.family+" ",logResult.os_family=platform.os.family),platform.os.version&&(e+=platform.os.version+" ",logResult.os_version=platform.os.version),platform.os.architecture&&(e+=platform.os.architecture+"bit ",logResult.os_architecture=platform.os.architecture),platform.product&&(e+="("+platform.product+")",logResult.product=platform.product)),logResult.language=navigator.language||navigator.userLanguage,showResult({hide:!0,title:"OS",info:e})},function(){let e,t,n;try{e=document.createElement("canvas");let o=0;if(t=e.getContext("webgl2",e),t?o=2:(t=e.getContext("webgl",e)||e.getContext("experimental-webgl",e),o=1),t){let s="";logResult.webgl_versionNum=o,n=t.getSupportedExtensions();const i=t.getParameter(t.VERSION);logResult.webgl_version=i;const a=t.getParameter(t.SHADING_LANGUAGE_VERSION);logResult.webgl_glsl_version=a;const r=t.getExtension("WEBGL_debug_renderer_info");if(r){const e=t.getParameter(r.UNMASKED_RENDERER_WEBGL);logResult.webgl_renderer=e,logResult.webgl_gpu_info=CABLES.getGPU(e),console.log(logResult.webgl_gpu_info);const n=t.getParameter(r.UNMASKED_VENDOR_WEBGL);logResult.webgl_vendor=n}e=document.createElement("canvas");const l=e.getContext("webgl",{failIfMajorPerformanceCaveat:!0})||e.getContext("experimental-webgl",{failIfMajorPerformanceCaveat:!0});logResult.webgl_majorPerformanceCaveat=!l,logResult.webgl_params={},void 0!==t.MAX_SAMPLES&&(logResult.webgl_params.MAX_SAMPLES=t.getParameter(t.MAX_SAMPLES)),logResult.webgl_params.MAX_FRAGMENT_UNIFORM_VECTORS=t.getParameter(t.MAX_FRAGMENT_UNIFORM_VECTORS),logResult.webgl_params.MAX_VERTEX_UNIFORM_VECTORS=t.getParameter(t.MAX_VERTEX_UNIFORM_VECTORS),logResult.webgl_params.MAX_TEXTURE_SIZE=t.getParameter(t.MAX_TEXTURE_SIZE),logResult.webgl_params.DEPTH_BITS=t.getParameter(t.DEPTH_BITS),logResult.webgl_params.MAX_VARYING_VECTORS=t.getParameter(t.MAX_VARYING_VECTORS),logResult.webgl_params.MAX_VERTEX_ATTRIBS=t.getParameter(t.MAX_VERTEX_ATTRIBS),logResult.webgl_params.MAX_COMBINED_TEXTURE_IMAGE_UNITS=t.getParameter(t.MAX_COMBINED_TEXTURE_IMAGE_UNITS),logResult.webgl_params.MAX_VERTEX_TEXTURE_IMAGE_UNITS=t.getParameter(t.MAX_VERTEX_TEXTURE_IMAGE_UNITS),logResult.webgl_params.MAX_RENDERBUFFER_SIZE=t.getParameter(t.MAX_RENDERBUFFER_SIZE);const c=t.getExtension("WEBGL_draw_buffers");if(c?logResult.webgl_params.MAX_DRAW_BUFFERS=t.getParameter(c.MAX_DRAW_BUFFERS_WEBGL):void 0!==t.MAX_DRAW_BUFFERS&&(logResult.webgl_params.MAX_DRAW_BUFFERS=t.getParameter(t.MAX_DRAW_BUFFERS)),n&&0!==n.length){logResult.webgl_extensions=n;for(let e=0;e"}else;showResult({success:!0,title:"WebGL",info:"WebGL "+o,html:s})}else logResult.webgl_versionNum=0,showResult({success:!1,title:"webgl"})}catch(e){return}e=void 0},async function(){let e=null!=navigator.gpu;if(e||showResult({title:"WebGPU",success:!1}),logResult.api_webGpu=e,navigator.gpu){let t=null;try{t=await navigator.gpu.requestAdapter()}catch(e){console.log("webgpu ex",e),t=null}e=!!t,showResult({title:"WebGPU",success:e}),logResult.api_webGpu=e}},function(){let e=null!=window.VideoEncoder;showResult({title:"WebGPU",success:e}),logResult.api_webCodecs=e},function(){logResult.display_pixel_ratio=window.devicePixelRatio,window.devicePixelRatio&&(logResult.display_pixel_ratio_float=window.devicePixelRatio.toPrecision(2)),logResult.window_size_width=window.innerWidth,logResult.window_size_height=window.innerHeight,logResult.window_size_pixel_width=window.innerWidth*window.devicePixelRatio,logResult.window_size_pixel_height=window.innerHeight*window.devicePixelRatio,logResult.screen_size_width=window.screen.availWidth,logResult.screen_size_height=window.screen.availHeight;let e="";e+=getDetail("Device Pixel Ratio",window.devicePixelRatio),e+=getDetail("Screen Size",window.screen.availWidth+" x "+window.screen.availHeight),e+=getDetail("Window Size",window.innerWidth+" x "+window.innerHeight),e+=getDetail("Window Size Pixels",window.innerWidth*window.devicePixelRatio+" x "+window.innerHeight*window.devicePixelRatio),showResult({title:"Screen",info:window.innerWidth+" x "+window.innerHeight,html:e})},function(){const e=!!navigator.getGamepads;logResult.api_gamepad=e,showResult({success:e,title:"Gamepad API"});const t=navigator.getGamepads?navigator.getGamepads():navigator.webkitGetGamepads?navigator.webkitGetGamepads:[];for(let e=0;e3)return"oops";o=o||"";for(const e in n)if("parent"!=e)if(n[e]&&"number"!=typeof n[e]&&"boolean"!=typeof n[e]&&"string"!=typeof n[e])if(Array.isArray(n[e])){let t="";try{t=JSON.stringify(n[e])}catch(n){}o+=""+e+""+t+""}else{if(!n[e].constructor)return;t="["+typeof n[e]+"] ";const s=n[e].constructor;s.name&&"Function"!=s.name&&(t+=s.name),o+=""+e+""+t+""}else o+=""+e+'',CABLES.lastBrowserEvent[n.type]&&CABLES.lastBrowserEvent[n.type][e]==n[e]||(o+=""),o+=n[e],CABLES.lastBrowserEvent[n.type]&&CABLES.lastBrowserEvent[n.type][e]==n[e]||(o+=""),o+="";return CABLES.lastBrowserEvent[n.type]=n,o}function logBrowserEvent(e){e.preventDefault();CABLES.browserEventHistory.unshift(e.type),CABLES.browserEventHistory.length>50&&(CABLES.browserEventHistory.length=50);let t="";for(let e=0;e";document.getElementById("history").innerHTML=t;let n="";return n+="

    "+e.type+"


    ",n+="",n+=logEventObject(e,e,"",0),n+="
    ",document.getElementById("output").innerHTML=n,!1}function initBrowserEvents(){document.getElementById("results").innerHTML="",window.addEventListener("load",logBrowserEvent),window.addEventListener("gamepadconnected",logBrowserEvent),window.addEventListener("DOMContentLoaded",logBrowserEvent),window.addEventListener("gamepadconnected",logBrowserEvent),window.addEventListener("gamepaddisconnected",logBrowserEvent),document.addEventListener("keydown",logBrowserEvent),document.addEventListener("keyup",logBrowserEvent),document.addEventListener("scroll",logBrowserEvent),document.getElementById("input").addEventListener("focus",logBrowserEvent),document.getElementById("input").addEventListener("blur",logBrowserEvent),document.getElementById("input").addEventListener("contextmenu",logBrowserEvent),document.getElementById("input").addEventListener("mouseenter",logBrowserEvent),document.getElementById("input").addEventListener("mouseleave",logBrowserEvent),document.getElementById("input").addEventListener("mousemove",logBrowserEvent),document.getElementById("input").addEventListener("pointerenter",logBrowserEvent),document.getElementById("input").addEventListener("pointerleave",logBrowserEvent),document.getElementById("input").addEventListener("pointermove",logBrowserEvent),document.getElementById("input").addEventListener("mousedown",logBrowserEvent),document.getElementById("input").addEventListener("mouseup",logBrowserEvent),document.getElementById("input").addEventListener("pointerdown",logBrowserEvent),document.getElementById("input").addEventListener("pointerup",logBrowserEvent),document.getElementById("input").addEventListener("click",logBrowserEvent),document.getElementById("input").addEventListener("dblclick",logBrowserEvent),document.getElementById("input").addEventListener("wheel",logBrowserEvent),document.getElementById("input").addEventListener("touchmove",logBrowserEvent),document.getElementById("input").addEventListener("touchstart",logBrowserEvent),document.getElementById("input").addEventListener("touchend",logBrowserEvent)}function saveDoc(e){CABLES.api.post("doc/ops/edit/"+e,{content:editor.getValue()},(function(e){console.log("res",e),e.success?document.getElementById("save_status").innerHTML="  saved...":document.getElementById("save_status").innerHTML="  not saved :/",setTimeout((function(){document.getElementById("save_status").innerHTML=""}),500)}),(function(e){console.log("err res",e)}))}CABLES.doBrowserTest=async function(e){for(const e in tests)await tests[e]();CABLES.CLIENT.COOKIES.createCookie("browserinfo",!0,100),CABLES.api.post("browser",{browser:logResult},t=>{console.log(t);const n=document.getElementById("resultIframe");n&&(n.src="/browser/r/"+t.id);const o=document.getElementsByTagName("head");if(o&&o.length>0){const e=document.createElement("base");e.setAttribute("target","_parent"),o[0].appendChild(e)}const s=document.getElementsByTagName("footer");s&&s.length>0&&s[0].remove(),e&&e(t)})},CABLES.doBrowserSpeedTest=function(e){function t(e){let t="abcdefghijklmnopqrstuvwxyz".split(""),n=t.length,o=[];for(;e--;)o.push(t[Math.random()*n|0]);return o.join("")}function n(e,t,n,o){const s=n.dir,i=document.getElementById("speedtestResult").querySelector(".results");if(n.result.clientDiff){i.querySelector("."+e+" .client .time").innerHTML=parseInt(n.result.clientDiff)+"ms";const o=i.querySelector("."+e+" .client .speed");o.innerHTML=parseInt(Number(n.result.clientDiff-n.result.diff))+"ms";const s=8*t/1024/1024/(n.result.clientDiff/1e3);o.innerHTML=s.toFixed(2)+" mbit/s"}if(n.result.hasOwnProperty("processingTime")){i.querySelector("."+e+" .servertime").innerHTML=parseInt(n.result.processingTime)+"ms"}if(s){if(o){const e=ele.byQuery("."+s+" .loading");e&&ele.hide(e)}ele.byQueryAll("."+s+" .result").forEach(e=>{ele.show(e)})}}function o(e){CABLES.stopSavingAnimButton();const t=document.getElementById("speedtestResult").querySelector(".error");if(t){t.classList.remove("hidden");const n=t.querySelector(".message");n&&(n.innerHTML="error during speedtest:"+e)}}CABLES.startSavingAnimButton(e);const s={};ele.byQueryAll(".results .result").forEach(e=>{ele.hide(e)});ele.byQueryAll(".results .loading").forEach(e=>{ele.show(e)});document.getElementById("speedtestResult").querySelector(".results").classList.remove("hidden");const i=performance.now(),a=Date.now();fetch("/test_files/onemb.txt?cb="+a).then(e=>e.text()).then(()=>{let e=performance.now(),r={};r.clientStart=i,r.clientEnd=e,r.clientDiff=e-i,s.fetchDirectSmall={dir:"downdirect",size:"small",result:r},n("downdirectsmall",1048576,s.fetchDirectSmall);const l=performance.now();CABLES.api.get("browser/download/small?cb="+a,e=>{const i=performance.now();r={},e.data&&(r=e.data),e.data.data&&delete e.data.data,r.clientStart=l,r.clientEnd=i,r.clientDiff=i-l,s.fetchSmall={dir:"down",size:"small",result:r},n("downsmall",1048576,s.fetchSmall);const c=performance.now();fetch("/test_files/tenmb.txt?cb="+a).then(e=>e.text()).then(()=>{const e=performance.now();r={},r.clientStart=c,r.clientEnd=e,r.clientDiff=e-c,s.fetchDirectBig={dir:"downdirect",size:"big",result:r},n("downdirectbig",10485760,s.fetchDirectBig,!0);const i=performance.now();CABLES.api.get("browser/download/big?cb="+a,e=>{const a=performance.now();r={},e.data&&(r=e.data),e.data.data&&delete e.data.data,r.clientStart=i,r.clientEnd=a,r.clientDiff=a-i,s.fetchBig={dir:"down",size:"big",result:r},n("downbig",10485760,s.fetchBig,!0);let l={data:t(1048576)};const c=performance.now();CABLES.api.post("browser/upload/small",l,e=>{const i=performance.now();r={},e.data&&(r=e.data),e.data.data&&delete e.data.data,r.clientStart=c,r.clientEnd=i,r.clientDiff=i-c,s.upSmall={dir:"up",size:"small",result:r},n("upsmall",1048576,s.upSmall),l={data:t(10485760)};const a=performance.now();CABLES.api.post("browser/upload/big",l,e=>{const t=performance.now();r={},e.data&&(r=e.data),e.data.data&&delete e.data.data,r.clientStart=a,r.clientEnd=t,r.clientDiff=t-a,s.upBig={dir:"up",size:"big",result:r},n("upbig",10485760,s.upBig,!0),CABLES.stopSavingAnimButton(),CABLES.api.post("browser/speedtest",s)},o)},o)},o)},o)},o)},o)},CABLES.Browser.browserCounter=0,(CABLES=CABLES||{}).lastBrowserEvent={},CABLES.browserEventHistory=[],(CABLES=CABLES||{}).startSavingAnimButton=e=>{if(!e.classList.contains("buttonSavingAnim")){CABLES.buttonSavingEle=e,CABLES.buttonSavingText=e.innerText,e.innerText="Working...";const t=document.createElement("span");t.classList.add("icon-loader"),t.classList.add("icon"),t.classList.add("icon-0_75x"),t.style["margin-left"]="10px",e.classList.add("buttonSavingAnim"),e.appendChild(t),CABLES.buttonSavingStart=performance.now()}},CABLES.stopSavingAnimButton=(e=!0)=>{if(performance.now()-CABLES.buttonSavingStart<200)return void setTimeout(()=>{CABLES.stopSavingAnimButton()},50);const t=document.querySelectorAll(".buttonSavingAnim .icon-loader"),n=document.querySelectorAll(".buttonSavingAnim");if(CABLES.buttonSavingEle&&(e&&(CABLES.buttonSavingEle.innerText="Done !"),setTimeout(()=>{CABLES.buttonSavingEle.innerText=CABLES.buttonSavingText},400)),n)for(let e=0;e":function(e,t){return e>t},"<=":function(e,t){return e<=t},">=":function(e,t){return e>=t},typeof:function(e,t){return typeof e==t}},!s[t])throw new Error("Handlerbars Helper 'compare' doesn't know the operator "+t);return i=s[t](e,n),!0===i?o.fn(this):o.inverse(this)})),Handlebars.registerHelper("paginationLoop",(function(e){let t=Number(this.currentPage)||0,n=t+19;n>this.pages&&(n=this.pages);let o=t-10;o<1&&(o=1);t+19>this.pages&&(o=this.pages-19),o<1&&(o=1);let s="",i=0;for(let t=o;t<=n&&!(i>19);t++){let a=t===n;19===i&&(a=!0),s+=e.fn({page:t,last:a,first:t==o}),i++}return s}))),(CABLES=CABLES||{}).CLIENT=CABLES.CLIENT||{},CABLES.CLIENT.COOKIES=CABLES.CLIENT.COOKIES||{},CABLES.CLIENT.COOKIES.KEYS=CABLES.CLIENT.COOKIES.KEYS={},CABLES.CLIENT.COOKIES.KEYS.ACCEPTED="accepted",CABLES.CLIENT.COOKIES.createCookie=function(e,t,n){let o="";if(n){const e=new Date;e.setTime(e.getTime()+24*n*60*60*1e3),o="; expires="+e.toUTCString()}document.cookie=e+"="+t+o+"; path=/; secure=true; SameSite=lax"},CABLES.CLIENT.COOKIES.readCookie=function(e){const t=e+"=",n=document.cookie.split(";");for(let e=0;e0?t[0]:null}byClassAll(e){e&&"."==e[0]&&console.warn("ele.byClassAll should not contain .");const t=document.getElementsByClassName(e);return t||[]}forEachClass(e,t){e&&"."==e[0]&&console.warn("ele.forEachClass should not contain .");const n=document.getElementsByClassName(e);for(let e=0;e';let o=e._id;e.shortId&&(o=e.shortId),ele.byId("open-project-button").setAttribute("href","/edit/"+o)}function getEmptyExample(){return{_id:null,name:"",tags:[],description:""}}function replaceDescription(e){e||(e=""),ele.byId("example-description").innerHTML=marked(e)}function replaceTags(e){const t=ele.byQuery(".tag-list-wrapper");if(t){if(t.innerHTML="",!e||0===e.length)return;e.forEach((function(e){const n=document.createElement("a");n.setAttribute("href","/tag/"+e),n.classList.add("tag"),n.innerHTML=e,t.appendChild(n)}))}}function updateState(e,t){if(e){if(window.hasOwnProperty("history")){const n={exampleName:e,exampleId:t},o="/examples/"+encodeURIComponent(e.trim());window.history.pushState(n,null,o),CABLES.EXAMPLES.state=n}}else console.error("no example name")}function handleExampleLinkClick(e){e.preventDefault(),e.stopPropagation();const t=e.target.dataset.id;return t!==CABLES.EXAMPLES.state.exampleId&&loadExampleAndShow(t,!0),!1}function loadExampleAndShow(e,t){e?CABLES.api.get("project/"+e,(function(e){showExample(e,t)})):showExample(getEmptyExample(),t)}function dataUriToFormData(e,t){let n;n=e.split(",")[0].indexOf("base64")>=0?atob(e.split(",")[1]):unescape(e.split(",")[1]);const o=e.split(",")[0].split(":")[1].split(";")[0],s=new Uint8Array(n.length);for(let e=0;e-1&&document.addEventListener("DOMContentLoaded",()=>{const e=ele.byId("example-page");CABLES.EXAMPLES.state={exampleName:e.dataset.exampleName,exampleId:e.dataset.exampleId},e.dataset.exampleName?CABLES.api.get("project/"+CABLES.EXAMPLES.state.exampleId,(function(e){showExample(e,!1)})):(CABLES.API.getExamples({selector:"#example-thumbs"}),window.history.pushState(CABLES.EXAMPLES.state,null,window.location.href),window.addEventListener("popstate",handlePopState)),ele.byQueryAll(".example-link").forEach(e=>{e.addEventListener("pointerdown",handleExampleLinkClick)}),ele.byQueryAll(".example-link-unsorted").forEach(e=>{e.addEventListener("pointerdown",handleExampleLinkClick)})}),CABLES.exportDialog=class{constructor(e){this._id=e,this._currentTab="",this.setTab("download")}setTab(e){this._currentTab&&(ele.hide(ele.byId("tab_"+this._currentTab)),ele.byId("tabitem_"+this._currentTab).classList.remove("active")),ele.show(ele.byId("tab_"+e)),ele.byId("tabitem_"+e).classList.add("active"),this._currentTab=e,window.location.hash="t="+e;let t="Export ZIP File";"netlify"===e&&(t="Deploy to Netlify"),"github"===e&&(t="Deploy to Github"),"exe"===e&&(t="Build Executable"),ele.byId("exportbutton").value=t,ele.byId("deploymentbutton").value="View Deployment",ele.byId("overviewbutton").innerHTML="Deployments overview","cmdline"===e||"iframe"===e?(ele.hide(ele.byId("exportoptions")),ele.hide(ele.byId("exportbutton"))):(ele.show(ele.byId("exportbutton")),ele.show(ele.byId("exportoptions"))),ele.byQueryAll(".exportOptionPanel").forEach(e=>{ele.hide(e)}),ele.show(ele.byId("export_options_"+this._currentTab))}_exportFinished(e,t){ele.hide(ele.byId("export_loading")),e&&(alert("export error"),console.log(e)),ele.byId("export_log").innerHTML=t.log||t.message,t.urls&&(t.urls.downloadUrl?(ele.show(ele.byId("downloadbutton")),ele.byId("downloadbutton").setAttribute("href",t.urls.downloadUrl)):ele.hide(ele.byId("downloadbutton")),t.urls.deploymentUrl?(ele.show(ele.byId("deploymentbutton")),ele.byId("deploymentbutton").setAttribute("href",t.urls.deploymentUrl)):ele.hide(ele.byId("deploymentbutton")),t.urls.overviewUrl?(ele.show(ele.byId("overviewbutton")),ele.byId("overviewbutton").setAttribute("href",t.urls.overviewUrl)):ele.hide(ele.byId("overviewbutton"))),ele.hide(ele.byId("export_dialog")),ele.show(ele.byId("export_result"))}export(){ele.hide(ele.byId("export_dialog")),ele.show(ele.byId("export_loading"));let e="project/"+this._id+"/export?a=1";e+="&type="+this._currentTab,e+="&assets="+ele.byId("export_settings_assets").value,"none"===ele.byId("export_settings_assets").value&&(e+="&ignoreAssets=true"),"single"===ele.byId("export_settings_combine").value&&(e+="&combineJS=true"),ele.byId("export_settings_skip_backups").checked&&(e+="&skipBackups=true"),ele.byId("export_settings_flat").checked&&(e+="&flat=true");document.querySelectorAll("#export_options_"+this._currentTab+" .exportOption").forEach(t=>{e+="&"+t.dataset.name+"="+encodeURIComponent(t.value)}),CABLES.api.get(e,e=>{this._exportFinished(null,e)},e=>{this._exportFinished(e,e)})}},CABLES.invitations=CABLES.invitations||{},CABLES.invitations.acceptPatchInvitation=function(e,t=null,n=!1){CABLES.api.post("patchinvite/"+e+"/accepted",{readOnly:n},(function(){t?window.location.href=t:window.location.reload()}),(function(){t?window.location.href=t:window.location.reload()}))},CABLES.invitations.declinePatchInvitation=function(e,t=null){CABLES.api.post("patchinvite/"+e+"/declined",{},(function(){t?window.location.href=t:window.location.reload()}),(function(){t?window.location.href=t:window.location.reload()}))},CABLES.invitations.leaveProject=function(e,t=null){confirm("really remove yourself as collaborator?")&&CABLES.api.patch("project/leave/"+e,{},(function(){t?window.location.href=t:window.location.reload()}),(function(e){alert("ERROR: "+e.msg)}))},CABLES.invitations.acceptTeamInvitation=function(e,t=null,n=!1){CABLES.api.post("teaminvites/"+e+"/accepted",{readOnly:n},(function(){t?window.location.href=t:window.location.reload()}),(function(){t?window.location.href=t:window.location.reload()}))},CABLES.invitations.declineTeamInvitation=function(e,t=null){CABLES.api.post("teaminvites/"+e+"/declined",{},(function(){t?window.location.href=t:window.location.reload()}),(function(){t?window.location.href=t:window.location.reload()}))},CABLES.API.requestPatchAccess=function(e){CABLES.api.post("patchinvite/request/"+e,{},e=>{ele.hide(ele.byId("request-access-button")),ele.show(ele.byId("request-pending-button"))})},CABLES.API.requestTeamAccess=function(e,t=!1){const n={};t&&(n.permission=t),CABLES.api.post("teaminvites/request/"+e,n,e=>{ele.hide(ele.byId("request-access-button")),ele.show(ele.byId("request-pending-button"))})},(CABLES=CABLES||{}).lazyLoad=function(){CABLES.revalidateLazyLoad(),setTimeout((function(){CABLES.revalidateLazyLoad()}),100),setTimeout((function(){CABLES.revalidateLazyLoad()}),500)},CABLES.revalidateLazyLoad=function(){CABLES.blazy.revalidate();document.querySelectorAll(".ribbon--example").forEach(e=>{e.dataset.hasOwnProperty("exampleFor")&&e.dataset.exampleFor&&e.addEventListener("click",()=>{window.location.href="/op/"+e.dataset.exampleFor})});document.querySelectorAll(".ribbon--featured").forEach(e=>{e.addEventListener("click",()=>{window.location.href="/patches?s=&w=featured"})})},CABLES.ajaxLogin=function(){const e={username:ele.byId("login_username").value,password:ele.byId("login_password").value};CABLES.api.post("loginajax",e,(function(e){if(e.success){const e=ele.byId("successRedir");e&&(e.value=document.location.pathname),ele.byId("loginform").submit()}else{let t="Login failed";e.error&&(t+=": "+e.error);const n=ele.byQuery(".errors_login");n.style.display="block",n.innerHTML="",n.innerHTML=t}}),(function(e){const t=ele.byQuery(".errors_login");t.style.display="block",t.innerHTML="",t.innerHTML="something went wrong... no success"}))},CABLES.signup=function(){const e={username:ele.byId("username").value,password:ele.byId("password").value,email:ele.byId("email").value,newsletter:document.getElementById("newslettercheckbox").checked},t=ele.byId("secret");if(t&&(e.secred=t.value),!document.getElementById("privacycheckbox").checked)return ele.byQuery(".errors_signup").style.display="block",void(ele.byQuery(".errors_signup").innerHTML="Please read and accept the privacy policy");ele.byQuery(".errors_signup").style.display="none",ele.byQuery(".errors_signup").innerHTML="",CABLES.api.post("user/signup",e,(function(e){"SUCCESS"==e.status&&e.goto?ele.byQuery(".tab_content_signup").innerHTML='
    Thank you for signing up!

    Please check your inbox to confirm your email.
    After that you can login
    ':(ele.byQuery(".errors_signup").style.display="block",ele.byQuery(".errors_signup").innerHTML="",ele.byQuery(".errors_signup").innerHTML="something went wrong... no success "+e.errors)}),(function(e){if(ele.byQuery(".errors_signup").style.display="block",ele.byQuery(".errors_signup").innerHTML="",e.errors&&e.errors.length>0){if(e.errors.includes("MAINTENANCE_MODE")||(ele.byQuery(".errors_signup").innerHTML="Please check your data:

    "),e.errors)for(const t in e.errors)"EMAIL_INVALID"===e.errors[t]?ele.byQuery(".errors_signup").innerHTML+="- This is not a valid email
    ":"PASSWORD_TOO_SHORT"===e.errors[t]?ele.byQuery(".errors_signup").innerHTML+="- Please use a longer password
    ":"USERNAME_TOO_SHORT"===e.errors[t]?ele.byQuery(".errors_signup").innerHTML+="- Please use a longer username
    ":"EMAIL_EXISTS"===e.errors[t]?ele.byQuery(".errors_signup").innerHTML+="- This email is already registered
    ":"USER_EXISTS"===e.errors[t]?ele.byQuery(".errors_signup").innerHTML+="- Username already exists
    ":"INVALID CHARACTERS"===e.errors[t]?ele.byQuery(".errors_signup").innerHTML+="- Please do not use any special characters or spaces in your username
    ":"ERROR_USERNAME_EQUALS_EMAIL"===e.errors[t]?ele.byQuery(".errors_signup").innerHTML+="- Your username cannot be your email-address
    ":"MAINTENANCE_MODE"===e.errors[t]?ele.byQuery(".errors_signup").innerHTML+="cables is currently in maintenance mode,
    please try again later":ele.byQuery(".errors_signup").innerHTML+="- "+e.errors[t]+"
    "}else ele.byQuery(".errors_signup").innerHTML="something went wrong..."}))},CABLES.signupNewsletter=function(){const e={username:ele.byId("username").value,password:ele.byId("password").value,email:ele.byId("email").value},t=ele.byId("secret");t&&(e.secred=t.value),CABLES.api.post("user/signupNewsletter",e,(function(e){"SUCCESS"==e.status&&e.goto?(ele.byQuery(".errors_signup").style.display="none",ele.byQuery(".tab_content_signup").innerHTML="Thank you for signing up! "):(ele.byQuery(".errors_signup").style.display="block",ele.byQuery(".errors_signup").innerHTML="",ele.byQuery(".errors_signup").innerHTML="something went wrong... no success")}),(function(e){if(ele.byQuery(".errors_signup").style.display="block",ele.byQuery(".errors_signup").innerHTML="",e.errors&&e.errors.length>0){ele.byQuery(".errors_signup").innerHTML="please check your data:

    ";for(const t in e.errors)ele.byQuery(".errors_signup").innerHTML+="- "+e.errors[t]+"
    "}else ele.byQuery(".errors_signup").innerHTML+="something went wrong..."}))},CABLES.setResetPassword=function(e,t){const n={password:ele.byId("password").value,hash:ele.byId("hash").value};CABLES.api.post("user/setResetPassword",n,(function(e){e.success?(ele.byQuery(".errors").style.display="block",ele.byQuery(".errors").innerHTML="",ele.byQuery(".tab_content_signup").innerHTML='new password set!

    login now'):(ele.byQuery(".errors").style.display="block",ele.byQuery(".errors").innerHTML="",ele.byQuery(".errors").innerHTML="something went wrong... no success")}),(function(e){if(ele.byQuery(".errors").style.display="block",ele.byQuery(".errors").innerHTML="",e.errors&&e.errors.length>0){e.errors.includes("USER_LOGGED_IN")||(ele.byQuery(".errors").innerHTML="please check your data:

    ");for(const t in e.errors)"USER_LOGGED_IN"===e.errors[t]?ele.byQuery(".errors").innerHTML+="you are currently logged in, log out to reset your password":ele.byQuery(".errors").innerHTML+="- "+e.errors[t]+"
    "}else ele.byQuery(".errors").innerHTML+="something went wrong..."}))},CABLES.resetPassword=function(){const e={email:ele.byId("email").value};CABLES.api.post("user/resetPassword",e,(function(e){e.success?(ele.byQuery(".errors").style.display="block",ele.byQuery(".errors").innerHTML="",ele.byQuery(".errors").innerHTML="password reset email sent!",ele.byId("resetpasswdbutton").style.display="none",ele.byId("email").style.display="block"):(ele.byQuery(".errors").style.display="block",ele.byQuery(".errors").innerHTML="",ele.byQuery(".errors").innerHTML="something went wrong... no success")}),(function(e){if(ele.byQuery(".errors").style.display="block",ele.byQuery(".errors").innerHTML="",e.errors&&e.errors.length>0){e.errors.includes("USER_LOGGED_IN")||(ele.byQuery(".errors").innerHTML="please check your data:

    ");for(const t in e.errors)"USER_LOGGED_IN"===e.errors[t]?ele.byQuery(".errors").innerHTML+="you are currently logged in, log out to reset your password":ele.byQuery(".errors").innerHTML+="- "+e.errors[t]+"
    "}else ele.byQuery(".errors").innerHTML+="something went wrong..."}))},CABLES.resendConfirmEmail=function(){const e={email:ele.byId("email").value};CABLES.api.post("send_confirm/",e,(function(e){e.success?(ele.byId("result").innerHTML="password reset email sent!",ele.byId("form").style.display="none"):(ele.byId("result").innerHTML="something went wrong... no success",ele.byId("form").style.display="none")}),(function(e){ele.byId("result").innerHTML="something went wrong... no success",ele.byId("form").style.display="none"}))},CABLES.showInfoBar=function(e,t){const n=document.getElementById("nav-infobar");n&&(n.innerHTML=e,n.classList.remove("warning"),n.classList.remove("info"),n.classList.remove("neutral"),n.classList.add(t),n.style.display="block")},CABLES.hideInfoBar=function(){const e=document.getElementById("nav-infobar");e&&(e.innerHTML="",e.classList.remove("warning"),e.classList.remove("info"),e.classList.remove("neutral"),e.style.display="none")},(CABLES=CABLES||{}).SandboxEditor=function(e){const t=document.getElementById("editorIframe");CABLES.talker=new CABLES.TalkerAPI(t.contentWindow),CABLES.patchId=e.config.patchId,CABLES.patchVersion=e.config.patchVersion,CABLES.talker.addEventListener("requestPatchData",(function(t,n){n&&n(e.config)})),CABLES.talker.addEventListener("sendBrowserInfo",(function(e,t){t&&t(platform)})),CABLES.talker.addEventListener("addMeUserlist",(function(t,n){console.log(e),CABLES.api.put("project/"+CABLES.patchId+"/user/"+e.config.user.id,(function(e){n(null,e)}),(function(e){n(e,e)}))})),CABLES.talker.addEventListener("checkProjectUpdated",(function(e,t){CABLES.api.get("project/"+e.projectId+"/updated",(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("patchCreateBackup",(function(e,t){CABLES.api.put("projectversion/createbackup/"+CABLES.patchId,{title:e.title},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("reload",(function(e,t){document.location.reload()})),CABLES.talker.addEventListener("savePatch",(function(e,t){e.buildInfo&&(e.buildInfo.host=location.hostname);let n="project/"+e.id;e.fromBackup&&(n+="?fromBackup=true"),CABLES.api.put(n,{name:e.name,namespace:e.namespace,data:e.data,dataB64:e.dataB64,buildInfo:e.buildInfo},(function(e){t(null,e)}),(function(e){t(e,e)}))})),CABLES.talker.addEventListener("getPatch",(function(t,n){let o="project/"+CABLES.patchId;e.config.patchVersion&&(o+="/version/"+e.config.patchVersion),CABLES.api.get(o,(function(e){n(null,e)}),(function(e){n(e,e)}))})),CABLES.talker.addEventListener("gotoPatch",(function(e){document.location.href="/edit/"+e.id})),CABLES.talker.addEventListener("newPatch",(function(e,t){CABLES.api.post("project",{name:e.name},(function(e){t(null,e)}),(function(e){t(e,e)}))})),CABLES.talker.addEventListener("saveProjectAs",(function(e,t){CABLES.api.post("project/copy",{name:e.name,originalId:e.originalId,copyCollaborators:e.copyCollaborators},(function(e){t(null,e)}),(function(e){t(e,e)}))})),CABLES.talker.addEventListener("saveScreenshot",(function(e,t){CABLES.api.put("project/"+e.id+"/screenshot",{screenshot:e.screenshot},(function(e){t(null,e)}),(function(e){t(e,e)}))})),CABLES.talker.addEventListener("getBuildInfo",(e,t)=>{CABLES.api.get("buildinfo/?nc="+Date.now(),(function(e){t(null,e.data)}),(function(e){t(e,e)}))}),CABLES.talker.addEventListener("getFilelist",(e,t)=>{let n="library/";"patch"==e.source&&(n="project/"+CABLES.patchId+"/files/"),CABLES.api.get(n,(function(e){t(null,e)}),(function(e){t(e,e)}))}),CABLES.talker.addEventListener("fileConvert",(e,t)=>{CABLES.api.post("project/"+CABLES.patchId+"/file/convert/"+e.fileId+"/"+e.converterId,{options:e.options},(function(e){console.log(e),e&&e.filename&&CABLES.talker.send("fileUpdated",{filename:e.filename}),t(null,e)}),(function(e){t(e,e)}))}),CABLES.talker.addEventListener("updatePatchName",(e,t)=>{document.title=e.name}),CABLES.talker.addEventListener("getFileDetails",(e,t)=>{CABLES.api.get("project/"+CABLES.patchId+"/file/info/"+e.fileid,(function(e){t(null,e)}),(function(e){t(e,e)}))}),CABLES.talker.addEventListener("getLibraryFileInfo",(e,t)=>{CABLES.api.get("files/library/info/"+e.fileCategory+"/"+e.filename,e=>t(null,e),e=>t(e,e))}),CABLES.talker.addEventListener("deleteFile",(e,t)=>{CABLES.api.delete("project/"+CABLES.patchId+"/file/"+e.fileid,{},(function(e){t(null,e)}),(function(e){t(e,e)}))}),CABLES.talker.addEventListener("checkNumAssetPatches",(e,t)=>{const n=e.filenames||[];CABLES.api.post("assets/uses/count",{filenames:n},(function(e){t(null,e)}),(function(e){t(e,e)}))}),CABLES.talker.addEventListener("createFile",(e,t)=>{CABLES.api.put("project/"+CABLES.patchId+"/"+e.name,{content:"this is an empty file..."},(function(e){t(null,e)}),(function(e){t(e,e)}))}),CABLES.talker.addEventListener("fileUploadStr",(e,t)=>{const n=dataUriToFormData(e.fileStr,e.filename);CABLES.uploadFormadata(n,e.filename)}),CABLES.talker.addEventListener("getAllProjectOps",(function(e,t){CABLES.api.get("ops/project/"+e.projectId+"/?nc="+Date.now(),(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("getAllOps",(function(e,t){CABLES.api.get("ops/?nc="+Date.now(),(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("getOpDocsAll",(function(e,t){CABLES.api.get("doc/ops/all?nc="+String(Date.now()).substr(-6),(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("getOpDocs",(function(e,t){let n="doc/ops/"+e.name+"?nc="+String(Date.now()).substr(-6);e.id&&"undefined"!==e.id&&(n+="&id="+e.id),CABLES.api.get(n,(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("getExtensionOpDocs",(function(e,t){CABLES.api.get("doc/ops/extension/"+e.name+"?nc="+Date.now(),(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("getTeamNamespaceOpDocs",(function(e,t){CABLES.api.get("doc/ops/team/namespace/"+e.name+"?nc="+Date.now(),(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("opCreate",(function(e,t){CABLES.api.post("ops/create/"+e.opname,{},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("saveOpCode",(function(e,t){CABLES.api.put("ops/"+e.opname,{code:e.code},(function(e){t(null,e)}),(function(e){t(e,e)}))})),CABLES.talker.addEventListener("getOpCode",(function(e,t){let n="ops/"+e.opname;e.projectId&&(n+="?projectId="+e.projectId),CABLES.api.get(n,(function(e){t(null,e)}),(function(e){t(e,e)}))})),CABLES.talker.addEventListener("getBlueprintOps",(function(e,t){CABLES.api.get("blueprints/"+e.blueprintId+"/"+e.patchId+"/"+e.subPatchId+"/"+e.opId+"/"+e.blueprintSubpatchId+"?nocache="+Date.now(),(function(e){t(null,e)}),(function(e){t(e,e)}))})),CABLES.talker.addEventListener("formatOpCode",(function(e,t){CABLES.api.post("ops/code/format",{code:e.code},(function(e){t(null,e)}),(function(e){t(e,e)}))})),CABLES.talker.addEventListener("opSaveLayout",(function(e,t){CABLES.api.post("op/layout/"+e.opname,{layout:e.layout},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("opAddLib",(function(e,t){CABLES.api.put("op/"+e.opname+"/libs",{libName:e.name},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("opRemoveLib",(function(e,t){CABLES.api.delete("op/"+e.opname+"/libs",{libName:e.name},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("opAddCoreLib",(function(e,t){CABLES.api.put("op/"+e.opname+"/corelibs/"+e.name,{},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("opRemoveCoreLib",(function(e,t){CABLES.api.delete("op/"+e.opname+"/corelibs/"+e.name,{},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("opClone",(function(e,t){CABLES.api.get("ops/clone/"+e.opname+"/"+e.name,(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("opAttachmentAdd",(function(e,t){CABLES.api.post("op/"+e.opname+"/attachments/"+e.name,{},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("opAttachmentGet",(function(e,t){CABLES.api.get("op/"+e.opname+"/attachment/"+e.name+"?nc="+(Date.now()+"").substr(-6),(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("opAttachmentDelete",(function(e,t){CABLES.api.delete("op/"+e.opname+"/attachments/"+e.name,{},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("opAttachmentSave",(function(e,t){CABLES.api.post("op/"+e.opname+"/attachment/"+e.name,{content:e.content},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("saveUserSettings",(function(e,t){CABLES.api.put("usersettings/",{settings:e.settings},(function(e){t(null,e)}),(function(e,n){t(e,n)}))})),CABLES.talker.addEventListener("toggleMultiplayerSession",(function(e,t){let n=window.location.href;const o=n.indexOf("mp=false")>-1;o?n=n.replaceAll("?mp=false","").replaceAll("&mp=false",""):n.indexOf("?")>-1?n+="&mp="+o:n+="?mp="+o,window.location.href=n}))},CABLES.uploadFormadata=function(e,t){const n="/api/project/"+CABLES.patchId+"/file";CABLES.talker.send("jobStart",{id:"upload"+t,title:"Uploading "+t});const o=new XMLHttpRequest;o.open("POST",n),o.upload.onprogress=function(e){if(e.lengthComputable){const n=e.loaded/e.total*100|0;CABLES.talker.send("jobProgress",{id:"upload"+t,progress:n}),100==n&&(CABLES.talker.send("notify",{msg:"File Uploaded"}),CABLES.talker.send("jobFinish",{id:"upload"+t}),setTimeout((function(){CABLES.talker.send("refreshFileManager"),CABLES.talker.send("fileUpdated",{filename:t})}),500))}},o.onload=function(e,t){let n="",s="";try{s=JSON.parse(e.target.response)}catch(e){console.log(e)}502!==o.status?(200===o.status?CABLES.talker.send("refreshFileManager"):(s.msg&&(n=s.msg),CABLES.talker.send("notifyError",{msg:"Upload error: "+(s.msg||"")}),CABLES.talker.send("refreshFileManager"),console.error("upload error",n)),s.hasOwnProperty("success")&&!s.success&&(CABLES.talker.send("notifyError",{msg:"Upload error: "+(s.msg||"")}),console.error("upload error",s.msg))):console.warn("ajax 502 error ! possibly upload ?")},o.send(e)},(CABLES=CABLES||{}).embedPatch=function(e,t=null){let n=e.sandboxUrl+"/sandboxviewer/"+e.projectId,o=e.secret;if(!o){const e=window.location.search.match(new RegExp("[?&]s=([^&]+)"));e&&e.length>0&&(o=o[1])}o?(n+="?s="+o,e.embedOverlay&&(n+="&embed=true")):e.embedOverlay&&(n+="?embed=true"),e.locationHash&&(n+=e.locationHash),document.getElementById(e.elementId).innerHTML='';const s=document.getElementById("canvasiframe"),i=new CABLES.TalkerAPI(s.contentWindow);i.addEventListener("sendPatch",(n,s)=>{let a="project/"+e.projectId+"?nocache="+Date.now();if(e.secret||o){a+="&s="+(e.secret||o)}CABLES.api.get(a,e=>{i.send("patch",{patch:e})},n=>{console.warn("api error on url:","project/"+e.projectId+"?nocache="+Date.now()),console.log(n),"function"==typeof t&&t(n)})}),i.addEventListener("sendBlueprint",(n,s)=>{let a="blueprints"+n.url+"?nocache="+Date.now();if(e.secret||o){const t=e.secret||o;a+="&s="+t}CABLES.api.get(a,(function(e){i.send("blueprint",{blueprint:e})}),(function(e){console.warn("api error on url:",a),console.log(e),"function"==typeof t&&t(e)}))})},CABLES.CLIENT=CABLES.CLIENT||{},CABLES.CLIENT.userInfos={},CABLES.CLIENT.loadFavsAndUpdateDom=e=>{CABLES.api.get("favs/"+e,(function(e){const t={};if(t.likes=e,t.likeColumns=Math.max(Math.ceil(e.length/10),1),e){if(e.length>0){const e=getHandleBarHtml("likelist",t);ele.byQueryAll(".fav-hover-view").forEach(t=>{t.innerHTML=e})}ele.byQueryAll(".num-favs").forEach(t=>{t.innerText=e.length})}}))},CABLES.CLIENT.disableIframeScrolling=function(){let e={insideIframe:!1};document.getElementById("canvasiframe").addEventListener("mouseenter",(function(){e.insideIframe=!0,e.scrollX=window.scrollX,e.scrollY=window.scrollY})),document.getElementById("canvasiframe").addEventListener("mouseleave",(function(){e.insideIframe=!1})),document.addEventListener("wheel",(function(t){if(e.insideIframe)return t.preventDefault(),!1}),!1),document.addEventListener("scroll",(function(t){if(e.insideIframe)return t.preventDefault(),window.scrollTo(0,0),!1}))},CABLES.API=CABLES.API||{},CABLES.API.saveProjectDescription=function(e){CABLES.api.post("project/"+e+"/save_description",{content:editor.getValue()},(function(e){e.success?ele.byId("save_status").innerHTML="  saved...":ele.byId("save_status").innerHTML="  not saved :/",setTimeout((function(){ele.byId("save_status").innerHTML=""}),500)}),(function(e){console.log("err res",e)}))},CABLES.API.updateFav=function(e,t){e?ele.byQueryAll(".fav-icon").forEach(e=>{e.classList.remove("icon-heart"),e.classList.add("icon-heart-fill")}):ele.byQueryAll(".fav-icon").forEach(e=>{e.classList.add("icon-heart"),e.classList.remove("icon-heart-fill")}),CABLES.CLIENT.loadFavsAndUpdateDom(t)},CABLES.API.follow=function(e){CABLES.api.post("follow/"+e,(function(e){}),(function(e){e.followstate?ele.byId("follow-button").innerHTML="Following":ele.byId("follow-button").innerHTML="Follow"}))},CABLES.API.toggleFav=function(e){CABLES.api.post("fav/toggle/"+e,(function(e){}),(function(t){CABLES.API.updateFav(t.favstate,e)}))},CABLES.API.getUserLikes=function(e,t,n=0){t=Number(t),n=Number(n);let o="user/favs/"+e;(t||0===t)&&(o+="?l="+t),n&&(o+="&o="+n),CABLES.api.get(o,(function(e){let o="";for(const t in e.projects)o+=getHandleBarHtml("project",e.projects[t]);ele.byId("projects").innerHTML=o,e.pagination&&initPagination("#projects",e.pagination,t,n,(e,t)=>{let n="";(e||0===e)&&(n+="?l="+e),t&&(n+="&o="+t),window.location=location.origin+location.pathname+n})}))},CABLES.API.getUserFeatured=function(e,t,n=0){t=Number(t),n=Number(n);let o="user/featured/"+e;(t||0===t)&&(o+="?l="+t),n&&(o+="&o="+n),CABLES.api.get(o,(function(e){let o="";for(const t in e.projects)o+=getHandleBarHtml("project",e.projects[t]);ele.byId("projects").innerHTML=o,e.pagination&&initPagination("#results_container",e.pagination,t,n,(e,t)=>{let n="";(e||0===e)&&(n+="?l="+e),t&&(n+="&o="+t),window.location=location.origin+location.pathname+n})}))},CABLES.API.getUserProjects=function(e,t,n,o=0){let s="user/"+e+"/projects";t&&(s="admin/user/"+e+"/projects"),n=Number(n),o=Number(o),(n||0===n)&&(s+="?l="+n),o&&(s+="&o="+o),CABLES.api.get(s,(function(e){let t="";for(const n in e.projects){const o=e.projects[n];o.publishedReadable=o.updatedReadable,t+=getHandleBarHtml("project",e.projects[n])}document.getElementById("projects").innerHTML=t,e.pagination&&initPagination("#results_container",e.pagination,n,o,(e,t)=>{let n="";(e||0===e)&&(n+="?l="+e),t&&(n+="&o="+t),window.location=location.origin+location.pathname+n})}))},CABLES.API.getTeamProjects=function(e,t){let n="teams/"+e+"/projects";CABLES.api.get(n,(function(n){let o="",s=!0;for(const t in n.data){s=!1;const i=n.data[t];i.teamId=e,o+=getHandleBarHtml("myproject",i)}s&&(o='

    This team has no patches yet


    Go to the collaboration-settings of any patch and assign this team to it.
    '),document.getElementById(t).innerHTML=o}))},CABLES.API.getTeamNamespaceOps=function(e,t){if(!e)return;let n="teams/namespace/Ops.Team."+e+"/ops",o=!0,s="";CABLES.api.get(n,e=>{for(const t in e.data.ops)o=!1,e.data.ops[t]&&(s+=getHandleBarHtml("projectOp",e.data.ops[t]));s+="
    ",o&&(s='

    This namespace has no ops yet


    Move a userop to the namespace to share them with the team.
    '),ele.byQuery(t).innerHTML=s},e=>{s='

    Failed to load ops for namespace


    '+e.msg+"
    ",ele.byQuery(t).innerHTML=s})},CABLES.API.getTeamExtensionOps=function(e,t){if(!e)return;let n="extension/Ops.Extension."+e+"/ops",o=!0,s="";CABLES.api.get(n,e=>{for(const t in e.data.ops)o=!1,e.data.ops[t]&&(s+=getHandleBarHtml("projectOp",e.data.ops[t]));s+="
    ",o&&(s='

    This namespace has no ops yet


    Move a userop/teamop to the namespace to share them with the community.
    '),ele.byQuery(t).innerHTML=s},e=>{s='

    Failed to load ops for namespace


    '+e.msg+"
    ",ele.byQuery(t).innerHTML=s})},CABLES.API.addTeamAsCollaborators=function(e,t,n){let o="teams/"+t+"/collaborators/add/"+e;CABLES.api.post(o,e=>{console.error("error",e)},e=>{if("function"==typeof n){const t=[];e.data.forEach(e=>{t.push(e.username)}),n(t)}})},CABLES.API.getTags=function(){CABLES.api.get("projects/tags",(function(e){let t="",n=0,o=0;for(o in e)n=Math.max(n,e[o]);for(o in e)t+=getHandleBarHtml("tag",{name:o,size:12+e[o]/n*8});ele.byId("tags").innerHTML=t}))},CABLES.API.getFeaturedProjectsList=function(e){e&&e.selector&&CABLES.api.get("projects/featured",(function(t){let n=getHandleBarHtml("project_list_headline",{title:"Featured Patches"});n+='
    ';for(const e in t)n+=getHandleBarHtml("project",t[e]);n+="
    \x3c!-- end row --\x3e",ele.byQuery(e.selector).innerHTML=n}))},CABLES.API.getExamples=function(e){e&&e.selector&&CABLES.api.get("examples",(function(t){let n="";for(const e in t)n+=getHandleBarHtml("example",t[e]);ele.byQuery(e.selector).innerHTML=n}))},CABLES.API.getMyFavs=function(e){CABLES.api.get("myfavs",(function(e){let t="";for(const n in e)t+=getHandleBarHtml("project",e[n]);ele.byId("myfavs").innerHTML=t}))},CABLES.API.getMyPatches=function(e,t,n=0,o=0){ele.byQueryAll(".tab-bar li").forEach(e=>{e.classList.remove("active")}),ele.byQueryAll(".tab-bar li[data-tab-name='tab_"+e+"']").forEach(e=>{e.classList.add("active")});const s=ele.byId("namespaces"),i=s.value;CABLES.API.MYPATCHES.lastFilter=e,CABLES.API.MYPATCHES.lastOrder=t,ele.byId("my-projects").innerHTML='


    ';let a="mypatches?filter="+e+"&ns="+i+"&sort="+t;n=Number(n),o=Number(o),(n||0===n)&&(a+="&l="+n),o&&(a+="&o="+o),CABLES.api.get(a,(function(a){const r=CABLES.getTagsFromProjects(a),l=ele.byId("tags");l&&(l.innerHTML=getHandleBarHtml("tags",{tags:r})),s.length=0;let c=document.createElement("option");c.value="all",c.innerText="all namespaces","all"===i&&c.setAttribute("selected","selected"),s.appendChild(c),a.namespaces.forEach(e=>{c=document.createElement("option"),c.value=e,c.innerText=e,i===e&&c.setAttribute("selected","selected"),s.appendChild(c)});let d="";d+='
    ';for(const e in a.patches){const n=a.patches[e];"created"===t&&(n.publishedReadable=n.createdReadable),d+=getHandleBarHtml("myproject",a.patches[e])}d+="
    \x3c!-- end row --\x3e",ele.byId("my-projects").innerHTML=d,a.pagination&&initPagination("#my-projects",a.pagination,n,o,(n,o)=>{CABLES.API.MYPATCHES.setUrl(e,t,n,o)})}))},CABLES.lastPatchTemplates=0,CABLES.API.getTemplateListNav=function(){0===CABLES.lastPatchTemplates&&(CABLES.lastPatchTemplates=Date.now(),CABLES.api.get("templates",(function(e){let t='

    Empty Project

    ';for(const n in e)e[n].linkedId=e[n].shortId?e[n].shortId:e[n]._id,t+=getHandleBarHtml("templates_nav",e[n]);ele.byId("template-list-dropdown").innerHTML=t})))},CABLES.lastRecentProjects="recentpatches",CABLES.API.getMyProjectListNav=function(e){if("recentpatches"===e&&CABLES.lastRecentProjects===e)return void(document.location.href="/mypatches");if("recentinvitedpatches"===e&&CABLES.lastRecentProjects===e)return void(document.location.href="/mypatches?w=sharedme");if("teams"===e&&CABLES.lastRecentProjects===e)return void(document.location.href="/teams");const t=document.getElementById("my-recent-projects-dropdown");if(!t)return;t.innerHTML='
    ',document.getElementById("mypatches_"+CABLES.lastRecentProjects).classList.remove("myPatchesFilterActive"),document.getElementById("mypatches_"+CABLES.lastRecentProjects).classList.add("myPatchesFilterInActive"),e=e||"recentpatches",document.getElementById("mypatches_"+e).classList.add("myPatchesFilterActive"),document.getElementById("mypatches_"+e).classList.remove("myPatchesFilterInActive"),CABLES.lastRecentProjects=e;let n=e;"teams"===e&&(n="teams/mine"),CABLES.api.get(n,(function(n){let o="";if("teams"===e){0===n.data.length&&(o+="
    No teams yet!

    "),n.data.length>4&&(n.data.length=4);for(const e in n.data)o+=getHandleBarHtml("team_thumb",n.data[e]);o+='
    Manage your teams
    '}else{n.msg&&(n=n.msg),0===n.length&&(o="None"),n.length>4&&(n.length=4);for(const e in n)o+=getHandleBarHtml("myproject_nav",n[e]);o+="recentinvitedpatches"===e?'
    See all invites
    ':'
    All my patches
    '}t.innerHTML=o}))},CABLES.API.getMyLatestProjectList=function(){CABLES.api.get("recentpatches",(function(e){let t="";e.length=4;for(const n in e)t+=getHandleBarHtml("project",e[n]);ele.byId("mylatestprojects").innerHTML=t}))},CABLES.API.getProjectList=function(){CABLES.api.get("projects",(function(e){let t="";for(const n in e)t+=getHandleBarHtml("project",e[n]);ele.byId("projectlist").innerHTML=t}))},CABLES.API.comment=function(e){if(""===ele.byQuery(".comment_content textarea").value)return CABLES.stopSavingAnimButton(this);CABLES.api.post("comment/"+e,{content:ele.byQuery(".comment_content textarea").value},(function(t){CABLES.API.getProjectComments(e,!0)}))},CABLES.API.getProjectComments=function(e,t){CABLES.api.get("comments/"+e,(function(n){let o="";n.reverse();for(const e in n)o+=getHandleBarHtml("comment",n[e]);t&&(o+=getHandleBarHtml("comments_head",{numComments:n.length,projectId:e})),ele.byId("project_comments").innerHTML=o,CABLES.stopSavingAnimButton(this)}))},CABLES.getTagsFromProjects=function(e){const t={},n=[];for(const n in e)if(e[n].tags)for(let o=0;oe.num-t.num),n},CABLES.API.getTagProjects=function(e){CABLES.api.get("tag/"+e+"/projects",(function(e){let t="";for(const n in e.projects)t+=getHandleBarHtml("project",e.projects[n]);const n=CABLES.getTagsFromProjects(e.projects);ele.byId("tags").innerHTML=getHandleBarHtml("tags",{tags:n}),ele.byId("tag_projects").innerHTML=t,e.projects&&0!==e.projects.length||(ele.byId("tag_projects").innerHTML="No Projects found.")}))},CABLES.API.getOpPatches=function(e,t,n,o,s=0,i=0){const a=document.querySelector("#loading");a&&a.classList.remove("hide");let r="op/"+e+"/patches";s=Number(s),i=Number(i),(s||0===s)&&(r+="?l="+s),i&&(r+="&o="+i),CABLES.api.get(r,(function(e){projectPagePatchList(e.projects,t,o,e.pagination),e.pagination&&initPagination("#"+n,e.pagination,s,i,(e,t)=>{CABLES.API.OPPATCHES.setUrl("public",e,t)}),a&&a.classList.add("hide")}))};const projectPagePatchList=(e,t,n,o={})=>{let s="";for(const t in e)s+=getHandleBarHtml("project",e[t]);const i=document.getElementById(t);i&&(i.innerHTML=s,e&&0!==e.length||(i.innerHTML='
    '+n+"
    "))};CABLES.API.getOpMyPatches=function(e,t,n,o,s=0,i=0){const a=document.querySelector("#loading");a&&a.classList.remove("hide");let r="op/"+e+"/mypatches";s=Number(s),i=Number(i),(s||0===s)&&(r+="?l="+s),i&&(r+="&o="+i),CABLES.api.get(r,(function(e){projectPagePatchList(e.projects,t,o,e.pagination),e.pagination&&initPagination("#"+n,e.pagination,s,i,(e,t)=>{CABLES.API.OPPATCHES.setUrl("mine",e,t)}),a&&a.classList.add("hide")}))},CABLES.API.getOpPrivatePatches=function(e,t,n,o,s=0,i=0){const a=document.querySelector("#loading");a&&a.classList.remove("hide");let r="op/"+e+"/privatepatches";s=Number(s),i=Number(i),(s||0===s)&&(r+="?l="+s),i&&(r+="&o="+i),CABLES.api.get(r,e=>{if(projectPagePatchList(e.projects,t,o,e.pagination),e.pagination){const t=document.getElementById("num_private");t&&(t.innerHTML="("+e.pagination.count+")"),initPagination("#"+n,e.pagination,s,i,(e,t)=>{CABLES.API.OPPATCHES.setUrl("private",e,t)})}a&&a.classList.add("hide")})},CABLES.API.getCountOpPatches=function(e,t="(",n=")"){CABLES.api.get("op/"+e+"/count",e=>{if(e&&e.data)for(let o in e.data){const s=document.getElementById("num_"+o);s&&(s.innerHTML=t+e.data[o]+n)}})},CABLES.API.getOpExamples=function(e,t,n,o,s=0,i=0){const a=document.querySelector("#loading");a&&a.classList.remove("hide");let r="op/"+e+"/examples";s=Number(s),i=Number(i),(s||0===s)&&(r+="?l="+s),i&&(r+="&o="+i),CABLES.api.get(r,(function(e){projectPagePatchList(e.projects,t,o,e.pagination),e.pagination&&initPagination("#"+n,e.pagination,s,i,(e,t)=>{CABLES.API.OPPATCHES.setUrl("examples",e,t)}),a&&a.classList.add("hide")}))},CABLES.API.getRandomOps=function(){CABLES.api.get("doc/ops/random",(function(e){let t=getHandleBarHtml("project_list_headline",{link:"/ops",title:"Random Ops"});t+='
    ';for(const n in e.ops)e.ops[n]&&(t+=getHandleBarHtml("projectOp",e.ops[n]));t+="
    ",document.getElementById("random-ops").innerHTML=t}))},CABLES.API.landingPatchteaser=function(){CABLES.api.get("fav/top",(function(e){let t=0,n=getHandleBarHtml("project_list_headline",{link:"/ops",title:""});for(t in n+='
    ',e.topFavs.length>4&&(e.topFavs.length=4),e.topFavs)n+=getHandleBarHtml("project",e.topFavs[t].proj);n+="
    ",document.getElementById("landingteaser").innerHTML=n;let o=getHandleBarHtml("project_list_headline",{link:"/ops",title:""});for(t in o+='
    ',e.allTimeTop.length>4&&(e.allTimeTop.length=4),e.allTimeTop)o+=getHandleBarHtml("project",e.allTimeTop[t].proj);o+="
    ",document.getElementById("landingteaser2").innerHTML=o}))},CABLES.API.getTopProjectList=function(){CABLES.api.get("fav/top",(function(e){let t=0,n=getHandleBarHtml("project_list_headline",{link:"/patches?w=topMonth",title:"Top Of The Month"});for(t in n+='
    ',e.topFavs.length>8&&(e.topFavs.length=8),e.topFavs.length<4&&(e.topFavs.length=Math.min(4,e.topFavs.length)),e.topFavs)n+=getHandleBarHtml("project",e.topFavs[t].proj);n+="
    ",document.getElementById("top-of-the-month").innerHTML=n;let o=getHandleBarHtml("project_list_headline",{link:"/patches?w=ppub&or=favs",title:"Top Patches (Six Months)"});for(t in o+='
    ',e.allTimeTop)o+=getHandleBarHtml("project",e.allTimeTop[t].proj);o+="
    ",document.getElementById("alltimetop").innerHTML=o;const s=document.getElementById("featured");s&&(s.innerHTML=getHandleBarHtml("featured_project",e.featured))}))},CABLES.API.getLatestProjectList=function(){CABLES.api.get("latestProjects",(function(e){let t=getHandleBarHtml("project_list_headline",{link:"/projects",title:"Latest Patches"});t+='
    ',e.length=12;for(const n in e)t+=getHandleBarHtml("project",e[n]);t+="
    ",ele.byId("latest-projects").innerHTML=t}))},CABLES.API.newProject=function(){CABLES.api.post("project/",null,(function(e){const t=e.shortId||e._id;document.location.href="/edit/"+t}))},CABLES.API.checkInvites=()=>{CABLES.api.get("invites/count",(function(e){const t=e.data.count,n=document.getElementById("mypatches_patchinvitesnew"),o=document.getElementById("myPatchesBubble"),s=document.getElementById("myInvites");t>0?(o&&(o.innerHTML=t),o&&o.classList.remove("hide"),s&&(s.innerHTML=t),n&&n.classList.remove("hide"),n&&n.classList.add("block")):(o&&o.classList.add("hide"),n&&n.classList.add("hide"),n&&n.classList.remove("block"))})),CABLES.api.get("teams/mine",(function(e){if(e.data.length>0){const e=document.getElementById("mypatches_teams");e&&(e.classList.remove("hide"),e.classList.add("block"))}}))},(CABLES=CABLES||{}).SEARCH=CABLES.SEARCH||{};const numResults=32;let lastOptions=[],lastTerm="";var CABLES;window.ele&&document.addEventListener("DOMContentLoaded",()=>{const e=ele.byId("headsearch");e&&e.addEventListener("keydown",t=>{13===t.keyCode&&(document.location.href="/search?s="+e.value)});const t=ele.byId("search");t&&t.addEventListener("keydown",t=>{13===t.keyCode&&(document.location.href="/search?s="+e.value)})}),CABLES.SEARCH.setOrder=function(e){lastOptions.order=e,CABLES.SEARCH.setWhat(lastOptions.what,e,lastOptions.limit,lastOptions.offset)},CABLES.SEARCH.didCount=!1,CABLES.SEARCH.getCount=function(){if(CABLES.SEARCH.didCount)return;CABLES.SEARCH.didCount=!0;const e=lastOptions,t=JSON.parse(JSON.stringify(lastOptions));t.count=!0,t.what="ppriv",CABLES.SEARCH.search(lastTerm,t,(function(e,t){ele.byId("num_ppriv").innerHTML=" ("+t.numResults+")"}),48),t.what="ppub",CABLES.SEARCH.search(lastTerm,t,(function(e,t){ele.byId("num_ppub").innerHTML=" ("+t.numResults+")"}),48),t.what="pshared",CABLES.SEARCH.search(lastTerm,t,(function(e,t){ele.byId("num_pshared").innerHTML=" ("+t.numResults+")"}),48),t.what="featured",CABLES.SEARCH.search(lastTerm,t,(function(e,t){ele.byId("num_featured").innerHTML=" ("+t.numResults+")"}),48),t.what="example",CABLES.SEARCH.search(lastTerm,t,(function(e,t){ele.byId("num_example").innerHTML=" ("+t.numResults+")"}),48),t.what="ops",CABLES.SEARCH.search(lastTerm,t,(function(e,t){ele.byId("num_ops").innerHTML=" ("+t.numResults+")"}),48),t.what="u",CABLES.SEARCH.search(lastTerm,t,(function(e,t){ele.byId("num_user").innerHTML=" ("+t.numResults+")"}),48),t.what="topMonth",CABLES.SEARCH.search(lastTerm,t,(function(e,t){ele.byId("num_topMonth").innerHTML=" ("+t.numResults+")"}),48),lastOptions=e},CABLES.SEARCH.setWhat=function(e,t,n,o){n=Number(n),o=Number(o);let s="?s="+lastTerm+"&w="+e;t&&(s+="&or="+t),(n||0===n)&&(s+="&l="+n),o&&(s+="&o="+o),window.location.search=s},CABLES.SEARCH.showResults=function(e,t){ele.byId("results").innerHTML="";let n="",o=0;if(t.tags&&t.tags.length>0){for(n+='
    Related tags: ',o=0;o'+t.tags[o].tag+" ");n+="

    "}if(t.projects)for(o in t.projects)n+=getHandleBarHtml("project",t.projects[o]);if(t.users)for(o in t.users)n+=getHandleBarHtml("user",t.users[o]);if(t.items)for(o in t.items)n+=getHandleBarHtml("op",t.items[o]);0===n.length&&(n='

    No Results

    '),ele.byId("results").innerHTML=n,CABLES.SEARCH.getCount(),ele.hide(ele.byId("loading")),ele.show(ele.byId("results"))},CABLES.SEARCH.search=function(e,t,n,o=0,s=0){(t=t||{}).count||(ele.hide(ele.byId("results")),ele.show(ele.byId("loading")));let i="s="+e;if(lastOptions=t,lastOptions.limit=o,lastOptions.offset=s,lastTerm=e,t.order){i+="&or="+t.order;const e=document.getElementById("order");e&&(e.value=t.order)}t.what&&(i+="&w="+t.what),t.count?i+="&count="+!0:(o=Number(o),s=Number(s),(o||0===o)&&(i+="&l="+o),s&&(i+="&o="+s));let a="search?"+i;CABLES.api.get(a,(function(e){n||(n=CABLES.SEARCH.showResults),n(null,e),e.pagination&&initPagination("#results_container",e.pagination,o,s,(e,n)=>{lastOptions.limit=e,lastOptions.offset=n,CABLES.SEARCH.setWhat(t.what,t.order,e,n)})}),(function(e){console.log(e)}))},CABLES.API=CABLES.API||{},CABLES.API.SETTINGS=CABLES.API.SETTINGS||{},CABLES.API.SETTINGS.sendConfirmationEMail=function(){document.getElementById("settings_emailconfirm").innerHTML='',CABLES.api.get("settings/sendConfirmation",(function(e){let t="an error occured, try again later.";e.success&&(t="confirmation email sent. check your inbox"),document.getElementById("settings_emailconfirm").innerHTML=t}))},CABLES.importProject=function(){const e=document.getElementById("hiddenfileElem");e&&e.click()},CABLES.importProjectFile=function(e){const t=new FormData;for(let n in e){const o=e[n];t.append(n,o)}const n=new XMLHttpRequest;n.open("POST","/api/project/import"),n.onload=function(e,t){console.log("ONLOAD..."),console.log(e.target.response);const n=JSON.parse(e.target.response);n.projectId?document.getElementById("importjson").innerHTML='open patch':document.getElementById("importjson").innerHTML="something went wrong..."},n.send(t)},CABLES.ignoreAutoSuggestRequest=!1,CABLES.initializeAutosuggestsFromApi=(e=[],t={})=>{const n=e=>{if(CABLES.ignoreAutoSuggestRequest)return;const n=e.target,o=n.value;t.minLength&&o.length{const t={title:e[n.dataset.autosuggestTitlefield],value:e[n.dataset.autosuggestValuefield]};e.image&&(t.image=e.image),o.push(t)}),CABLES.ignoreAutoSuggestRequest||CABLES.showInputSuggestions(n,o,t)}))};e.forEach(e=>{e.addEventListener("focus",(function(e){CABLES.ignoreAutoSuggestRequest=!1,n(e)})),e.addEventListener("input",n)})},CABLES.showInputSuggestions=(e,t,n={})=>{e.onkeydown=null,document.querySelectorAll(".inputSuggestionList").forEach(e=>e.remove());const o=document.createElement("div");o.classList.add("inputSuggestionList");let s="";for(let e=0;e',t[e].image&&(s+=''),s+=t[e].title,s+="
    ";o.innerHTML=s;const i=e.getBoundingClientRect();o.style.top=i.top+i.height+window.scrollY+"px",o.style.left=i.left+"px",o.style.width=i.width+"px";const a=getComputedStyle(e);o.style.background=a.background,o.style["background-color"]=a["background-color"],o.dataset.selected=0;const r=s=>{const i=s.key;if("ArrowDown"===i)o.dataset.selected++,o.dataset.selected>t.length&&(o.dataset.selected=t.length),o.childNodes.forEach((e,t)=>{const n=t+1;o.dataset.selected==n?(e.classList.add("selected"),o.scrollTop=e.offsetTop):e.classList.remove("selected")});else if("ArrowUp"===i)o.dataset.selected--,o.dataset.selected<1&&(o.dataset.selected=1),o.childNodes.forEach((e,t)=>{const n=t+1;o.dataset.selected==n?(e.classList.add("selected"),o.scrollTop=e.offsetTop):e.classList.remove("selected")});else if("Enter"===i&&o.dataset.selected&&o.dataset.selected>0&&o.dataset.selected<=t.length){let s=t[o.dataset.selected-1].title,i=t[o.dataset.selected-1].title,a=t[o.dataset.selected-1].value;if(n.append){const r=n.seperator||",";s=e.value+=r+t[o.dataset.selected-1].title,i=e.dataset.autosuggestTitle+r+t[o.dataset.selected-1].title,a=e.dataset.autosuggestValue+r+t[o.dataset.selected-1].value,s=s.replace(r+r,r),i=i.replace(r+r,r),a=a.replace(r+r,r)}e.value=s,e.dataset.autosuggestTitle=i,e.dataset.autosuggestValue=a,e.removeEventListener("keydown",r),o.remove()}};e.onkeydown=r,e.addEventListener("blur",()=>{CABLES.ignoreAutoSuggestRequest=!0,setTimeout(()=>{e.onkeydown=null,o.remove()},100)}),o.addEventListener("pointerdown",t=>{n.append?e.value=e.value+","+t.target.dataset.title:e.value=t.target.dataset.title,e.dataset.autosuggestTitle=t.target.dataset.title,e.dataset.autosuggestValue=t.target.dataset.value,e.onkeydown=null,o.remove()}),document.body.appendChild(o)},(CABLES=CABLES||{}).PAGE=CABLES.PAGE||{},CABLES.PAGE.SETTINGS=CABLES.PAGE.SETTINGS||{},CABLES.PAGE.SETTINGS.saveAccount=function(){const e=ele.byId("accountsavebutton");e.innerHTML="Working....",CABLES.api.put("user/account/",{account_email:ele.byId("account_email").value,account_newsletter:document.getElementById("account_newsletter").checked},(function(t){t.success?setTimeout((function(){e.innerHTML="Account updated!"}),500):console.log("fail ",t),console.log("res",t)}),(function(t){e.innerHTML="Something went wrong: "+t.msg,console.log("err res",t)}))},CABLES.PAGE.SETTINGS.deleteAccount=function(e,t){const n=prompt("please enter your username:","");n&&n===t?CABLES.api.delete("user/"+e+"?userName="+t,{},(function(e){window.location="/logout"}),(function(e){document.getElementById("errors_delete").innerHTML="this did not work as expected..."})):document.getElementById("errors_delete").innerHTML="usernames do not match..."},CABLES.PAGE.SETTINGS.saveProfile=function(){const e=ele.byId("profilesavebutton");e.innerHTML="working....";let t=ele.byId("profile-website").value;if(t.length>0){let e=!1;if(-1===t.indexOf("https://")&&-1===t.indexOf("http://")&&(e=!0),-1===t.indexOf(".")&&(e=!0),e)return void(ele.byId("errors_links").innerHTML="Website invalid url");ele.byId("errors_links").innerHTML=""}CABLES.api.put("user/profile/",{profile_bio:ele.byId("profile-bio").value,profile_website:t,profile_twitter:ele.byId("profile-twitter").value,profile_facebook:ele.byId("profile-facebook").value,profile_instagram:ele.byId("profile-instagram").value,profile_mastodon:ele.byId("profile-mastodon").value,profile_country:ele.byId("profile-country").value,profile_city:ele.byId("profile-city").value},(function(t){t.success?setTimeout((function(){e.innerHTML="profile saved!",setTimeout((function(){e.innerHTML="save again"}),3e3)}),500):console.log("fail ",t),console.log("res",t)}),(function(t){e.innerHTML="something went wrong",console.log("err res",t)}))},CABLES.PAGE.SETTINGS.savePassword=function(){const e=ele.byId("passwordSavebutton");e.innerHTML="working....",CABLES.api.put("user/password/",{password1:ele.byId("password1").value,password2:ele.byId("password2").value},(function(t){t.success?setTimeout((function(){e.innerHTML="password changed!",setTimeout((function(){e.innerHTML="set password"}),3e3)}),500):console.log("fail ",t),console.log("res",t)}),(function(t){t.msg?e.innerHTML=t.msg:e.innerHTML="something went wrong",console.log("err res",t),setTimeout((function(){e.innerHTML="set password"}),3e3)}))},CABLES.PAGE.SETTINGS.init=function(){const e=ele.byQueryAll(".tab-bar li");e.forEach(t=>{t.addEventListener("pointerdown",()=>{e.forEach(e=>{e.classList.remove("active")}),t.classList.add("active");const n=t.dataset.tabName;ele.byQueryAll(".tab-bar-content").forEach(e=>{e.classList.add("hidden")}),ele.byQuery(".tab-bar-content[data-tab-name='"+n+"']").classList.remove("hidden")})})},(CABLES=CABLES||{}).TalkerAPI=function(e){CABLES.EventTarget.apply(this),this._talker=new Talker(e,"*"),this._callbackCounter=0,this._callbacks={},this._talker.onMessage=function(e){"callback"==e.data.cmd?this._callbacks[e.data.cb]&&this._callbacks[e.data.cb](e.data.error,e.data.response):this.emitEvent(e.data.cmd,e.data.data,function(t,n){this._talker.send("cables",{cmd:"callback",cb:e.data.cb,response:n,error:t})}.bind(this))}.bind(this)},CABLES.TalkerAPI.prototype.send=function(e,t,n){const o={cmd:e,data:t};n&&(this._callbackCounter++,this._callbacks[this._callbackCounter]=n,o.cb=this._callbackCounter),this._talker.send("cables",o)},CABLES.TEST={},CABLES.overwriteTime=1e-5,CABLES.TEST.Tests=class{constructor(e,t){CABLES.EventTarget.apply(this),this.tests=[],this.testResults=[],this.testsToRun=[],this.selectedTags=new Set,this.runId=uuid(),this.browserTestId=null,this.currentScene=null,this.currentIndex=null,this.testIsRunning=!1,this._codeHost=e,this._sandboxHost=t,this._storageKey="cables_tests_environment",this._currentProjectId=null,this.envConfig=this._buildEnvironmentConfig(),this.allDoneCb=null,"hidden"in document?document.addEventListener("visibilitychange",this.cancelTests):"mozHidden"in document?document.addEventListener("mozvisibilitychange",this.cancelTests):"webkitHidden"in document?document.addEventListener("webkitvisibilitychange",this.cancelTests):"msHidden"in document?document.addEventListener("msvisibilitychange",this.cancelTests):"onfocusin"in document?document.onfocusin=document.onfocusout=this.cancelTests:window.onpageshow=window.onpagehide=window.onfocus=window.onblur=this.cancelTests,this.loadTestLibs(()=>{this.emitEvent("ready")},e=>{this.emitEvent("error")})}getEnvironmentConfig(){return this.envConfig}setEnvironmentConfig(e,t){this.envConfig={codeHost:e,sandboxHost:t},window.localStorage&&window.localStorage.setItem(this._storageKey,JSON.stringify(this.envConfig))}cancelTests(){this.testIsRunning&&(this.testIsRunning=!1,ele.byId("resultall").innerHTML="Testing canceled... do not switch focus to another tab while runnign tests...",ele.byId("resultall").classList.add("warning"))}logTest(e,t){console.log(e,t)}loadTestLibs(e,t){const n="cables.gl"===this.envConfig.codeHost?"cables.min.js":"cables.max.js",o=document.createElement("script");o.type="text/javascript",o.src="//"+this.envConfig.sandboxHost+"/ui/js/"+n+"?nc="+Date.now(),o.onload=()=>{const n="cables.gl"===this.envConfig.codeHost?"libs.core.min.js":"libs.core.js",o=document.createElement("script");o.type="text/javascript",o.src="//"+this.envConfig.sandboxHost+"/ui/js/"+n+"?nc="+Date.now(),o.onload=()=>{const n=document.createElement("script");n.type="text/javascript",n.src="//"+this.envConfig.codeHost+"/api/ops/code?nc="+Date.now(),n.onload=()=>{e&&e()},n.onerror=()=>{t("failed to load "+n.src)},(ele.byQuery("head")||ele.byQuery("body")).appendChild(n)},o.onerror=()=>{t("failed to load "+o.src)},(ele.byQuery("head")||ele.byQuery("body")).appendChild(o)},o.onerror=()=>{t("failed to load "+o.src)},(ele.byQuery("head")||ele.byQuery("body")).appendChild(o)}showResults(e){ele.byId("testresult").innerHTML="";ele.byId("testresult").innerHTML=getHandleBarHtml("test",this.testsToRun[e]);const t=new Image;t.src=this.testsToRun[e].resultData.getImageDataUrl();ele.byId("container_comparison").appendChild(t);const n=this.testsToRun[e].correctImg;ele.byId("container_specification").appendChild(n);ele.byId("container_resultimage").appendChild(this.testsToRun[e].testImg)}runTestId(e,t){CABLES.doBrowserTest(n=>{n&&n.id&&(this.browserTestId=n.id),CABLES.api.get("tests/"+e,e=>{this.runId=uuid(),this.testsToRun=[e],this.testResults=[],this.runTest(0,t)})})}runAllTests(e){CABLES.doBrowserTest(t=>{if(t&&t.id&&(this.browserTestId=t.id),ele.byQueryAll(".testcontainer").forEach(()=>{ele.byQueryAll(".test_failed").forEach(e=>{e.remove()}),ele.byQueryAll(".test_passed").forEach(e=>{e.remove()})}),this.testsToRun=[],0===this.selectedTags.size)this.testsToRun=this.tests;else{const e=".testcontainer:not(.hidden)";ele.byQueryAll(e).forEach(e=>{const t=e.dataset.testIndex;this.testsToRun.push(this.tests[t])})}this.testResults=[],this.runId=uuid(),this.runTest(0,e)})}allTestsFinished(e){const t=ele.byId("testcontrols");t&&t.classList.remove("hidden");const n=ele.byId("viewrun");n&&(n.href="/tests/run/"+this.runId,n.classList.remove("hidden"),n.dataset.runId=this.runId),e&&e(this.testResults||[])}saveResult(e){ele.byId("saveimage-button").innerHTML="saving...",this.logTest("save result... "+e),CABLES.api.put("test/save_result/"+e,{screenshot:ele.byId("glcanvas_"+this.currentIndex).toDataURL()},e=>{ele.byId("saveimage-button").innerHTML="IMAGE SAVED!",this.logTest(e)})}runTest(e,t){if(this.allDoneCb=t,this.testIsRunning)return void this.logTest("test still running...");const n=ele.byId("viewrun");n&&n.classList.add("hidden"),this.testIsRunning=!0,ele.byId("testresult").innerHTML='
    running test...
    ',this.currentScene&&(this.currentScene.dispose(),ele.byId("glcanvas_"+this.currentIndex).remove(),this.currentScene=null,this.logTest("patch disposed...")),this.logTest("***************************************************"),this.logTest("run test ",e+": "+this.testsToRun[e].name),this.currentIndex=e;const o=ele.byId("glContainer"),s=document.createElement("canvas");s.setAttribute("id","glcanvas_"+this.currentIndex),s.setAttribute("width","640"),s.setAttribute("height","360"),s.style.width="640px",s.style.height="360px",o.appendChild(s),this.currentFinished=-1,CABLES.internalNow=function(){return 1e3*CABLES.overwriteTime},CABLES.Timer.prototype.get=function(){return CABLES.overwriteTime},this._resetTimers(1e-5);let i=new CABLES.Patch({glCanvasId:"glcanvas_"+e,prefixAssetPath:"",onFinishedLoading:()=>{this._finishedLoading(i)},canvas:{antialias:!1,preserveDrawingBuffer:!0,alpha:!0},onError(e){console.error("test on error",e)}});CABLES.api.get("project/"+this.testsToRun[e]._id,t=>{const n=document.createElement("script");n.type="text/javascript",n.async=!0,n.src="//"+this.envConfig.codeHost+"/api/project/"+this.testsToRun[e]._id+"/libs.js?nc=tests",n.onload=()=>{const n=document.createElement("script");n.type="text/javascript",n.src="//"+this.envConfig.codeHost+"/api/project/"+this.testsToRun[e]._id+"/projectops.js?nc="+Date.now(),n.onload=()=>{this.testResults[e]={},i.deSerialize(t),this.currentScene=i},(ele.byQuery("head")||ele.byQuery("body")).appendChild(n)},(ele.byQuery("head")||ele.byQuery("body")).appendChild(n)})}showAll(e){const t=new Set,n=getQueryVariable("op");let o="tests";n&&(o+="?op="+n),CABLES.api.get(o,n=>{const o=ele.byId("tests");this.tests=n;let s="",i="";const a={};for(const e in n){const o=n[e];o.testIndex=e;const r=[];o.tags&&Array.isArray(o.tags)&&o.tags.forEach(e=>{if(a.hasOwnProperty(e)||(a[e]=0),a[e]++,a[e]>1){const n=this._sanitizeTagName(e);r.push(n),t.add(e)}}),i+=getHandleBarHtml("test_row",{test:o,tagClasses:r.join(" ")}),s+=getHandleBarHtml("project_test",{project:o,tagClasses:r.join(" "),cb:Date.now()})}this._initTagContainer(t),o.innerHTML=i,ele.byId("thumbnails").innerHTML=s,e&&e()})}runBrowserstack(e,t,n,o){CABLES.startSavingAnimButton(e);const s=ele.byId("browserstack-progress");s&&ele.show(s),CABLES.api.post("tests/browserstack",{device:t,os:n,browser:o},e=>{const t=ele.byId("testprogressinner");t&&t.classList.remove("failed");const n=ele.byId("resultall");n&&(n.innerHTML="
     ");const o=ele.byId("resultcontainer");if(ele.show(o),e.data.runId){const t=e.data.runId,n=ele.byId("viewrun");n&&(n.href="/tests/run/"+t,n.classList.remove("hidden"),n.dataset.runId=t)}CABLES.stopSavingAnimButton()},e=>{const t=ele.byId("resultcontainer");ele.show(t);const n=ele.byId("resultall");n&&(n.innerHTML="
    ERROR: "+e.msg.error);const o=ele.byId("testprogressinner");o&&o.classList.add("failed"),CABLES.stopSavingAnimButton()})}filterByTag(e){this.removeAllTagFilters(),this.selectedTags.add(e),this._updateTagSelection()}filterByTags(e){this.selectedTags=new Set(e),this._updateTagSelection()}removeAllTagFilters(){this.selectedTags.clear();ele.byQueryAll(".testcontainer, #thumbnails .project").forEach(e=>{e.classList.remove("hidden"),e.classList.remove("filtered")});ele.byQueryAll("#tagcontainer .tag").forEach(e=>{e.classList.remove("active")}),ele.byId("runalltests").innerHTML="Run all tests",history.replaceState(null,null," "),window.localStorage&&window.localStorage.removeItem("cables_tests_tags")}initCategories(e,t,n=null){e.innerHTML=getHandleBarHtml("test_categories",{categories:t}),this._currentProjectId=n;const o=ele.byQueryAll("#categories li"),s=o[0];o.forEach(e=>{e.addEventListener("pointerdown",()=>{this._setCategoryTab(e,10,0)})});let i=new URL(window.location.href),a=i.searchParams.get("l")||10,r=i.searchParams.get("o")||0;s&&this._setCategoryTab(s,a,r)}_setCategoryTab(e,t,n){const o=ele.byId("loading"),s=ele.byId("testresults");s.innerHTML="",o&&ele.show(o);let i=e.dataset.category;ele.byQueryAll("#categories li").forEach(e=>e.classList.remove("active")),e.classList.add("active"),CABLES.api.post("test/category/results?l="+t+"&o="+n,{projectId:this._currentProjectId,category:i},i=>{if(o&&ele.hide(o),i.data&&Array.isArray(i.data)){let e="";i.data.forEach(t=>{e+=getHandleBarHtml("test_resultrow",t)}),s.innerHTML=e+"
    "}i.pagination&&initPagination("#pagination",i.pagination,t,n,(t,n)=>{this._setCategoryTab(e,t,n)})})}initEnvironments(e,t){e.innerHTML=getHandleBarHtml("test_environments",{environments:t});const n=this.getEnvironmentConfig(),o=ele.byId("codeHost");o&&(o.value=n.codeHost);const s=ele.byId("sandboxHost");s&&(s.value=n.sandboxHost);const i=ele.byId("addTestEnv");i&&i.addEventListener("pointerdown",()=>{this.setEnvironmentConfig(o.value,s.value),window.location.reload()});const a=ele.byQueryAll("#environments li");let r=!1;if(a.forEach(e=>{e.dataset.codeHost===n.codeHost&&(r=!0),e.addEventListener("pointerdown",()=>{a.forEach(e=>e.classList.remove("active")),e.classList.add("active"),this.setEnvironmentConfig(e.dataset.codeHost,e.dataset.sandboxHost),window.location.reload()})}),!r){const e=ele.byId("environments"),t=document.createElement("li");t.dataset.codeHost=n.codeHost,t.dataset.sandboxHost=n.sandboxHost,t.innerText=n.codeHost,e.appendChild(t)}const l=ele.byQuery('#environments li[data-code-host="'+n.codeHost+'"]');l&&l.classList.add("active");const c=ele.byId("add_test_env");c&&c.addEventListener("pointerdown",()=>{const e=ele.byId("add_env"),t=e.classList.contains("hidden");e.classList.toggle("hidden"),t?(c.classList.remove("icon-plus"),c.classList.add("icon-minus")):(c.classList.add("icon-plus"),c.classList.remove("icon-minus"))})}_updateTagSelection(){ele.byQueryAll(".testcontainer, #thumbnails .project").forEach(e=>{this.selectedTags.forEach(t=>{if(e.classList.contains(String(t))&&!e.classList.contains("filtered"))e.classList.remove("hidden");else{const n=".tag[data-tag='"+t+"']";ele.byQueryAll(n).forEach(e=>{e.classList.add("active")}),e.classList.add("hidden"),e.classList.add("filtered")}})}),ele.byId("runalltests").innerHTML="run tests",window.location.hash=Array.from(this.selectedTags).join(",")}_getTestRow(e,t){const n=document.createElement("div");try{const o=document.createElement("a");o.href="/tests/"+t._id,o.appendChild(document.createTextNode(t.name+" ")),n.appendChild(o),n.classList.add("testcontainer"),n.setAttribute("id","test_"+e),n.dataset.testIndex=e}catch(e){this.logTest(t.name,"exception ",e)}return n}_initTagContainer(e){const t=ele.byId("tagcontainer");t&&e.forEach(e=>{const n=this._sanitizeTagName(e),o=document.createElement("a");o.innerText=e,o.classList.add("tag"),o.dataset.tag=n,o.onclick=()=>{o.classList.contains("active")?(o.classList.remove("active"),this.removeAllTagFilters()):(o.classList.add("active"),this.filterByTag(n))},t.appendChild(o)})}_sanitizeTagName(e){let t=e||"";return t=t.toLowerCase(),t=t.split(" ").join("_"),t=t.replace(/\./g,"_"),t.match(/^\d/)&&(t="u_"+t),t}_updateProgress(){let e=0,t="";for(let t=0;tfailed: "+e+"/"+this.testsToRun.length;const n=ele.byId("testprogressinner");n&&(e>0?n.classList.add("failed"):n.classList.remove("failed"),ele.byId("testprogressinner").style.width=Math.round(this.currentIndex/(this.testsToRun.length-1)*100)+"%");const o=ele.byId("resultall");o&&(o.innerHTML=t);const s=ele.byId("testcontrols");s&&s.classList.add("hidden");const i=ele.byId("resultcontainer");i&&i.classList.remove("hidden")}_saveResultToServer(e,t,n,o,s,i){let a="failed";o&&(a="passed");let r=null;const l=document.getElementById("resultimage_"+i);l&&(r=l.src);const c=s.resultData.getImageDataUrl(),d={run:e,status:a,browserInfo:t,resultThreshold:s.resultData?s.resultData.rawMisMatchPercentage:null,platform:platform||{},resultData:s.resultData,comparisonImage:c,renderImage:r,codeHost:this.envConfig.codeHost,sandboxHost:this.envConfig.sandboxHost};CABLES.api.put("test/save/"+n,d,e=>{const t=ele.byId("testresulturl");t&&t.setAttribute("href",e.data.resultUrl)},()=>{this.logTest("ERROR SAVING TO SERVER")})}_rendered(e){e.pause(),this.logTest("rendered...",e.getFrameNum());const t=new Image,n=new Image;n.setAttribute("id","resultimage_"+this.currentIndex),n.src=ele.byId("glcanvas_"+this.currentIndex).toDataURL(),t.onabort=t.onerror=()=>{ele.byId("testresult").innerHTML="",this.testIsRunning=!1;const e=ele.byId("testresult"),t=document.createElement("div");t.appendChild(document.createTextNode("could not load correct image to compare...")),this.logTest("could not load correct image to compare...",this.testsToRun[this.currentIndex].name),t.classList.add("failed"),this.testResults[this.currentIndex].passed=!1,e.appendChild(t);const n=ele.byId("test_"+this.currentIndex);n.classList.add("testcontainer"),n.classList.add("failed"),this.logTest("not complete.... ignore..."),this._updateProgress(),this.testsToRun[this.currentIndex+1]?this.runTest(this.currentIndex+1):this.allTestsFinished(this.allDoneCb)},t.onload=()=>{this.testsToRun[this.currentIndex].correctImg=t,this.testsToRun[this.currentIndex].testImg=n,resemble(n.src).compareTo(t.src).onComplete(e=>{this._onResembleComplete(e,this.testsToRun[this.currentIndex],this.allDoneCb)})},t.src="/api/test/result/"+this.testsToRun[this.currentIndex]._id+"?nc="+Date.now()}_resetTimers(e){CABLES.overwriteTime=e}_finishedLoading(e){e.onFinishedLoading=null,this.logTest("finishedloading...",e.getFrameNum()),this.currentFinished!==e.config.glCanvasId&&(this.currentFinished=e.config.glCanvasId,e.pause(),this._resetTimers(.01),e.renderOneFrame(),e.renderOneFrame(),this._resetTimers(.3),e.renderOneFrame(),e.onOneFrameRendered=()=>{this._rendered(e)},e.renderOneFrame(),e.pause(),this._resetTimers(1e-5))}_onResembleComplete(e,t,n){if(!this.testResults[this.currentIndex])return;if(this.testResults[this.currentIndex]&&this.testResults[this.currentIndex].hasOwnProperty("passed"))return;const o=ele.byId("test_"+t.testIndex);let s="passed";this.logTest("currentIndex",this.currentIndex,this.testResults[this.currentIndex]),this.testResults[this.currentIndex].passed=!0,this.testResults[this.currentIndex].name=this.testsToRun[this.currentIndex].name,this.testResults[this.currentIndex].projectId=this.testsToRun[this.currentIndex]._id,e.misMatchPercentage>2&&(s="failed",this.testResults[this.currentIndex].passed=!1),this.logTest("result ",this.testsToRun[this.currentIndex].name,this.testResults[this.currentIndex].passed);const i=ele.byId("testthumb_"+this.testsToRun[this.currentIndex]._id);if(i)if("passed"===s)i.style.border="3px solid green";else{i.style.border="3px solid red";const t=e.getImageDataUrl();i.setAttribute("src",t)}if(o){const e=document.createElement("span");e.classList.add("test_"+s),e.innerText=s,o.appendChild(e)}this.testsToRun[this.currentIndex].resultClass=s,this.testsToRun[this.currentIndex].resultData=e,this.showResults(this.currentIndex),this.testIsRunning=!1,this.logTest("complete...."),this._saveResultToServer(this.runId,this.browserTestId,this.testsToRun[this.currentIndex]._id,this.testResults[this.currentIndex].passed,this.testsToRun[this.currentIndex],this.currentIndex),this._updateProgress(),this.testsToRun[this.currentIndex+1]?this.runTest(this.currentIndex+1):this.allTestsFinished(n)}_buildEnvironmentConfig(){let e={codeHost:this._codeHost,sandboxHost:this._sandboxHost};if(window.localStorage&&window.localStorage.getItem(this._storageKey))try{const t=JSON.parse(window.localStorage.getItem(this._storageKey));t.codeHost&&(e.codeHost=t.codeHost),t.sandboxHost&&(e.sandboxHost=t.sandboxHost)}catch(t){this.logTest("could not parse stored testenvironment, using",e.codeHost,e.sandboxHost,"error: ",t)}return e}}; +//# sourceMappingURL=script.min.js.map +CABLES.API.build = {"timestamp":1671443420321,"created":"2022-12-19T09:50:20.321Z","git":{"branch":"master","commit":"739587e6bcac1872e707cf575961a89230dc30d1","date":"2022-12-19T09:48:19.000Z","message":"changelogitem headline"}}; \ No newline at end of file diff --git a/survey_dashboard/hmc_layout/static/en_files/talkerapi.js b/survey_dashboard/hmc_layout/static/en_files/talkerapi.js new file mode 100644 index 0000000..2714894 --- /dev/null +++ b/survey_dashboard/hmc_layout/static/en_files/talkerapi.js @@ -0,0 +1,2 @@ +var CABLES=CABLES||{};CABLES.EventTarget=function(){this._eventCallbacks={},this._logName="",this._doLog=!1,this._listeners={},this.addEventListener=this.on=function(e,t,s){const n={id:(s||"")+CABLES.uuid(),name:e,cb:t};return this._eventCallbacks[e]?this._eventCallbacks[e].push(n):this._eventCallbacks[e]=[n],this._listeners[n.id]=n,n.id},this.hasEventListener=function(e,t){if(e&&!t)return!!this._listeners[e];if(console.warn("old eventtarget function haseventlistener!"),e&&t&&this._eventCallbacks[e]){return-1!=this._eventCallbacks[e].indexOf(t)}},this.removeEventListener=this.off=function(e,t){if(null==e)return;if(!t){const t=this._listeners[e];if(!t)return;let s=!0;for(;s;){s=!1;let n=-1;for(let a=0;ae;e++)o[e]()}))),i};return h.then=function(h,l){var c=a(),u=function(){try{var s=i?h:l;e(s)?function s(a){var i,r=0;try{if(a&&(t(a)||e(a))&&e(i=a.then)){if(a===c)throw new TypeError;i.call(a,(function(){r++||s.apply(n,arguments)}),(function(e){r++||c(!1,[e])}))}else c(!0,arguments)}catch(e){r++||c(!1,[e])}}(s.apply(n,r||[])):c(i,r)}catch(e){c(!1,[e])}};return null!=i?s(u):o.push(u),c},h}}(),objectCreate=function(e){function t(){}return t.prototype=e,new t},Talker=function(e,t){this.remoteWindow=e,this.remoteOrigin=t,this.timeout=3e3,this.handshaken=!1,this.handshake=pinkySwearPromise(),this._id=0,this._queue=[],this._sent={};var s=this;return window.addEventListener("message",(function(e){s._receiveMessage(e)}),!1),this._sendHandshake(),this};Talker.prototype.send=function(e,t,s){var n=new Talker.OutgoingMessage(this,e,t,s),a=pinkySwearPromise();return this._sent[n.id]=a,this._queue.push(n),this._flushQueue(),setTimeout((function(){a(!1,[new Error(TALKER_ERR_TIMEOUT)])}),this.timeout),a},Talker.prototype._receiveMessage=function(e){var t;try{t=JSON.parse(e.data)}catch(e){t={}}return!!this._isSafeMessage(e.source,e.origin,t.type)&&(t.handshake||t.handshakeConfirmation?this._handleHandshake(t):this._handleMessage(t))},Talker.prototype._isSafeMessage=function(e,t,s){var n,a;return n=e===this.remoteWindow,a="*"===this.remoteOrigin||t===this.remoteOrigin,n&&a&&s===TALKER_TYPE},Talker.prototype._handleHandshake=function(e){e.handshake&&this._sendHandshake(this.handshaken),this.handshaken=!0,this.handshake(!0,[this.handshaken]),this._flushQueue()},Talker.prototype._handleMessage=function(e){var t=new Talker.IncomingMessage(this,e.namespace,e.data,e.id),s=e.responseToId;return s?this._respondToMessage(s,t):this._broadcastMessage(t)},Talker.prototype._respondToMessage=function(e,t){this._sent[e]&&(this._sent[e](!0,[t]),delete this._sent[e])},Talker.prototype._broadcastMessage=function(e){this.onMessage&&this.onMessage.call(this,e)},Talker.prototype._sendHandshake=function(e){var t={type:TALKER_TYPE};t[e?"handshakeConfirmation":"handshake"]=!0,this._postMessage(t)},Talker.prototype._nextId=function(){return this._id+=1},Talker.prototype._postMessage=function(e){this.remoteWindow&&this.remoteOrigin&&this.remoteWindow.postMessage(JSON.stringify(e),this.remoteOrigin)},Talker.prototype._flushQueue=function(){if(this.handshaken){var e=this._queue.shift();if(!e)return this._queue;if(this._postMessage(e),this._queue.length>0)return this._flushQueue()}return this._queue},Talker.Message=function(e,t,s){return this.talker=e,this.namespace=t,this.data=s,this.type=TALKER_TYPE,this},Talker.OutgoingMessage=function(e,t,s,n){Talker.Message.call(this,e,t,s),this.responseToId=n||null,this.id=this.talker._nextId()},Talker.OutgoingMessage.prototype=objectCreate(Talker.Message.prototype),Talker.OutgoingMessage.prototype.constructor=Talker.Message,Talker.OutgoingMessage.prototype.toJSON=function(){return{id:this.id,responseToId:this.responseToId,namespace:this.namespace,data:this.data,type:this.type}},Talker.IncomingMessage=function(e,t,s,n){Talker.Message.call(this,e,t,s),this.id=n},Talker.IncomingMessage.prototype=objectCreate(Talker.Message.prototype),Talker.IncomingMessage.prototype.constructor=Talker.Message,Talker.IncomingMessage.prototype.respond=function(e){return this.talker.send(null,e,this.id)},window.Talker=Talker;var CABLESUILOADER=CABLESUILOADER||{};CABLESUILOADER.TalkerAPI=function(e){CABLES.EventTarget.apply(this),this._talker=new Talker(e,"*"),this._callbackCounter=0,this._callbacks={},this._talker.onMessage=function(e){"callback"==e.data.cmd?this._callbacks[e.data.cb]&&this._callbacks[e.data.cb](e.data.error,e.data.response):this.emitEvent(e.data.cmd,e.data.data,function(t,s){this._talker.send("cables",{cmd:"callback",cb:e.data.cb,response:s,error:t})}.bind(this))}.bind(this)},CABLESUILOADER.TalkerAPI.prototype.send=function(e,t,s){let n={cmd:e,data:t};s&&(this._callbackCounter++,this._callbacks[this._callbackCounter]=s,n.cb=this._callbackCounter),this._talker.send("cables",n)}; +//# originalSourceMappingURL=talkerapi.js.map diff --git a/survey_dashboard/hmc_layout/static/en_files/visual_EMGlossary_2022-06_1-md.png b/survey_dashboard/hmc_layout/static/en_files/visual_EMGlossary_2022-06_1-md.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2b436eb259b64781639ceb644eaf599cceef1c GIT binary patch literal 254061 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

    OaMx;YT7VRIIG+q`LtQO?m= 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%0DR?{$-}jfEL2fSX!*eW!J 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ⅆ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%0AB)MT2 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 Survey Data Explorer') + +#static_path = this_folder / 'hmc_layout' / 'static' / 'en_files' +template.servable(title='HMC Survey Data Explorer') +# serve like this: panel serve --port 50006 survey_dashboard/ --static-dirs en_files=./survey_dashboard/hmc_layout/static/en_files print('All done') \ No newline at end of file From d40e6a3c474618544940e89869fd7f04a0dcd427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Br=C3=B6der?= Date: Mon, 13 Mar 2023 16:27:11 +0100 Subject: [PATCH 04/64] First Dockerfiles for container deployment --- survey_dashboard/Dockerfile.yml | 8 +++ survey_dashboard/docker-compose.yml | 77 +++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 survey_dashboard/Dockerfile.yml create mode 100644 survey_dashboard/docker-compose.yml diff --git a/survey_dashboard/Dockerfile.yml b/survey_dashboard/Dockerfile.yml new file mode 100644 index 0000000..6293f20 --- /dev/null +++ b/survey_dashboard/Dockerfile.yml @@ -0,0 +1,8 @@ +FROM python:3.8-slim-buster + +WORKDIR /app + +COPY . . +RUN pip3 install . + +CMD ["panel", "serve", "--port", "5006", "survey_dashboard/", "--static-dirs en_files=./survey_dashboard/hmc_layout/static/en_files"] diff --git a/survey_dashboard/docker-compose.yml b/survey_dashboard/docker-compose.yml new file mode 100644 index 0000000..465d660 --- /dev/null +++ b/survey_dashboard/docker-compose.yml @@ -0,0 +1,77 @@ +networks: + default: + driver: bridge + # this is only required in openvswitch networks. + driver_opts: + com.docker.network.driver.mtu: 1400 + +services: + nginx-proxy: + restart: on-failure + logging: + driver: "json-file" + options: + max-size: 50m + image: "nginxproxy/nginx-proxy:latest" + networks: + default: + aliases: + - $HOST + ports: + - "80:80" + - "443:443" + volumes: + - "./nginx/conf.d/proxy.conf:/etc/nginx/conf.d/proxy.conf" + - "./nginx/vhost.d:/etc/nginx/vhost.d" + - "./nginx/ssl/certs:/etc/nginx/certs" + - "./nginx/html:/usr/share/nginx/html" + - "./nginx/htpasswd:/etc/nginx/htpasswd" + - "/var/run/docker.sock:/tmp/docker.sock:ro" + labels: + com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true" + letsencrypt-nginx-proxy-companion: + image: "nginxproxy/acme-companion" + environment: + - NGINX_PROXY_CONTAINER=nginx-proxy + volumes: + - "./nginx/conf.d/proxy.conf:/etc/nginx/conf.d/proxy.conf" + - "./nginx/vhost.d:/etc/nginx/vhost.d" + - "./nginx/ssl/certs:/etc/nginx/certs" + - "./nginx/html:/usr/share/nginx/html" + - "./nginx/htpasswd:/etc/nginx/htpasswd" + - "/var/run/docker.sock:/var/run/docker.sock:ro" + - "le-data:/etc/acme.sh" + depends_on: + - nginx-proxy + + dashboard: + build: + context: . + dockerfile: Dockerfile + networks: + default: + ports: + - "5006:6001" + environment: + - LANGUAGE_DASHBOARD='EN' + - VIRTUAL_HOST=${HOST} + - VIRTUAL_PROTO=http + - LETSENCRYPT_HOST=${HOST} + - LETSENCRYPT_EMAIL="HMC@fz-juelich.de" + + + dashboard_de: + build: + context: . + dockerfile: Dockerfile + networks: + default: + ports: + - "5006:6002" + environment: + - LANGUAGE_DASHBOARD='DE' + - VIRTUAL_HOST=${HOST} + - VIRTUAL_PROTO=http + - LETSENCRYPT_HOST=${HOST} + - LETSENCRYPT_EMAIL="HMC@fz-juelich.de" + From 82c93f73a5ba290394deb0fd841e30a4673b361c Mon Sep 17 00:00:00 2001 From: Silke Gerlich Date: Tue, 14 Mar 2023 10:06:43 +0100 Subject: [PATCH 05/64] page layout modification - removed navigation from page header - adjusted font settings in `en_file/app.css` - added title banner - decluttered `en_files` --- survey_dashboard/hmc_layout/Banner.png | Bin 0 -> 285265 bytes survey_dashboard/hmc_layout/en_template.html | 2978 +---------------- .../en_files/2022_HMC-Conference_News-md.jpg | Bin 87651 -> 0 bytes .../static/en_files/2022_HMC_@FDO-md.jpg | Bin 35801 -> 0 bytes .../en_files/2022_HMC_FAIR-DOscope2-md.jpg | Bin 47781 -> 0 bytes .../static/en_files/2022_HMC_FAIRwish-md.jpg | Bin 41999 -> 0 bytes .../static/en_files/2022_HMC_PIDA-md.jpg | Bin 49470 -> 0 bytes .../2023-01-23_ProjectCall_Visual_news-md.jpg | Bin 21543 -> 0 bytes .../en_files/2023-01_CourseVisual-md.png | Bin 93700 -> 0 bytes .../2023_HMC-FAIRFriday_vanReisen-md.jpg | Bin 45460 -> 0 bytes ...23_HMC-Projects_Workshops_Secop@HMC-md.jpg | Bin 43433 -> 0 bytes ...HMC_Projects@RDA-FAIR-DO-Fabric-IG--md.jpg | Bin 42958 -> 0 bytes .../static/en_files/2023_HMC_at_DESY-md.jpg | Bin 55796 -> 0 bytes .../en_files/AI-HERO_flyer-(002)-md.png | Bin 230280 -> 0 bytes ...MC-FAIRFriday_Bertuch_Garcia-Castro-md.jpg | Bin 53376 -> 0 bytes .../en_files/HMC-FAIRFriday_Britta-md.jpg | Bin 41486 -> 0 bytes .../en_files/HMC-FAIRFriday_huang-md.jpg | Bin 45795 -> 0 bytes .../static/en_files/HMC-HEC-2023-md.PNG | Bin 75794 -> 0 bytes .../HMC_Events_deRSE-20223-preview-md.jpg | Bin 21686 -> 0 bytes .../en_files/HMC_TEACH-2-2022-lg-md.jpg | Bin 54927 -> 0 bytes .../static/en_files/MC2023_Header-md.jpg | Bin 52345 -> 0 bytes .../en_files/Marble_Workshop_visual-md.png | Bin 103510 -> 0 bytes .../en_files/Partner-Website_900x675px-md.png | Bin 318627 -> 0 bytes .../en_files/Teaser_M4R_20230327-md.PNG | Bin 61438 -> 0 bytes .../static/en_files/Teaser_raw-md.PNG | Bin 60815 -> 0 bytes .../hmc_layout/static/en_files/app.css | 2319 ++++++++++++- .../visual_EMGlossary_2022-06_1-md.png | Bin 254061 -> 0 bytes 27 files changed, 2359 insertions(+), 2938 deletions(-) create mode 100644 survey_dashboard/hmc_layout/Banner.png delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2022_HMC-Conference_News-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2022_HMC_@FDO-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2022_HMC_FAIR-DOscope2-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2022_HMC_FAIRwish-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2022_HMC_PIDA-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2023-01-23_ProjectCall_Visual_news-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2023-01_CourseVisual-md.png delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2023_HMC-FAIRFriday_vanReisen-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2023_HMC-Projects_Workshops_Secop@HMC-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2023_HMC_Projects@RDA-FAIR-DO-Fabric-IG--md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/2023_HMC_at_DESY-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/AI-HERO_flyer-(002)-md.png delete mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC-FAIRFriday_Bertuch_Garcia-Castro-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC-FAIRFriday_Britta-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC-FAIRFriday_huang-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC-HEC-2023-md.PNG delete mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC_Events_deRSE-20223-preview-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/HMC_TEACH-2-2022-lg-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/MC2023_Header-md.jpg delete mode 100644 survey_dashboard/hmc_layout/static/en_files/Marble_Workshop_visual-md.png delete mode 100644 survey_dashboard/hmc_layout/static/en_files/Partner-Website_900x675px-md.png delete mode 100644 survey_dashboard/hmc_layout/static/en_files/Teaser_M4R_20230327-md.PNG delete mode 100644 survey_dashboard/hmc_layout/static/en_files/Teaser_raw-md.PNG delete mode 100644 survey_dashboard/hmc_layout/static/en_files/visual_EMGlossary_2022-06_1-md.png diff --git a/survey_dashboard/hmc_layout/Banner.png b/survey_dashboard/hmc_layout/Banner.png new file mode 100644 index 0000000000000000000000000000000000000000..e79f97faad4495366b67babc4a70b6e6a976a14d GIT binary patch literal 285265 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

    9(ntPt@`e$Z$|Y|9L#4X_iox0pLiVm4 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 + + + + + + + + + + + + + + +{% endblock %} + + {% block inner_body %} +

    +
    +
    +
    + + +
    +
    + + +
    + + +
    +
    + +
    + + + + +
    +
    +
    + {% block contents %} +
    +
    + {% for doc in docs %} + {{ embed(doc) if doc.elementid }} + {% for root in doc.roots %} + {{ embed(root) | indent(10) }} + {% endfor %} + {% endfor %} + {{ plot_script | indent(8) }} +
    +
    +
    + {{ embed(roots.App) }} +
    +
    +
    + {% endblock %} +
    +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + {% endblock %} diff --git a/survey_dashboard/hmc_layout/en_template.html b/survey_dashboard/hmc_layout/en_template.html index 3c0d59e..9c04525 100644 --- a/survey_dashboard/hmc_layout/en_template.html +++ b/survey_dashboard/hmc_layout/en_template.html @@ -720,13 +720,9 @@ - - + @@ -799,10 +794,10 @@ -