better looking simulation
This commit is contained in:
parent
f80086727c
commit
b8bc977a8e
6 changed files with 44 additions and 39 deletions
|
|
@ -1,11 +1,17 @@
|
||||||
details.active {
|
details.active {
|
||||||
background-color: rgba(255, 140, 0, 0.2);
|
/* background-color: rgba(128, 72, 0, 0.855);
|
||||||
|
color: white; */
|
||||||
|
|
||||||
|
box-shadow: rgba(128, 72, 0, 0.856) 0 0 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
details {
|
details {
|
||||||
border: 1px black solid;
|
border: 1px black solid;
|
||||||
/* border-radius: 5px; */
|
/* border-radius: 5px; */
|
||||||
background-color: white;
|
background-color: white;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 4px;
|
||||||
padding-right: 2px;
|
padding-right: 2px;
|
||||||
|
color: black;
|
||||||
|
width: fit-content;
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
@ -58,13 +58,10 @@ export function App() {
|
||||||
if (refRightSideBar.current) {
|
if (refRightSideBar.current) {
|
||||||
|
|
||||||
const el = refRightSideBar.current;
|
const el = refRightSideBar.current;
|
||||||
// console.log('scrolling to', el.scrollHeight);
|
|
||||||
console.log('scrolling to:', el);
|
console.log('scrolling to:', el);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
el.scrollIntoView({block: "end", behavior: "smooth"});
|
el.scrollIntoView({block: "end", behavior: "smooth"});
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
// el.scrollTo(0, el.scrollHeight+1000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,6 +103,11 @@ export function App() {
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const highlightActive = (rtIdx !== undefined) && new Set([...rt[rtIdx].mode].filter(uid => {
|
||||||
|
const state = ast.uid2State.get(uid);
|
||||||
|
return state && state.parent?.kind !== "and";
|
||||||
|
})) || new Set();
|
||||||
|
|
||||||
return <Stack sx={{height:'100vh'}}>
|
return <Stack sx={{height:'100vh'}}>
|
||||||
{/* Top bar */}
|
{/* Top bar */}
|
||||||
<Box
|
<Box
|
||||||
|
|
@ -123,7 +125,7 @@ export function App() {
|
||||||
<Stack direction="row" sx={{height:'calc(100vh - 64px)'}}>
|
<Stack direction="row" sx={{height:'calc(100vh - 64px)'}}>
|
||||||
{/* main */}
|
{/* main */}
|
||||||
<Box sx={{flexGrow:1, overflow:'auto'}}>
|
<Box sx={{flexGrow:1, overflow:'auto'}}>
|
||||||
<VisualEditor {...{ast, setAST, rt: rt.at(rtIdx!), setRT, errors, setErrors, mode}}/>
|
<VisualEditor {...{ast, setAST, rt: rt.at(rtIdx!), setRT, errors, setErrors, mode, highlightActive}}/>
|
||||||
</Box>
|
</Box>
|
||||||
{/* right sidebar */}
|
{/* right sidebar */}
|
||||||
<Box
|
<Box
|
||||||
|
|
@ -133,10 +135,8 @@ export function App() {
|
||||||
flex: '0 0 content',
|
flex: '0 0 content',
|
||||||
height: 'calc(100vh-32px)',
|
height: 'calc(100vh-32px)',
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
// paddingRight: 1,
|
|
||||||
// paddingLeft: 1,
|
|
||||||
}}>
|
}}>
|
||||||
<ShowAST {...{...ast, rt: rt.at(rtIdx!)}}/>
|
<ShowAST {...{...ast, rt: rt.at(rtIdx!), highlightActive}}/>
|
||||||
<br/>
|
<br/>
|
||||||
<div ref={refRightSideBar}>
|
<div ref={refRightSideBar}>
|
||||||
<RTHistory {...{ast, rt, rtIdx, setTime, setRTIdx, refRightSideBar}}/>
|
<RTHistory {...{ast, rt, rtIdx, setTime, setRTIdx, refRightSideBar}}/>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Dispatch, SetStateAction } from "react";
|
import { Dispatch, Ref, SetStateAction } from "react";
|
||||||
import { Statechart, stateDescription } from "../statecharts/abstract_syntax";
|
import { Statechart, stateDescription } from "../statecharts/abstract_syntax";
|
||||||
import { BigStep, Environment, Mode, RaisedEvent } from "../statecharts/runtime_types";
|
import { BigStep, Environment, Mode, RaisedEvent } from "../statecharts/runtime_types";
|
||||||
import { formatTime } from "./util";
|
import { formatTime } from "./util";
|
||||||
|
|
@ -10,23 +10,26 @@ type RTHistoryProps = {
|
||||||
ast: Statechart,
|
ast: Statechart,
|
||||||
setRTIdx: Dispatch<SetStateAction<number|undefined>>,
|
setRTIdx: Dispatch<SetStateAction<number|undefined>>,
|
||||||
setTime: Dispatch<SetStateAction<TimeMode>>,
|
setTime: Dispatch<SetStateAction<TimeMode>>,
|
||||||
|
refRightSideBar: Ref<HTMLDivElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RTHistory({rt, rtIdx, ast, setRTIdx, setTime}: RTHistoryProps) {
|
export function RTHistory({rt, rtIdx, ast, setRTIdx, setTime, refRightSideBar}: RTHistoryProps) {
|
||||||
function gotoRt(idx: number, timestamp: number) {
|
function gotoRt(idx: number, timestamp: number) {
|
||||||
setRTIdx(idx);
|
setRTIdx(idx);
|
||||||
setTime({kind: "paused", simtime: timestamp});
|
setTime({kind: "paused", simtime: timestamp});
|
||||||
}
|
}
|
||||||
|
|
||||||
return rt.map((rt, idx) => <>
|
return <div>
|
||||||
<div className={"runtimeState"+(idx===rtIdx?" active":"")} onClick={() => gotoRt(idx, rt.simtime)}>
|
{rt.map((r, idx) => <>
|
||||||
<div>({formatTime(rt.simtime)}, {rt.inputEvent || "<init>"})</div>
|
<div className={"runtimeState"+(idx===rtIdx?" active":"")} onClick={() => gotoRt(idx, r.simtime)}>
|
||||||
<ShowMode mode={rt.mode} statechart={ast}/>
|
<div>({formatTime(r.simtime)}, {r.inputEvent || "<init>"})</div>
|
||||||
<ShowEnvironment environment={rt.environment}/>
|
<ShowMode mode={r.mode} statechart={ast}/>
|
||||||
{rt.outputEvents.length>0 && <div>
|
<ShowEnvironment environment={r.environment}/>
|
||||||
{rt.outputEvents.map((e:RaisedEvent) => '^'+e.name).join(', ')}
|
{r.outputEvents.length>0 && <div>
|
||||||
|
{r.outputEvents.map((e:RaisedEvent) => '^'+e.name).join(', ')}
|
||||||
</div>}
|
</div>}
|
||||||
</div></>);
|
</div></>)}
|
||||||
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -45,13 +48,5 @@ function ShowMode(props: {mode: Mode, statechart: Statechart}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActiveLeafs(mode: Mode, sc: Statechart) {
|
function getActiveLeafs(mode: Mode, sc: Statechart) {
|
||||||
const toDelete = [];
|
return new Set([...mode].filter(uid => sc.uid2State.get(uid)?.children?.length === 0));
|
||||||
for (const stateA of mode) {
|
|
||||||
for (const stateB of mode) {
|
|
||||||
if (sc.uid2State.get(stateA)!.parent === sc.uid2State.get(stateB)) {
|
|
||||||
toDelete.push(stateB);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mode.difference(new Set(toDelete));
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,11 @@ export function ShowAction(props: {action: Action}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ShowAST(props: {root: ConcreteState | PseudoState, transitions: Map<string, Transition[]>, rt: RT_Statechart | undefined}) {
|
export function ShowAST(props: {root: ConcreteState | PseudoState, transitions: Map<string, Transition[]>, rt: RT_Statechart | undefined, highlightActive: Set<string>}) {
|
||||||
const description = stateDescription(props.root);
|
const description = stateDescription(props.root);
|
||||||
const outgoing = props.transitions.get(props.root.uid) || [];
|
const outgoing = props.transitions.get(props.root.uid) || [];
|
||||||
|
|
||||||
return <details open={true} className={props.rt?.mode.has(props.root.uid) ? "active" : ""}>
|
return <details open={true} className={props.highlightActive.has(props.root.uid) ? "active" : ""}>
|
||||||
<summary>{props.root.kind}: {description}</summary>
|
<summary>{props.root.kind}: {description}</summary>
|
||||||
|
|
||||||
{props.root.kind !== "pseudo" && props.root.entryActions.length>0 &&
|
{props.root.kind !== "pseudo" && props.root.entryActions.length>0 &&
|
||||||
|
|
@ -51,7 +51,7 @@ export function ShowAST(props: {root: ConcreteState | PseudoState, transitions:
|
||||||
}
|
}
|
||||||
{props.root.kind !== "pseudo" && props.root.children.length>0 &&
|
{props.root.kind !== "pseudo" && props.root.children.length>0 &&
|
||||||
props.root.children.map(child =>
|
props.root.children.map(child =>
|
||||||
<ShowAST root={child} transitions={props.transitions} rt={props.rt} />
|
<ShowAST root={child} transitions={props.transitions} rt={props.rt} highlightActive={props.highlightActive} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{outgoing.length>0 &&
|
{outgoing.length>0 &&
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
/* fill-opacity: 0.2; */
|
/* fill-opacity: 0.2; */
|
||||||
/* stroke: rgb(100, 149, 237); */
|
/* stroke: rgb(100, 149, 237); */
|
||||||
/* stroke: */
|
/* stroke: */
|
||||||
filter: drop-shadow( 0px 0px 6px rgba(0, 150, 255, 0.8));
|
filter: drop-shadow( 0px 0px 6px rgba(128, 72, 0, 0.856));
|
||||||
/* stroke-width: 3px; */
|
/* stroke-width: 3px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,14 +61,16 @@ export const sides: [RountanglePart, (r:Rect2D)=>Line2D][] = [
|
||||||
export type InsertMode = "and"|"or"|"pseudo"|"shallow"|"deep"|"transition"|"text";
|
export type InsertMode = "and"|"or"|"pseudo"|"shallow"|"deep"|"transition"|"text";
|
||||||
|
|
||||||
type VisualEditorProps = {
|
type VisualEditorProps = {
|
||||||
|
ast: Statechart,
|
||||||
setAST: Dispatch<SetStateAction<Statechart>>,
|
setAST: Dispatch<SetStateAction<Statechart>>,
|
||||||
rt: BigStep|undefined,
|
rt: BigStep|undefined,
|
||||||
errors: TraceableError[],
|
errors: TraceableError[],
|
||||||
setErrors: Dispatch<SetStateAction<TraceableError[]>>,
|
setErrors: Dispatch<SetStateAction<TraceableError[]>>,
|
||||||
mode: InsertMode,
|
mode: InsertMode,
|
||||||
|
highlightActive: Set<string>,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function VisualEditor({setAST, rt, errors, setErrors, mode}: VisualEditorProps) {
|
export function VisualEditor({ast, setAST, rt, errors, setErrors, mode, highlightActive}: VisualEditorProps) {
|
||||||
const [historyState, setHistoryState] = useState<HistoryState>({current: emptyState, history: [], future: []});
|
const [historyState, setHistoryState] = useState<HistoryState>({current: emptyState, history: [], future: []});
|
||||||
|
|
||||||
const state = historyState.current;
|
const state = historyState.current;
|
||||||
|
|
@ -680,6 +682,8 @@ export function VisualEditor({setAST, rt, errors, setErrors, mode}: VisualEditor
|
||||||
|
|
||||||
const active = rt?.mode || new Set();
|
const active = rt?.mode || new Set();
|
||||||
|
|
||||||
|
console.log(highlightActive);
|
||||||
|
|
||||||
const rootErrors = errors.filter(({shapeUid}) => shapeUid === "root").map(({message}) => message);
|
const rootErrors = errors.filter(({shapeUid}) => shapeUid === "root").map(({message}) => message);
|
||||||
|
|
||||||
return <svg width="4000px" height="4000px"
|
return <svg width="4000px" height="4000px"
|
||||||
|
|
@ -711,8 +715,8 @@ export function VisualEditor({setAST, rt, errors, setErrors, mode}: VisualEditor
|
||||||
|
|
||||||
{(rootErrors.length>0) && <text className="error" x={5} y={20}>{rootErrors.join(' ')}</text>}
|
{(rootErrors.length>0) && <text className="error" x={5} y={20}>{rootErrors.join(' ')}</text>}
|
||||||
|
|
||||||
{state.rountangles.map(rountangle =>
|
{state.rountangles.map(rountangle => {
|
||||||
<RountangleSVG
|
return <RountangleSVG
|
||||||
key={rountangle.uid}
|
key={rountangle.uid}
|
||||||
rountangle={rountangle}
|
rountangle={rountangle}
|
||||||
selected={selection.find(r => r.uid === rountangle.uid)?.parts || []}
|
selected={selection.find(r => r.uid === rountangle.uid)?.parts || []}
|
||||||
|
|
@ -720,8 +724,8 @@ export function VisualEditor({setAST, rt, errors, setErrors, mode}: VisualEditor
|
||||||
errors={errors
|
errors={errors
|
||||||
.filter(({shapeUid}) => shapeUid === rountangle.uid)
|
.filter(({shapeUid}) => shapeUid === rountangle.uid)
|
||||||
.map(({message}) => message)}
|
.map(({message}) => message)}
|
||||||
active={active.has(rountangle.uid)}
|
active={highlightActive.has(rountangle.uid)}
|
||||||
/>)}
|
/>})}
|
||||||
|
|
||||||
{state.diamonds.map(diamond => <>
|
{state.diamonds.map(diamond => <>
|
||||||
<DiamondSVG
|
<DiamondSVG
|
||||||
|
|
@ -732,7 +736,7 @@ export function VisualEditor({setAST, rt, errors, setErrors, mode}: VisualEditor
|
||||||
errors={errors
|
errors={errors
|
||||||
.filter(({shapeUid}) => shapeUid === diamond.uid)
|
.filter(({shapeUid}) => shapeUid === diamond.uid)
|
||||||
.map(({message}) => message)}
|
.map(({message}) => message)}
|
||||||
active={active.has(diamond.uid)}/>
|
active={false}/>
|
||||||
</>)}
|
</>)}
|
||||||
|
|
||||||
{state.history.map(history => <>
|
{state.history.map(history => <>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue