Compare commits
11 Commits
ac6020cb8e
...
9f75631d58
Author | SHA1 | Date | |
---|---|---|---|
9f75631d58 | |||
bdb4db100e | |||
e68ebface7 | |||
27e7a11eba | |||
ee5cd5dea5 | |||
55f21bd18e | |||
be5cb2cb12 | |||
d3b9142e4f | |||
21ee690409 | |||
0a56844784 | |||
ee6584a396 |
@ -6,7 +6,6 @@ from api import uploads
|
|||||||
default_pattern = {
|
default_pattern = {
|
||||||
'warp': {
|
'warp': {
|
||||||
'shafts': 8,
|
'shafts': 8,
|
||||||
'threads': 100,
|
|
||||||
'threading': [{'shaft': 0}] * 100,
|
'threading': [{'shaft': 0}] * 100,
|
||||||
'defaultColour': '178,53,111',
|
'defaultColour': '178,53,111',
|
||||||
'defaultSpacing': 1,
|
'defaultSpacing': 1,
|
||||||
@ -14,7 +13,6 @@ default_pattern = {
|
|||||||
},
|
},
|
||||||
'weft': {
|
'weft': {
|
||||||
'treadles': 8,
|
'treadles': 8,
|
||||||
'threads': 50,
|
|
||||||
'treadling': [{'treadle': 0}] * 50,
|
'treadling': [{'treadle': 0}] * 50,
|
||||||
'defaultColour': '53,69,178',
|
'defaultColour': '53,69,178',
|
||||||
'defaultSpacing': 1,
|
'defaultSpacing': 1,
|
||||||
|
@ -62,7 +62,7 @@ def dumps(obj):
|
|||||||
wif.append('\n[WARP]')
|
wif.append('\n[WARP]')
|
||||||
wif.append('Units=centimeters')
|
wif.append('Units=centimeters')
|
||||||
wif.append('Color={0}'.format(get_colour_index(obj['pattern']['colours'], obj['pattern']['warp']['defaultColour'])))
|
wif.append('Color={0}'.format(get_colour_index(obj['pattern']['colours'], obj['pattern']['warp']['defaultColour'])))
|
||||||
wif.append('Threads={0}'.format(obj['pattern']['warp']['threads']))
|
wif.append('Threads={0}'.format(len(obj['pattern']['warp']['threading'])))
|
||||||
wif.append('Spacing=0.212')
|
wif.append('Spacing=0.212')
|
||||||
wif.append('Thickness=0.212')
|
wif.append('Thickness=0.212')
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ def dumps(obj):
|
|||||||
wif.append('\n[WEFT]')
|
wif.append('\n[WEFT]')
|
||||||
wif.append('Units=centimeters')
|
wif.append('Units=centimeters')
|
||||||
wif.append('Color={0}'.format(get_colour_index(obj['pattern']['colours'], obj['pattern']['weft']['defaultColour'])))
|
wif.append('Color={0}'.format(get_colour_index(obj['pattern']['colours'], obj['pattern']['weft']['defaultColour'])))
|
||||||
wif.append('Threads={0}'.format(obj['pattern']['weft']['threads']))
|
wif.append('Threads={0}'.format(len(obj['pattern']['weft']['treadling'])))
|
||||||
wif.append('Spacing=0.212')
|
wif.append('Spacing=0.212')
|
||||||
wif.append('Thickness=0.212')
|
wif.append('Thickness=0.212')
|
||||||
|
|
||||||
@ -151,7 +151,6 @@ def loads(wif_file):
|
|||||||
while int(x) >= len(draft['warp']['threading']) - 1:
|
while int(x) >= len(draft['warp']['threading']) - 1:
|
||||||
draft['warp']['threading'].append({'shaft': 0})
|
draft['warp']['threading'].append({'shaft': 0})
|
||||||
draft['warp']['threading'][int(x) - 1] = {'shaft': shaft}
|
draft['warp']['threading'][int(x) - 1] = {'shaft': shaft}
|
||||||
draft['warp']['threads'] = len(draft['warp']['threading'])
|
|
||||||
try:
|
try:
|
||||||
warp_colours = config['warp colors']
|
warp_colours = config['warp colors']
|
||||||
for x in warp_colours:
|
for x in warp_colours:
|
||||||
@ -180,7 +179,6 @@ def loads(wif_file):
|
|||||||
while int(x) >= len(draft['weft']['treadling']) - 1:
|
while int(x) >= len(draft['weft']['treadling']) - 1:
|
||||||
draft['weft']['treadling'].append({'treadle': 0})
|
draft['weft']['treadling'].append({'treadle': 0})
|
||||||
draft['weft']['treadling'][int(x) - 1] = {'treadle': shaft}
|
draft['weft']['treadling'][int(x) - 1] = {'treadle': shaft}
|
||||||
draft['weft']['threads'] = len(draft['weft']['treadling'])
|
|
||||||
try:
|
try:
|
||||||
weft_colours = config['weft colors']
|
weft_colours = config['weft colors']
|
||||||
for x in weft_colours:
|
for x in weft_colours:
|
||||||
|
BIN
web/public/images/docs/editing19.png
Normal file
BIN
web/public/images/docs/editing19.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 302 KiB |
BIN
web/public/images/docs/editing20.png
Normal file
BIN
web/public/images/docs/editing20.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 274 KiB |
@ -91,6 +91,26 @@ To begin, select the **colour** tool, click a colour from the **palette**, and t
|
|||||||
|
|
||||||
As you paint, you'll notice the drawdown update to show your new colours.
|
As you paint, you'll notice the drawdown update to show your new colours.
|
||||||
|
|
||||||
|
### Selecting and deleting threads
|
||||||
|
|
||||||
|
You can delete threads from the warp and weft.
|
||||||
|
|
||||||
|
First, enable the **select** tool from the toolbox. Once selected, you can click on individual threads, or click and drag along the warp or weft, in order to select threads.
|
||||||
|
|
||||||
|
Once you have threads selected, a button will display to allow you to delete the selected threads.
|
||||||
|
|
||||||
|
![Deleting threads](/images/docs/editing19.png)
|
||||||
|
|
||||||
|
### Inserting threads
|
||||||
|
|
||||||
|
Threads can be inserted at any point along the warp or weft.
|
||||||
|
|
||||||
|
To do so, select the **insert** tool from the toolbox and then click on the thread that you'd like to shift leftwards along the warp or downwards along the weft.
|
||||||
|
|
||||||
|
You'll be asked how many threads you'd like to insert before completing the process.
|
||||||
|
|
||||||
|
![Inserting threads](/images/docs/editing20.png)
|
||||||
|
|
||||||
### Configuring your pattern
|
### Configuring your pattern
|
||||||
|
|
||||||
You can rename and adjust the number of **treadles** and **shafts** your pattern uses in the **properties** menu. This information can be changed at any time.
|
You can rename and adjust the number of **treadles** and **shafts** your pattern uses in the **properties** menu. This information can be changed at any time.
|
||||||
|
@ -8,8 +8,6 @@ const StyledDrawdown = styled.canvas`
|
|||||||
border:1px dashed rgb(70,70,70);
|
border:1px dashed rgb(70,70,70);
|
||||||
top: ${props => (props.warp.shafts * props.baseSize) + 20}px;
|
top: ${props => (props.warp.shafts * props.baseSize) + 20}px;
|
||||||
right: ${props => (props.weft.treadles * props.baseSize) + 20}px;
|
right: ${props => (props.weft.treadles * props.baseSize) + 20}px;
|
||||||
height: ${props => props.weft.threads * props.baseSize}px;
|
|
||||||
width: ${props => props.warp.threads * props.baseSize}px;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Cache
|
// Cache
|
||||||
@ -86,10 +84,12 @@ function Drawdown({ baseSize, warp, weft, tieups }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const warpThreads = warp.threading?.length || 0;
|
||||||
|
const weftThreads = weft.treadling?.length || 0;
|
||||||
return (
|
return (
|
||||||
<StyledDrawdown ref={drawdownRef} className="drawdown joyride-drawdown"
|
<StyledDrawdown ref={drawdownRef} className="drawdown joyride-drawdown"
|
||||||
width={warp.threads * baseSize}
|
width={warpThreads * baseSize}
|
||||||
height={weft.threads * baseSize}
|
height={weftThreads * baseSize}
|
||||||
weft={weft} warp={warp} baseSize={baseSize}
|
weft={weft} warp={warp} baseSize={baseSize}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -40,7 +40,7 @@ function Tieups({ cellStyle, warp, weft, tieups, updatePattern, baseSize }) {
|
|||||||
|
|
||||||
const paintTieups = () => {
|
const paintTieups = () => {
|
||||||
const canvas = tieupRef.current;
|
const canvas = tieupRef.current;
|
||||||
const ctx = canvas.getContext('2d');// , { alpha: false });
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Confirm, Select, Segment, Accordion, Grid, Icon, Input, Button, Popup
|
Confirm, Header, Select, Segment, Accordion, Grid, Icon, Input, Button, Popup
|
||||||
} from 'semantic-ui-react';
|
} from 'semantic-ui-react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
@ -40,6 +40,7 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
|
|||||||
const [activeDrawers, setActiveDrawers] = useState(['properties', 'drawing', 'palette']);
|
const [activeDrawers, setActiveDrawers] = useState(['properties', 'drawing', 'palette']);
|
||||||
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
||||||
const [newColour, setNewColour] = useState('#22194D');
|
const [newColour, setNewColour] = useState('#22194D');
|
||||||
|
const [selectedThreadCount, setSelectedThreadCount] = useState(0);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { objectId, username, projectPath } = useParams();
|
const { objectId, username, projectPath } = useParams();
|
||||||
@ -52,6 +53,15 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
|
|||||||
return { project, editor: state.objects.editor };
|
return { project, editor: state.objects.editor };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!pattern) return;
|
||||||
|
const { warp, weft } = pattern;
|
||||||
|
let selected = 0;
|
||||||
|
selected += warp?.threading?.filter(t => t.isSelected)?.length;
|
||||||
|
selected += weft?.treadling?.filter(t => t.isSelected)?.length;
|
||||||
|
setSelectedThreadCount(selected);
|
||||||
|
}, [pattern]);
|
||||||
|
|
||||||
const enableTool = (tool) => {
|
const enableTool = (tool) => {
|
||||||
dispatch(actions.objects.updateEditor({ tool, colour: editor.colour }));
|
dispatch(actions.objects.updateEditor({ tool, colour: editor.colour }));
|
||||||
};
|
};
|
||||||
@ -76,6 +86,27 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
|
|||||||
updatePattern({ weft: { ...weft, treadles: parseInt(event.target.value, 10) || 1 } });
|
updatePattern({ weft: { ...weft, treadles: parseInt(event.target.value, 10) || 1 } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteSelectedThreads = () => {
|
||||||
|
const sure = window.confirm('Really delete the selected threads?');
|
||||||
|
if (!sure) return;
|
||||||
|
const newWarp = Object.assign({}, pattern.warp);
|
||||||
|
const newWeft = Object.assign({}, pattern.weft);
|
||||||
|
if (pattern?.warp?.threading) {
|
||||||
|
newWarp.threading = newWarp.threading.filter(t => !t.isSelected);
|
||||||
|
}
|
||||||
|
if (pattern?.weft?.treadling) {
|
||||||
|
newWeft.treadling = newWeft.treadling.filter(t => !t.isSelected);
|
||||||
|
}
|
||||||
|
updatePattern({ warp: newWarp, weft: newWeft });
|
||||||
|
}
|
||||||
|
const deselectThreads = () => {
|
||||||
|
const newWarp = Object.assign({}, pattern.warp);
|
||||||
|
const newWeft = Object.assign({}, pattern.weft);
|
||||||
|
newWarp.threading = newWarp?.threading?.map(t => ({ ...t, isSelected: undefined }));
|
||||||
|
newWeft.treadling = newWeft?.treadling?.map(t => ({ ...t, isSelected: undefined }));
|
||||||
|
updatePattern({ warp: newWarp, weft: newWeft });
|
||||||
|
}
|
||||||
|
|
||||||
const onZoomChange = zoom => updatePattern({ baseSize: zoom || 10 });
|
const onZoomChange = zoom => updatePattern({ baseSize: zoom || 10 });
|
||||||
|
|
||||||
const drawerIsActive = drawer => activeDrawers.indexOf(drawer) > -1;
|
const drawerIsActive = drawer => activeDrawers.indexOf(drawer) > -1;
|
||||||
@ -143,8 +174,16 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pattern-toolbox joyride-tools">
|
<div className="pattern-toolbox joyride-tools">
|
||||||
{unsaved &&
|
{selectedThreadCount > 0 &&
|
||||||
<Segment attached="top">
|
<Segment attached="top">
|
||||||
|
<Header>{selectedThreadCount} threads selected</Header>
|
||||||
|
<Button basic onClick={deselectThreads}>De-select all</Button>
|
||||||
|
<Button color='orange' onClick={deleteSelectedThreads}>Delete threads</Button>
|
||||||
|
</Segment>
|
||||||
|
}
|
||||||
|
|
||||||
|
{unsaved &&
|
||||||
|
<Segment attached>
|
||||||
<Button fluid color="teal" icon="save" content="Save pattern" onClick={() => saveObject(/*this.refs.canvas*/)} loading={saving}/>
|
<Button fluid color="teal" icon="save" content="Save pattern" onClick={() => saveObject(/*this.refs.canvas*/)} loading={saving}/>
|
||||||
<Button style={{marginTop: 5}} fluid icon='refresh' content='Undo changes' onClick={revertChanges} />
|
<Button style={{marginTop: 5}} fluid icon='refresh' content='Undo changes' onClick={revertChanges} />
|
||||||
</Segment>
|
</Segment>
|
||||||
@ -214,7 +253,9 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
|
|||||||
<Accordion.Content active={drawerIsActive('drawing')}>
|
<Accordion.Content active={drawerIsActive('drawing')}>
|
||||||
<Button.Group fluid>
|
<Button.Group fluid>
|
||||||
<Button className='joyride-pan' data-tooltip="Pan (drag to move) pattern" color={editor.tool === 'pan' && 'blue'} size="tiny" icon onClick={() => enableTool('pan')}><Icon name="move" /></Button>
|
<Button className='joyride-pan' data-tooltip="Pan (drag to move) pattern" color={editor.tool === 'pan' && 'blue'} size="tiny" icon onClick={() => enableTool('pan')}><Icon name="move" /></Button>
|
||||||
<Button className='joyride-colour' data-tooltip="Paint selected colour" color={editor.tool === 'colour' && 'blue'} size="tiny" icon onClick={() => enableTool('colour')}><Icon name="paint brush" /></Button>
|
<Button data-tooltip="Select threads" color={editor.tool === 'select' && 'blue'} size="tiny" icon onClick={() => enableTool('select')}><Icon name="i cursor" /></Button>
|
||||||
|
<Button data-tooltip="Insert threads" color={editor.tool === 'insert' && 'blue'} size="tiny" icon onClick={() => enableTool('insert')}><Icon name="plus" /></Button>
|
||||||
|
<Button className='joyride-colour' data-tooltip="Apply thread colour" color={editor.tool === 'colour' && 'blue'} size="tiny" icon onClick={() => enableTool('colour')}><Icon name="paint brush" /></Button>
|
||||||
<Button className='joyride-straight' data-tooltip="Straight draw" color={editor.tool === 'straight' && 'blue'} size="tiny" icon onClick={() => enableTool('straight')}>/ /</Button>
|
<Button className='joyride-straight' data-tooltip="Straight draw" color={editor.tool === 'straight' && 'blue'} size="tiny" icon onClick={() => enableTool('straight')}>/ /</Button>
|
||||||
<Button className='joyride-point' data-tooltip="Point draw" color={editor.tool === 'point' && 'blue'} size="tiny" icon onClick={() => enableTool('point')}><Icon name="chevron up" /></Button>
|
<Button className='joyride-point' data-tooltip="Point draw" color={editor.tool === 'point' && 'blue'} size="tiny" icon onClick={() => enableTool('point')}><Icon name="chevron up" /></Button>
|
||||||
</Button.Group>
|
</Button.Group>
|
||||||
|
@ -8,8 +8,9 @@ const StyledWarp = styled.div`
|
|||||||
right:0px;
|
right:0px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: ${props => (props.treadles * props.baseSize) + 20}px;
|
right: ${props => (props.treadles * props.baseSize) + 20}px;
|
||||||
height: ${props => (props.shafts * props.baseSize) + 10}px;
|
height: ${props => (props.shafts * props.baseSize) + 40}px;
|
||||||
width: ${props => (props.threading * props.baseSize) + 10}px;
|
width: 100%;
|
||||||
|
cursor: ${props => props.tool === 'insert' ? 'w-resize': 'initial'};
|
||||||
.warp-colourway td{
|
.warp-colourway td{
|
||||||
border:none;
|
border:none;
|
||||||
border-top:1px solid rgb(150,150,150);
|
border-top:1px solid rgb(150,150,150);
|
||||||
@ -18,6 +19,7 @@ const StyledWarp = styled.div`
|
|||||||
|
|
||||||
const squares = {};
|
const squares = {};
|
||||||
const markers = {};
|
const markers = {};
|
||||||
|
const selectedMarkers = {};
|
||||||
let dragging = false;
|
let dragging = false;
|
||||||
let startShaft = null;
|
let startShaft = null;
|
||||||
let startThread = null;
|
let startThread = null;
|
||||||
@ -154,18 +156,39 @@ function Warp({ baseSize, cellStyle, warp, weft, updatePattern }) {
|
|||||||
if (y > hY || y <= lY) yDirection = 0 - yDirection;
|
if (y > hY || y <= lY) yDirection = 0 - yDirection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (editor.tool === 'select') {
|
||||||
|
while (x <= hX && x >= lX) {
|
||||||
|
newWarp.threading[x].isSelected = true;
|
||||||
|
x += xDirection;
|
||||||
|
}
|
||||||
|
}
|
||||||
updatePattern({ warp: newWarp });
|
updatePattern({ warp: newWarp });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const click = (event) => {
|
const click = (event) => {
|
||||||
|
const { thread, shaft } = getThreadShaft(event, warpRef.current);
|
||||||
if (editor.tool === 'point' || editor.tool === 'straight') {
|
if (editor.tool === 'point' || editor.tool === 'straight') {
|
||||||
const { thread, shaft } = getThreadShaft(event, warpRef.current);
|
|
||||||
const newWarp = Object.assign({}, warp);
|
const newWarp = Object.assign({}, warp);
|
||||||
if (thread > warp.threading.length || warp.threading.length - thread < 5) fillUpTo(newWarp, thread + 5);
|
if (thread > warp.threading.length || warp.threading.length - thread < 5) fillUpTo(newWarp, thread + 5);
|
||||||
const warpThread = newWarp.threading[thread];
|
const warpThread = newWarp.threading[thread];
|
||||||
warpThread.shaft = warpThread.shaft === shaft ? 0 : shaft;
|
warpThread.shaft = warpThread.shaft === shaft ? 0 : shaft;
|
||||||
updatePattern({ warp: newWarp });
|
updatePattern({ warp: newWarp });
|
||||||
}
|
}
|
||||||
|
if (editor.tool === 'select') {
|
||||||
|
const newWarp = Object.assign({}, warp);
|
||||||
|
const warpThread = newWarp.threading[thread];
|
||||||
|
warpThread.isSelected = !warpThread.isSelected;
|
||||||
|
updatePattern({ warp: newWarp });
|
||||||
|
}
|
||||||
|
if (editor.tool === 'insert') {
|
||||||
|
const number = parseInt(prompt('Enter a number of threads to insert before this point.'));
|
||||||
|
if (number && number > 0) {
|
||||||
|
const newThreads = [...new Array(number)].map(() => ({ shaft: 0 }));
|
||||||
|
const newWarp = Object.assign({}, warp);
|
||||||
|
newWarp.threading?.splice(thread, 0, ...newThreads);
|
||||||
|
updatePattern({ warp: newWarp });
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fillUpTo = (w, limit) => {
|
const fillUpTo = (w, limit) => {
|
||||||
@ -188,6 +211,22 @@ function Warp({ baseSize, cellStyle, warp, weft, updatePattern }) {
|
|||||||
markers[size] = m_canvas;
|
markers[size] = m_canvas;
|
||||||
return m_canvas;
|
return m_canvas;
|
||||||
};
|
};
|
||||||
|
const getSelectedMarker = (size, height) => {
|
||||||
|
const m_canvas = document.createElement('canvas');
|
||||||
|
m_canvas.width = baseSize + 1;
|
||||||
|
m_canvas.height = height;
|
||||||
|
const mc = m_canvas.getContext('2d');
|
||||||
|
mc.fillStyle = 'rgb(233,245,248)';
|
||||||
|
mc.fillRect(0, 1, baseSize, height);
|
||||||
|
mc.moveTo(0, 0);
|
||||||
|
mc.lineTo(baseSize+1, 0);
|
||||||
|
mc.lineTo(baseSize+1, height);
|
||||||
|
mc.lineTo(0, height);
|
||||||
|
mc.lineTo(0, 0);
|
||||||
|
mc.strokeStyle = 'rgb(99,184,205)';
|
||||||
|
mc.stroke();
|
||||||
|
return m_canvas;
|
||||||
|
};
|
||||||
|
|
||||||
const getSquare = (size, colour) => {
|
const getSquare = (size, colour) => {
|
||||||
if (squares[size] && squares[size][colour]) return squares[size][colour];
|
if (squares[size] && squares[size][colour]) return squares[size][colour];
|
||||||
@ -221,9 +260,14 @@ function Warp({ baseSize, cellStyle, warp, weft, updatePattern }) {
|
|||||||
ctx.strokeStyle = 'rgba(0,0,0,0.3)';
|
ctx.strokeStyle = 'rgba(0,0,0,0.3)';
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
|
const selectedMarker = getSelectedMarker(baseSize, canvas.height);
|
||||||
|
const marker = getMarker(baseSize);
|
||||||
for (let thread = 0; thread < warp.threading.length; thread++) {
|
for (let thread = 0; thread < warp.threading.length; thread++) {
|
||||||
const shaft = warp.threading[thread].shaft;
|
const shaft = warp.threading[thread].shaft;
|
||||||
const marker = getMarker(baseSize);
|
const isSelected = warp.threading[thread].isSelected;
|
||||||
|
if (isSelected) {
|
||||||
|
ctx.drawImage(selectedMarker, canvas.width - ((thread + 1) * baseSize), 0);
|
||||||
|
}
|
||||||
ctx.drawImage(marker, canvas.width - ((thread + 1) * baseSize), canvas.height - (shaft * baseSize));
|
ctx.drawImage(marker, canvas.width - ((thread + 1) * baseSize), canvas.height - (shaft * baseSize));
|
||||||
const colourSquare = getSquare(baseSize, warp.threading[thread].colour || warp.defaultColour);
|
const colourSquare = getSquare(baseSize, warp.threading[thread].colour || warp.defaultColour);
|
||||||
ctx2.drawImage(colourSquare, canvas.width - ((thread + 1) * baseSize), 0);
|
ctx2.drawImage(colourSquare, canvas.width - ((thread + 1) * baseSize), 0);
|
||||||
@ -231,7 +275,7 @@ function Warp({ baseSize, cellStyle, warp, weft, updatePattern }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWarp treadles={weft.treadles} shafts={warp.shafts} baseSize={baseSize}>
|
<StyledWarp treadles={weft.treadles} shafts={warp.shafts} baseSize={baseSize} tool={tool}>
|
||||||
<canvas className='warp-colourway joyride-warpColourway' ref={colourwayRef} width={warp.threading.length * baseSize} height={10}
|
<canvas className='warp-colourway joyride-warpColourway' ref={colourwayRef} width={warp.threading.length * baseSize} height={10}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute', top: 0, right: 0, height: 10, width: warp.threading.length * baseSize,
|
position: 'absolute', top: 0, right: 0, height: 10, width: warp.threading.length * baseSize,
|
||||||
|
@ -12,6 +12,7 @@ const StyledWeft = styled.div`
|
|||||||
min-height: 1000px;
|
min-height: 1000px;
|
||||||
width: ${props => (props.treadles * props.baseSize)}px;
|
width: ${props => (props.treadles * props.baseSize)}px;
|
||||||
height: ${props => props.threads * props.baseSize}px;
|
height: ${props => props.threads * props.baseSize}px;
|
||||||
|
cursor: ${props => props.tool === 'insert' ? 's-resize': 'initial'};
|
||||||
.weft-colourway{
|
.weft-colourway{
|
||||||
border:none;
|
border:none;
|
||||||
border-right:1px solid rgb(150,150,150);
|
border-right:1px solid rgb(150,150,150);
|
||||||
@ -157,12 +158,18 @@ function Weft({ cellStyle, warp, weft, baseSize, updatePattern }) {
|
|||||||
if (x > hX || x <= lX) xDirection = 0 - xDirection;
|
if (x > hX || x <= lX) xDirection = 0 - xDirection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (editor.tool === 'select') {
|
||||||
|
while (y <= hY && y >= lY) {
|
||||||
|
newWeft.treadling[y - 1].isSelected = true;
|
||||||
|
y += yDirection;
|
||||||
|
}
|
||||||
|
}
|
||||||
updatePattern({ weft: newWeft });
|
updatePattern({ weft: newWeft });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const click = (event) => {
|
const click = (event) => {
|
||||||
|
let { thread, treadle } = getThreadTreadle(event, weftRef.current);
|
||||||
if (editor.tool === 'point' || editor.tool === 'straight') {
|
if (editor.tool === 'point' || editor.tool === 'straight') {
|
||||||
let { thread, treadle } = getThreadTreadle(event, weftRef.current);
|
|
||||||
treadle += 1;
|
treadle += 1;
|
||||||
const newWeft = Object.assign({}, weft);
|
const newWeft = Object.assign({}, weft);
|
||||||
if (thread >= newWeft.treadling.length || newWeft.treadling.length - thread < 5) fillUpTo(newWeft, thread + 5);
|
if (thread >= newWeft.treadling.length || newWeft.treadling.length - thread < 5) fillUpTo(newWeft, thread + 5);
|
||||||
@ -170,6 +177,21 @@ function Weft({ cellStyle, warp, weft, baseSize, updatePattern }) {
|
|||||||
weftThread.treadle = weftThread.treadle === treadle ? 0 : treadle;
|
weftThread.treadle = weftThread.treadle === treadle ? 0 : treadle;
|
||||||
updatePattern({ weft: newWeft });
|
updatePattern({ weft: newWeft });
|
||||||
}
|
}
|
||||||
|
if (editor.tool === 'select') {
|
||||||
|
const newWeft = Object.assign({}, weft);
|
||||||
|
const weftThread = newWeft.treadling[thread - 1];
|
||||||
|
weftThread.isSelected = !weftThread.isSelected;
|
||||||
|
updatePattern({ weft: newWeft });
|
||||||
|
}
|
||||||
|
if (editor.tool === 'insert') {
|
||||||
|
const number = parseInt(prompt('Enter a number of threads to insert above this point.'));
|
||||||
|
if (number && number > 0) {
|
||||||
|
const newThreads = [...new Array(number)].map(() => ({ treadle: 0 }));
|
||||||
|
const newWeft = Object.assign({}, weft);
|
||||||
|
newWeft.treadling?.splice(thread - 1, 0, ...newThreads);
|
||||||
|
updatePattern({ weft: newWeft });
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fillUpTo = (weft, limit) => {
|
const fillUpTo = (weft, limit) => {
|
||||||
@ -192,6 +214,22 @@ function Weft({ cellStyle, warp, weft, baseSize, updatePattern }) {
|
|||||||
markers[size] = m_canvas;
|
markers[size] = m_canvas;
|
||||||
return m_canvas;
|
return m_canvas;
|
||||||
};
|
};
|
||||||
|
const getSelectedMarker = (size, width) => {
|
||||||
|
const m_canvas = document.createElement('canvas');
|
||||||
|
m_canvas.width = width + 1;
|
||||||
|
m_canvas.height = baseSize;
|
||||||
|
const mc = m_canvas.getContext('2d');
|
||||||
|
mc.fillStyle = 'rgb(233,245,248)';
|
||||||
|
mc.fillRect(0, 1, width, baseSize);
|
||||||
|
mc.moveTo(0, 0);
|
||||||
|
mc.lineTo(width+1, 0);
|
||||||
|
mc.lineTo(width+1, baseSize);
|
||||||
|
mc.lineTo(0, baseSize);
|
||||||
|
mc.lineTo(0, 0);
|
||||||
|
mc.strokeStyle = 'rgb(99,184,205)';
|
||||||
|
mc.stroke();
|
||||||
|
return m_canvas;
|
||||||
|
};
|
||||||
|
|
||||||
const getSquare = (size, colour) => {
|
const getSquare = (size, colour) => {
|
||||||
if (squares[size] && squares[size][colour]) return squares[size][colour];
|
if (squares[size] && squares[size][colour]) return squares[size][colour];
|
||||||
@ -225,29 +263,35 @@ function Weft({ cellStyle, warp, weft, baseSize, updatePattern }) {
|
|||||||
ctx.strokeStyle = 'rgba(0,0,0,0.3)';
|
ctx.strokeStyle = 'rgba(0,0,0,0.3)';
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
for (let thread = 0; thread < weft.threads; thread++) {
|
const marker = getMarker(baseSize);
|
||||||
|
const selectedMarker = getSelectedMarker(baseSize, canvas.width);
|
||||||
|
for (let thread = 0; thread < weft.treadling.length; thread++) {
|
||||||
const treadle = weft.treadling[thread].treadle;
|
const treadle = weft.treadling[thread].treadle;
|
||||||
const marker = getMarker(baseSize);
|
const isSelected = weft.treadling[thread].isSelected;
|
||||||
|
if (isSelected) {
|
||||||
|
ctx.drawImage(selectedMarker, 0, ((thread) * baseSize));
|
||||||
|
}
|
||||||
ctx.drawImage(marker, ((treadle - 1) * baseSize), ((thread) * baseSize));
|
ctx.drawImage(marker, ((treadle - 1) * baseSize), ((thread) * baseSize));
|
||||||
const colourSquare = getSquare(baseSize, weft.treadling[thread].colour || weft.defaultColour);
|
const colourSquare = getSquare(baseSize, weft.treadling[thread].colour || weft.defaultColour);
|
||||||
ctx2.drawImage(colourSquare, 0, (thread * baseSize));
|
ctx2.drawImage(colourSquare, 0, (thread * baseSize));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const threadCount = weft.treadling?.length || 0;
|
||||||
return (
|
return (
|
||||||
<StyledWeft baseSize={baseSize} treadles={weft.treadles} shafts={warp.shafts} threads={weft.threads}>
|
<StyledWeft baseSize={baseSize} treadles={weft.treadles} shafts={warp.shafts} threads={threadCount} tool={tool}>
|
||||||
<canvas className='weft-colourway' ref={colourwayRef} width={10} height={weft.threads * baseSize}
|
<canvas className='weft-colourway' ref={colourwayRef} width={10} height={threadCount * baseSize}
|
||||||
style={{ position: 'absolute', top: 0, right: 0, width: 10, height: weft.threads * baseSize}}
|
style={{ position: 'absolute', top: 0, right: 0, width: 10, height: threadCount * baseSize}}
|
||||||
onClick={mouseClickColourway}
|
onClick={mouseClickColourway}
|
||||||
onMouseDown={mouseDownColourway}
|
onMouseDown={mouseDownColourway}
|
||||||
onMouseMove={mouseMoveColourway}
|
onMouseMove={mouseMoveColourway}
|
||||||
onMouseUp={mouseUpColourway}
|
onMouseUp={mouseUpColourway}
|
||||||
onMouseLeave={mouseUpColourway}
|
onMouseLeave={mouseUpColourway}
|
||||||
/>
|
/>
|
||||||
<canvas className='weft-threads joyride-weft' ref={weftRef} width={weft.treadles * baseSize} height={weft.threads * baseSize}
|
<canvas className='weft-threads joyride-weft' ref={weftRef} width={weft.treadles * baseSize} height={threadCount * baseSize}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0, right: 10, height: weft.threads * baseSize, width: weft.treadles * baseSize,
|
top: 0, right: 10, height: threadCount * baseSize, width: weft.treadles * baseSize,
|
||||||
borderRadius: 4, boxShadow: '0px 0px 10px rgba(0,0,0,0.15)',
|
borderRadius: 4, boxShadow: '0px 0px 10px rgba(0,0,0,0.15)',
|
||||||
}}
|
}}
|
||||||
onClick={click}
|
onClick={click}
|
||||||
|
Loading…
Reference in New Issue
Block a user