cursor, initialization

This commit is contained in:
Seth Trowbridge 2022-12-10 13:24:31 -05:00
parent da9f569308
commit 78049da309
3 changed files with 39 additions and 27 deletions

View File

@ -48,32 +48,30 @@ const Controls =()=>
const Audiogram =()=> const Audiogram =()=>
{ {
const [State] = Store.Consumer(); const [State] = Store.Consumer();
/** @type {(inAmount:number)=>string} */ const Perc =(inAmount)=> (inAmount*100)+"%";
const testMarksL = State.Draw.TestL.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${false}/>`); const testMarksL = State.Draw.TestL.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${false}/>`);
const userMarksL = State.Draw.UserL.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(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`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${false} classes=${State.Live.Mark == p.Mark ? "stroke-bold":""}/>`);
const testMarksR = State.Draw.TestR.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${true} />`); const testMarksR = State.Draw.TestR.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${true} />`);
const userMarksR = State.Draw.UserR.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(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`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark?.Resp} right=${true} classes=${State.Live.Mark == p.Mark ? "stroke-bold":""}/>`);
const testLinesL = State.Draw.TestL.Paths.map( p=>html`<line class="opacity-60" x1=${Perc(p.Head.X)} y1=${Perc(p.Head.Y)} x2=${Perc(p.Tail.X)} y2=${Perc(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=${Perc(p.Head.X)} y1=${Perc(p.Head.Y)} x2=${Perc(p.Tail.X)} y2=${Perc(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 testLinesR = State.Draw.TestR.Paths.map( p=>html`<line class="opacity-60" x1=${Perc(p.Head.X)} y1=${Perc(p.Head.Y)} x2=${Perc(p.Tail.X)} y2=${Perc(p.Tail.Y)} />`); const testLinesR = State.Draw.TestR.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 userLinesR = State.Draw.UserR.Paths.map( p=>html`<line class="opacity-60" x1=${Perc(p.Head.X)} y1=${Perc(p.Head.Y)} x2=${Perc(p.Tail.X)} y2=${Perc(p.Tail.Y)} />`); const userLinesR = State.Draw.UserR.Paths.map( p=>html`<line class="opacity-60" x1=${p.Head.X} y1=${p.Head.Y} x2=${p.Tail.X} y2=${p.Tail.Y} />`);
return html` return html`
<svg class="absolute top-0 w-full h-full overflow-visible stroke(blue-700 bold draw) opacity-50">${testMarksL}${testLinesL}</svg> <svg class="absolute top-0 w-full h-full overflow-visible stroke(blue-700 bold draw) opacity-50">${testMarksL}${testLinesL}</svg>
<svg class="absolute top-0 w-full h-full overflow-visible stroke(red-700 bold draw) opacity-50">${testMarksR}${testLinesR}</svg> <svg class="absolute top-0 w-full h-full overflow-visible stroke(red-700 bold draw) opacity-50">${testMarksR}${testLinesR}</svg>
<svg class="absolute top-0 w-full h-full overflow-visible stroke(blue-700 2 draw)">${userMarksL}${userLinesL}</svg> <svg class="absolute top-0 w-full h-full overflow-visible stroke(blue-700 2 draw)">${userMarksL}${userLinesL}</svg>
<svg class="absolute top-0 w-full h-full overflow-visible stroke(red-700 2 draw)">${userMarksR}${userLinesR}</svg> <svg class="absolute top-0 w-full h-full overflow-visible stroke(red-700 2 draw)">${userMarksR}${userLinesR}</svg>
<svg class="absolute top-0 w-full h-full overflow-visible" > <svg class="absolute top-0 w-full h-full overflow-visible transition-all duration-500" style=${{top:State.Draw.Cross?.Y, left:State.Draw.Cross?.X}}>
<ellipse cx="0" cy="0" rx="5" ry="30" fill="url(#glow)"></ellipse> <ellipse cx="0" cy="0" rx="8" ry="30" fill="url(#glow)"></ellipse>
<ellipse cx="0" cy="0" rx="30" ry="5" fill="url(#glow)"></ellipse> <ellipse cx="0" cy="0" rx="30" ry="8" fill="url(#glow)"></ellipse>
<defs> <defs>
<radialGradient id="glow"> <radialGradient id="glow">
<stop stop-color="blue" stop-opacity="0.6" offset="0.0"></stop> <stop stop-color=${State.Chan.Value ? "red" : "blue"} stop-opacity="0.6" offset="0.0"></stop>
<stop stop-color="blue" stop-opacity="0.3" offset="0.2"></stop> <stop stop-color=${State.Chan.Value ? "red" : "blue"} stop-opacity="0.3" offset="0.2"></stop>
<stop stop-color="blue" stop-opacity="0.0" offset="1.0"></stop> <stop stop-color=${State.Chan.Value ? "red" : "blue"} stop-opacity="0.0" offset="1.0"></stop>
</radialGradient> </radialGradient>
</defs> </defs>
</svg> </svg>

View File

@ -25,6 +25,9 @@ export const ColumnLookup =(inFrequency)=>
return false; return false;
}; };
/** @type {(inDecimal:number)=>string} */
const Perc =(inDecimal)=> `${inDecimal*100}%`;
/** 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)=>
@ -69,8 +72,8 @@ const Redraw =(inTest, inChan, inStim, inIsUser)=>
{ {
/** @type {Store.DrawPoint} */ /** @type {Store.DrawPoint} */
const point = { const point = {
X: lookup[1], X: Perc(lookup[1]),
Y: (mark.Stim - inStim.Min)/(inStim.Max - inStim.Min), Y: Perc((mark.Stim - inStim.Min)/(inStim.Max - inStim.Min)),
Mark: mark Mark: mark
}; };
output.Points.push(point); output.Points.push(point);
@ -81,14 +84,21 @@ const Redraw =(inTest, inChan, inStim, inIsUser)=>
{ {
/** @type {Store.DrawLine} */ /** @type {Store.DrawLine} */
const line = {Head:output.Points[i-1], Tail:output.Points[i]}; const line = {Head:output.Points[i-1], Tail:output.Points[i]};
if(line.Head.Mark.Resp && line.Tail.Mark.Resp) if(line.Head.Mark?.Resp && line.Tail.Mark?.Resp)
{ {
output.Paths.push(line); output.Paths.push(line);
} }
} }
} }
return output; return output;
} };
/** Create a new cursor position from the state
* @type {(inState:Store.State)=>Store.DrawPoint} */
const Reposition =(inState)=> ({
X: Perc(ColumnMapping[inState.Freq.Value][1]),
Y: Perc((inState.Stim.Value - inState.Stim.Min)/(inState.Stim.Max - inState.Stim.Min))
});
/** @type {Store.Reducer} */ /** @type {Store.Reducer} */
export function Reducer(inState, inAction) export function Reducer(inState, inAction)
@ -100,7 +110,9 @@ export function Reducer(inState, inAction)
{ {
const test = clone.Tests[Data]; const test = clone.Tests[Data];
clone.Live = Reselect(clone, test); clone.Live = Reselect(clone, test);
clone.Draw = { clone.Draw =
{
Cross: Reposition(clone),
UserL: Redraw(test, 0, clone.Stim, true ), UserL: Redraw(test, 0, clone.Stim, true ),
UserR: Redraw(test, 1, clone.Stim, true ), UserR: Redraw(test, 1, clone.Stim, true ),
TestL: Redraw(test, 0, clone.Stim, false), TestL: Redraw(test, 0, clone.Stim, false),
@ -123,6 +135,8 @@ export function Reducer(inState, inAction)
tone.Value += Data*tone.Step; tone.Value += Data*tone.Step;
tone.Value = Math.max(tone.Value, tone.Min); tone.Value = Math.max(tone.Value, tone.Min);
tone.Value = Math.min(tone.Value, tone.Max); tone.Value = Math.min(tone.Value, tone.Max);
clone.Draw.Cross = Reposition(clone);
if(Name != "Stim") if(Name != "Stim")
{ {
clone.Live = Reselect(clone); clone.Live = Reselect(clone);
@ -133,7 +147,7 @@ export function Reducer(inState, inAction)
} }
/** @type {Store.State} */ /** @type {Store.State} */
export const Initial = export const Initial = Reducer(
{ {
Chan: { Min:0, Max:1, Value:0, Step:1 }, Chan: { Min:0, Max:1, Value:0, Step:1 },
Freq: { Min:2, Max:8, Value:2, Step:1 }, Freq: { Min:2, Max:8, Value:2, Step:1 },
@ -166,7 +180,7 @@ export const Initial =
] ]
} }
] ]
}; }, {Name:"Test", Data:0});
/** @type {preact.Context<Store.Binding>} */ /** @type {preact.Context<Store.Binding>} */
export const Context = React.createContext([Initial, (_a)=>{}]); export const Context = React.createContext([Initial, (_a)=>{}]);
@ -175,7 +189,7 @@ export const Context = React.createContext([Initial, (_a)=>{}]);
export const Provider =(props)=> export const Provider =(props)=>
{ {
/** @type {Store.Binding} */ /** @type {Store.Binding} */
const reducer = React.useReducer(Reducer, Initial, ()=>Reducer(Initial, {Name:"Test", Data:0})); const reducer = React.useReducer(Reducer, Initial);
return React.createElement(Context.Provider, {value:reducer, children:props.children}); return React.createElement(Context.Provider, {value:reducer, children:props.children});
}; };

6
store.d.ts vendored
View File

@ -25,7 +25,7 @@ declare namespace Store {
Freq: Range; Freq: Range;
Stim: Range; Stim: Range;
Live: Context; Live: Context;
Draw: DrawTest; Draw: DrawChart;
Tests: Array<Test>; Tests: Array<Test>;
}; };
@ -42,10 +42,10 @@ declare namespace Store {
type PlotKeyTest = "TestL" | "TestR"; type PlotKeyTest = "TestL" | "TestR";
type PlotKey = PlotKeyUser | PlotKeyTest; type PlotKey = PlotKeyUser | PlotKeyTest;
type DrawPoint = { X: number; Y: number; Mark: TestFrequencySample }; type DrawPoint = { X: string; Y: string; Mark?: TestFrequencySample };
type DrawLine = { Head:DrawPoint, Tail:DrawPoint}; type DrawLine = { Head:DrawPoint, Tail:DrawPoint};
type DrawGroup = { Points: Array<DrawPoint>; Paths: Array<DrawLine> }; type DrawGroup = { Points: Array<DrawPoint>; Paths: Array<DrawLine> };
type DrawTest = { UserL: DrawGroup, UserR: DrawGroup, TestL: DrawGroup, TestR: DrawGroup }; type DrawChart = { Cross?:DrawPoint, UserL: DrawGroup, UserR: DrawGroup, TestL: DrawGroup, TestR: DrawGroup };
type Binding = [state:State, dispatch:(inAction:Action)=>void] type Binding = [state:State, dispatch:(inAction:Action)=>void]
} }