FarmBot/Farmbot-Web-App

View on GitHub
frontend/farm_designer/map/layers/tool_slots/tool_graphics.tsx

Summary

Maintainability
B
5 hrs
Test Coverage
import React from "react";
import { t } from "../../../../i18next_wrapper";
import { BotOriginQuadrant } from "../../../interfaces";
import { ToolPulloutDirection } from "farmbot/dist/resources/api_resources";
import { Actions } from "../../../../constants";
import { TaggedToolSlotPointer } from "farmbot";
import { noop } from "lodash";
import { ToolTransformProps } from "../../../../tools/interfaces";
import { isToolFlipped } from "../../../../tools/tool_slot_edit_components";
import { ToolbaySlot } from "../../tool_graphics/slot";
import { GantryToolSlot } from "../../tool_graphics/seed_trough";
import { ToolGraphicProps, ToolProps } from "../../tool_graphics/interfaces";
import { reduceToolName, Tool } from "../../tool_graphics/all_tools";
import {
  getToolDirection, slotPulloutAxis, ToolProfile,
} from "../../profile/tools";
import { ToolDimensions } from "../../tool_graphics/tool";

export const toolbaySlotAngle = (
  pulloutDirection: ToolPulloutDirection,
  quadrant: BotOriginQuadrant,
  xySwap: boolean) => {
  const rawAngle = () => {
    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;
      case ToolPulloutDirection.POSITIVE_Y: return 270;
      default: return 0;
    }
  };
  const adjustAngle = (angle: number) => {
    const horizontal = angle % 180 === 0;
    switch (quadrant) {
      case 1: return angle + 180;
      case 2: return horizontal ? angle : angle + 180;
      case 3: return angle;
      case 4: return horizontal ? angle + 180 : angle;
      default: return angle;
    }
  };
  return (adjustAngle(rawAngle())) % 360;
};

export const RotatedTool = (props: ToolProps) => {
  const { uuid, x, y, pulloutDirection, dispatch, flipped } = props.toolProps;
  const { quadrant, xySwap } = props.toolProps.toolTransformProps;
  const pullDirection = pulloutDirection || ToolPulloutDirection.POSITIVE_X;
  const angle = toolbaySlotAngle(pullDirection, quadrant, xySwap)
    + (flipped ? 180 : 0);
  return <g id={`rotated-tool-${uuid}`}>
    <defs id="unrotated-tool-source">
      <g id={`unrotated-tool-${uuid}`}>
        <Tool tool={props.tool}
          toolProps={props.toolProps} />
      </g>
    </defs>

    <use xlinkHref={`#unrotated-tool-${uuid}`}
      onMouseOver={() => dispatch(setToolHover(uuid))}
      onMouseLeave={() => dispatch(setToolHover(undefined))}
      transform={`rotate(${angle - 90}, ${x}, ${y})`} />
  </g>;
};

export const setToolHover = (payload: string | undefined) =>
  ({ type: Actions.HOVER_TOOL_SLOT, payload });

export interface ToolSlotSVGProps {
  toolSlot: TaggedToolSlotPointer;
  toolName: string | undefined;
  toolTransformProps: ToolTransformProps;
  profile?: boolean;
  size?: number;
}

export const ToolSlotSVG = (props: ToolSlotSVGProps) => {
  const toolProps: ToolGraphicProps = {
    toolName: props.toolName,
    x: 0, y: 0,
    hovered: false,
    dispatch: noop,
    uuid: "panel-" + props.toolSlot.uuid,
    pulloutDirection: props.toolSlot.body.pullout_direction,
    flipped: isToolFlipped(props.toolSlot.body.meta),
    toolTransformProps: {
      quadrant: props.toolTransformProps.quadrant,
      xySwap: props.toolTransformProps.xySwap,
    },
  };
  const pulloutDirection = props.toolSlot.body.pullout_direction;
  const size = `${props.size || 3}rem`;
  return props.toolSlot.body.gantry_mounted
    ? <svg width={size} height={size} viewBox={"-40 0 80 1"}>
      <GantryToolSlot x={0} y={0} xySwap={props.toolTransformProps.xySwap} />
      {props.toolSlot.body.tool_id &&
        <RotatedTool
          tool={reduceToolName(props.toolName)}
          toolProps={toolProps} />}
    </svg>
    : <div className={"tool-svg"}>
      <div className={"top"}>
        <svg width={size} height={size} viewBox={"-50 0 100 1"}>
          {pulloutDirection &&
            <ToolbaySlot
              id={-(props.toolSlot.body.id || 1)}
              x={0}
              y={0}
              pulloutDirection={pulloutDirection}
              quadrant={props.toolTransformProps.quadrant}
              occupied={false}
              xySwap={props.toolTransformProps.xySwap} />}
          {(props.toolSlot.body.tool_id || !pulloutDirection) &&
            <RotatedTool
              tool={reduceToolName(props.toolName)}
              toolProps={toolProps} />}
        </svg>
        {props.profile && <p>{t("top")}</p>}
      </div>
      {props.profile &&
        <div className={"front"}>
          <svg width={size} height={size} viewBox={"-15 40 100 1"}>
            <ToolProfile toolName={props.toolName} reversed={false} x={0} y={0}
              width={ToolDimensions.diameter} height={ToolDimensions.thickness}
              sideView={"y" == slotPulloutAxis(pulloutDirection)}
              slotDirection={pulloutDirection} coordinate={false}
              toolFlipped={getToolDirection(
                pulloutDirection,
                isToolFlipped(props.toolSlot.body.meta),
                false)} />
          </svg>
          <p>{t("front")}</p>
        </div>}
    </div>;
};

export interface ToolSVGProps {
  toolName: string | undefined;
  profile?: boolean;
}

export const ToolSVG = (props: ToolSVGProps) => {
  const toolProps = {
    toolName: props.toolName,
    x: 0, y: 0, hovered: false, dispatch: noop, uuid: "", flipped: false,
    pulloutDirection: 0, toolTransformProps: { xySwap: false, quadrant: 2 },
  };
  const viewBox = "-40 0 80 1";
  return <div className={"tool-svg"}>
    <div className={"top"}>
      <svg width="3rem" height="3rem" viewBox={viewBox}>
        <Tool tool={reduceToolName(props.toolName)} toolProps={toolProps} />
      </svg>
      {props.profile && <p>{t("top")}</p>}
    </div>
    {props.profile &&
      <div className={"front"}>
        <svg width="3rem" height="3rem" viewBox={"-5 40 80 1"}>
          <ToolProfile toolName={props.toolName} reversed={false} x={0} y={0}
            width={ToolDimensions.diameter} height={ToolDimensions.thickness}
            sideView={false} slotDirection={ToolPulloutDirection.NONE}
            toolFlipped={false} coordinate={false} />
        </svg>
        <p>{t("front")}</p>
      </div>}
  </div>;
};