-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[choreo] Add Lane Constraint to UI (#845)
Signed-off-by: Jade Turner <[email protected]> Co-authored-by: Tyler Veness <[email protected]> Co-authored-by: shueja-personal <[email protected]> Co-authored-by: shueja <[email protected]>
- Loading branch information
1 parent
6099a0e
commit a61d317
Showing
10 changed files
with
339 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import * as React from "react"; | ||
import { SvgIcon as MuiSvgIcon, SvgIconProps, styled } from "@mui/material"; | ||
const SvgIcon = styled(MuiSvgIcon, { | ||
name: "KeepInLaneIcon", | ||
shouldForwardProp: (prop) => prop !== "fill" | ||
})<SvgIconProps>(() => ({ | ||
fill: "currentColor", | ||
stroke: "none" | ||
})); | ||
|
||
const KeepInLane: React.FunctionComponent<SvgIconProps> = (props) => { | ||
return ( | ||
<SvgIcon | ||
viewBox="0 -960 960 960" | ||
focusable="false" | ||
aria-hidden="true" | ||
{...props} | ||
> | ||
<path d="M160-160v-640h80v640h-80Zm280 0v-160h80v160h-80Zm280 0v-640h80v640h-80ZM440-400v-160h80v160h-80Zm0-240v-160h80v160h-80Z" /> | ||
</SvgIcon> | ||
); | ||
}; | ||
export default KeepInLane; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
src/components/field/svg/constraintDisplay/KeepInLaneOverlay.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import React, { Component } from "react"; | ||
import { IConstraintDataStore } from "../../../../document/ConstraintDataStore"; | ||
import { | ||
ConstraintKey, | ||
DataMap | ||
} from "../../../../document/ConstraintDefinitions"; | ||
import { IHolonomicWaypointStore } from "../../../../document/HolonomicWaypointStore"; | ||
import * as d3 from "d3"; | ||
import { observer } from "mobx-react"; | ||
import { doc } from "../../../../document/DocumentManager"; | ||
|
||
const STROKE = 0.02; | ||
const DOT = 0.1; | ||
|
||
type Props<K extends ConstraintKey> = { | ||
data: IConstraintDataStore<K>; | ||
start?: IHolonomicWaypointStore; | ||
end?: IHolonomicWaypointStore; | ||
}; | ||
class KeepInLaneOverlay extends Component<Props<"KeepInLane">, object> { | ||
rootRef: React.RefObject<SVGGElement> = React.createRef<SVGGElement>(); | ||
componentDidMount() { | ||
if (this.rootRef.current) { | ||
const dragHandleDrag = d3 | ||
.drag<SVGCircleElement, undefined>() | ||
.on("drag", (event) => this.dragPointTolerance(event)) | ||
.on("start", () => { | ||
doc.history.startGroup(() => {}); | ||
}) | ||
.on("end", (_event) => { | ||
doc.history.stopGroup(); | ||
}) | ||
.container(this.rootRef.current); | ||
d3.select<SVGCircleElement, undefined>( | ||
`#dragTarget-keepInLaneAbove` | ||
).call(dragHandleDrag); | ||
d3.select<SVGCircleElement, undefined>( | ||
`#dragTarget-keepInLaneBelow` | ||
).call(dragHandleDrag); | ||
} | ||
} | ||
dragPointTolerance(event: DragEvent) { | ||
const data = this.props.data; | ||
const { x, y } = event; | ||
const start = this.props.start; | ||
const end = this.props.end; | ||
if (start === undefined || end === undefined || start.uuid === end.uuid) { | ||
return; | ||
} | ||
const startX = start.x.value; | ||
const startY = start.y.value; | ||
const endX = end.x.value; | ||
const endY = end.y.value; | ||
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points | ||
const dx = endX - startX; | ||
const dy = endY - startY; | ||
const dist = | ||
Math.abs(dy * x - dx * y + endX * startY - endY * startX) / | ||
Math.hypot(dx, dy); | ||
data.tolerance.set(dist); | ||
} | ||
render() { | ||
const data = this.props.data.serialize as DataMap["KeepInLane"]; | ||
const tolerance = data.props.tolerance.val + STROKE / 2; | ||
const start = this.props.start; | ||
const end = this.props.end; | ||
if (start === undefined || end === undefined || start.uuid === end.uuid) { | ||
return <></>; | ||
} | ||
const startX = start.x.value; | ||
const startY = start.y.value; | ||
const endX = end.x.value; | ||
const endY = end.y.value; | ||
|
||
const dx = endX - startX; | ||
const dy = endY - startY; | ||
const dist = Math.hypot(dy, dx); | ||
if (dist === 0) { | ||
return <></>; | ||
} | ||
const [offsetX, offsetY] = [ | ||
-tolerance * (dy / dist), | ||
tolerance * (dx / dist) | ||
]; | ||
const [startAboveX, startAboveY] = [startX + offsetX, startY + offsetY]; | ||
const [endAboveX, endAboveY] = [endX + offsetX, endY + offsetY]; | ||
const [midAboveX, midAboveY] = [ | ||
(endAboveX + startAboveX) / 2, | ||
(endAboveY + startAboveY) / 2 | ||
]; | ||
const [startBelowX, startBelowY] = [startX - offsetX, startY - offsetY]; | ||
const [endBelowX, endBelowY] = [endX - offsetX, endY - offsetY]; | ||
const [midBelowX, midBelowY] = [ | ||
(endBelowX + startBelowX) / 2, | ||
(endBelowY + startBelowY) / 2 | ||
]; | ||
return ( | ||
<g ref={this.rootRef}> | ||
{/* Lines */} | ||
|
||
<line | ||
x1={startAboveX} | ||
x2={endAboveX} | ||
y1={startAboveY} | ||
y2={endAboveY} | ||
stroke="green" | ||
strokeWidth={STROKE} | ||
strokeOpacity={1.0} | ||
id="line-keepInLaneAbove" | ||
></line> | ||
<line | ||
x1={startBelowX} | ||
x2={endBelowX} | ||
y1={startBelowY} | ||
y2={endBelowY} | ||
stroke="green" | ||
strokeWidth={STROKE} | ||
strokeOpacity={1.0} | ||
id="line-keepInLaneBelow" | ||
></line> | ||
<circle | ||
cx={midAboveX} | ||
cy={midAboveY} | ||
r={DOT} | ||
fill={"green"} | ||
fillOpacity={1.0} | ||
pointerEvents={"visible"} | ||
id="dragTarget-keepInLaneAbove" | ||
></circle> | ||
<circle | ||
cx={midBelowX} | ||
cy={midBelowY} | ||
r={DOT} | ||
fill={"green"} | ||
fillOpacity={1.0} | ||
pointerEvents={"visible"} | ||
id="dragTarget-keepInLaneBelow" | ||
></circle> | ||
</g> | ||
); | ||
} | ||
} | ||
export default observer(KeepInLaneOverlay); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.