components: implement basic selection algorithm

This commit is contained in:
Artur Tamborski 2020-11-28 22:09:52 +01:00
parent fd6a9f52a7
commit f3498331ad
4 changed files with 147 additions and 33 deletions

View File

@ -16,7 +16,7 @@ export default function App(): JSX.Element {
<Board <Board
numRows={10} numRows={10}
numCols={10} numCols={10}
numLines={5} numLines={1}
/> />
</div> </div>
<div /> <div />

View File

@ -9,6 +9,11 @@ export type Point = {
y: number; y: number;
} }
type Selection = {
start: Point;
end: Point;
}
interface IBoardProps { interface IBoardProps {
numRows: number; numRows: number;
numCols: number; numCols: number;
@ -17,52 +22,54 @@ interface IBoardProps {
interface IBoardState { interface IBoardState {
cells: string[][]; cells: string[][];
lines: boolean[]; selections: Selection[];
isSelecting: boolean; isSelecting: boolean;
startPos: Point; start: Point;
endPos: Point; end: Point;
} }
const Point0: Point = {x: 0, y: 0};
const Selection0: Selection = {start: Point0, end: Point0};
export default class Board extends React.Component<IBoardProps, IBoardState> { export default class Board extends React.Component<IBoardProps, IBoardState> {
constructor(props: IBoardProps) { constructor(props: IBoardProps) {
super(props); super(props);
this.state = { this.state = {
cells: Array(props.numCols).fill(Array(props.numRows).fill('A')), cells: Array(props.numCols).fill(Array(props.numRows).fill('A')),
lines: Array(props.numLines).fill(false), selections: Array(props.numLines).fill(Selection0),
isSelecting: false, isSelecting: false,
startPos: {x: 0, y: 0}, start: Point0,
endPos: {x: 0, y: 0}, end: Point0,
} }
} }
moveIsValid(pos: Point): boolean { moveIsValid(pos: Point): boolean {
const x = Math.abs(pos.x - this.state.startPos.x); const x = Math.abs(pos.x - this.state.start.x);
const y = Math.abs(pos.y - this.state.startPos.y); const y = Math.abs(pos.y - this.state.start.y);
const lineIsValid = x === 0 || y === 0 || x === y; const lineIsValid = x === 0 || y === 0 || x === y;
return this.state.isSelecting && lineIsValid; return this.state.isSelecting && lineIsValid;
} }
handleMouseDown(startPos: Point) { handleMouseDown(start: Point) {
const endPos = startPos; const end = start;
let lines = [...this.state.lines]; let selections = [...this.state.selections];
lines[0] = true; this.setState({...this.state, start, end, selections, isSelecting: true});
this.setState({...this.state, startPos, endPos, lines, isSelecting: true});
} }
handleMouseUp(endPos: Point) { handleMouseUp(end: Point) {
if (!this.moveIsValid(endPos)) if (!this.moveIsValid(end))
return; return;
this.setState({...this.state, endPos, isSelecting: false}); this.setState({...this.state, end, isSelecting: false});
} }
handleMouseOver(endPos: Point) { handleMouseOver(end: Point) {
if (!this.moveIsValid(endPos)) if (!this.moveIsValid(end))
return; return;
this.setState({...this.state, endPos}); this.setState({...this.state, end});
} }
renderCell(s: string, pos: Point): JSX.Element { renderCell(s: string, pos: Point): JSX.Element {
@ -83,8 +90,8 @@ export default class Board extends React.Component<IBoardProps, IBoardState> {
return ( return (
<Line <Line
key={`Line${x}`} key={`Line${x}`}
startPos={{...this.state.startPos}} startPos={{...this.state.start}}
endPos={{...this.state.endPos}} endPos={{...this.state.end}}
cellSize={60} cellSize={60}
/> />
); );
@ -96,7 +103,7 @@ export default class Board extends React.Component<IBoardProps, IBoardState> {
oy.map((s, x) => oy.map((s, x) =>
this.renderCell(s, {x, y}))); this.renderCell(s, {x, y})));
let lines = this.state.lines.filter(x => x).map((ox, x) => let lines = this.state.selections.filter(x => x).map((ox, x) =>
this.renderLine(x)); this.renderLine(x));
return ( return (

View File

@ -1,5 +1,11 @@
.Line { .Line {
stroke: red; stroke: #8b8be5;
stroke-width: 10px; stroke-width: 5px;
opacity: 0.3; fill: none;
}
.Line2 {
stroke: #d6a54d;
stroke-width: 5px;
fill: none;
} }

View File

@ -4,6 +4,18 @@ import {Point} from '../Board/Board'
import './Line.scss'; import './Line.scss';
enum Direction {
None = 0,
Up = 1,
Down = 2,
Left = 10,
LeftUp = 11,
LeftDown = 12,
Right = 20,
RightUp = 21,
RightDown = 22,
}
interface ILineProps { interface ILineProps {
startPos: Point; startPos: Point;
endPos: Point; endPos: Point;
@ -12,14 +24,103 @@ interface ILineProps {
export default function Line(props: ILineProps): JSX.Element { export default function Line(props: ILineProps): JSX.Element {
const pos = (v: number) => v * props.cellSize + props.cellSize / 2; const pos = (v: number) => v * props.cellSize + props.cellSize / 2;
const dir = (v: number) => v < 0 ? 2 : v > 0 ? 1 : 0;
const offset = 2.5;
const margin = 30;
let [x, y, w, h, t] = [0, 0, 0, 0, ''];
const ox = dir(props.startPos.x - props.endPos.x);
const oy = dir(props.startPos.y - props.endPos.y);
const d = Number(`${ox}${oy}`) as Direction;
switch (d) {
case Direction.None:
break;
case Direction.Up:
x = pos(props.endPos.x) - margin + offset;
y = pos(props.endPos.y) - margin + offset;
w = 55;
h = pos(props.startPos.y - props.endPos.y +1) - margin - offset * 2;
t = '';
break;
case Direction.Down:
x = pos(props.startPos.x) - margin + offset;
y = pos(props.startPos.y) - margin + offset;
w = 55;
h = pos(props.endPos.y - props.startPos.y +1) - margin - offset * 2;
t = '';
break;
case Direction.Left:
x = pos(props.endPos.x) - margin + offset;
y = pos(props.endPos.y) - margin + offset;
w = pos(props.startPos.x - props.endPos.x +1) - margin - offset * 2;
h = 55;
t = '';
break;
case Direction.LeftUp:
x = pos(props.endPos.x-1) + margin;
y = pos(props.endPos.y) - margin + offset;
w = props.startPos.x - props.endPos.x;
w = pos(w) + (margin - offset * 2) + w * (margin - offset * 2);
h = 55;
t = `translate(29,-14),rotate(45, ${x-offset}, ${y+offset})`
break;
case Direction.LeftDown:
x = pos(props.endPos.x-1) + margin;
y = pos(props.endPos.y) - margin + offset;
w = props.startPos.x - props.endPos.x;
w = pos(w) + (margin - offset * 2) + w * (margin - offset * 2);
h = 55;
t = `translate(-12,27),rotate(-45, ${x+offset}, ${y-offset})`
break;
case Direction.Right:
x = pos(props.startPos.x) - margin + offset;
y = pos(props.startPos.y) - margin + offset;
w = pos(props.endPos.x - props.startPos.x +1) - margin - offset * 2;
h = 55;
t = '';
break;
case Direction.RightUp:
x = pos(props.startPos.x) - margin + offset;
y = pos(props.startPos.y) - margin + offset;
w = props.endPos.x - props.startPos.x;
w = pos(w) + (margin - offset * 2) + w * (margin - offset * 2);
h = 55;
t = `translate(-9, 29),rotate(-45, ${x-offset}, ${y+offset})`
break;
case Direction.RightDown:
x = pos(props.startPos.x) - margin + offset;
y = pos(props.startPos.y) - margin + offset;
w = props.endPos.x - props.startPos.x;
w = pos(w) + (margin - offset * 2) + w * (margin - offset * 2);
h = 55;
t = `translate(27, -14),rotate(45, ${x-offset}, ${y+offset})`
break;
}
return ( return (
<line <svg>
className="Line" <rect
x1={`${pos(props.endPos.x)}`} className="Line2"
y1={`${pos(props.endPos.y)}`} x={x}
x2={`${pos(props.startPos.x)}`} y={y}
y2={`${pos(props.startPos.y)}`} width={w}
/> height={h}
/>
<rect
className="Line"
x={x}
y={y}
width={w}
height={h}
transform={t}
rx={margin - offset}
ry={margin - offset}
/>
<circle cx={x} cy={y} r="5" fill="orange" />
<circle cx={x+w} cy={y+h} r="5" fill="red" />
</svg>
); );
} }