Merge pull request #2 from TreetopFlyer/feature/layout-updates
Feature/layout updates
This commit is contained in:
commit
a32415f952
29
js/store.js
29
js/store.js
@ -77,12 +77,29 @@ export const Grade =(inTest)=>
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ErrorCol =
|
||||||
|
[30, 25, 20, 15, 10, 5, 0, -5, -10, -15 ]
|
||||||
|
const ErrorLUT = [
|
||||||
|
[0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00],
|
||||||
|
[0.00, 0.00, 0.00, 0.00, 0.10, 0.15, 0.10, 0.10, 0.00, 0.00],
|
||||||
|
[0.00, 0.00, 0.02, 0.05, 0.15, 0.20, 0.30, 0.15, 0.05, 0.00],
|
||||||
|
[0.00, 0.02, 0.05, 0.10, 0.20, 0.40, 0.60, 0.30, 0.05, 0.00]
|
||||||
|
];
|
||||||
|
/** @type {(inState:Store.State)=>void} */
|
||||||
|
const ErrorProbability =(inState)=>
|
||||||
|
{
|
||||||
|
const miss = inState.Stim.Value - (inState.Live.Mark.Test?.Stim ?? inState.Stim.Value);
|
||||||
|
inState.Live.Mark.Errs = ErrorLUT[inState.Errs]?.[ErrorCol.indexOf(miss)] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Creates a new Store.Context object that contain the current selections
|
/** Creates a new Store.Context object that contain the current selections
|
||||||
* @type {(inState:Store.State, inTest?:Store.Test)=>Store.Context} */
|
* @type {(inState:Store.State, inTest?:Store.Test)=>Store.Context} */
|
||||||
const Reselect =(inState, inTest)=>
|
const Reselect =(inState, inTest)=>
|
||||||
{
|
{
|
||||||
/** @type {Store.Context} */
|
/** @type {Store.Context} */
|
||||||
const output = { Test:inTest??inState.Live.Test };
|
const output = { Test:inTest??inState.Live.Test, Mark:{User:undefined} };
|
||||||
const column = ColumnMapping[inState.Freq.Value];
|
const column = ColumnMapping[inState.Freq.Value];
|
||||||
if(column && output.Test)
|
if(column && output.Test)
|
||||||
{
|
{
|
||||||
@ -93,7 +110,8 @@ const Reselect =(inState, inTest)=>
|
|||||||
if(plot.Hz == hz)
|
if(plot.Hz == hz)
|
||||||
{
|
{
|
||||||
output.Freq = plot;
|
output.Freq = plot;
|
||||||
output.Mark = inState.Chan.Value ? plot.UserR : plot.UserL;
|
output.Mark.User = inState.Chan.Value ? plot.UserR : plot.UserL;
|
||||||
|
output.Mark.Test = inState.Chan.Value ? plot.TestR : plot.TestL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,8 +197,8 @@ export function Reducer(inState, inAction)
|
|||||||
if(clone.Live.Test && clone.Live.Freq)
|
if(clone.Live.Test && clone.Live.Freq)
|
||||||
{
|
{
|
||||||
const key = clone.Chan.Value == 0 ? "UserL" : "UserR";
|
const key = clone.Chan.Value == 0 ? "UserL" : "UserR";
|
||||||
clone.Live.Mark = Data !== null ? {Stim:clone.Stim.Value, Resp:Data} : undefined;
|
clone.Live.Mark.User = Data !== null ? {Stim:clone.Stim.Value, Resp:Data} : undefined;
|
||||||
clone.Live.Freq[key] = clone.Live.Mark;
|
clone.Live.Freq[key] = clone.Live.Mark.User;
|
||||||
clone.Draw[key] = Redraw(clone.Live.Test, clone.Chan.Value, clone.Stim, true);
|
clone.Draw[key] = Redraw(clone.Live.Test, clone.Chan.Value, clone.Stim, true);
|
||||||
clone.Live.Test.Done = Grade(clone.Live.Test);
|
clone.Live.Test.Done = Grade(clone.Live.Test);
|
||||||
SaveTests(clone);
|
SaveTests(clone);
|
||||||
@ -227,6 +245,7 @@ export function Reducer(inState, inAction)
|
|||||||
clone.Show.Answer = Data;
|
clone.Show.Answer = Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorProbability(clone);
|
||||||
SaveSettings(clone);
|
SaveSettings(clone);
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
@ -328,7 +347,7 @@ export const Initial = Reducer(
|
|||||||
{
|
{
|
||||||
Test: undefined,
|
Test: undefined,
|
||||||
Freq: undefined,
|
Freq: undefined,
|
||||||
Mark: undefined
|
Mark: {User: undefined}
|
||||||
},
|
},
|
||||||
Draw:
|
Draw:
|
||||||
{
|
{
|
||||||
|
47
js/ui.js
47
js/ui.js
@ -5,8 +5,8 @@ import * as Tone from "./tone.js";
|
|||||||
|
|
||||||
/** @typedef {({children, classes}:{children?:preact.ComponentChildren, classes?:string})=>preact.VNode} BasicElement */
|
/** @typedef {({children, classes}:{children?:preact.ComponentChildren, classes?:string})=>preact.VNode} BasicElement */
|
||||||
|
|
||||||
/** @type {({children, icon, light, disabled, inactive, onClick, classes}:{children:preact.VNode, icon?:preact.VNode, light:boolean, disabled:boolean, inactive:boolean, onClick:()=>void, classes?:string})=>preact.VNode} */
|
/** @type {({children, icon, light, disabled, inactive, onClick, classes, classesActive}:{children:preact.VNode, icon?:preact.VNode, light:boolean, disabled:boolean, inactive:boolean, onClick:()=>void, classes?:string, classesActive?:string})=>preact.VNode} */
|
||||||
export function Button({children, icon, light, disabled, inactive, onClick, classes})
|
export function Button({children, icon, light, disabled, inactive, onClick, classes, classesActive})
|
||||||
{
|
{
|
||||||
const [FlashGet, FlashSet] = React.useState(0);
|
const [FlashGet, FlashSet] = React.useState(0);
|
||||||
const handleClick =()=>
|
const handleClick =()=>
|
||||||
@ -19,7 +19,7 @@ export function Button({children, icon, light, disabled, inactive, onClick, clas
|
|||||||
return html`
|
return html`
|
||||||
<button
|
<button
|
||||||
onClick=${handleClick}
|
onClick=${handleClick}
|
||||||
class="relative flex items-stretch rounded-lg text(lg white) border-t(1 solid [#00000011]) border-b(2 solid [#ffffff]) ring-inset ring-black font-sans group transition-all duration-500 ${classes} ${disabled ? "bg-zinc-400" : "bg-earmark"} ${(inactive||disabled) && "cursor-default"}"
|
class="relative flex items-stretch rounded-lg text(lg white) border-t(1 solid [#00000011]) border-b(2 solid [#ffffff]) ring-inset ring-black font-sans group transition-all duration-500 ${classes} ${disabled ? "bg-zinc-400" : (classesActive||"bg-earmark")} ${(inactive||disabled) && "cursor-default"}"
|
||||||
>
|
>
|
||||||
<span class="absolute top-0 left-0 w-full h-full rounded-lg bg-black transition-opacity duration-300 opacity-0 ${(!inactive && !disabled) && "group-hover:opacity-50"}"></span>
|
<span class="absolute top-0 left-0 w-full h-full rounded-lg bg-black transition-opacity duration-300 opacity-0 ${(!inactive && !disabled) && "group-hover:opacity-50"}"></span>
|
||||||
${ FlashGet > 0 && html`<span key=${FlashGet} class="absolute top-0 left-0 w-full h-full rounded-lg bg-green-400 shadow-glow-green-300 animate-flash"></span>` }
|
${ FlashGet > 0 && html`<span key=${FlashGet} class="absolute top-0 left-0 w-full h-full rounded-lg bg-green-400 shadow-glow-green-300 animate-flash"></span>` }
|
||||||
@ -58,11 +58,11 @@ export const Header =()=>
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-buttons w-full mt-2">
|
<div class="box-buttons w-full mt-2">
|
||||||
<p>Patient Error:</p>
|
<p class="px-2">Patient Reliability:</p>
|
||||||
<${Button} inactive=${State.Errs == 0.0} light=${State.Errs == 0.0} classes="flex-1 text-xs" onClick=${()=>Dispatch({Name:"Errs", Data:0.0})}>None<//>
|
<${Button} inactive=${State.Errs == 0} light=${State.Errs == 0} classes="flex-[1.5] text-xs" classesActive="" onClick=${()=>Dispatch({Name:"Errs", Data:0})}>Perfect (Training Mode)<//>
|
||||||
<${Button} inactive=${State.Errs == 0.1} light=${State.Errs == 0.1} classes="flex-1 text-xs" onClick=${()=>Dispatch({Name:"Errs", Data:0.1})}>Slight<//>
|
<${Button} inactive=${State.Errs == 1} light=${State.Errs == 1} classes="flex-1 text-xs" classesActive="bg-yellow-600" onClick=${()=>Dispatch({Name:"Errs", Data:1})}>Good<//>
|
||||||
<${Button} inactive=${State.Errs == 0.3} light=${State.Errs == 0.3} classes="flex-1 text-xs" onClick=${()=>Dispatch({Name:"Errs", Data:0.3})}>Moderate<//>
|
<${Button} inactive=${State.Errs == 2} light=${State.Errs == 2} classes="flex-1 text-xs" classesActive="bg-orange-600" onClick=${()=>Dispatch({Name:"Errs", Data:2})}>Reduced<//>
|
||||||
<${Button} inactive=${State.Errs == 0.6} light=${State.Errs == 0.6} classes="flex-1 text-xs" onClick=${()=>Dispatch({Name:"Errs", Data:0.6})}>Severe<//>
|
<${Button} inactive=${State.Errs == 3} light=${State.Errs == 3} classes="flex-1 text-xs" classesActive="bg-red-600" onClick=${()=>Dispatch({Name:"Errs", Data:3})}>Poor<//>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -95,6 +95,7 @@ export const Display =()=>
|
|||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** @type {BasicElement} */
|
/** @type {BasicElement} */
|
||||||
export const Controls =()=>
|
export const Controls =()=>
|
||||||
{
|
{
|
||||||
@ -113,27 +114,14 @@ export const Controls =()=>
|
|||||||
|
|
||||||
if(State.Live.Freq)
|
if(State.Live.Freq)
|
||||||
{
|
{
|
||||||
const testMark = State.Live.Freq[/** @type {"TestL"|"TestR"}*/(`Test${State.Chan.Value ? "R":"L"}`)];
|
const audible = State.Stim.Value >= (State.Live.Mark.Test?.Stim??0);
|
||||||
const audible = State.Stim.Value >= testMark.Stim;
|
|
||||||
|
|
||||||
const error = State.Stim.Value - testMark.Stim;
|
const errorScaled = State.Live.Mark.Errs;
|
||||||
let errorMapped = error;
|
|
||||||
if(error >= 30){ errorMapped = 0.0; }
|
|
||||||
else if(error >= 25){ errorMapped = 0.1; }
|
|
||||||
else if(error >= 20){ errorMapped = 0.2; }
|
|
||||||
else if(error >= 15){ errorMapped = 0.3; }
|
|
||||||
else if(error >= 10){ errorMapped = 0.5; }
|
|
||||||
else if(error >= 5){ errorMapped = 0.7; }
|
|
||||||
else if(error == 0){ errorMapped = 1.0; }
|
|
||||||
else if(error >= -5){ errorMapped = 0.5; }
|
|
||||||
else if(error >=-10){ errorMapped = 0.1; }
|
|
||||||
else if(error >=-15){ errorMapped = 0.0; }
|
|
||||||
|
|
||||||
const errorScaled = State.Errs*errorMapped;
|
|
||||||
const errorSampled = Math.random() < errorScaled;
|
const errorSampled = Math.random() < errorScaled;
|
||||||
const percieved = errorSampled ? !audible : audible
|
const percieved = errorSampled ? !audible : audible;
|
||||||
|
|
||||||
const handler = percieved ? ()=>playSet(2) : ()=>playSet(0);
|
const handler = percieved ? ()=>playSet(2) : ()=>playSet(0);
|
||||||
console.log("Error:", error, "Error Mapped:", errorMapped, "Error Scaled:", errorScaled, "Error Sampled:", errorSampled);
|
console.log("Audible:", audible, "Error Scaled:", errorScaled, "Error Sampled:", errorSampled, "Percieved", percieved);
|
||||||
timer = setTimeout(handler, 800 + Math.random()*1300);
|
timer = setTimeout(handler, 800 + Math.random()*1300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,6 +193,7 @@ export const Controls =()=>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="text-center mt-2">Response${State.Live.Mark.Errs > 0 && ` (${State.Live.Mark.Errs*100}% Error Chance)` }:</p>
|
||||||
<svg width="80" height="80" preserveAspectRatio="none" viewBox="0 0 79 79" fill="none" class="mx-auto mt-2">
|
<svg width="80" height="80" preserveAspectRatio="none" viewBox="0 0 79 79" fill="none" class="mx-auto mt-2">
|
||||||
<circle fill="url(#metal)" cx="39" cy="40" r="35"></circle>
|
<circle fill="url(#metal)" cx="39" cy="40" r="35"></circle>
|
||||||
<circle fill="url(#metal)" cx="39.5" cy="39.5" r="29.5" transform="rotate(180 39.5 39.5)"></circle>
|
<circle fill="url(#metal)" cx="39.5" cy="39.5" r="29.5" transform="rotate(180 39.5 39.5)"></circle>
|
||||||
@ -267,7 +256,7 @@ export const Controls =()=>
|
|||||||
`}
|
`}
|
||||||
onClick=${()=>Dispatch({Name:"Mark", Data:null })}
|
onClick=${()=>Dispatch({Name:"Mark", Data:null })}
|
||||||
classes="text-sm w-full"
|
classes="text-sm w-full"
|
||||||
disabled=${State.Live.Mark == undefined}
|
disabled=${State.Live.Mark.User == undefined}
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
<//>
|
<//>
|
||||||
@ -284,9 +273,9 @@ export const Audiogram =()=>
|
|||||||
const [State] = Store.Consumer();
|
const [State] = Store.Consumer();
|
||||||
|
|
||||||
const testMarksL = State.Draw.TestL.Points.map(p=>html`<${Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${false}/>`);
|
const testMarksL = State.Draw.TestL.Points.map(p=>html`<${Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${false}/>`);
|
||||||
const userMarksL = State.Draw.UserL.Points.map(p=>html`<${Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${false} classes=${State.Live.Mark == p.Mark ? "stroke-bold":""}/>`);
|
const userMarksL = State.Draw.UserL.Points.map(p=>html`<${Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${false} classes=${State.Live.Mark.User == p.Mark ? "stroke-bold":""}/>`);
|
||||||
const testMarksR = State.Draw.TestR.Points.map(p=>html`<${Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${true} />`);
|
const testMarksR = State.Draw.TestR.Points.map(p=>html`<${Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${true} />`);
|
||||||
const userMarksR = State.Draw.UserR.Points.map(p=>html`<${Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${true} classes=${State.Live.Mark == p.Mark ? "stroke-bold":""}/>`);
|
const userMarksR = State.Draw.UserR.Points.map(p=>html`<${Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${true} classes=${State.Live.Mark.User == p.Mark ? "stroke-bold":""}/>`);
|
||||||
|
|
||||||
const testLinesL = State.Draw.TestL.Paths.map( p=>html`<line class="opacity-60" x1=${p.Head.X} y1=${p.Head.Y} x2=${p.Tail.X} y2=${p.Tail.Y} />`);
|
const testLinesL = State.Draw.TestL.Paths.map( p=>html`<line class="opacity-60" x1=${p.Head.X} y1=${p.Head.Y} x2=${p.Tail.X} y2=${p.Tail.Y} />`);
|
||||||
const userLinesL = State.Draw.UserL.Paths.map( p=>html`<line class="opacity-60" x1=${p.Head.X} y1=${p.Head.Y} x2=${p.Tail.X} y2=${p.Tail.Y} />`);
|
const userLinesL = State.Draw.UserL.Paths.map( p=>html`<line class="opacity-60" x1=${p.Head.X} y1=${p.Head.Y} x2=${p.Tail.X} y2=${p.Tail.Y} />`);
|
||||||
|
7
ts/store.d.ts
vendored
7
ts/store.d.ts
vendored
@ -21,7 +21,12 @@ declare namespace Store {
|
|||||||
type Context = {
|
type Context = {
|
||||||
Test?: Test;
|
Test?: Test;
|
||||||
Freq?: TestFrequency;
|
Freq?: TestFrequency;
|
||||||
Mark?: TestFrequencySample;
|
Mark:
|
||||||
|
{
|
||||||
|
User?: TestFrequencySample,
|
||||||
|
Test?: TestFrequencySample,
|
||||||
|
Errs : number
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
type StatePartSimple =
|
type StatePartSimple =
|
||||||
|
Loading…
Reference in New Issue
Block a user