From 10e7ce5764b3b456e1f909cd6b6a90f076c2e014 Mon Sep 17 00:00:00 2001 From: Martin Ashby Date: Thu, 17 May 2018 20:17:01 +0100 Subject: Fixed rotation problem. Added rotate function. Added docker file --- Dockerfile | 5 ++ src/App.js | 139 +++++++++++++++++++++++++++++++++--------------------- src/PaintArea.js | 39 ++++++++------- src/Utils.js | 53 +++++++++++++++++++-- src/Utils.test.js | 36 +++++++++++++- 5 files changed, 194 insertions(+), 78 deletions(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f195bee --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +FROM python +RUN pip install unicornhathd Flask Flask-Sockets +COPY build/ . +COPY server.py . +ENTRYPOINT python server.py \ No newline at end of file diff --git a/src/App.js b/src/App.js index 68473ce..98611b2 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' import './App.css' -import { rgb, xy, findContiguousPixels, getPixel } from './Utils' +import { rgb, xy, findContiguousPixels, getPixel, rotatePixelsClock, rotatePixelsCounterClock } from './Utils' import { setPixel, clear, noop, save, load } from './Actions' import Palette from './Palette' import PaintArea from './PaintArea' @@ -11,40 +11,56 @@ import LoadDialog from './LoadDialog' import SaveDialog from './SaveDialog' const tools = [ - { - name: "paint", - icon: "fas fa-pencil-alt", + { + name: "paint", + icon: "fas fa-pencil-alt", action: function (x, y) { let { r, g, b } = rgb(this.state.selectedColor) - setPixel(this._websocket, x, y, r, g, b) + setPixel(this._websocket, x, y, r, g, b) } }, - { - name: "fill", - icon: "fab fa-bitbucket", + { + name: "fill", + icon: "fab fa-bitbucket", action: function (x, y) { let pixelsToColor = findContiguousPixels(x, y, this.state.pixels) pixelsToColor.forEach((coord) => { - let px = { ...xy(coord), ...rgb(this.state.selectedColor)} + let px = { ...xy(coord), ...rgb(this.state.selectedColor) } setPixel(this._websocket, px.x, px.y, px.r, px.g, px.b) }) } }, - { - name: "erase", - icon: "fas fa-eraser", + { + name: "erase", + icon: "fas fa-eraser", action: function (x, y) { - setPixel(this._websocket, x, y, 0, 0, 0) + setPixel(this._websocket, x, y, 0, 0, 0) }, }, - { - name: "pick", - icon: "fas fa-eye-dropper", + { + name: "pick", + icon: "fas fa-eye-dropper", action: function (x, y) { let color = getPixel(x, y, this.state.pixels) this.setState({ selectedColor: color }) }, }, + { + name: "rotate-clockwise", + icon: "fas fa-redo", + onSelect: function () { + let newPixels = rotatePixelsClock(this.state.pixels) + this._setAllPixels(newPixels) + } + }, + { + name: "rotate-anticlockwise", + icon: "fas fa-undo", + onSelect: function () { + let newPixels = rotatePixelsCounterClock(this.state.pixels) + this._setAllPixels(newPixels) + } + }, // { // name: "lighten", // icon: "far fa-sun" @@ -53,23 +69,23 @@ const tools = [ // name: "darken", // icon: "fas fa-sun" // }, - { - name: "save", - icon: "fas fa-save", + { + name: "save", + icon: "fas fa-save", onSelect: function () { this.setState({ showingSave: true }) } }, - { - name: "load", - icon: "fas fa-save", + { + name: "load", + icon: "fas fa-save", onSelect: function () { this.setState({ showingLoad: true }) } }, - { - name: "trash", - icon: "fas fa-trash", + { + name: "trash", + icon: "fas fa-trash", onSelect: function () { clear(this._websocket) } @@ -103,7 +119,7 @@ class App extends Component { this._connectWebsocket() } - _onMessage({data}) { + _onMessage({ data }) { let state = JSON.parse(data) this.setState({ ...state // Includes pixels and saves @@ -111,22 +127,23 @@ class App extends Component { } _onOpen() { - this.setState({connected: true}) + this.setState({ connected: true }) noop(this._websocket) } _onClose() { - this.setState({connected: false}) + this.setState({ connected: false }) this._connectWebsocket() } _onError() { - this.setState({connected: false}) + this.setState({ connected: false }) this._connectWebsocket() } _connectWebsocket() { - this._websocket = new WebSocket('ws://' + window.location.hostname + ':3001/ws') + // this._websocket = new WebSocket('ws://' + window.location.hostname + ':3001/ws') + this._websocket = new WebSocket('ws://shinypi:3001/ws') this._websocket.onmessage = this._onMessage this._websocket.onopen = this._onOpen this._websocket.onclose = this._onClose @@ -140,7 +157,7 @@ class App extends Component { } let action = tool.action if (!action) { - return + return } action.bind(this)(x, y) } @@ -150,52 +167,64 @@ class App extends Component { if (selectAction) { selectAction.bind(this)() } else { - this.setState({selectedTool: tool}) + this.setState({ selectedTool: tool }) } } _loadDrawing(name) { load(this._websocket, name) - this.setState({showingLoad: false}) + this.setState({ showingLoad: false }) } _saveDrawing(name) { save(this._websocket, name) - this.setState({showingSave: false}) + this.setState({ showingSave: false }) + } + + _setAllPixels(newPixels) { + let width = newPixels.length + let height = newPixels[0].length + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + let px = getPixel(x, y, newPixels) + let { r, g, b } = rgb(px) + setPixel(this._websocket, x, y, r, g, b) + } + } } render() { return (
- + - + - + this.setState({selectedColor: color})} /> - + onSelectColor={(color) => this.setState({ selectedColor: color })} /> +
- { - this.state.showingLoad - && this._loadDrawing(drawing)} - onClose={() => this.setState({showingLoad: false})}/> - } + { + this.state.showingLoad + && this._loadDrawing(drawing)} + onClose={() => this.setState({ showingLoad: false })} /> + }
- { - this.state.showingSave - && this._saveDrawing(name)} - onClose={() => this.setState({showingSave: false})}/> - } + { + this.state.showingSave + && this._saveDrawing(name)} + onClose={() => this.setState({ showingSave: false })} /> + }
diff --git a/src/PaintArea.js b/src/PaintArea.js index 1ffec83..3b64beb 100644 --- a/src/PaintArea.js +++ b/src/PaintArea.js @@ -1,4 +1,5 @@ import React, { Component } from 'react' +import { rgb, getPixel } from './Utils' /** * Expects props: @@ -44,29 +45,33 @@ export default class PaintArea extends Component { } render() { - let cells = this.props.data.map((row, iy) => { - let rowCells = row.map((cell, ix) => { - let r = cell[0] - let g = cell[1] - let b = cell[2] - return this.handleMouseMove(ix, iy)} - onClick={() => this.props.onTool(ix, iy)} - className="paintareacell" - style={{ - background: `rgb(${r},${g},${b})` - }} - key={(ix * 100000) + iy}/> - }) - return {rowCells} - }) + let data = this.props.data + let height = data[0] ? data[0].length : 0 + let rows = [] + for (var y=height-1; y>=0; y--) { + let cells = [] + for (var x=0; x this.handleMouseMove(ix, iy)} + onClick={() => this.props.onTool(ix, iy)} + className="paintareacell" + style={{ + background: `rgb(${r},${g},${b})` + }} + key={(ix * 100000) + iy}/>) + } + rows.push({cells}) + } return ( - {cells} + {rows}
) diff --git a/src/Utils.js b/src/Utils.js index f7bba5c..7432c49 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -52,11 +52,11 @@ function coordsEqual(item1, item2) { } function getPixel(x, y, pixels) { - let row = pixels[y] - if (!row) { + let column = pixels[x] + if (!column) { return } - return row[x] + return column[y] } function findContiguousPixels(x, y, pixels, targetColor = getPixel(x, y, pixels), contiguousPixels=[[x, y]]) { @@ -90,7 +90,48 @@ function findContiguousPixels(x, y, pixels, targetColor = getPixel(x, y, pixels) return contiguousPixels } - + +function rotatePixelsCounterClock(pixels) { + let rotateClock = (x, y, width, height) => { + return { + newx: -y + (width - 1), + newy : x + } + } + return transformPixels(pixels, rotateClock) +} + +function rotatePixelsClock(pixels) { + let rotateClock = (x, y, width, height) => { + return { + newx: y, + newy : - x + (height - 1) + } + } + return transformPixels(pixels, rotateClock) +} + +function transformPixels(pixels, transform) { + let width = pixels.length + let height = pixels[0].length + let newPixels = [] + for (var x = 0; x < width; x++) { + let column = [] + for (var y = 0; y < height; y++) { + column.push([0, 0, 0]) + } + newPixels.push(column) + } + + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + let px = getPixel(x, y, pixels) + let {newx, newy} = transform(x, y, width, height) + newPixels[newx][newy] = px + } + } + return newPixels +} export { xy, @@ -98,6 +139,8 @@ export { colorEqual, coordsEqual, findContiguousPixels, - getPixel + getPixel, + rotatePixelsClock, + rotatePixelsCounterClock } diff --git a/src/Utils.test.js b/src/Utils.test.js index 6c9161b..33d63f0 100644 --- a/src/Utils.test.js +++ b/src/Utils.test.js @@ -1,4 +1,4 @@ -import { colorEqual, coordsEqual, xy, rgb, findContiguousPixels } from './Utils' +import { colorEqual, coordsEqual, xy, rgb, findContiguousPixels, rotatePixelsClock, rotatePixelsCounterClock } from './Utils' test('test colorEqual function', () => { expect(colorEqual([0, 0, 0], [0, 0, 0])) @@ -78,4 +78,38 @@ test('contiguousPixels', () => { [[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1]] ]).length) .toBe(4) +}) + +test('rotate clockwise function', () => { + let rot1 = rotatePixelsClock([ + [[0, 0, 1], [0, 0, 0], [0, 0, 0], [0, 0, 2]], + [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], + [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], + [[0, 0, 4], [0, 0, 0], [0, 0, 0], [0, 0, 3]] + ]) + expect(rot1[0][3][2]) + .toBe(1) + expect(rot1[3][3][2]) + .toBe(2) + expect(rot1[3][0][2]) + .toBe(3) + expect(rot1[0][0][2]) + .toBe(4) +}) + +test('rotate counter-clockwise function', () => { + let rot1 = rotatePixelsCounterClock([ + [[0, 0, 1], [0, 0, 0], [0, 0, 0], [0, 0, 2]], + [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], + [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], + [[0, 0, 4], [0, 0, 0], [0, 0, 0], [0, 0, 3]] + ]) + expect(rot1[0][3][2]) + .toBe(3) + expect(rot1[3][3][2]) + .toBe(4) + expect(rot1[3][0][2]) + .toBe(1) + expect(rot1[0][0][2]) + .toBe(2) }) \ No newline at end of file -- cgit v1.2.3-ZIG