mirror of
https://github.com/Doodle3D/Doodle3D-Core.git
synced 2024-12-22 11:03:48 +01:00
Merge branch 'master' of https://github.com/Doodle3D/Doodle3D-Core
This commit is contained in:
commit
908050bbd0
BIN
img/contextmenu/btnFont.png
Normal file
BIN
img/contextmenu/btnFont.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
65
package-lock.json
generated
65
package-lock.json
generated
@ -1932,6 +1932,11 @@
|
|||||||
"isarray": "1.0.0"
|
"isarray": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"buffer-crc32": {
|
||||||
|
"version": "0.2.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||||
|
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
|
||||||
|
},
|
||||||
"buffer-from": {
|
"buffer-from": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.1.tgz",
|
||||||
@ -4286,6 +4291,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fd-slicer": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
|
||||||
|
"requires": {
|
||||||
|
"pend": "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"figures": {
|
"figures": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
|
||||||
@ -5666,6 +5679,33 @@
|
|||||||
"pinkie-promise": "2.0.1"
|
"pinkie-promise": "2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"google-fonts-webpack-plugin": {
|
||||||
|
"version": "0.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/google-fonts-webpack-plugin/-/google-fonts-webpack-plugin-0.4.4.tgz",
|
||||||
|
"integrity": "sha512-+e2D9/DVBG9EDydRovzoqMZ658SsTBGbC0c65GyZqkwNvdj8vRSYQKXqbz7/yt7QaXsCPT1MpH45r3ivWOitcw==",
|
||||||
|
"requires": {
|
||||||
|
"lodash": "4.17.4",
|
||||||
|
"node-fetch": "1.7.3",
|
||||||
|
"webpack-sources": "0.2.3",
|
||||||
|
"yauzl": "2.9.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"source-list-map": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-mIkBnRAkzOVc3AaUmDN+9hhqEaE="
|
||||||
|
},
|
||||||
|
"webpack-sources": {
|
||||||
|
"version": "0.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz",
|
||||||
|
"integrity": "sha1-F8Yr+vE8cH+dAsR54Nzd6DgGl/s=",
|
||||||
|
"requires": {
|
||||||
|
"source-list-map": "1.1.2",
|
||||||
|
"source-map": "0.5.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"got": {
|
"got": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/got/-/got-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/got/-/got-3.3.1.tgz",
|
||||||
@ -8398,6 +8438,11 @@
|
|||||||
"sort-keys": "1.1.2"
|
"sort-keys": "1.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"normalize-wheel": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU="
|
||||||
|
},
|
||||||
"npm": {
|
"npm": {
|
||||||
"version": "2.15.12",
|
"version": "2.15.12",
|
||||||
"resolved": "https://registry.npmjs.org/npm/-/npm-2.15.12.tgz",
|
"resolved": "https://registry.npmjs.org/npm/-/npm-2.15.12.tgz",
|
||||||
@ -10550,6 +10595,11 @@
|
|||||||
"integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=",
|
"integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"pend": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
|
||||||
|
},
|
||||||
"pepjs": {
|
"pepjs": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/pepjs/-/pepjs-0.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/pepjs/-/pepjs-0.4.3.tgz",
|
||||||
@ -11747,12 +11797,6 @@
|
|||||||
"prop-types": "15.6.0"
|
"prop-types": "15.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-router-redux": {
|
|
||||||
"version": "4.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-router-redux/-/react-router-redux-4.0.8.tgz",
|
|
||||||
"integrity": "sha1-InQDWWtRUeGCN32rg1tdRfD4BU4=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"react-svg-inline": {
|
"react-svg-inline": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-svg-inline/-/react-svg-inline-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-svg-inline/-/react-svg-inline-2.0.1.tgz",
|
||||||
@ -14370,6 +14414,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"yauzl": {
|
||||||
|
"version": "2.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.1.tgz",
|
||||||
|
"integrity": "sha1-qBmB6nCleUYTOIPwKcWCGok1mn8=",
|
||||||
|
"requires": {
|
||||||
|
"buffer-crc32": "0.2.13",
|
||||||
|
"fd-slicer": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"yml-loader": {
|
"yml-loader": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/yml-loader/-/yml-loader-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yml-loader/-/yml-loader-2.1.0.tgz",
|
||||||
|
@ -27,11 +27,13 @@
|
|||||||
"blueimp-canvas-to-blob": "^3.14.0",
|
"blueimp-canvas-to-blob": "^3.14.0",
|
||||||
"bowser": "^1.8.1",
|
"bowser": "^1.8.1",
|
||||||
"fit-curve": "^0.1.6",
|
"fit-curve": "^0.1.6",
|
||||||
|
"google-fonts-webpack-plugin": "^0.4.4",
|
||||||
"imports-loader": "^0.7.1",
|
"imports-loader": "^0.7.1",
|
||||||
"jss": "^9.4.0",
|
"jss": "^9.4.0",
|
||||||
"keycode": "^2.1.9",
|
"keycode": "^2.1.9",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"memoizee": "^0.3.9",
|
"memoizee": "^0.3.9",
|
||||||
|
"normalize-wheel": "^1.0.1",
|
||||||
"pouchdb": "^6.3.4",
|
"pouchdb": "^6.3.4",
|
||||||
"proptypes": "^1.1.0",
|
"proptypes": "^1.1.0",
|
||||||
"raf": "^3.4.0",
|
"raf": "^3.4.0",
|
||||||
@ -82,7 +84,6 @@
|
|||||||
"normalize-jss": "^4.0.0",
|
"normalize-jss": "^4.0.0",
|
||||||
"raw-loader": "^0.5.1",
|
"raw-loader": "^0.5.1",
|
||||||
"react-dom": "^16.1.1",
|
"react-dom": "^16.1.1",
|
||||||
"react-router-redux": "^4.0.8",
|
|
||||||
"react-tap-event-plugin": "^3.0.2",
|
"react-tap-event-plugin": "^3.0.2",
|
||||||
"redux": "^3.7.2",
|
"redux": "^3.7.2",
|
||||||
"redux-action-wrapper": "^1.0.1",
|
"redux-action-wrapper": "^1.0.1",
|
||||||
|
36
shaders/anaglyph_frag.glsl
Normal file
36
shaders/anaglyph_frag.glsl
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
uniform sampler2D mapLeft;
|
||||||
|
uniform sampler2D mapRight;
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
uniform mat3 colorMatrixLeft;
|
||||||
|
uniform mat3 colorMatrixRight;
|
||||||
|
|
||||||
|
float lin( float c ) {
|
||||||
|
return c <= 0.04045 ? c * 0.0773993808 :
|
||||||
|
pow( c * 0.9478672986 + 0.0521327014, 2.4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 lin( vec4 c ) {
|
||||||
|
return vec4( lin( c.r ), lin( c.g ), lin( c.b ), c.a );
|
||||||
|
}
|
||||||
|
|
||||||
|
float dev( float c ) {
|
||||||
|
return c <= 0.0031308 ? c * 12.92 : pow( c, 0.41666 ) * 1.055 - 0.055;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
vec2 uv = vUv;
|
||||||
|
|
||||||
|
vec4 colorL = lin( texture2D( mapLeft, uv ) );
|
||||||
|
vec4 colorR = lin( texture2D( mapRight, uv ) );
|
||||||
|
|
||||||
|
vec3 color = clamp(
|
||||||
|
colorMatrixLeft * colorL.rgb +
|
||||||
|
colorMatrixRight * colorR.rgb, 0., 1. );
|
||||||
|
|
||||||
|
gl_FragColor = vec4(
|
||||||
|
dev( color.r ), dev( color.g ), dev( color.b ),
|
||||||
|
max( colorL.a, colorR.a ) );
|
||||||
|
|
||||||
|
}
|
5
shaders/anaglyph_vert.glsl
Normal file
5
shaders/anaglyph_vert.glsl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
varying vec2 vUv;
|
||||||
|
void main() {
|
||||||
|
vUv = vec2( uv.x, uv.y );
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import { ActionCreators as undo } from 'redux-undo';
|
import { ActionCreators as undo } from 'redux-undo';
|
||||||
import * as notification from 'react-notification-system-redux';
|
import * as notification from 'react-notification-system-redux';
|
||||||
import { routerActions as router } from 'react-router-redux';
|
|
||||||
import * as selectionUtils from '../utils/selectionUtils.js';
|
import * as selectionUtils from '../utils/selectionUtils.js';
|
||||||
import { calculatePointInImage, decomposeMatrix } from '../utils/matrixUtils.js';
|
import { calculatePointInImage, decomposeMatrix } from '../utils/matrixUtils.js';
|
||||||
import { loadImage, prepareImage } from '../utils/imageUtils.js';
|
import { loadImage, prepareImage } from '../utils/imageUtils.js';
|
||||||
@ -74,7 +73,6 @@ export const ALIGN = 'ALIGN';
|
|||||||
export const ADD_IMAGE = 'ADD_IMAGE';
|
export const ADD_IMAGE = 'ADD_IMAGE';
|
||||||
export const D2_TEXT_INIT = 'D2_TEXT_INIT';
|
export const D2_TEXT_INIT = 'D2_TEXT_INIT';
|
||||||
export const D2_TEXT_INPUT_CHANGE = 'D2_TEXT_INPUT_CHANGE';
|
export const D2_TEXT_INPUT_CHANGE = 'D2_TEXT_INPUT_CHANGE';
|
||||||
export const D2_TEXT_ADD = 'D2_TEXT_ADD';
|
|
||||||
export const UNION = 'UNION';
|
export const UNION = 'UNION';
|
||||||
export const INTERSECT = 'INTERSECT';
|
export const INTERSECT = 'INTERSECT';
|
||||||
export const MOVE_SELECTION = 'MOVE_SELECTION';
|
export const MOVE_SELECTION = 'MOVE_SELECTION';
|
||||||
@ -390,14 +388,10 @@ export function addImage(file) {
|
|||||||
export function d2textInit(position, textId, screenMatrixContainer, screenMatrixZoom) {
|
export function d2textInit(position, textId, screenMatrixContainer, screenMatrixZoom) {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch({ type: D2_TEXT_INIT, position, textId, screenMatrixContainer, screenMatrixZoom });
|
dispatch({ type: D2_TEXT_INIT, position, textId, screenMatrixContainer, screenMatrixZoom });
|
||||||
dispatch(router.push('/sketch/inputtext'));
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function d2textInputChange(text, family, weight, style, fill) {
|
export function d2textInputChange(text) {
|
||||||
return { type: D2_TEXT_INPUT_CHANGE, text, family, weight, style, fill };
|
return { type: D2_TEXT_INPUT_CHANGE, text };
|
||||||
}
|
|
||||||
export function d2textAdd() {
|
|
||||||
return { type: D2_TEXT_ADD };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const traceDragThrottle = createThrottle();
|
const traceDragThrottle = createThrottle();
|
||||||
|
@ -23,6 +23,7 @@ import ShapesManager from '../d2/ShapesManager.js';
|
|||||||
import EventGroup from '../d2/EventGroup.js';
|
import EventGroup from '../d2/EventGroup.js';
|
||||||
import ReactResizeDetector from 'react-resize-detector';
|
import ReactResizeDetector from 'react-resize-detector';
|
||||||
import { load as loadPattern } from '../d2/Shape.js';
|
import { load as loadPattern } from '../d2/Shape.js';
|
||||||
|
import InputText from './InputText.js';
|
||||||
// import createDebug from 'debug';
|
// import createDebug from 'debug';
|
||||||
// const debug = createDebug('d3d:d2');
|
// const debug = createDebug('d3d:d2');
|
||||||
|
|
||||||
@ -63,6 +64,9 @@ class D2Panel extends React.Component {
|
|||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
classes: PropTypes.objectOf(PropTypes.string)
|
classes: PropTypes.objectOf(PropTypes.string)
|
||||||
};
|
};
|
||||||
|
state = {
|
||||||
|
screenMatrix: new CAL.Matrix()
|
||||||
|
};
|
||||||
activeNeedRender = false;
|
activeNeedRender = false;
|
||||||
inactiveNeedRender = false;
|
inactiveNeedRender = false;
|
||||||
|
|
||||||
@ -117,7 +121,7 @@ class D2Panel extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switchTool(toolName) {
|
switchTool(toolName) {
|
||||||
if (this.state && toolName === this.state.d2.tool) return;
|
if (this.state.state && toolName === this.state.state.d2.tool) return;
|
||||||
// cleanup & remove previous tool
|
// cleanup & remove previous tool
|
||||||
if (this.tool) {
|
if (this.tool) {
|
||||||
this.tool.destroy();
|
this.tool.destroy();
|
||||||
@ -135,18 +139,18 @@ class D2Panel extends React.Component {
|
|||||||
this.objectContainerActive.add(this.tool);
|
this.objectContainerActive.add(this.tool);
|
||||||
}
|
}
|
||||||
|
|
||||||
update(newState) {
|
update(state) {
|
||||||
if (this.state === newState) return;
|
if (this.state.state === state) return;
|
||||||
|
|
||||||
this.updateTool(newState);
|
this.updateTool(state);
|
||||||
|
|
||||||
const shapesNeedRender = this.shapesManager.update(newState);
|
const shapesNeedRender = this.shapesManager.update(state);
|
||||||
if (shapesNeedRender.active) this.activeNeedRender = true;
|
if (shapesNeedRender.active) this.activeNeedRender = true;
|
||||||
if (shapesNeedRender.inactive) this.inactiveNeedRender = true;
|
if (shapesNeedRender.inactive) this.inactiveNeedRender = true;
|
||||||
|
|
||||||
// Update Objects Container Space with zoom & panning
|
// Update Objects Container Space with zoom & panning
|
||||||
const newCanvasMatrix = newState.d2.canvasMatrix;
|
const newCanvasMatrix = state.d2.canvasMatrix;
|
||||||
if (this.state && newCanvasMatrix !== this.state.d2.canvasMatrix) {
|
if (this.state.state && newCanvasMatrix !== this.state.state.d2.canvasMatrix) {
|
||||||
this.objectContainerActive.copyMatrix(newCanvasMatrix);
|
this.objectContainerActive.copyMatrix(newCanvasMatrix);
|
||||||
this.objectContainerInactive.copyMatrix(newCanvasMatrix);
|
this.objectContainerInactive.copyMatrix(newCanvasMatrix);
|
||||||
|
|
||||||
@ -154,9 +158,9 @@ class D2Panel extends React.Component {
|
|||||||
this.inactiveNeedRender = true;
|
this.inactiveNeedRender = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selection = (this.state) ? this.state.selection : null;
|
const selection = (this.state.state) ? this.state.state.selection : null;
|
||||||
const newSelection = newState.selection;
|
const newSelection = state.selection;
|
||||||
if (!this.state || newSelection !== selection) {
|
if (!this.state.state || newSelection !== selection) {
|
||||||
const newSelectedObjects = newSelection.objects;
|
const newSelectedObjects = newSelection.objects;
|
||||||
if (!selection || selection.objects !== newSelectedObjects) {
|
if (!selection || selection.objects !== newSelectedObjects) {
|
||||||
const selected = newSelectedObjects.map((object) => object.id);
|
const selected = newSelectedObjects.map((object) => object.id);
|
||||||
@ -167,25 +171,29 @@ class D2Panel extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dragSelect = (this.state) ? this.state.d2.transform.dragSelect : null;
|
const dragSelect = (this.state.state) ? this.state.state.d2.transform.dragSelect : null;
|
||||||
const newDragSelect = newState.d2.transform.dragSelect;
|
const newDragSelect = state.d2.transform.dragSelect;
|
||||||
if (!dragSelect || dragSelect !== newDragSelect) {
|
if (!dragSelect || dragSelect !== newDragSelect) {
|
||||||
this.activeNeedRender = true;
|
this.activeNeedRender = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = newState;
|
this.setState({ state });
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeHandler = (width, height) => {
|
resizeHandler = (width, height) => {
|
||||||
this.sceneActive.setSize(width, height, PIXEL_RATIO);
|
this.sceneActive.setSize(width, height, PIXEL_RATIO);
|
||||||
this.sceneInactive.setSize(width, height, PIXEL_RATIO);
|
this.sceneInactive.setSize(width, height, PIXEL_RATIO);
|
||||||
|
|
||||||
this.sceneInactive.x = this.sceneActive.x = Math.round(width / 2 * PIXEL_RATIO);
|
const x = Math.round(width / 2 * PIXEL_RATIO);
|
||||||
this.sceneInactive.y = this.sceneActive.y = Math.round(height / 2 * PIXEL_RATIO);
|
const y = Math.round(height / 2 * PIXEL_RATIO);
|
||||||
|
|
||||||
const scale = Math.min(width * PIXEL_RATIO / CANVAS_WIDTH, height * PIXEL_RATIO / CANVAS_HEIGHT);
|
const scale = Math.min(width * PIXEL_RATIO / CANVAS_WIDTH, height * PIXEL_RATIO / CANVAS_HEIGHT);
|
||||||
|
|
||||||
this.sceneInactive.scale = this.sceneActive.scale = scale;
|
const screenMatrix = new CAL.Matrix({ sx: scale, sy: scale, x, y });
|
||||||
|
|
||||||
|
this.sceneInactive.copyMatrix(screenMatrix);
|
||||||
|
this.sceneActive.copyMatrix(screenMatrix);
|
||||||
|
|
||||||
|
this.setState({ screenMatrix });
|
||||||
|
|
||||||
this.inactiveNeedRender = this.activeNeedRender = true;
|
this.inactiveNeedRender = this.activeNeedRender = true;
|
||||||
this.renderRequest();
|
this.renderRequest();
|
||||||
@ -210,12 +218,14 @@ class D2Panel extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
// debug('this.props.state: ', this.props.state);
|
// debug('this.props.state: ', this.props.state);
|
||||||
const { state, classes } = this.props;
|
const { state, classes } = this.props;
|
||||||
|
const { screenMatrix } = this.state;
|
||||||
this.update(state);
|
this.update(state);
|
||||||
this.renderCanvas();
|
this.renderCanvas();
|
||||||
return (
|
return (
|
||||||
<div className={classes.container}>
|
<div className={classes.container}>
|
||||||
<ReactResizeDetector handleWidth handleHeight onResize={this.resizeHandler} />
|
<ReactResizeDetector handleWidth handleHeight onResize={this.resizeHandler} />
|
||||||
<div className={classes.canvasContainer} ref="canvasContainer" />
|
<div className={classes.canvasContainer} ref="canvasContainer" />
|
||||||
|
<InputText screenMatrix={screenMatrix} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import TwistTransformer from '../d3/transformers/TwistTransformer.js';
|
|||||||
import SculptTransformer from '../d3/transformers/SculptTransformer.js';
|
import SculptTransformer from '../d3/transformers/SculptTransformer.js';
|
||||||
import StampTransformer from '../d3/transformers/StampTransformer.js';
|
import StampTransformer from '../d3/transformers/StampTransformer.js';
|
||||||
import SelectionBox from '../d3/SelectionBox.js';
|
import SelectionBox from '../d3/SelectionBox.js';
|
||||||
import RenderChain from '../d3/RenderChain';
|
import RenderChain, { TOONSHADER_OUTLINE, TOONSHADER } from '../d3/RenderChain';
|
||||||
import BaseTransformer from '../d3/transformers/BaseTransformer.js';
|
import BaseTransformer from '../d3/transformers/BaseTransformer.js';
|
||||||
import Camera from '../d3/Camera.js';
|
import Camera from '../d3/Camera.js';
|
||||||
import ReactResizeDetector from 'react-resize-detector';
|
import ReactResizeDetector from 'react-resize-detector';
|
||||||
@ -62,8 +62,8 @@ class D3Panel extends React.Component {
|
|||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.createScene();
|
this.createScene();
|
||||||
|
|
||||||
const toonShader = hasExtensionsFor.toonShaderPreview;
|
const shader = hasExtensionsFor.toonShaderPreview ? TOONSHADER_OUTLINE : TOONSHADER;
|
||||||
this.renderChain = new RenderChain(this.renderer, this.scene, this.camera, toonShader, {
|
this.renderChain = new RenderChain(this.renderer, this.scene, this.camera, shader, {
|
||||||
UI: this.UIContainer,
|
UI: this.UIContainer,
|
||||||
shapes: this.shapesManager,
|
shapes: this.shapesManager,
|
||||||
boundingBox: this.selectionBox,
|
boundingBox: this.selectionBox,
|
||||||
|
@ -45,7 +45,7 @@ class DoodlePreview extends React.Component {
|
|||||||
scene: null
|
scene: null
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentWillMount() {
|
||||||
let { docData, sketchData } = this.props;
|
let { docData, sketchData } = this.props;
|
||||||
|
|
||||||
if (docData) sketchData = await JSONToSketchData(this.props.docData);
|
if (docData) sketchData = await JSONToSketchData(this.props.docData);
|
||||||
|
101
src/components/InputText.js
Normal file
101
src/components/InputText.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import injectSheet from 'react-jss';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import * as actions from '../actions/index.js';
|
||||||
|
import * as CAL from 'cal';
|
||||||
|
import { TEXT_TOOL_FONT_SIZE } from '../constants/d2Constants.js';
|
||||||
|
|
||||||
|
const CONTEXT = document.createElement('canvas').getContext('2d');
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
textInputContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
display: 'flex'
|
||||||
|
},
|
||||||
|
textInput: {
|
||||||
|
position: 'absolute',
|
||||||
|
fontSize: `${TEXT_TOOL_FONT_SIZE}px`,
|
||||||
|
background: 'transparent',
|
||||||
|
border: 'none',
|
||||||
|
color: 'black',
|
||||||
|
textFillColor: 'transparent',
|
||||||
|
outline: 'none'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class InputText extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
state: PropTypes.object.isRequired,
|
||||||
|
classes: PropTypes.objectOf(PropTypes.string),
|
||||||
|
changeText: PropTypes.func.isRequired,
|
||||||
|
screenMatrix: PropTypes.instanceOf(CAL.Matrix).isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
onInputChange = () => {
|
||||||
|
const shapeData = this.getShapeData();
|
||||||
|
if (!shapeData) return;
|
||||||
|
|
||||||
|
const { changeText } = this.props;
|
||||||
|
const text = this.refs.text.value;
|
||||||
|
|
||||||
|
changeText(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
getShapeData = () => {
|
||||||
|
const { state } = this.props;
|
||||||
|
if (!state.d2.activeShape) return null;
|
||||||
|
const shapeData = state.objectsById[state.d2.activeShape];
|
||||||
|
if (shapeData.type !== 'TEXT') return null;
|
||||||
|
return shapeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUpdate() {
|
||||||
|
if (this.refs.text) this.refs.text.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { classes, state, screenMatrix } = this.props;
|
||||||
|
const shapeData = this.getShapeData();
|
||||||
|
|
||||||
|
if (shapeData) {
|
||||||
|
const { _matrix: m } = shapeData.transform
|
||||||
|
.multiplyMatrix(state.d2.canvasMatrix)
|
||||||
|
.multiplyMatrix(screenMatrix);
|
||||||
|
|
||||||
|
const { family, style, weight, text } = shapeData.text;
|
||||||
|
|
||||||
|
CONTEXT.font = `${style} normal ${weight} ${TEXT_TOOL_FONT_SIZE}px ${family}`;
|
||||||
|
const width = Math.max(10, CONTEXT.measureText(shapeData.text.text).width);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classes.textInputContainer}
|
||||||
|
style={{ transform: `matrix(${m[0]}, ${m[3]}, ${m[1]}, ${m[4]}, ${m[2]}, ${m[5]})` }}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className={classes.textInput}
|
||||||
|
style={{
|
||||||
|
fontFamily: family,
|
||||||
|
fontStyle: style,
|
||||||
|
fontWeight: weight,
|
||||||
|
width: `${width}px`
|
||||||
|
}}
|
||||||
|
value={text}
|
||||||
|
ref="text"
|
||||||
|
spellCheck="false"
|
||||||
|
autoFocus
|
||||||
|
onChange={this.onInputChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectSheet(styles)(connect(state => ({
|
||||||
|
state: state.sketcher.present
|
||||||
|
}), {
|
||||||
|
changeText: actions.d2textInputChange
|
||||||
|
})(InputText));
|
@ -1,7 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import initialMenuStructure from '../constants/menu.js';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
// import createDebug from 'debug';
|
// import createDebug from 'debug';
|
||||||
// const debug = createDebug('d3d:menu');
|
// const debug = createDebug('d3d:menu');
|
||||||
|
|
||||||
@ -14,7 +12,7 @@ class Menu extends React.Component {
|
|||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
stateMenu: PropTypes.object
|
id: PropTypes.string
|
||||||
};
|
};
|
||||||
onSelect = (event) => {
|
onSelect = (event) => {
|
||||||
const { onSelect, value } = this.props;
|
const { onSelect, value } = this.props;
|
||||||
@ -23,7 +21,7 @@ class Menu extends React.Component {
|
|||||||
if (onSelect) onSelect({ ...event, menuValue });
|
if (onSelect) onSelect({ ...event, menuValue });
|
||||||
};
|
};
|
||||||
render() {
|
render() {
|
||||||
const { className = '', id, selectedValue, onOpen, onClose, value, children, stateMenu } = this.props;
|
const { className = '', id, selectedValue, onOpen, onClose, children } = this.props;
|
||||||
return (
|
return (
|
||||||
<ul id={id} className={`menu ${className}`}>
|
<ul id={id} className={`menu ${className}`}>
|
||||||
{React.Children.map(children, (child) => {
|
{React.Children.map(children, (child) => {
|
||||||
@ -39,6 +37,4 @@ class Menu extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(state => ({
|
export default Menu;
|
||||||
stateMenu: state.sketcher.present.menus
|
|
||||||
}))(Menu);
|
|
||||||
|
@ -9,6 +9,8 @@ import * as contextTools from '../constants/contextTools.js';
|
|||||||
import * as d2Tools from '../constants/d2Tools.js';
|
import * as d2Tools from '../constants/d2Tools.js';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import injectSheet from 'react-jss';
|
import injectSheet from 'react-jss';
|
||||||
|
import { FONT_TOOLS } from '../constants/contextTools.js';
|
||||||
|
import { FONT_FACE } from '../constants/general.js';
|
||||||
// TODO move this to jss instead of css
|
// TODO move this to jss instead of css
|
||||||
import '../../styles/styles.css';
|
import '../../styles/styles.css';
|
||||||
// import createDebug from 'debug';
|
// import createDebug from 'debug';
|
||||||
@ -120,6 +122,7 @@ function renderChildren(children) {
|
|||||||
|
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
let component;
|
let component;
|
||||||
|
|
||||||
if (child.children.length > 0) {
|
if (child.children.length > 0) {
|
||||||
component = (
|
component = (
|
||||||
<SubMenu
|
<SubMenu
|
||||||
@ -135,6 +138,18 @@ function renderChildren(children) {
|
|||||||
{renderChildren(child.children)}
|
{renderChildren(child.children)}
|
||||||
</SubMenu>
|
</SubMenu>
|
||||||
);
|
);
|
||||||
|
} else if (FONT_TOOLS.includes(child.value)) {
|
||||||
|
component = (
|
||||||
|
<MenuItem
|
||||||
|
id={child.value}
|
||||||
|
value={child.value}
|
||||||
|
key={child.value}
|
||||||
|
svg={child.svg}
|
||||||
|
disabled={child.disabled}
|
||||||
|
>
|
||||||
|
<p style={{ fontFamily: FONT_FACE[child.value] }}>{FONT_FACE[child.value]}</p>
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
component = (
|
component = (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@ -151,7 +166,7 @@ function renderChildren(children) {
|
|||||||
return components;
|
return components;
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, menus) {
|
function filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, selectionIncludesText, menus) {
|
||||||
const showUnion = activeTool === d2Tools.TRANSFORM && numFilledObjects && numSelectedObjects >= 2;
|
const showUnion = activeTool === d2Tools.TRANSFORM && numFilledObjects && numSelectedObjects >= 2;
|
||||||
const showIntersect = activeTool === d2Tools.TRANSFORM && numSelectedObjects >= 1;
|
const showIntersect = activeTool === d2Tools.TRANSFORM && numSelectedObjects >= 1;
|
||||||
return {
|
return {
|
||||||
@ -191,11 +206,14 @@ function filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidO
|
|||||||
case contextTools.HOLE_TOGGLE:
|
case contextTools.HOLE_TOGGLE:
|
||||||
return numSelectedObjects > 0;
|
return numSelectedObjects > 0;
|
||||||
|
|
||||||
|
case contextTools.FONT:
|
||||||
|
return selectionIncludesText || activeTool === d2Tools.TEXT;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}).map(child => {
|
}).map(child => {
|
||||||
return filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, child);
|
return filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, selectionIncludesText, child);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -216,12 +234,19 @@ const getMenus = createSelector([
|
|||||||
state => state.sketcher.present.menus,
|
state => state.sketcher.present.menus,
|
||||||
state => state.sketcher.present.d2.tool,
|
state => state.sketcher.present.d2.tool,
|
||||||
state => state.sketcher.present.selection.objects.length,
|
state => state.sketcher.present.selection.objects.length,
|
||||||
state => state.sketcher.present.selection.objects.filter(({ id }) => state.sketcher.present.objectsById[id].fill).length,
|
state => state.sketcher.present.selection.objects.filter(({ id }) => {
|
||||||
state => state.sketcher.present.selection.objects.filter(({ id }) => state.sketcher.present.objectsById[id].solid).length
|
return state.sketcher.present.objectsById[id].fill;
|
||||||
], (menus, activeTool, numSelectedObjects, numFilledObjects, numSolidObjects) => ({
|
}).length,
|
||||||
toolbar2d: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, nestChildren(menus, menus[TOOLBAR2D])),
|
state => state.sketcher.present.selection.objects.filter(({ id }) => {
|
||||||
toolbar3d: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, nestChildren(menus, menus[TOOLBAR3D])),
|
return state.sketcher.present.objectsById[id].solid;
|
||||||
context: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, nestChildren(menus, menus[CONTEXT]))
|
}).length,
|
||||||
|
state => state.sketcher.present.selection.objects.some(({ id }) => {
|
||||||
|
return state.sketcher.present.objectsById[id].type === 'TEXT';
|
||||||
|
})
|
||||||
|
], (menus, ...args) => ({
|
||||||
|
toolbar2d: filterMenus(...args, nestChildren(menus, menus[TOOLBAR2D])),
|
||||||
|
toolbar3d: filterMenus(...args, nestChildren(menus, menus[TOOLBAR3D])),
|
||||||
|
context: filterMenus(...args, nestChildren(menus, menus[CONTEXT]))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default injectSheet(styles)(connect(getMenus)(SketcherToolbars));
|
export default injectSheet(styles)(connect(getMenus)(SketcherToolbars));
|
||||||
|
@ -67,7 +67,9 @@ class SubMenu extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
render() {
|
render() {
|
||||||
const { id, value, selected, open, selectedValue, children, svg, toggleBehavior, color, solid } = this.props;
|
const {
|
||||||
|
id, value, selected, open, selectedValue, children, svg, toggleBehavior, color, solid
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
const style = {};
|
const style = {};
|
||||||
if (id === 'color-picker-tool') {
|
if (id === 'color-picker-tool') {
|
||||||
|
@ -8,6 +8,7 @@ export const HOLE_TOGGLE = 'hole-toggle-tool';
|
|||||||
export const ALIGN = 'align-tool';
|
export const ALIGN = 'align-tool';
|
||||||
export const UNION = 'union-tool';
|
export const UNION = 'union-tool';
|
||||||
export const INTERSECT = 'intersect-tool';
|
export const INTERSECT = 'intersect-tool';
|
||||||
|
export const FONT = 'font-tool';
|
||||||
|
|
||||||
export const LIGHT_BLUE_A = 'color-light-blue-a';
|
export const LIGHT_BLUE_A = 'color-light-blue-a';
|
||||||
export const LIGHT_BLUE_B = 'color-light-blue-b';
|
export const LIGHT_BLUE_B = 'color-light-blue-b';
|
||||||
@ -79,3 +80,23 @@ export const ALIGN_TOOLS = [
|
|||||||
ALIGN_VERTICAL,
|
ALIGN_VERTICAL,
|
||||||
ALIGN_BOTTOM
|
ALIGN_BOTTOM
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const OSWALD = 'oswald';
|
||||||
|
export const RANGA = 'ranga';
|
||||||
|
export const JOTI_ONE = 'joti-one';
|
||||||
|
export const BELLEFAIR = 'bellefair';
|
||||||
|
export const LOBSTER = 'lobster';
|
||||||
|
export const ABRIL_FATFACE = 'abril-fatface';
|
||||||
|
export const PLAY = 'play';
|
||||||
|
export const FASCINATE = 'fascinate';
|
||||||
|
|
||||||
|
export const FONT_TOOLS = [
|
||||||
|
OSWALD,
|
||||||
|
RANGA,
|
||||||
|
JOTI_ONE,
|
||||||
|
BELLEFAIR,
|
||||||
|
LOBSTER,
|
||||||
|
ABRIL_FATFACE,
|
||||||
|
PLAY,
|
||||||
|
FASCINATE
|
||||||
|
];
|
||||||
|
@ -41,3 +41,4 @@ export const BRUSH_SIZES = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CLIPPER_PRECISION = 100; // accurate to the hundredth
|
export const CLIPPER_PRECISION = 100; // accurate to the hundredth
|
||||||
|
export const TEXT_TOOL_FONT_SIZE = 40;
|
||||||
|
@ -37,3 +37,14 @@ export const COLOR_STRING_TO_HEX = {
|
|||||||
[contextTools.BLACK_B]: 0xAAAAAA,
|
[contextTools.BLACK_B]: 0xAAAAAA,
|
||||||
[contextTools.BLACK_C]: 0x444444
|
[contextTools.BLACK_C]: 0x444444
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const FONT_FACE = {
|
||||||
|
[contextTools.OSWALD]: 'Oswald',
|
||||||
|
[contextTools.RANGA]: 'Ranga',
|
||||||
|
[contextTools.JOTI_ONE]: 'Joti One',
|
||||||
|
[contextTools.BELLEFAIR]: 'Bellefair',
|
||||||
|
[contextTools.LOBSTER]: 'Lobster',
|
||||||
|
[contextTools.ABRIL_FATFACE]: 'Abril Fatface',
|
||||||
|
[contextTools.PLAY]: 'Play',
|
||||||
|
[contextTools.FASCINATE]: 'Fascinate'
|
||||||
|
};
|
||||||
|
@ -109,6 +109,11 @@ const context = {
|
|||||||
selected: contextTools.ALIGN_HORIZONTAL,
|
selected: contextTools.ALIGN_HORIZONTAL,
|
||||||
children: contextTools.ALIGN_TOOLS.map(value => ({ value })),
|
children: contextTools.ALIGN_TOOLS.map(value => ({ value })),
|
||||||
...selectorBehavior
|
...selectorBehavior
|
||||||
|
}, {
|
||||||
|
value: contextTools.FONT,
|
||||||
|
selected: contextTools.OSWALD,
|
||||||
|
children: contextTools.FONT_TOOLS.map(value => ({ value })),
|
||||||
|
...selectorBehavior
|
||||||
},
|
},
|
||||||
{ value: contextTools.UNION },
|
{ value: contextTools.UNION },
|
||||||
{ value: contextTools.INTERSECT }
|
{ value: contextTools.INTERSECT }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { Vector, Matrix } from '@doodle3d/cal';
|
import { Vector, Matrix } from '@doodle3d/cal';
|
||||||
import * as d2Tools from './d2Tools';
|
import * as d2Tools from './d2Tools';
|
||||||
import * as d3Tools from './d3Tools';
|
import * as d3Tools from './d3Tools';
|
||||||
|
import * as contextTools from './contextTools';
|
||||||
|
import { FONT_FACE } from '../constants/general.js';
|
||||||
|
|
||||||
const SHAPE = {
|
const SHAPE = {
|
||||||
D3Visible: true,
|
D3Visible: true,
|
||||||
@ -58,7 +60,7 @@ export const SHAPE_TYPE_PROPERTIES = {
|
|||||||
...SHAPE,
|
...SHAPE,
|
||||||
defaultProperties: {
|
defaultProperties: {
|
||||||
...defaultProperties,
|
...defaultProperties,
|
||||||
text: { text: '', family: 'Arial', weight: 'normal', style: 'normal' },
|
text: { text: '', family: FONT_FACE[contextTools.OSWALD], weight: 'normal', style: 'normal' },
|
||||||
fill: true
|
fill: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2,6 +2,7 @@ import { Group, Utils } from 'cal';
|
|||||||
import createListener from 'touch-events';
|
import createListener from 'touch-events';
|
||||||
import bowser from 'bowser';
|
import bowser from 'bowser';
|
||||||
import { convertEvent, isMouseEvent } from '../utils/pointerUtils.js';
|
import { convertEvent, isMouseEvent } from '../utils/pointerUtils.js';
|
||||||
|
import normalizeWheel from 'normalize-wheel';
|
||||||
|
|
||||||
const events = [
|
const events = [
|
||||||
'wheel', 'tap',
|
'wheel', 'tap',
|
||||||
@ -64,7 +65,9 @@ export default class EventGroup extends Group {
|
|||||||
if (event.event) gestureEvent.position = _convertEvent(event.event);
|
if (event.event) gestureEvent.position = _convertEvent(event.event);
|
||||||
if (event.events) gestureEvent.positions = event.events.map(_convertEvent);
|
if (event.events) gestureEvent.positions = event.events.map(_convertEvent);
|
||||||
if (event.preEvents) gestureEvent.preDrags = event.preEvents.map(_convertEvent);
|
if (event.preEvents) gestureEvent.preDrags = event.preEvents.map(_convertEvent);
|
||||||
if (event.event && event.event.deltaY !== undefined) gestureEvent.wheelDelta = event.event.deltaY;
|
if (event.event && event.event.deltaY !== undefined) {
|
||||||
|
gestureEvent.wheelDelta = normalizeWheel(event.event).pixelY;
|
||||||
|
}
|
||||||
|
|
||||||
this.onEvent(gestureEvent);
|
this.onEvent(gestureEvent);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import { shapeToPoints } from '../../shape/shapeToPoints';
|
|||||||
import { LINE_COLLISION_MARGIN } from '../../constants/d2Constants';
|
import { LINE_COLLISION_MARGIN } from '../../constants/d2Constants';
|
||||||
import { LINE_WIDTH } from '../../constants/d2Constants';
|
import { LINE_WIDTH } from '../../constants/d2Constants';
|
||||||
import { PIXEL_RATIO } from '../../constants/general';
|
import { PIXEL_RATIO } from '../../constants/general';
|
||||||
import { Matrix } from 'cal';
|
import { Matrix, Vector } from 'cal';
|
||||||
|
|
||||||
const HIT_ORDER = {
|
const HIT_ORDER = {
|
||||||
RECT: 0,
|
RECT: 0,
|
||||||
@ -132,13 +132,27 @@ export default class BaseTool extends EventGroup {
|
|||||||
const objects = shapeDatas
|
const objects = shapeDatas
|
||||||
.filter(shapeData => {
|
.filter(shapeData => {
|
||||||
const shapeMatrix = shapeData.transform.multiplyMatrix(matrix);
|
const shapeMatrix = shapeData.transform.multiplyMatrix(matrix);
|
||||||
|
let shapePoints = shapeToPoints(shapeData);
|
||||||
|
let { fill } = shapeData;
|
||||||
|
|
||||||
const isHit = shapeToPoints(shapeData)
|
if (shapeData.type === 'TEXT') {
|
||||||
|
if (shapeData.text.text === '') return false;
|
||||||
|
const { min, max } = getPointsBounds(shapePoints);
|
||||||
|
shapePoints = [{ points: [
|
||||||
|
new Vector(min.x, min.y),
|
||||||
|
new Vector(min.x, max.y),
|
||||||
|
new Vector(max.x, max.y),
|
||||||
|
new Vector(max.x, min.y)
|
||||||
|
], holes: [] }];
|
||||||
|
fill = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isHit = shapePoints
|
||||||
.some(({ points, holes }) => {
|
.some(({ points, holes }) => {
|
||||||
const shape = applyMatrixOnShape([points, ...holes], shapeMatrix);
|
const shape = applyMatrixOnShape([points, ...holes], shapeMatrix);
|
||||||
const clipperShape = new ClipperShape(shape, shapeData.fill, true, true);
|
const clipperShape = new ClipperShape(shape, fill, true, true);
|
||||||
|
|
||||||
if (shapeData.fill) {
|
if (fill) {
|
||||||
return clipperShape
|
return clipperShape
|
||||||
.fixOrientation()
|
.fixOrientation()
|
||||||
.pointInShape(position, true, true);
|
.pointInShape(position, true, true);
|
||||||
|
@ -58,24 +58,6 @@ export default class PolygonTool extends BaseTool {
|
|||||||
context.restore();
|
context.restore();
|
||||||
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
|
|
||||||
// const anglePosition = this.anglePosition.applyMatrix(matrix);
|
|
||||||
// context.arc(anglePosition.x, anglePosition.y, 20, 0, this.angleDegrees, this.angleDegrees > Math.PI);
|
|
||||||
//
|
|
||||||
// context.strokeStyle = 'black';
|
|
||||||
// context.lineWidth = 1;
|
|
||||||
// context.stroke();
|
|
||||||
//
|
|
||||||
// const angleTextPos = new Vector(28, 0)
|
|
||||||
// .rotate((this.angleDegrees < Math.PI ? this.angleDegrees : this.angleDegrees - Math.PI * 2.0) / 2)
|
|
||||||
// .add(anglePosition);
|
|
||||||
// context.textAlign = 'start';
|
|
||||||
// context.textBaseline = 'middle';
|
|
||||||
// context.fillStyle = '#000';
|
|
||||||
// context.font = '10px Arial';
|
|
||||||
//
|
|
||||||
// const angleDegrees = this.angleDegrees < Math.PI ? this.angleDegrees : Math.PI * 2 - this.angleDegrees;
|
|
||||||
// dimensionsText.drawText(context, humanReadable.degrees(angleDegrees), angleTextPos.x, angleTextPos.y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
src/d3/EventScene.js
vendored
5
src/d3/EventScene.js
vendored
@ -3,6 +3,7 @@ import { Utils } from 'cal';
|
|||||||
import createListener from 'touch-events';
|
import createListener from 'touch-events';
|
||||||
import bowser from 'bowser';
|
import bowser from 'bowser';
|
||||||
import { convertEvent, isMouseEvent } from '../utils/pointerUtils.js';
|
import { convertEvent, isMouseEvent } from '../utils/pointerUtils.js';
|
||||||
|
import normalizeWheel from 'normalize-wheel';
|
||||||
|
|
||||||
const events = ['wheel', 'tap', 'dragstart', 'drag', 'dragend', 'seconddragstart', 'seconddrag',
|
const events = ['wheel', 'tap', 'dragstart', 'drag', 'dragend', 'seconddragstart', 'seconddrag',
|
||||||
'seconddragend', 'multitouchstart', 'multitouch', 'multitouchend'];
|
'seconddragend', 'multitouchstart', 'multitouch', 'multitouchend'];
|
||||||
@ -41,7 +42,9 @@ export class EventScene extends THREE.Scene {
|
|||||||
if (event.event) gestureEvent.position = _convertEvent(event.event);
|
if (event.event) gestureEvent.position = _convertEvent(event.event);
|
||||||
if (event.events) gestureEvent.positions = event.events.map(_convertEvent);
|
if (event.events) gestureEvent.positions = event.events.map(_convertEvent);
|
||||||
if (event.preEvents) gestureEvent.preDrags = event.preEvents.map(_convertEvent);
|
if (event.preEvents) gestureEvent.preDrags = event.preEvents.map(_convertEvent);
|
||||||
if (event.event && event.event.deltaY !== undefined) gestureEvent.wheelDelta = event.event.deltaY;
|
if (event.event && event.event.deltaY !== undefined) {
|
||||||
|
gestureEvent.wheelDelta = normalizeWheel(event.event).pixelY;
|
||||||
|
}
|
||||||
|
|
||||||
this.onEvent(gestureEvent);
|
this.onEvent(gestureEvent);
|
||||||
};
|
};
|
||||||
|
71
src/d3/RenderChain.js
vendored
71
src/d3/RenderChain.js
vendored
@ -1,43 +1,62 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import OutlinePass from './OutlinePass.js';
|
import OutlinePass from './effects/OutlinePass.js';
|
||||||
import RenderPass from './RenderPass.js';
|
import RenderPass from './effects/RenderPass.js';
|
||||||
|
import AnaglyphPass from './effects/AnaglyphPass.js';
|
||||||
import 'three/examples/js/shaders/CopyShader.js';
|
import 'three/examples/js/shaders/CopyShader.js';
|
||||||
import 'three/examples/js/postprocessing/EffectComposer.js';
|
import 'three/examples/js/postprocessing/EffectComposer.js';
|
||||||
import 'three/examples/js/postprocessing/ShaderPass.js';
|
import 'three/examples/js/postprocessing/ShaderPass.js';
|
||||||
|
|
||||||
|
export const TOONSHADER_OUTLINE = 'toonshader-outline';
|
||||||
|
export const ANAGLYPH = 'anaglyph';
|
||||||
|
export const TOONSHADER = 'toonshader';
|
||||||
|
|
||||||
export default class RenderChain extends THREE.EffectComposer {
|
export default class RenderChain extends THREE.EffectComposer {
|
||||||
constructor(renderer, scene, camera, toonShader, groups) {
|
constructor(renderer, scene, camera, shader, groups) {
|
||||||
super(renderer);
|
super(renderer);
|
||||||
this._groups = groups;
|
this._groups = groups;
|
||||||
|
|
||||||
if (toonShader) {
|
switch (shader) {
|
||||||
const renderPass = new RenderPass(scene, camera, () => {
|
case TOONSHADER_OUTLINE: {
|
||||||
this._setVisible(this._initalValues, [groups.shapes, groups.plane, groups.boundingBox]);
|
const renderPass = new RenderPass(scene, camera, () => {
|
||||||
});
|
this._setVisible(this._initalValues, [groups.shapes, groups.plane, groups.boundingBox]);
|
||||||
this.addPass(renderPass);
|
});
|
||||||
|
this.addPass(renderPass);
|
||||||
|
|
||||||
const outlinePass = new OutlinePass(scene, camera, () => {
|
const outlinePass = new OutlinePass(scene, camera, () => {
|
||||||
this._setVisible(this._initalValues, [groups.shapes]);
|
this._setVisible(this._initalValues, [groups.shapes]);
|
||||||
});
|
});
|
||||||
outlinePass.renderToScreen = true;
|
outlinePass.renderToScreen = true;
|
||||||
this.addPass(outlinePass);
|
this.addPass(outlinePass);
|
||||||
|
|
||||||
const renderPassUI = new RenderPass(scene, camera, () => {
|
const renderPassUI = new RenderPass(scene, camera, () => {
|
||||||
this._setVisible(this._initalValues, [groups.UI]);
|
this._setVisible(this._initalValues, [groups.UI]);
|
||||||
});
|
});
|
||||||
renderPassUI.clear = false;
|
renderPassUI.clear = false;
|
||||||
renderPassUI.renderToScreen = true;
|
renderPassUI.renderToScreen = true;
|
||||||
this.addPass(renderPassUI);
|
this.addPass(renderPassUI);
|
||||||
} else {
|
break;
|
||||||
const renderPass = new RenderPass(scene, camera);
|
}
|
||||||
renderPass.renderToScreen = true;
|
|
||||||
this.addPass(renderPass);
|
case ANAGLYPH: {
|
||||||
|
const anaglyphPass = new AnaglyphPass(scene, camera);
|
||||||
|
anaglyphPass.renderToScreen = true;
|
||||||
|
this.addPass(anaglyphPass);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TOONSHADER:
|
||||||
|
default: {
|
||||||
|
const renderPass = new RenderPass(scene, camera);
|
||||||
|
renderPass.renderToScreen = true;
|
||||||
|
this.addPass(renderPass);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._renderer = renderer;
|
this._renderer = renderer;
|
||||||
this._camera = camera;
|
this._camera = camera;
|
||||||
this._scene = scene;
|
this._scene = scene;
|
||||||
this._toonShader = toonShader;
|
this._shader = shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getCurrentVisibleValues() {
|
_getCurrentVisibleValues() {
|
||||||
@ -74,13 +93,13 @@ export default class RenderChain extends THREE.EffectComposer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this._toonShader) {
|
if (this._shader === TOONSHADER_OUTLINE) {
|
||||||
this._initalValues = this._getCurrentVisibleValues();
|
this._initalValues = this._getCurrentVisibleValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
super.render();
|
super.render();
|
||||||
|
|
||||||
if (this._toonShader) {
|
if (this._shader === TOONSHADER_OUTLINE) {
|
||||||
const { shapes, UI, plane, boundingBox } = this._groups;
|
const { shapes, UI, plane, boundingBox } = this._groups;
|
||||||
this._setVisible(this._initalValues, [shapes, UI, plane, boundingBox]);
|
this._setVisible(this._initalValues, [shapes, UI, plane, boundingBox]);
|
||||||
}
|
}
|
||||||
|
2
src/d3/ShapeMesh.js
vendored
2
src/d3/ShapeMesh.js
vendored
@ -21,7 +21,7 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
super();
|
super();
|
||||||
this.name = shapeData.UID;
|
this.name = shapeData.UID;
|
||||||
|
|
||||||
const { sculpt, rotate, twist, height, type, transform, z, color, fill, solid } = shapeData;
|
const { sculpt, rotate, twist, height, type, transform, z, color, solid } = shapeData;
|
||||||
|
|
||||||
const material = new MatcapMaterial({ color: new THREE.Color(color) });
|
const material = new MatcapMaterial({ color: new THREE.Color(color) });
|
||||||
|
|
||||||
|
7
src/d3/createScene.js
vendored
7
src/d3/createScene.js
vendored
@ -1,6 +1,6 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import ShapesManager from './ShapesManager.js';
|
import ShapesManager from './ShapesManager.js';
|
||||||
import RenderChain from './RenderChain.js';
|
import RenderChain, { TOONSHADER, TOONSHADER_OUTLINE } from './RenderChain.js';
|
||||||
import { hasExtensionsFor } from '../utils/webGLSupport.js';
|
import { hasExtensionsFor } from '../utils/webGLSupport.js';
|
||||||
import { CANVAS_SIZE } from '../constants/d2Constants.js';
|
import { CANVAS_SIZE } from '../constants/d2Constants.js';
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ export default function createScene(state, canvas) {
|
|||||||
|
|
||||||
scene.add(camera);
|
scene.add(camera);
|
||||||
|
|
||||||
const shapesManager = new ShapesManager({ toonShader: hasExtensionsFor.toonShaderThumbnail });
|
const shapesManager = new ShapesManager();
|
||||||
shapesManager.update(state);
|
shapesManager.update(state);
|
||||||
|
|
||||||
scene.add(shapesManager);
|
scene.add(shapesManager);
|
||||||
@ -37,7 +37,8 @@ export default function createScene(state, canvas) {
|
|||||||
|
|
||||||
const renderer = new THREE.WebGLRenderer({ canvas, alpha: true });
|
const renderer = new THREE.WebGLRenderer({ canvas, alpha: true });
|
||||||
|
|
||||||
const renderChain = new RenderChain(renderer, scene, camera, hasExtensionsFor.toonShaderThumbnail, {
|
const shader = hasExtensionsFor.toonShaderThumbnail ? TOONSHADER_OUTLINE : TOONSHADER;
|
||||||
|
const renderChain = new RenderChain(renderer, scene, camera, shader, {
|
||||||
plane,
|
plane,
|
||||||
UI: new THREE.Object3D(),
|
UI: new THREE.Object3D(),
|
||||||
shapes: shapesManager,
|
shapes: shapesManager,
|
||||||
|
66
src/d3/effects/AnaglyphPass.js
vendored
Normal file
66
src/d3/effects/AnaglyphPass.js
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import * as THREE from 'three';
|
||||||
|
import anaglyphVert from '../../../shaders/anaglyph_vert.glsl';
|
||||||
|
import anaglyphFrag from '../../../shaders/anaglyph_frag.glsl';
|
||||||
|
|
||||||
|
const COLOR_MATRIX_LEFT = new THREE.Matrix3().fromArray([
|
||||||
|
1.0671679973602295, -0.0016435992438346148, 0.0001777536963345483,
|
||||||
|
-0.028107794001698494, -0.00019593400065787137, -0.0002875397040043026,
|
||||||
|
-0.04279090091586113, 0.000015809757314855233, -0.00024287120322696865
|
||||||
|
]);
|
||||||
|
const COLOR_MATRIX_RIGHT = new THREE.Matrix3().fromArray([
|
||||||
|
-0.0355340838432312, -0.06440307199954987, 0.018319187685847282,
|
||||||
|
-0.10269022732973099, 0.8079727292060852, -0.04835830628871918,
|
||||||
|
0.0001224992738571018, -0.009558862075209618, 0.567823588848114
|
||||||
|
]);
|
||||||
|
|
||||||
|
export default class AnaglyphPass {
|
||||||
|
constructor(scene, camera) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.camera = camera;
|
||||||
|
|
||||||
|
this.clear = true;
|
||||||
|
this.renderToScreen = false;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
minFilter: THREE.LinearFilter,
|
||||||
|
magFilter: THREE.NearestFilter,
|
||||||
|
format: THREE.RGBAFormat
|
||||||
|
};
|
||||||
|
|
||||||
|
this._stereo = new THREE.StereoCamera();
|
||||||
|
|
||||||
|
this._renderTargetL = new THREE.WebGLRenderTarget(1, 1, params);
|
||||||
|
this._renderTargetR = new THREE.WebGLRenderTarget(1, 1, params);
|
||||||
|
|
||||||
|
this._material = new THREE.ShaderMaterial({
|
||||||
|
uniforms: {
|
||||||
|
mapLeft: { value: this._renderTargetL.texture },
|
||||||
|
mapRight: { value: this._renderTargetR.texture },
|
||||||
|
colorMatrixLeft: { value: COLOR_MATRIX_LEFT },
|
||||||
|
colorMatrixRight: { value: COLOR_MATRIX_RIGHT }
|
||||||
|
},
|
||||||
|
vertexShader: anaglyphVert,
|
||||||
|
fragmentShader: anaglyphFrag
|
||||||
|
});
|
||||||
|
|
||||||
|
this._camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
||||||
|
this._scene = new THREE.Scene();
|
||||||
|
this._quad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), this._material);
|
||||||
|
this._quad.frustumCulled = false;
|
||||||
|
this._scene.add(this._quad);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSize(width, height, pixelRatio = 1) {
|
||||||
|
this._renderTargetL.setSize(width * pixelRatio, height * pixelRatio);
|
||||||
|
this._renderTargetR.setSize(width * pixelRatio, height * pixelRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(renderer, writeBuffer, readBuffer) {
|
||||||
|
this.scene.updateMatrixWorld();
|
||||||
|
this._stereo.update(this.camera);
|
||||||
|
|
||||||
|
renderer.render(this.scene, this._stereo.cameraL, this._renderTargetL, true);
|
||||||
|
renderer.render(this.scene, this._stereo.cameraR, this._renderTargetR, true);
|
||||||
|
renderer.render(this._scene, this._camera, this.renderToScreen ? null : readBuffer, this.clear);
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import normalDepthVert from '../../shaders/normal_depth_vert.glsl';
|
import normalDepthVert from '../../../shaders/normal_depth_vert.glsl';
|
||||||
import normalDepthFrag from '../../shaders/normal_depth_frag.glsl';
|
import normalDepthFrag from '../../../shaders/normal_depth_frag.glsl';
|
||||||
import edgeVert from '../../shaders/edge_vert.glsl';
|
import edgeVert from '../../../shaders/edge_vert.glsl';
|
||||||
import edgeFrag from '../../shaders/edge_frag.glsl';
|
import edgeFrag from '../../../shaders/edge_frag.glsl';
|
||||||
import combineVert from '../../shaders/combine_vert.glsl';
|
import combineVert from '../../../shaders/combine_vert.glsl';
|
||||||
import combineFrag from '../../shaders/combine_frag.glsl';
|
import combineFrag from '../../../shaders/combine_frag.glsl';
|
||||||
|
|
||||||
export default class OutlinePass {
|
export default class OutlinePass {
|
||||||
constructor(scene, camera, callbackBeforeRender) {
|
constructor(scene, camera, callbackBeforeRender) {
|
@ -1,5 +1,3 @@
|
|||||||
import * as THREE from 'three';
|
|
||||||
|
|
||||||
export default class RenderPass {
|
export default class RenderPass {
|
||||||
constructor(scene, camera, callbackBeforeRender) {
|
constructor(scene, camera, callbackBeforeRender) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
@ -10,10 +8,10 @@ export default class RenderPass {
|
|||||||
this.renderToScreen = false;
|
this.renderToScreen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSize(width, height) {
|
setSize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
render(renderer, writeBuffer, readBuffer, delta, maskActive) {
|
render(renderer, writeBuffer, readBuffer) {
|
||||||
if (this._callbackBeforeRender) this._callbackBeforeRender();
|
if (this._callbackBeforeRender) this._callbackBeforeRender();
|
||||||
|
|
||||||
let oldAutoClear;
|
let oldAutoClear;
|
2
src/d3/transformers/BaseTransformer.js
vendored
2
src/d3/transformers/BaseTransformer.js
vendored
@ -2,8 +2,6 @@ import * as THREE from 'three';
|
|||||||
import * as actions from '../../actions/index.js';
|
import * as actions from '../../actions/index.js';
|
||||||
import { EventObject3D } from '../EventScene.js';
|
import { EventObject3D } from '../EventScene.js';
|
||||||
import transposeEvents from '../../utils/transposeEvents.js';
|
import transposeEvents from '../../utils/transposeEvents.js';
|
||||||
import ShapeMesh from '../ShapeMesh.js';
|
|
||||||
import { SpriteHandle } from '../../utils/threeUtils.js';
|
|
||||||
|
|
||||||
const RAY_CASTER = new THREE.Raycaster();
|
const RAY_CASTER = new THREE.Raycaster();
|
||||||
const MOUSE = new THREE.Vector2();
|
const MOUSE = new THREE.Vector2();
|
||||||
|
1
src/d3/transformers/StampTransformer.js
vendored
1
src/d3/transformers/StampTransformer.js
vendored
@ -1,5 +1,4 @@
|
|||||||
import { CANVAS_SIZE } from '../../constants/d2Constants';
|
import { CANVAS_SIZE } from '../../constants/d2Constants';
|
||||||
import ShapeMesh from '../ShapeMesh.js';
|
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import BaseTransformer from './BaseTransformer.js';
|
import BaseTransformer from './BaseTransformer.js';
|
||||||
import * as actions from '../../actions/index.js';
|
import * as actions from '../../actions/index.js';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import update from 'react-addons-update';
|
import update from 'react-addons-update';
|
||||||
import * as contextTools from '../constants/contextTools.js';
|
import * as contextTools from '../constants/contextTools.js';
|
||||||
import { COLOR_STRING_TO_HEX } from '../constants/general.js';
|
import { COLOR_STRING_TO_HEX, FONT_FACE } from '../constants/general.js';
|
||||||
import { ERASER_SIZES, BRUSH_SIZES } from '../constants/d2Constants.js';
|
import { ERASER_SIZES, BRUSH_SIZES } from '../constants/d2Constants.js';
|
||||||
import { SHAPE_TYPE_PROPERTIES } from '../constants/shapeTypeProperties.js';
|
import { SHAPE_TYPE_PROPERTIES } from '../constants/shapeTypeProperties.js';
|
||||||
import * as actions from '../actions/index.js';
|
import * as actions from '../actions/index.js';
|
||||||
@ -69,6 +69,33 @@ export default function (state, action) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case contextTools.OSWALD:
|
||||||
|
case contextTools.RANGA:
|
||||||
|
case contextTools.JOTI_ONE:
|
||||||
|
case contextTools.BELLEFAIR:
|
||||||
|
case contextTools.LOBSTER:
|
||||||
|
case contextTools.ABRIL_FATFACE:
|
||||||
|
case contextTools.PLAY:
|
||||||
|
case contextTools.FASCINATE: {
|
||||||
|
const family = FONT_FACE[action.tool];
|
||||||
|
const { activeShape } = state.d2;
|
||||||
|
if (activeShape && state.objectsById[activeShape].type === 'TEXT') {
|
||||||
|
state = update(state, { objectsById: { [activeShape]: { text: { family: { $set: family } } } } });
|
||||||
|
}
|
||||||
|
|
||||||
|
return update(state, {
|
||||||
|
objectsById: state.selection.objects.reduce((updateObject, { id }) => {
|
||||||
|
if (state.objectsById[id].type === 'TEXT') {
|
||||||
|
updateObject[id] = { text: { family: { $set: FONT_FACE[action.tool] } } };
|
||||||
|
}
|
||||||
|
return updateObject;
|
||||||
|
}, {}),
|
||||||
|
context: {
|
||||||
|
font: { $set: FONT_FACE[action.tool] }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case contextTools.LIGHT_BLUE_A:
|
case contextTools.LIGHT_BLUE_A:
|
||||||
case contextTools.LIGHT_BLUE_B:
|
case contextTools.LIGHT_BLUE_B:
|
||||||
case contextTools.LIGHT_BLUE_C:
|
case contextTools.LIGHT_BLUE_C:
|
||||||
@ -94,6 +121,11 @@ export default function (state, action) {
|
|||||||
case contextTools.BLACK_B:
|
case contextTools.BLACK_B:
|
||||||
case contextTools.BLACK_C: {
|
case contextTools.BLACK_C: {
|
||||||
const color = COLOR_STRING_TO_HEX[action.tool];
|
const color = COLOR_STRING_TO_HEX[action.tool];
|
||||||
|
const { activeShape } = state.d2;
|
||||||
|
if (activeShape) {
|
||||||
|
state = update(state, { objectsById: { [activeShape]: { color: { $set: color } } } });
|
||||||
|
}
|
||||||
|
|
||||||
return updateColor(state, color);
|
return updateColor(state, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import update from 'react-addons-update';
|
import update from 'react-addons-update';
|
||||||
import { Matrix } from 'cal';
|
|
||||||
import constrainMatrix from './constrainMatrix.js';
|
import constrainMatrix from './constrainMatrix.js';
|
||||||
import { calculateGestureMatrix } from '../../utils/matrixUtils.js';
|
import { calculateGestureMatrix } from '../../utils/matrixUtils.js';
|
||||||
import * as actions from '../../actions/index.js';
|
import * as actions from '../../actions/index.js';
|
||||||
|
@ -8,7 +8,7 @@ import starReducer from './tools/shapes/starReducer.js';
|
|||||||
import triangleReducer from './tools/shapes/triangleReducer.js';
|
import triangleReducer from './tools/shapes/triangleReducer.js';
|
||||||
import bucketReducer from './tools/bucketReducer.js';
|
import bucketReducer from './tools/bucketReducer.js';
|
||||||
import penReducer from './tools/penReducer.js';
|
import penReducer from './tools/penReducer.js';
|
||||||
import textReducer from './tools/textReducer.js';
|
import textReducer, { removeEmptyText } from './tools/textReducer.js';
|
||||||
import photoGuideReducer from './tools/photoGuideReducer.js';
|
import photoGuideReducer from './tools/photoGuideReducer.js';
|
||||||
import { transformReducer } from './tools/transformReducer.js';
|
import { transformReducer } from './tools/transformReducer.js';
|
||||||
import eraserReducer from './tools/eraserReducer.js';
|
import eraserReducer from './tools/eraserReducer.js';
|
||||||
@ -44,6 +44,7 @@ export default function toolReducer(state, action) {
|
|||||||
// change 2D tool after explicit tool change action or on some selection
|
// change 2D tool after explicit tool change action or on some selection
|
||||||
if (action.type === actions.D2_CHANGE_TOOL) {
|
if (action.type === actions.D2_CHANGE_TOOL) {
|
||||||
state = updateTool(state, action.tool);
|
state = updateTool(state, action.tool);
|
||||||
|
state = removeEmptyText(state);
|
||||||
}
|
}
|
||||||
if (action.category === actions.CAT_SELECTION) {
|
if (action.category === actions.CAT_SELECTION) {
|
||||||
state = updateTool(state, tools.TRANSFORM);
|
state = updateTool(state, tools.TRANSFORM);
|
||||||
|
@ -31,5 +31,8 @@ function handleEraser(state, path, screenMatrixZoom) {
|
|||||||
roundPrecision: 0.25
|
roundPrecision: 0.25
|
||||||
});
|
});
|
||||||
|
|
||||||
return subtractShapeFromState(state, eraserShape, d2Tools.ERASER, { matrix: screenMatrixZoom, scale: CLIPPER_PRECISION });
|
return subtractShapeFromState(state, eraserShape, d2Tools.ERASER, {
|
||||||
|
matrix: screenMatrixZoom,
|
||||||
|
scale: CLIPPER_PRECISION
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { updateColor } from '../../selectionReducer.js';
|
import { updateColor } from '../../selectionReducer.js';
|
||||||
import { getColor } from '../../../utils/objectSelectors.js';
|
import { getColor } from '../../../utils/objectSelectors.js';
|
||||||
import { select } from '../../menusReducer.js';
|
|
||||||
import update from 'react-addons-update';
|
|
||||||
import { updateTool as updateTool2d } from '../toolReducer.js';
|
import { updateTool as updateTool2d } from '../toolReducer.js';
|
||||||
|
|
||||||
export default function pipetteReducer(state, action) {
|
export default function pipetteReducer(state, action) {
|
||||||
|
@ -8,10 +8,10 @@ const debug = createDebug('d3d:reducer:text');
|
|||||||
export default function textReducer(state, action) {
|
export default function textReducer(state, action) {
|
||||||
if (action.log !== false) debug(action.type);
|
if (action.log !== false) debug(action.type);
|
||||||
|
|
||||||
const activeShape = state.d2.activeShape;
|
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case actions.D2_TEXT_INIT: {
|
case actions.D2_TEXT_INIT: {
|
||||||
|
state = removeEmptyText(state);
|
||||||
|
|
||||||
const { position, textId, screenMatrixZoom } = action;
|
const { position, textId, screenMatrixZoom } = action;
|
||||||
const screenPosition = (position && screenMatrixZoom) ?
|
const screenPosition = (position && screenMatrixZoom) ?
|
||||||
position.applyMatrix(screenMatrixZoom.inverseMatrix()) :
|
position.applyMatrix(screenMatrixZoom.inverseMatrix()) :
|
||||||
@ -22,37 +22,40 @@ export default function textReducer(state, action) {
|
|||||||
} else {
|
} else {
|
||||||
return addObjectActive2D(state, {
|
return addObjectActive2D(state, {
|
||||||
transform: new Matrix({ x: screenPosition.x, y: screenPosition.y }),
|
transform: new Matrix({ x: screenPosition.x, y: screenPosition.y }),
|
||||||
type: 'TEXT'
|
type: 'TEXT',
|
||||||
|
text: {
|
||||||
|
text: '',
|
||||||
|
family: state.context.font,
|
||||||
|
weight: 'normal',
|
||||||
|
style: 'normal'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
case actions.D2_TEXT_INPUT_CHANGE: {
|
case actions.D2_TEXT_INPUT_CHANGE: {
|
||||||
const { text, family, style, weight, fill } = action;
|
const { text } = action;
|
||||||
|
const { activeShape } = state.d2;
|
||||||
return update(state, {
|
return update(state, {
|
||||||
objectsById: {
|
objectsById: {
|
||||||
[activeShape]: {
|
[activeShape]: {
|
||||||
text: {
|
text: {
|
||||||
text: { $set: text },
|
text: { $set: text }
|
||||||
family: { $set: family },
|
}
|
||||||
style: { $set: style },
|
|
||||||
weight: { $set: weight }
|
|
||||||
},
|
|
||||||
fill: { $set: fill }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
case actions.D2_TEXT_ADD: {
|
|
||||||
if (activeShape && state.objectsById[activeShape].text.text.length === 0) {
|
|
||||||
return setActive2D(removeObject(state, activeShape), null);
|
|
||||||
} else {
|
|
||||||
return setActive2D(state, null);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeEmptyText(state) {
|
||||||
|
const { activeShape } = state.d2;
|
||||||
|
|
||||||
|
if (activeShape && state.objectsById[activeShape].text.text === '') {
|
||||||
|
return setActive2D(removeObject(state, activeShape), null);
|
||||||
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import undoFilter from '../utils/undoFilter.js';
|
|||||||
import * as actions from '../actions/index.js';
|
import * as actions from '../actions/index.js';
|
||||||
import * as d2Tools from '../constants/d2Tools.js';
|
import * as d2Tools from '../constants/d2Tools.js';
|
||||||
import * as d3Tools from '../constants/d3Tools.js';
|
import * as d3Tools from '../constants/d3Tools.js';
|
||||||
import { COLOR_STRING_TO_HEX } from '../constants/general.js';
|
import { COLOR_STRING_TO_HEX, FONT_FACE } from '../constants/general.js';
|
||||||
import * as contextTools from '../constants/contextTools.js';
|
import * as contextTools from '../constants/contextTools.js';
|
||||||
import { ERASER_SIZES, BRUSH_SIZES } from '../constants/d2Constants.js';
|
import { ERASER_SIZES, BRUSH_SIZES } from '../constants/d2Constants.js';
|
||||||
import update from 'react-addons-update';
|
import update from 'react-addons-update';
|
||||||
@ -36,7 +36,8 @@ const initialState = {
|
|||||||
objectIdCounter: 0,
|
objectIdCounter: 0,
|
||||||
context: {
|
context: {
|
||||||
solid: true,
|
solid: true,
|
||||||
color: COLOR_STRING_TO_HEX[contextTools.LIGHT_BLUE_B]
|
color: COLOR_STRING_TO_HEX[contextTools.LIGHT_BLUE_B],
|
||||||
|
font: FONT_FACE[contextTools.OSWALD]
|
||||||
},
|
},
|
||||||
selection: {
|
selection: {
|
||||||
transform: new Matrix(),
|
transform: new Matrix(),
|
||||||
@ -142,7 +143,6 @@ function sketcherReducer(state = initialState, action) {
|
|||||||
case actions.MULTITOUCH_TRANSFORM_END:
|
case actions.MULTITOUCH_TRANSFORM_END:
|
||||||
case actions.D2_TEXT_INIT:
|
case actions.D2_TEXT_INIT:
|
||||||
case actions.D2_TEXT_INPUT_CHANGE:
|
case actions.D2_TEXT_INPUT_CHANGE:
|
||||||
case actions.D2_TEXT_ADD:
|
|
||||||
case actions.MOVE_SELECTION:
|
case actions.MOVE_SELECTION:
|
||||||
return d2ToolReducer(state, action);
|
return d2ToolReducer(state, action);
|
||||||
|
|
||||||
@ -188,11 +188,11 @@ function sketcherReducer(state = initialState, action) {
|
|||||||
return d3ToolReducer(state, action);
|
return d3ToolReducer(state, action);
|
||||||
|
|
||||||
case actions.D2_CHANGE_TOOL:
|
case actions.D2_CHANGE_TOOL:
|
||||||
state = setActive2D(state, null);
|
|
||||||
state = selectionReducer(state, action);
|
state = selectionReducer(state, action);
|
||||||
state = d2ToolReducer(state, action); // switch and initialize tool
|
state = d2ToolReducer(state, action); // switch and initialize tool
|
||||||
state = updateMenus(state, action);
|
state = updateMenus(state, action);
|
||||||
state = contextReducer(state, action);
|
state = contextReducer(state, action);
|
||||||
|
state = setActive2D(state, null);
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
case actions.D3_CHANGE_TOOL:
|
case actions.D3_CHANGE_TOOL:
|
||||||
|
@ -8,6 +8,7 @@ import { MAX_ANGLE } from '../constants/d3Constants.js';
|
|||||||
import { SHAPE_CACHE_LIMIT } from '../constants/general.js';
|
import { SHAPE_CACHE_LIMIT } from '../constants/general.js';
|
||||||
import { createText } from '../utils/textUtils.js';
|
import { createText } from '../utils/textUtils.js';
|
||||||
import { segmentBezierPath } from '../utils/curveUtils.js';
|
import { segmentBezierPath } from '../utils/curveUtils.js';
|
||||||
|
import { TEXT_TOOL_FONT_SIZE } from '../constants/d2Constants.js';
|
||||||
|
|
||||||
const setDirection = (clockwise) => (path) => {
|
const setDirection = (clockwise) => (path) => {
|
||||||
return (THREE.ShapeUtils.isClockWise(path) === clockwise) ? path : path.reverse();
|
return (THREE.ShapeUtils.isClockWise(path) === clockwise) ? path : path.reverse();
|
||||||
@ -64,7 +65,7 @@ function shapeToPointsRaw(shapeData) {
|
|||||||
const points = [];
|
const points = [];
|
||||||
let even = false;
|
let even = false;
|
||||||
const numLines = rays * 2;
|
const numLines = rays * 2;
|
||||||
for (let i = 0, rad = 0; i <= numLines; i++, rad += Math.PI / rays) {
|
for (let i = 0, rad = 0; i <= numLines; i ++, rad += Math.PI / rays, even = !even) {
|
||||||
if (i === numLines) { // last line?
|
if (i === numLines) { // last line?
|
||||||
points.push(points[0].clone()); // go to first point
|
points.push(points[0].clone()); // go to first point
|
||||||
} else {
|
} else {
|
||||||
@ -72,7 +73,6 @@ function shapeToPointsRaw(shapeData) {
|
|||||||
let x = Math.sin(rad) * radius;
|
let x = Math.sin(rad) * radius;
|
||||||
let y = -Math.cos(rad) * radius;
|
let y = -Math.cos(rad) * radius;
|
||||||
points.push(new Vector(x, y));
|
points.push(new Vector(x, y));
|
||||||
even = !even;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shapes.push({ points, holes: [] });
|
shapes.push({ points, holes: [] });
|
||||||
@ -120,7 +120,7 @@ function shapeToPointsRaw(shapeData) {
|
|||||||
}
|
}
|
||||||
case 'TEXT': {
|
case 'TEXT': {
|
||||||
const { text, family, style, weight } = shapeData.text;
|
const { text, family, style, weight } = shapeData.text;
|
||||||
const textShapes = createText(text, 400, family, style, weight)
|
const textShapes = createText(text, TEXT_TOOL_FONT_SIZE, 10, family, style, weight)
|
||||||
.map(([points, ...holes]) => ({ points, holes }));
|
.map(([points, ...holes]) => ({ points, holes }));
|
||||||
|
|
||||||
shapes.push(...textShapes);
|
shapes.push(...textShapes);
|
||||||
|
@ -9,19 +9,17 @@ import memoize from 'memoizee';
|
|||||||
const MARGIN = 200;
|
const MARGIN = 200;
|
||||||
|
|
||||||
export const createText = memoize(createTextRaw, { max: SHAPE_CACHE_LIMIT });
|
export const createText = memoize(createTextRaw, { max: SHAPE_CACHE_LIMIT });
|
||||||
export function createTextRaw(text, size, family, style, weight) {
|
export function createTextRaw(text, size, precision, family, style, weight) {
|
||||||
if (text === '') return [];
|
if (text === '') return [];
|
||||||
|
|
||||||
const { width, height, canvas } = createTextCanvas(text, size, family, style, weight);
|
const canvas = createTextCanvas(text, size * precision, family, style, weight);
|
||||||
|
|
||||||
// TODO merge with potrace in flood fill trace reducer
|
// TODO merge with potrace in flood fill trace reducer
|
||||||
const paths = POTRACE.getPaths(POTRACE.traceCanvas(canvas, POTRACE_OPTIONS));
|
const paths = POTRACE.getPaths(POTRACE.traceCanvas(canvas, POTRACE_OPTIONS));
|
||||||
|
|
||||||
const halfWidth = width / 2;
|
|
||||||
const halfHeight = height / 2;
|
|
||||||
const pathsOffset = paths.map(path => path.map(({ x, y }) => ({
|
const pathsOffset = paths.map(path => path.map(({ x, y }) => ({
|
||||||
x: (x - halfWidth) / 10,
|
x: (x - MARGIN) / precision,
|
||||||
y: (y - halfHeight) / 10
|
y: (y - MARGIN) / precision
|
||||||
})));
|
})));
|
||||||
|
|
||||||
const shapes = new ClipperShape(pathsOffset, true, true, false)
|
const shapes = new ClipperShape(pathsOffset, true, true, false)
|
||||||
@ -33,7 +31,7 @@ export function createTextRaw(text, size, family, style, weight) {
|
|||||||
return shapes;
|
return shapes;
|
||||||
}
|
}
|
||||||
|
|
||||||
const textContext = new Text();
|
const textContext = new Text({ baseline: 'top' });
|
||||||
export function createTextCanvas(text, size, family, style, weight) {
|
export function createTextCanvas(text, size, family, style, weight) {
|
||||||
textContext.size = size;
|
textContext.size = size;
|
||||||
textContext.family = family;
|
textContext.family = family;
|
||||||
@ -52,7 +50,7 @@ export function createTextCanvas(text, size, family, style, weight) {
|
|||||||
context.fillStyle = 'white';
|
context.fillStyle = 'white';
|
||||||
context.fillRect(0, 0, width, height);
|
context.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
textContext.drawText(context, text, MARGIN, height / 2);
|
textContext.drawText(context, text, MARGIN, MARGIN);
|
||||||
|
|
||||||
return { width, height, canvas };
|
return canvas;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ const INCLUDE = [
|
|||||||
actions.DESELECT_ALL,
|
actions.DESELECT_ALL,
|
||||||
actions.STAMP,
|
actions.STAMP,
|
||||||
actions.SELECT,
|
actions.SELECT,
|
||||||
actions.D2_TEXT_ADD,
|
|
||||||
actions.CLEAR,
|
actions.CLEAR,
|
||||||
actions.TWIST_END,
|
actions.TWIST_END,
|
||||||
actions.SCULPT_END,
|
actions.SCULPT_END,
|
||||||
|
@ -249,6 +249,24 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#font-tool > .button {
|
||||||
|
background-image: url('../img/contextmenu/btnFont.png');
|
||||||
|
background-size: 40px auto;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#font-tool .menu {
|
||||||
|
flex-direction: column;
|
||||||
|
max-height: 170px;
|
||||||
|
width: 120px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#font-tool .menu .menuitem {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
#color-light-blue-a { fill: #BCFFFF; }
|
#color-light-blue-a { fill: #BCFFFF; }
|
||||||
#color-light-blue-b { fill: #68E1FD; }
|
#color-light-blue-b { fill: #68E1FD; }
|
||||||
#color-light-blue-c { fill: #01B8FF; }
|
#color-light-blue-c { fill: #01B8FF; }
|
||||||
|
@ -3,6 +3,7 @@ const path = require('path');
|
|||||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||||
const HTMLWebpackPlugin = require('html-webpack-plugin');
|
const HTMLWebpackPlugin = require('html-webpack-plugin');
|
||||||
const CordovaPlugin = require('webpack-cordova-plugin');
|
const CordovaPlugin = require('webpack-cordova-plugin');
|
||||||
|
const GoogleFontsPlugin = require('google-fonts-webpack-plugin');
|
||||||
|
|
||||||
const devMode = process.env.NODE_ENV !== 'production';
|
const devMode = process.env.NODE_ENV !== 'production';
|
||||||
const appMode = process.env.TARGET === 'app';
|
const appMode = process.env.TARGET === 'app';
|
||||||
@ -75,9 +76,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
'process.env': {
|
'process.env.TARGET': JSON.stringify(process.env.TARGET)
|
||||||
'TARGET': JSON.stringify(process.env.TARGET)
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
new HTMLWebpackPlugin({
|
new HTMLWebpackPlugin({
|
||||||
title: 'Doodle3D Core - Simple example',
|
title: 'Doodle3D Core - Simple example',
|
||||||
@ -86,6 +85,18 @@ module.exports = {
|
|||||||
scripts: appMode ? ['cordova.js'] : null,
|
scripts: appMode ? ['cordova.js'] : null,
|
||||||
appMountId: 'app'
|
appMountId: 'app'
|
||||||
}),
|
}),
|
||||||
|
new GoogleFontsPlugin({
|
||||||
|
fonts: [
|
||||||
|
{ family: 'Oswald' },
|
||||||
|
{ family: 'Ranga' },
|
||||||
|
{ family: 'Joti One' },
|
||||||
|
{ family: 'Bellefair' },
|
||||||
|
{ family: 'Lobster' },
|
||||||
|
{ family: 'Abril Fatface' },
|
||||||
|
{ family: 'Play' },
|
||||||
|
{ family: 'Fascinate' }
|
||||||
|
]
|
||||||
|
}),
|
||||||
...(appMode ? [
|
...(appMode ? [
|
||||||
new CordovaPlugin({
|
new CordovaPlugin({
|
||||||
config: 'config.xml',
|
config: 'config.xml',
|
||||||
@ -95,7 +106,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
] : [])
|
] : [])
|
||||||
],
|
],
|
||||||
devtool: "source-map",
|
devtool: 'source-map',
|
||||||
devServer: {
|
devServer: {
|
||||||
contentBase: 'dist'
|
contentBase: 'dist'
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user