From fe4d17c7f2f3d62f26b408793269d6c0259b4368 Mon Sep 17 00:00:00 2001
From: Mark van Renswoude
Date: Wed, 30 Dec 2020 23:00:03 +0100
Subject: [PATCH] Added support for multiple boards in a single end grain
result
...and probably loads of little things changed in the process
---
TODO.md | 5 +-
src/App.vue | 31 +-
src/components/CuttingList.vue | 104 +++--
src/components/EdgeGrainPreview.vue | 15 +-
src/components/EndGrainPreview.vue | 250 +++++++++--
src/components/Layers.vue | 281 ++++++++++--
src/components/Settings.vue | 16 +-
src/components/Wood.vue | 19 +-
src/lib/enums.js | 50 +++
src/store.js | 667 ++++++++++++++++++++++------
10 files changed, 1146 insertions(+), 292 deletions(-)
create mode 100644 src/lib/enums.js
diff --git a/TODO.md b/TODO.md
index f71c6dc..403ccc2 100644
--- a/TODO.md
+++ b/TODO.md
@@ -3,6 +3,8 @@ ToDo
Should have
----
+- Highlight strips for the current board, highlight layer for the focused/hovered layer
+- Show list of end grain board numbers and reversed in a "Glue list" for printing
- Support for fractional inches (see, not all europeans look down on freedom units!)
Nice to have
@@ -10,5 +12,6 @@ Nice to have
- Show remaining material
- Theme selection for the preview background
- Render width and height of the boards in the previews (simplified version implemented, moved to Nice to have)
-- More advanced options, like custom direction per strip and mixing multiple edge grain boards with different layers for the end grain board (the code is half prepared for this by having the boards array encapsulating the layers, though it's all hardcoded to board[0] now)
+- Support for mixing multiple boards in the end grain version
- 3D effect for previews emulating thickness / crosscut width
+- Actual wood textures
\ No newline at end of file
diff --git a/src/App.vue b/src/App.vue
index 745fb4c..15d619c 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -27,14 +27,14 @@
Use your browser's built-in print functionality (for example, Ctrl+P on Windows) or click the button below to get a printable version of your board and cutting list.
-
-
-
Edge grain
-
-
-
End grain
+
+
Edge grain
+
+ Board {{ boardIndex + 1}}
+
+
+
+
Cutting list
@@ -153,6 +156,8 @@ export default {
},
computed: {
+ boards() { return this.$store.state.boards; },
+
hash()
{
return bytesToBase64(this.$store.getters.saveMsgPack);
@@ -234,6 +239,12 @@ html, body
height: 100%;
}
+h2
+{
+ color: #808080;
+ font-size: 80%;
+}
+
a
{
color: #99ccff;
@@ -266,7 +277,7 @@ button
padding-top: .3em;
padding-bottom: .3em;
- &:hover
+ &:hover:not([disabled])
{
background-color: #808080;
}
diff --git a/src/components/CuttingList.vue b/src/components/CuttingList.vue
index 0caf06a..4897255 100644
--- a/src/components/CuttingList.vue
+++ b/src/components/CuttingList.vue
@@ -5,27 +5,39 @@
Wood species
Width
-
- {{ index + 1 }}
- {{ getLayerWood(index) }}
- {{ getLayerWidth(index) }}
-
+
+
+ Board {{ boardIndex + 1 }}
+
+
+
+ {{ index + 1 }}
+ {{ getLayerWood(board, index) }}
+ {{ getLayerWidth(board, index) }}
+
+
Bill of materials
Wood species
- Thickness
Length
Width
+ Thickness
-
- {{ stock.woodName }}
- {{ display(settings.boardThickness) }}
- {{ display(settings.boardLength) }}
- {{ display(stock.width) }}
-
+
+
+ Board {{ boardIndex + 1 }}
+
+
+
+ {{ stock.woodName }}
+ {{ display(stock.length) }}
+ {{ display(stock.width) }}
+ {{ display(stock.thickness) }}
+
+
@@ -35,59 +47,66 @@ import { units } from '../lib/units';
export default {
computed: {
settings() { return this.$store.state.settings; },
- layers() { return this.$store.state.boards[0].layers; },
+ boards() { return this.$store.state.boards; },
wood() { return this.$store.state.wood; },
bom()
{
- const woodTally = {};
+ const self = this;
- this.layers.forEach(layer =>
+ return self.boards.map((board, boardIndex) =>
{
- if (woodTally.hasOwnProperty(layer.wood))
- woodTally[layer.wood] += layer.width + this.settings.bladeKerf;
- else
- woodTally[layer.wood] = layer.width;
- });
+ const bom = [];
+ const woodTally = {};
- const bom = [];
-
- for (let wood in woodTally)
- {
- if (!woodTally.hasOwnProperty(wood))
- continue;
-
- bom.push({
- woodName: wood !== null && wood >= 0 && wood < this.wood.length ? this.wood[wood].name : '',
- width: woodTally[wood]
+ board.layers.forEach(layer =>
+ {
+ if (woodTally.hasOwnProperty(layer.wood))
+ woodTally[layer.wood] += layer.width + self.settings.bladeKerf;
+ else
+ woodTally[layer.wood] = layer.width;
});
- }
- return bom;
+ for (let wood in woodTally)
+ {
+ if (!woodTally.hasOwnProperty(wood))
+ continue;
+
+ bom.push({
+ board: boardIndex,
+ woodName: wood >= 0 && wood < self.wood.length ? self.wood[wood].name : '',
+ length: board.length,
+ width: woodTally[wood],
+ thickness: board.thickness
+ });
+ }
+
+ return bom;
+ });
}
},
methods: {
- getLayerWood(index)
+ getLayerWood(board, index)
{
- if (index < 0 || index >= this.layers.length)
+ if (index < 0 || index >= board.layers.length)
return '';
- const woodIndex = this.layers[index].wood;
- if (woodIndex === null || woodIndex < 0 || woodIndex >= this.wood.length)
+ const woodIndex = board.layers[index].wood;
+ if (woodIndex < 0 || woodIndex >= this.wood.length)
return '';
return this.wood[woodIndex].name;
},
- getLayerWidth(index)
+ getLayerWidth(board, index)
{
- if (index < 0 || index >= this.layers.length)
+ if (index < 0 || index >= board.layers.length)
return '';
- return this.display(this.layers[index].width);
+ return this.display(board.layers[index].width);
},
@@ -123,7 +142,12 @@ h2
text-align: right;
}
- tr:nth-child(even) td
+ tr.board td
+ {
+ font-style: italic;
+ }
+
+ tr:nth-child(even):not(.board) td
{
background-color: #555555;
diff --git a/src/components/EdgeGrainPreview.vue b/src/components/EdgeGrainPreview.vue
index 2868250..fbc4991 100644
--- a/src/components/EdgeGrainPreview.vue
+++ b/src/components/EdgeGrainPreview.vue
@@ -1,6 +1,6 @@
-
Dimensions: {{ display(boardWidth) }} x {{ display(boardHeight) }} x {{ display(settings.boardThickness) }}
+
Dimensions: {{ display(boardWidth) }} x {{ display(boardHeight) }} x {{ display(board.thickness) }}
currentValue.width)
- .reduce((accumulator, currentValue) => accumulator + currentValue);
+ .reduce((accumulator, currentValue) => accumulator + currentValue, 0);
},
boardPixelWidth()
@@ -90,7 +91,7 @@ export default {
return 'fill: fuchsia';
const woodIndex = this.layers[index].wood;
- if (woodIndex === null)
+ if (woodIndex < 0 || woodIndex >= this.wood.length)
return '';
const borderStyle = this.settings.borders
diff --git a/src/components/EndGrainPreview.vue b/src/components/EndGrainPreview.vue
index a5a7164..e86c8f5 100644
--- a/src/components/EndGrainPreview.vue
+++ b/src/components/EndGrainPreview.vue
@@ -2,28 +2,41 @@
Dimensions: {{ display(boardWidth) }} x {{ display(boardHeight) }} x {{ display(settings.crosscutWidth) }}
+
Click and drag strips to reorder them. Click once to reverse the direction.
+
+ :viewBox="viewBox"
+ :class="{ dragging: dropTarget !== null }">
-
+
+ :y="getBoardLayerOffset(board, index)"
+ :style="getBoardLayerStyle(board, index)" />
+
+
+
+ :transform="getLayerTransform(index)"
+ @mousedown.prevent="mouseDown(index, $event)" />
+
+
@@ -37,38 +50,39 @@ export default {
},
+ data()
+ {
+ return {
+ dragIndex: null,
+ dropTarget: null
+ };
+ },
+
+
computed: {
settings() { return this.$store.state.settings; },
+ boards() { return this.$store.state.boards; },
wood() { return this.$store.state.wood; },
- layers() { return this.$store.state.boards[0].layers; },
-
- stripsPerBoard()
- {
- const stripAndKerf = this.settings.crosscutWidth + this.settings.bladeKerf;
- if (stripAndKerf === 0)
- return 0;
-
- let stripsPerBoard = (this.settings.boardLength + this.settings.bladeKerf) / stripAndKerf;
-
- // Try to account for rounding errors
- stripsPerBoard = units.limitDecimals(stripsPerBoard, 3);
-
- return Math.floor(stripsPerBoard);
- },
+ endGrain() { return this.$store.state.endGrain },
boardWidth()
{
- return this.stripsPerBoard * this.settings.boardThickness;
+ const self = this;
+
+ return this.endGrain
+ .map(layer => layer.board >= 0 && layer.board < self.boards.length ? self.boards[layer.board].thickness : 0)
+ .reduce((accumulator, currentValue) => accumulator + currentValue, 0);
},
boardHeight()
{
- if (this.layers.length == 0)
- return 0;
-
- return this.layers
- .map(currentValue => currentValue.width)
- .reduce((accumulator, currentValue) => accumulator + currentValue);
+ // Calculate the total width of each board (adding all the layers, inner map/reduce),
+ // then use the maximum value (outer map/reduce)
+ return this.boards
+ .map(board => board.layers
+ .map(layer => layer.width)
+ .reduce((accumulator, currentValue) => accumulator + currentValue, 0))
+ .reduce((accumulator, currentValue) => currentValue > accumulator ? currentValue : accumulator, 0);
},
boardPixelWidth()
@@ -98,26 +112,26 @@ export default {
return units.display(value, this.settings.units);
},
- getLayerOffset(index)
+ getBoardLayerOffset(board, index)
{
- if (index < 0 || index >= this.layers.length)
+ if (index < 0 || index >= board.layers.length)
return 0;
let offset = 0;
for (let i = 0; i < index; i++)
- offset += this.layers[i].width;
+ offset += board.layers[i].width;
return this.toPixels(offset);
},
- getLayerStyle(index)
+ getBoardLayerStyle(board, index)
{
- if (index < 0 || index >= this.layers.length)
+ if (index < 0 || index >= board.layers.length)
return 'fill: fuchsia';
- const woodIndex = this.layers[index].wood;
- if (woodIndex === null)
+ const woodIndex = board.layers[index].wood;
+ if (woodIndex < 0 || woodIndex >= this.wood.length)
return '';
const borderStyle = this.settings.borders
@@ -127,12 +141,151 @@ export default {
return 'fill: ' + this.wood[woodIndex].color + borderStyle;
},
+ getLayerOffset(index)
+ {
+ if (index < 0 || index > this.endGrain.length)
+ return 0;
+
+ let offset = 0;
+
+ for (let i = 0; i < index; i++)
+ {
+ const boardIndex = this.endGrain[i].board;
+ if (boardIndex >= 0 && boardIndex < this.boards.length)
+ offset += this.boards[boardIndex].thickness;
+ }
+
+ return this.toPixels(offset);
+ },
+
getLayerTransform(index)
{
- if (!this.settings.alternateDirection || (index % 2) == 0)
- return '';
+ let reversed = false;
- return 'scale(1, -1) translate(0, -' + this.boardPixelHeight + ')';
+ switch (this.settings.direction)
+ {
+ case 'alternate':
+ reversed = (index % 2) == 0;
+ break;
+
+ case 'custom':
+ reversed = index >= 0 && index < this.endGrain.length && this.endGrain[index].reversed;
+ break;
+ }
+
+ return reversed ? 'scale(1, -1) translate(0, -' + this.boardPixelHeight + ')' : '';
+ },
+
+ reverseLayer(index)
+ {
+ if (this.settings.direction !== 'custom')
+ return;
+
+ if (index < 0 || index >= this.endGrain.length)
+ return;
+
+ this.endGrain[index].reversed = !this.endGrain[index].reversed;
+ },
+
+
+ mouseDown(index, event)
+ {
+ const self = this;
+ const startX = event.pageX;
+ let dragging = false;
+
+ const dragMouseMove = (moveEvent) =>
+ {
+ if (!dragging)
+ {
+ if (Math.abs(moveEvent.pageX - startX) >= 5)
+ {
+ self.dragIndex = index;
+ dragging = true;
+ }
+ }
+
+ if (dragging)
+ self.dropTarget = self.getTargetStrip(moveEvent.pageX);
+ };
+
+ let dragMouseUp;
+ dragMouseUp = () =>
+ {
+ document.removeEventListener('mousemove', dragMouseMove);
+ document.removeEventListener('mouseup', dragMouseUp);
+
+ if (dragging)
+ {
+ if (self.dragIndex !== self.dropTarget)
+ self.$store.commit('moveEndgrain', { from: self.dragIndex, to: self.dropTarget });
+
+ self.dropTarget = null;
+ self.dragIndex = null;
+ }
+ else
+ self.reverseLayer(index);
+ };
+
+ document.addEventListener('mousemove', dragMouseMove);
+ document.addEventListener('mouseup', dragMouseUp);
+ },
+
+
+ getTargetStrip(xPos)
+ {
+ if (this.endGrain.length == 0)
+ return null;
+
+ const firstStrip = this.getPageOffsetRect(this.$refs.strip0);
+ const lastStrip = this.getPageOffsetRect(this.$refs['strip' + (this.endGrain.length - 1)]);
+
+ // On or above the first item
+ if (xPos <= firstStrip.right)
+ return 0;
+
+ // Below the last item
+ if (xPos >= lastStrip.right)
+ return this.endGrain.length;
+
+ // On the last item
+ if (xPos >= lastStrip.left)
+ return this.endGrain.length - 1;
+
+ // Check the previous target first, as it is most likely unchanged due to how
+ // often mouseMove events occur
+ if (this.dropTarget !== null && this.dropTarget > 0 && this.dropTarget < this.endGrain.length - 1)
+ {
+ const currentTarget = this.getPageOffsetRect(this.$refs['strip' + this.dropTarget]);
+ if (xPos >= currentTarget.left && xPos < currentTarget.right)
+ return this.dropTarget;
+ }
+
+ // Just loop through all the strips, there shouldn't be enough to warrant anything more efficient
+ for (let i = 1; i < this.endGrain.length - 1; i++)
+ {
+ const testTarget = this.getPageOffsetRect(this.$refs['strip' + i]);
+ if (xPos >= testTarget.left && xPos < testTarget.right)
+ return i;
+ }
+
+ // This should never occur, so it probably will!
+ return null;
+ },
+
+
+ getPageOffsetRect(element)
+ {
+ const clientRect = element.getBoundingClientRect();
+ const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
+ const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
+
+ return {
+ top: clientRect.top + scrollTop,
+ left: clientRect.left + scrollLeft,
+ right: clientRect.right + scrollLeft,
+ bottom: clientRect.bottom + scrollTop
+ };
}
}
}
@@ -145,8 +298,16 @@ export default {
}
+.draghint
+{
+ margin-bottom: 2em;
+}
+
+
svg
{
+ user-select: none;
+
@media screen
{
box-shadow: 0 0 3em black;
@@ -156,5 +317,10 @@ svg
{
max-width: 100%;
}
+
+ &.dragging
+ {
+ cursor: grabbing;
+ }
}
\ No newline at end of file
diff --git a/src/components/Layers.vue b/src/components/Layers.vue
index b9fa986..6872067 100644
--- a/src/components/Layers.vue
+++ b/src/components/Layers.vue
@@ -1,31 +1,79 @@
-
-
- Add layer
-
-
-
- Tip: click and drag the layer number to move a layer
-
-
-
-
-
-
-
-
-
- {{ index + 1 }}
-
- {{ item.name }}
-
-
-
-
- X
-
-
+
+
<
+
Board {{ boardIndex + 1 }} of {{ boards.length }}
+
Remove
+
Add
+
>
+
+
+
+ Board length
+
+
+ Board thickness
+
+
+
+
+
+ Tip: click and drag the layer number to move a layer
+
+
+
+
+
+
+
+
+ {{ index + 1 }}
+
+ {{ item.name }}
+
+
+
+
+ X
+
+
+
+
+ {{ widthWarning }}
+
+
+
+ Add layer
+
+
+
+
+
Preview settings
+
+ Show borders
+
+
+
+
End grain layer direction
+
+
+ Uniform
+
+
+
+
+ Alternate
+
+
+
+
+
Custom
+
+ Click the strips in the preview to reverse their direction. This may be easier if you turn on the 'Show borders' setting.
+
+
+
+