Skip to content

Commit

Permalink
Add half link support to chain calculator. (#1591)
Browse files Browse the repository at this point in the history
  • Loading branch information
NifleySnifley authored Dec 5, 2024
1 parent 94edde0 commit df98f70
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 23 deletions.
26 changes: 25 additions & 1 deletion playwright-tests/chain.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,37 @@ test.describe("Chain Calculator", () => {
await expect(page.getByTestId("selectlargerCenter")).toHaveValue("in");
});

test("Enable Half Links", async ({ page }) => {
await page.getByTestId("allowHalfLinks").setChecked(true);
await expect(page.getByTestId("p1PD")).toHaveValue("1.2815");
await expect(page.getByTestId("selectp1PD")).toHaveValue("in");
await expect(page.getByTestId("p2PD")).toHaveValue("2.8684");
await expect(page.getByTestId("selectp2PD")).toHaveValue("in");
await expect(page.getByTestId("smallerLinks")).toHaveValue("66");
await expect(page.getByTestId("smallerCenter")).toHaveValue("4.9359");
await expect(page.getByTestId("selectsmallerCenter")).toHaveValue("in");
await expect(page.getByTestId("largerLinks")).toHaveValue("67");
await expect(page.getByTestId("largerCenter")).toHaveValue("5.0625");
await expect(page.getByTestId("selectlargerCenter")).toHaveValue("in");
await expect(page.getByTestId("chain")).toHaveValue("#25");
await expect(page.getByTestId("desiredCenter")).toHaveValue("5");
await expect(page.getByTestId("selectdesiredCenter")).toHaveValue("in");
await expect(page.getByTestId("extraCenter")).toHaveValue("0");
await expect(page.getByTestId("selectextraCenter")).toHaveValue("mm");

await expect(page.getByRole("button", { name: "Copy Link" })).toBeVisible();
await expect(
page.getByRole("cell", { name: "Matching COTS Sprockets" }),
).toBeVisible();
});

test("Copy link button works", async ({ page, browserName }) => {
test.skip(browserName === "webkit");

await page.getByRole("button", { name: "Copy Link" }).click();
const value = await page.evaluate("navigator.clipboard.readText()");
expect(value).toEqual(
"http://localhost:3000/chains?chain=%7B%22name%22%3A%22%2325%22%7D&desiredCenter=%7B%22s%22%3A5%2C%22u%22%3A%22in%22%7D&extraCenter=%7B%22s%22%3A0%2C%22u%22%3A%22mm%22%7D&p1Teeth=16&p2Teeth=36",
"http://localhost:3000/chains?allowHalfLinks=0&chain=%7B%22name%22%3A%22%2325%22%7D&desiredCenter=%7B%22s%22%3A5%2C%22u%22%3A%22in%22%7D&extraCenter=%7B%22s%22%3A0%2C%22u%22%3A%22mm%22%7D&p1Teeth=16&p2Teeth=36",
);
});
});
67 changes: 50 additions & 17 deletions src/web/calculators/chain/components/ChainCalculator.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import SimpleHeading from "common/components/heading/SimpleHeading";
import SingleInputLine from "common/components/io/inputs/SingleInputLine";
import { MeasurementInput, NumberInput } from "common/components/io/new/inputs";
import {
BooleanInput,
MeasurementInput,
NumberInput,
} from "common/components/io/new/inputs";
import ChainInput from "common/components/io/new/inputs/L3/ChainInput";
import MeasurementOutput from "common/components/io/outputs/MeasurementOutput";
import NumericOutput from "common/components/io/outputs/NumberOutput";
Expand Down Expand Up @@ -40,6 +44,7 @@ export default function ChainCalculator(): JSX.Element {
get.p1Teeth,
get.p2Teeth,
get.desiredCenter,
get.allowHalfLinks,
);

const [smallerCenter, setSmallerCenter] = useState(
Expand All @@ -61,13 +66,21 @@ export default function ChainCalculator(): JSX.Element {
get.p1Teeth,
get.p2Teeth,
get.desiredCenter,
get.allowHalfLinks,
);

setSmallerCenter(results.smaller.distance.add(get.extraCenter));
setSmallerLinks(results.smaller.links);
setLargerCenter(results.larger.distance.add(get.extraCenter));
setLargerLinks(results.larger.links);
}, [get.chain, get.p1Teeth, get.p2Teeth, get.desiredCenter, get.extraCenter]);
}, [
get.chain,
get.p1Teeth,
get.p2Teeth,
get.desiredCenter,
get.extraCenter,
get.allowHalfLinks,
]);

useEffect(() => {
setP1PD(calculate.p1PD());
Expand Down Expand Up @@ -111,23 +124,43 @@ export default function ChainCalculator(): JSX.Element {
/>
<Columns desktop centered>
<Column>
<SingleInputLine
label="Chain Type"
id="chain"
tooltip={
<>
{Object.entries(chainPitchMap).map(([name, pitch]) => (
<Columns formColumns>
<Column narrow>
<SingleInputLine
label="Chain Type"
id="chain"
tooltip={
<>
<span>{`${name} has pitch ${pitch.format()}.`}</span>
<br />
{Object.entries(chainPitchMap).map(([name, pitch]) => (
<>
<span>{`${name} has pitch ${pitch.format()}.`}</span>
<br />
</>
))}
Bike chain is typically #40.
</>
))}
Bike chain is typically #40.
</>
}
>
<ChainInput stateHook={[get.chain, set.setChain]} />
</SingleInputLine>
}
>
<ChainInput stateHook={[get.chain, set.setChain]} />
</SingleInputLine>
</Column>
<Column narrow>
<SingleInputLine
label="Allow Half Links"
id="allowHalfLinks"
tooltip={
<>
Allow the use of 1/2 links to allow for odd numbers of links
</>
}
>
<BooleanInput
stateHook={[get.allowHalfLinks, set.setAllowHalfLinks]}
/>
</SingleInputLine>
</Column>
</Columns>

<SingleInputLine
label="Desired Center"
id="desiredCenter"
Expand Down
3 changes: 2 additions & 1 deletion src/web/calculators/chain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Measurement from "common/models/Measurement";
import PageConfig from "common/models/PageConfig";
import { ChainParam, MeasurementParam } from "common/models/Params";
import { lazy } from "react";
import { NumberParam, withDefault } from "serialize-query-params";
import { BooleanParam, NumberParam, withDefault } from "serialize-query-params";

const chainConfig: PageConfig = {
url: "/chains",
Expand All @@ -23,5 +23,6 @@ export const ChainParamsV1 = {
p2Teeth: withDefault(NumberParam, 36),
desiredCenter: withDefault(MeasurementParam, new Measurement(5, "in")),
extraCenter: withDefault(MeasurementParam, new Measurement(0, "in")),
allowHalfLinks: withDefault(BooleanParam, false),
};
export type ChainStateV1 = Stateify<typeof ChainParamsV1>;
7 changes: 5 additions & 2 deletions src/web/calculators/chain/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function calculateCenters(
p1Teeth: number,
p2Teeth: number,
desiredCenter: Measurement,
allowHalfLinks: boolean,
): ChainClosestCentersResult {
if (
[desiredCenter.scalar, p1Teeth, p2Teeth].includes(0) ||
Expand Down Expand Up @@ -59,8 +60,10 @@ export function calculateCenters(
const t3 = p.mul(Math.pow(Math.abs(z2 - z1) / (2 * Math.PI), 2)).div(c0);
const x0 = t1.scalar + t2 + t3.scalar;

const roundLinksUp = (n: number) => Math.ceil(n / 2) * 2;
const roundLinksDown = (n: number) => Math.floor(n / 2) * 2;
const roundLinksUp = (n: number) =>
allowHalfLinks ? Math.ceil(n) : Math.ceil(n / 2) * 2;
const roundLinksDown = (n: number) =>
allowHalfLinks ? Math.floor(n) : Math.floor(n / 2) * 2;

return {
smaller: {
Expand Down
24 changes: 22 additions & 2 deletions src/web/calculators/chain/tests/math.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe("math", () => {
p1Teeth: 24,
p2Teeth: 36,
desiredCenter: inch(5),
allowHalfLinks: false,
expected: {
smaller: { links: 70, distance: inch(4.977) },
larger: { links: 72, distance: inch(5.228) },
Expand All @@ -53,6 +54,7 @@ describe("math", () => {
p1Teeth: 52,
p2Teeth: 54,
desiredCenter: inch(8),
allowHalfLinks: false,
expected: {
smaller: { links: 94, distance: inch(7.6865) },
larger: { links: 96, distance: inch(8.0616) },
Expand All @@ -63,15 +65,33 @@ describe("math", () => {
p1Teeth: 100,
p2Teeth: 14,
desiredCenter: inch(1),
allowHalfLinks: false,
expected: {
smaller: { links: 0, distance: inch(0) },
larger: { links: 0, distance: inch(0) },
},
},
{
chain: new Chain("#25"),
p1Teeth: 100,
p2Teeth: 14,
desiredCenter: inch(10),
allowHalfLinks: true,
expected: {
smaller: { links: 141, distance: inch(9.909) },
larger: { links: 142, distance: inch(10.042) },
},
},
])(
"%p calculateCenters",
({ chain, p1Teeth, p2Teeth, desiredCenter, expected }) => {
const centers = calculateCenters(chain, p1Teeth, p2Teeth, desiredCenter);
({ chain, p1Teeth, p2Teeth, desiredCenter, expected, allowHalfLinks }) => {
const centers = calculateCenters(
chain,
p1Teeth,
p2Teeth,
desiredCenter,
allowHalfLinks,
);

expect(centers.larger.distance).toBeCloseToMeasurement(
expected.larger.distance,
Expand Down

1 comment on commit df98f70

@tervay
Copy link
Owner

@tervay tervay commented on df98f70 Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for recalc ready!

✅ Preview
https://recalc-zs7gvo0yp-tervays-projects.vercel.app

Built with commit df98f70.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.