From b343d441925e1a185109960cda757e4a88d8b725 Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Fri, 13 Apr 2018 02:54:00 -0700 Subject: [PATCH] implement xySwap in map --- .../map/__tests__/bot_extents_test.tsx | 27 +++++++ .../map/__tests__/tool_graphics_test.tsx | 70 ++++++++++++------- .../map/__tests__/tool_label_test.ts | 48 ++++++++----- .../farm_designer/map/__tests__/util_test.ts | 20 +++++- webpack/farm_designer/map/bot_extents.tsx | 5 +- webpack/farm_designer/map/drag_helpers.tsx | 6 +- webpack/farm_designer/map/garden_map.tsx | 17 +++-- webpack/farm_designer/map/grid.tsx | 10 +-- webpack/farm_designer/map/map_background.tsx | 16 +++-- webpack/farm_designer/map/tool_graphics.tsx | 11 +-- webpack/farm_designer/map/tool_label.tsx | 11 +-- webpack/farm_designer/map/tool_slot_point.tsx | 8 ++- webpack/farm_designer/map/util.ts | 42 ++++++++--- .../__tests__/bot_figure_test.tsx | 28 +++++--- .../__tests__/bot_peripherals_test.tsx | 29 ++++++++ .../map/virtual_farmbot/bot_figure.tsx | 12 ++-- .../map/virtual_farmbot/bot_peripherals.tsx | 23 +++--- 17 files changed, 272 insertions(+), 111 deletions(-) diff --git a/webpack/farm_designer/map/__tests__/bot_extents_test.tsx b/webpack/farm_designer/map/__tests__/bot_extents_test.tsx index 48a098ba2..95e5c7f6a 100644 --- a/webpack/farm_designer/map/__tests__/bot_extents_test.tsx +++ b/webpack/farm_designer/map/__tests__/bot_extents_test.tsx @@ -95,6 +95,33 @@ describe("", () => { expect(maxLines.at(1).props()).toEqual({ "x1": 2998, "x2": 2900, "y1": 100, "y2": 100 }); }); + it("renders max line in correct location", () => { + const p = fakeProps(); + p.stopAtHome.x = false; + p.stopAtHome.y = false; + p.botSize = { + x: { value: 100, isDefault: false }, + y: { value: 100, isDefault: true } + }; + const wrapper = shallow(); + const maxLines = wrapper.find("#max-lines").find("line"); + expect(maxLines.at(0).props()).toEqual({ "x1": 100, "x2": 100, "y1": 2, "y2": 100 }); + }); + + it("renders max line in correct location with swapped axes", () => { + const p = fakeProps(); + p.stopAtHome.x = false; + p.stopAtHome.y = false; + p.mapTransformProps.xySwap = true; + p.botSize = { + x: { value: 100, isDefault: false }, + y: { value: 100, isDefault: true } + }; + const wrapper = shallow(); + const maxLines = wrapper.find("#max-lines").find("line"); + expect(maxLines.at(0).props()).toEqual({ "x1": 2, "x2": 100, "y1": 100, "y2": 100 }); + }); + it("renders no lines", () => { const p = fakeProps(); p.stopAtHome.x = false; diff --git a/webpack/farm_designer/map/__tests__/tool_graphics_test.tsx b/webpack/farm_designer/map/__tests__/tool_graphics_test.tsx index 06d40e51d..d205c2053 100644 --- a/webpack/farm_designer/map/__tests__/tool_graphics_test.tsx +++ b/webpack/farm_designer/map/__tests__/tool_graphics_test.tsx @@ -13,31 +13,49 @@ describe("", () => { x: 10, y: 20, pulloutDirection: 0, - quadrant: 2 + quadrant: 2, + xySwap: false, }; }; const checkSlotDirection = - (direction: number, quadrant: BotOriginQuadrant, expected: string) => { - it(`renders slot, pullout: ${direction} quad: ${quadrant}`, () => { - const p = fakeProps(); - p.pulloutDirection = direction; - p.quadrant = quadrant; - const wrapper = mount(); - expect(wrapper.find("use").props().transform).toEqual(expected); - }); + (direction: number, + quadrant: BotOriginQuadrant, + xySwap: boolean, + expected: string) => { + it(`renders slot, pullout: ${direction} quad: ${quadrant} yx: ${xySwap}`, + () => { + const p = fakeProps(); + p.pulloutDirection = direction; + p.quadrant = quadrant; + p.xySwap = xySwap; + const wrapper = mount(); + expect(wrapper.find("use").props().transform).toEqual(expected); + }); }; - checkSlotDirection(0, 2, "rotate(0, 10, 20)"); - checkSlotDirection(1, 1, "rotate(180, 10, 20)"); - checkSlotDirection(1, 2, "rotate(0, 10, 20)"); - checkSlotDirection(1, 3, "rotate(0, 10, 20)"); - checkSlotDirection(1, 4, "rotate(180, 10, 20)"); - checkSlotDirection(2, 3, "rotate(180, 10, 20)"); - checkSlotDirection(3, 1, "rotate(90, 10, 20)"); - checkSlotDirection(3, 2, "rotate(90, 10, 20)"); - checkSlotDirection(3, 3, "rotate(270, 10, 20)"); - checkSlotDirection(3, 4, "rotate(270, 10, 20)"); - checkSlotDirection(4, 3, "rotate(90, 10, 20)"); + checkSlotDirection(0, 2, false, "rotate(0, 10, 20)"); + checkSlotDirection(1, 1, false, "rotate(180, 10, 20)"); + checkSlotDirection(1, 2, false, "rotate(0, 10, 20)"); + checkSlotDirection(1, 3, false, "rotate(0, 10, 20)"); + checkSlotDirection(1, 4, false, "rotate(180, 10, 20)"); + checkSlotDirection(2, 3, false, "rotate(180, 10, 20)"); + checkSlotDirection(3, 1, false, "rotate(90, 10, 20)"); + checkSlotDirection(3, 2, false, "rotate(90, 10, 20)"); + checkSlotDirection(3, 3, false, "rotate(270, 10, 20)"); + checkSlotDirection(3, 4, false, "rotate(270, 10, 20)"); + checkSlotDirection(4, 3, false, "rotate(90, 10, 20)"); + + checkSlotDirection(0, 2, true, "rotate(180, 10, 20)"); + checkSlotDirection(1, 1, true, "rotate(90, 10, 20)"); + checkSlotDirection(1, 2, true, "rotate(90, 10, 20)"); + checkSlotDirection(1, 3, true, "rotate(270, 10, 20)"); + checkSlotDirection(1, 4, true, "rotate(270, 10, 20)"); + checkSlotDirection(2, 3, true, "rotate(90, 10, 20)"); + checkSlotDirection(3, 1, true, "rotate(180, 10, 20)"); + checkSlotDirection(3, 2, true, "rotate(0, 10, 20)"); + checkSlotDirection(3, 3, true, "rotate(0, 10, 20)"); + checkSlotDirection(3, 4, true, "rotate(180, 10, 20)"); + checkSlotDirection(4, 3, true, "rotate(180, 10, 20)"); }); describe("", () => { @@ -58,7 +76,7 @@ describe("", () => { }; it("renders standard tool styling", () => { - const wrapper = mount(); + const wrapper = mount(); const props = wrapper.find("circle").last().props(); expect(props.r).toEqual(35); expect(props.cx).toEqual(10); @@ -69,7 +87,7 @@ describe("", () => { it("tool hover", () => { const p = fakeProps(); p.toolProps.hovered = true; - const wrapper = mount(); + const wrapper = mount(); const props = wrapper.find("circle").last().props(); expect(props.fill).toEqual(Color.darkGray); }); @@ -77,7 +95,7 @@ describe("", () => { it("renders special tool styling: bin", () => { const p = fakeProps(); p.tool = "seedBin"; - const wrapper = mount(); + const wrapper = mount(); const elements = wrapper.find("#seed-bin").find("circle"); expect(elements.length).toEqual(2); expect(elements.last().props().fill).toEqual("url(#SeedBinGradient)"); @@ -87,7 +105,7 @@ describe("", () => { const p = fakeProps(); p.tool = "seedBin"; p.toolProps.hovered = true; - const wrapper = mount(); + const wrapper = mount(); p.toolProps.hovered = true; expect(wrapper.find("#seed-bin").find("circle").length).toEqual(3); }); @@ -95,7 +113,7 @@ describe("", () => { it("renders special tool styling: tray", () => { const p = fakeProps(); p.tool = "seedTray"; - const wrapper = mount(); + const wrapper = mount(); const elements = wrapper.find("#seed-tray"); expect(elements.find("circle").length).toEqual(2); expect(elements.find("rect").length).toEqual(1); @@ -106,7 +124,7 @@ describe("", () => { const p = fakeProps(); p.tool = "seedTray"; p.toolProps.hovered = true; - const wrapper = mount(); + const wrapper = mount(); p.toolProps.hovered = true; expect(wrapper.find("#seed-tray").find("circle").length).toEqual(3); }); diff --git a/webpack/farm_designer/map/__tests__/tool_label_test.ts b/webpack/farm_designer/map/__tests__/tool_label_test.ts index dcf50ff1c..130c77f37 100644 --- a/webpack/farm_designer/map/__tests__/tool_label_test.ts +++ b/webpack/farm_designer/map/__tests__/tool_label_test.ts @@ -7,30 +7,46 @@ describe("textAnchorPosition()", () => { const MIDDLE_BOTTOM = { anchor: "middle", x: 0, y: -40 }; it("returns correct label position: positive x", () => { - expect(textAnchorPosition(1, 1)).toEqual(END); - expect(textAnchorPosition(1, 2)).toEqual(START); - expect(textAnchorPosition(1, 3)).toEqual(START); - expect(textAnchorPosition(1, 4)).toEqual(END); + expect(textAnchorPosition(1, 1, false)).toEqual(END); + expect(textAnchorPosition(1, 2, false)).toEqual(START); + expect(textAnchorPosition(1, 3, false)).toEqual(START); + expect(textAnchorPosition(1, 4, false)).toEqual(END); + expect(textAnchorPosition(1, 1, true)).toEqual(MIDDLE_TOP); + expect(textAnchorPosition(1, 2, true)).toEqual(MIDDLE_TOP); + expect(textAnchorPosition(1, 3, true)).toEqual(MIDDLE_BOTTOM); + expect(textAnchorPosition(1, 4, true)).toEqual(MIDDLE_BOTTOM); }); it("returns correct label position: negative x", () => { - expect(textAnchorPosition(2, 1)).toEqual(START); - expect(textAnchorPosition(2, 2)).toEqual(END); - expect(textAnchorPosition(2, 3)).toEqual(END); - expect(textAnchorPosition(2, 4)).toEqual(START); + expect(textAnchorPosition(2, 1, false)).toEqual(START); + expect(textAnchorPosition(2, 2, false)).toEqual(END); + expect(textAnchorPosition(2, 3, false)).toEqual(END); + expect(textAnchorPosition(2, 4, false)).toEqual(START); + expect(textAnchorPosition(2, 1, true)).toEqual(MIDDLE_BOTTOM); + expect(textAnchorPosition(2, 2, true)).toEqual(MIDDLE_BOTTOM); + expect(textAnchorPosition(2, 3, true)).toEqual(MIDDLE_TOP); + expect(textAnchorPosition(2, 4, true)).toEqual(MIDDLE_TOP); }); it("returns correct label position: positive y", () => { - expect(textAnchorPosition(3, 1)).toEqual(MIDDLE_TOP); - expect(textAnchorPosition(3, 2)).toEqual(MIDDLE_TOP); - expect(textAnchorPosition(3, 3)).toEqual(MIDDLE_BOTTOM); - expect(textAnchorPosition(3, 4)).toEqual(MIDDLE_BOTTOM); + expect(textAnchorPosition(3, 1, false)).toEqual(MIDDLE_TOP); + expect(textAnchorPosition(3, 2, false)).toEqual(MIDDLE_TOP); + expect(textAnchorPosition(3, 3, false)).toEqual(MIDDLE_BOTTOM); + expect(textAnchorPosition(3, 4, false)).toEqual(MIDDLE_BOTTOM); + expect(textAnchorPosition(3, 1, true)).toEqual(END); + expect(textAnchorPosition(3, 2, true)).toEqual(START); + expect(textAnchorPosition(3, 3, true)).toEqual(START); + expect(textAnchorPosition(3, 4, true)).toEqual(END); }); it("returns correct label position: negative y", () => { - expect(textAnchorPosition(4, 1)).toEqual(MIDDLE_BOTTOM); - expect(textAnchorPosition(4, 2)).toEqual(MIDDLE_BOTTOM); - expect(textAnchorPosition(4, 3)).toEqual(MIDDLE_TOP); - expect(textAnchorPosition(4, 4)).toEqual(MIDDLE_TOP); + expect(textAnchorPosition(4, 1, false)).toEqual(MIDDLE_BOTTOM); + expect(textAnchorPosition(4, 2, false)).toEqual(MIDDLE_BOTTOM); + expect(textAnchorPosition(4, 3, false)).toEqual(MIDDLE_TOP); + expect(textAnchorPosition(4, 4, false)).toEqual(MIDDLE_TOP); + expect(textAnchorPosition(4, 1, true)).toEqual(START); + expect(textAnchorPosition(4, 2, true)).toEqual(END); + expect(textAnchorPosition(4, 3, true)).toEqual(END); + expect(textAnchorPosition(4, 4, true)).toEqual(START); }); }); diff --git a/webpack/farm_designer/map/__tests__/util_test.ts b/webpack/farm_designer/map/__tests__/util_test.ts index 67db45711..601ab1e73 100644 --- a/webpack/farm_designer/map/__tests__/util_test.ts +++ b/webpack/farm_designer/map/__tests__/util_test.ts @@ -170,9 +170,18 @@ describe("getbotSize()", () => { describe("getMapSize()", () => { it("calculates map size", () => { const mapSize = getMapSize( - { x: 2000, y: 1000 }, + fakeMapTransformProps(), { x: 100, y: 50 }); - expect(mapSize).toEqual({ x: 2200, y: 1100 }); + expect(mapSize).toEqual({ h: 1600, w: 3200 }); + }); + + it("calculates map size: X&Y Swapped", () => { + const fakeMPT = fakeMapTransformProps(); + fakeMPT.xySwap = true; + const mapSize = getMapSize( + fakeMPT, + { x: 100, y: 50 }); + expect(mapSize).toEqual({ h: 3200, w: 1600 }); }); }); @@ -184,10 +193,17 @@ describe("transformXY", () => { const transformCheck = (original: QXY, transformed: QXY, transformProps: MapTransformProps) => { + transformProps.xySwap = false; expect(transformXY(original.qx, original.qy, transformProps)) .toEqual(transformed); expect(transformXY(transformed.qx, transformed.qy, transformProps)) .toEqual(original); + transformProps.xySwap = true; + const transformedYX = { qx: transformed.qy, qy: transformed.qx }; + expect(transformXY(original.qx, original.qy, transformProps)) + .toEqual(transformedYX); + expect(transformXY(transformed.qx, transformed.qy, transformProps)) + .toEqual({ qx: original.qy, qy: original.qx }); }; it("calculates transformed coordinate: quadrant 2", () => { diff --git a/webpack/farm_designer/map/bot_extents.tsx b/webpack/farm_designer/map/bot_extents.tsx index 3e8e5ba01..999031df5 100644 --- a/webpack/farm_designer/map/bot_extents.tsx +++ b/webpack/farm_designer/map/bot_extents.tsx @@ -4,6 +4,7 @@ import { BotExtentsProps } from "./interfaces"; export function BotExtents(props: BotExtentsProps) { const { stopAtHome, botSize, mapTransformProps } = props; + const { xySwap } = mapTransformProps; const homeLength = transformXY( botSize.x.value, botSize.y.value, mapTransformProps); const homeZero = transformXY(2, 2, mapTransformProps); @@ -27,12 +28,12 @@ export function BotExtents(props: BotExtentsProps) { } - {!botSize.x.isDefault && + {(xySwap ? !botSize.y.isDefault : !botSize.x.isDefault) && } - {!botSize.y.isDefault && + {(xySwap ? !botSize.x.isDefault : !botSize.y.isDefault) && diff --git a/webpack/farm_designer/map/drag_helpers.tsx b/webpack/farm_designer/map/drag_helpers.tsx index 42f90a2ef..a1c40d6b1 100644 --- a/webpack/farm_designer/map/drag_helpers.tsx +++ b/webpack/farm_designer/map/drag_helpers.tsx @@ -55,7 +55,7 @@ export function DragHelpers(props: DragHelpersProps) { const { dragging, plant, zoomLvl, activeDragXY, mapTransformProps, plantAreaOffset } = props; - const mapSize = getMapSize(mapTransformProps.gridSize, plantAreaOffset); + const mapSize = getMapSize(mapTransformProps, plantAreaOffset); const { radius, x, y } = plant.body; const scale = 1 + Math.round(15 * (1.8 - zoomLvl)) / 10; // scale factor @@ -71,8 +71,8 @@ export function DragHelpers(props: DragHelpersProps) { } {dragging && // Active plant - - + + } {dragging && // Active plant diff --git a/webpack/farm_designer/map/garden_map.tsx b/webpack/farm_designer/map/garden_map.tsx index dffd6ccc8..3b2b9ee24 100644 --- a/webpack/farm_designer/map/garden_map.tsx +++ b/webpack/farm_designer/map/garden_map.tsx @@ -263,16 +263,19 @@ export class GardenMap extends const plant = this.getPlant(); const map = document.querySelector(".farm-designer-map"); const { gridSize } = this.props; + const { quadrant, xySwap } = this.mapTransformProps; if (this.state.isDragging && plant && map) { const zoomLvl = parseFloat(window.getComputedStyle(map).zoom || "1"); 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); + const dX = xySwap && (quadrant % 2 === 1) ? -deltaX : deltaX; + const dY = xySwap && (quadrant % 2 === 1) ? -deltaY : deltaY; this.setState({ pageX: qx, pageY: qy, - activeDragXY: { x: plant.body.x + deltaX, y: plant.body.y + deltaY, z: 0 } + activeDragXY: { x: plant.body.x + dX, y: plant.body.y + dY, z: 0 } }); - this.props.dispatch(movePlant({ deltaX, deltaY, plant, gridSize })); + this.props.dispatch(movePlant({ deltaX: dX, deltaY: dY, plant, gridSize })); } break; case Mode.boxSelect: @@ -312,13 +315,14 @@ export class GardenMap extends render() { const { gridSize } = this.props; - const mapSize = getMapSize(gridSize, this.props.gridOffset); + const mapSize = getMapSize(this.mapTransformProps, this.props.gridOffset); const mapTransformProps = this.mapTransformProps; + const { xySwap } = mapTransformProps; return
+ width={gridSizeW} height={gridSizeH} fill="url(#minor_grid)" /> - + diff --git a/webpack/farm_designer/map/map_background.tsx b/webpack/farm_designer/map/map_background.tsx index 354990dd4..8039ef5b8 100644 --- a/webpack/farm_designer/map/map_background.tsx +++ b/webpack/farm_designer/map/map_background.tsx @@ -1,13 +1,15 @@ import * as React from "react"; import { MapBackgroundProps } from "./interfaces"; import { Color } from "../../ui/index"; +import { getMapSize } from "./util"; export function MapBackground(props: MapBackgroundProps) { const { mapTransformProps, plantAreaOffset } = props; - const { gridSize } = mapTransformProps; + const { gridSize, xySwap } = mapTransformProps; + const gridSizeW = xySwap ? gridSize.y : gridSize.x; + const gridSizeH = xySwap ? gridSize.x : gridSize.y; const boardWidth = 20; - const mapWidth = gridSize.x + plantAreaOffset.x * 2; - const mapHeight = gridSize.y + plantAreaOffset.y * 2; + const mapSize = getMapSize(mapTransformProps, plantAreaOffset); return + width={gridSizeW} height={gridSizeH} fill={Color.gridSoil} /> ; } diff --git a/webpack/farm_designer/map/tool_graphics.tsx b/webpack/farm_designer/map/tool_graphics.tsx index 5523eef4a..c2fd5655e 100644 --- a/webpack/farm_designer/map/tool_graphics.tsx +++ b/webpack/farm_designer/map/tool_graphics.tsx @@ -22,13 +22,16 @@ export interface ToolSlotGraphicProps { y: number; pulloutDirection: ToolPulloutDirection; quadrant: BotOriginQuadrant; + xySwap: boolean; } const toolbaySlotAngle = ( pulloutDirection: ToolPulloutDirection, - quadrant: BotOriginQuadrant) => { + quadrant: BotOriginQuadrant, + xySwap: boolean) => { const rawAngle = () => { - switch (pulloutDirection) { + const direction = pulloutDirection + (xySwap ? 2 : 0); + switch (direction > 4 ? direction % 4 : direction) { case ToolPulloutDirection.POSITIVE_X: return 0; case ToolPulloutDirection.NEGATIVE_X: return 180; case ToolPulloutDirection.NEGATIVE_Y: return 90; @@ -56,8 +59,8 @@ export enum ToolNames { } export const ToolbaySlot = (props: ToolSlotGraphicProps) => { - const { id, x, y, pulloutDirection, quadrant } = props; - const angle = toolbaySlotAngle(pulloutDirection, quadrant); + const { id, x, y, pulloutDirection, quadrant, xySwap } = props; + const angle = toolbaySlotAngle(pulloutDirection, quadrant, xySwap); return { + quadrant: BotOriginQuadrant, + xySwap: boolean): { x: number, y: number, anchor: string } => { const rawAnchor = () => { - switch (pulloutDirection) { + const direction = pulloutDirection + (xySwap ? 2 : 0); + switch (direction > 4 ? direction % 4 : direction) { case ToolPulloutDirection.POSITIVE_X: return Anchor.start; case ToolPulloutDirection.NEGATIVE_X: return Anchor.end; case ToolPulloutDirection.NEGATIVE_Y: return Anchor.middleTop; @@ -48,11 +50,12 @@ interface ToolLabelProps { y: number; pulloutDirection: ToolPulloutDirection; quadrant: BotOriginQuadrant; + xySwap: boolean; } export const ToolLabel = (props: ToolLabelProps) => { - const { toolName, hovered, x, y, pulloutDirection, quadrant } = props; - const labelAnchor = textAnchorPosition(pulloutDirection, quadrant); + const { toolName, hovered, x, y, pulloutDirection, quadrant, xySwap } = props; + const labelAnchor = textAnchorPosition(pulloutDirection, quadrant, xySwap); return } + quadrant={quadrant} + xySwap={xySwap} />} {(this.slot.tool || !pullout_direction) && + quadrant={quadrant} + xySwap={xySwap} /> ; } } diff --git a/webpack/farm_designer/map/util.ts b/webpack/farm_designer/map/util.ts index e8692c1bb..d3195d798 100644 --- a/webpack/farm_designer/map/util.ts +++ b/webpack/farm_designer/map/util.ts @@ -44,7 +44,7 @@ export function round(num: number) { * zoom level * quadrant * grid size - * XY swap (coming soon) + * XY swap * */ @@ -69,6 +69,7 @@ export function translateScreenToGarden( params: ScreenToGardenParams ): XYCoordinate { const { page, scroll, zoomLvl, mapTransformProps, gridOffset } = params; + const { xySwap } = mapTransformProps; const screenXY = page; const mapXY = ["x", "y"].reduce( (result: XYCoordinate, axis: "x" | "y") => { @@ -79,8 +80,11 @@ export function translateScreenToGarden( result[axis] = unscaled; return result; }, { x: 0, y: 0 }); - const gardenXY = quadTransform({ coordinate: mapXY, mapTransformProps }); - return gardenXY; + const coordinate = xySwap ? { x: mapXY.y, y: mapXY.x } : mapXY; + const gardenXY = transformXY(coordinate.x, coordinate.y, mapTransformProps); + return xySwap + ? { x: gardenXY.qy, y: gardenXY.qx } + : { x: gardenXY.qx, y: gardenXY.qy }; } /* BotOriginQuadrant diagram @@ -112,7 +116,7 @@ interface QuadTransformParams { /** Quadrant coordinate transformation */ function quadTransform(params: QuadTransformParams): XYCoordinate { const { coordinate, mapTransformProps } = params; - const { quadrant, gridSize } = mapTransformProps; + const { gridSize, quadrant } = mapTransformProps; if (isBotOriginQuadrant(quadrant)) { return ["x", "y"].reduce( (result: XYCoordinate, axis: "x" | "y") => { @@ -146,9 +150,24 @@ function quadTransform(params: QuadTransformParams): XYCoordinate { export function transformXY( x: number, y: number, - mapTransformProps: MapTransformProps + rawMapTransformProps: MapTransformProps ): { qx: number, qy: number } { - const transformed = quadTransform({ coordinate: { x, y }, mapTransformProps }); + const { quadrant, gridSize, xySwap } = rawMapTransformProps; + const coordinate = { + x: xySwap ? y : x, + y: xySwap ? x : y, + }; + const transformed = quadTransform({ + coordinate, + mapTransformProps: { + quadrant, + gridSize: { + x: xySwap ? gridSize.y : gridSize.x, + y: xySwap ? gridSize.x : gridSize.y, + }, + xySwap + } + }); return { qx: transformed.x, qy: transformed.y @@ -184,13 +203,18 @@ export function getBotSize( /** Calculate map dimensions */ export function getMapSize( - gridSize: AxisNumberProperty, + mapTransformProps: MapTransformProps, gridOffset: AxisNumberProperty -): AxisNumberProperty { - return { +): { w: number, h: number } { + const { gridSize, xySwap } = mapTransformProps; + const mapSize = { x: gridSize.x + gridOffset.x * 2, y: gridSize.y + gridOffset.y * 2 }; + return { + w: xySwap ? mapSize.y : mapSize.x, + h: xySwap ? mapSize.x : mapSize.y + }; } /** Transform object based on selected map quadrant and grid size. */ diff --git a/webpack/farm_designer/map/virtual_farmbot/__tests__/bot_figure_test.tsx b/webpack/farm_designer/map/virtual_farmbot/__tests__/bot_figure_test.tsx index 4f174c72c..101b74b31 100644 --- a/webpack/farm_designer/map/virtual_farmbot/__tests__/bot_figure_test.tsx +++ b/webpack/farm_designer/map/virtual_farmbot/__tests__/bot_figure_test.tsx @@ -17,21 +17,23 @@ describe("", () => { function checkPositionForQuadrant( quadrant: BotOriginQuadrant, + xySwap: boolean, expected: { x: number, y: number }, name: string, opacity: number) { it(`shows ${name} in correct location for quadrant ${quadrant}`, () => { const p = fakeProps(); p.mapTransformProps.quadrant = quadrant; + p.mapTransformProps.xySwap = xySwap; p.name = name; const result = shallow(); const expectedGantryProps = expect.objectContaining({ id: "gantry", - x: expected.x - 10, - y: -100, - width: 20, - height: 1700, + x: xySwap ? -100 : expected.x - 10, + y: xySwap ? expected.x - 10 : -100, + width: xySwap ? 1700 : 20, + height: xySwap ? 20 : 1700, fill: Color.darkGray, fillOpacity: opacity }); @@ -40,8 +42,8 @@ describe("", () => { const expectedUTMProps = expect.objectContaining({ id: "UTM", - cx: expected.x, - cy: expected.y, + cx: xySwap ? expected.y : expected.x, + cy: xySwap ? expected.x : expected.y, r: 35, fill: Color.darkGray, fillOpacity: opacity @@ -51,11 +53,15 @@ describe("", () => { }); } - checkPositionForQuadrant(1, { x: 3000, y: 0 }, "motors", 0.75); - checkPositionForQuadrant(2, { x: 0, y: 0 }, "motors", 0.75); - checkPositionForQuadrant(3, { x: 0, y: 1500 }, "motors", 0.75); - checkPositionForQuadrant(4, { x: 3000, y: 1500 }, "motors", 0.75); - checkPositionForQuadrant(2, { x: 0, y: 0 }, "encoders", 0.25); + checkPositionForQuadrant(1, false, { x: 3000, y: 0 }, "motors", 0.75); + checkPositionForQuadrant(2, false, { x: 0, y: 0 }, "motors", 0.75); + checkPositionForQuadrant(3, false, { x: 0, y: 1500 }, "motors", 0.75); + checkPositionForQuadrant(4, false, { x: 3000, y: 1500 }, "motors", 0.75); + checkPositionForQuadrant(1, true, { x: 0, y: 1500 }, "motors", 0.75); + checkPositionForQuadrant(2, true, { x: 0, y: 0 }, "motors", 0.75); + checkPositionForQuadrant(3, true, { x: 3000, y: 0 }, "motors", 0.75); + checkPositionForQuadrant(4, true, { x: 3000, y: 1500 }, "motors", 0.75); + checkPositionForQuadrant(2, false, { x: 0, y: 0 }, "encoders", 0.25); it("changes location", () => { const p = fakeProps(); diff --git a/webpack/farm_designer/map/virtual_farmbot/__tests__/bot_peripherals_test.tsx b/webpack/farm_designer/map/virtual_farmbot/__tests__/bot_peripherals_test.tsx index 9d3c9d25b..a69d1b833 100644 --- a/webpack/farm_designer/map/virtual_farmbot/__tests__/bot_peripherals_test.tsx +++ b/webpack/farm_designer/map/virtual_farmbot/__tests__/bot_peripherals_test.tsx @@ -59,6 +59,35 @@ describe("", () => { fill: "url(#LightingGradient)", height: 1700, width: 400, x: 0, y: -100 }); + expect(wrapper.find("use").first().props()).toEqual({ + xlinkHref: "#light-half", + transform: "rotate(0, 0, 750)" + }); + expect(wrapper.find("use").last().props()).toEqual({ + xlinkHref: "#light-half", + transform: "rotate(180, 0, 750)" + }); + }); + + it("displays light: X&Y swapped", () => { + const p = fakeProps(); + p.peripherals[0].label = "lights"; + p.peripherals[0].value = true; + p.mapTransformProps.xySwap = true; + const wrapper = shallow(); + expect(wrapper.find("#lights").length).toEqual(1); + expect(wrapper.find("rect").last().props()).toEqual({ + fill: "url(#LightingGradient)", + height: 1700, width: 400, x: -100, y: 0 + }); + expect(wrapper.find("use").first().props()).toEqual({ + xlinkHref: "#light-half", + transform: "rotate(90, 750, 850)" + }); + expect(wrapper.find("use").last().props()).toEqual({ + xlinkHref: "#light-half", + transform: "rotate(270, -100, 0)" + }); }); it("displays water", () => { diff --git a/webpack/farm_designer/map/virtual_farmbot/bot_figure.tsx b/webpack/farm_designer/map/virtual_farmbot/bot_figure.tsx index 9bef86243..6df76a628 100644 --- a/webpack/farm_designer/map/virtual_farmbot/bot_figure.tsx +++ b/webpack/farm_designer/map/virtual_farmbot/bot_figure.tsx @@ -26,18 +26,18 @@ export class BotFigure extends render() { const { name, position, plantAreaOffset, eStopStatus, mapTransformProps } = this.props; - const mapSize = getMapSize(mapTransformProps.gridSize, plantAreaOffset); + const { xySwap } = mapTransformProps; + const mapSize = getMapSize(mapTransformProps, 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 @@ -34,9 +35,13 @@ function lightsFigure( + transform={trim(`rotate(${xySwap ? 90 : 0}, + ${xySwap ? height / 2 + x : x}, + ${mapHeightMid})`)} /> + transform={trim(`rotate(${xySwap ? 270 : 180}, + ${x}, + ${xySwap ? y : mapHeightMid})`)} /> ; } @@ -117,7 +122,8 @@ function vacuumFigure( export function BotPeripherals(props: BotPeripheralsProps) { const { peripherals, position, plantAreaOffset, mapTransformProps } = props; - const mapSize = getMapSize(mapTransformProps.gridSize, plantAreaOffset); + const { xySwap } = mapTransformProps; + const mapSize = getMapSize(mapTransformProps, plantAreaOffset); const positionQ = transformXY( (position.x || 0), (position.y || 0), mapTransformProps); @@ -126,9 +132,10 @@ export function BotPeripherals(props: BotPeripheralsProps) { if (x.label.toLowerCase().includes("light") && x.value) { return lightsFigure({ i, - x: positionQ.qx, - y: -plantAreaOffset.y, - height: mapSize.y + x: xySwap ? -plantAreaOffset.y : positionQ.qx, + y: xySwap ? positionQ.qy : -plantAreaOffset.y, + height: xySwap ? mapSize.w : mapSize.h, + xySwap, }); } else if (x.label.toLowerCase().includes("water") && x.value) { return waterFigure({