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

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';