From 5575f307b835ee002a42ce4958ef65a6492c7d09 Mon Sep 17 00:00:00 2001 From: Mohamed Adil Date: Fri, 15 Dec 2023 16:39:21 +0100 Subject: [PATCH] refactor to use model store (#276) --- src/App/App.tsx | 103 ++++++++++++----------- src/App/App.types.ts | 48 +++++++++++ src/App/solveWorker.ts | 3 +- src/Export/Export.tsx | 16 +--- src/Export/exportToDXF.ts | 15 ++-- src/Export/exportToJSON.test.ts | 140 ++++++++++++-------------------- src/Export/exportToJSON.ts | 26 +++--- src/Report/Report.tsx | 24 +++--- 8 files changed, 192 insertions(+), 183 deletions(-) create mode 100644 src/App/App.types.ts diff --git a/src/App/App.tsx b/src/App/App.tsx index f28a72fc..0b0adc02 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -7,7 +7,7 @@ import { on, onMount, } from "solid-js"; -import { createMutable } from "solid-js/store"; +import { createMutable, createStore } from "solid-js/store"; import { Layouter } from "../Layouter/Layouter"; import { Editor } from "../Editor/Editor"; import { Viewer, setRenderAction } from "../Viewer/Viewer"; @@ -25,12 +25,13 @@ import { Parameters, ParametersType } from "../Parameters/Parameters"; import { Login, supabase } from "../Login/Login"; import { Axes } from "../Viewer/objects/Axes"; import { Export } from "../Export/Export"; +import { Model } from "./App.types"; import { Report } from "../Report/Report"; -// todo: refactor to use model store -// todo: split analysis result in convert to 3d object +// todo: parse assignments and results outside solver // todo: then isolate dynamic script loading from static // todo: then refactor editor to be toggled through source-code menu +// refactor app to accept model and onchange type AppProps = { script?: string; }; @@ -84,17 +85,20 @@ export const analysisResults = analyze(nodes, elements, assignments);`; nodeResults: "none", }; const settings = createMutable(defaultSettings); + const [model, setModel] = createStore({ + nodes: [], + elements: [], + assignments: [], + analysisResults: {}, + designResults: [], + }); const [script, setScript] = createSignal(""); const [currentScript, setCurrentScript] = createSignal(""); const [showSave, setShowSave] = createSignal(false); - const [undeformedNodes, setUndeformedNodes] = createSignal([]); const [deformedNodes, setDeformedNodes] = createSignal([]); - const [elements, setElements] = createSignal([]); - const [assignments, setAssignments] = createSignal([]); const [nodeSupports, setNodeSupports] = createSignal([]); const [nodeLoads, setNodeLoads] = createSignal([]); const [elementResults, setElementResults] = createSignal([]); - const [designResults, setDesignResults] = createSignal([]); const [nodeResults, setNodeResults] = createSignal([]); const [error, setError] = createSignal(undefined); const [projectId, setProjectId] = createSignal(undefined); @@ -155,8 +159,7 @@ export const analysisResults = analyze(nodes, elements, assignments);`; }); // on setting deformedShape change: set nodes - const nodes = () => - settings.deformedShape ? deformedNodes() : undeformedNodes(); + const nodes = () => (settings.deformedShape ? deformedNodes() : model.nodes); // on settings.displayScale change: set displayScale const displayScale = () => @@ -190,21 +193,25 @@ export const analysisResults = analyze(nodes, elements, assignments);`; // on undeformed node change: compute deformed nodes createEffect( - on(undeformedNodes, () => { - const deformation = new Map(); - if (nodeResults().length) { - nodeResults().forEach((nodeResult: any) => { - if ("deformation" in nodeResult) - deformation.set(nodeResult.node, nodeResult.deformation); - }); + on( + () => model.nodes, + () => { + const deformation = new Map(); + if (nodeResults().length) { + nodeResults().forEach((nodeResult: any) => { + if ("deformation" in nodeResult) + deformation.set(nodeResult.node, nodeResult.deformation); + }); + } + + setDeformedNodes( + model.nodes.map((v, i) => { + const dis = deformation.get(i) || [0, 0, 0]; + return v.map((vv: any, ii: any) => vv + dis[ii]); + }) + ); } - setDeformedNodes( - undeformedNodes().map((v: any, i) => { - const dis = deformation.get(i) || [0, 0, 0]; - return v.map((vv: any, ii: any) => vv + dis[ii]); - }) - ); - }) + ) ); // on save: solve model from the script, then sync the script @@ -234,14 +241,20 @@ export const analysisResults = analyze(nodes, elements, assignments);`; if (e.data.parameters) setParameters(e.data.parameters); setError(undefined); - setUndeformedNodes(e.data.nodes); - setElements(e.data.elements); - setAssignments(e.data.assignments); + + setModel({ + nodes: e.data.nodes, + elements: e.data.elements, + assignments: e.data.assignments, + analysisResults: e.data.analysisResults, + designResults: e.data.designResults, + }); + setNodeSupports(e.data.nodeSupports); setNodeLoads(e.data.nodeLoads); + setNodeResults(e.data.nodeResults); setElementResults(e.data.elementResults); - setDesignResults(e.data.designResults); Object.assign(settings, e.data.settings); }); @@ -249,10 +262,6 @@ export const analysisResults = analyze(nodes, elements, assignments);`; }; } - function computeCenter(point1: number[], point2: number[]): number[] { - return point1?.map((v, i) => (v + point2[i]) * 0.5); - } - return ( - + {(element) => ( - + {(element, index) => ( {(elementResult) => ( - + - - {/* */} + + + {/* */} ); } + +function computeCenter(point1: number[], point2: number[]): number[] { + return point1?.map((v, i) => (v + point2[i]) * 0.5); +} diff --git a/src/App/App.types.ts b/src/App/App.types.ts new file mode 100644 index 00000000..f93a75c0 --- /dev/null +++ b/src/App/App.types.ts @@ -0,0 +1,48 @@ +export type Model = { + nodes: Node[]; + elements: Element[]; + assignments: Assignment[]; + analysisResults: AnalysisResults; + designResults: DesignResults; +}; + +// nodes and elements +export type Node = [number, number, number]; +export type Element = [number, number]; + +// assignments +export type Assignment = + | supportAssignment + | loadAssignment + | propertyAssignment; +type supportAssignment = { node: number; support: [boolean, boolean, boolean] }; +type loadAssignment = { node: number; load: [number, number, number] }; +type propertyAssignment = { element: number; elasticity: number; area: number }; + +// analysis results +export type AnalysisResults = Record< + string, + (deformationResult | reactionResult | normalResult)[] +>; + +type deformationResult = { + node: number; + deformation: [number, number, number]; +}; +type reactionResult = { + node: number; + reaction: [number, number, number]; +}; +type normalResult = { + element: number; + normal: [number, number]; +}; + +// design results +export type DesignResults = timberResult[]; + +type timberResult = { + element: number; + utilizationFactor: number; + effectiveLength: number; +}; diff --git a/src/App/solveWorker.ts b/src/App/solveWorker.ts index d2cf730b..ce7686b7 100644 --- a/src/App/solveWorker.ts +++ b/src/App/solveWorker.ts @@ -64,12 +64,13 @@ self.onmessage = async (e) => { parameters: e.data.key ? undefined : parameters, nodes, elements, + assignments, nodeSupports, nodeLoads, + analysisResults, nodeResults, elementResults, designResults, - assignments, settings: module?.settings || {}, }); }; diff --git a/src/Export/Export.tsx b/src/Export/Export.tsx index 35a05261..b0fda146 100644 --- a/src/Export/Export.tsx +++ b/src/Export/Export.tsx @@ -3,12 +3,10 @@ import { exportToJSON } from "./exportToJSON"; import FileSaver from "file-saver"; import { createStore } from "solid-js/store"; import { exportToDXF } from "./exportToDXF"; +import { Model } from "../App/App.types"; type ExportProps = { - nodes: any; - elements: any; - assignments: any[]; - analysisResults: any; + model: Model; }; enum ExportType { @@ -16,7 +14,7 @@ enum ExportType { DXF = "DXF", } -type ExportOptions = { +export type ExportOptions = { nodes: boolean; elements: boolean; supports: boolean; @@ -54,13 +52,7 @@ export function Export(props: ExportProps) { [ExportType.DXF]: exportToDXF, }; - const string = exporters[exportType()]( - props.nodes, - props.elements, - props.assignments, - props.analysisResults, - exportOptions - ); + const string = exporters[exportType()](props.model, exportOptions); var blob = new Blob([string], { type: "text/plain;charset=utf-8" }); FileSaver.saveAs(blob, `awatif-model.${exportType()}`); diff --git a/src/Export/exportToDXF.ts b/src/Export/exportToDXF.ts index 60126462..e59f3ba1 100644 --- a/src/Export/exportToDXF.ts +++ b/src/Export/exportToDXF.ts @@ -1,18 +1,13 @@ import { DxfWriter, Units, point3d } from "@tarikjabiri/dxf"; -import { ExportOptions } from "./export.types"; +import { ExportOptions } from "./Export"; +import { Model } from "../App/App.types"; -export function exportToDXF( - nodes: [number, number, number][], - elements: [number, number][], - assignments: any[], - analysisResults: any, - exportOptions: ExportOptions -) { +export function exportToDXF(model: Model, exportOptions: ExportOptions) { const dxf = new DxfWriter(); dxf.setUnits(Units.Meters); - elements.forEach(([e1, e2]) => - dxf.addLine(point3d(...nodes[e1]), point3d(...nodes[e2])) + model.elements.forEach(([e1, e2]) => + dxf.addLine(point3d(...model.nodes[e1]), point3d(...model.nodes[e2])) ); return dxf.stringify(); diff --git a/src/Export/exportToJSON.test.ts b/src/Export/exportToJSON.test.ts index 702878be..d72f1a84 100644 --- a/src/Export/exportToJSON.test.ts +++ b/src/Export/exportToJSON.test.ts @@ -1,45 +1,53 @@ import { exportToJSON } from "./exportToJSON"; -import { ExportOptions } from "./export.types"; +import { ExportOptions } from "./Export"; +import { Model } from "../App/App.types"; +// these tests need to be updated describe("exportToJson", () => { - const nodes = [ - [0, 0, 0], - [5, 0, 0], - [0, 0, 5], - ]; - const elements = [ - [0, 1], - [1, 2], - ]; - const assignments = [ - { - node: 0, - support: [true, true, true], + const model: Model = { + nodes: [ + [0, 0, 0], + [5, 0, 0], + [0, 0, 5], + ], + + elements: [ + [0, 1], + [1, 2], + ], + assignments: [ + { + node: 0, + support: [true, true, true], + }, + { + node: 1, + support: [true, false, false], + }, + { + node: 1, + load: [0, 0, -10], + }, + { + element: 0, + area: 1.2, + elasticity: 200, + }, + ], + analysisResults: { + default: [ + { + node: 1, + deformation: [-0.2083, 0, -0.7976], + }, + { + element: 1, + normal: [14.1421, 14.1421], + }, + ], }, - { - node: 1, - support: [true, false, false], - }, - { - node: 1, - load: [0, 0, -10], - }, - { - element: 0, - area: 1.2, - elasticity: 200, - }, - ]; - const analysisResults = [ - { - node: 1, - deformation: [-0.2083, 0, -0.7976], - }, - { - element: 1, - normal: [14.1421, 14.1421], - }, - ]; + designResults: [], + }; let exportOptions: ExportOptions; beforeEach(() => { @@ -56,13 +64,7 @@ describe("exportToJson", () => { it("should export nodes", () => { exportOptions.nodes = true; - const output = exportToJSON( - nodes, - elements, - assignments, - analysisResults, - exportOptions - ); + const output = exportToJSON(model, exportOptions); expect(output).toBe(`{"nodes":[[0,0,0],[5,0,0],[0,0,5]]}`); }); @@ -70,13 +72,7 @@ describe("exportToJson", () => { it("should export elements", () => { exportOptions.elements = true; - const output = exportToJSON( - nodes, - elements, - assignments, - analysisResults, - exportOptions - ); + const output = exportToJSON(model, exportOptions); expect(output).toBe(`{"elements":[[0,1],[1,2]]}`); }); @@ -84,13 +80,7 @@ describe("exportToJson", () => { it("should export supports", () => { exportOptions.supports = true; - const output = exportToJSON( - nodes, - elements, - assignments, - analysisResults, - exportOptions - ); + const output = exportToJSON(model, exportOptions); expect(output).toBe( `{"assignments":[{"node":0,"support":[true,true,true]},{"node":1,"support":[true,false,false]}]}` @@ -100,13 +90,7 @@ describe("exportToJson", () => { it("should export loads", () => { exportOptions.loads = true; - const output = exportToJSON( - nodes, - elements, - assignments, - analysisResults, - exportOptions - ); + const output = exportToJSON(model, exportOptions); expect(output).toBe(`{"assignments":[{"node":1,"load":[0,0,-10]}]}`); }); @@ -114,13 +98,7 @@ describe("exportToJson", () => { it("should export properties", () => { exportOptions.properties = true; - const output = exportToJSON( - nodes, - elements, - assignments, - analysisResults, - exportOptions - ); + const output = exportToJSON(model, exportOptions); expect(output).toBe( `{"assignments":[{"element":0,"area":1.2,"elasticity":200}]}` @@ -130,13 +108,7 @@ describe("exportToJson", () => { it("should export analysis results", () => { exportOptions.analysisResults = true; - const output = exportToJSON( - nodes, - elements, - assignments, - analysisResults, - exportOptions - ); + const output = exportToJSON(model, exportOptions); expect(output).toBe( `{"analysisResults":[{"node":1,"deformation":[-0.2083,0,-0.7976]},{"element":1,"normal":[14.1421,14.1421]}]}` @@ -153,13 +125,7 @@ describe("exportToJson", () => { analysisResults: true, }; - const output = exportToJSON( - nodes, - elements, - assignments, - analysisResults, - exportOptions - ); + const output = exportToJSON(model, exportOptions); expect(output).toBe( `{"nodes":[[0,0,0],[5,0,0],[0,0,5]],"elements":[[0,1],[1,2]],` + diff --git a/src/Export/exportToJSON.ts b/src/Export/exportToJSON.ts index d97fa9e4..6b654897 100644 --- a/src/Export/exportToJSON.ts +++ b/src/Export/exportToJSON.ts @@ -1,29 +1,27 @@ -import { ExportOptions } from "./export.types"; +import { Model } from "../App/App.types"; +import { ExportOptions } from "./Export"; -export function exportToJSON( - nodes: any, - elements: any, - assignments: any[], - analysisResults: any, - exportOptions: ExportOptions -) { +export function exportToJSON(model: Model, exportOptions: ExportOptions) { const jsonObject = {}; - if (exportOptions.nodes) Object.assign(jsonObject, { nodes }); - if (exportOptions.elements) Object.assign(jsonObject, { elements }); + if (exportOptions.nodes) Object.assign(jsonObject, { nodes: model.nodes }); + if (exportOptions.elements) + Object.assign(jsonObject, { elements: model.elements }); const jsonAssignments = []; if (exportOptions.supports) - jsonAssignments.push(...assignments.filter((a) => a.support)); + jsonAssignments.push(...model.assignments.filter((a: any) => a.support)); if (exportOptions.loads) - jsonAssignments.push(...assignments.filter((a) => a.load)); + jsonAssignments.push(...model.assignments.filter((a: any) => a.load)); if (exportOptions.properties) - jsonAssignments.push(...assignments.filter((a) => a.elasticity || a.area)); + jsonAssignments.push( + ...model.assignments.filter((a: any) => a.elasticity || a.area) + ); if (jsonAssignments.length) Object.assign(jsonObject, { assignments: jsonAssignments }); if (exportOptions.analysisResults) - Object.assign(jsonObject, { analysisResults }); + Object.assign(jsonObject, { analysisResults: model.analysisResults }); return JSON.stringify(jsonObject); } diff --git a/src/Report/Report.tsx b/src/Report/Report.tsx index 1f86efbe..e7061584 100644 --- a/src/Report/Report.tsx +++ b/src/Report/Report.tsx @@ -1,12 +1,9 @@ import { createEffect, createSignal } from "solid-js"; import "./Report.css"; +import { Model } from "../App/App.types"; type ReportProps = { - nodes: any; - elements: any; - assignments: any[]; - analysisResults: any[]; - designResults: any[]; + model: Model; }; // TODO: refactor modal into own Component store in common/Modal.tsx @@ -47,12 +44,15 @@ export function Report(props: ReportProps) { « - Element {elementIndex()} fo {props.analysisResults.length - 1} + Element {elementIndex()} of{" "} + {props.model.designResults.length - 1} @@ -69,12 +69,16 @@ export function Report(props: ReportProps) {

The report goes here:

  • - Utilization factor: - {props.designResults[elementIndex()]?.utilizationFactor} + {`Utilization factor: + ${ + props.model.designResults[elementIndex()]?.utilizationFactor + }`}
  • + {` Effective Length: - {props.designResults[elementIndex()]?.effectiveLength} + ${props.model.designResults[elementIndex()]?.effectiveLength} + `}