Skip to content

Commit 83661b2

Browse files
committed
Merge branch 'master' into more-linting-fixes
2 parents f51c8cb + 5befe63 commit 83661b2

File tree

16 files changed

+254
-28
lines changed

16 files changed

+254
-28
lines changed

linting/.core.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,4 @@ module.exports = {
5959
"arrow-spacing": "error",
6060
"eol-last": "error",
6161
},
62-
}
62+
}

linting/.custom.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ module.exports = {
55
"rules": {
66
"opencircuits/object-curly-spacing": ["error", "always"],
77
},
8-
}
8+
}

linting/.deprecation.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ module.exports = {
55
"rules": {
66
"deprecation/deprecation": "warn",
77
}
8-
}
8+
}

linting/.imports.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,4 @@ module.exports = {
190190
}
191191
},
192192
},
193-
}
193+
}

linting/.jestRules.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ module.exports = {
3434

3535
"jest-formatting/padding-around-test-blocks": "off",
3636
},
37-
}
37+
}

linting/.jsdoc.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ module.exports = {
4141
"jsdoc/tag-lines": ["error", "never"],
4242
"jsdoc/valid-types": "error",
4343
},
44-
}
44+
}

linting/.jsxa11y.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,4 @@ module.exports = {
9999
"jsx-a11y/scope": "warn",
100100
"jsx-a11y/tabindex-no-positive": "warn",
101101
},
102-
}
102+
}

linting/.react.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,4 @@ module.exports = {
136136
"version": "detect",
137137
},
138138
},
139-
}
139+
}

linting/.sonarjs.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ module.exports = {
1818
"sonarjs/cognitive-complexity": "off",
1919
"sonarjs/no-duplicate-string": "off",
2020
},
21-
}
21+
}

linting/.ts.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,4 @@ module.exports = {
194194
"asyncArrow": "always"
195195
}],
196196
},
197-
}
197+
}

linting/.unicorn.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,4 @@ module.exports = {
5252
"unicorn/prevent-abbreviations": "off",
5353
"unicorn/no-array-callback-reference": "off",
5454
},
55-
}
55+
}

scripts/webpack/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export default async (dir: string, project: string, mode: "development" | "produ
7171
const privateTest = /^10\.|^172\.(1[6-9]|2\d|3[01])\.|^192\.168\./;
7272
// Check if private
7373
if (privateTest.test(ip))
74-
return formatURL(protocol, hostname, chalk.bold(port), pathname);
74+
return formatURL(protocol, ip, chalk.bold(port), pathname);
7575
}
7676
} catch {
7777
// Ignore, just defer to localhost

src/app/digital/actions/addition/BusActionFactory.ts

+60-5
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,66 @@ import {V} from "Vector";
22

33
import {Transform} from "math/Transform";
44

5+
import {GetAllPorts} from "core/utils/ComponentUtils";
6+
57
import {GroupAction} from "core/actions/GroupAction";
68

79
import {ConnectionAction} from "core/actions/addition/ConnectionAction";
810

9-
import {Component} from "core/models";
11+
import {Component, Port} from "core/models";
1012

1113
import {InputPort} from "digital/models/ports/InputPort";
1214
import {OutputPort} from "digital/models/ports/OutputPort";
1315

1416

17+
/**
18+
* For use in component bussing (#1056):
19+
* Gathers all the ports of the given components that should be bus'd together.
20+
*
21+
* @param components The component to get the ports of.
22+
* @returns A tuple of the input and output ports to bus.
23+
*/
24+
export function GetComponentBusPorts(components: Component[]): [InputPort[], OutputPort[]] {
25+
const EmptyInputPorts = (p: Port): p is InputPort => (p instanceof InputPort && p.getWires().length === 0);
26+
const EmptyOutputPorts = (p: Port): p is OutputPort => (p instanceof OutputPort && p.getWires().length === 0);
27+
28+
// Filter out components that don't have exclusively available input or output ports
29+
// these are "ambiguous" components since we might need to bus to their inputs or outputs
30+
const ambiComps = components.filter((s) => (
31+
s.getPorts().some(EmptyInputPorts) === s.getPorts().some(EmptyOutputPorts)
32+
// Then filter out ones that have no available ports
33+
)).filter((c) => !(c.getPorts().every((p) => (p.getWires().length > 0))));
34+
35+
// Output components are components where there are no empty input ports => there are only output ports
36+
const outputComps = components.filter((c) => (!c.getPorts().some(EmptyInputPorts)));
37+
// Input components are components where there are no empty output ports => there are only input ports
38+
const inputComps = components.filter((c) => (!c.getPorts().some(EmptyOutputPorts)));
39+
40+
// There cannot be input, output, and ambiguous components as this would be an ambigious case
41+
if (inputComps.length > 0 && outputComps.length > 0 && ambiComps.length > 0)
42+
return [[], []];
43+
44+
const [finalInputComps, finalOutputComps] = (() => {
45+
// So, then there are only 3 cases:
46+
// Only input and output components are selected (no ambiguous components)
47+
if (ambiComps.length === 0)
48+
return [inputComps, outputComps];
49+
// Only input and ambiguous components are selected (no output components)
50+
if (outputComps.length === 0)
51+
return [inputComps, ambiComps];
52+
// Only output and ambiguous components are selected (no input components)
53+
if (inputComps.length === 0)
54+
return [ambiComps, outputComps];
55+
return [[], []];
56+
})();
57+
58+
return [
59+
GetAllPorts(finalInputComps).filter(EmptyInputPorts),
60+
GetAllPorts(finalOutputComps).filter(EmptyOutputPorts),
61+
];
62+
}
63+
64+
1565
export function CreateBusAction(outputPorts: OutputPort[], inputPorts: InputPort[]): GroupAction {
1666
if (inputPorts.length !== outputPorts.length)
1767
throw new Error("Expected equal size input and output ports to bus!");
@@ -67,15 +117,20 @@ export function CreateBusAction(outputPorts: OutputPort[], inputPorts: InputPort
67117

68118
/* eslint-disable space-in-parens */
69119
// Associate the ports with their angle
70-
const outputMap = new Map(outputAngles.map((a, i) => [a, outputPorts[i]]));
71-
const inputMap = new Map( inputAngles.map((a, i) => [a, inputPorts[i]]));
72-
/* eslint-enable space-in-parens */
120+
// Needs to be a map of arrays, in the case where two ports share the exact same angle
121+
const outputMap = new Map<number, OutputPort[]>;
122+
const inputMap = new Map<number, InputPort[]>;
123+
124+
// Add each ports to the maps by angle
125+
outputAngles.forEach((a, i) => outputMap.set(a, [...(outputMap.get(a) ?? []), outputPorts[i]]));
126+
inputAngles.forEach((a, i) => inputMap.set(a, [...( inputMap.get(a) ?? []), inputPorts[i]]));
73127

74128
outputAngles.sort(sortByAngle);
75129
inputAngles.sort(sortByAngle).reverse();
76130

77131
// Connect Ports according to their target pos on the Average Component
78132
return new GroupAction(inputAngles.map((inputAngle, i) =>
79-
new ConnectionAction(designer, inputMap.get(inputAngle)!, outputMap.get(outputAngles[i])!)
133+
// Pop the ports out of their map so they only get used once
134+
new ConnectionAction(designer, inputMap.get(inputAngle)!.pop()!, outputMap.get(outputAngles[i])!.pop()!)
80135
), "Bus Action");
81136
}

src/app/tests/digital/utils/actions/BusAction.test.ts

+87-3
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ import {V, Vector} from "Vector";
55

66
import {GetHelpers} from "test/helpers/Helpers";
77

8-
import {CreateBusAction} from "digital/actions/addition/BusActionFactory";
8+
import {CreateBusAction, GetComponentBusPorts} from "digital/actions/addition/BusActionFactory";
99

1010
import {DigitalCircuitDesigner, InputPort, OutputPort} from "digital/models";
1111

12-
import {ANDGate, BCDDisplay, ConstantNumber, IC, ICData, LED, Multiplexer, Switch} from "digital/models/ioobjects";
12+
import {ANDGate, BCDDisplay, BUFGate, ConstantNumber, IC,
13+
ICData, LED, Multiplexer, Switch} from "digital/models/ioobjects";
1314

1415

1516
describe("Bus Action", () => {
1617
const designer = new DigitalCircuitDesigner(0);
1718
const { Place } = GetHelpers(designer);
1819

19-
2020
function expectBusConnections(outs: OutputPort[], ins: InputPort[]) {
2121
// Helper functions to for expected connectivity
2222
const expectAllDisconnected = () => {
@@ -285,4 +285,88 @@ describe("Bus Action", () => {
285285
expect(i3.getConnections()[0]).toBe(ic.getConnections()[3]);
286286
expect(i4.getConnections()[0]).toBe(ic.getConnections()[1]);
287287
});
288+
test("Switches at exact same position", () => {
289+
const [i1, i2, i3, i4, bcd1] = Place(new Switch(), new Switch(), new Switch(),
290+
new Switch(), new BCDDisplay());
291+
292+
i1.setPos(V(0,0));
293+
i2.setPos(V(0,0));
294+
i3.setPos(V(0,0));
295+
i4.setPos(V(0,0));
296+
bcd1.setPos(V(200,0));
297+
298+
// Expect every single switch to connect, but we can't assert anything about the order of connections
299+
expectBusConnections([i1,i2,i3,i4].flatMap((i) => i.getOutputPorts()), bcd1.getInputPorts());
300+
});
301+
302+
describe("Bus Components", () => {
303+
// Buses that should work
304+
test("Constant Number -> BCD Display", () => {
305+
const [c1, bcd1] = Place(new ConstantNumber(), new BCDDisplay());
306+
307+
const [iports, oports] = GetComponentBusPorts([c1, bcd1]);
308+
309+
expect(iports).toHaveLength(4);
310+
expect(oports).toHaveLength(4);
311+
312+
expectBusConnections(oports, iports);
313+
});
314+
test("4 Switches -> BCD Display", () => {
315+
const [i1, i2, i3, i4, bcd1] = Place(new Switch(), new Switch(), new Switch(),
316+
new Switch(), new BCDDisplay());
317+
318+
const [iports, oports] = GetComponentBusPorts([i1, i2, bcd1, i3, i4]);
319+
320+
expect(iports).toHaveLength(4);
321+
expect(oports).toHaveLength(4);
322+
323+
expectBusConnections(oports, iports);
324+
});
325+
test("4 Switches -> 2 ANDGates", () => {
326+
const [i1, i2, i3, i4, a1, a2] = Place(new Switch(), new Switch(), new Switch(),
327+
new Switch(), new ANDGate(), new ANDGate());
328+
329+
const [iports, oports] = GetComponentBusPorts([i1, a2, i3, i2, i4, a1]);
330+
331+
expect(iports).toHaveLength(4);
332+
expect(oports).toHaveLength(4);
333+
334+
expectBusConnections(oports, iports);
335+
});
336+
test("6 Switches -> Mux", () => {
337+
const [i1, i2, i3, i4, i5, i6, m1] = Place(new Switch(), new Switch(), new Switch(), new Switch(),
338+
new Switch(), new Switch(), new Multiplexer());
339+
340+
const [iports, oports] = GetComponentBusPorts([i1, i2, i3, i4, i5, i6, m1]);
341+
342+
expect(iports).toHaveLength(6);
343+
expect(oports).toHaveLength(6);
344+
345+
expectBusConnections(oports, iports);
346+
});
347+
348+
// Buses that should not work
349+
test("Fail: BUFGate -> BUFGate", () => {
350+
const [b1, b2] = Place(new BUFGate(), new BUFGate());
351+
352+
const [iports, oports] = GetComponentBusPorts([b1, b2]);
353+
354+
expect(iports).not.toHaveLength(oports.length);
355+
});
356+
test("Fail: 3 Switches -> BCD Display", () => {
357+
const [i1, i2, i3, bcd1] = Place(new Switch(), new Switch(), new Switch(), new BCDDisplay());
358+
359+
const [iports, oports] = GetComponentBusPorts([i1, i2, i3, bcd1]);
360+
361+
expect(iports).not.toHaveLength(oports.length);
362+
});
363+
test("Fail: 7 Switches -> Mux", () => {
364+
const [i1, i2, i3, i4, i5, i6, i7, m1] = Place(new Switch(), new Switch(), new Switch(), new Switch(),
365+
new Switch(), new Switch(), new Switch(), new Multiplexer());
366+
367+
const [iports, oports] = GetComponentBusPorts([i1, i2, i3, i4, i5, i6, i7, m1]);
368+
369+
expect(iports).not.toHaveLength(oports.length);
370+
});
371+
});
288372
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import {V} from "Vector";
2+
3+
import {GetHelpers} from "test/helpers/Helpers";
4+
import {Setup} from "test/helpers/Setup";
5+
6+
import {ConstantLow} from "digital/models/ioobjects";
7+
8+
import {LED} from "digital/models/ioobjects/outputs/LED";
9+
10+
11+
describe("FitToScreenHandler", () => {
12+
const { input, designer, selections, camera } = Setup();
13+
const { Place } = GetHelpers(designer);
14+
15+
afterEach(() => {
16+
designer.reset();
17+
selections.get().forEach((s) => selections.deselect(s));
18+
});
19+
20+
test("Fit to Screen of a Single Object", () => {
21+
Place(new ConstantLow());
22+
23+
expect(designer.getObjects()).toHaveLength(1)
24+
expect(selections.amount()).toBe(0);
25+
26+
input.click(V(0, 0));
27+
28+
expect(selections.amount()).toBe(1);
29+
30+
input.pressKey("f")
31+
.releaseKey("f");
32+
33+
expect(selections.amount()).toBe(1);
34+
expect(camera.getPos()).toEqual(V(21.5, 0));
35+
36+
});
37+
38+
test("Fit to Screen with no objects", () => {
39+
40+
expect(designer.getObjects()).toHaveLength(0)
41+
expect(selections.amount()).toBe(0);
42+
43+
input.pressKey("f")
44+
.releaseKey("f");
45+
46+
expect(selections.amount()).toBe(0);
47+
expect(camera.getPos()).toEqual(V(0, 0));
48+
49+
});
50+
51+
test("Fit to Screen of Two Connected Objects", () => {
52+
const { Connect } = GetHelpers(designer);
53+
const [lo, a] = Place(new ConstantLow(), new LED());
54+
55+
Connect(lo, a);
56+
57+
expect(designer.getObjects()).toHaveLength(2)
58+
expect(designer.getWires()).toHaveLength(1);
59+
expect(selections.amount()).toBe(0);
60+
61+
input.click(V(0, 0));
62+
63+
expect(selections.amount()).toBe(1);
64+
65+
input.pressKey("f")
66+
.releaseKey("f");
67+
68+
expect(selections.amount()).toBe(1);
69+
expect(camera.getPos()).toEqual(V(0, 40.5));
70+
71+
});
72+
});

0 commit comments

Comments
 (0)