diff --git a/.eslintrc.js b/.eslintrc.js index 8799d9c..c8ecec2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -31,7 +31,11 @@ module.exports = { asyncArrow: "always" }], "quote-props": ["error", "consistent"], - "quotes": ["error", "double"] + "quotes": ["error", "double"], + "sort-imports": ["error", { + "ignoreCase": true, + "allowSeparatedGroups": true + }] }, overrides: [ { diff --git a/src/components/Canvas.tsx b/src/components/Canvas.tsx new file mode 100644 index 0000000..5e56704 --- /dev/null +++ b/src/components/Canvas.tsx @@ -0,0 +1,33 @@ +import React from "react" + +import CanvasContext from "./CanvasContext" + +export interface SystemCanvasProps { + height: number, + width: number, + children?: JSX.Element[] +} + +function SystemCanvas({ height, width, children }: SystemCanvasProps) { + const canvasRef = React.useRef(null) + const [context, setContext] = React.useState(null) + + React.useEffect(() => { + const context = canvasRef.current.getContext("2d") + context.save() + context.fillStyle = "black" + context.fillRect(0, 0, width, height) + context.restore() + setContext(context) + }) + + return (
+ + + {children} + +
+ ) +} + +export default SystemCanvas diff --git a/src/components/CanvasContext.ts b/src/components/CanvasContext.ts new file mode 100644 index 0000000..553c8a0 --- /dev/null +++ b/src/components/CanvasContext.ts @@ -0,0 +1,5 @@ +import React from "react" + +const CanvasContext = React.createContext(null) + +export default CanvasContext diff --git a/src/components/Home.tsx b/src/components/Home.tsx index c2d2e2d..cbfe7fa 100644 --- a/src/components/Home.tsx +++ b/src/components/Home.tsx @@ -1,17 +1,19 @@ -import React, { useState, useEffect } from "react" +import React, { useEffect, useState } from "react" -import { Message, Object } from "@app/types" +import { Body, Bounds, Message } from "@app/types" +import Canvas from "./Canvas" import Client from "@app/client" -import ObjectList from "./ObjectList" +import RenderedBody from "./RenderedBody" function Home() { - const [objects, setObjects] = useState([]) + const [bodies, setBodies] = useState([]) const [iteration, setIteration] = useState(0) const [messageCount, setMessageCount] = useState(0) const [client, setClient] = useState(null) + const [bounds, setBounds] = useState(new Bounds(0, 0, 30, 30)) const handleMessage = (msg: Message) => { - setObjects(msg.objects) + setBodies(msg.objects) setIteration(msg.iteration) setMessageCount((mc) => mc + 1) } @@ -31,11 +33,29 @@ function Home() { } }, [client, messageCount]) + useEffect(() => { + setBounds((bounds) => bodies.reduce( + (bounds, obj) => new Bounds( + Math.min(obj.x, bounds.minX), + Math.min(obj.y, bounds.minY), + Math.max(obj.x, bounds.maxX), + Math.max(obj.y, bounds.maxY)), bounds)) + }, [bodies]) + + const actualBounds = new Bounds(0, 0, 300, 300) + return ( <>

Hello, World!

-

Iteration: { iteration }, message count: { messageCount }.

- +

Iteration: { iteration }, message count: { messageCount }, nObjects: { bodies.length }.

+ + {bodies.map((body) => )} + ) } diff --git a/src/components/ObjectList.tsx b/src/components/ObjectList.tsx deleted file mode 100644 index 6f47e38..0000000 --- a/src/components/ObjectList.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react" - -import { Object } from "@app/types" - -export interface ObjectListProps { - objects: Object[] -} - -function ObjectList({ objects }: ObjectListProps) { - return (
    - {objects.map((o) =>
  • {o.name} @ ({o.x}, {o.y}, {o.z})
  • )} -
) -} - -export default ObjectList diff --git a/src/components/RenderedBody.tsx b/src/components/RenderedBody.tsx new file mode 100644 index 0000000..a505768 --- /dev/null +++ b/src/components/RenderedBody.tsx @@ -0,0 +1,33 @@ +import React from "react" + +import { Body, Bounds } from "types" +import CanvasContext from "./CanvasContext" + +export interface RenderedBodyProps { + body: Body + bounds: Bounds + actualBounds: Bounds +} + +function RenderedBody({ body, bounds, actualBounds } : RenderedBodyProps): JSX.Element { + const context = React.useContext(CanvasContext) + if (context === null) { + return + } + + const xScale = actualBounds.width / bounds.width + const yScale = actualBounds.height / bounds.width + + context.save() + context.scale(xScale, yScale) + context.translate(-bounds.minX, -bounds.minY) + context.fillStyle = "white" + context.beginPath() + context.ellipse(body.x, body.y, 1 / xScale, 1 / yScale, 0, 0, Math.PI * 2) + context.fill() + context.restore() + + return null +} + +export default RenderedBody diff --git a/src/index.tsx b/src/index.tsx index b892400..0080c5d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,6 @@ -import React from 'react' -import ReactDOM from 'react-dom' +import React from "react" +import ReactDOM from "react-dom" -import Home from '@app/components/Home' +import Home from "@app/components/Home" -ReactDOM.render(, document.getElementById('root')) +ReactDOM.render(, document.getElementById("root")) diff --git a/src/types.ts b/src/types.ts index 23f5536..cb3afa5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -export interface Object { +export interface Body { name: string, id: number, x: number, @@ -8,5 +8,22 @@ export interface Object { export interface Message { iteration: number, - objects: Object[] + objects: Body[] +} + +export class Bounds { + minX: number + minY: number + maxX: number + maxY: number + + constructor(minX: number, minY: number, maxX: number, maxY: number) { + this.minX = minX + this.minY = minY + this.maxX = maxX + this.maxY = maxY + } + + get width() { return this.maxX - this.minX } + get height() { return this.maxY - this.minY } } diff --git a/tsconfig.json b/tsconfig.json index 2e8a911..b4626ea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,4 +13,4 @@ "@app/*": ["../src/*"] } } -} \ No newline at end of file +}