diff --git a/app/src/App.tsx b/app/src/App.tsx
index af5f9ed..d45dd26 100644
--- a/app/src/App.tsx
+++ b/app/src/App.tsx
@@ -5,6 +5,7 @@ import { Counter } from "./features/counter/Counter"
import { Quotes } from "./features/quotes/Quotes"
import { Bike } from "./features/bike/Bike"
import logo from "./logo.svg"
+import { Map, CompassControl } from 'react-mapycz';
const App = () => {
@@ -18,7 +19,7 @@ const App = () => {
setSpinAngle(0)
}
}
- const interval = setInterval(() => incrementAngle(), 1);
+ const interval = setInterval(() => incrementAngle(), 5);
return () => {
clearInterval(interval);
};
@@ -82,6 +83,9 @@ const App = () => {
+
)
}
diff --git a/app/src/features/bike/Bike.tsx b/app/src/features/bike/Bike.tsx
index 88edf20..041a722 100644
--- a/app/src/features/bike/Bike.tsx
+++ b/app/src/features/bike/Bike.tsx
@@ -3,7 +3,7 @@ import { useMemo } from "react";
import { BikeGeometry } from "../../utils/bike-geometry";
import { Wheel } from "../../utils/wheel";
-export const Bike = ({spinAngle}: {spinAngle: number}) => {
+export const Bike = ({ spinAngle }: { spinAngle: number }) => {
const bike = useMemo(() => new BikeGeometry(
{
reachLength: 389,
@@ -30,7 +30,7 @@ export const Bike = ({spinAngle}: {spinAngle: number}) => {
riderArmLength: 690,
riderSpineLength: 700,
effectiveSeatTubeAngle: 72.5,
- handleBarReach: 76,
+ handleBarReach: 106,
handleBarHeight: 0,
spinAngle: spinAngle,
}
@@ -43,39 +43,65 @@ export const Bike = ({spinAngle}: {spinAngle: number}) => {
)
}
+
+// todo
+// shape, see bikecad
+// feet, rotate
+// head size
\ No newline at end of file
diff --git a/app/src/utils/bike-geometry.ts b/app/src/utils/bike-geometry.ts
index fd00da7..dc725cc 100644
--- a/app/src/utils/bike-geometry.ts
+++ b/app/src/utils/bike-geometry.ts
@@ -288,10 +288,73 @@ class Fork extends Segment {
}
}
-class LowerBody extends Segment {
+class RoundedSegment extends Segment {
+
+ protected readonly radius: number;
+
+ constructor({start, end, radius}: {start:Coordinates, end:Coordinates, radius:number}){
+ super({start, end});
+ this.radius = radius;
+ }
+
+ draw(): string {
+ const aboveStart = {
+ x: this.start.x + this.radius ,
+ y: this.start.y + this.radius / 2, // above in horizontal
+ }
+ const aboveEnd = {
+ x: this.end.x + this.radius / 2, // above in vertical
+ y: this.end.y + this.radius
+ }
+ const angle = Math.atan2(aboveEnd.y - aboveStart.y, aboveEnd.x - aboveStart.x);
+ const offsetX = this.radius * Math.sin(angle);
+ const offsetY = this.radius * Math.cos(angle);
+
+ return `
+ M${aboveStart.x - offsetX},${aboveStart.y + offsetY}
+ A${this.radius},${this.radius} 0 0,1 ${aboveStart.x + offsetX},${aboveStart.y - offsetY}
+ L${aboveEnd.x + offsetX},${aboveEnd.y - offsetY}
+ A${this.radius},${this.radius} 0 0,1 ${aboveEnd.x - offsetX},${aboveEnd.y + offsetY}
+ Z
+ `;
+ }
+}
+
+class SvgDrawing {
+ private readonly _path: string;
+ private readonly _start: Coordinates;
+ private readonly _end: Coordinates;
+
+ constructor({start, end, path}:{start: Coordinates, end: Coordinates, path: string}){
+ this._path = path;
+ this._start = start;
+ this._end = end ;
+ }
+
+ transform(newStart: Coordinates, newEnd: Coordinates): string{
+ let result = `translate(${newStart.x}, ${newStart.y}) `
+ result += `rotate(${(- Math.atan2((this._end.y - this._start.y), (this._end.x - this._start.x))) * 180 / Math.PI}) `
+ result += `scale(${distance(newStart, newEnd) / distance(this._start, this._end)}) `
+ result += `rotate(${(( Math.atan2((newEnd.y - newStart.y), (newEnd.x - newStart.x))) * 180 / Math.PI)}) `
+ result += `translate(${-this._start.x}, ${-this._start.y})`
+ return result
+ }
+ get path(){
+ return this._path;
+ }
+
+}
+
+class LowerBody {
private readonly __brand = "LowerBody";
- private readonly knee: Coordinates;
- private readonly heel: Coordinates;
+ private readonly upperLeg: Segment;
+ private readonly upperLegDrawing: SvgDrawing;
+ private readonly lowerLeg: Segment;
+ private readonly lowerLegDrawing: SvgDrawing;
+ private readonly feet: Segment;
+ private readonly feetDrawing: SvgDrawing;
+ private readonly _knee: Coordinates;
+ private readonly _end: Coordinates;
constructor({
bottomBracket,
@@ -316,7 +379,6 @@ class LowerBody extends Segment {
x: crank.end.x - riderFootLength * 2 / 3,
y: crank.end.y
}
-
let knee: Coordinates;
@@ -336,29 +398,56 @@ class LowerBody extends Segment {
y: Math.abs(-lowerLeg * Math.sin(theta - gamma)) + heel.y
};
}
-
- const end = {
+ this._end = {
x: heel.x + riderFootLength,
y: heel.y
}
- super({start: seatPost.start, end});
- this.knee = knee;
- this.heel = heel;
+ this._knee = knee;
+ this.upperLeg = new Segment({start: seatPost.start, end: knee});
+ this.upperLegDrawing = new SvgDrawing({start: {x: 1594, y: 484}, end: {x:479, y:1900}, path: "M671 368C253.5 994 143.923 1267.02.5 1918c34.388 327.13 387.5 238.5 506.5 0S1495 857.001 1610 472c115-385-521.5-730-939-104Z"})
+ this.lowerLeg= new Segment({start: knee, end: heel});
+ this.lowerLegDrawing = new SvgDrawing({start: {x:467, y:178} , end: {x: 1087, y: 2102}, path: "M79 415C36.5 231.5 23.27 163.365 69 107c86-106 310.839-133.474 403 0 261 378 247 774 281 976s224 702 201.5 750.5-287.5 54.5-298.5 0S121.5 598.5 79 415Z"})
+ this.feet = new Segment({start: heel, end: this._end});
+ this.feetDrawing = new SvgDrawing({start: {x: 1306, y: 381}, end: {x:3, y: 719}, path: "M514 259C366.818 382.995 131.999 395 50 425c-82 30-44 128 16 160 60 31.999 276.409 78.931 414 42 137.59-36.931 220.98-80.143 394-154 76.928-33.565 150.75-32.65 256-48 80.39-15.522 120.89-28.533 142-100 40.12-58.305-28.99-315.54-90-322-56.24 21.285-51.78 29.893-52 46-21.69 106.773-127.23 169.389-460 24-27.5 84.715-83.497 124.919-156 186Z"})
+
+
}
draw(): string {
- return d3.line()([
- [this.start.x, this.start.y],
- [this.knee.x, this.knee.y],
- [this.heel.x, this.heel.y],
- [this.end.x, this.end.y],
- ]) ?? "";
+ return this.upperLeg.draw() + " " + this.lowerLeg.draw() + " " + this.feet.draw()
+ }
+
+ upperLegTransform(): string {
+ return this.upperLegDrawing.transform(this.upperLeg.start, this.upperLeg.end);
+ }
+ lowerLegTransform(): string{
+ return this.lowerLegDrawing.transform(this.lowerLeg.start, this.lowerLeg.end);
+ }
+ feetTransform(): string{
+ return this.feetDrawing.transform(this.lowerLeg.end, this._end);
+ }
+
+ getfeetPath(): string {
+ return this.feetDrawing.path;
+ }
+
+ getUpperLegPath(){
+ return this.upperLegDrawing.path;
+ }
+
+ getLowerLegPath(){
+ return this.lowerLegDrawing.path;
}
}
-class UpperBody extends Segment {
+class UpperBody {
private readonly __brand = "UpperBody";
private readonly shoulder: Coordinates;
+ private readonly spine: Segment;
+ private readonly spineDrawing: SvgDrawing;
+ private readonly arm: Segment;
+ private readonly armDrawing: SvgDrawing;
+ private readonly headDrawing: SvgDrawing;
constructor({
seatPost,
@@ -385,20 +474,45 @@ class UpperBody extends Segment {
y = m * x + k;
}
- super({start: seatPost.start, end: handleBar.coordinates});
-
this.shoulder = {
x: x,
y: y
};
+
+ this.spine = new RoundedSegment({start: seatPost.start, end: this.shoulder, radius:45});
+ this.spineDrawing = new SvgDrawing({start: {x:2545, y:2222}, end: {x:617, y:1}, path: "M482.5 1C340.094 266.091 306.455 285.78 1.152 388 64 500 53.63 626.733 114 774c132 322 286.916 552.85 460.001 618C1286 1660 1452 1798 1624 1996s499.86 428.23 721.5 375c221.65-53.23 308.5-223 283.5-462C2610 1562 1968.68 648.966 482.5 1Z"})
+ this.arm= new RoundedSegment({start: this.shoulder, end: handleBar.coordinates, radius: 35});
+ this.armDrawing = new SvgDrawing({start: {x:2244 , y:0}, end: {x:412, y:2246}, path: "M993.334 1698.26c161.796-158.66 235.826-212.3 357.876-373.01 182.26-239.99 312.78-564.56 354.07-685.251 41.28-120.69 93.19-226.603 122.98-275.113 29.8-48.511 91.91-99.872 185.4-114.771C2221.3 217.021 2381.5 476.854 2328.28 640c-53.23 163.146-141.57 240.184-194.8 300.683L1619.61 1539.6c-44.57 51-211.56 237.45-310.28 340.94-98.72 103.5-495.311 336.85-538.24 393.26-42.929 56.41-51.873 87.35-93.752 97.9-19.512 3.37-257.953 45.96-369.296 71.31-111.343 25.34-151.592 64.19-196.545 163.29-6.663 23.63-6.602 37.88-29.982 42.2-12.841 1.37-20.292.83-36.168-13.51 0 0-13.325-19.95-13.325-42.19s-28.554-47.26-28.554-64.14c0-16.88 10.659-52.05 9.112-85.65-1.08-23.48-9.112-43.72-9.112-59.5 0-15.78 2.476-36.35 28.554-70.04 10.89-16.28 49.493-64.62 49.493-64.62s12.082-54.74 58.06-68.72c45.977-13.99 253.177 17.72 292.2 10.13 39.024-7.6 39.535-5.57 62.819-16.04 25.749-11.57 60.915-37.13 60.915-37.13 27.381-29.19 276.028-280.18 437.825-438.83Z"})
+ this.headDrawing = new SvgDrawing({start: {x:1138, y:747}, end: {x:1141, y:807}, path: "M92.323 432.507s2.201 35.318 15.893 62.078l46.896 45.198 3.899 56.752 26.081 144.283-62.26 96.344c-21.062 44.888-17.33 57.797 10.102 64.467l77.698 25.534c26.967 2.66-30.412 57.058-7.392 68.1 23.019 11.037 39.779-7.211 41.257 4.479.895 7.068-46.503 20.228-21.792 33.518 24.71 13.29 53.365 68.19 52.206 98.21-1.159 30.01-6.59 51.75 14.665 73.84 21.256 22.09 40.289 58.44 170.002 18.45 40.527-11.47 63.469-19.92 99.416 10.79l44.493 39.54L792 1371c164 24 455.06-147.24 402-375.737-1.94-8.334-81.52-92.041-78.28-121.866l2.37-21.872c31.68-11.499 36.83-25.326 44.04-44.696l.06-.181c7.25-19.464 45.78-4.713 45.78-4.713l39.09-45.045-5.8-20.746c-7.74-11.737-7.61-20.278 4.42-40.636l51.2 5.558c11.43 1.241 19.97-26.499 19.97-26.499l-24.52-66.031s-29.53-114.406-11.35-108.486c18.17 5.921 35.08-41.872 29.71-60.647-2.09-7.33-5.06-8.947-5.44-14.673-.61-8.939 2.19-20.255 2.19-20.255l-295.99-238.462S387.687-199.941 131.06 395.344c-2.07 7.944-38.737 37.163-38.737 37.163Z"})
+ }
+
+ spineDrawingTransform(): string{
+ return this.spineDrawing.transform(this.spine.start, this.spine.end);
+ }
+
+ getSpineDrawingPath(): string{
+ return this.spineDrawing.path;
+ }
+
+ getArmDrawingPath(): string{
+ return this.armDrawing.path
+ }
+
+ armDrawingTransform(): string{
+ return this.armDrawing.transform(this.arm.start, this.arm.end);
+ }
+
+ headTransform(): string {
+ return this.headDrawing.transform({x: this.spine.end.x + 45, y: this.spine.end.y + 70},
+ {x: this.spine.end.x + 45, y: this.spine.end.y - (this.spine.end.y - this.spine.start.y) / 44 + 70 } )
+ }
+
+ getHeadDrawing(): string{
+ return this.headDrawing.path;
}
draw(): string {
- return d3.line()([
- [this.start.x, this.start.y],
- [this.shoulder.x, this.shoulder.y],
- [this.end.x, this.end.y],
- ]) ?? "";
+ return this.arm.draw()
}
}