Compare commits

..

No commits in common. "9f75631d582b11f0ab85f98f18cf348b61749bf3" and "ac6020cb8eb3a7ab82beb5f17c689b2ee1415ccd" have entirely different histories.

10 changed files with 28 additions and 173 deletions

View File

@ -6,6 +6,7 @@ from api import uploads
default_pattern = {
'warp': {
'shafts': 8,
'threads': 100,
'threading': [{'shaft': 0}] * 100,
'defaultColour': '178,53,111',
'defaultSpacing': 1,
@ -13,6 +14,7 @@ default_pattern = {
},
'weft': {
'treadles': 8,
'threads': 50,
'treadling': [{'treadle': 0}] * 50,
'defaultColour': '53,69,178',
'defaultSpacing': 1,

View File

@ -62,7 +62,7 @@ def dumps(obj):
wif.append('\n[WARP]')
wif.append('Units=centimeters')
wif.append('Color={0}'.format(get_colour_index(obj['pattern']['colours'], obj['pattern']['warp']['defaultColour'])))
wif.append('Threads={0}'.format(len(obj['pattern']['warp']['threading'])))
wif.append('Threads={0}'.format(obj['pattern']['warp']['threads']))
wif.append('Spacing=0.212')
wif.append('Thickness=0.212')
@ -78,7 +78,7 @@ def dumps(obj):
wif.append('\n[WEFT]')
wif.append('Units=centimeters')
wif.append('Color={0}'.format(get_colour_index(obj['pattern']['colours'], obj['pattern']['weft']['defaultColour'])))
wif.append('Threads={0}'.format(len(obj['pattern']['weft']['treadling'])))
wif.append('Threads={0}'.format(obj['pattern']['weft']['threads']))
wif.append('Spacing=0.212')
wif.append('Thickness=0.212')
@ -151,6 +151,7 @@ def loads(wif_file):
while int(x) >= len(draft['warp']['threading']) - 1:
draft['warp']['threading'].append({'shaft': 0})
draft['warp']['threading'][int(x) - 1] = {'shaft': shaft}
draft['warp']['threads'] = len(draft['warp']['threading'])
try:
warp_colours = config['warp colors']
for x in warp_colours:
@ -179,6 +180,7 @@ def loads(wif_file):
while int(x) >= len(draft['weft']['treadling']) - 1:
draft['weft']['treadling'].append({'treadle': 0})
draft['weft']['treadling'][int(x) - 1] = {'treadle': shaft}
draft['weft']['threads'] = len(draft['weft']['treadling'])
try:
weft_colours = config['weft colors']
for x in weft_colours:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

View File

@ -91,26 +91,6 @@ 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.
### 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
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.

View File

@ -8,6 +8,8 @@ const StyledDrawdown = styled.canvas`
border:1px dashed rgb(70,70,70);
top: ${props => (props.warp.shafts * 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
@ -84,12 +86,10 @@ function Drawdown({ baseSize, warp, weft, tieups }) {
}
};
const warpThreads = warp.threading?.length || 0;
const weftThreads = weft.treadling?.length || 0;
return (
<StyledDrawdown ref={drawdownRef} className="drawdown joyride-drawdown"
width={warpThreads * baseSize}
height={weftThreads * baseSize}
width={warp.threads * baseSize}
height={weft.threads * baseSize}
weft={weft} warp={warp} baseSize={baseSize}
/>
);

View File

@ -40,7 +40,7 @@ function Tieups({ cellStyle, warp, weft, tieups, updatePattern, baseSize }) {
const paintTieups = () => {
const canvas = tieupRef.current;
const ctx = canvas.getContext('2d');
const ctx = canvas.getContext('2d');// , { alpha: false });
ctx.clearRect(0, 0, canvas.width, canvas.height);

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import {
Confirm, Header, Select, Segment, Accordion, Grid, Icon, Input, Button, Popup
Confirm, Select, Segment, Accordion, Grid, Icon, Input, Button, Popup
} from 'semantic-ui-react';
import { useSelector, useDispatch } from 'react-redux';
import { useNavigate, useParams } from 'react-router-dom';
@ -40,7 +40,6 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
const [activeDrawers, setActiveDrawers] = useState(['properties', 'drawing', 'palette']);
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
const [newColour, setNewColour] = useState('#22194D');
const [selectedThreadCount, setSelectedThreadCount] = useState(0);
const navigate = useNavigate();
const dispatch = useDispatch();
const { objectId, username, projectPath } = useParams();
@ -53,15 +52,6 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
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) => {
dispatch(actions.objects.updateEditor({ tool, colour: editor.colour }));
};
@ -86,27 +76,6 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
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 drawerIsActive = drawer => activeDrawers.indexOf(drawer) > -1;
@ -174,16 +143,8 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
return (
<div className="pattern-toolbox joyride-tools">
{selectedThreadCount > 0 &&
<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>
<Segment attached="top">
<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} />
</Segment>
@ -253,9 +214,7 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
<Accordion.Content active={drawerIsActive('drawing')}>
<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 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-colour' data-tooltip="Paint selected 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-point' data-tooltip="Point draw" color={editor.tool === 'point' && 'blue'} size="tiny" icon onClick={() => enableTool('point')}><Icon name="chevron up" /></Button>
</Button.Group>

View File

@ -8,9 +8,8 @@ const StyledWarp = styled.div`
right:0px;
position: absolute;
right: ${props => (props.treadles * props.baseSize) + 20}px;
height: ${props => (props.shafts * props.baseSize) + 40}px;
width: 100%;
cursor: ${props => props.tool === 'insert' ? 'w-resize': 'initial'};
height: ${props => (props.shafts * props.baseSize) + 10}px;
width: ${props => (props.threading * props.baseSize) + 10}px;
.warp-colourway td{
border:none;
border-top:1px solid rgb(150,150,150);
@ -19,7 +18,6 @@ const StyledWarp = styled.div`
const squares = {};
const markers = {};
const selectedMarkers = {};
let dragging = false;
let startShaft = null;
let startThread = null;
@ -156,39 +154,18 @@ function Warp({ baseSize, cellStyle, warp, weft, updatePattern }) {
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 });
}
};
const click = (event) => {
const { thread, shaft } = getThreadShaft(event, warpRef.current);
if (editor.tool === 'point' || editor.tool === 'straight') {
const { thread, shaft } = getThreadShaft(event, warpRef.current);
const newWarp = Object.assign({}, warp);
if (thread > warp.threading.length || warp.threading.length - thread < 5) fillUpTo(newWarp, thread + 5);
const warpThread = newWarp.threading[thread];
warpThread.shaft = warpThread.shaft === shaft ? 0 : shaft;
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) => {
@ -211,22 +188,6 @@ function Warp({ baseSize, cellStyle, warp, weft, updatePattern }) {
markers[size] = 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) => {
if (squares[size] && squares[size][colour]) return squares[size][colour];
@ -260,14 +221,9 @@ function Warp({ baseSize, cellStyle, warp, weft, updatePattern }) {
ctx.strokeStyle = 'rgba(0,0,0,0.3)';
ctx.stroke();
const selectedMarker = getSelectedMarker(baseSize, canvas.height);
const marker = getMarker(baseSize);
for (let thread = 0; thread < warp.threading.length; thread++) {
const shaft = warp.threading[thread].shaft;
const isSelected = warp.threading[thread].isSelected;
if (isSelected) {
ctx.drawImage(selectedMarker, canvas.width - ((thread + 1) * baseSize), 0);
}
const marker = getMarker(baseSize);
ctx.drawImage(marker, canvas.width - ((thread + 1) * baseSize), canvas.height - (shaft * baseSize));
const colourSquare = getSquare(baseSize, warp.threading[thread].colour || warp.defaultColour);
ctx2.drawImage(colourSquare, canvas.width - ((thread + 1) * baseSize), 0);
@ -275,7 +231,7 @@ function Warp({ baseSize, cellStyle, warp, weft, updatePattern }) {
};
return (
<StyledWarp treadles={weft.treadles} shafts={warp.shafts} baseSize={baseSize} tool={tool}>
<StyledWarp treadles={weft.treadles} shafts={warp.shafts} baseSize={baseSize}>
<canvas className='warp-colourway joyride-warpColourway' ref={colourwayRef} width={warp.threading.length * baseSize} height={10}
style={{
position: 'absolute', top: 0, right: 0, height: 10, width: warp.threading.length * baseSize,

View File

@ -12,7 +12,6 @@ const StyledWeft = styled.div`
min-height: 1000px;
width: ${props => (props.treadles * props.baseSize)}px;
height: ${props => props.threads * props.baseSize}px;
cursor: ${props => props.tool === 'insert' ? 's-resize': 'initial'};
.weft-colourway{
border:none;
border-right:1px solid rgb(150,150,150);
@ -158,18 +157,12 @@ function Weft({ cellStyle, warp, weft, baseSize, updatePattern }) {
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 });
}
};
const click = (event) => {
let { thread, treadle } = getThreadTreadle(event, weftRef.current);
if (editor.tool === 'point' || editor.tool === 'straight') {
let { thread, treadle } = getThreadTreadle(event, weftRef.current);
treadle += 1;
const newWeft = Object.assign({}, weft);
if (thread >= newWeft.treadling.length || newWeft.treadling.length - thread < 5) fillUpTo(newWeft, thread + 5);
@ -177,21 +170,6 @@ function Weft({ cellStyle, warp, weft, baseSize, updatePattern }) {
weftThread.treadle = weftThread.treadle === treadle ? 0 : treadle;
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) => {
@ -214,22 +192,6 @@ function Weft({ cellStyle, warp, weft, baseSize, updatePattern }) {
markers[size] = 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) => {
if (squares[size] && squares[size][colour]) return squares[size][colour];
@ -263,35 +225,29 @@ function Weft({ cellStyle, warp, weft, baseSize, updatePattern }) {
ctx.strokeStyle = 'rgba(0,0,0,0.3)';
ctx.stroke();
const marker = getMarker(baseSize);
const selectedMarker = getSelectedMarker(baseSize, canvas.width);
for (let thread = 0; thread < weft.treadling.length; thread++) {
for (let thread = 0; thread < weft.threads; thread++) {
const treadle = weft.treadling[thread].treadle;
const isSelected = weft.treadling[thread].isSelected;
if (isSelected) {
ctx.drawImage(selectedMarker, 0, ((thread) * baseSize));
}
const marker = getMarker(baseSize);
ctx.drawImage(marker, ((treadle - 1) * baseSize), ((thread) * baseSize));
const colourSquare = getSquare(baseSize, weft.treadling[thread].colour || weft.defaultColour);
ctx2.drawImage(colourSquare, 0, (thread * baseSize));
}
}
const threadCount = weft.treadling?.length || 0;
return (
<StyledWeft baseSize={baseSize} treadles={weft.treadles} shafts={warp.shafts} threads={threadCount} tool={tool}>
<canvas className='weft-colourway' ref={colourwayRef} width={10} height={threadCount * baseSize}
style={{ position: 'absolute', top: 0, right: 0, width: 10, height: threadCount * baseSize}}
<StyledWeft baseSize={baseSize} treadles={weft.treadles} shafts={warp.shafts} threads={weft.threads}>
<canvas className='weft-colourway' ref={colourwayRef} width={10} height={weft.threads * baseSize}
style={{ position: 'absolute', top: 0, right: 0, width: 10, height: weft.threads * baseSize}}
onClick={mouseClickColourway}
onMouseDown={mouseDownColourway}
onMouseMove={mouseMoveColourway}
onMouseUp={mouseUpColourway}
onMouseLeave={mouseUpColourway}
/>
<canvas className='weft-threads joyride-weft' ref={weftRef} width={weft.treadles * baseSize} height={threadCount * baseSize}
<canvas className='weft-threads joyride-weft' ref={weftRef} width={weft.treadles * baseSize} height={weft.threads * baseSize}
style={{
position: 'absolute',
top: 0, right: 10, height: threadCount * baseSize, width: weft.treadles * baseSize,
top: 0, right: 10, height: weft.threads * baseSize, width: weft.treadles * baseSize,
borderRadius: 4, boxShadow: '0px 0px 10px rgba(0,0,0,0.15)',
}}
onClick={click}