diff --git a/README.md b/README.md index 2eceb27..551ae1c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,23 @@ -![logo](_media/lineboi-logo.png) +![logo](docs/_media/lineboi-logo.png) -### **lineboi3000 is a way cool graphical interface for drawing, painting, adding EFX, and then sending your designs to a pen plotter or creating animations.** {docsify-ignore} +### **lineboi3000 is a way cool graphical interface for drawing, painting, adding EFX, and then sending your designs to a pen plotter or creating animations.** -BETA - click here for known issues and caveats +![logo](docs/_media/lineboi-main-example.png) -\*\*\* For full documentation, visit the [Docs Page](http://username.github.io/repository) +## Getting Started + +### Install + +`yarn` or `npm i` to install all dependencies + +### Run The App + +`yarn dev` or `npm dev` to run the dev server (devtools open, and Webpack rebuilds on changes to files) + +## Full documentation lives [HERE](http://username.github.io/repository) + +![logo](docs/_media/lineboi-main-example-2.png) + +![logo](docs/_media/gif-mode-example-two.gif) + +![logo](docs/_media/gif-mode-example.gif) diff --git a/docs/_media/gif-mode-example-two.gif b/docs/_media/gif-mode-example-two.gif new file mode 100644 index 0000000..2d43cc2 Binary files /dev/null and b/docs/_media/gif-mode-example-two.gif differ diff --git a/docs/_media/gif-mode-example.gif b/docs/_media/gif-mode-example.gif new file mode 100644 index 0000000..d10238c Binary files /dev/null and b/docs/_media/gif-mode-example.gif differ diff --git a/docs/_media/gif-mode.png b/docs/_media/gif-mode.png new file mode 100644 index 0000000..c1649ba Binary files /dev/null and b/docs/_media/gif-mode.png differ diff --git a/docs/_media/lineboi-demo.gif b/docs/_media/lineboi-demo.gif new file mode 100644 index 0000000..de35895 Binary files /dev/null and b/docs/_media/lineboi-demo.gif differ diff --git a/docs/_media/lineboi-main-example-2.png b/docs/_media/lineboi-main-example-2.png new file mode 100644 index 0000000..a3ee7b2 Binary files /dev/null and b/docs/_media/lineboi-main-example-2.png differ diff --git a/docs/_media/lineboi-main-example.png b/docs/_media/lineboi-main-example.png new file mode 100644 index 0000000..ee6983b Binary files /dev/null and b/docs/_media/lineboi-main-example.png differ diff --git a/docs/_media/main-example.gif b/docs/_media/main-example.gif new file mode 100644 index 0000000..0eb510f Binary files /dev/null and b/docs/_media/main-example.gif differ diff --git a/docs/_media/plot-mode-pen-controls.png b/docs/_media/plot-mode-pen-controls.png new file mode 100644 index 0000000..aeb48c7 Binary files /dev/null and b/docs/_media/plot-mode-pen-controls.png differ diff --git a/docs/_media/plot-mode-pen-options.png b/docs/_media/plot-mode-pen-options.png new file mode 100644 index 0000000..c200456 Binary files /dev/null and b/docs/_media/plot-mode-pen-options.png differ diff --git a/docs/_media/plot-mode-plot-actions.png b/docs/_media/plot-mode-plot-actions.png new file mode 100644 index 0000000..506069e Binary files /dev/null and b/docs/_media/plot-mode-plot-actions.png differ diff --git a/docs/_media/plot-mode-plot-status.png b/docs/_media/plot-mode-plot-status.png new file mode 100644 index 0000000..af41111 Binary files /dev/null and b/docs/_media/plot-mode-plot-status.png differ diff --git a/docs/_media/plot-mode.png b/docs/_media/plot-mode.png new file mode 100644 index 0000000..4df890d Binary files /dev/null and b/docs/_media/plot-mode.png differ diff --git a/docs/getting-started.md b/docs/getting-started.md index 3127079..bdf5c0b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -4,12 +4,16 @@ I haven't set up Electron app packaging yet, so you can run locally via `npm` or ## Quick Start +### Install + `yarn` or `npm i` to install all dependencies -`yarn start` or `npm start` to run the dev server +### Run The App `yarn dev` or `npm dev` to run the dev server (devtools open, and Webpack rebuilds on changes to files) +`yarn start` or `npm start` to run the dev server + ## Useful Yarn/NPM Commands In a terminal window, navigate to the main folder and enter `yarn [command]` or `npm [command]` diff --git a/docs/gif-mode.md b/docs/gif-mode.md index e48cb25..88e969f 100644 --- a/docs/gif-mode.md +++ b/docs/gif-mode.md @@ -1,3 +1,9 @@ -# Plot Mode +# Gif Mode -![efx mode](_media/efx-mode.png ':class=docsImg') +Docs for Gif Mode coming soon + +![gif mode](_media/gif-mode.png ':class=docsImg') + +## Example Output + +![gif mode](_media/gif-mode-example.gif ':class=docsImg') diff --git a/docs/plot-mode.md b/docs/plot-mode.md index e48cb25..9bd82f3 100644 --- a/docs/plot-mode.md +++ b/docs/plot-mode.md @@ -1,3 +1,46 @@ # Plot Mode -![efx mode](_media/efx-mode.png ':class=docsImg') +Once you've added EFX, you can send the result to a pen plotter in `Plot Mode` + +**NOTE**: I've tested all of this locally and it works fine. However, this is all beta, so I can't promise there aren't bugs or issues. I would run small tests with your plotter to make sure it's all good before doing anything serious. + +![plot mode](_media/plot-mode.png ':class=docsImg') + +## Layer Controls + +The same controls in [Plot Mode](/draw-mode#layer-controls) apply to layers in EFX Mode. + +## Plotter Status + +![Plotter Status](_media/plot-mode-plot-status.png ':class=docsImg') + +This shows the current status of the pen plotter. Possible states are: + +- **SEARCHING**: lineboi3000 is checking for the Plotter +- **DISCONNECTED**: No pen plotter was found +- **SIMULATED**: CNCServer is connected but no Plotter was found. Plot actions will be simulated +- **CONNECTED**: A bona fide Pen Plotter was found. Plot actions will be sent to the real plotter + +## Plot Actions + +![Plot Actions](_media/plot-mode-plot-actions.png ':class=docsImg') + +- **PLOT**: Start Plotting all lines on visible layers. + +## Pen Actions + +- **PEN UP/DOWN**: Move the pen up or down to the set height +- **SET HEIGHTS**: Lock in the heights in the below options. +- **PARK PEN**: Set pen state as X:0, Y:O and move +- **RETURN PEN TO START**: Delete Pen status and reset it to default position + +To set heights, move the options below, then click `SET HEIGHTS`, then press `PEN UP` or `PEN DOWN` to check the output. Once the heights are set, the app will use those values when plotting. + +## Plot Options + +![Plot Options](_media/plot-mode-pen-options.png ':class=docsImg') + +- **OPTIMIZE LINE ORDER**: Order Plot lines to reduce the amount of pen travel, by finding the nearest next line to plot after finishing a given line. As a note I've seen slightly "off" results with this that might have to do with the physical difference between the sides of the pen tip,. +- **PEN UP HEIGHT**: Height when the pen is in Up state. 0 is highest. +- **PEN DOWN HEIGHT**: Height when the pen is in Down state. 1 is lowest. +- **SCALE**: Scale of the plotted lines in percent diff --git a/src/components/AppContainer.js b/src/components/AppContainer.js index 3dd06d2..c7935f0 100644 --- a/src/components/AppContainer.js +++ b/src/components/AppContainer.js @@ -9,6 +9,7 @@ import { setGifmakerLoading } from 'store/gifmaker/gifmakerActions'; import DrawingContent from './Drawing/DrawingContent/DrawingContent'; import DrawingSidebar from './Drawing/DrawingSidebar/DrawingSidebar'; import PlotContent from './PlotContent/PlotContent'; +import PlotHeader from './PlotHeader/PlotHeader'; import GifmakerContent from './GifmakerContent/GifmakerContent'; import GifmakerSidebar from './GifmakerContent/GifmakerSidebar'; import PlotSidebar from './PlotSidebar/PlotSidebar'; @@ -73,9 +74,10 @@ class AppContainer extends React.Component { const { mode } = this.props; const showGrid = ['efx', 'draw'].includes(mode); const showLayerHeader = ['draw', 'efx'].includes(mode); + const showPlotHeader = mode === 'plot'; const showGifHeader = mode === 'gifmaker'; const headerRightClass = - mode === 'plot' || mode === 'options' + mode === 'options' ? styles.headerRightDisabled : styles.headerRight; @@ -85,6 +87,7 @@ class AppContainer extends React.Component {
lineboi3000
{showLayerHeader && } + {showPlotHeader && } {showGifHeader && }
diff --git a/src/components/Drawing/DrawingContent/DrawingContent.js b/src/components/Drawing/DrawingContent/DrawingContent.js index a4a143d..683446c 100644 --- a/src/components/Drawing/DrawingContent/DrawingContent.js +++ b/src/components/Drawing/DrawingContent/DrawingContent.js @@ -283,6 +283,10 @@ export class DrawingContent extends React.Component { fillRadius, fillCircle } = this.props; + if (!tempLinesRef || !tempLinesRef.current) { + return; + } + const context = tempLinesRef.current.getContext('bitmaprenderer'); const offScreenCanvas = new OffscreenCanvas(width, height); const offScreenContext = offScreenCanvas.getContext('2d'); diff --git a/src/components/PlotContent/PlotContent.js b/src/components/PlotContent/PlotContent.js index 51506be..0d8483e 100644 --- a/src/components/PlotContent/PlotContent.js +++ b/src/components/PlotContent/PlotContent.js @@ -2,7 +2,10 @@ import React from 'react'; import { connect } from 'react-redux'; import { getAllEfxLines } from 'store/line/lineSelectors'; import { getCurrentOptions } from 'store/options/optionsSelectors'; -import { CanvasLayer } from 'components/common/SvgLayer/SvgLayer'; +import { + CanvasLayer, + PenPositionLayer +} from 'components/common/SvgLayer/SvgLayer'; import { formatLayersForPlotDisplay, generatePlotBoundaries @@ -13,6 +16,8 @@ import styles from './PlotContent.styles.css'; // to do make configurable const PIXELS_PER_INCH = 75; +const AXIDRAW_WIDTH_IN_STEPS = 12000; +const AXIDRAW_HEIGHT_IN_STEPS = 8720; class PlotContent extends React.Component { componentDidMount() {} @@ -24,6 +29,7 @@ class PlotContent extends React.Component { height: paperHeightInPixels, scale }); + return ( -
+ {plotting && ( + + )} {isPlotBoundaryVisible && this.renderPlotBoundary()} {formattedLayers.map((layer) => { return ( { scale, isPlotBoundaryVisible, penLocation, - currentLineId + currentLineId, + currentPlotPercentage, + plotting } = state.plotReducer; const { height, width } = getCurrentOptions(state); @@ -105,13 +128,15 @@ const mapStateToProps = (state) => { penX, penY, currentLineId, + currentPlotPercentage, paperWidthInPixels, paperHeightInPixels, scale, paperWidth, paperHeight, formattedLayers, - isPlotBoundaryVisible + isPlotBoundaryVisible, + plotting }; }; diff --git a/src/components/PlotHeader/PlotHeader.js b/src/components/PlotHeader/PlotHeader.js new file mode 100644 index 0000000..508642e --- /dev/null +++ b/src/components/PlotHeader/PlotHeader.js @@ -0,0 +1,65 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import styles from './PlotHeader.styles.css'; + +export const PlotHeader = ({ + currentPlotPercentage, + totalPlotLineCount, + currentPlotLineIndex, + plotting +}) => { + const plotComplete = currentPlotPercentage === 100; + const textString = plotting ? 'PLOTTING' : 'NOT PLOTTING'; + const plottingClass = plotting + ? styles.currentlyPlotting + : styles.notCurrentlyPlotting; + + return ( +
+
+
+ {plotComplete ? 'DONE' : textString} +
+
+
lines done
+
+ {currentPlotLineIndex} +
+
+
+
total lines
+
+ {totalPlotLineCount} +
+
+
+
percent done
+
+ {Math.round(currentPlotPercentage)}% +
+
+
+
+
+
+
+ ); +}; + +const mapStateToProps = (state) => { + return { + ...state.plotReducer + }; +}; + +export default connect(mapStateToProps)(PlotHeader); diff --git a/src/components/PlotHeader/PlotHeader.styles.css b/src/components/PlotHeader/PlotHeader.styles.css new file mode 100644 index 0000000..72af5f3 --- /dev/null +++ b/src/components/PlotHeader/PlotHeader.styles.css @@ -0,0 +1,109 @@ +@value colors: "~components/common/Colors/colorVariables.css"; +@value color-teal-light from colors; + +.plotHeaderContainer { + display: grid; + grid-template-rows: 1fr 1fr; + height: 100%; + overflow: hidden; + position: relative; + text-align: center; + width: 100%; +} + +.plotMessage { + display: grid; + grid-column-gap: 3px; + grid-row: 1; + grid-template-columns: 1fr 1fr 1fr 1fr; + height: 50px; + left: 0; + outline: 3px solid black; + position: relative; + top: 0; + width: 100%; + z-index: 30; +} + +.plotInfo { + display: grid; + grid-template-rows: 20px 30px; + outline: 3px solid black; +} + +.plotInfoTitle { + align-items: center; + display: flex; + grid-row: 1; + justify-content: center; + outline: 3px solid black; +} + +.plotInfoBody { + align-items: center; + display: flex; + grid-row: 2; + justify-content: center; +} + +.currentlyPlotting { + align-items: center; + background-color: lightgreen; + display: flex; + grid-column: span 1; + justify-content: center; + outline: 3px solid black; +} + +.notCurrentlyPlotting { + align-items: center; + background-color: lightyellow; + display: flex; + grid-column: span 1; + justify-content: center; + outline: 3px solid black; +} + +.finishedPlot { + background-color: lightgreen; + height: 100%; + width: 100%; +} + +.loadingContainer { + grid-row: 2; + height: 100%; + overflow: hidden; + position: absolute; +} + +.loadingBackground { + animation-duration: 0.6s; + animation-iteration-count: infinite; + animation-name: MOVE-BG; + animation-timing-function: linear; + + background: repeating-linear-gradient( + 135deg, + transparent, + transparent 10px, + rgba(205, 205, 205, 1) 10px, + rgb(36, 167, 153) 16px + ); + + bottom: 0; + left: -46px; + position: absolute; + right: 0; + top: 0; + z-index: -1; +} + +@keyframes MOVE-BG { + from { + -webkit-transform: translateX(0); + } + to { + -webkit-transform: translateX(46px); + } +} diff --git a/src/components/PlotSidebar/PlotSidebar.js b/src/components/PlotSidebar/PlotSidebar.js index 044d8ee..e9dd938 100644 --- a/src/components/PlotSidebar/PlotSidebar.js +++ b/src/components/PlotSidebar/PlotSidebar.js @@ -9,9 +9,10 @@ import { EnabledToggleButton } from 'components/common/SidebarButton/SidebarButt import { setPlotSettingByKey, - togglePlotBoundary, + // togglePlotBoundary, setPenLocation, - setCurrentLineId + setCurrentLineId, + setCurrentPlotPercentage } from 'store/plot/plotActions'; import { SidebarContainer, @@ -28,14 +29,13 @@ import id from '../../utils/id'; import styles from './PlotSidebar.styles.css'; const INIT_STATE = { - isConnected: null, + connectionStatus: 'searching', penState: null }; class PlotSidebar extends React.Component { constructor(props) { super(props); - this.state = INIT_STATE; } @@ -48,8 +48,15 @@ class PlotSidebar extends React.Component { } setupAxidraw = async () => { + const { dispatch } = this.props; this.plotter = new Plotter(); await this.plotter.createAxidraw(); + + this.plotter.setResultCallback((result) => { + const { x, y } = result; + dispatch(setPenLocation([x, y])); + }); + this.setPenHeights(); this.setPlotterCheckInterval(); }; @@ -61,29 +68,77 @@ class PlotSidebar extends React.Component { }; checkPlotterConnection = async () => { - const { dispatch } = this.props; - try { const result = await this.plotter.getPlotterStatus(); - const penX = result.x; - const penY = result.y; - dispatch(setPenLocation([penX, penY])); - const { simulation, state } = result; - const isConnected = simulation != null && simulation === 0; + if (result.connectionStatus === 'disconnected') { + this.setState({ + connectionStatus: 'disconnected' + }); + return; + } + + const { connectionStatus, state } = result; this.setState({ - isConnected, + connectionStatus, penState: state }); } catch { this.setState({ - isConnected: false + connectionStatus: 'off' }); } }; + setPlottingStatus = (value) => { + const { dispatch } = this.props; + dispatch(setPlotSettingByKey('plotting', value)); + }; + + setTotalLineCount = (count) => { + const { dispatch } = this.props; + dispatch(setPlotSettingByKey('totalPlotLineCount', count)); + }; + + setCurrentLineIndex = (index) => { + const { dispatch } = this.props; + dispatch(setPlotSettingByKey('currentPlotLineIndex', index)); + }; + + startPlot = async (lines) => { + const { dispatch } = this.props; + this.setTotalLineCount(lines.length); + this.setCurrentLineIndex(0); + dispatch(setCurrentPlotPercentage(0)); + + const lineDrawCallback = (response) => { + const { + currentLineId, + currentLineCount, + totalLineCount + } = response; + + dispatch(setCurrentLineId(currentLineId)); + this.setCurrentLineIndex(currentLineCount); + + const finishedPercentage = + (currentLineCount / totalLineCount) * 100; + + dispatch(setCurrentPlotPercentage(finishedPercentage)); + }; + + this.setPlottingStatus(true); + this.plotter.setLines(lines); + await this.plotter.print((response) => { + lineDrawCallback(response); + }); + this.setPlottingStatus(false); + }; + plotFullBounds = () => { + const { paperHeightInPixels, paperWidthInPixels } = this.props; + const fullBounds = [ [0, 0], [100, 0], @@ -91,13 +146,44 @@ class PlotSidebar extends React.Component { [0, 100], [0, 0] ]; - - const boundsAsLine = { + const plotBoundsLine = { id: id(), pointArrayContainer: fullBounds }; - this.plotter.setLines([boundsAsLine]); - this.plotter.print(); + + const withCoords = addPercentageCoordinatesToLine( + plotBoundsLine, + paperWidthInPixels, + paperHeightInPixels + ); + this.startPlot([withCoords]); + }; + + plotTestLines = () => { + const { paperHeightInPixels, paperWidthInPixels } = this.props; + + const lines = []; + for (let i = 0; i < 10; i += 1) { + const points = []; + for (let p = 0; p < 5; p += 1) { + const randoX = _.random(300, 350); + const randoY = _.random(250, 300); + points.push([randoX, randoY]); + } + const formattedLine = { + id: id(), + pointArrayContainer: points + }; + + const withPercentage = addPercentageCoordinatesToLine( + formattedLine, + paperWidthInPixels, + paperHeightInPixels + ); + lines.push(withPercentage); + } + + this.startPlot(lines); }; plotPlotBoundary = () => { @@ -108,13 +194,13 @@ class PlotSidebar extends React.Component { width: paperWidthInPixels, scale }); + const withCoords = addPercentageCoordinatesToLine( plotBoundaryLine, paperWidthInPixels, paperHeightInPixels ); - this.plotter.setLines([withCoords]); - this.plotter.print(); + this.startPlot([withCoords]); }; plotCoords = () => { @@ -122,8 +208,7 @@ class PlotSidebar extends React.Component { formattedLayers, paperHeightInPixels, paperWidthInPixels, - optimizeLineOrder, - dispatch + optimizeLineOrder } = this.props; this.parkPen(); @@ -141,49 +226,11 @@ class PlotSidebar extends React.Component { ) ); - // to do make linecallback work - this.plotter.setLines(mappedRelativeLines); - this.plotter.print((currentLineId) => { - dispatch(setCurrentLineId(currentLineId)); - }); + this.startPlot(mappedRelativeLines); }; - // testPlotCoords = () => { - // const { - // formattedLayers, - // paperHeightInPixels, - // paperWidthInPixels - // } = this.props; - - // const justLines = _.flatten(formattedLayers.map((x) => x.lines)); - // const originalLines = _.flatten(formattedLayers.map((x) => x.lines)); - // const sortedLines = sortLinesForPlotter(justLines); - - // const mappedSorted = sortedLines.map((line) => - // addPercentageCoordinatesToLine( - // line, - // paperWidthInPixels, - // paperHeightInPixels - // ) - // ); - - // const mappedOriginal = originalLines.map((line) => - // addPercentageCoordinatesToLine( - // line, - // paperWidthInPixels, - // paperHeightInPixels - // ) - // ); - - // const flatOriginalPercent = _.flatten( - // mappedOriginal.map((x) => x.percentageCoordinates) - // ); - // const flatSorted = _.flatten( - // mappedSorted.map((x) => x.percentageCoordinates) - // ); - // }; - abort = () => { + this.setPlottingStatus(false); this.plotter.abort(); }; @@ -213,33 +260,20 @@ class PlotSidebar extends React.Component { }; connectionStatusDisplay = () => { - const { isConnected } = this.state; - - if (isConnected == null) { - return ( -
- SEARCHING... -
- ); - } + const { connectionStatus } = this.state; - if (isConnected === true) { - return ( -
- CONNECTED -
- ); - } + const statuses = { + searching: ['SEARCHING', styles.searching], + connected: ['CONNECTED', styles.connected], + simulated: ['SIMULATED', styles.simulation], + disconnected: ['DISCONNECTED', styles.disconnected] + }; + + const [statusString, statusClass] = statuses[connectionStatus]; return ( -
- DISCONNECTED +
+ {statusString}
); }; @@ -248,7 +282,7 @@ class PlotSidebar extends React.Component { const { penState } = this.state; let penStateString; if (penState == null) { - penStateString = 'hi'; + penStateString = '0'; } else { penStateString = penState.toString(); } @@ -271,6 +305,7 @@ class PlotSidebar extends React.Component { penUpHeight, penDownHeight, scale, + plotting, optimizeLineOrder, dispatch } = this.props; @@ -281,60 +316,34 @@ class PlotSidebar extends React.Component { {this.connectionStatusDisplay()} {this.penHeightStatusDisplay()} - - { - dispatch( - setPlotSettingByKey( - 'optimizeLineOrder', - !optimizeLineOrder - ) - ); - }} - active={optimizeLineOrder} - labelActive="optimize line order on" - labelInactive="optimize line order off" - /> - - - - - + + + {!plotting ? ( + + ) : ( + + )} - + {/* + // testing utils - don't show for now - - - - + + */} + + + + + + + { + dispatch( + setPlotSettingByKey( + 'optimizeLineOrder', + !optimizeLineOrder + ) + ); + }} + active={optimizeLineOrder} + labelActive="optimize line order on" + labelInactive="optimize line order off" + /> { dispatch( diff --git a/src/components/PlotSidebar/PlotSidebar.styles.css b/src/components/PlotSidebar/PlotSidebar.styles.css index a462763..999e152 100644 --- a/src/components/PlotSidebar/PlotSidebar.styles.css +++ b/src/components/PlotSidebar/PlotSidebar.styles.css @@ -34,7 +34,17 @@ button { text-align: center; } -.checking { +.searching { + background: repeating-linear-gradient( + 135deg, + transparent, + transparent 10px, + rgba(205, 205, 205, 1) 10px, + rgba(205, 205, 205, 1) 16px + ); +} + +.simulation { background-color: lightyellow; } diff --git a/src/components/common/SvgLayer/SvgLayer.js b/src/components/common/SvgLayer/SvgLayer.js index f7fbe72..727c5cf 100644 --- a/src/components/common/SvgLayer/SvgLayer.js +++ b/src/components/common/SvgLayer/SvgLayer.js @@ -76,6 +76,55 @@ export const CanvasLayer = ({ ); }; +export const PenPositionLayer = ({ + penX = 0, + penY = 0, + width = 800, + height = 600, + position = 'absolute' +}) => { + const pixelRatio = window.devicePixelRatio; + + const canvas = useRef(null); + + useEffect(() => { + try { + const context = canvas.current.getContext('bitmaprenderer'); + const offScreenCanvas = new OffscreenCanvas(width, height); + const offScreenContext = offScreenCanvas.getContext('2d'); + + offScreenContext.save(); + offScreenContext.clearRect(0, 0, width, height); + offScreenContext.strokeStyle = 'black'; + offScreenContext.fillStyle = 'red'; + + offScreenContext.fillRect(penX - 10, penY - 10, 20, 20); + offScreenContext.strokeRect(penX - 10, penY - 10, 20, 20); + + offScreenContext.restore(); + const offscreenBitmap = offScreenCanvas.transferToImageBitmap(); + context.transferFromImageBitmap(offscreenBitmap); + } catch (e) { + console.error('***** line error *****'); + console.error('***** line error *****'); + } + }, [penX, penY]); + + const dw = Math.floor(pixelRatio * width); + const dh = Math.floor(pixelRatio * height); + const style = { width, height, position, zIndex: 50 }; + + return ( + + ); +}; + export const CombinedLayer = ({ layers, width = 800, diff --git a/src/plotting/AxidrawAPI.js b/src/plotting/AxidrawAPI.js index 1086185..e5c0acd 100644 --- a/src/plotting/AxidrawAPI.js +++ b/src/plotting/AxidrawAPI.js @@ -9,19 +9,35 @@ const headers = { 'cache-control': 'no-cache' }; +const PLOTTER_CONNECTION_STATES = { + 0: 'connected', + 1: 'simulated', + 2: 'disconnected' +}; + class AxidrawAPI { constructor() { this.penUpHeight = 0.5; this.penDownHeight = 0.7; + this.resultCallback = () => {}; } async getStatus() { - const result = await fetch(`${API_URL}/pen/`, { - method: 'GET', - headers - }); - const json = await result.json(); - return json; + try { + const result = await fetch(`${API_URL}/pen/`, { + method: 'GET', + headers + }); + const json = await result.json(); + return { + ...json, + connectionStatus: PLOTTER_CONNECTION_STATES[json.simulation] + }; + } catch { + return { + connectionStatus: PLOTTER_CONNECTION_STATES[2] + }; + } } async getBuffer() { @@ -36,17 +52,17 @@ class AxidrawAPI { async drawPath(path) { const { penUpHeight = 0, penDownHeight = 1 } = this; await this.setPenState(`state=${penUpHeight}`); - for (let i = 0; i < path.length; i += 1) { const [x, y] = path[i]; - await this.setPenState(`x=${x}&y=${y}`); + const result = await this.setPenState(`x=${x}&y=${y}`); + + this.resultCallback(result); if (i === 0) { await this.setPenState(`state=${penDownHeight}`); } } await this.setPenState(`state=${penUpHeight}`); - await this.timeout(100); return Promise.resolve(); } @@ -55,13 +71,15 @@ class AxidrawAPI { } async setPenState(state) { - await fetch(`${API_URL}/pen`, { + const result = await fetch(`${API_URL}/pen`, { method: 'PUT', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: state }); + const json = result.json(); + return Promise.resolve(json); } async resetMotor() { diff --git a/src/plotting/Plotter.js b/src/plotting/Plotter.js index f2beb28..2f1c8ef 100644 --- a/src/plotting/Plotter.js +++ b/src/plotting/Plotter.js @@ -10,6 +10,10 @@ export default class Plotter { this.axidraw = await axidrawAPI(); } + setResultCallback(resultCallback) { + this.axidraw.resultCallback = resultCallback; + } + async getPlotterStatus() { const result = await this.axidraw.getStatus(); return result; @@ -64,8 +68,12 @@ export default class Plotter { return; } const line = this.lines[i]; - lineCallback(line.id); await this.axidraw.drawPath(line.percentageCoordinates); + lineCallback({ + currentLineId: line.id, + currentLineCount: i + 1, + totalLineCount: this.lines.length + }); } await this.axidraw.parkPen(); diff --git a/src/store/plot/plotActions.js b/src/store/plot/plotActions.js index c005c2b..b28a0c9 100644 --- a/src/store/plot/plotActions.js +++ b/src/store/plot/plotActions.js @@ -2,6 +2,7 @@ export const SET_PLOT_SETTING_BY_KEY = 'SET_PLOT_SETTING_BY_KEY'; export const TOGGLE_PLOT_BOUNDARY = 'TOGGLE_PLOT_BOUNDARY'; export const SET_PEN_LOCATION = 'SET_PEN_LOCATION'; export const SET_CURRENT_LINE_ID = 'SET_CURRENT_LINE_ID'; +export const SET_CURRENT_PLOT_PERCENTAGE = 'SET_CURRENT_PLOT_PERCENTAGE'; // action creators @@ -26,6 +27,13 @@ export const setCurrentLineId = (lineId) => { }; }; +export const setCurrentPlotPercentage = (percentage) => { + return { + type: SET_CURRENT_PLOT_PERCENTAGE, + value: percentage + }; +}; + export const togglePlotBoundary = () => { return { type: TOGGLE_PLOT_BOUNDARY diff --git a/src/store/plot/plotReducer.js b/src/store/plot/plotReducer.js index 651309c..ace286c 100644 --- a/src/store/plot/plotReducer.js +++ b/src/store/plot/plotReducer.js @@ -2,7 +2,8 @@ import { SET_CURRENT_LINE_ID, SET_PLOT_SETTING_BY_KEY, TOGGLE_PLOT_BOUNDARY, - SET_PEN_LOCATION + SET_PEN_LOCATION, + SET_CURRENT_PLOT_PERCENTAGE } from './plotActions'; const initState = { @@ -14,8 +15,12 @@ const initState = { scale: 50, center: true, isPlotBoundaryVisible: false, + plotting: false, penLocation: [0, 0], - currentLineId: null + currentLineId: null, + currentPlotPercentage: null, + totalPlotLineCount: null, + currentPlotLineIndex: null }; const plotReducer = (state = initState, action) => { @@ -40,6 +45,11 @@ const plotReducer = (state = initState, action) => { ...state, currentLineId: action.value }; + case SET_CURRENT_PLOT_PERCENTAGE: + return { + ...state, + currentPlotPercentage: action.value + }; default: return state; } diff --git a/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-1-snap.png b/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-1-snap.png index e1763bf..34ab10b 100644 Binary files a/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-1-snap.png and b/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-1-snap.png differ diff --git a/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-2-snap.png b/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-2-snap.png index bb7d169..f0b50b1 100644 Binary files a/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-2-snap.png and b/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-2-snap.png differ diff --git a/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-3-snap.png b/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-3-snap.png index cef3161..0bce556 100644 Binary files a/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-3-snap.png and b/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-3-snap.png differ diff --git a/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-4-snap.png b/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-4-snap.png index 2fc7e76..7c59aab 100644 Binary files a/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-4-snap.png and b/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-4-snap.png differ diff --git a/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-5-snap.png b/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-5-snap.png index 0b1836c..354d996 100644 Binary files a/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-5-snap.png and b/tests/screenshots/spectron-test-js-app-tests-opens-the-main-window-and-clicks-around-5-snap.png differ