diff --git a/apps/scouting/backend/src/fuel/fuel-averaging.ts b/apps/scouting/backend/src/fuel/calculations/fuel-averaging.ts similarity index 98% rename from apps/scouting/backend/src/fuel/fuel-averaging.ts rename to apps/scouting/backend/src/fuel/calculations/fuel-averaging.ts index ee0e334..6ed8b87 100644 --- a/apps/scouting/backend/src/fuel/fuel-averaging.ts +++ b/apps/scouting/backend/src/fuel/calculations/fuel-averaging.ts @@ -1,6 +1,6 @@ // בס"ד import type { Match, ShootEvent } from "@repo/scouting_types"; -import type { BPS, FuelObject } from "./fuel-object"; +import type { BPS, FuelObject } from "../fuel-object"; import { calculateSum, firstElement, lastElement } from "@repo/array-functions"; const EMPTY_INTERVAL_DURATION = 0; diff --git a/apps/scouting/backend/src/fuel/fuel-match.ts b/apps/scouting/backend/src/fuel/calculations/fuel-match.ts similarity index 93% rename from apps/scouting/backend/src/fuel/fuel-match.ts rename to apps/scouting/backend/src/fuel/calculations/fuel-match.ts index 137a434..7f8fead 100644 --- a/apps/scouting/backend/src/fuel/fuel-match.ts +++ b/apps/scouting/backend/src/fuel/calculations/fuel-match.ts @@ -1,6 +1,6 @@ // בס"ד import type { ShootEvent } from "@repo/scouting_types"; -import type { BPS, FuelObject } from "./fuel-object"; +import type { BPS, FuelObject } from "../fuel-object"; const getIncludedShots = (section: number[], shot: ShootEvent) => { return section.filter( diff --git a/apps/scouting/backend/src/fuel/distance-split.ts b/apps/scouting/backend/src/fuel/distance-split.ts new file mode 100644 index 0000000..771f0ac --- /dev/null +++ b/apps/scouting/backend/src/fuel/distance-split.ts @@ -0,0 +1,33 @@ +// בס"ד +import { convertPixelToCentimeters, distanceFromHub } from "@repo/rebuilt_map"; +import type { FuelEvents, FuelObject } from "./fuel-object"; +import { calculateAverage } from "@repo/array-functions"; + +const averageFuel = (fuels: FuelObject[]): FuelObject => { + const averageOfKey = (key: FuelEvents) => + calculateAverage(fuels, (value) => value[key]); + return { + scored: averageOfKey("scored"), + shot: averageOfKey("shot"), + missed: averageOfKey("missed"), + positions: fuels.flatMap((fuel) => fuel.positions), + }; +}; + +export const splitByDistances = ( + fuels: FuelObject[], + distances: T[], +): Record => + Object.assign( + {}, + ...distances.map((distance) => ({ + [distance]: averageFuel( + fuels.filter((fuel) => + fuel.positions.every( + (position) => + distanceFromHub(convertPixelToCentimeters(position)) < distance, + ), + ), + ), + })), + ); diff --git a/apps/scouting/backend/src/fuel/fuel-object.ts b/apps/scouting/backend/src/fuel/fuel-object.ts index 41f5578..4ae4698 100644 --- a/apps/scouting/backend/src/fuel/fuel-object.ts +++ b/apps/scouting/backend/src/fuel/fuel-object.ts @@ -1,15 +1,15 @@ // בס"ד import type { GameObject } from "../game-object"; import type { Match, Point, ShootEvent } from "@repo/scouting_types"; -import { calculateFuelByAveraging } from "./fuel-averaging"; -import { calculateFuelByMatch } from "./fuel-match"; +import { calculateFuelByAveraging } from "./calculations/fuel-averaging"; +import { calculateFuelByMatch } from "./calculations/fuel-match"; export interface BPS { events: { shoot: number[]; score: number[] }[]; match: Match; } -type FuelEvents = "scored" | "shot" | "missed"; +export type FuelEvents = "scored" | "shot" | "missed"; export type FuelObject = GameObject< FuelEvents, { diff --git a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx index 4b562ad..6b53525 100644 --- a/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx +++ b/apps/scouting/frontend/src/scouter/components/ScoreMap.tsx @@ -10,6 +10,13 @@ import { type Touch, } from "react"; import type { Alliance, Point } from "@repo/scouting_types"; +import { + ALLIANCE_ZONE_WIDTH_PIXELS, + FIELD_HEIGHT_PIXELS, + TWO_THIRDS_FIELD_WIDTH_PIXELS, + blueField, + redField, +} from "@repo/rebuilt_map"; import { pipe } from "fp-ts/lib/function"; interface ScoreMapProps { @@ -19,17 +26,14 @@ interface ScoreMapProps { mapZone: Alliance; } -const ALLIANCE_ZONE_WIDTH_PIXELS = 395; -const TWO_THIRDS_WIDTH_PIXELS = 1010; -const HEIGHT_PIXELS = 652; const alliancizePosition = (alliance: Alliance, position: Point): Point => { if (alliance === "red") { return position; } return { - x: TWO_THIRDS_WIDTH_PIXELS - position.x, - y: HEIGHT_PIXELS - position.y, + x: TWO_THIRDS_FIELD_WIDTH_PIXELS - position.x, + y: FIELD_HEIGHT_PIXELS - position.y, }; }; @@ -41,8 +45,8 @@ const switchZone = (point: Point) => { }; const normalizePosition = (point: Point, bounds: Point) => ({ - x: (point.x * TWO_THIRDS_WIDTH_PIXELS) / bounds.x, - y: (point.y * HEIGHT_PIXELS) / bounds.y, + x: (point.x * TWO_THIRDS_FIELD_WIDTH_PIXELS) / bounds.x, + y: (point.y * FIELD_HEIGHT_PIXELS) / bounds.y, }); const dotRadius = 10; @@ -51,11 +55,17 @@ const dotDiameter = dotRadius * radiusToDiameterRatio; const firstTouchIndex = 0; export const defaultPoint: Point = { x: 0, y: 0 }; + +interface RobotPositionInfo { + mapPoint: Point; + normalizedPoint: Point; + imageSize: Point; +} const getRobotPosition = ( touch: Touch, imageRect: DOMRect, containerRect: DOMRect, -) => { +): RobotPositionInfo => { const x = touch.clientX - imageRect.left; const y = touch.clientY - imageRect.top; @@ -77,12 +87,12 @@ const getRobotPosition = ( y: boundedY + imageRect.top - containerRect.top, }, normalizedPoint: { - x: boundedX, - y: boundedY - }, - imageSize: { - x: imageRect.width, - y: imageRect.height + x: boundedX, + y: boundedY, + }, + imageSize: { + x: imageRect.width, + y: imageRect.height, }, }; }; @@ -103,18 +113,18 @@ export const ScoreMap: FC = ({ } const containerElement = containerRef.current; const imageElement = imageRef.current; - if (!(containerElement&&imageElement)) { + if (!(containerElement && imageElement)) { return; } const containerRect = containerElement.getBoundingClientRect(); const imageRect = imageElement.getBoundingClientRect(); const touch = event.targetTouches[firstTouchIndex]; - const { mapPoint: dotPoint, normalizedPoint, imageSize } = getRobotPosition( - touch, - imageRect, - containerRect, - ); + const { + mapPoint: dotPoint, + normalizedPoint, + imageSize, + } = getRobotPosition(touch, imageRect, containerRect); setMapPoint(dotPoint); @@ -136,7 +146,7 @@ export const ScoreMap: FC = ({ > { setHolding(true); diff --git a/packages/array-functions/index.ts b/packages/array-functions/index.ts index 645cd90..836b0b8 100644 --- a/packages/array-functions/index.ts +++ b/packages/array-functions/index.ts @@ -7,6 +7,11 @@ export const calculateSum = ( ): number => arr.reduce((sum, value) => sum + transformation(value), startingSumValue); +export const calculateAverage = ( + arr: T[], + transformation: (value: T) => number, +): number => calculateSum(arr, transformation) / arr.length; + export const getMax = (arr: T[], transformation: (value: T) => number): T => arr .map((item) => ({ value: transformation(item), item })) diff --git a/apps/scouting/frontend/public/blue-field-4418.png b/packages/rebuilt-map/images/blue-field-4418.png similarity index 100% rename from apps/scouting/frontend/public/blue-field-4418.png rename to packages/rebuilt-map/images/blue-field-4418.png diff --git a/apps/scouting/frontend/public/full-field-4418.png b/packages/rebuilt-map/images/full-field-4418.png similarity index 100% rename from apps/scouting/frontend/public/full-field-4418.png rename to packages/rebuilt-map/images/full-field-4418.png diff --git a/apps/scouting/frontend/public/red-field-4418.png b/packages/rebuilt-map/images/red-field-4418.png similarity index 100% rename from apps/scouting/frontend/public/red-field-4418.png rename to packages/rebuilt-map/images/red-field-4418.png diff --git a/packages/rebuilt-map/index.ts b/packages/rebuilt-map/index.ts new file mode 100644 index 0000000..7e8d6a2 --- /dev/null +++ b/packages/rebuilt-map/index.ts @@ -0,0 +1,3 @@ +// בס"ד +export * from "./map"; +export * from "./units"; diff --git a/packages/rebuilt-map/map.ts b/packages/rebuilt-map/map.ts new file mode 100644 index 0000000..02cbd68 --- /dev/null +++ b/packages/rebuilt-map/map.ts @@ -0,0 +1,5 @@ +// בס"ד +import blue from "./images/blue-field-4418.png"; +export const blueField = blue; +import red from "./images/red-field-4418.png"; +export const redField = red; diff --git a/packages/rebuilt-map/package.json b/packages/rebuilt-map/package.json new file mode 100644 index 0000000..2675ada --- /dev/null +++ b/packages/rebuilt-map/package.json @@ -0,0 +1,13 @@ +{ + "name": "@repo/rebuilt_map", + "version": "0.1.0", + "type": "module", + "private": true, + "exports": { + ".": "./index.ts" + }, + "dependencies": {}, + "devDependencies": { + "typescript": "latest" + } +} diff --git a/packages/rebuilt-map/units.ts b/packages/rebuilt-map/units.ts new file mode 100644 index 0000000..c86a78a --- /dev/null +++ b/packages/rebuilt-map/units.ts @@ -0,0 +1,32 @@ +// בס"ד +import type { Point } from "@repo/scouting_types"; + +export const ALLIANCE_ZONE_WIDTH_PIXELS = 395; +export const TWO_THIRDS_FIELD_WIDTH_PIXELS = 1010; +export const FIELD_HEIGHT_PIXELS = 652; + +const FIELD_SIZE_CENTIMETERS: Point = { + x: 1654, + y: 807, +}; +const CENTER_HUB_POINT_CENTIMETERS: Point = { + x: 462.534, + y: 403.5, +}; + +export const convertPixelToCentimeters = (point: Point): Point => ({ + x: (point.x / TWO_THIRDS_FIELD_WIDTH_PIXELS) * FIELD_SIZE_CENTIMETERS.x, + y: (point.y / FIELD_HEIGHT_PIXELS) * FIELD_SIZE_CENTIMETERS.y, +}); + +const distanceFromDifference = (difference: Point): number => + Math.sqrt(difference.x * difference.x + difference.y * difference.y); + +const distance = (p1: Point, p2: Point): number => + distanceFromDifference({ + x: p1.x - p2.x, + y: p1.y - p2.y, + }); + +export const distanceFromHub = (point: Point): number => + distance(point, CENTER_HUB_POINT_CENTIMETERS);