Introduce arrows #82
Merged
maxime.batista
merged 12 commits from editor/arrows
into master
1 year ago
Loading…
Reference in new issue
There is no content yet.
Delete Branch 'editor/arrows'
Deleting a branch is permanent. It CANNOT be undone. Continue?
This pull request adds the ability to drag and drop an arrow from a player to another player or anywhere on the court.
Arrows can be bend using control points, that can be added by double-clicking on an arrow.
The arrow types are automatically detected and changed depending on the player states :
If a player has the ball in his hand :
If a player does not have the ball in his hand :
here's an example :

l.objects is undefined
(isBallOnCourt
) on https://maxou.dev/IQBall/editor/arrows/publicparentBase,
)
const setControlPointPos = (newPos: Pos | undefined) => {
The idiomatic way to think about that is that
null
is the explicit absence of something, whereasundefined
is the "I haven't a value for that yet so you shouldn't use me".In your case
(newPos: Pos | null)
would be more appropriated.undefined
is effectively an erro type or a way to declare a parameter as optional(newPos?: Pos)
.return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2)
}
export function size(vector: Pos): number {
The length of a vector is called its magnitude or its norm.
*/
export function angle(a: Pos, b: Pos): number {
const r = relativeTo(a, b)
return Math.atan2(r.x, r.y)
The
atan2
function arguments are inverted.i know but i did this because i would negate the result everywhere otherwise
}
}
export function between(a: Pos, b: Pos): Pos {
This "between" is a middle of a segment.
}
}
export function rotate(vec: Pos, deg: number): Pos {
This function takes a angle in degrees, whereas the
Math
functions use radians.export function rotate(vec: Pos, deg: number): Pos {
return {
x: Math.cos(deg * vec.x) - Math.sin(deg * vec.y),
y: Math.sin(deg * vec.x) + Math.cos(deg * vec.y),
This formula is incorrect.
x_2 = cos(\alpha)x_1 - sin(\alpha)y_1
y_2 = sin(\alpha)x_1 + cos(\alpha)y_1
import React from "react"
Unused import
import { NULL_POS, ratioWithinBase } from "../arrows/Pos"
export interface PlayerProps {
export interface PlayerProps<A extends ReactNode> {
Don't. A component should never rely on the type behind
ReactNode
.return (args: A) => {
clearTimeout(task)
return new Promise((resolve) => {
task = setTimeout(() => f(args).then(resolve), delay)
The inner promise should also be catched.
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.6",
"react-xarrows": "^2.0.2",
Unused dependency
"devDependencies": {
"@vitejs/plugin-react": "^4.1.0",
"vite-plugin-svgr": "^4.1.0",
"eslint-plugin-react-hooks": "^4.6.0",
Unused dependency
ed2cfa2013
toec46fb78a2
1 year agoI saw you committed a
sql/.guard
file 👀// If the (original) segments changes, overwrite the current ones.
useLayoutEffect(() => {
setInternalSegments(computeInternalSegments(segments))
}, [startPos, segments])
This dependency always changes, so memorize it.
didn't added it because it causes an infinite recursion
Yes, it recurses infinitely. That's why memoization is useful here. Wrap
computeInternalSegments
in auseCallback
hook.startRadius,
endRadius,
style,
])
}, [update, containerRef])
// Inserts a segment where the mouse double clicks on the arrow
useEffect(() => {
So... Why not using a React event handler?
pathRef?.current?.addEventListener("dblclick", addSegment)
return () => {
pathRef?.current?.removeEventListener("dblclick", addSegment)
This is highly unsafe. The reference might have changed if the component has been re-rendered. Place the current reference in a temporary.
* A player that is placed on the court, which can be selected, and moved in the associated bounds
* */
export default function CourtPlayer({
export default function CourtPlayer<A extends ReactNode>({
role: player.role,
hasBall: player.hasBall,
})
} as Player)
The result looks great! Don't forget memoization and you are good to go.
368acee92e
into master 1 year agoReviewers
368acee92e
.