Handle pointed and curved

This commit is contained in:
DD 2017-12-21 14:49:05 -05:00
parent 0fc9fd151d
commit 07b30aa55b
4 changed files with 113 additions and 12 deletions

View file

@ -95,18 +95,18 @@ const ModeToolsComponent = props => {
return ( return (
<div className={classNames(props.className, styles.modeTools)}> <div className={classNames(props.className, styles.modeTools)}>
<LabeledIconButton <LabeledIconButton
disabled={!props.hasSelectedPoints} disabled={!props.hasSelectedUncurvedPoints}
imgAlt="Curved Point Icon" imgAlt="Curved Point Icon"
imgSrc={curvedPointIcon} imgSrc={curvedPointIcon}
title="Curved" title="Curved"
onClick={function () {}} onClick={props.onCurvePoints}
/> />
<LabeledIconButton <LabeledIconButton
disabled={!props.hasSelectedPoints} disabled={!props.hasSelectedUnpointedPoints}
imgAlt="Straight Point Icon" imgAlt="Straight Point Icon"
imgSrc={straightPointIcon} imgSrc={straightPointIcon}
title="Pointed" title="Pointed"
onClick={function () {}} onClick={props.onPointPoints}
/> />
</div> </div>
); );
@ -154,13 +154,16 @@ ModeToolsComponent.propTypes = {
className: PropTypes.string, className: PropTypes.string,
clipboardItems: PropTypes.arrayOf(PropTypes.array), clipboardItems: PropTypes.arrayOf(PropTypes.array),
eraserValue: PropTypes.number, eraserValue: PropTypes.number,
hasSelectedPoints: PropTypes.bool, hasSelectedUncurvedPoints: PropTypes.bool,
hasSelectedUnpointedPoints: PropTypes.bool,
intl: intlShape.isRequired, intl: intlShape.isRequired,
mode: PropTypes.string.isRequired, mode: PropTypes.string.isRequired,
onBrushSliderChange: PropTypes.func, onBrushSliderChange: PropTypes.func,
onCopyToClipboard: PropTypes.func.isRequired, onCopyToClipboard: PropTypes.func.isRequired,
onCurvePoints: PropTypes.func.isRequired,
onEraserSliderChange: PropTypes.func, onEraserSliderChange: PropTypes.func,
onPasteFromClipboard: PropTypes.func.isRequired, onPasteFromClipboard: PropTypes.func.isRequired,
onPointPoints: PropTypes.func.isRequired,
selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)) selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item))
}; };

View file

@ -13,21 +13,115 @@ class ModeTools extends React.Component {
constructor (props) { constructor (props) {
super(props); super(props);
bindAll(this, [ bindAll(this, [
'hasSelectedPoints', '_getSelectedUncurvedPoints',
'_getSelectedUnpointedPoints',
'hasSelectedUncurvedPoints',
'hasSelectedUnpointedPoints',
'handleCopyToClipboard', 'handleCopyToClipboard',
'handlePasteFromClipboard' 'handleCurvePoints',
'handlePasteFromClipboard',
'handlePointPoints'
]); ]);
} }
hasSelectedPoints () { _getSelectedUncurvedPoints () {
const items = [];
const selectedItems = getSelectedLeafItems(); const selectedItems = getSelectedLeafItems();
for (const item of selectedItems) { for (const item of selectedItems) {
if (!item.segments) continue;
for (const seg of item.segments) { for (const seg of item.segments) {
if (seg.selected) { if (seg.selected) {
return true; const prev = seg.getPrevious();
const next = seg.getNext();
const isCurved =
(!prev || seg.handleIn.length > 0) &&
(!next || seg.handleOut.length > 0) &&
(prev && next ? seg.handleOut.isColinear(seg.handleIn) : true);
if (!isCurved) items.push(seg);
} }
} }
} }
return false; return items;
}
_getSelectedUnpointedPoints () {
const points = [];
const selectedItems = getSelectedLeafItems();
for (const item of selectedItems) {
if (!item.segments) continue;
for (const seg of item.segments) {
if (seg.selected) {
if (seg.handleIn.length > 0 || seg.handleOut.length > 0) {
points.push(seg);
}
}
}
}
return points;
}
hasSelectedUncurvedPoints () {
const points = this._getSelectedUncurvedPoints();
return points.length > 0;
}
hasSelectedUnpointedPoints () {
const points = this._getSelectedUnpointedPoints();
return points.length > 0;
}
handleCurvePoints () {
let changed;
const points = this._getSelectedUncurvedPoints();
for (const point of points) {
const prev = point.getPrevious();
const next = point.getNext();
const noHandles = point.handleIn.length === 0 && point.handleOut.length === 0;
if (!prev && !next) {
continue;
} else if (prev && (!next || noHandles)) {
// Point is end point or has no handles
// Direction is average of normal at the point and direction to prev point
// Lenth is curve length / 2
point.handleIn = (prev.getCurve().getNormalAtTime(1)
.add(prev.point.subtract(point.point).normalize()))
.normalize()
.multiply(prev.getCurve().length / 2);
} else if (next && !prev) {
// Point is start point
// Direction is average of normal at the point and direction to next point
// Lenth is curve length / 2
point.handleOut = (point.getCurve().getNormalAtTime(0)
.add(next.point.subtract(point.point).normalize()))
.normalize()
.multiply(point.getCurve().length / 2);
}
// Point guaranteed to have a handle now. Make the second handle match the length and direction of first.
// This defines a curved point.
if (point.handleIn.length > 0 && next) {
point.handleOut = point.handleIn.multiply(-1);
} else if (point.handleOut.length > 0 && prev) {
point.handleIn = point.handleOut.multiply(-1);
}
changed = true;
}
if (changed) {
this.props.setSelectedItems();
this.props.onUpdateSvg();
}
}
handlePointPoints () {
let changed;
const points = this._getSelectedUnpointedPoints();
for (const point of points) {
const noHandles = point.handleIn.length === 0 && point.handleOut.length === 0;
if (!noHandles) {
point.handleIn = null;
point.handleOut = null;
changed = true;
}
}
if (changed) {
debugger;
this.props.setSelectedItems();
this.props.onUpdateSvg();
}
} }
handleCopyToClipboard () { handleCopyToClipboard () {
const selectedItems = getSelectedRootItems(); const selectedItems = getSelectedRootItems();
@ -62,9 +156,12 @@ class ModeTools extends React.Component {
render () { render () {
return ( return (
<ModeToolsComponent <ModeToolsComponent
hasSelectedPoints={this.hasSelectedPoints()} hasSelectedUncurvedPoints={this.hasSelectedUncurvedPoints()}
hasSelectedUnpointedPoints={this.hasSelectedUnpointedPoints()}
onCopyToClipboard={this.handleCopyToClipboard} onCopyToClipboard={this.handleCopyToClipboard}
onCurvePoints={this.handleCurvePoints}
onPasteFromClipboard={this.handlePasteFromClipboard} onPasteFromClipboard={this.handlePasteFromClipboard}
onPointPoints={this.handlePointPoints}
/> />
); );
} }

View file

@ -79,6 +79,7 @@ class HandleTool {
} }
} }
if (moved) { if (moved) {
this.setSelectedItems();
this.onUpdateSvg(); this.onUpdateSvg();
} }
this.selectedItems = []; this.selectedItems = [];

View file

@ -14,7 +14,7 @@ const reducer = function (state, action) {
if (action.selectedItems.length !== state.length) { if (action.selectedItems.length !== state.length) {
return action.selectedItems; return action.selectedItems;
} }
// Shallow equality check (we may need to update this later for more granularity) // Shallow equality check
for (let i = 0; i < action.selectedItems.length; i++) { for (let i = 0; i < action.selectedItems.length; i++) {
if (action.selectedItems[i] !== state[i]) { if (action.selectedItems[i] !== state[i]) {
return action.selectedItems; return action.selectedItems;