Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model refactor api expr to circ #1238

Merged
merged 30 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a4fb430
add main files back in
TGCrystal Feb 9, 2023
2cc1949
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal Mar 16, 2023
daf7d00
expr to circuit refactor progress
TGCrystal Mar 16, 2023
e698b47
vscode issues
TGCrystal Mar 27, 2023
4085471
Merge branch 'model_refactor_api_views1' into model_refactor_api_expr…
TGCrystal Mar 27, 2023
b290cf7
Reimplement basic circuit tree to component algo
TGCrystal Mar 30, 2023
f237b81
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal Apr 16, 2023
a319216
Progress on Result Type
TGCrystal Apr 16, 2023
2e74a00
treeToCircuitCore working better
TGCrystal Apr 17, 2023
19f1f60
Incorporate Result into the rest of Expr to Circ algo
TGCrystal Apr 21, 2023
89267df
Attempt at fixing expression parser imports
TGCrystal Apr 21, 2023
320bbf8
Add toBeOk matcher
TGCrystal Apr 22, 2023
818cfb6
Get basic test working
TGCrystal Apr 23, 2023
2c8b5c4
Fix error case tests
TGCrystal Apr 24, 2023
82d736c
Merge branch 'model_refactor_api_headless_view' into model_refactor_a…
TGCrystal Apr 24, 2023
c38943c
Uncomment last error test
TGCrystal Apr 25, 2023
7168f31
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal Apr 25, 2023
1227b7a
Uncommented some more
TGCrystal Apr 25, 2023
4d12152
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal Apr 25, 2023
c49e0bd
Comment popup test
TGCrystal Apr 25, 2023
1fdb90d
Finsh cleaning up tests
TGCrystal Apr 26, 2023
258df28
Final changes before review
TGCrystal Apr 26, 2023
0f0859b
Apply Leon's suggestions
TGCrystal May 6, 2023
ba6df30
Add `.toIncludeError` matcher
TGCrystal May 6, 2023
274499c
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal May 7, 2023
095f0da
Fix lingering merge issue
TGCrystal May 25, 2023
bb321ff
Remove overzealous result usage
TGCrystal May 31, 2023
3ef208b
Final cleaning
TGCrystal May 31, 2023
0078640
Merge branch 'model_refactor_api' into model_refactor_api_expr_to_circ
TGCrystal Feb 6, 2024
32ffb96
Fix suggestions, update branch
TGCrystal Feb 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 8 additions & 5 deletions linting/.imports.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,17 @@ module.exports = {
"from": [
"./src/site/pages/!(analog)/**",
"./src/app/!(analog|core)/**",
"./src/app/analog/!(public)/**",
"./src/app/core/!(public)/**",
"./src/app/analog/!(public|utils)/**",
"./src/app/core/!(public|utils)/**",
],
},
{
"target": "./src/site/pages/digital/**",
"from": [
"./src/site/pages/!(digital)/**",
"./src/app/!(digital|core)/**",
"./src/app/digital/!(public)/**",
"./src/app/core/!(public)/**",
"./src/app/digital/!(public|utils)/**",
"./src/app/core/!(public|utils)/**",
],
},
{
Expand Down Expand Up @@ -163,7 +163,10 @@ module.exports = {
},
{
"target": "./src/app/core/public/**",
"from": ["./src/app/!(core)/**", "./src/app/core/!(internal|public)/**"],
"from": [
"./src/app/!(core)/**",
"./src/app/core/!(internal|public|utils)/**",
],
},
{
"target": "./src/app/digital/public/**",
Expand Down
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@
"@babel/preset-env": "^7.18.6",
"@babel/preset-react": "^7.18.6",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.7",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^14.2.1",
"@types/jest": "^29.0.0",
"@types/jest": "^29.5.1",
"@types/node": "^18.7.16",
"@types/prompts": "^2.0.14",
"@types/react": "^18.0.18",
"@types/react-dom": "^18.0.6",
"@types/react-redux": "^7.1.24",
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"babel-loader": "^8.2.5",
"chalk": "^5.0.1",
"copyfiles": "^2.4.1",
Expand All @@ -44,13 +44,13 @@
"dotenv": "^16.0.1",
"dotenv-expand": "^8.0.3",
"emscript-loader": "^1.0.6",
"eslint": "^8.19.0",
"eslint": "^8.39.0",
"eslint-config-react-app": "^7.0.1",
"eslint-import-resolver-typescript": "^3.5.1",
"eslint-plugin-align-import": "^1.0.0",
"eslint-plugin-deprecation": "^1.3.2",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^26.5.3",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-jest-formatting": "^3.1.0",
"eslint-plugin-jsdoc": "^39.3.13",
"eslint-plugin-jsx-a11y": "^6.6.0",
Expand All @@ -64,9 +64,9 @@
"friendly-errors-webpack-plugin": "^1.7.0",
"html-webpack-plugin": "^5.5.0",
"interpolate-html-plugin": "^4.0.0",
"jest": "^28.1.2",
"jest-environment-jsdom": "^28.1.2",
"jest-watch-typeahead": "^1.1.0",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"jest-watch-typeahead": "^2.2.2",
"mini-css-extract-plugin": "^2.6.1",
"open": "^8.4.0",
"ora": "^6.1.2",
Expand All @@ -78,9 +78,9 @@
"react-refresh-typescript": "^2.0.9",
"sass": "^1.53.0",
"sass-loader": "^13.0.2",
"ts-jest": "^28.0.5",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2",
"ts-morph": "^15.1.0",
"ts-morph": "^18.0.0",
"typescript": "^5.0.4",
"url-loader": "^4.1.1",
"webpack": "^5.73.0",
Expand Down
1 change: 1 addition & 0 deletions src/app/core/public/api/Circuit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface Circuit {
getObj(id: GUID): Obj | undefined;
getObjs(): Obj[];
getComponents(): Component[];
getWires(): Wire[];
getComponentInfo(kind: string): ComponentInfo | undefined;

// Object manipulation
Expand Down
11 changes: 7 additions & 4 deletions src/app/core/public/api/impl/Circuit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import {GUID} from "core/internal";
import {RenderHelper} from "core/internal/view/rendering/RenderHelper";
import {RenderOptions} from "core/internal/view/rendering/RenderOptions";

import {Camera} from "../Camera";
import {Circuit} from "../Circuit";
import {Selections} from "../Selections";
import {isObjComponent} from "../Utilities";
import {Camera} from "../Camera";
import {Circuit} from "../Circuit";
import {Selections} from "../Selections";
import {isObjComponent, isObjWire} from "../Utilities";

import {CameraImpl} from "./Camera";
import {CircuitState, CircuitTypes} from "./CircuitState";
Expand Down Expand Up @@ -133,6 +133,9 @@ export function CircuitImpl<CircuitT extends Circuit, T extends CircuitTypes>(st
getComponents(): T["Component[]"] {
return this.getObjs().filter(isObjComponent);
},
getWires(): T["Wire[]"] {
return this.getObjs().filter(isObjWire);
},
getComponentInfo(kind: string): T["ComponentInfo"] | undefined {
// TODO[.](kevin) - getComponentInfo should probably return a Result right?
// Or should we add a method to check if a component exists?
Expand Down
24 changes: 24 additions & 0 deletions src/app/core/utils/CircuitUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {Graph} from "math/Graph";
import {Circuit} from "../public/api/Circuit";

/**
* This function converts the provided circuit to a graph where the nodes are components and edges are wires.
* Both are represented by guids rather than object references.
*
* @param circuit The circuit to convert to a graph.
* @returns The provided circuit as a graph.
*/
export function CreateGraph(circuit: Circuit): Graph<string, string> {
const graph = new Graph<string, string>();

const objs = circuit.getComponents();
for (const obj of objs) {
graph.createNode(obj.id);
}
const wires = circuit.getWires();
for (const wire of wires) {
graph.createEdge(wire.p1.parent.id, wire.p2.parent.id, wire.id);
}

return graph;
}
11 changes: 11 additions & 0 deletions src/app/core/utils/StringUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Checks if the substring of a given input starting at a given index is equal to a given sequence.
*
* @param input The input string that a substring of will be examined.
* @param index The starting index of input to compare at.
* @param sequence The sequence to check equality with.
* @returns True if input has a substring starting at index that matches sequence, false otherwise.
*/
export function SubStrEquals(input: string, index: number, sequence: string): boolean {
return input.slice(index, index + sequence.length) === sequence;
}
11 changes: 6 additions & 5 deletions src/app/core/utils/math/Graph.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/**
* @param V The type for the vertices of the edge.
* @param E The type for weight of the edge.
Expand Down Expand Up @@ -53,13 +52,15 @@ export class Graph<V, E> {
}

public createEdge(source: V, target: V, weight: E): void {
if (!this.list.has(source))
const sourceNode = this.list.get(source);
const targetNode = this.reverseList.get(target);
if (!sourceNode)
throw new Error("Graph doesn't have node of value: " + source);
if (!this.list.has(target))
if (!targetNode)
throw new Error("Graph doesn't have node of value: " + target);

this.list.get(source)!.push(new Edge<V, E>(target, weight));
this.reverseList.get(target)!.push(new Edge<V, E>(source, weight));
sourceNode.push(new Edge<V, E>(target, weight));
targetNode.push(new Edge<V, E>(source, weight));
}

public isConnected(): boolean {
Expand Down
75 changes: 39 additions & 36 deletions src/app/tests/Extensions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import crypto from "node:crypto";
import {Result} from "core/utils/Result";
import crypto from "node:crypto";


// Define crypto for Jest for uuid generation
Expand All @@ -11,6 +12,8 @@ declare global {
interface Matchers<R> {
toApproximatelyEqual(expected: unknown, epsilon?: number): CustomMatcherResult;
toBeCloseToAngle(otherAngle: number, epsilon?: number): CustomMatcherResult;
toBeOk(): CustomMatcherResult;
toIncludeError(message: string): CustomMatcherResult;
// toBeConnectedTo(a: DigitalComponent, options?: {depth?: number}): CustomMatcherResult;
}
}
Expand Down Expand Up @@ -81,40 +84,40 @@ expect.extend({
};
},

// toBeConnectedTo(source: unknown, target: DigitalComponent, options = { depth: Infinity }) {
// if (!(source instanceof DigitalComponent))
// throw new Error("toBeConnectedTo can only be used with DigitalComponents!");

// const { depth } = options;

// const visited = new Set<DigitalComponent>();
// function bfs(layer: DigitalComponent[], depth: number): boolean {
// if (depth === 0 || layer.length === 0)
// return false;

// const queue = [] as DigitalComponent[];
// for (const cur of layer) {
// visited.add(cur);

// const connections = [
// ...cur.getOutputs().map((w) => w.getOutputComponent()),
// ...cur.getInputs().map((w) => w.getInputComponent()),
// ].filter((c) => !visited.has(c));

// if (connections.includes(target))
// return true;

// queue.push(...connections);
// }
// return bfs(queue, depth-1);
// }

// const pass = bfs([source], depth);
toBeOk(received: Result) {
const result = received as Result;
if (result.ok) {
return {
message: () => "expected Result to be Ok",
pass: true,
}
}
return {
message: () => `expected Result to not have errors:\n - ${
result.error.errors
.map((err) => err.message)
.join("\n - ")
}`,
pass: false,
}
},

// return {
// message: () => `expected ${source.getName()} to ${pass ? "" : "not "}be connected to ${target.getName()}` +
// ` within ${options.depth} connections`,
// pass,
// };
// },
toIncludeError(received: Result, message: string) {
const result = received as Result;
if (result.ok) {
return {
message: () => "expected Result to be not be Ok",
pass: false,
}
}
return {
message: () => `expected Result to contain an error including the text "${message}", ` +
`instead includes:\n - ${
result.error.errors
.map((err) => err.message)
.join("\n - ")
}`,
pass: result.error.errors.some((error) => error.message.includes(message)),
}
},
});
6 changes: 3 additions & 3 deletions src/app/tests/FirstAvailable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe("FirstAvailable", () => {
test("All ports available", () => {
const circuit = CreateCircuit();

const c = circuit.placeComponentAt(V(0, 0), "Multiplexer");
const c = circuit.placeComponentAt("Multiplexer", V(0, 0));

// Case: 'output' port group
expect(c.firstAvailable("outputs")?.id).toEqual(c.ports["outputs"][0].id);
Expand All @@ -20,8 +20,8 @@ describe("FirstAvailable", () => {
test("Only some/no ports are available", () => {
const circuit = CreateCircuit();

const s1 = circuit.placeComponentAt(V(-5, 5), "Switch");
const c1 = circuit.placeComponentAt(V(0, 0), "ANDGate");
const s1 = circuit.placeComponentAt("Switch", V(-5, 5));
const c1 = circuit.placeComponentAt("ANDGate", V(0, 0));

s1.ports["outputs"][0].connectTo(c1.ports["inputs"][0]);

Expand Down
10 changes: 5 additions & 5 deletions src/app/tests/PlaceComponentAt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ describe("PlaceComponentAt", () => {
test("Basic Placement", () => {
const circuit = CreateCircuit();

const c = circuit.placeComponentAt(V(0, 0), "ANDGate");
const c = circuit.placeComponentAt("ANDGate", V(0, 0));

expect(circuit.getObjs()).toHaveLength(4); // 2 inputs, 1 output, 1 component
expect(c.pos).toEqual(V(0, 0));
});
test("Multiple Placements", () => {
const circuit = CreateCircuit();

const s1 = circuit.placeComponentAt(V(-5, 5), "Switch");
const s2 = circuit.placeComponentAt(V(-5, -5), "Switch");
const c1 = circuit.placeComponentAt(V(0, 0), "ANDGate");
const l1 = circuit.placeComponentAt(V(5, 0), "LED");
const s1 = circuit.placeComponentAt("Switch", V(-5, 5));
const s2 = circuit.placeComponentAt("Switch", V(-5, -5));
const c1 = circuit.placeComponentAt("ANDGate", V(0, 0));
const l1 = circuit.placeComponentAt("LED", V(5, 0));

expect(circuit.getObjs()).toHaveLength(2 + 2 + 4 + 2);
expect(s1.pos).toEqual(V(-5, 5));
Expand Down
10 changes: 5 additions & 5 deletions src/app/tests/SelectionsMidpoint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe("SelectionsMidpoint", () => {
test("Single Selection", () => {
// Create and place new component
const circuit = CreateCircuit();
const s1 = circuit.placeComponentAt(V(0, 0), "ANDGate");
const s1 = circuit.placeComponentAt("ANDGate", V(0, 0));

// Select created component
s1.isSelected = true;
Expand All @@ -25,10 +25,10 @@ describe("SelectionsMidpoint", () => {
test("Multiple Selections", () => {
// Create and place new components
const circuit = CreateCircuit();
const s1 = circuit.placeComponentAt(V(-5, 5), "Switch");
const s2 = circuit.placeComponentAt(V(-5, -5), "Switch");
const c1 = circuit.placeComponentAt(V(0, 0), "ANDGate");
const l1 = circuit.placeComponentAt(V(6, 0), "LED");
const s1 = circuit.placeComponentAt("Switch", V(-5, 5));
const s2 = circuit.placeComponentAt("Switch", V(-5, -5));
const c1 = circuit.placeComponentAt("ANDGate", V(0, 0));
const l1 = circuit.placeComponentAt("LED", V(6, 0));

// Select created components
s1.isSelected = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";

import {OperatorFormat, TokenType} from "digital/utils/ExpressionParser/Constants/DataStructures";
import {OperatorFormat, TokenType} from "site/digital/utils/ExpressionParser/Constants/DataStructures";

import {InputField} from "shared/components/InputField";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,3 @@ export function Generate(info: DigitalCircuitInfo, expression: string,
// info.history.add(action);
// info.renderer.render();
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useState} from "react";

import {OperatorFormat, OperatorFormatLabel} from "digital/utils/ExpressionParser/Constants/DataStructures";
import {FORMATS} from "digital/utils/ExpressionParser/Constants/Formats";
import {OperatorFormat, OperatorFormatLabel} from "site/digital/utils/ExpressionParser/Constants/DataStructures";
import {FORMATS} from "site/digital/utils/ExpressionParser/Constants/Formats";

import {DigitalCircuitInfo} from "digital/utils/DigitalCircuitInfo";

Expand Down