refactor map transform calculations
parent
471b4b9b74
commit
27129844c9
|
@ -11,7 +11,7 @@
|
|||
.farm-designer-map {
|
||||
min-width: 100%;
|
||||
display: inline-block;
|
||||
padding: 11rem 2rem 2rem 31.8rem;
|
||||
padding: 11rem 2rem 2rem 31.8rem; // at zoom = 1.0: 110px 20px 20px 318px
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,13 @@ import { SelectionBoxData } from "./map/selection_box";
|
|||
import { BooleanConfigKey } from "../config_storage/web_app_configs";
|
||||
import { GetWebAppConfigValue } from "../config_storage/actions";
|
||||
|
||||
/* BotOriginQuadrant diagram
|
||||
|
||||
2 --- 1
|
||||
| |
|
||||
3 --- 4
|
||||
|
||||
*/
|
||||
export enum BotOriginQuadrant { ONE = 1, TWO = 2, THREE = 3, FOUR = 4 }
|
||||
|
||||
type Mystery = BotOriginQuadrant | number | undefined;
|
||||
|
|
|
@ -3,11 +3,11 @@ import {
|
|||
translateScreenToGarden,
|
||||
getBotSize,
|
||||
getMapSize,
|
||||
getXYFromQuadrant,
|
||||
transformXY,
|
||||
transformForQuadrant
|
||||
} from "../util";
|
||||
import { McuParams } from "farmbot";
|
||||
import { AxisNumberProperty, BotSize } from "../interfaces";
|
||||
import { AxisNumberProperty, BotSize, MapTransformProps } from "../interfaces";
|
||||
import { StepsPerMmXY } from "../../../devices/interfaces";
|
||||
|
||||
describe("Utils", () => {
|
||||
|
@ -20,11 +20,11 @@ describe("Utils", () => {
|
|||
describe("translateScreenToGarden()", () => {
|
||||
it("translates garden coords to screen coords: corner case", () => {
|
||||
const cornerCase = translateScreenToGarden({
|
||||
quadrant: 2,
|
||||
pageX: 520,
|
||||
pageY: 212,
|
||||
mapTransformProps: { quadrant: 2, gridSize: { x: 3000, y: 1500 } },
|
||||
page: { x: 520, y: 212 },
|
||||
scroll: { left: 0, top: 0 },
|
||||
zoomLvl: 1,
|
||||
gridSize: { x: 3000, y: 1500 }
|
||||
gridOffset: { x: 0, y: 0 },
|
||||
});
|
||||
expect(cornerCase.x).toEqual(200);
|
||||
expect(cornerCase.y).toEqual(100);
|
||||
|
@ -32,11 +32,11 @@ describe("translateScreenToGarden()", () => {
|
|||
|
||||
it("translates garden coords to screen coords: edge case", () => {
|
||||
const edgeCase = translateScreenToGarden({
|
||||
quadrant: 2,
|
||||
pageX: 1132,
|
||||
pageY: 382,
|
||||
mapTransformProps: { quadrant: 2, gridSize: { x: 3000, y: 1500 } },
|
||||
page: { x: 1132, y: 382 },
|
||||
scroll: { left: 0, top: 0 },
|
||||
zoomLvl: 0.3,
|
||||
gridSize: { x: 3000, y: 1500 }
|
||||
gridOffset: { x: 0, y: 0 },
|
||||
});
|
||||
|
||||
expect(Math.round(edgeCase.x)).toEqual(2710);
|
||||
|
@ -153,44 +153,59 @@ describe("getMapSize()", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("getXYFromQuadrant()", () => {
|
||||
describe("transformXY", () => {
|
||||
const gridSize = { x: 2000, y: 1000 };
|
||||
|
||||
type QXY = { qx: number, qy: number };
|
||||
|
||||
const transformCheck =
|
||||
(original: QXY, transformed: QXY, transformProps: MapTransformProps) => {
|
||||
expect(transformXY(original.qx, original.qy, transformProps))
|
||||
.toEqual(transformed);
|
||||
expect(transformXY(transformed.qx, transformed.qy, transformProps))
|
||||
.toEqual(original);
|
||||
};
|
||||
|
||||
it("calculates transformed coordinate: quadrant 2", () => {
|
||||
const { qx, qy } = getXYFromQuadrant(100, 200, 2, { x: 2000, y: 1000 });
|
||||
expect(qx).toEqual(100);
|
||||
expect(qy).toEqual(200);
|
||||
const original = { qx: 100, qy: 200 };
|
||||
const transformed = { qx: 100, qy: 200 };
|
||||
const transformProps = { quadrant: 2, gridSize };
|
||||
transformCheck(original, transformed, transformProps);
|
||||
});
|
||||
|
||||
it("calculates transformed coordinate: quadrant 4", () => {
|
||||
const { qx, qy } = getXYFromQuadrant(100, 200, 4, { x: 2000, y: 1000 });
|
||||
expect(qx).toEqual(1900);
|
||||
expect(qy).toEqual(800);
|
||||
const original = { qx: 100, qy: 200 };
|
||||
const transformed = { qx: 1900, qy: 800 };
|
||||
const transformProps = { quadrant: 4, gridSize };
|
||||
transformCheck(original, transformed, transformProps);
|
||||
});
|
||||
|
||||
it("calculates transformed coordinate: quadrant 4 (outside of grid)", () => {
|
||||
const { qx, qy } = getXYFromQuadrant(2200, 1100, 4, { x: 2000, y: 1000 });
|
||||
expect(qx).toEqual(-200);
|
||||
expect(qy).toEqual(-100);
|
||||
const original = { qx: 2200, qy: 1100 };
|
||||
const transformed = { qx: -200, qy: -100 };
|
||||
const transformProps = { quadrant: 4, gridSize };
|
||||
transformCheck(original, transformed, transformProps);
|
||||
});
|
||||
});
|
||||
|
||||
describe("transformForQuadrant()", () => {
|
||||
it("calculates transform for quadrant 1", () => {
|
||||
expect(transformForQuadrant(1, { x: 200, y: 100 }))
|
||||
expect(transformForQuadrant({ quadrant: 1, gridSize: { x: 200, y: 100 } }))
|
||||
.toEqual("scale(-1, 1) translate(-200, 0)");
|
||||
});
|
||||
|
||||
it("calculates transform for quadrant 2", () => {
|
||||
expect(transformForQuadrant(2, { x: 200, y: 100 }))
|
||||
expect(transformForQuadrant({ quadrant: 2, gridSize: { x: 200, y: 100 } }))
|
||||
.toEqual("scale(1, 1) translate(0, 0)");
|
||||
});
|
||||
|
||||
it("calculates transform for quadrant 3", () => {
|
||||
expect(transformForQuadrant(3, { x: 200, y: 100 }))
|
||||
expect(transformForQuadrant({ quadrant: 3, gridSize: { x: 200, y: 100 } }))
|
||||
.toEqual("scale(1, -1) translate(0, -100)");
|
||||
});
|
||||
|
||||
it("calculates transform for quadrant 4", () => {
|
||||
expect(transformForQuadrant(4, { x: 200, y: 100 }))
|
||||
expect(transformForQuadrant({ quadrant: 4, gridSize: { x: 200, y: 100 } }))
|
||||
.toEqual("scale(-1, -1) translate(-200, -100)");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import * as React from "react";
|
||||
import { getXYFromQuadrant } from "./util";
|
||||
import { transformXY } from "./util";
|
||||
import { BotExtentsProps } from "./interfaces";
|
||||
|
||||
export function BotExtents(props: BotExtentsProps) {
|
||||
const { stopAtHome, botSize, mapTransformProps } = props;
|
||||
const { quadrant, gridSize } = mapTransformProps;
|
||||
const homeLength = getXYFromQuadrant(
|
||||
botSize.x.value, botSize.y.value, quadrant, gridSize);
|
||||
const homeZero = getXYFromQuadrant(2, 2, quadrant, gridSize);
|
||||
const homeLength = transformXY(
|
||||
botSize.x.value, botSize.y.value, mapTransformProps);
|
||||
const homeZero = transformXY(2, 2, mapTransformProps);
|
||||
|
||||
return <g
|
||||
id="extents"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { DragHelpersProps } from "./interfaces";
|
||||
import { round, getXYFromQuadrant, getMapSize } from "./util";
|
||||
import { round, transformXY, getMapSize } from "./util";
|
||||
import { isUndefined } from "util";
|
||||
import { BotPosition } from "../../devices/interfaces";
|
||||
import { Color } from "../../ui/index";
|
||||
|
@ -54,14 +54,13 @@ export function DragHelpers(props: DragHelpersProps) {
|
|||
|
||||
const {
|
||||
dragging, plant, zoomLvl, activeDragXY, mapTransformProps, plantAreaOffset
|
||||
} = props;
|
||||
const { quadrant, gridSize } = mapTransformProps;
|
||||
const mapSize = getMapSize(gridSize, plantAreaOffset);
|
||||
} = props;
|
||||
const mapSize = getMapSize(mapTransformProps.gridSize, plantAreaOffset);
|
||||
const { radius, x, y } = plant.body;
|
||||
|
||||
const scale = 1 + Math.round(15 * (1.8 - zoomLvl)) / 10; // scale factor
|
||||
|
||||
const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize);
|
||||
const { qx, qy } = transformXY(round(x), round(y), mapTransformProps);
|
||||
const gardenCoord: BotPosition = { x: round(x), y: round(y), z: 0 };
|
||||
|
||||
return <g id="drag-helpers" fill={Color.darkGray}>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { MapTransformProps } from "./interfaces";
|
||||
import { getXYFromQuadrant } from "./util";
|
||||
import { transformXY } from "./util";
|
||||
import { CurrentPointPayl } from "../interfaces";
|
||||
|
||||
export interface DrawnPointProps {
|
||||
|
@ -10,9 +10,8 @@ export interface DrawnPointProps {
|
|||
|
||||
export function DrawnPoint(props: DrawnPointProps) {
|
||||
const { data, mapTransformProps } = props;
|
||||
const { quadrant, gridSize } = mapTransformProps;
|
||||
const { cx, cy, r, color } = data;
|
||||
const { qx, qy } = getXYFromQuadrant(cx, cy, quadrant, gridSize);
|
||||
const { qx, qy } = transformXY(cx, cy, mapTransformProps);
|
||||
return <g
|
||||
id="current-point"
|
||||
stroke={color ? color : "green"}
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
translateScreenToGarden,
|
||||
round,
|
||||
ScreenToGardenParams,
|
||||
getXYFromQuadrant,
|
||||
transformXY,
|
||||
getMapSize
|
||||
} from "./util";
|
||||
import { findBySlug } from "../search_selectors";
|
||||
|
@ -29,7 +29,7 @@ import {
|
|||
ImageLayer,
|
||||
} from "./layers";
|
||||
import { cachedCrop } from "../../open_farm/icons";
|
||||
import { AxisNumberProperty } from "./interfaces";
|
||||
import { AxisNumberProperty, MapTransformProps } from "./interfaces";
|
||||
import { SelectionBox, SelectionBoxData } from "./selection_box";
|
||||
import { Actions } from "../../constants";
|
||||
import { isNumber } from "lodash";
|
||||
|
@ -69,6 +69,13 @@ export class GardenMap extends
|
|||
this.state = {};
|
||||
}
|
||||
|
||||
get mapTransformProps(): MapTransformProps {
|
||||
return {
|
||||
quadrant: this.props.botOriginQuadrant,
|
||||
gridSize: this.props.gridSize
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
unselectPlant(this.props.dispatch)();
|
||||
}
|
||||
|
@ -106,13 +113,12 @@ export class GardenMap extends
|
|||
const page = document.querySelector(".farm-designer");
|
||||
if (el && map && page) {
|
||||
const zoomLvl = parseFloat(window.getComputedStyle(map).zoom || DRAG_ERROR);
|
||||
const { pageX, pageY } = e;
|
||||
const params: ScreenToGardenParams = {
|
||||
quadrant: this.props.botOriginQuadrant,
|
||||
pageX: pageX + page.scrollLeft - this.props.gridOffset.x * zoomLvl,
|
||||
pageY: pageY + map.scrollTop * zoomLvl - this.props.gridOffset.y * zoomLvl,
|
||||
page: { x: e.pageX, y: e.pageY },
|
||||
scroll: { left: page.scrollLeft, top: map.scrollTop * zoomLvl },
|
||||
mapTransformProps: this.mapTransformProps,
|
||||
gridOffset: this.props.gridOffset,
|
||||
zoomLvl,
|
||||
gridSize: this.props.gridSize
|
||||
};
|
||||
return translateScreenToGarden(params);
|
||||
} else {
|
||||
|
@ -256,11 +262,10 @@ export class GardenMap extends
|
|||
case Mode.editPlant:
|
||||
const plant = this.getPlant();
|
||||
const map = document.querySelector(".farm-designer-map");
|
||||
const { botOriginQuadrant, gridSize } = this.props;
|
||||
const { gridSize } = this.props;
|
||||
if (this.state.isDragging && plant && map) {
|
||||
const zoomLvl = parseFloat(window.getComputedStyle(map).zoom || DRAG_ERROR);
|
||||
const { qx, qy } = getXYFromQuadrant(
|
||||
e.pageX, e.pageY, botOriginQuadrant, gridSize);
|
||||
const { qx, qy } = transformXY(e.pageX, e.pageY, this.mapTransformProps);
|
||||
const deltaX = Math.round((qx - (this.state.pageX || qx)) / zoomLvl);
|
||||
const deltaY = Math.round((qy - (this.state.pageY || qy)) / zoomLvl);
|
||||
this.setState({
|
||||
|
@ -308,10 +313,7 @@ export class GardenMap extends
|
|||
render() {
|
||||
const { gridSize } = this.props;
|
||||
const mapSize = getMapSize(gridSize, this.props.gridOffset);
|
||||
const mapTransformProps = {
|
||||
quadrant: this.props.botOriginQuadrant,
|
||||
gridSize
|
||||
};
|
||||
const mapTransformProps = this.mapTransformProps;
|
||||
return <div
|
||||
className="drop-area"
|
||||
style={{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react";
|
||||
import { GardenPlantProps, GardenPlantState } from "./interfaces";
|
||||
import { cachedCrop, DEFAULT_ICON, svgToUrl } from "../../open_farm/icons";
|
||||
import { round, getXYFromQuadrant } from "./util";
|
||||
import { round, transformXY } from "./util";
|
||||
import { DragHelpers } from "./drag_helpers";
|
||||
import { Session } from "../../session";
|
||||
import { BooleanSetting } from "../../session_keys";
|
||||
|
@ -52,11 +52,10 @@ export class GardenPlant extends
|
|||
render() {
|
||||
const { selected, dragging, plant, grayscale, mapTransformProps,
|
||||
activeDragXY, zoomLvl } = this.props;
|
||||
const { quadrant, gridSize } = mapTransformProps;
|
||||
const { id, radius, x, y } = plant.body;
|
||||
const { icon } = this.state;
|
||||
|
||||
const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize);
|
||||
const { qx, qy } = transformXY(round(x), round(y), mapTransformProps);
|
||||
const alpha = dragging ? 0.4 : 1.0;
|
||||
const animate = !Session.deprecatedGetBool(BooleanSetting.disable_animations);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react";
|
||||
import { GardenPointProps } from "./interfaces";
|
||||
import { defensiveClone } from "../../util";
|
||||
import { getXYFromQuadrant } from "./util";
|
||||
import { transformXY } from "./util";
|
||||
|
||||
const POINT_STYLES = {
|
||||
stroke: "green",
|
||||
|
@ -12,11 +12,10 @@ const POINT_STYLES = {
|
|||
|
||||
export function GardenPoint(props: GardenPointProps) {
|
||||
const { point, mapTransformProps } = props;
|
||||
const { quadrant, gridSize } = mapTransformProps;
|
||||
const { id, x, y } = point.body;
|
||||
const styles = defensiveClone(POINT_STYLES);
|
||||
styles.stroke = point.body.meta.color || "green";
|
||||
const { qx, qy } = getXYFromQuadrant(x, y, quadrant, gridSize);
|
||||
const { qx, qy } = transformXY(x, y, mapTransformProps);
|
||||
return <g id={"point-" + id}>
|
||||
<circle id="point-radius" cx={qx} cy={qy} r={point.body.radius} {...styles} />
|
||||
<circle id="point-center" cx={qx} cy={qy} r={2} {...styles} />
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import * as React from "react";
|
||||
import { GridProps } from "./interfaces";
|
||||
import { getXYFromQuadrant, transformForQuadrant } from "./util";
|
||||
import { transformXY, transformForQuadrant } from "./util";
|
||||
import * as _ from "lodash";
|
||||
import { Color } from "../../ui/index";
|
||||
|
||||
export function Grid(props: GridProps) {
|
||||
const { quadrant, gridSize } = props.mapTransformProps;
|
||||
const origin = getXYFromQuadrant(0, 0, quadrant, gridSize);
|
||||
const arrowEnd = getXYFromQuadrant(25, 25, quadrant, gridSize);
|
||||
const xLabel = getXYFromQuadrant(15, -10, quadrant, gridSize);
|
||||
const yLabel = getXYFromQuadrant(-11, 18, quadrant, gridSize);
|
||||
const { mapTransformProps } = props;
|
||||
const { gridSize } = mapTransformProps;
|
||||
const origin = transformXY(0, 0, mapTransformProps);
|
||||
const arrowEnd = transformXY(25, 25, mapTransformProps);
|
||||
const xLabel = transformXY(15, -10, mapTransformProps);
|
||||
const yLabel = transformXY(-11, 18, mapTransformProps);
|
||||
return <g className="drop-area-background" onClick={props.onClick}>
|
||||
<defs>
|
||||
<pattern id="minor_grid"
|
||||
|
@ -34,7 +35,7 @@ export function Grid(props: GridProps) {
|
|||
<g id="grid">
|
||||
<rect id="minor-grid"
|
||||
width={gridSize.x} height={gridSize.y} fill="url(#minor_grid)" />
|
||||
<rect id="major-grid" transform={transformForQuadrant(quadrant, gridSize)}
|
||||
<rect id="major-grid" transform={transformForQuadrant(mapTransformProps)}
|
||||
width={gridSize.x} height={gridSize.y} fill="url(#major_grid)" />
|
||||
<rect id="border" width={gridSize.x} height={gridSize.y} fill="none"
|
||||
stroke="rgba(0,0,0,0.3)" strokeWidth={2} />
|
||||
|
@ -57,12 +58,12 @@ export function Grid(props: GridProps) {
|
|||
<g id="axis-values" fontFamily="Arial" fontSize="10"
|
||||
textAnchor="middle" dominantBaseline="central" fill="rgba(0, 0, 0, 0.3)">
|
||||
{_.range(100, gridSize.x, 100).map((i) => {
|
||||
const location = getXYFromQuadrant(i, -10, quadrant, gridSize);
|
||||
const location = transformXY(i, -10, mapTransformProps);
|
||||
return <text key={"x-label-" + i}
|
||||
x={location.qx} y={location.qy}>{i}</text>;
|
||||
})}
|
||||
{_.range(100, gridSize.y, 100).map((i) => {
|
||||
const location = getXYFromQuadrant(-15, i, quadrant, gridSize);
|
||||
const location = transformXY(-15, i, mapTransformProps);
|
||||
return <text key={"y-label-" + i}
|
||||
x={location.qx} y={location.qy}>{i}</text>;
|
||||
})}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react";
|
||||
import { TaggedPlantPointer } from "../../../resources/tagged_resources";
|
||||
import { DesignerState } from "../../interfaces";
|
||||
import { getXYFromQuadrant, round } from "../util";
|
||||
import { transformXY, round } from "../util";
|
||||
import { MapTransformProps } from "../interfaces";
|
||||
import { SpreadCircle } from "./spread_layer";
|
||||
import { Circle } from "../circle";
|
||||
|
@ -41,9 +41,8 @@ export class HoveredPlantLayer extends
|
|||
render() {
|
||||
const { icon } = this.props.designer.hoveredPlant;
|
||||
const { currentPlant, mapTransformProps, dragging, isEditing } = this.props;
|
||||
const { quadrant, gridSize } = mapTransformProps;
|
||||
const { id, x, y, radius } = this.plantInfo;
|
||||
const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize);
|
||||
const { qx, qy } = transformXY(round(x), round(y), mapTransformProps);
|
||||
const hovered = !!this.props.designer.hoveredPlant.icon;
|
||||
const scaledRadius = currentPlant ? radius : radius * 1.2;
|
||||
const alpha = dragging ? 0.4 : 1.0;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react";
|
||||
import { Component } from "react";
|
||||
import { TaggedPlantPointer } from "../../../resources/tagged_resources";
|
||||
import { round, getXYFromQuadrant } from "../util";
|
||||
import { round, transformXY } from "../util";
|
||||
import { cachedCrop } from "../../../open_farm/icons";
|
||||
import { MapTransformProps } from "../interfaces";
|
||||
import { SpreadOverlapHelper } from "../spread_overlap_helper";
|
||||
|
@ -82,8 +82,7 @@ export class SpreadCircle extends
|
|||
render() {
|
||||
const { radius, x, y, id } = this.props.plant.body;
|
||||
const { selected, mapTransformProps } = this.props;
|
||||
const { quadrant, gridSize } = mapTransformProps;
|
||||
const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize);
|
||||
const { qx, qy } = transformXY(round(x), round(y), mapTransformProps);
|
||||
const animate = !Session.deprecatedGetBool(BooleanSetting.disable_animations);
|
||||
|
||||
return <g id={"spread-" + id}>
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from "react";
|
|||
import { TaggedImage } from "../../resources/tagged_resources";
|
||||
import { CameraCalibrationData, BotOriginQuadrant } from "../interfaces";
|
||||
import { MapTransformProps } from "./interfaces";
|
||||
import { getXYFromQuadrant } from "./util";
|
||||
import { transformXY } from "./util";
|
||||
import { isNumber, round } from "lodash";
|
||||
|
||||
const PRECISION = 3; // Number of decimals for image placement coordinates
|
||||
|
@ -123,7 +123,7 @@ export function MapImage(props: MapImageProps) {
|
|||
const imageOffsetX = parse(offset.x);
|
||||
const imageOffsetY = parse(offset.y);
|
||||
const imageOrigin = origin ? origin.split("\"").join("") : undefined;
|
||||
const { quadrant, gridSize } = props.mapTransformProps;
|
||||
const { quadrant } = props.mapTransformProps;
|
||||
|
||||
/* Check if the image exists. */
|
||||
if (image) {
|
||||
|
@ -143,7 +143,7 @@ export function MapImage(props: MapImageProps) {
|
|||
x: x + imageOffsetX - size.x / 2,
|
||||
y: y + imageOffsetY - size.y / 2
|
||||
};
|
||||
const qCoords = getXYFromQuadrant(o.x, o.y, quadrant, gridSize);
|
||||
const qCoords = transformXY(o.x, o.y, props.mapTransformProps);
|
||||
const transformProps = { quadrant, qCoords, size, imageOrigin };
|
||||
return <image
|
||||
xlinkHref={imageUrl}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { MapTransformProps } from "./interfaces";
|
||||
import { getXYFromQuadrant, round } from "./util";
|
||||
import { transformXY, round } from "./util";
|
||||
import { isNumber } from "lodash";
|
||||
|
||||
export type SelectionBoxData =
|
||||
|
@ -13,10 +13,9 @@ export interface SelectionBoxProps {
|
|||
|
||||
export function SelectionBox(props: SelectionBoxProps) {
|
||||
const { x0, y0, x1, y1 } = props.selectionBox;
|
||||
const { quadrant, gridSize } = props.mapTransformProps;
|
||||
if (isNumber(x0) && isNumber(y0) && isNumber(x1) && isNumber(y1)) {
|
||||
const initial = getXYFromQuadrant(round(x0), round(y0), quadrant, gridSize);
|
||||
const drag = getXYFromQuadrant(round(x1), round(y1), quadrant, gridSize);
|
||||
const initial = transformXY(round(x0), round(y0), props.mapTransformProps);
|
||||
const drag = transformXY(round(x1), round(y1), props.mapTransformProps);
|
||||
const x = Math.min(initial.qx, drag.qx);
|
||||
const y = Math.min(initial.qy, drag.qy);
|
||||
const width = Math.max(initial.qx, drag.qx) - x;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { SpreadOverlapHelperProps } from "./interfaces";
|
||||
import { round, getXYFromQuadrant } from "./util";
|
||||
import { round, transformXY } from "./util";
|
||||
import { isUndefined } from "util";
|
||||
import { BotPosition } from "../../devices/interfaces";
|
||||
import { cachedCrop } from "../../open_farm/icons";
|
||||
|
@ -144,10 +144,8 @@ export class SpreadOverlapHelper extends
|
|||
render() {
|
||||
const { dragging, plant, activeDragXY, activeDragSpread,
|
||||
mapTransformProps } = this.props;
|
||||
const { quadrant, gridSize } = mapTransformProps;
|
||||
const { radius, x, y } = plant.body;
|
||||
const { qx, qy } = getXYFromQuadrant(
|
||||
round(x), round(y), quadrant, gridSize);
|
||||
const { qx, qy } = transformXY(round(x), round(y), mapTransformProps);
|
||||
const gardenCoord: BotPosition = { x: round(x), y: round(y), z: 0 };
|
||||
|
||||
// Convert spread diameter to radius (in mm).
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { MapTransformProps } from "./interfaces";
|
||||
import { getXYFromQuadrant, round } from "./util";
|
||||
import { transformXY, round } from "./util";
|
||||
import { BotPosition } from "../../devices/interfaces";
|
||||
import { isNumber } from "lodash";
|
||||
import { Color } from "../../ui/index";
|
||||
|
@ -12,9 +12,8 @@ export interface TargetCoordinateProps {
|
|||
|
||||
export function TargetCoordinate(props: TargetCoordinateProps) {
|
||||
const { x, y } = props.chosenLocation;
|
||||
const { quadrant, gridSize } = props.mapTransformProps;
|
||||
if (isNumber(x) && isNumber(y)) {
|
||||
const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize);
|
||||
const { qx, qy } = transformXY(round(x), round(y), props.mapTransformProps);
|
||||
return <g id="target-coordinate">
|
||||
<defs>
|
||||
<g id={"target-coordinate-crosshair-segment"}>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { SlotWithTool } from "../../resources/interfaces";
|
||||
import { getXYFromQuadrant } from "./util";
|
||||
import { transformXY } from "./util";
|
||||
import { MapTransformProps } from "./interfaces";
|
||||
import * as _ from "lodash";
|
||||
import { ToolbaySlot, ToolNames, Tool } from "./tool_graphics";
|
||||
|
@ -35,8 +35,9 @@ export class ToolSlotPoint extends
|
|||
|
||||
render() {
|
||||
const { id, x, y, pullout_direction } = this.slot.toolSlot.body;
|
||||
const { quadrant, gridSize } = this.props.mapTransformProps;
|
||||
const { qx, qy } = getXYFromQuadrant(x, y, quadrant, gridSize);
|
||||
const { mapTransformProps } = this.props;
|
||||
const { quadrant } = mapTransformProps;
|
||||
const { qx, qy } = transformXY(x, y, this.props.mapTransformProps);
|
||||
const toolName = this.slot.tool ? this.slot.tool.body.name : "no tool";
|
||||
const toolProps = {
|
||||
x: qx,
|
||||
|
|
|
@ -1,9 +1,26 @@
|
|||
import { BotOriginQuadrant, isBotOriginQuadrant } from "../interfaces";
|
||||
import { McuParams } from "farmbot";
|
||||
import { StepsPerMmXY } from "../../devices/interfaces";
|
||||
import { CheckedAxisLength, AxisNumberProperty, BotSize } from "./interfaces";
|
||||
import {
|
||||
CheckedAxisLength, AxisNumberProperty, BotSize, MapTransformProps
|
||||
} from "./interfaces";
|
||||
import { trim } from "../../util";
|
||||
|
||||
/*
|
||||
* Farm Designer Map Utilities
|
||||
*
|
||||
* Terms and Definitions:
|
||||
* GARDEN coordinates: real coordinates (could be sent to bot)
|
||||
* MAP coordinates: displayed locations (transformed according to map)
|
||||
*
|
||||
* Example:
|
||||
* garden coordinate of {x: 100, y: 100}
|
||||
* would be displayed at {x: 300, y: 300}
|
||||
* if the bot axis sizes are {x: 400, y: 400}
|
||||
* and the origin is in the lower right (map quadrant)
|
||||
*/
|
||||
|
||||
/** multiple to round to for round() */
|
||||
const SNAP = 10;
|
||||
|
||||
/**
|
||||
|
@ -14,80 +31,131 @@ export function round(num: number) {
|
|||
return (Math.round(num / SNAP) * SNAP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map coordinate calculations
|
||||
*
|
||||
* Constant:
|
||||
* left and top map padding
|
||||
* Map grid offset (for now)
|
||||
*
|
||||
* Dynamic:
|
||||
* mouse position
|
||||
* left and top scroll
|
||||
* zoom level
|
||||
* quadrant
|
||||
* grid size
|
||||
* XY swap (coming soon)
|
||||
*
|
||||
*/
|
||||
|
||||
/** Controlled by .farm-designer-map padding x10 */
|
||||
const mapPadding = { left: 318, top: 110 };
|
||||
|
||||
/** "x" => "left" and "y" => "top" */
|
||||
const leftOrTop: Record<"x" | "y", "top" | "left"> = { x: "left", y: "top" };
|
||||
|
||||
type XYCoordinate = { x: number, y: number };
|
||||
|
||||
export interface ScreenToGardenParams {
|
||||
quadrant: BotOriginQuadrant;
|
||||
pageX: number;
|
||||
pageY: number;
|
||||
page: XYCoordinate;
|
||||
scroll: { left: number, top: number };
|
||||
zoomLvl: number;
|
||||
gridSize: AxisNumberProperty;
|
||||
mapTransformProps: MapTransformProps;
|
||||
gridOffset: AxisNumberProperty;
|
||||
}
|
||||
|
||||
export function translateScreenToGarden(params: ScreenToGardenParams) {
|
||||
const { pageX, pageY, zoomLvl, quadrant, gridSize } = params;
|
||||
|
||||
const rawX = round((pageX - 320) / zoomLvl);
|
||||
const rawY = round((pageY - 110) / zoomLvl);
|
||||
|
||||
const x = calculateXBasedOnQuadrant({ value: rawX, quadrant, gridAxisLength: gridSize.x });
|
||||
const y = calculateYBasedOnQuadrant({ value: rawY, quadrant, gridAxisLength: gridSize.y });
|
||||
|
||||
return { x, y };
|
||||
/** Transform screen coordinates into garden coordinates */
|
||||
export function translateScreenToGarden(
|
||||
params: ScreenToGardenParams
|
||||
): XYCoordinate {
|
||||
const { page, scroll, zoomLvl, mapTransformProps, gridOffset } = params;
|
||||
const screenXY = page;
|
||||
const mapXY = ["x", "y"].reduce<XYCoordinate>(
|
||||
(result: XYCoordinate, axis: "x" | "y") => {
|
||||
const unscrolled = screenXY[axis] - scroll[leftOrTop[axis]];
|
||||
const map = unscrolled - mapPadding[leftOrTop[axis]];
|
||||
const grid = map - gridOffset[axis] * zoomLvl;
|
||||
const unscaled = round(grid / zoomLvl);
|
||||
result[axis] = unscaled;
|
||||
return result;
|
||||
}, { x: 0, y: 0 });
|
||||
const gardenXY = quadTransform({ coordinate: mapXY, mapTransformProps });
|
||||
return gardenXY;
|
||||
}
|
||||
|
||||
interface CalculateQuadrantParams {
|
||||
value: number;
|
||||
quadrant: BotOriginQuadrant;
|
||||
gridAxisLength: number;
|
||||
/* BotOriginQuadrant diagram
|
||||
|
||||
2 --- 1
|
||||
| |
|
||||
3 --- 4
|
||||
|
||||
*/
|
||||
|
||||
const NORMAL_QUADRANTS: Record<"x" | "y", BotOriginQuadrant[]> = {
|
||||
// `2` is shared, i.e. no change needed for either axis when it is selected
|
||||
x: [3, 2],
|
||||
y: [2, 1]
|
||||
};
|
||||
const MIRRORED_QUADRANTS: Record<"x" | "y", BotOriginQuadrant[]> = {
|
||||
// `4` is shared, i.e. change needed for both axes when it is selected
|
||||
x: [1, 4],
|
||||
y: [4, 3]
|
||||
};
|
||||
|
||||
interface QuadTransformParams {
|
||||
/** garden or map coordinates */
|
||||
coordinate: XYCoordinate;
|
||||
/** props necessary for coordinate transformation */
|
||||
mapTransformProps: MapTransformProps;
|
||||
}
|
||||
|
||||
function calculateXBasedOnQuadrant(params: CalculateQuadrantParams) {
|
||||
const { value, quadrant, gridAxisLength } = params;
|
||||
/** Quadrant coordinate transformation */
|
||||
function quadTransform(params: QuadTransformParams): XYCoordinate {
|
||||
const { coordinate, mapTransformProps } = params;
|
||||
const { quadrant, gridSize } = mapTransformProps;
|
||||
if (isBotOriginQuadrant(quadrant)) {
|
||||
switch (quadrant) {
|
||||
case 1:
|
||||
case 4:
|
||||
return gridAxisLength - value;
|
||||
case 2:
|
||||
case 3:
|
||||
return value;
|
||||
default:
|
||||
throw new Error("Something went wrong calculating the X origin.");
|
||||
}
|
||||
return ["x", "y"].reduce<XYCoordinate>(
|
||||
(result: XYCoordinate, axis: "x" | "y") => {
|
||||
switch (quadrant) {
|
||||
case MIRRORED_QUADRANTS[axis][0]:
|
||||
case MIRRORED_QUADRANTS[axis][1]:
|
||||
result[axis] = gridSize[axis] - coordinate[axis];
|
||||
return result;
|
||||
case NORMAL_QUADRANTS[axis][0]:
|
||||
case NORMAL_QUADRANTS[axis][1]:
|
||||
result[axis] = coordinate[axis];
|
||||
return result;
|
||||
default:
|
||||
throw new Error(`Something went wrong calculating the ${axis} origin.`);
|
||||
}
|
||||
}, { x: 0, y: 0 });
|
||||
} else {
|
||||
throw new Error("Invalid bot origin quadrant.");
|
||||
}
|
||||
}
|
||||
|
||||
function calculateYBasedOnQuadrant(params: CalculateQuadrantParams) {
|
||||
const { value, quadrant, gridAxisLength } = params;
|
||||
if (isBotOriginQuadrant(quadrant)) {
|
||||
switch (quadrant) {
|
||||
case 3:
|
||||
case 4:
|
||||
return gridAxisLength - value;
|
||||
case 1:
|
||||
case 2:
|
||||
return value;
|
||||
default:
|
||||
throw new Error("Something went wrong calculating the Y origin.");
|
||||
}
|
||||
} else {
|
||||
throw new Error("Invalid bot origin quadrant.");
|
||||
}
|
||||
}
|
||||
|
||||
export function getXYFromQuadrant(
|
||||
/**
|
||||
* Transform between garden and map coordinates
|
||||
*
|
||||
* Used for placing things in the Farm Designer map
|
||||
* or getting the real coordinates of things in the map.
|
||||
*
|
||||
* @param coordinate: garden or map coordinate
|
||||
* @param mapTransformProps: props necessary for coordinate transformation
|
||||
*/
|
||||
export function transformXY(
|
||||
x: number,
|
||||
y: number,
|
||||
q: BotOriginQuadrant,
|
||||
gridSize: AxisNumberProperty
|
||||
mapTransformProps: MapTransformProps
|
||||
): { qx: number, qy: number } {
|
||||
const transformed = quadTransform({ coordinate: { x, y }, mapTransformProps });
|
||||
return {
|
||||
qx: calculateXBasedOnQuadrant({ value: x, quadrant: q, gridAxisLength: gridSize.x }),
|
||||
qy: calculateYBasedOnQuadrant({ value: y, quadrant: q, gridAxisLength: gridSize.y })
|
||||
qx: transformed.x,
|
||||
qy: transformed.y
|
||||
};
|
||||
}
|
||||
|
||||
/** Determine bot axis lengths according to firmware settings */
|
||||
export function getBotSize(
|
||||
botMcuParams: McuParams,
|
||||
stepsPerMmXY: StepsPerMmXY,
|
||||
|
@ -114,6 +182,7 @@ export function getBotSize(
|
|||
return { x: getAxisLength("x"), y: getAxisLength("y") };
|
||||
}
|
||||
|
||||
/** Calculate map dimensions */
|
||||
export function getMapSize(
|
||||
gridSize: AxisNumberProperty,
|
||||
gridOffset: AxisNumberProperty
|
||||
|
@ -124,11 +193,11 @@ export function getMapSize(
|
|||
};
|
||||
}
|
||||
|
||||
/* Transform object based on selected map quadrant and grid size. */
|
||||
/** Transform object based on selected map quadrant and grid size. */
|
||||
export const transformForQuadrant =
|
||||
(quadrant: BotOriginQuadrant, gridSize: AxisNumberProperty) => {
|
||||
(mapTransformProps: MapTransformProps): string => {
|
||||
const quadrantFlips = () => {
|
||||
switch (quadrant) {
|
||||
switch (mapTransformProps.quadrant) {
|
||||
case 1: return { x: -1, y: 1 };
|
||||
case 2: return { x: 1, y: 1 };
|
||||
case 3: return { x: 1, y: -1 };
|
||||
|
@ -136,7 +205,7 @@ export const transformForQuadrant =
|
|||
default: return { x: 1, y: 1 };
|
||||
}
|
||||
};
|
||||
const origin = getXYFromQuadrant(0, 0, quadrant, gridSize);
|
||||
const origin = transformXY(0, 0, mapTransformProps);
|
||||
const flip = quadrantFlips();
|
||||
const translate = { x: flip.x * origin.qx, y: flip.y * origin.qy };
|
||||
return trim(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { AxisNumberProperty, MapTransformProps } from "../interfaces";
|
||||
import { getMapSize, getXYFromQuadrant } from "../util";
|
||||
import { getMapSize, transformXY } from "../util";
|
||||
import { BotPosition } from "../../../devices/interfaces";
|
||||
import { Color } from "../../../ui/index";
|
||||
import { botPositionLabel } from "./bot_position_label";
|
||||
|
@ -24,11 +24,11 @@ export class BotFigure extends
|
|||
setHover = (state: boolean) => { this.setState({ hovered: state }); };
|
||||
|
||||
render() {
|
||||
const { name, position, plantAreaOffset, eStopStatus } = this.props;
|
||||
const { quadrant, gridSize } = this.props.mapTransformProps;
|
||||
const mapSize = getMapSize(gridSize, plantAreaOffset);
|
||||
const positionQ = getXYFromQuadrant(
|
||||
(position.x || 0), (position.y || 0), quadrant, gridSize);
|
||||
const { name, position, plantAreaOffset, eStopStatus, mapTransformProps
|
||||
} = this.props;
|
||||
const mapSize = getMapSize(mapTransformProps.gridSize, plantAreaOffset);
|
||||
const positionQ = transformXY(
|
||||
(position.x || 0), (position.y || 0), mapTransformProps);
|
||||
const color = eStopStatus ? Color.virtualRed : Color.darkGray;
|
||||
const opacity = name.includes("encoder") ? 0.25 : 0.75;
|
||||
return <g id={name}>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { AxisNumberProperty, MapTransformProps } from "../interfaces";
|
||||
import { getMapSize, getXYFromQuadrant } from "../util";
|
||||
import { getMapSize, transformXY } from "../util";
|
||||
import { BotPosition } from "../../../devices/interfaces";
|
||||
import * as _ from "lodash";
|
||||
import { Session } from "../../../session";
|
||||
|
@ -116,11 +116,10 @@ function vacuumFigure(
|
|||
}
|
||||
|
||||
export function BotPeripherals(props: BotPeripheralsProps) {
|
||||
const { peripherals, position, plantAreaOffset } = props;
|
||||
const { quadrant, gridSize } = props.mapTransformProps;
|
||||
const mapSize = getMapSize(gridSize, plantAreaOffset);
|
||||
const positionQ = getXYFromQuadrant(
|
||||
(position.x || 0), (position.y || 0), quadrant, gridSize);
|
||||
const { peripherals, position, plantAreaOffset, mapTransformProps } = props;
|
||||
const mapSize = getMapSize(mapTransformProps.gridSize, plantAreaOffset);
|
||||
const positionQ = transformXY(
|
||||
(position.x || 0), (position.y || 0), mapTransformProps);
|
||||
|
||||
return <g className={"virtual-peripherals"}>
|
||||
{peripherals.map((x, i) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react";
|
||||
import * as _ from "lodash";
|
||||
import { MapTransformProps } from "../interfaces";
|
||||
import { getXYFromQuadrant } from "../util";
|
||||
import { transformXY } from "../util";
|
||||
import { BotPosition } from "../../../devices/interfaces";
|
||||
import { Color } from "../../../ui";
|
||||
|
||||
|
@ -38,9 +38,8 @@ export interface BotTrailProps {
|
|||
}
|
||||
|
||||
export function BotTrail(props: BotTrailProps) {
|
||||
const { quadrant, gridSize } = props.mapTransformProps;
|
||||
const toQ = (ox: number, oy: number) =>
|
||||
getXYFromQuadrant(ox, oy, quadrant, gridSize);
|
||||
transformXY(ox, oy, props.mapTransformProps);
|
||||
|
||||
const { x, y } = props.position;
|
||||
const watering = !!_.first(props.peripherals
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react";
|
||||
import { BotPosition } from "../../../devices/interfaces";
|
||||
import { MapTransformProps, AxisNumberProperty } from "../interfaces";
|
||||
import { getXYFromQuadrant } from "../util";
|
||||
import { transformXY } from "../util";
|
||||
import { Color } from "../../../ui";
|
||||
import { botPositionLabel } from "./bot_position_label";
|
||||
|
||||
|
@ -13,13 +13,12 @@ export interface NegativePositionLabelProps {
|
|||
|
||||
export function NegativePositionLabel(props: NegativePositionLabelProps) {
|
||||
const { position, mapTransformProps, plantAreaOffset } = props;
|
||||
const { quadrant, gridSize } = mapTransformProps;
|
||||
const xIsNegative = position.x && position.x < 0;
|
||||
const yIsNegative = position.y && position.y < 0;
|
||||
const origin = getXYFromQuadrant(
|
||||
const origin = transformXY(
|
||||
-plantAreaOffset.x + 40,
|
||||
-plantAreaOffset.y - 10,
|
||||
quadrant, gridSize);
|
||||
mapTransformProps);
|
||||
|
||||
return <g id={"negative-position-label"}
|
||||
fontFamily="Arial" textAnchor="middle" dominantBaseline="central"
|
||||
|
|
Loading…
Reference in New Issue