change directory name and run scripts to account for name change

This commit is contained in:
ADParedes
2022-09-20 13:27:37 -05:00
parent 8829c31b7b
commit 3b8f5ee363
10242 changed files with 2 additions and 2027522 deletions

25
visualization_interface/.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/flask-server/__pycache__

View File

@@ -0,0 +1,70 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

View File

@@ -0,0 +1,203 @@
import pandas as pd
import json
from os.path import exists
def read_derivedAttr(ar):
derivedFilename = ar+"/derived_variables/derived_output.csv"
if not exists(derivedFilename):
return pd.DataFrame()
derived_df = pd.read_csv(derivedFilename)
facial_cols = [col for col in derived_df if "fac_" in col]
acoustic_cols = [col for col in derived_df if "aco_" in col]
movement_cols = [col for col in derived_df if "mov_" in col]
nlp_cols = [col for col in derived_df if "nlp_" in col]
derived_facial = derived_df.loc[:,derived_df.columns.isin(facial_cols)]
derived_acoustic = derived_df.loc[:,derived_df.columns.isin(acoustic_cols)]
derived_movement = derived_df.loc[:,derived_df.columns.isin(movement_cols)]
derived_nlp = derived_df.loc[:,derived_df.columns.isin(nlp_cols)]
ids = derived_df["Filename"].tolist()
return {"ids": ids,
"facialAttr": facial_cols, "acousticAttr": acoustic_cols, "movementAttr": movement_cols, "speechAttr": nlp_cols}
def read_medatada(ar, ar2):
metaData={}
metdataFilename = ar+"/"+ar2
if exists(metdataFilename):
metadataDf = pd.read_csv(metdataFilename)
metaData = metadataDf.to_json(orient="records")
return metaData
def read_derivedDf(ar):
derivedFilename = ar+"/derived_variables/derived_output.csv"
if not exists(derivedFilename):
return pd.DataFrame()
derived_df = pd.read_csv(derivedFilename)
return derived_df
def read_rawFacialDf(ar, id):
skip_cols = ["frame", "face_id", "error_reason", "timestamp", "confidence", "success", "dbm_master_url", "error_reason", " confidence", " face_id", " success", " timestamp", "s_confidence", ]
facial_asym_filename = ar + "/raw_variables/"+id+"/facial/face_asymmetry/"+id+"_facasym.csv"
facial_au_filename = ar + "/raw_variables/"+id+"/facial/face_au/"+id+"_facau.csv"
facial_expr_filename = ar + "/raw_variables/"+id+"/facial/face_expressivity/"+id+"_facemo.csv"
landmark_filename = ar + "/raw_variables/"+id+"/facial/face_landmark/"+id+"_faclmk.csv"
if not exists(facial_asym_filename) or not exists(facial_au_filename) or not exists(facial_expr_filename):
return pd.DataFrame()
facial_asym = pd.read_csv(facial_asym_filename)
facial_asym_cols = [col for col in facial_asym if col not in skip_cols]
facial_au = pd.read_csv(facial_au_filename)
facial_au_cols = [col for col in facial_au if col not in skip_cols]
facial_expr = pd.read_csv(facial_expr_filename)
facial_expr_cols = [col for col in facial_expr if col not in skip_cols and "AU" not in col]
landmark = pd.read_csv(landmark_filename)
landmark_cols = [col for col in landmark if col not in skip_cols]
face_df = landmark.loc[:, ~landmark.columns.isin(skip_cols)].copy()
for el in facial_expr_cols:
face_df[el] = facial_expr[el]
for el in facial_au_cols:
face_df[el] = facial_au[el]
for el in facial_asym_cols:
face_df[el] = facial_asym[el]
return face_df[face_df.columns[::-1]].fillna(0)
def read_rawMovementDf(ar, id):
skip_cols = ["error_reason", "dbm_master_url", "Frames", " Frames", "vid_dur", "fps" ]
gaze_filename = ar + "/raw_variables/"+id+"/movement/gaze/"+id+"_eyegaze.csv"
head_movement_filename = ar + "/raw_variables/"+id+"/movement/head_movement/"+id+"_headmov.csv"
head_pose_filename = ar + "/raw_variables/"+id+"/movement/head_pose/"+id+"_headpose.csv"
blinks_filename = ar + "/raw_variables/"+id+"/movement/eye_blink/"+id+"_eyeblinks.csv"
fac_tremor_filename = ar + "/raw_variables/"+id+"/movement/facial_tremor/"+id+"_fac_tremor.csv"
voice_tremor_filename = ar + "/raw_variables/"+id+"/movement/voice_tremor/"+id+"_vtremor.csv"
if not exists(gaze_filename) or not exists(head_movement_filename) or not exists(head_pose_filename):
return pd.DataFrame()
gaze = pd.read_csv(gaze_filename)
gaze_cols = [col for col in gaze if col not in skip_cols]
head_movement = pd.read_csv(head_movement_filename)
head_movement_cols = [col for col in head_movement if col not in skip_cols]
blinks = pd.read_csv(blinks_filename)
blinks_cols = [col for col in blinks if col not in skip_cols]
fac_tremor = pd.read_csv(fac_tremor_filename)
fac_tremor_cols = [col for col in fac_tremor if col not in skip_cols]
voice_tremor = pd.read_csv(voice_tremor_filename)
voice_tremor_cols = [col for col in voice_tremor if col not in skip_cols]
head_pose = pd.read_csv(head_pose_filename)
head_pose_cols = [col for col in head_pose if col not in skip_cols]
movement_df = head_pose.loc[:, ~head_pose.columns.isin(skip_cols)].copy()
for el in head_movement_cols:
movement_df[el] = head_movement[el]
for el in gaze_cols:
movement_df[el] = gaze[el]
for el in blinks_cols:
movement_df[el] = blinks[el]
for el in fac_tremor_cols:
movement_df[el] = fac_tremor[el]
for el in voice_tremor_cols:
movement_df[el] = voice_tremor[el]
return movement_df.fillna(0)
def read_rawAcousticDf(ar, id):
skip_cols = ["error_reason", "dbm_master_url", "Frames", " Frames", "aco_voicelabel"]
fm_filename = ar + "/raw_variables/"+id+"/acoustic/formant_freq/"+id+"_formant.csv"
gne_filename = ar + "/raw_variables/"+id+"/acoustic/glottal_noise/"+id+"_gne.csv"
hnr_filename = ar + "/raw_variables/"+id+"/acoustic/harmonic_noise/"+id+"_hnr.csv"
intt_filename = ar + "/raw_variables/"+id+"/acoustic/intensity/"+id+"_intensity.csv"
mfcc_filename = ar + "/raw_variables/"+id+"/acoustic/mfcc/"+id+"_mfcc.csv"
pitch_filename = ar + "/raw_variables/"+id+"/acoustic/pitch/"+id+"_pitch.csv"
jitter_filename = ar + "/raw_variables/"+id+"/acoustic/jitter/"+id+"_jitter.csv"
pause_segment_filename = ar + "/raw_variables/"+id+"/acoustic/pause_segment/"+id+"_pausechar.csv"
shimmer_filename = ar + "/raw_variables/"+id+"/acoustic/shimmer/"+id+"_shimmer.csv"
voice_frame_score_filename = ar + "/raw_variables/"+id+"/acoustic/voice_frame_score/"+id+"_voiceprev.csv"
filename_list = [fm_filename, gne_filename, hnr_filename, intt_filename,mfcc_filename, pitch_filename, pause_segment_filename, shimmer_filename, voice_frame_score_filename]
if not exists(fm_filename) or not exists(gne_filename) or not exists(hnr_filename) or not exists(intt_filename) or not exists(mfcc_filename) or not exists(pitch_filename):
return pd.DataFrame
fm = pd.read_csv(fm_filename)
fm_cols = [col for col in fm if col not in skip_cols]
gne = pd.read_csv(gne_filename)
gne_cols = [col for col in gne if col not in skip_cols]
hnr = pd.read_csv(hnr_filename)
hnr_cols = [col for col in hnr if col not in skip_cols]
intt = pd.read_csv(intt_filename)
intt_cols = [col for col in intt if col not in skip_cols]
mfcc = pd.read_csv(mfcc_filename)
mfcc_cols = [col for col in mfcc if col not in skip_cols]
pitch = pd.read_csv(pitch_filename)
pitch_cols = [col for col in pitch if col not in skip_cols]
pause_segment = pd.read_csv(pause_segment_filename)
pause_segment_cols = [col for col in pause_segment if col not in skip_cols]
jitter = pd.read_csv(jitter_filename)
jitter_cols = [col for col in jitter if col not in skip_cols]
shimmer = pd.read_csv(shimmer_filename)
shimmer_cols = [col for col in shimmer if col not in skip_cols]
voice_frame_score = pd.read_csv(voice_frame_score_filename)
voice_frame_score_cols = [col for col in voice_frame_score if col not in skip_cols]
acoustic_df = fm.loc[:, ~fm.columns.isin(skip_cols)].copy()
for el in gne_cols:
acoustic_df[el] = gne[el]
for el in hnr_cols:
acoustic_df[el] = hnr[el]
for el in intt_cols:
acoustic_df[el] = intt[el]
for el in mfcc_cols:
acoustic_df[el] = mfcc[el]
for el in pitch_cols:
acoustic_df[el] = pitch[el]
for el in pause_segment_cols:
acoustic_df[el] = pause_segment[el]
for el in jitter_cols:
acoustic_df[el] = jitter[el]
for el in shimmer_cols:
acoustic_df[el] = shimmer[el]
for el in voice_frame_score_cols:
acoustic_df[el] = voice_frame_score[el]
return acoustic_df.fillna(0)
def load():
global videoIds
read_derivedAttr()
read_derivedDf()
if __name__=="__main__":
load()

View File

@@ -0,0 +1,381 @@
from flask import Flask
from flask import request
import pandas as pd
import json
import numpy as np
from sklearn.decomposition import PCA
import sys
import process_input_data
app = Flask(__name__)
@app.route('/fetchIndividualFacialRawData', methods=["POST"])
def fetchIndividualFacialRawData():
id = request.json['id']
if id:
individualFacialRawData = process_input_data.read_rawFacialDf(sys.argv[1], id)
else:
individualFacialRawData = process_input_data.read_rawFacialDf(sys.argv[1], inputData['Filename'][0])
if individualFacialRawData.empty:
return {}
return individualFacialRawData.fillna(0).to_json(orient="records")
@app.route('/fetchIndividualMovementRawData', methods=["POST"])
def fetchIndividualMovementRawData():
id = request.json['id']
if id:
individualMovementRawData = process_input_data.read_rawMovementDf(sys.argv[1], id)
else:
individualMovementRawData = process_input_data.read_rawMovementDf(sys.argv[1], inputData['Filename'][0])
if individualMovementRawData.empty:
return {}
return individualMovementRawData.fillna(0).to_json(orient="records")
@app.route('/fetchIndividualAcousticRawData', methods=["POST"])
def fetchIndividualAcousticRawData():
id = request.json['id']
if id:
individualAcousticRawData = process_input_data.read_rawAcousticDf(sys.argv[1], id)
else:
individualAcousticRawData = process_input_data.read_rawAcousticDf(sys.argv[1], inputData['Filename'][0])
if individualAcousticRawData.empty:
return {}
return individualAcousticRawData.fillna(0).to_json(orient="records")
@app.route('/fetchIndividualDerivedData', methods=["POST"])
def fetchIndividualDerivedData():
if len(list(inputData.columns)) <2:
return {}
id = request.json['id']
if id:
res = inputData.loc[inputData['Filename'] == id, ~inputData.columns.isin(['Filename'])]
else:
res = inputData.iloc[:1, :].loc[:, ~inputData.columns.isin(['Filename'])]
return res.fillna(0).to_json(orient="records")
@app.route('/fetchIndividualFacialTimelineData', methods=["POST"])
def fetchIndividualFacialTimelineData():
id = request.json['id']
timepoints = 20
if id:
dfFace = process_input_data.read_rawFacialDf(sys.argv[1], id)
dfMovement = process_input_data.read_rawMovementDf(sys.argv[1], id)
else:
dfFace = process_input_data.read_rawFacialDf(sys.argv[1], inputData['Filename'][0])
dfMovement = process_input_data.read_rawMovementDf(sys.argv[1], inputData['Filename'][0])
if dfFace.empty:
return {}
dfFace=dfFace.fillna(0)
attrOfInterest= ["fac_angintsoft", "fac_feaintsoft", "fac_disintsoft", "fac_sadintsoft",
"fac_conintsoft", "fac_surintsoft", "fac_hapintsoft",
"fac_AU01int", "fac_AU02int", "fac_AU04int", "fac_AU05int", "fac_AU06int",
"fac_AU07int", "fac_AU09int", "fac_AU10int", "fac_AU12int","fac_AU14int",
"fac_AU15int", "fac_AU17int", "fac_AU20int", "fac_AU23int", "fac_AU25int",
"fac_AU26int", "fac_asymmaskcom", "fac_asymmaskeye", "fac_asymmaskeyebrow",
"fac_asymmaskmouth", "fac_paiintsoft", "fac_comintsoft",
"fac_comlowintsoft", "fac_comuppintsoft"]
timelineObject = {}
for a in attrOfInterest:
timelineObject[a] = []
seg = len(dfFace)//19
reminder = len(dfFace)%19
for t in range(0,timepoints):
for k in attrOfInterest:
if t <= reminder:
timelineObject[k].append(sum(list(dfFace[t*(seg + 1):(t+1)*(seg+1)][k]))/(seg+1))
else:
timelineObject[k].append(sum(list(dfFace[t*seg:(t+1)*seg][k]))/seg)
if dfMovement.empty:
return timelineObject
dfMovement=dfMovement.fillna(0)
movementAttr = ["mov_hposepitch", "mov_hposeyaw", "mov_hposeroll"]
for a in movementAttr:
timelineObject[a] = []
seg = len(dfMovement)//20
reminder = len(dfMovement)%20
for t in range(0,timepoints):
for k in movementAttr:
if t <= reminder:
timelineObject[k].append(sum(list(dfMovement[t*(seg + 1):(t+1)*(seg+1)][k]))/(seg+1))
else:
timelineObject[k].append(sum(list(dfMovement[t*seg:(t+1)*seg][k]))/seg)
return timelineObject
@app.route('/getRawAttributesAndIds')
def getRawAttributesAndIds():
result = {}
if not individualFacialRawData.empty:
result['facial'] = [x for x in list(individualFacialRawData.columns)]
else:
result['facial'] =[]
if not individualAcousticRawData.empty:
result['acoustic'] = [x for x in list(individualAcousticRawData.columns)]
else:
result['acoustic'] = []
if not individualMovementRawData.empty:
result['movement'] = [x for x in list(individualMovementRawData.columns)]
else:
result['movement'] = []
if len(rawDataArgs) > 0:
result['ids'] = rawDataArgs['ids']
else:
result['ids'] = []
return result
def individualCorrMatrixData(id, corrArgs):
individualFacialRawData = pd.DataFrame()
individualMovementRawData = pd.DataFrame()
individualAcousticRawData = pd.DataFrame()
if id:
if len([x for x in corrArgs if "fac_" in x])>0:
individualFacialRawData = process_input_data.read_rawFacialDf(sys.argv[1], id)
if len([x for x in corrArgs if "mov_" in x])>0:
individualMovementRawData = process_input_data.read_rawMovementDf(sys.argv[1], id)
if len([x for x in corrArgs if "aco_" in x])>0:
individualAcousticRawData = process_input_data.read_rawAcousticDf(sys.argv[1], id)
else:
if len([x for x in corrArgs if "fac_" in x])>0:
individualFacialRawData = process_input_data.read_rawFacialDf(sys.argv[1], inputData['Filename'][0])
if len([x for x in corrArgs if "mov_" in x])>0:
individualMovementRawData = process_input_data.read_rawMovementDf(sys.argv[1], inputData['Filename'][0])
if len(list(filter(lambda x : "aco_" in x or "tremor_median" in x or "fac_features_mean" in x or "fac_corr" in x, individualMovementRawData))) >0:
individualAcousticRawData = process_input_data.read_rawAcousticDf(sys.argv[1], inputData['Filename'][0])
f = individualFacialRawData.loc[:, individualFacialRawData.columns.isin(corrArgs)]
m = individualMovementRawData.loc[:, individualMovementRawData.columns.isin(corrArgs)]
a = individualAcousticRawData.loc[:, individualAcousticRawData.columns.isin(corrArgs)]
if f.empty:
f = pd.DataFrame()
if m.empty:
m = pd.DataFrame()
if a.empty:
a = pd.DataFrame()
min_len = float('inf')
if not f.empty:
min_len = len(f)
if not m.empty:
min_len = min(min_len, len(m))
if not a.empty:
min_len=min(min_len, len(a))
if min_len == float('inf'):
min_len = 0
if min_len == len(f):
all_df = f.copy()
if len(f) == len(m):
for x in m.columns:
all_df[x] = m[x]
else:
seg = int(len(m)/(max(len(f),1)))
reminder = len(m)%(max(len(f),1))
for i, row in all_df.iterrows():
for x in m.columns:
if i <reminder:
all_df.loc[i,x] = sum(list(m[i*(seg + 1):(i+1)*(seg+1)][x]))/(seg+1)
else:
all_df.loc[i,x] = sum(list(m[i*seg:(i+1)*seg][x]))/seg
if len(a) > len(f):
seg = int(len(a)/(max(len(f),1)))
reminder = len(a)%(max(len(f),1))
for i, row in all_df.iterrows():
for x in a.columns:
if i <reminder:
all_df.loc[i,x] = sum(list(a[i*(seg + 1):(i+1)*(seg+1)][x]))/(seg+1)
else:
all_df.loc[i,x] = sum(list(a[i*seg:(i+1)*seg][x]))/seg
else:
for x in a.columns:
all_df[x] = a[x]
elif len(m) == min_len:
all_df = m.copy()
seg = int(len(f)/(max(len(m),1)))
reminder = len(f)%(max(len(m),1))
for i, row in all_df.iterrows():
for x in f.columns:
if i <reminder:
all_df.loc[i,x] = sum(list(f[i*(seg + 1):(i+1)*(seg+1)][x]))/(seg+1)
else:
all_df.loc[i,x] = sum(list(f[i*seg:(i+1)*seg][x]))/seg
if len(a) > len(m):
seg = int(len(a)/(max(len(m),1)))
reminder = len(a)%(max(len(m),1))
for i, row in all_df.iterrows():
for x in a.columns:
if i <reminder:
all_df.loc[i,x] = sum(list(a[i*(seg + 1):(i+1)*(seg+1)][x]))/(seg+1)
else:
all_df.loc[i,x] = sum(list(a[i*seg:(i+1)*seg][x]))/seg
else:
for x in a.columns:
all_df[x] = a[x]
else:
all_df = a.copy()
seg = int(len(f)/(max(len(a),1)))
reminder = len(f)%(max(len(a),1))
for i, row in all_df.iterrows():
for x in f.columns:
if i <reminder:
all_df.loc[i,x] = sum(list(f[i*(seg + 1):(i+1)*(seg+1)][x]))/(seg+1)
else:
all_df.loc[i,x] = sum(list(f[i*seg:(i+1)*seg][x]))/seg
seg = int(len(m)/(max(len(a),1)))
reminder = len(m)%(max(len(a),1))
for i, row in all_df.iterrows():
for x in m.columns:
if i <reminder:
all_df.loc[i,x] = sum(list(m[i*(seg + 1):(i+1)*(seg+1)][x]))/(seg+1)
else:
all_df.loc[i,x] = sum(list(m[i*seg:(i+1)*seg][x]))/seg
return all_df.fillna(0)
@app.route("/performPCA")
def performPCA(PCAargs=[]):
if len(list(inputData.columns)) <2:
return {}
pca = PCA()
if len(PCAargs) == 0:
X = pd.DataFrame(columns = [x for x in list(inputData.columns) if x not in ['Filename']])
X = inputData.loc[:, ~inputData.columns.isin(['Filename'])]
else:
X = pd.DataFrame(columns = [x for x in PCAargs ])
X = inputData.loc[:, inputData.columns.isin(PCAargs)]
X = X.fillna(0)
x_pca = pca.fit_transform(X)
x_pca = pd.DataFrame(x_pca)
x_pca = x_pca.iloc[:, list(range(2))]
x_pca.columns = ['PC1', "PC2"]
pc1 = x_pca["PC1"].to_numpy()
pc2 = x_pca["PC2"].to_numpy()
pc1 = list((pc1 -pc1.mean())/np.std(pc1))
pc2 = list((pc2 -pc2.mean())/np.std(pc2))
pca_results =[[pc1[i], pc2[i]] for i in range(0, len(pc1))]
return dict(zip(inputData['Filename'], pca_results))
@app.route('/updatePCA', methods=["POST"])
def updatePCA():
PCAargs = request.json['PCA_args']
return performPCA(PCAargs)
@app.route('/defaultDistribution', methods=["POST"])
def defaultDistribution():
args = request.json['distributionArgs']
return {"data":updateDistribution(args)}
@app.route('/updateDistribution', methods=["POST"])
def updateDistribution(attr=[]):
if len(list(inputData.columns)) <2:
return {}
if len(attr) !=0:
distributionArgs = attr
else:
distributionArgs = request.json['distributionArgs']
if len(distributionArgs) == 0:
df = pd.DataFrame(columns = [x for x in list(inputData.columns) if x not in ['Filename']])
df = inputData.loc[:, ~inputData.columns.isin(['Filename'])]
else:
cols = ['Filename']+ distributionArgs
df = pd.DataFrame(columns = [c for c in list(cols )])
df = inputData.loc[:, inputData.columns.isin(cols)]
df = df.fillna(0)
if len(distributionArgs) ==0:
return {}
result = {}
for i, row in df.iterrows():
result[row['Filename']]={}
result[row['Filename']]['id'] = row['Filename']
for attr in distributionArgs:
result[row['Filename']][attr] = row[attr]
return result
@app.route('/defaultCorrMatrix', methods=["POST"])
def defaultCorrMatrix():
corrMatrixArgs = request.json['corrMatrix_args']
individual_corr = request.json['individual']
id= request.json['id']
return updateCorrMatrix(corrMatrixArgs, individual_corr, id)
@app.route('/updateCorrMatrix', methods=["POST"])
def updateCorrMatrix(attr=[], individual = False, id=None):
if len(attr) !=0:
corrMatrixArgs = attr
else:
corrMatrixArgs = request.json['corrMatrix_args']
individual = request.json['individual']
if len(corrMatrixArgs) < 2:
return {}
if len(corrMatrixArgs) > 24:
return{}
df = pd.DataFrame(columns = [c for c in corrMatrixArgs])
if individual:
if not id:
id = request.json['id']
d = individualCorrMatrixData(id, corrMatrixArgs)
df = d
# df = d.loc[:, d.columns.isin(corrMatrixArgs)]
else:
df = inputData.loc[:, inputData.columns.isin(corrMatrixArgs)]
corrMatrix = df.corr(method="spearman").fillna(0)
return corrMatrix.to_dict()
@app.route("/getDerivedAttributes")
def getDerivedAttributes():
if not len(rawDataArgs):
return {"facial": [], "acoustic":[], "movement":[], "speech": [], "ids": []}
res={"facial": rawDataArgs['facialAttr'], "acoustic": rawDataArgs['acousticAttr'], "movement": rawDataArgs['movementAttr'],
"speech": rawDataArgs["speechAttr"], "ids": rawDataArgs['ids']}
return res
@app.route("/getMetadata")
def getMetadata():
if not len(metadata):
return {}
return metadata
def load():
global rawDataArgs, metadata
rawDataArgs = process_input_data.read_derivedAttr(sys.argv[1])
if len(sys.argv) >2:
metadata = process_input_data.read_medatada(sys.argv[1], sys.argv[2])
else:
metadata=[]
global inputData
inputData = process_input_data.read_derivedDf(sys.argv[1])
if len(inputData):
inputData['Filename'] = inputData['Filename'].apply(lambda x: x.split('/')[1].replace(".mp4", ""))
else:
inputData['Filename'] = [""]
global individualFacialRawData, individualDerivedData, individualAcousticRawData, individualMovementRawData
individualFacialRawData = process_input_data.read_rawFacialDf(sys.argv[1], inputData['Filename'][0])
individualMovementRawData = process_input_data.read_rawMovementDf(sys.argv[1], inputData['Filename'][0])
individualAcousticRawData = process_input_data.read_rawAcousticDf(sys.argv[1], inputData['Filename'][0])
if __name__=="__main__":
# from waitress import serve
load()
# serve(app, host='127.0.0.1', port=5000)
app.run(debug=True)
# app.run(debug=False)

11729
visualization_interface/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
{
"name": "dashboard",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:5000",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"bootstrap": "^5.1.3",
"bootstrap-icons": "^1.8.3",
"d3": "^7.6.1",
"d3-axis": "^3.0.0",
"d3-collection": "^1.0.7",
"d3-ez": "^3.3.15",
"d3-scale-chromatic": "^3.0.0",
"d3-simple-slider": "^1.10.4",
"html2canvas": "^1.4.1",
"jquery": "^3.6.0",
"react": "^18.2.0",
"react-bootstrap": "^2.4.0",
"react-component-export-image": "^1.0.6",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"react-to-print": "^2.14.7",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>OpenDBM Dashboard</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@@ -0,0 +1,39 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@@ -0,0 +1,49 @@
import React, { useState, useEffect } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Row, Col } from 'react-bootstrap';
import CohortPanel from './components/cohortPanel';
import IndividualPanel from './components/individualPanel';
function App() {
const [cohortButton, setCohortButton] = useState(true)
const [individualButton, setIndividualButton] = useState(false)
const handleView = param => {
setCohortButton(param === "cohort" ? true : false)
setIndividualButton(param !== "cohort" ? true : false)
}
return (
<div className='App' style={{ fontFamily: "monospace", width: "max-content", height: "max-content" }}>
<Row style={{}}>
<Col className="col-2" style={{ width: "90vh", height: "max-content", marginTop: "10px", marginBottom: "10px", display: "inline-flex", flexDirection: "row", gap: "3px" }}>
<div style={{ display: "inline-flex", flexDirection: "row", gap: "10px", marginLeft: "10px" }}>
<button
type="button"
className={`btn btn-sm ${cohortButton === true ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => handleView("cohort")}
id="cohortViewButton"
style={{ height: "max-content", }}>
Cohort
</button>
<button
type="button"
className={`btn btn-sm ${individualButton === true ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => handleView("individual")}
style={{ height: "max-content", }}
id="individualViewButton">
Individual
</button>
</div>
</Col>
</Row>
{cohortButton ? <CohortPanel /> : <IndividualPanel />}
</div>
);
}
export default App;

View File

@@ -0,0 +1,16 @@
import { cleanup, render, screen } from '@testing-library/react';
import App from './App';
afterEach(cleanup)
test('render cohort panel', () => {
render(<App />);
const cohortElem = screen.getByText("Cohort");
expect(cohortElem).toHaveClass("btn-primary");
});
test('render individual panel', () => {
render(<App />);
const individualElem = screen.getByText("Individual");
expect(individualElem).toHaveClass("btn-secondary");
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
import { cleanup, render, screen } from '@testing-library/react';
import IndividualPanel from './components/individualPanel'
afterEach(cleanup)
test('render Individual components', () => {
render(<IndividualPanel/>)
const individualElem = screen.getByText("Asym");
expect(individualElem).toBeVisible();
});

View File

@@ -0,0 +1,148 @@
#metaDataButton {
height: 5%;
position: absolute;
top: 10px;
left: 250px;
display: flex;
flex-direction: row;
}
#attributesTag {
margin-left: 5px;
font-weight: bolder;
}
#metadataAttributes {
display: flex;
flex-direction: row;
justify-content: flex-end;
margin: auto;
margin-left: 20px;
opacity: 0;
color: #000;
}
.metadataAttr {
display: flex;
flex-direction: row;
opacity: 0;
margin-left: 20px;
}
.metadataAttr div {
margin-left: 10px;
font-weight: bolder;
}
#cohortContainer {
height: 90vh;
width: 95vw;
margin-left: 10px;
}
#queryPanelContainer {
border-radius: 15px;
padding-top: 10px;
width: max-content;
height: 98%;
box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
}
#queryPanelButtonsContainer {
display: inline-flex;
flex-direction: row;
gap: 3px;
margin-bottom: 10px;
}
#queryListContainer {
font-size: 0.8rem;
height: 90%;
}
.queryList {
height: 95%;
}
.queryUpdateButton {
display: inline-flex;
flex-direction: row;
}
.queryCheckboxContainer {
overflow-y: scroll;
height: 97%;
}
#scatterplotCorrMatrixContainer {
height: 95%;
width: 35%;
}
#scatterplotContainer {
height: 35vh;
}
#corrMatrixContainer {
height: 50%;
margin-top: 10%;
font-size: 0.8rem;
}
#idPanelContainer {
display: flex;
flex-direction: column;
border-radius: 15px;
height: 98%;
padding-top: 10px;
font-size: 0.7rem;
width: max-content;
box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
}
#hideIdButton {
margin-top: 10px
}
#idListContainer {
display: inline-flex;
flex-direction: column;
margin-top: 10px;
height: 94%;
overflow-y: auto;
}
#distributionChartContainer {
height: 93%;
overflow-y: auto;
}
h6 {
font-weight: bolder;
}
.distributionChart {
display: inline-flex;
flex-direction: row;
margin-left: 10px;
}
.printButton {
width: max-content;
}
#scatterplotComponent {
display: flex;
justify-content: space-between;
}
#corrMatrixComponent {
display: flex;
justify-content: space-between;
}
.queryPanel h6 {
margin-bottom: 10px;
}

View File

@@ -0,0 +1,381 @@
import React, { useState, useEffect } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Row, Col } from 'react-bootstrap';
import Scatterplot from './scatterplot'
import DistributionChart from './distributionChart';
import CorrelationMatrix from './correlationMatrix';
import QueryPanel from './queryPanel';
import $ from 'jquery';
import "./cohortPanel.css"
function CohortPanel() {
const [distrData, setDistrData] = useState([])
const [corrMatrixData, setCorrMatrixData] = useState([])
const [pcaCoords, setPcaCoords] = useState([])
const distrDefaultArgs = ['fac_asymmaskmouth_mean', 'fac_asymmaskeyebrow_mean', 'fac_asymmaskeye_mean', 'fac_asymmaskcom_mean']
const [distrArgs, setDistrArgs] = useState(distrDefaultArgs)
const [checkedDistrState, setCheckedDistrState] = useState([]);
const [checkedPCAState, setCheckedPCAState] = useState([]);
const [checkedCorrMatrixState, setCheckedCorrMatrixState] = useState([]);
const [idData, setIdData] = useState([])
const [filteredIdData, setFilteredIdData] = useState([])
const [checkedHideIdsState, setCheckedHideIdsState] = useState(false)
const [pcaButton, setPcaButton] = useState(true)
const [metadataButton, setMetadataButton] = useState(false)
const [distrButton, setDistrButton] = useState(false)
const [corrMatrixButton, setCorrMatrixButton] = useState(false)
const [allFacialArg, setAllFacialArg] = useState([])
const [allAcousticArg, setAllAcousticArg] = useState([])
const [allMovementArg, setAllMovementArg] = useState([])
const [allSpeechArg, setAllSpeechArg] = useState([])
const [allDBMArg, setAllDBMArg] = useState([])
const [metadata, setMetadata] = useState([])
const metadataColorList = ['none', 'gold', 'blue', 'pink', 'darkviolet', 'cyan', 'black', 'brown', 'greenyellow', 'orchid','mediumpurple']
const [metadataAttrColor, setMetadataAttrColor] = useState({})
useEffect(() => {
fetch("/getDerivedAttributes").then(
res => res.json()
).then(
data => {
setAllFacialArg(data['facial'])
setAllAcousticArg(data['acoustic'])
setAllMovementArg(data['movement'])
setAllSpeechArg(data['speech'])
setAllDBMArg([...data['facial'], ...data['movement'], ...data['acoustic'], ...data['speech']])
setIdData(data['ids'].map(el => el.split("/")[1].replace(".mp4", "")))
setCheckedCorrMatrixState(new Array([...data['facial'], ...data['movement'], ...data['acoustic'], ...data['speech']].length).fill(false))
setCheckedDistrState(new Array([...data['facial'], ...data['movement'], ...data['acoustic'], ...data['speech']].length).fill(false))
setCheckedPCAState(new Array([...data['facial'], ...data['movement'], ...data['acoustic'], ...data['speech']].length).fill(false))
}
)
}, [])
useEffect(() => {
fetch("/getMetadata").then(
res => res.json()
).then(
data => {
var attr = [...new Set(Object.values(data).map(el => el['attr']).filter(e => e != null))]
if (attr.length <= metadataColorList.length - 1) {
setMetadata(data)
var metaColorData = {}
var attrDict = {}
attr.forEach((e, i) => {
attrDict[e] = metadataColorList[i + 1]
})
if (data.length > 0)
data.forEach(e => {
if (e['attr']) {
metaColorData[e['id']] = attrDict[e['attr']]
}
})
}
setMetadataAttrColor(metaColorData)
}
)
}, [])
useEffect(() => {
fetch("/performPCA").then(
res => res.json()
).then(
data => {
setPcaCoords(data)
}
)
}, [])
useEffect(() => {
fetch("/defaultDistribution", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"distributionArgs": distrArgs
})
}).then(
res => res.json()
).then(
data => {
setDistrData(data['data'])
}
)
}, [])
useEffect(() => {
fetch("/defaultCorrMatrix", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"corrMatrix_args": distrDefaultArgs,
"individual": false,
"id": null
})
}).then(
res => res.json()
).then(
data => {
setCorrMatrixData(data)
}
)
}, [])
const handlePCAUpdate = () => {
var PCA_args = allDBMArg.filter((el, index) => checkedPCAState[index] === true)
if (PCA_args.length !== 1) {
fetch("/updatePCA", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"PCA_args": PCA_args
})
}).then(
res => res.json()
).then(
data => {
setPcaCoords(data)
}
)
}
}
const handlePCACheckboxChange = position => {
const updatedCheckedState = checkedPCAState.map((item, index) =>
index === position ? !item : item)
setCheckedPCAState(updatedCheckedState)
}
const handleDistrUpdate = attr => {
var args = allDBMArg.filter((el, index) => checkedDistrState[index] === true)
if (args.length === 0 & attr.length > 0) {
args = attr
}
fetch("/updateDistribution", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"distributionArgs": args
})
}).then(
res => res.json()
).then(
data => {
setDistrData(data)
setDistrArgs(args)
}
)
}
const handleDistrCheckboxChange = position => {
const updatedCheckedState = checkedDistrState.map((item, index) =>
index === position ? !item : item)
setCheckedDistrState(updatedCheckedState)
}
const handleCorrMatrixUpdate = () => {
var corrMatrix_args = allDBMArg.filter((el, index) => checkedCorrMatrixState[index] === true)
fetch("/updateCorrMatrix", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"corrMatrix_args": corrMatrix_args,
"individual": false,
"id": null
})
}).then(
res => res.json()
).then(
data => {
setCorrMatrixData(data)
}
)
}
const handleCorrMatrixCheckboxChange = position => {
const updatedCheckedState = checkedCorrMatrixState.map((item, index) =>
index === position ? !item : item)
setCheckedCorrMatrixState(updatedCheckedState)
}
const handlePanel = param => {
setDistrButton(param === "Distr" ? true : false)
setCorrMatrixButton(param === "Corr" ? true : false)
setPcaButton(param === "PCA" ? true : false)
}
const handleIdCheckboxChange = ev => {
$('#' + ev.target.id).is(":checked") ?
$(".dot_" + ev.target.id.replace("_id", "")).css("fill", "darkorange") :
$(".dot_" + ev.target.id.replace("_id", "")).css("fill",
metadataButton
? metadataAttrColor[ev.target.id.replace("_id", "")] : "#41ab5d")
var filteredIdData_aux = idData.filter(id =>
$('#' + id + "_id").is(":checked"))
setFilteredIdData(filteredIdData_aux)
}
const handleMetadata = () => {
setMetadataButton(!metadataButton)
handlePCAUpdate()
handleDistrUpdate(distrDefaultArgs)
}
const handleHideIds = () => {
if (checkedHideIdsState) {
$('.dot').css("opacity", "0.4")
}
else {
$('.dot').css("opacity", "0")
filteredIdData.forEach(id => {
$('.dot_' + id).css("opacity", "0.4")
})
}
setCheckedHideIdsState(!checkedHideIdsState)
}
const handleUnselectCheckboxes = () => {
const updatedCheckboxes = checkedCorrMatrixState.map(e => false)
if (distrButton) setCheckedDistrState(updatedCheckboxes)
else if (pcaButton) setCheckedPCAState(updatedCheckboxes)
else setCheckedCorrMatrixState(updatedCheckboxes)
}
return (
<div>
{metadata && metadata.length > 0 &&
<div id="metaDataButton">
<div>
<button type="button" className={`btn btn-sm ${metadataButton === true ? 'btn-outline-primary' : 'btn-outline-secondary'}`}
id="metadataButton" onClick={handleMetadata}>Extra Attributes</button>
</div>
<div id="metadataAttributes">
{metadataColorList.map((value, index) =>
<div id={value + "AttrContainer"} key={value + "color"} className="metadataAttr">
<svg height="14" width="14" transform="translate(3,3)" id={value + "circle"}>
<circle cx="7" cy="7" r="50%" fill={value === "none" ? "#41ab5d" : value} />
</svg>
<div
id={(value !== "none" ? value : "none") + "color"}>{value === "none" ? "None" : ""}</div>
</div>
)}
</div>
</div>
}
{allDBMArg.length > 0 &&
<Row id="cohortContainer">
<Col className="col-2" id="queryPanelContainer">
<div id="queryPanelButtonsContainer">
<button type="button" className={`btn btn-sm ${pcaButton === true ? 'btn-primary' : 'btn-secondary'}`} onClick={() => handlePanel("PCA")}>PCA</button>
<button type="button" className={`btn btn-sm ${distrButton === true ? 'btn-primary' : 'btn-secondary'}`} onClick={() => handlePanel("Distr")}>Distr</button>
<button type="button" className={`btn btn-sm ${corrMatrixButton === true ? 'btn-primary' : 'btn-secondary'}`} onClick={() => handlePanel("Corr")}>Corr</button>
<button type="button" className="btn-close" aria-label="Close" style={{ marginTop: "5px" }} onClick={handleUnselectCheckboxes}></button>
</div>
<div id="queryListContainer">
{pcaButton &&
<div className='queryList'>
<div className="queryUpdateButton">
<button type="button" className='btn btn-sm btn-outline-primary' onClick={handlePCAUpdate}>Update PCA</button>
</div>
<div className="queryCheckboxContainer">
<QueryPanel allAcousticArg={allAcousticArg} allMovementArg={allMovementArg} allSpeechArg={allSpeechArg} allFacialArg={allFacialArg}
checkedState={checkedPCAState} handleCheckboxChange={handlePCACheckboxChange} idVal={"pcaInputArg_"} />
</div>
</div>
}
{distrButton &&
<div className='queryList'>
<div className="queryUpdateButton">
<button type="button" className='btn btn-sm btn-outline-primary' onClick={handleDistrUpdate}>Update Distributions</button>
</div>
<div className="queryCheckboxContainer">
<QueryPanel allAcousticArg={allAcousticArg} allMovementArg={allMovementArg} allSpeechArg={allSpeechArg} allFacialArg={allFacialArg}
checkedState={checkedDistrState} handleCheckboxChange={handleDistrCheckboxChange} idVal={"distributionInputArg_"} />
</div>
</div>
}
{corrMatrixButton &&
<div className='queryList'>
<div className="queryUpdateButton">
<button type="button" className='btn btn-sm btn-outline-primary' onClick={handleCorrMatrixUpdate}>Update Correlations</button>
</div>
<div className="queryCheckboxContainer">
<QueryPanel allAcousticArg={allAcousticArg} allMovementArg={allMovementArg} allSpeechArg={allSpeechArg} allFacialArg={allFacialArg}
checkedState={checkedCorrMatrixState} handleCheckboxChange={handleCorrMatrixCheckboxChange} idVal={"corrmatrixInputArg_"} />
</div>
</div>
}
</div>
</Col>
<Col className="col-4" id="scatterplotCorrMatrixContainer">
<Row id="scatterplotContainer">
{pcaCoords && <Scatterplot data={pcaCoords} filteredIds={filteredIdData}
metadata={metadataButton === true ? metadata : []} hideIds={checkedHideIdsState} metadataAttrColor={metadataAttrColor} />}
</Row>
<Row id="corrMatrixContainer">
<CorrelationMatrix data={corrMatrixData} parentId={"corrMatrixContainer"} />
</Row>
</Col>
<Col id="idPanelContainer" className="col-2">
<button type="button" className='btn btn-primary btn-sm'>Filter ID(s)</button>
<button type="button" className={`btn ${checkedHideIdsState === true ? 'btn-primary' : 'btn-outline-primary'} btn-sm`}
id="hideIdButton" onClick={handleHideIds}>Hide Unselected</button>
<div id="idListContainer">
{idData.map((value, index) =>
<label className="id_checkbox_container" id={value + "_id_container"} key={value + "id"}>
<input
type="checkbox"
id={value + "_id"}
onChange={handleIdCheckboxChange}
/>
{" " + value}
</label>
)}
</div>
</Col>
<Col id="distributionChartContainer">
<h6>Attribute Distribution</h6>
{distrArgs.map((value) =>
<div className="distributionChart" key={value + "distr"}>
{<DistributionChart data={distrData} attr={value} id={"distributionChart_" + value}
filteredIds={filteredIdData} metadata={metadataButton === true ? metadata : []}
hideIds={checkedHideIdsState} metadataAttrColor={metadataAttrColor} />}
</div>
)
}
</Col>
</Row>
}
</div>
);
}
export default CohortPanel;

View File

@@ -0,0 +1,114 @@
import * as d3 from 'd3';
import $ from "jquery";
import "../index.css"
export default class ColorLegendD3 {
constructor(chart, range, colorScale, derivedData, id, timelineData, timeframe) {
if (!d3.select(chart).select('svg').empty()) {
d3.select(chart).select('svg').remove()
}
var parentContainer = document.getElementById("colorScaleContainer")
var width = parentContainer.clientWidth
var height = parentContainer.clientHeight
const svg = d3.select(chart)
.append('svg')
.attr('width', width)
.attr('height', height)
.attr("id", id)
if (!derivedData) {
return
}
const asymetryColorScale = d3.scaleLinear().domain([0, 40]).range(["white", '#de77ae'])
const painColorScale = d3.scaleLinear().domain([0, 1]).range(["white", 'red'])
const expressivityColorScale = d3.scaleLinear().domain([0, 1]).range(["white", 'orange'])
const AUsColorScale = d3.scaleLinear().domain([0, 5]).range(["white", '#cb181d'])
const negMovementColorScale = d3.scaleLinear().domain([-3.14, 0]).range(['#cb181d', "#fff5f0"])
const posMovementColorScale = d3.scaleLinear().domain([0, 3.14]).range(["#fff5f0", '#cb181d'])
const vissibleAUs = ['AU01', 'AU02', 'AU04', 'AU05', 'AU06', 'AU07', 'AU09', 'AU10', 'AU12', 'AU14', 'AU15', 'AU17', 'AU20', 'AU23', 'AU25', 'AU26']
if (parseInt(timeframe) === 0) {
vissibleAUs.forEach(a => {
var el = "fac_" + a + "int"
$('.' + a).css("stroke", AUsColorScale(derivedData[el + "_mean"]))
})
$('#faceAsymetryMask').css("fill", asymetryColorScale(derivedData['fac_asymmaskcom_mean']))
$('#leftEyeAsymetryMask').css("fill", asymetryColorScale(derivedData['fac_asymmaskeye_mean']))
$('#rightEyeAsymetryMask').css("fill", asymetryColorScale(derivedData['fac_asymmaskeye_mean']))
$('#leftEyebrowAsymetryMask').css("fill", asymetryColorScale(derivedData['fac_asymmaskeyebrow_mean']))
$('#rightEyebrowAsymetryMask').css("fill", asymetryColorScale(derivedData['fac_asymmaskeyebrow_mean']))
$('#mouthAsymetryMask').css("fill", asymetryColorScale(derivedData['fac_asymmaskmouth_mean']))
$('#faceExpresivityMask').css("fill", expressivityColorScale(derivedData["fac_comintsoft_mean"]))
$('#upperFaceExpresivityMask').css("fill", expressivityColorScale(derivedData["fac_comuppintsoft_mean"]))
$('#lowerFaceExpresivityMask').css("fill", expressivityColorScale(derivedData["fac_comlowintsoft_mean"]))
$('#facePainExpresivityMask').css("fill", painColorScale(derivedData["fac_paiintsoft_mean"]))
$('#pitchUp').css("stroke", posMovementColorScale(derivedData["mov_hposepitch_mean"]))
$('#pitchDown').css("stroke", negMovementColorScale(derivedData["mov_hposepitch_mean"]))
$('#rollRight').css("stroke", negMovementColorScale(derivedData["mov_hposeroll_mean"]))
$('#rollLeft').css("stroke", posMovementColorScale(derivedData["mov_hposeroll_mean"]))
$('#yawLeft').css("stroke", posMovementColorScale(derivedData["mov_hposeyaw_mean"]))
$('#yawRight').css("stroke", negMovementColorScale(derivedData["mov_hposeyaw_mean"]))
}
else {
vissibleAUs.forEach(a => {
var el = "fac_" + a + "int"
$('.' + a).css("stroke", AUsColorScale(timelineData[el][parseInt(timeframe) - 1]))
})
if (timelineData["mov_hposeyaw"]) {
$('#pitchUp').css("stroke", posMovementColorScale(timelineData["mov_hposepitch"][parseInt(timeframe) - 1]))
$('#pitchDown').css("stroke", negMovementColorScale(timelineData["mov_hposepitch"][parseInt(timeframe) - 1]))
$('#rollRight').css("stroke", negMovementColorScale(timelineData["mov_hposeroll"][parseInt(timeframe) - 1]))
$('#rollLeft').css("stroke", posMovementColorScale(timelineData["mov_hposeroll"][parseInt(timeframe) - 1]))
$('#yawLeft').css("stroke", posMovementColorScale(timelineData["mov_hposeyaw"][parseInt(timeframe) - 1]))
$('#yawRight').css("stroke", negMovementColorScale(timelineData["mov_hposeyaw"][parseInt(timeframe) - 1]))
}
$('#faceAsymetryMask').css("fill", asymetryColorScale(timelineData['fac_asymmaskcom'][parseInt(timeframe) - 1]))
$('#leftEyeAsymetryMask').css("fill", asymetryColorScale(timelineData['fac_asymmaskeye'][parseInt(timeframe) - 1]))
$('#rightEyeAsymetryMask').css("fill", asymetryColorScale(timelineData['fac_asymmaskeye'][parseInt(timeframe) - 1]))
$('#leftEyebrowAsymetryMask').css("fill", asymetryColorScale(timelineData['fac_asymmaskeyebrow'][parseInt(timeframe) - 1]))
$('#rightEyebrowAsymetryMask').css("fill", asymetryColorScale(timelineData['fac_asymmaskeyebrow'][parseInt(timeframe) - 1]))
$('#mouthAsymetryMask').css("fill", asymetryColorScale(timelineData['fac_asymmaskmouth'][parseInt(timeframe) - 1]))
$('#faceExpresivityMask').css("fill", expressivityColorScale(timelineData["fac_comintsoft"][parseInt(timeframe) - 1]))
$('#upperFaceExpresivityMask').css("fill", expressivityColorScale(timelineData["fac_comuppintsoft"][parseInt(timeframe) - 1]))
$('#lowerFaceExpresivityMask').css("fill", expressivityColorScale(timelineData["fac_comlowintsoft"][parseInt(timeframe) - 1]))
$('#facePainExpresivityMask').css("fill", painColorScale(timelineData["fac_paiintsoft"][parseInt(timeframe) - 1]))
}
var linearGradient = svg.append("linearGradient")
.attr("id", "linear-gradient")
linearGradient
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "100%")
.attr("y2", "0%");
linearGradient.append("stop")
.attr("offset", "0%")
.attr("stop-color", colorScale[0])
linearGradient.append("stop")
.attr("offset", "100%")
.attr("stop-color", colorScale[1])
const colorLegendHeight = height - 10
svg.append("rect")
.attr("width", width - 30)
.attr("height", colorLegendHeight)
.style("fill", "url(#linear-gradient)")
.attr("x", 10)
.attr("y", colorLegendHeight - 20)
svg.append("text")
.text(range[0])
.attr("x", 1)
.attr("y", height - 10)
svg.append("text")
.text(range[1])
.attr("x", width - 17)
.attr("y", height - 10)
}
}

View File

@@ -0,0 +1,19 @@
import React, { useRef, useEffect } from 'react';
import ColorLegendD3 from './colorLegend.d3';
const ColorLegend = ({ range, colorScale, derivedData, id, timelineData, timeframe }) => {
const ref = useRef(null)
useEffect(() => {
const currElement = ref.current
if (range) {
new ColorLegendD3(currElement, range, colorScale, derivedData, id, timelineData, timeframe)
}
}, [range, colorScale, derivedData, timelineData, timeframe])
return (
<div ref={ref}></div>
)
}
export default ColorLegend;

View File

@@ -0,0 +1,7 @@
import React from 'react';
export const ComponentToPrint = React.forwardRef((props, ref) => {
return (
<div ref={ref}></div>
);
});

View File

@@ -0,0 +1,117 @@
import * as d3 from 'd3';
import $ from "jquery";
import "../index.css"
import DBMDict from '../DBM_attribute_dict.json'
export default class CorrelationMatrixD3 {
constructor(chart, data, parentId) {
if (!d3.select(chart).select('svg').empty()) {
d3.select(chart).select('svg').remove()
}
var parentContainer = document.getElementById(parentId)
var width = parentContainer.clientWidth
var height = parentContainer.clientHeight
const svg = d3.select(chart)
.append('svg')
.attr('width', width)
.attr('height', height)
var values = Object.values(data).map(el => Object.values(el))
var keys = Object.keys(data).map(e => DBMDict[e]['label'])
const minRowValue = values.map(el => Math.min(...(el)))
const maxRowValue = values.map(el => Math.max(...(el)))
const minValue = Math.min(...(minRowValue))
const maxValue = Math.max(...(maxRowValue))
const colorScale = d3.scaleLinear().domain([minValue, 0, maxValue]).range(["#c34e4c", "#f2f2f2", "#4476a9"])
var xScale = d3.scaleBand()
.domain(keys).range([80, width - 20])
.paddingInner(.2)
var yScale = d3.scaleBand()
.domain(keys).range([height - 80, 10])
.paddingInner(.2)
svg.append("g")
.attr("transform", "translate(0," + (height - 70) + ")")
.attr("class", "greyAxis")
.call(d3.axisBottom(xScale))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)")
svg.append("g")
.attr("transform", "translate(" + 80 + "," + 10 + " )")
.attr("class", "greyAxis")
.call(d3.axisLeft(yScale))
values.forEach((row, i) => {
row.forEach((el, j) => {
if (j >= i)
svg.append("rect")
.attr("id", "corr_" + keys[j] + "_" + keys[i])
.attr("x", xScale(keys[i]) + 2)
.attr("y", yScale(keys[j]) + 5)
.attr("width", xScale.bandwidth())
.attr("height", yScale.bandwidth())
.style("fill", d => colorScale(el))
.style("opacity", 0.8)
.on("mouseover", d => {
$("#" + d.target.id).css("stroke", "black")
.css("stroke-width", '2px')
})
.on("mouseout", d => {
$("#" + d.target.id).css("stroke-width", '0px')
})
.append("title")
.text(d => keys[j] + " & " + keys[i] + ": " + el)
})
});
var linearGradient = svg.append("linearGradient")
.attr("id", "gradient");
linearGradient
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "0%")
.attr("y2", "100%");
linearGradient.append("stop")
.attr("offset", "0%")
.attr("stop-color", "#c34e4c")
linearGradient.append("stop")
.attr("offset", "50%")
.attr("stop-color", "#f2f2f2")
linearGradient.append("stop")
.attr("offset", "100%")
.attr("stop-color", "#4476a9")
const colorLegendHeight = height / 3
svg.append("rect")
.attr("width", 20)
.attr("height", height / 3)
.style("fill", "url(#gradient)")
.attr("x", width - 50 - 20)
.attr("y", height - 75 - colorLegendHeight)
svg.append("text")
.text("-1")
.attr("x", width - 50 + 3)
.attr("y", height - 75 - colorLegendHeight + 6)
svg.append("text")
.text(" 1")
.attr("x", width - 50 + 3)
.attr("y", height - 75 + 3)
svg.append("text")
.text(" 0")
.attr("x", width - 50 + 3)
.attr("y", height - 75 - colorLegendHeight / 2 + 3)
}
}

View File

@@ -0,0 +1,36 @@
import React, { useRef, useEffect } from 'react';
import { ComponentToPrint } from './componentToPrint';
import { exportComponentAsJPEG } from 'react-component-export-image';
import CorrelationMatrixD3 from './correlationMatrix.d3';
import "./cohortPanel.css"
const CorrelationMatrix = ({ data, parentId }) => {
const ref = useRef(null)
useEffect(() => {
const currElement = ref.current
if (data) {
new CorrelationMatrixD3(currElement, data, parentId)
}
}, [data])
return (
<React.Fragment>
<div id="corrMatrixComponent">
<h6>Correlation Matrix</h6>
<button type="button" className='btn btn-outline-secondary btn-sm printButton' onClick={() => exportComponentAsJPEG(ref)}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-printer" viewBox="0 0 16 16">
<path d="M2.5 8a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z" />
<path d="M5 1a2 2 0 0 0-2 2v2H2a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h1v1a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-1h1a2 2
0 0 0 2-2V7a2 2 0 0 0-2-2h-1V3a2 2 0 0 0-2-2H5zM4 3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2H4V3zm1 5a2 2 0 0 0-2
2v1H2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v-1a2 2 0 0 0-2-2H5zm7 2v3a1 1 0 0
1-1 1H5a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1z" />
</svg>
</button>
</div>
<ComponentToPrint ref={ref} />
</React.Fragment>
)
}
export default CorrelationMatrix;

View File

@@ -0,0 +1,125 @@
import * as d3 from 'd3';
import $ from "jquery";
import "../index.css";
import DBMDict from '../DBM_attribute_dict.json'
export default class DistributionChart {
constructor(chart, data, attr, filteredIds, metadata, hideIds, metadataAttrColor) {
if (!d3.select(chart).select('svg').empty()) {
d3.select(chart).select('svg').remove()
}
var parentContainer = document.getElementById("distributionChartContainer")
var width = parentContainer.clientWidth / 2.8
var height = parentContainer.clientHeight / 3.5
const svg = d3.select(chart)
.append('svg')
.attr('width', width)
.attr('height', height)
.append("g")
var jitterWidth = width / 8
const values = Object.values(data).map(el => el[attr])
const keys = Object.keys(data)
const inputObject = Object.values(data).map(el => [el['id'], el[attr]])
var metadataDict = {}
if (metadata.length > 0) {
var d = Object.values(metadata).map(el => [el['id'], el['attr']])
d.forEach(el => {
metadataDict[el[0]] = el[1]
})
}
var yScale = d3.scaleLinear()
.domain(DBMDict[attr]['range'].length > 0 ? DBMDict[attr]['range'] : [Math.min(...values), Math.max(...values)])
.range([height - 30, 20])
svg.append("g")
.attr("transform", "translate(40 ,0)")
.attr("class", "greyAxis")
.call(d3.axisLeft(yScale))
var xScale = d3.scaleBand()
.range([1, width - 20])
.domain([attr])
.padding(0.05)
svg.append("g")
.attr("transform", "translate(40," + (height - 30) + ")")
.attr("class", "greyAxis")
.call(d3.axisBottom(xScale))
.call(s => s.selectAll("text").attr("dx", "-30").attr("y", "10").text(DBMDict[attr]['label']))
var histogram = d3.bin()
.domain(yScale.domain())
.thresholds(yScale.ticks(20))
.value(d => d)
var sumstat = [{ "key": attr, "value": histogram(values) }]
var binLens = sumstat[0].value.map(l => l.length)
var xNum = d3.scaleLinear()
.range([0, xScale.bandwidth()])
.domain([-Math.max(...binLens), Math.max(...binLens)])
svg.selectAll("distr")
.data(sumstat)
.enter()
.append("g")
.attr("transform", d => "translate(" + xScale(d.key) + " ,0)")
.append("path")
.datum(d => d.value)
.style("stroke", "none")
.style("fill", "#e7d4e8")
.attr("d", d3.area()
.x0(xNum(-0.01) + xScale.bandwidth() / 10)
.x1(d => (xNum(d.length)) + xScale.bandwidth() / 10)
.y(d => Math.abs(d.x0) !== Infinity ? yScale(d.x0) : yScale(0))
.curve(d3.curveCatmullRom)
)
d3.selectAll(".tick line").attr("opacity", 0)
svg.selectAll("indPoints")
.data(inputObject)
.enter()
.append("circle")
.attr("id", d => d[0] + "_distr_" + attr)
.attr("class", d => "dot dotDistr dot_" + d[0])
.attr("cx", d => (xScale(attr) + xScale.bandwidth() / 2 - Math.random() * jitterWidth - 5))
.attr("cy", d => yScale(d[1]))
.attr("r", 5.5)
.style("fill", d => filteredIds.includes(d[0]) ? "darkorange" : (metadataDict[d[0]] ? metadataAttrColor[d[0]] : "#41ab5d"))
.style("opacity", d => filteredIds.includes(d[0]) ? "0.4" : hideIds ? "0" : "0.4")
.attr("stroke", "grey")
.attr("stroke-width", '1.5px')
.on("mouseover", d => {
var className = "dot_" + d.target.id.replace("_distr_" + attr, "")
d3.selectAll("." + className).style("fill", "darkorange")
$("#" + d.target.id.replace("_distr_" + attr, "") + "_id_container").css("color", "#fc6a03")
keys.forEach(k => {
if ($('#' + k + "_id").is(":checked"))
$(".dot_" + k).css("fill", "darkorange")
})
})
.on("mouseout", d => {
keys.forEach(k => {
$(".dot_" + k).css("fill", $('#' + k+ "_id").is(":checked") ? "darkorange" : (metadataDict[k] ? metadataAttrColor[k] : "#41ab5d"))
if ($('#' + k + "_id").is(":checked")) {
$(".dot_" + k).css("fill", "darkorange")
}
})
$(".id_checkbox_container").css("color", "black")
})
.append("title")
.text(d => "ID: " + String(d[0]) + "\n" + "Value: " + String(d[1]))
}
}

View File

@@ -0,0 +1,35 @@
import React, { useRef, useEffect } from 'react';
import { ComponentToPrint } from './componentToPrint';
import { exportComponentAsJPEG } from 'react-component-export-image';
import DistributionChartD3 from './distributionChart.d3';
const DistributionChart = ({ data, attr, filteredIds, metadata, hideIds, metadataAttrColor }) => {
const ref = useRef(null)
useEffect(() => {
const currElement = ref.current
if (data) {
new DistributionChartD3(currElement, data, attr, filteredIds, metadata, hideIds, metadataAttrColor)
}
}, [data])
return (
<React.Fragment>
<ComponentToPrint ref={ref} />
<div>
<button type="button" className='btn btn-outline-secondary btn-sm printButton' onClick={() => exportComponentAsJPEG(ref)} >
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-printer" viewBox="0 0 16 16">
<path d="M2.5 8a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z" />
<path d="M5 1a2 2 0 0 0-2 2v2H2a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h1v1a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-1h1a2 2
0 0 0 2-2V7a2 2 0 0 0-2-2h-1V3a2 2 0 0 0-2-2H5zM4 3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2H4V3zm1 5a2 2 0 0 0-2
2v1H2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v-1a2 2 0 0 0-2-2H5zm7 2v3a1 1 0 0
1-1 1H5a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1z" />
</svg>
</button>
</div>
</React.Fragment>
)
}
export default DistributionChart;

View File

@@ -0,0 +1,325 @@
import React, { useRef } from 'react';
import $ from 'jquery';
import { exportComponentAsJPEG } from 'react-component-export-image';
const HeadSVG = ({ width, height }, ref) => {
const componentRef = useRef()
const showTooltip = () => {
if ($('#AUsMaskButton').hasClass("btn-primary") | $('#movMaskButton').hasClass("btn-primary")) {
$('.AUtooltip').css("opacity", "1")
}
}
const hideTooltip = () => {
$('.AUtooltip').css("opacity", "0")
}
return (
<React.Fragment>
<div ref={componentRef} style={{ position: "relative", overflow: "none", display: "inline-block", }} onMouseOver={showTooltip} onMouseLeave={hideTooltip}>
<svg style={{ position: "absolute" }}
width={width}
height={height}
viewBox={`0 0 ${width} ${parseInt(height) + 100}`}>
<g transform="translate(-36,0) scale(0.9,0.9)"
fill="#000000" stroke="none">
<path fill="#3a3b3c" id="headSketch"
d="M1275 3785c-49-8-119-16-155-20-80-8-186-35-276-71-281-112-521-412-595-744-41-182-59-495-39-660 13-97 14-129 7-136-2-2-24 3-49 13-38 14-51 14-75 4-101-42-79-192 82-566 7-16 19-50 25-75 12-46 35-99 120-275 27-55 67-143 89-195 23-52 55-117 71-145 16-27 41-70 54-95 14-25 42-68 63-97 21-28 59-81 86-118l47-66V270c0-233 2-270 15-270s15 35 15 253v252l27-35c67-89 283-276 403-349 89-55 150-70 275-71 137 0 322 18 355 36 44 23 193 147 251 209 122 131 141 152 177 198l37 48 3-270c2-232 4-271 17-271s15 40 15 291v292l33 51c19 28 64 98 102 155 61 93 165 304 165 336 0 14 55 122 75 145 13 16 105 239 105 255 0 9 6 23 79 177 74 159 76 164 76 279 0 116-5 128-61 155-31 15-38 15-80 0l-46-15 6 37c24 159 27 237 17 405-5 98-13 182-16 187s-11 51-16 102c-16 148-24 171-142 407-47 95-61 113-157 204-144 137-232 201-310 227-36 12-74 25-85 30-173 71-539 101-790 65zm398-26c29-6 59-11 67-10 38 1 189-22 225-34 22-7 48-13 58-12 17 0 79-25 165-67 73-36 168-113 254-208 46-50 82-94 81-98s9-18 22-30c14-13 25-27 25-32s15-30 33-56c18-27 41-70 51-98 10-27 21-58 26-69 14-33 40-161 61-300 21-141 26-427 9-501-5-22-12-67-15-100-3-32-8-72-12-88l-5-30-13 30-13 29-1-33c-1-18 4-43 10-54 14-27-3-141-37-240-13-39-21-74-18-77 11-11 42 39 49 79 4 22 11 40 17 40 7 0 8-12 4-32-4-18-11-78-15-133-19-221-74-443-153-620-94-207-299-498-504-712-62-65-226-193-254-199-14-2-44-9-68-14-57-13-412-13-412 0 0 6-8 10-17 10-25 0-110 51-163 97-25 21-75 60-111 85-88 63-197 180-320 347-93 127-197 294-235 381-9 19-37 82-64 140-60 131-79 219-121 545-17 142-20 173-9 140 5-16 15-50 21-75 9-36 13-41 19-25 4 11-3 55-16 104-23 83-25 130-11 241 4 25 4 37 1 28-12-38-22-14-34 80-25 198-16 534 19 702 60 285 199 514 408 672 43 33 105 72 138 88 66 31 221 71 300 77 28 2 71 8 97 13s98 13 160 16 114 7 116 9c8 7 141 3 185-6zM177 2129c44-18 50-25 60-62 6-23 8-58 4-77l-7-34-17 45c-20 51-70 105-107 114-22 5-28 1-42-27s-17-30-17-11c-1 22 44 73 65 73 6 0 34-9 61-21zm2723-24c10-12 9-15-6-15-32 0-81-37-120-89-21-28-40-49-42-46-3 2 3 33 13 69l17 64 52 15c66 20 71 20 86 2zm-2748-42c14-16 39-55 57-87 26-50 31-71 31-127 0-37 4-89 9-116 6-26 15-96 21-154 6-59 17-131 24-160 8-30 13-56 10-58-6-6-71 139-79 177-4 18-27 81-52 140-59 140-92 244-99 313-11 94 26 127 78 72zm2750-10c27-31 38-129 20-182-8-25-20-53-27-61-21-27-103-223-138-332-35-107-96-228-82-163 46 228 56 307 56 460 0 121 2 133 24 167 24 36 114 128 126 128 3 0 13-8 21-17z"
transform="matrix(.1 0 0 -.1 0 386)"
></path>
<path
d="M550 2601c-106-44-180-88-180-108 0-17 4-17 48 5 20 10 80 29 133 42 95 23 95 23 185 6 49-10 127-24 174-31 112-17 228-49 286-80 26-14 48-25 50-25 6 0 24 103 18 111-13 21-73 42-217 75-116 27-181 37-277 40l-125 5-95-40zm355-21c90-14 246-49 278-61 15-6 27-18 27-25 0-18-14-18-48 0-41 21-167 53-262 67-101 14-143 28-90 28 19 0 62-4 95-9zM2110 2604c-36-8-85-18-110-23-259-55-269-59-254-113 16-56 25-56 231-5 110 28 231 51 293 57 99 8 112 7 229-21 142-33 141-33 141-16 0 30-55 58-234 117-70 24-198 25-296 4zm65-49c-38-8-133-30-210-49-77-20-148-36-159-36-30 0-2 26 37 33 17 4 95 20 172 36s160 30 185 29c42 0 40-1-25-13zM696 2313c-49-13-186-116-186-139 0-21 9-17 64 29 88 75 118 88 206 94 45 3 80 2 80-3s-27-10-60-11c-49-1-71-7-113-33-46-28-97-85-97-108 0-12 46-51 103-89 49-31 134-53 211-53 41 0 206 74 206 92 0 43-105 139-192 175-29 12-44 22-34 23 11 0 57-18 103-40 86-42 168-112 197-169 9-17 23-31 31-31 23 0 18 14-24 67-72 89-114 122-206 160-79 33-102 38-180 40-49 1-99-1-109-4zm74-51c0-4-9-13-20-20-23-14-28-88-7-114 37-46 100-60 166-37 39 14 65 60 57 100s8 36 55-13c64-69 65-74 22-101-71-45-105-52-196-41-67 9-93 17-138 46-30 19-62 43-72 52-16 17-16 19 3 44 21 26 108 92 123 92 4 0 7-3 7-8zm118-34c18-18 15-42-7-62-26-24-72-17-82 12-16 43 56 83 89 50zM2099 2305c-83-17-155-41-195-66-45-27-123-114-140-154-17-42-17-45-1-45 7 0 21 17 30 38 22 51 91 119 152 150 71 36 178 62 258 62s140-24 238-96c33-25 64-43 67-40 10 11-67 81-120 110-104 56-167 65-289 41z"
transform="matrix(.1 0 0 -.1 0 386)"
></path>
<path
d="M2045 2245c-38-14-71-25-73-25-14 0-112-107-112-122 0-13 23-29 87-59 80-37 95-41 168-41 101-1 169 26 251 99l58 52-45 40c-65 59-103 75-189 78-59 2-90-2-145-22zm130-60c0-40 0-40-39-40-51 0-67 22-42 60 14 21 24 26 49 23 30-3 32-5 32-43zm148 9c26-20 47-40 47-43 0-16-69-72-120-97-84-43-155-44-257-5-108 42-113 49-68 99 20 22 49 45 65 50 28 10 28 10 23-22-12-74 97-132 173-93 48 25 65 50 66 101 0 29 5 46 13 46 6 0 33-16 58-36zM1295 2240c-4-7-4-15 1-19 5-3 22-21 39-39l29-32 2-298 2-297-34-85c-18-46-31-86-29-88 14-14 34 16 58 84 27 79 27 80 27 360 0 155-3 294-6 310-8 39-78 122-89 104zM1640 2198l-34-42-7-160c-4-89-8-232-8-319l-1-157 36-71c24-48 39-68 46-61 6 6 1 26-15 58-44 89-50 134-38 335 6 100 11 224 11 274 0 90 1 92 35 132 38 45 42 53 23 53-7 0-29-19-48-42z"
transform="matrix(.1 0 0 -.1 0 386)"
></path>
<path
d="M1227 1481c-17-31-27-64-27-90 0-36 6-47 38-77 54-52 75-57 139-39 45 13 60 14 89 3 31-11 40-10 65 5 27 15 32 15 45 2 25-25 110-19 161 10 44 26 47 34 44 95-2 40-49 142-62 134-5-2 1-28 13-57 37-94 36-123-7-151-22-14-28-15-47-2-39 24-88 28-135 10-36-14-49-15-85-4-56 16-104 14-138-7-27-15-29-15-58 8-26 20-31 31-31 69 0 31 7 58 24 85 28 46 30 55 12 55-8 0-26-22-40-49zM1310 949c-110-32-163-52-209-76-24-13-57-23-73-23-19 0-28-4-25-12 2-7 10-13 18-13s46-26 84-58c39-32 96-76 129-98 58-39 59-39 160-40 248-2 341 2 363 16 12 8 59 51 105 96 55 55 91 82 107 83 29 1 41 26 13 26-12 0-58 14-104 30-46 17-112 39-148 50s-78 24-93 29c-22 8-42 5-83-10l-54-20-58 20c-32 12-60 21-63 20-2 0-33-9-69-20zm143-34c38-19 51-19 89 1 17 9 45 16 62 16 33 0 205-49 219-62s-94-34-163-32c-40 1-83-5-112-15-44-16-48-16-106 5-38 14-84 22-128 23-38 0-87 6-109 13l-40 13 45 13c25 7 70 20 100 30 61 19 98 17 143-5zm-15-117c28-11 62-18 75-15 12 3 40 11 62 17 34 9 304 14 313 6 1-2-34-37-79-77l-82-74-213 2-214 1-68 49c-118 83-151 108-152 115 0 3 69 4 153 1 120-4 163-9 205-25z"
transform="matrix(.1 0 0 -.1 0 386)">
</path>
</g>
</svg>
<div id="expressivityMaskContainer">
<svg height="300" width="100" style={{ position: "absolute" }} id="faceExpresivityMask" transform="translate(-4, 0)" opacity="0.5">
<circle cx="205" cy="150" r="49%" transform="translate(-70, -20)" />
</svg>
<svg height="140" width="100" style={{ position: "absolute" }} transform="translate(107, -2)" id="upperFaceExpresivityMask" opacity="0.5">
<circle cx="0" cy="150" r="91%" transform="translate(-43, -20)" />
</svg>
<svg height="140" width="100" style={{ position: "absolute" }} transform="translate(107, 148)" id="lowerFaceExpresivityMask" opacity="0.5">
<circle cx="0" cy="150" r="91%" transform="translate(-43, -165)" />
</svg>
</div>
<svg height="300" width="200" style={{ position: "absolute" }} id="facePainExpresivityMask" >
<ellipse cx="0" cy="150" rx="38%" ry="35%" transform="translate(99, -20)" />
</svg>
<div id="asymetryMaskContainer">
<svg height="300" width="200" style={{ position: "absolute" }} id="faceAsymetryMask" opacity="0.5">
<ellipse cx="0" cy="150" rx="38%" ry="35%" transform="translate(99, -20)" />
</svg>
<svg height="300" width="200" style={{ position: "absolute" }} id="leftEyeAsymetryMask" opacity="0.5">
<ellipse cx="0" cy="150" ry="5%" rx="14%" transform="translate(58, -31)" />
</svg>
<svg height="300" width="200" style={{ position: "absolute" }} id="rightEyeAsymetryMask" opacity="0.5">
<ellipse cx="0" cy="150" ry="5%" rx="14%" transform="translate(144, -31)" />
</svg>
<svg height="300" width="200" style={{ position: "absolute" }} id="leftEyebrowAsymetryMask" opacity="0.5">
<ellipse cx="0" cy="150" ry="3%" rx="15%" transform="translate(60, -60)" />
</svg>
<svg height="300" width="200" style={{ position: "absolute" }} id="rightEyebrowAsymetryMask" opacity="0.5">
<ellipse cx="0" cy="150" ry="3%" rx="15%" transform="translate(144, -60)" />
</svg>
<svg height="300" width="200" style={{ position: "absolute" }} id="mouthAsymetryMask" opacity="0.5">
<ellipse cx="0" cy="150" rx="20%" ry="7%" transform="translate(100, 55)" />
</svg>
</div>
<div id="AUHighlightsContainer">
<svg height="20" width="20" style={{ position: "absolute" }} id="au12RightHighlight" transform="translate(125, 180)" className="hap_highlight con_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg height="20" width="20" style={{ position: "absolute" }} id="au12LeftHighlight" transform="translate(56, 180)" className="hap_highlight con_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(91, 226)" id="au26Highlight" className="sur_highlight fea_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(58, 145)" id="au9RightHighlight" className='dig_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(123, 145)" id="au9LeftHighlight" className='dig_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(43, 90)" id="au4LeftHighlight" className="sad_highlight fea_highlight ang_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(141, 90)" id="au4RightHighlight" className="sad_highlight fea_highlight ang_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(148, 65)" id="au2RightHighlight" className="sur_highlight fea_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(35, 65)" id="au2LeftHighlight" className="sur_highlight fea_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(118, 68)" id="au1RightHighlight" className="sad_highlight sur_highlight fea_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(68, 68)" id="au1LeftHighlight" className="sad_highlight sur_highlight fea_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(162, 104)" id="au5RightHighlight" className="sur_highlight fea_highlight ang_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(16, 104)" id="au5LeftHighlight" className="sur_highlight fea_highlight ang_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(158, 158), rotate(-90)" id="au6RightHighlight" className="hap_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(23, 158),rotate(90)" id="au6LeftHighlight" className="hap_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(135, 188)" id="au20LeftHighlight" className='fea_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(48, 188)" id="au20RightHighlight" className='fea_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(134, 200), rotate(-90)" id="au15RightHighlight" className="sad_highlight dig_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(47, 201), rotate(90)" id="au15LeftHighlight" className="sad_highlight dig_highlight emotion_highlight">
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(65, 212), rotate(45)" id="au16LeftHighlight" className='dig_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(115, 212), rotate(-45)" id="au16RightHighlight" className='dig_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(108, 180), rotate(45)" id="au23RightUpHighlight" className='ang_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(72, 180), rotate(-45)" id="au23leftUpHighlight" className='ang_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(78, 215), rotate(-45)" id="au23LeftDownHighlight" className='ang_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(102, 215), rotate(45)" id="au23RightDownHighlight" className='ang_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(28, 125), rotate(-45)" id="au7LeftHighlight" className='fea_highlight ang_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} transform="translate(150, 125), rotate(45)" id="au7RightHighlight" className='fea_highlight ang_highlight emotion_highlight'>
<circle cx="10" cy="10" r="50%" fill="purple" opacity="0.4" />
</svg>
</div>
<div id="AUContainer" style={{ position: "absolute" }}>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-right AU12" viewBox="0 0 20 20" transform="translate(127, 180), rotate(-45)" id="au12Left">
<path id="au12Left_path" strokeWidth="12%" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z" />
<text className='AUtooltip' opacity='0' x="90%" y="1%" transform="rotate(45)" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >12</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-left AU12" viewBox="0 0 20 20" transform="translate(54, 182), rotate(45)" id="au12Right">
<path id="au12Right_path" strokeWidth="12%" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z" />
<text className='AUtooltip' opacity='0' x="0%" y="50%" transform="rotate(-45)" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >12</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-down AU26" viewBox="0 0 16 16" transform="translate(93, 226)" id="au26">
<path id="au26_path" strokeWidth="12%" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >26</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-right AU09" viewBox="0 0 20 20" transform="translate(60, 146)" id="au9Right">
<path id="au9Right_path" strokeWidth="12%" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >9</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-left AU09" viewBox="0 0 20 20" transform="translate(125, 147)" id="au9Left">
<path id="au9Left_path" strokeWidth="12%" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2">9</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-right AU04" viewBox="0 0 20 20" transform="translate(45, 89)" id="au4Left">
<path id="au4Left_path" strokeWidth="12%" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >4</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-left AU04" viewBox="0 0 20 20" transform="translate(143, 89)" id="au4Right">
<path id="au4Right_path" strokeWidth="12%" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >4</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-up AU02" viewBox="0 0 16 16" transform="translate(150, 68)" id="au2Right">
<path id="au2Right_path" strokeWidth="12%" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >2</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-up AU02" viewBox="0 0 16 16" transform="translate(37, 67)" id="au2Left">
<path id="au2Left_path" strokeWidth="12%" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >2</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-up AU01" viewBox="0 0 16 16" transform="translate(120, 70)" id="au1Right">
<path id="au1Right_path" strokeWidth="12%" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >1</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-up AU01" viewBox="0 0 16 16" transform="translate(70, 70)" id="au1Left">
<path id="au1Left_path" strokeWidth="12%" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >1</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-up AU05" viewBox="0 0 16 16" transform="translate(165, 105)" id="au5Right">
<path id="au5Right_path" strokeWidth="12%" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >5</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-up AU05" viewBox="0 0 16 16" transform="translate(18, 105)" id="au5Left">
<path id="au5Left_path" strokeWidth="12%" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >5</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-up AU06" viewBox="0 0 16 16" transform="translate(160, 160), rotate(-90)" id="au6Right">
<path id="au6Right_path" strokeWidth="12%" d="M1.5 1.5A.5.5 0 0 0 1 2v4.8a2.5 2.5 0 0 0 2.5 2.5h9.793l-3.347 3.346a.5.5 0 0 0 .708.708l4.2-4.2a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 8.3H3.5A1.5 1.5 0 0 1 2 6.8V2a.5.5 0 0 0-.5-.5z" />
<text className='AUtooltip' opacity='0' x="30%" y="-50%" transform='rotate(90)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2">6</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-up AU06" viewBox="0 0 16 16" transform="translate(25, 160),rotate(90)" id="au6Left">
<path id="au6Left_path" strokeWidth="12%" d="M14.5 1.5a.5.5 0 0 1 .5.5v4.8a2.5 2.5 0 0 1-2.5 2.5H2.707l3.347 3.346a.5.5 0 0 1-.708.708l-4.2-4.2a.5.5 0 0 1 0-.708l4-4a.5.5 0 1 1 .708.708L2.707 8.3H12.5A1.5 1.5 0 0 0 14 6.8V2a.5.5 0 0 1 .5-.5z" />
<text className='AUtooltip' opacity='0' x="-30%" y="50%" transform='rotate(-90)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >6</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-right AU20" viewBox="0 0 20 20" transform="translate(135, 190)" id="au20Left">
<path id="au20left_path" strokeWidth="12%" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >20</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-left AU20" viewBox="0 0 20 20" transform="translate(50, 190)" id="au20Right">
<path id="au20Right_path" strokeWidth="12%" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z" />
<text className='AUtooltip' opacity='0' x="40%" y="50%" textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >20</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-left AU15" viewBox="0 0 20 20" transform="translate(136, 200), rotate(-90)" id="au15Right">
<path id="au15Right_path" strokeWidth="12%" d="M14.5 1.5a.5.5 0 0 1 .5.5v4.8a2.5 2.5 0 0 1-2.5 2.5H2.707l3.347 3.346a.5.5 0 0 1-.708.708l-4.2-4.2a.5.5 0 0 1 0-.708l4-4a.5.5 0 1 1 .708.708L2.707 8.3H12.5A1.5 1.5 0 0 0 14 6.8V2a.5.5 0 0 1 .5-.5z" />
<text className='AUtooltip' opacity='0' x="30%" y="-50%" transform='rotate(90)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >15</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-left AU15" viewBox="0 0 20 20" transform="translate(43, 204), rotate(90)" id="au15Left">
<path id="au15Left_path" strokeWidth="12%" d="M1.5 1.5A.5.5 0 0 0 1 2v4.8a2.5 2.5 0 0 0 2.5 2.5h9.793l-3.347 3.346a.5.5 0 0 0 .708.708l4.2-4.2a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 8.3H3.5A1.5 1.5 0 0 1 2 6.8V2a.5.5 0 0 0-.5-.5z" />
<text className='AUtooltip' opacity='0' x="-30%" y="50%" transform='rotate(-90)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >15</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-down AU17" viewBox="0 0 16 16" transform="translate(67, 215), rotate(45)" id="au16Left">
<path id="au16Left_path" strokeWidth="12%" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z" />
<text className='AUtooltip' opacity='0' x="0%" y="80%" transform='rotate(-45)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >16</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-down AU17" viewBox="0 0 16 16" transform="translate(117, 215), rotate(-45)" id="au16Right">
<path id="au16Right_path" strokeWidth="12%" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z" />
<text className='AUtooltip' opacity='0' x="80%" y="10%" transform='rotate(45)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >16</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-down AU23" viewBox="0 0 16 16" transform="translate(112, 180), rotate(45)" id="au23RightUp">
<path id="au23RightUp_path" strokeWidth="12%" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z" />
<text className='AUtooltip' opacity='0' x="0%" y="80%" transform='rotate(-45)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2">23</text>
</svg>
<svg width="16" height="16" style={{ position: "absolute" }} className="bi bi-arrow-down AU23" viewBox="0 0 16 16" transform="translate(72, 180), rotate(-45)" id="au23leftUp">
<path id="au23LeftUp_path" strokeWidth="12%" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z" />
<text className='AUtooltip' opacity='0' x="80%" y="10%" transform='rotate(45)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2">23</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-right AU23" viewBox="0 0 20 20" transform="translate(80, 217), rotate(-45)" id="au23LeftDown">
<path id="au23LeftDown_path" strokeWidth="12%" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z" />
<text className='AUtooltip' opacity='0' x="80%" y="10%" transform='rotate(45)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2">23</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-left AU23" viewBox="0 0 20 20" transform="translate(103, 220), rotate(45)" id="au23RightDown">
<path id="au23RightDown_path" strokeWidth="12%" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z" />
<text className='AUtooltip' opacity='0' x="0%" y="80%" transform='rotate(-45)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >23</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-right AU07" viewBox="0 0 20 20" transform="translate(30, 125), rotate(-45)" id="au7Left">
<path id="au7Left_path" strokeWidth="12%" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z" />
<text className='AUtooltip' opacity='0' x="80%" y="20%" transform='rotate(45)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2">7</text>
</svg>
<svg width="20" height="20" style={{ position: "absolute" }} className="bi bi-arrow-left AU07" viewBox="0 0 20 20" transform="translate(150, 127), rotate(45)" id="au7Right">
<path id="au7Right_path" strokeWidth="12%" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z" />
<text className='AUtooltip' opacity='0' x="0%" y="80%" transform='rotate(-45)' textAnchor="middle" fontSize="10px" stroke="black" strokeWidth="0.2" >7</text>
</svg>
</div>
<div id="headMovementMaskContainer">
<svg width="32" height="32" style={{ position: "absolute" }} className="bi bi-arrow-down" viewBox="0 0 32 32" transform="translate(85, -25)" id="pitchUp"
>
<path id="pitchUp_path" strokeWidth="4px" d="M17.504 26.025l.001-14.287 6.366 6.367L26 15.979 15.997 5.975 6 15.971 8.129 18.1l6.366-6.368v14.291z" />
</svg>
<svg width="302" height="32" style={{ position: "absolute" }} className="bi bi-arrow-down" viewBox="0 0 32 32" transform="translate(-50, 260)" id="pitchDown">
<path id="pitchDown_path" strokeWidth="4px" d="m14.496 5.975l-.001 14.287-6.366-6.367L6 16.021l10.003 10.004L26 16.029 23.871 13.9l-6.366 6.368V5.977z" />
</svg>
<svg width="32" height="32" style={{ position: "absolute" }} className="bi bi-arrow-down" viewBox="0 0 32 32" transform="translate(170, 0), rotate(-45)" id="rollRight">
<path id="rollRight_path" strokeWidth="4px" d="M5.975 17.504l14.287.001-6.367 6.366L16.021 26l10.004-10.003L16.029 6l-2.128 2.129 6.367 6.366H5.977z" />
</svg>
<svg width="32" height="32" style={{ position: "absolute" }} className="bi bi-arrow-down" viewBox="0 0 32 32" transform="translate(5, 0), rotate(45)" id="rollLeft">
<path id="rollLeft_path" strokeWidth="4px" d="M26.025 14.496l-14.286-.001 6.366-6.366L15.979 6 5.975 16.003 15.971 26l2.129-2.129-6.367-6.366h14.29z" />
</svg>
<svg width="32" height="32" style={{ position: "absolute" }} className="bi bi-arrow-down" viewBox="0 0 32 32" transform="translate(-30, 120)" id="yawLeft">
<path id="yawLeft_path" strokeWidth="4px" d="M26.025 14.496l-14.286-.001 6.366-6.366L15.979 6 5.975 16.003 15.971 26l2.129-2.129-6.367-6.366h14.29z" />
</svg>
<svg width="32" height="32" style={{ position: "absolute" }} className="bi bi-arrow-down" viewBox="0 0 32 32" transform="translate(200, 120)" id="yawRight">
<path id="yawRight_path" strokeWidth="4px" d="M5.975 17.504l14.287.001-6.367 6.366L16.021 26l10.004-10.003L16.029 6l-2.128 2.129 6.367 6.366H5.977z" />
</svg>
</div>
<div id="tooltip" display="none"
style={{ position: "absolute", display: "none", fontSize: "0.6rem", background: "white", border: "1px solid black", padding: "2px" }}></div>
</div>
<button type="button" className='btn btn-outline-secondary btn-sm' style={{ width: "max-content", position: "absolute", left: "0", }} onClick={() => exportComponentAsJPEG(componentRef)}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-printer" viewBox="0 0 16 16">
<path d="M2.5 8a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z" />
<path d="M5 1a2 2 0 0 0-2 2v2H2a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h1v1a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-1h1a2 2
0 0 0 2-2V7a2 2 0 0 0-2-2h-1V3a2 2 0 0 0-2-2H5zM4 3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2H4V3zm1 5a2 2 0 0 0-2
2v1H2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v-1a2 2 0 0 0-2-2H5zm7 2v3a1 1 0 0
1-1 1H5a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1z" />
</svg>
</button>
</React.Fragment>
);
}
export default HeadSVG;

View File

@@ -0,0 +1,87 @@
import * as d3 from 'd3';
import { axisLeft, axisBottom } from "https://cdn.skypack.dev/d3-axis@3";
import "../index.css"
import $ from "jquery";
import DBMDict from '../DBM_attribute_dict.json'
export default class HistogramD3 {
constructor(chart, data, attr, color, timeframe) {
if (!d3.select(chart).select('svg').empty()) {
d3.select(chart).select('svg').remove()
}
var parentContainer = attr.includes("fac_") ? document.getElementById("facialActivityContainer") :
attr.includes("mov_") ? document.getElementById("headMovementContainer") :
document.getElementById("voiceAcousticsContainer")
var width = parentContainer.clientWidth > 600 ? parentContainer.clientWidth / 3.6 : parentContainer.clientWidth / 2
var height = 70
var marginLeft = 35
const svg = d3.select(chart)
.append('svg')
.attr('width', width)
.attr('height', height)
var values = data.map(el => el[attr])
var xScale = d3.scaleLinear()
.domain([0, values.length - 1])
.range([marginLeft, width - 5])
var xAxis = axisBottom(xScale)
xAxis.ticks(5)
svg.append("g")
.attr("transform", "translate(0," + (height - 20) + ")")
.call(xAxis)
var minVal = DBMDict[attr]['range'].length > 0 ? DBMDict[attr]['range'][0] : Math.min(...values)
var maxVal = DBMDict[attr]['range'].length > 0 ? DBMDict[attr]['range'][1] : Math.max(...values)
var yScale = d3.scaleLinear()
.range([height - 20, 5])
.domain(attr ==="aco_int" ? [0, maxVal]: [minVal, maxVal])
var yAxis = axisLeft(yScale)
yAxis.ticks(3)
svg.append("g")
.attr("transform", "translate(" + (marginLeft - 2) + ",0)")
.call(yAxis)
svg.append("path")
.datum(values)
.attr("fill", color[1])
.attr("stroke", color[0])
.attr("stroke-width", 1)
.attr("d", d3.area()
.x((d, i) => xScale(i))
.y0(yScale(0))
.y1(d => yScale(d))
)
// CHECK IF CORRECT
var segmentLength = parseInt(values.length / 19)
var valuesAux = values
for (var t = 0; t < 20; t++) {
svg.append("path")
.datum(valuesAux.slice(0, segmentLength))
.attr("class", "areaSegment areaSegment_" + (t + 1))
.attr("fill", '#cb181d')
.attr("stroke", "#fb6a4a")
.attr("stroke-width", 0.5)
.attr("opacity", 0)
.attr("d", d3.area()
.x((d, i) => xScale(i + (t * segmentLength)))
.y0(yScale(0))
.y1(d => yScale(d))
)
valuesAux.splice(0, segmentLength)
}
if (parseInt(timeframe) !== 0) {
$(".areaSegment").css("opacity", "0")
$(`.areaSegment_${parseInt(timeframe)}`).css("opacity", "1")
}
}
}

View File

@@ -0,0 +1,42 @@
import React, { useRef, useEffect } from 'react';
import { exportComponentAsJPEG } from 'react-component-export-image';
import HistogramD3 from './histogram.d3';
import DBMDict from '../DBM_attribute_dict.json'
import "./individualPanel.css"
const Histogram = ({ data, derivedData, attr, color, timeframe }) => {
const ref = useRef(null)
useEffect(() => {
const currElement = ref.current
if (data) {
new HistogramD3(currElement, data, attr, color, timeframe)
}
}, [data, attr])
return (
<React.Fragment>
<div ref={ref}>
<div>
<div>{DBMDict[attr]['label']}</div>
<div className="histogramStatisticsContainer">
{derivedData[attr + "_mean"] ? <div className="histogramStatistics">{"mean: " + derivedData[attr + "_mean"]}</div> : ""}
{derivedData[attr + "_std"] ? <div className="histogramStatistics">{"std: " + derivedData[attr + "_mean"]}</div> : ""}
</div>
{DBMDict[attr]['range'].length > 0 && <div className="histogramStatistics">{"range: [" + DBMDict[attr]['range'] + "]"}</div>}
</div>
</div>
<button type="button" className='btn btn-outline-secondary btn-sm printButton' onClick={() => exportComponentAsJPEG(ref)} >
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-printer" viewBox="0 0 16 16">
<path d="M2.5 8a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z" />
<path d="M5 1a2 2 0 0 0-2 2v2H2a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h1v1a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-1h1a2 2
0 0 0 2-2V7a2 2 0 0 0-2-2h-1V3a2 2 0 0 0-2-2H5zM4 3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2H4V3zm1 5a2 2 0 0 0-2
2v1H2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v-1a2 2 0 0 0-2-2H5zm7 2v3a1 1 0 0
1-1 1H5a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1z" />
</svg>
</button>
</React.Fragment>
)
}
export default Histogram;

View File

@@ -0,0 +1,337 @@
.histogramComponent {
display: flex;
justify-content: space-between;
}
.printButton {
width: max-content;
height: max-content;
}
.spiderChartLabel {
font-size: 0.8rem;
}
.spiderChartComponent {
display: flex;
justify-content: space-between;
}
.histogramStatisticsContainer {
display: flex;
flex-direction: row;
}
.histogramStatistics {
font-size: 0.7rem;
margin-top: 3px;
margin-right: 10px;
}
#timelineContainer {
height: 5%;
position: absolute;
top: 0px;
left: 250px;
display: flex;
flex-direction: row;
}
.sliderContainer {
margin: 5px;
display: flex;
flex-direction: row;
}
#timelineRange {
width: 400px;
}
#steplist {
display: inline-flex;
margin-top: -10px !important;
}
#steplist option {
margin-left: 2px;
margin-right: 4px;
font-size: 0.7rem;
}
#timeFramesContainer {
display: flex;
flex-direction: row;
justify-content: flex-end;
margin: auto;
width: 70%;
}
#facialFrameLabel {
margin: auto;
font-weight: bolder;
color: #41ab5d;
}
#movementFrameLabel {
margin: auto;
font-weight: bolder;
color: #4292c6;
}
#acousticFrameLabel {
margin: auto;
font-weight: bolder;
color: #807dba;
}
#frameLabel {
margin: auto;
font-weight: bolder;
}
#headContainer {
width: 300px;
}
#headAndIdPanelContainer {
height: 100%;
width: 250px;
}
#componentsContainer {
height: 90vh;
width: 99vw;
margin-left: 10px;
}
#faceMaskButtonContainer {
display: inline-flex;
flex-direction: row;
gap: 3px;
margin-bottom: 10px;
margin-left: -12px;
}
#headComponentContainer {
margin-left: 10px;
}
#colorLegendAndIdPanelContainer {
margin-top: 300px;
}
#AUButtonContainer {
width: 100%;
height: 3vh;
font-size: 0.7rem;
padding-left: 0px;
padding-right: 0px;
margin-bottom: 5px;
}
.id_checkbox_container {
margin-right: 3px;
}
#colorScaleContainer {
width: 90%;
height: 5vh;
}
#idPanelContainerIndividual {
display: flex;
flex-direction: column;
border-radius: 15px;
padding-top: 10px;
font-size: 0.7rem;
width: 230px;
box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
height: calc(90vh - 420px);
}
#idPanelContainerIndividual div {
font-size: 0.8rem;
width: 210px;
display: inline-flex;
flex-direction: column;
margin-top: 10px;
overflow-y: auto;
}
#facialAndSpeechContainer {
height: 100%;
margin-right: 10px;
width: 40%;
}
#facialActivityContainer {
height: 48%;
background-color: #f7fcf5;
padding: 10px;
overflow-y: scroll;
}
#facialContainer {
font-size: 0.8rem;
width: max-content;
}
#facialQueryPanelContainer {
margin-left: -10px;
margin-right: 5px;
}
#facialQueryPanelButtonsContainer {
display: inline-flex;
flex-direction: row;
}
#facialHistogramsContainer {
width: 77%;
height: 100%;
overflow-y: hidden;
}
.histogramContainer {
display: inline-flex;
flex-direction: row;
padding: 10px;
font-size: 0.8rem;
}
#facialSpeechAndCorrPanelContainer {
margin-top: 30px;
display: flex;
flex-direction: row;
height: 48%;
overflow-y: auto;
}
#facialSpeechAnCorrPanelButtons {
display: inline-flex;
flex-direction: row;
gap: 10px;
margin-top: 10px;
}
#facialSpeechAnCorrPanelButtons .btn {
height: max-content;
}
#facialAndSpeechPanelContainer {
display: flex;
flex-direction: row;
}
#SpiderChartContainer {
display: flex;
flex-direction: row;
}
.spiderComponent {
width: max-content;
margin-right: 20px;
}
.spiderChart {
padding: 10px;
}
#speechLabel {
margin-bottom: 10px;
font-weight: bolder;
}
#speechComponent {
font-size: 0.8rem;
}
.corrMatrixContainer {
height: 85%;
margin-top: 5px;
}
#corrQueryPanelContainer {
width: 25%;
height: 95%;
overflow-y: scroll;
font-size: 0.8rem;
border-radius: 15px;
box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
overflow-x: hidden;
}
#corrQuerryButton {
display: inline-flex;
flex-direction: row;
margin-bottom: 10px;
margin-top: 5px;
}
#corrMatrixComponentIndividual {
width: 74%;
height: 90%;
}
#movementAndAcousticContainer {
height: 100%;
width: 40%;
}
#headMovementContainer {
height: 48%;
overflow-Y: scroll;
background-color: #f7fbff;
display: flex;
padding: 10px;
}
#movementContainer {
width: max-content;
font-size: 0.8rem;
}
#movementQueryButtonContainer {
display: inline-flex;
flex-direction: row;
}
.btn-close {
margin-top: 5px;
margin-left: 5px;
}
#movementHistogramContainer {
width: 80%;
height: 100%;
overflow-y: hidden;
}
#voiceAcousticsContainer {
height: 48%;
overflow-y: scroll;
background-color: #f7f4f9;
margin-top: 30px;
}
#acousticsContainer {
padding: 10px;
margin-left: -10px;
font-size: 0.8rem;
margin-right: 5px;
width: max-content;
}
#acousticsQueryButtonContainer {
display: inline-flex;
flex-direction: row;
}
#acousticsHistogramContainer {
width: 79%;
height: 100%;
overflow-y: hidden;
font-size: 0.8rem;
}

View File

@@ -0,0 +1,584 @@
import React, { useState, useEffect } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Row, Col } from 'react-bootstrap';
import $ from 'jquery';
import HeadSVG from './headSVG.js'
import SpiderChart from './spiderChart.js';
import Histogram from './histogram.js';
import QueryPanel from './queryPanel.js';
import ColorLegend from './colorLegend.js';
import CorrelationMatrix from './correlationMatrix.js';
import DBMDict from "../DBM_attribute_dict.json"
import './individualPanel.css'
function IndividualPanel() {
const [movementButton, setMovementButton] = useState(false)
const [asymetryButton, setAsymetryButton] = useState(false)
const [painButton, setPainButton] = useState(false)
const [expressivityButton, setExpressivityButton] = useState(false)
const [AUsButton, setAUsButton] = useState(false)
const [facialMaskColor, setFacialMaskColor] = useState('white')
const [facialMaskVals, setFacialMaskVals] = useState(null)
const emotions = ["ang", "fea", "dig", "sad", "con", "sur", "hap"]
const [checkedEmotions, setCheckedEmotions] = useState(new Array(emotions.length).fill(false))
const [rawFacialData, setFacialRawData] = useState(null)
const [facialTimelineData, setFacialTimelineData] = useState(null)
const [rawMovementData, setMovementRawData] = useState(null)
const [rawAcousticData, setAcousticRawData] = useState(null)
const [derivedData, setDerivedData] = useState({})
const meanEmotionsSoft = ["fac_feaintsoft_mean", "fac_disintsoft_mean", "fac_sadintsoft_mean",
"fac_conintsoft_mean", "fac_surintsoft_mean", "fac_hapintsoft_mean", "fac_angintsoft_mean"]
const meanAUsSoft = ["fac_AU02int_mean", "fac_AU04int_mean", "fac_AU05int_mean", "fac_AU06int_mean",
"fac_AU07int_mean", "fac_AU09int_mean", "fac_AU10int_mean",
"fac_AU12int_mean", "fac_AU14int_mean", "fac_AU15int_mean", "fac_AU17int_mean", "fac_AU20int_mean",
"fac_AU23int_mean", "fac_AU25int_mean", "fac_AU26int_mean", "fac_AU01int_mean",]
const [timeslotValue, setTimeslotValue] = useState("0")
const timepoints = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"]
const [checkedFacialState, setCheckedFacialState] = useState([]);
const [checkedAcousticState, setCheckedAcousticState] = useState([]);
const [checkedMovementState, setCheckedMovementState] = useState([]);
const [facialAttr, setFacialAttr] = useState([])
const [movementAttr, setMovementAttr] = useState([])
const [acousticAttr, setAcousticAttr] = useState([])
const [allFacialAttr, setAllFacialAttr] = useState([])
const [allMovementAttr, setAllMovementAttr] = useState([])
const [allAcousticAttr, setAllAcousticAttr] = useState([])
const [speechDerivedData, setSpeechDerivedData] = useState([])
const [corrMatrixData, setCorrMatrixData] = useState([])
const [checkedCorrMatrixState, setCheckedCorrMatrixState] = useState([]);
const [allCorrMatrixAttr, setAllCorrMatrixAttr] = useState([])
const [idData, setIdData] = useState([])
const [selectedId, setSelectedId] = useState(null)
const [speechButton, setSpeechButton] = useState(true)
const [corrButton, setCorrButton] = useState(false)
useEffect(() => {
$('#asymetryMaskContainer').css("opacity", "0")
$('#expressivityMaskContainer').css("opacity", "0")
$('#facePainExpresivityMask').css("opacity", "0")
$('#AUContainer').css("opacity", "0")
$('#headMovementMaskContainer').css("opacity", "0")
$('.emotion_highlight').css("opacity", "0")
}, [])
const handleFaceMask = param => {
$('#asymetryMaskContainer').css("opacity", param === "asym" & !asymetryButton ? "1" : "0")
$('#facePainExpresivityMask').css("opacity", param === "pain" & !painButton ? "0.5" : "0")
$('#expressivityMaskContainer').css("opacity", param === "expr" & !expressivityButton ? "1" : "0")
$('#AUContainer').css("opacity", param === "aus" & !AUsButton ? "1" : "0")
setAsymetryButton(param === "asym" ? !asymetryButton : false)
setPainButton(param === "pain" ? !painButton : false)
setExpressivityButton(param === "expr" ? !expressivityButton : false)
if(param === "aus" & AUsButton){
$('.emotion_highlight').css("opacity", "0")
}
setAUsButton(param === "aus" ? !AUsButton : false)
setFacialMaskColor(param === "asym" ? "#de77ae" : param === "pain" ? "red" : param === "expr" ? "orange" : "#cb181d")
setFacialMaskVals(param === "asym" ? [0, 40] : param === "pain" ? [0, 1] : param === "expr" ? [0, 1] : [0, 1])
}
const handleMovementMask = () => {
$('#headMovementMaskContainer').css("opacity", !movementButton ? "1" : "0")
setMovementButton(!movementButton)
setFacialMaskColor(asymetryButton ? "#de77ae" : painButton ? "red" : expressivityButton ? "orange" : "#cb181d")
setFacialMaskVals(asymetryButton ? [0, 40] : painButton ? [0, 1] : expressivityButton ? [0, 1] : [0, 1])
}
const fetchIndividualData = id => {
if (id) {
fetch("/fetchIndividualFacialTimelineData", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"id": id
})
}).then(
res => res.json()
).then(
data => {
setFacialTimelineData(data)
}
)
fetch("/fetchIndividualFacialRawData", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"id": id
})
}).then(
res => res.json()
).then(
data => {
setFacialRawData(data)
}
)
fetch("/fetchIndividualMovementRawData", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"id": id
})
}).then(
res => res.json()
).then(
data => {
setMovementRawData(data)
}
)
fetch("/fetchIndividualAcousticRawData", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"id": id
})
}).then(
res => res.json()
).then(
data => {
setAcousticRawData(data)
}
)
fetch("/fetchIndividualDerivedData", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"id": id
})
}).then(
res => res.json()
).then(
data => {
setDerivedData(data[0])
setSpeechDerivedData(Object.keys(data[0]).filter(e => e.includes("nlp")))
}
)
}
}
useEffect(() => {
fetch("/getRawAttributesAndIds").then(
res => res.json()
).then(
data => {
setAllFacialAttr(data['facial'])
setAllAcousticAttr(data['acoustic'])
setAllMovementAttr(data['movement'])
if (data['facial'].length > 6)
setFacialAttr(data['facial'].slice(0, 6))
if (data['movement'].length > 6)
setMovementAttr(data['movement'].slice(0, 6))
if (data['acoustic'].length > 6)
setAcousticAttr(data['acoustic'].slice(0, 6))
setAllCorrMatrixAttr(data['facial'].concat(data['movement']).concat(data['acoustic']))
setCheckedAcousticState(new Array(data['acoustic'].length).fill(false))
setCheckedMovementState(new Array(data['movement'].length).fill(false))
setCheckedFacialState(new Array(data['facial'].length).fill(false))
setIdData(data['ids'].map(el => el.split("/")[1].replace(".mp4", "")))
setCheckedCorrMatrixState(new Array(data['acoustic'].length + data['facial'].length + data['movement'].length).fill(false))
if (data['ids'].length > 0) {
var firstId = data['ids'].map(el => el.split("/")[1].replace(".mp4", ""))[0]
setSelectedId(firstId)
fetchIndividualData(firstId)
}
}
)
}, [])
const setTime = (ev) => {
setTimeslotValue(ev.target.value)
$(".areaSegment").css("opacity", "0")
$(`.areaSegment_${ev.target.value}`).css("opacity", "1")
var facialSeg = parseInt(rawFacialData.length / 19)
var movementSeg = parseInt(rawMovementData.length / 19)
var acousticSeg = parseInt(rawAcousticData.length / 19)
const facialReminder = rawFacialData.length - facialSeg * 19
const movementReminder = rawMovementData.length - movementSeg * 19
const acousticReminder = rawAcousticData.length - acousticSeg * 19
facialSeg = facialSeg + (parseInt(ev.target.value) <= facialReminder ? 1 : 0)
movementSeg = movementSeg + (parseInt(ev.target.value) <= movementReminder ? 1 : 0)
acousticSeg = acousticSeg + (parseInt(ev.target.value) <= acousticReminder ? 1 : 0)
$("#facialFrameLabel").html("Facial: " + String(parseInt(ev.target.value) !== 0 ? (facialSeg * (parseInt(ev.target.value) - 1) + 1) +
" - " + (parseInt(ev.target.value) === 20 ? rawFacialData.length : facialSeg * (parseInt(ev.target.value))) : ""))
$("#movementFrameLabel").html("Movement: " + String(parseInt(ev.target.value) !== 0 ? (movementSeg * (parseInt(ev.target.value) - 1) + 1) +
" - " + (parseInt(ev.target.value) === 20 ? rawMovementData.length : movementSeg * (parseInt(ev.target.value))) : ""))
$("#acousticFrameLabel").html("Acoustic: " + String(parseInt(ev.target.value) !== 0 ? (acousticSeg * (parseInt(ev.target.value) - 1) + 1) +
" - " + (parseInt(ev.target.value) === 20 ? rawAcousticData.length : acousticSeg * (parseInt(ev.target.value))) : ""))
}
const handleUpdate = param => {
if (param === "facial") {
var facialArg = allFacialAttr.filter((el, index) => checkedFacialState[index] === true)
setFacialAttr(facialArg)
}
else if (param === "movement") {
var movementArg = allMovementAttr.filter((el, index) => checkedMovementState[index] === true)
setMovementAttr(movementArg)
}
else {
var acousticArg = allAcousticAttr.filter((el, index) => checkedAcousticState[index] === true)
setAcousticAttr(acousticArg)
}
}
const handleFacialCheckboxChange = position => {
var updatedCheckedState = checkedFacialState.map((item, index) =>
index === position ? !item : item)
setCheckedFacialState(updatedCheckedState)
}
const handleAcousticCheckboxChange = position => {
var updatedCheckedState = checkedAcousticState.map((item, index) =>
index === position ? !item : item)
setCheckedAcousticState(updatedCheckedState)
}
const handleMovementCheckboxChange = position => {
var updatedCheckedState = checkedMovementState.map((item, index) =>
index === position ? !item : item)
setCheckedMovementState(updatedCheckedState)
}
const handleCorrMatrixCheckboxChange = position => {
const updatedCheckedState = checkedCorrMatrixState.map((item, index) =>
index === position ? !item : item)
setCheckedCorrMatrixState(updatedCheckedState)
}
const handleCorrMatrixUpdate = id => {
var corrMatrix_args = allCorrMatrixAttr.filter((el, index) => checkedCorrMatrixState[index] === true)
if (corrMatrix_args.length === 0) {
corrMatrix_args = meanEmotionsSoft.map(e => e.replace("_mean", ""))
}
var sendId = typeof id === 'string' ? id : selectedId
fetch("/updateCorrMatrix", {
method: "POST",
headers: {
'Content-type': "application/json",
},
body: JSON.stringify({
"corrMatrix_args": corrMatrix_args,
"individual": true,
"id": sendId
})
}).then(
res => res.json()
).then(
data => {
setCorrMatrixData(data)
}
)
}
const handleIdCheckboxChange = ev => {
$(".individual_checkbox").prop("checked", false)
$("#" + ev.target.id).prop("checked", true)
var id = ev.target.id.replace("_id_checkbox", "")
setSelectedId(id)
if (corrButton) {
handleCorrMatrixUpdate(id)
}
setTimeslotValue("0")
fetchIndividualData(id)
}
const handleEmotionCheckboxChange = position => {
const updatededEmotions = checkedEmotions.map((item, index) =>
index === position ? !item : false)
setCheckedEmotions(updatededEmotions)
$(".emotion_highlight").css("opacity", "0")
if ($('#' + emotions[position] + "_highlight").is(":checked")) {
$("." + emotions[position] + "_highlight").css("opacity", "1")
}
}
const handleSpeechPanel = panel => {
if (panel === "speech") {
setSpeechButton(true)
setCorrButton(false)
}
else {
setSpeechButton(false)
setCorrButton(true)
handleCorrMatrixUpdate()
}
}
const handleUnselectCheckboxes = param => {
var updatedCheckedState = null
if (param === "facial") {
updatedCheckedState = checkedFacialState.map(e => false)
setCheckedFacialState(updatedCheckedState)
}
else if (param === "movement") {
updatedCheckedState = checkedMovementState.map(e => false)
setCheckedMovementState(updatedCheckedState)
}
else if (param === "acoustics"){
updatedCheckedState = checkedAcousticState.map(e => false)
setCheckedAcousticState(updatedCheckedState)
}
else {
updatedCheckedState = checkedCorrMatrixState.map(e => false)
setCheckedCorrMatrixState(updatedCheckedState)
}
}
return (
<div>
{rawFacialData && rawFacialData.length > 0 &&
<div id="timelineContainer">
<div className="sliderContainer">
<div>Timeline</div>
<div>
<input type="range" min='0' max="20" id="timelineRange" className="slider"
step="1" value={timeslotValue} onChange={setTime} list="steplist"></input>
<datalist id="steplist">
{timepoints.map((e, i) =>
<option value={e} key={e + "timepoint"}>{i === 0 ? "*" : e}</option>
)}
</datalist>
</div>
</div>
<div id="timeFramesContainer">
<div id="frameLabel">Frames:</div>
<h6 id="facialFrameLabel">Facial</h6>
<h6 id="movementFrameLabel">Movement</h6>
<h6 id="acousticFrameLabel">Acoustics</h6>
</div>
</div>
}
<Row id="componentsContainer">
{derivedData &&
<Col id="headAndIdPanelContainer" className="col-2">
<Row id='headContainer'>
<div id="faceMaskButtonContainer">
<button type="button" id="asymMaskButton"
className={`btn btn-sm ${asymetryButton === true ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => handleFaceMask("asym")}>Asym</button>
<button type="button" id="painMaskButton"
className={`btn btn-sm ${painButton === true ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => handleFaceMask("pain")}>Pain</button>
<button type="button" id="exprMaskButton"
className={`btn btn-sm ${expressivityButton === true ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => handleFaceMask("expr")}>Expr</button>
<button type="button" id="AUsMaskButton"
className={`btn btn-sm ${AUsButton === true ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => handleFaceMask("aus")}>AUs</button>
<button type="button" id="movMaskButton"
className={`btn btn-sm ${movementButton === true ? 'btn-primary' : 'btn-secondary'}`}
onClick={handleMovementMask}>Mov</button>
</div>
<div id="headComponentContainer">
< HeadSVG width={200} height={300} />
</div>
</Row>
<Row id="colorLegendAndIdPanelContainer">
<div id="AUButtonContainer">
{AUsButton && emotions.map((value, index) =>
<label className="id_checkbox_container" key={value + "AU"}>
<input
type="checkbox"
id={value + "_highlight"}
className="emotion_checkbox"
checked={checkedEmotions[index]}
onChange={() => handleEmotionCheckboxChange(index)}
/>
{value}
</label>
)}
</div>
<div id="colorScaleContainer" style={{ opacity: ((painButton | expressivityButton | AUsButton | asymetryButton) ? "1" : "0") }}>
<ColorLegend range={facialMaskVals} colorScale={["white", facialMaskColor]}
derivedData={derivedData} id={"faceMaskColorLegend"} timelineData={facialTimelineData} timeframe={timeslotValue} />
</div>
<Col id="idPanelContainerIndividual" className="col-2">
<button type="button" className='btn btn-primary btn-sm'>Filter ID(s)</button>
<div >
{idData.map((value, index) =>
<label className="id_checkbox_container" id={value + "_id_"} key={value + "idIndividual"}>
<input
type="checkbox"
id={value + "_id_checkbox"}
className="individual_checkbox"
onChange={handleIdCheckboxChange}
/>
{" " + value}
</label>
)}
</div>
</Col>
</Row>
</Col>
}
<Col id="facialAndSpeechContainer">
{rawFacialData && rawFacialData.length > 0 &&
<Row id="facialActivityContainer">
<div style={{ display: "flex", }}>
<Col id="facialContainer" className='col-2'>
{allFacialAttr &&
<div id="facialQueryPanelContainer" >
<div id="facialQueryPanelButtonsContainer">
<button type="button" className='btn btn-sm btn-outline-primary' onClick={() => handleUpdate("facial")}>Update</button>
<button type="button" className="btn-close" aria-label="Close"
onClick={() => handleUnselectCheckboxes("facial")}></button>
</div>
<QueryPanel allAcousticArg={[]} allMovementArg={[]} allSpeechArg={[]} allFacialArg={allFacialAttr}
checkedState={checkedFacialState} handleCheckboxChange={handleFacialCheckboxChange} idVal={"rawFacialIndividualAttr_"} />
</div>}
</Col>
<Col id="facialHistogramsContainer" className='col-2'>
{facialAttr.map((value) =>
<div className="histogramContainer" key={value + "facHistogram"}>
{<Histogram data={rawFacialData} derivedData={derivedData} attr={value} id={"histogram_" + value}
color={["#41ab5d", "#c7e9c0"]} timeframe={timeslotValue} />}
</div>
)}
</Col>
</div>
</Row>
}
<Row id="facialSpeechAndCorrPanelContainer">
<div id="facialSpeechAnCorrPanelButtons">
<button type="button" className={`btn btn-sm ${speechButton === true ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => handleSpeechPanel("speech")} id="speechPanelButton" >{'Facial & Speech'}</button>
<button type="button" className={`btn btn-sm ${corrButton === true ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => handleSpeechPanel("corr")} id="corrMatrixPanelButton">Correlation</button>
</div>
{speechButton &&
<div id="facialAndSpeechPanelContainer">
<Col id="SpiderChartContainer" className='col-8'>
<div className="spiderComponent" >
<SpiderChart label={'Emotion Intensity'} derivedData={derivedData}
timelineData={facialTimelineData} timeframe={timeslotValue} levels={[0, 0.25, 0.5, 0.75, 1]}
axes={meanEmotionsSoft.reverse()} />
</div>
<div className="spiderComponent" >
<SpiderChart className="spiderChart" label={'AU Intensity'} derivedData={derivedData}
timelineData={facialTimelineData} timeframe={timeslotValue} levels={[0, 1, 2, 3, 4, 5]}
axes={meanAUsSoft.reverse()} />
</div>
</Col>
<Col className='col-4'>
<h6 id="speechLabel">Speech</h6>
{speechDerivedData && speechDerivedData.map((value, index) =>
<div id="speechComponent" key={value + "speech"}>{DBMDict[value]['label'] + ": " + derivedData[value]}</div>
)}
</Col>
</div>}
{corrButton && ((rawFacialData && rawFacialData.length > 0) || (rawAcousticData && rawAcousticData.length > 0) || (rawMovementData && rawMovementData.length > 0)) &&
<Row className="corrMatrixContainer">
<Col id="corrQueryPanelContainer" className='col-2'>
<div >
<div id="corrQuerryButton">
<button type="button" className='btn btn-sm btn-outline-primary' onClick={handleCorrMatrixUpdate}>Update</button>
<button type="button" className="btn-close" aria-label="Close"
onClick={() => handleUnselectCheckboxes("corr")}></button>
</div>
<QueryPanel allAcousticArg={allAcousticAttr} allMovementArg={allMovementAttr} allSpeechArg={[]} allFacialArg={allFacialAttr}
checkedState={checkedCorrMatrixState} handleCheckboxChange={handleCorrMatrixCheckboxChange} idVal={"corrMatrixIndividual_"} />
</div>
</Col>
<Col className='col-2' id="corrMatrixComponentIndividual" >
<CorrelationMatrix data={corrMatrixData} parentId={"corrMatrixComponentIndividual"} />
</Col>
</Row>}
</Row>
</Col>
<Col id="movementAndAcousticContainer">
{rawMovementData && rawMovementData.length > 0 &&
<Row id="headMovementContainer">
<div style={{ display: "flex", }}>
<Col className='col-2' id="movementContainer">
{allMovementAttr &&
<div>
<div id="movementQueryButtonContainer">
<button type="button" className='btn btn-sm btn-outline-primary' onClick={() => handleUpdate("movement")}>Update</button>
<button type="button" className="btn-close" aria-label="Close" onClick={() => handleUnselectCheckboxes("movement")}></button>
</div>
<QueryPanel allAcousticArg={[]} allMovementArg={allMovementAttr} allSpeechArg={[]} allFacialArg={[]}
checkedState={checkedMovementState} handleCheckboxChange={handleMovementCheckboxChange} idVal={"rawMovementIndividualAttr_"} />
</div>
}
</Col>
<Col id="movementHistogramContainer" className='col-2'>
{movementAttr.map((value) =>
<div className="histogramContainer" key={value + "movHistogram"}>
{<Histogram data={rawMovementData} derivedData={derivedData} attr={value}
id={"histogram_" + value} color={["#4292c6", "#c6dbef"]} timeframe={timeslotValue} />}
</div>
)}
</Col>
</div>
</Row>
}
{rawAcousticData && rawAcousticData.length > 0 &&
<Row id="voiceAcousticsContainer">
<div style={{ display: "flex" }}>
<Col className='col-2' id="acousticsContainer">
{allAcousticAttr &&
<div >
<div id="acousticsQueryButtonContainer">
<button type="button" className='btn btn-sm btn-outline-primary' onClick={() => handleUpdate("acoustics")}>Update</button>
<button type="button" className="btn-close" aria-label="Close" onClick={() => handleUnselectCheckboxes("acoustics")}></button>
</div>
<QueryPanel allAcousticArg={allAcousticAttr} allMovementArg={[]} allSpeechArg={[]} allFacialArg={[]}
checkedState={checkedAcousticState} handleCheckboxChange={handleAcousticCheckboxChange} idVal={"rawAcousticIndividualAttr_"} />
</div>}
</Col>
<Col className='col-2' id="acousticsHistogramContainer">
{acousticAttr.map((value) =>
<div className="histogramContainer" key={value + "acoHistogram"}>
{<Histogram data={rawAcousticData} derivedData={derivedData} attr={value}
id={"histogram_" + value} color={["#807dba", "#dadaeb"]} timeframe={timeslotValue} />}
</div>
)}
</Col>
</div>
</Row>
}
</Col>
</Row>
</div>
);
}
export default IndividualPanel;

View File

@@ -0,0 +1,72 @@
import DBMDict from "../DBM_attribute_dict.json"
import "./cohortPanel.css"
const QueryPanel = ({ allAcousticArg, allMovementArg, allSpeechArg, allFacialArg, checkedState, handleCheckboxChange, idVal }) => {
return (
<div className="queryPanel">
{allFacialArg.length > 0 && <h6>Facial</h6>}
{allFacialArg.length > 0 && allFacialArg.map((value, index) =>
<div key={idVal + value + "key"}><label className="">
<input
type="checkbox"
id={idVal + value}
checked={checkedState[index]}
className={idVal}
onChange={() => handleCheckboxChange(index)}
/>
{" " + DBMDict[value]['label']}
</label></div>
)
}
{allMovementArg.length > 0 && <h6>Movement</h6>}
{allMovementArg.length > 0 && allMovementArg.map((value, index) =>
<div key={idVal + value + "key"}><label className="">
<input
type="checkbox"
id={idVal + value}
checked={checkedState[index + allFacialArg.length]}
className={idVal}
onChange={() => handleCheckboxChange(index + allFacialArg.length)}
/>
{" " + DBMDict[value]['label']}
</label></div>
)
}
{allAcousticArg.length > 0 && <h6>Acoustics</h6>}
{allAcousticArg.length > 0 && allAcousticArg.map((value, index) =>
<div key={idVal + value + "key"}><label className="">
<input
type="checkbox"
id={idVal + value}
checked={checkedState[index + allFacialArg.length + allMovementArg.length]}
className={idVal}
onChange={() => handleCheckboxChange(index + allFacialArg.length + allMovementArg.length)}
/>
{" " + DBMDict[value]['label']}
</label></div>
)
}
{allSpeechArg.length > 0 && <h6>Speech</h6>}
{allSpeechArg.map((value, index) =>
<div key={idVal + value + "key"}><label className="">
<input
type="checkbox"
id={idVal + value}
checked={checkedState[index + allFacialArg.length + allAcousticArg.length + allMovementArg.length]}
className={idVal}
onChange={() => handleCheckboxChange(index + allFacialArg.length + allAcousticArg.length + allMovementArg.length)}
/>
{" " + DBMDict[value]['label']}
</label></div>
)
}
</div>
)
}
export default QueryPanel;

View File

@@ -0,0 +1,110 @@
import * as d3 from 'd3';
import $ from "jquery";
import "../index.css"
export default class ScatterplotD3 {
constructor(chart, data, filteredIds, metadata, hideIds, metadataAttrColor) {
if (data) {
if (!d3.select(chart).select('svg').empty()) {
d3.select(chart).select('svg').remove()
}
var parentContainer = document.getElementById("scatterplotContainer")
var width = parentContainer.clientWidth
var height = parentContainer.clientHeight
const svg = d3.select(chart)
.append('svg')
.attr('width', width)
.attr('height', height)
var pca1 = Object.values(data).map(el => el[0])
var pca2 = Object.values(data).map(el => el[1])
var keys = Object.keys(data)
var scatterplotObj = keys.map((el, i) => {
return { "id": el, "pca1": pca1[i], "pca2": pca2[i] }
})
var metadataDict = {}
if (metadata.length > 0) {
$('#metadataAttributes').css("opacity", 1)
var medataAttr = [...new Set(Object.values(metadata).map(el => el['attr']).filter(e => e != null))]
var metadataColor = d3.scaleOrdinal().domain(medataAttr)
.range(['gold', 'blue', 'pink', 'darkviolet', 'cyan', 'black', 'brown', 'greenyellow', 'orchid','mediumpurple'])
var d = Object.values(metadata).map(el => [el['id'], el['attr']])
d.forEach(el => {
metadataDict[el[0]] = el[1]
})
medataAttr.forEach(a => {
$(`#${metadataColor(a)}AttrContainer`).css("opacity", 0.5)
$(`#${metadataColor(a)}color`).text(a)
})
$('#noneAttrContainer').css("opacity", 0.5)
}
else {
$('#metadataAttributes').css("opacity", 0)
}
var xScale = d3.scaleLinear()
.domain([Math.min(...pca1), Math.max(...pca1)]).range([10, width - 20])
var yScale = d3.scaleLinear()
.domain([Math.min(...pca2), Math.max(...pca2)]).range([height - 10, 10])
svg.append("g")
.attr("transform", "translate(-10," + (height - 3) + ")")
.attr("class", "greyAxis")
.call(d3.axisBottom(xScale));
svg.append("g")
.attr("transform", "translate(" + 2 + "," + 5 + " )")
.attr("class", "greyAxis")
.call(d3.axisLeft(yScale));
svg.append('g')
.selectAll("dot")
.data(scatterplotObj)
.enter()
.append("circle")
.attr("id", d => d['id'] + "_pca")
.attr("cx", d => xScale(d['pca1']))
.attr("cy", d => yScale(d['pca2']))
.attr("class", d => "dot dotScatterplot dot_" + d['id'])
.attr("r", 5.5)
.style("fill", d => filteredIds.includes(d['id']) ? "darkorange" : (metadataDict[d['id']] ? metadataAttrColor[d['id']] : "#41ab5d"))
.style("opacity", d => filteredIds.includes(d['id']) ? "0.4" : hideIds ? "0" : "0.4")
.attr("stroke", "grey")
.attr("stroke-width", '1.5px')
svg.append("g")
.call(d3.brush()
.extent([[0, 0], [width, height]])
.on("start brush end", e => brushed(e)));
function brushed(e) {
let value = [];
if (e.selection) {
const [[x0, y0], [x1, y1]] = e.selection;
// default color for scatterplot points
d3.selectAll(".dotScatterplot").style("fill", d => filteredIds.includes(d['id']) ? "darkorange" : (metadataDict[d['id']] ? metadataAttrColor[d['id']] : "#41ab5d"))
// default color for distribution chart points
d3.selectAll(".dotDistr").style("fill", d => filteredIds.includes(d[0]) ? "darkorange" : (metadataDict[d[0]] ? metadataAttrColor[d[0]] : "#41ab5d"))
value = scatterplotObj.filter(d => (x0 <= xScale(d['pca1']) && xScale(d['pca1']) < x1 && y0 <= yScale(d['pca2']) && yScale(d['pca2']) < y1))
.map(e => e.id)
$(".id_checkbox_container").css("color", "black")
value.forEach(e => {
d3.selectAll(".dot_" + e).style("fill", "darkorange")
$("#" + e + "_id_container").css("color", "#fc6a03")
})
}
keys.forEach(k => {
if ($('#' + k + "_id").is(":checked"))
$(".dot_" + k).css("fill", "darkorange")
})
if ($('#metadataButton').hasClass("btn-outline-primary")) {
}
svg.property("value", value).dispatch("input");
}
d3.selectAll(".dotDistr").style("fill", d => filteredIds.includes(d[0]) ? "darkorange" : (metadataDict[d[0]] ? metadataAttrColor[d[0]] : "#41ab5d"))
}
}
}

View File

@@ -0,0 +1,36 @@
import React, { useRef, useEffect } from 'react';
import { ComponentToPrint } from './componentToPrint';
import { exportComponentAsJPEG } from 'react-component-export-image';
import ScatterplotD3 from './scatterplot.d3';
import "./cohortPanel.css"
const Scatterplot = ({ data, filteredIds, metadata, hideIds, metadataAttrColor }) => {
const ref = useRef(null)
useEffect(() => {
const currElement = ref.current
if (data) {
new ScatterplotD3(currElement, data, filteredIds, metadata, hideIds, metadataAttrColor)
}
}, [data])
return (
<React.Fragment>
<div id="scatterplotComponent">
<h6>PCA</h6>
<button type="button" className='btn btn-outline-secondary btn-sm printButton' onClick={() => exportComponentAsJPEG(ref)} >
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-printer" viewBox="0 0 16 16">
<path d="M2.5 8a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z" />
<path d="M5 1a2 2 0 0 0-2 2v2H2a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h1v1a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-1h1a2 2
0 0 0 2-2V7a2 2 0 0 0-2-2h-1V3a2 2 0 0 0-2-2H5zM4 3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2H4V3zm1 5a2 2 0 0 0-2
2v1H2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v-1a2 2 0 0 0-2-2H5zm7 2v3a1 1 0 0
1-1 1H5a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1z" />
</svg>
</button>
</div>
<ComponentToPrint ref={ref} />
</React.Fragment>
)
}
export default Scatterplot;

View File

@@ -0,0 +1,120 @@
import * as d3 from 'd3';
import "../index.css"
import DBMDict from "../DBM_attribute_dict.json"
export default class SpiderChartD3 {
constructor(chart, derivedData, timelineData, timeframe, levels, axes) {
if (!d3.select(chart).select('svg').empty()) {
d3.select(chart).select('svg').remove()
}
var width = 140
var height = 140
const radius = Math.min((width - 5) / 2, (height - 5) / 2)
const totalAxes = axes.length
const radians = 2 * Math.PI
var vertices = []
var values = null
const svg = d3.select(chart)
.append('svg')
.attr('width', width)
.attr('height', height)
const g = svg.append("g")
.attr("transform", "translate(0,5)")
if (Object.keys(derivedData).length === 0) {
return
}
if (parseInt(timeframe) === 0) {
values = Object.keys(derivedData)
.reduce((obj, key) => {
if (axes.includes(key)) {
obj[key] = derivedData[key]
}
return obj
}, {})
}
else {
axes = axes.map(a => a.replace("_mean", ""))
values = Object.keys(timelineData)
.reduce((obj, key) => {
if (axes.includes(key)) {
obj[key] = timelineData[key][parseInt(timeframe) - 1]
}
return obj
}, {})
}
for (var level = 0; level < levels.length - 1; level++) {
var levelFactor = radius * ((level + 1) / levels.length);
for (var i = 0; i < axes.length; i++) {
g.append("line").attr("class", "level_line")
.attr("x1", levelFactor * (1 - Math.sin(i * radians / totalAxes)))
.attr("y1", levelFactor * (1 - Math.cos(i * radians / totalAxes)))
.attr("x2", levelFactor * (1 - Math.sin((i + 1) * radians / totalAxes)))
.attr("y2", levelFactor * (1 - Math.cos((i + 1) * radians / totalAxes)))
.attr("transform", "translate(" + ((width - 2) / 2 - levelFactor) + ", " + ((height - 2) / 2 - levelFactor) + ")")
.attr("stroke", "#dedede")
.attr("stroke-width", "0.5px");
if (i === 1) {
g
.append("svg:text").classed("level-labels", true)
.text(levels[level + 1])
.attr("x", levelFactor * (1 - Math.sin(0)))
.attr("y", levelFactor * (1 - Math.cos(0)))
.attr("transform", "translate(" + ((width - 2) / 2 - levelFactor + 2) + ", " + ((height - 2) / 2 - levelFactor) + ")")
.attr("fill", "grey")
.attr("font-size", "0.6rem")
}
}
}
axes.forEach((a, i) => {
g.append("line").classed("axis-lines", true)
.attr("x1", (width - 2) / 2)
.attr("y1", (height - 2) / 2)
.attr("x2", (width - 2) / 2 * (1 - Math.sin(i * radians / totalAxes)))
.attr("y2", (height - 2) / 2 * (1 - Math.cos(i * radians / totalAxes)))
.attr("stroke", "#dedede")
.attr("stroke-width", "1px");
g
.append("text")
.text(DBMDict[a]['label'].replace("intsoft", "").replace("_mean", "").replace("int", ""))
.attr("text-anchor", "middle")
.attr("x", () => {
var rez = width / 2 * (1 - Math.sin(i * radians / totalAxes))
if (rez < width / 2.2) return rez + 10
else return rez - 10
})
.attr("y", () => {
var rez = height / 2 * (1 - Math.cos(i * radians / totalAxes))
if (rez < height / 2.2) return rez + 5
else return rez - 5
})
.attr("font-size", "0.6rem");
})
axes.forEach((a, i) => {
var x = (width - 2) / 2 * (1 - (parseFloat(Math.max(values[a], 0)) / Math.max(...levels)) * Math.sin(i * radians / totalAxes))
var y = (height - 2) / 2 * (1 - (parseFloat(Math.max(values[a], 0)) / Math.max(...levels)) * Math.cos(i * radians / totalAxes))
g.append("circle")
.attr("r", 1)
.attr("cx", x)
.attr("cy", y)
.attr("fill", "green")
vertices.push([x, y])
});
g.append("polygon")
.attr("points", vertices)
.attr("stroke-width", "2px")
.attr("stroke", "green")
.attr("fill", "green")
.attr("fill-opacity", 0.5)
.attr("stroke-opacity", 0.3)
}
}

View File

@@ -0,0 +1,36 @@
import React, { useRef, useEffect } from 'react';
import { ComponentToPrint } from './componentToPrint';
import { exportComponentAsJPEG } from 'react-component-export-image';
import SpiderChartD3 from './spiderChart.d3';
import "./individualPanel.css"
const SpiderChart = ({ derivedData, timelineData, timeframe, levels, axes, label }) => {
const ref = useRef(null)
useEffect(() => {
const currElement = ref.current
if (derivedData) {
new SpiderChartD3(currElement, derivedData, timelineData, timeframe, levels, axes)
}
}, [derivedData, timelineData, timeframe])
return (
<React.Fragment>
<div className="spiderChartComponent">
<div className="spiderChartLabel">{label}</div>
<button type="button" className='btn btn-outline-secondary btn-sm printButton' onClick={() => exportComponentAsJPEG(ref)}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-printer" viewBox="0 0 16 16">
<path d="M2.5 8a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z" />
<path d="M5 1a2 2 0 0 0-2 2v2H2a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h1v1a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-1h1a2 2
0 0 0 2-2V7a2 2 0 0 0-2-2h-1V3a2 2 0 0 0-2-2H5zM4 3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2H4V3zm1 5a2 2 0 0 0-2
2v1H2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v-1a2 2 0 0 0-2-2H5zm7 2v3a1 1 0 0
1-1 1H5a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1z" />
</svg>
</button>
</div>
<ComponentToPrint ref={ref} />
</React.Fragment>
)
}
export default SpiderChart;

View File

@@ -0,0 +1,23 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.greyAxis line{
stroke: #bababa;
}
.greyAxis path{
stroke: #bababa;
}

View File

@@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@@ -0,0 +1,7 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';