diff --git a/img/contextmenu/btnFont.png b/img/contextmenu/btnFont.png
new file mode 100644
index 0000000..6cba1ab
Binary files /dev/null and b/img/contextmenu/btnFont.png differ
diff --git a/package-lock.json b/package-lock.json
index 4c1f9a2..9d2f26e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1932,6 +1932,11 @@
"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": {
"version": "0.1.1",
"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": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
@@ -5666,6 +5679,33 @@
"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": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/got/-/got-3.3.1.tgz",
@@ -8398,6 +8438,11 @@
"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": {
"version": "2.15.12",
"resolved": "https://registry.npmjs.org/npm/-/npm-2.15.12.tgz",
@@ -10550,6 +10595,11 @@
"integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=",
"dev": true
},
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
+ },
"pepjs": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/pepjs/-/pepjs-0.4.3.tgz",
@@ -11747,12 +11797,6 @@
"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": {
"version": "2.0.1",
"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": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/yml-loader/-/yml-loader-2.1.0.tgz",
diff --git a/package.json b/package.json
index 8604d69..d789a6f 100755
--- a/package.json
+++ b/package.json
@@ -27,11 +27,13 @@
"blueimp-canvas-to-blob": "^3.14.0",
"bowser": "^1.8.1",
"fit-curve": "^0.1.6",
+ "google-fonts-webpack-plugin": "^0.4.4",
"imports-loader": "^0.7.1",
"jss": "^9.4.0",
"keycode": "^2.1.9",
"lodash": "^4.17.4",
"memoizee": "^0.3.9",
+ "normalize-wheel": "^1.0.1",
"pouchdb": "^6.3.4",
"proptypes": "^1.1.0",
"raf": "^3.4.0",
@@ -82,7 +84,6 @@
"normalize-jss": "^4.0.0",
"raw-loader": "^0.5.1",
"react-dom": "^16.1.1",
- "react-router-redux": "^4.0.8",
"react-tap-event-plugin": "^3.0.2",
"redux": "^3.7.2",
"redux-action-wrapper": "^1.0.1",
diff --git a/shaders/anaglyph_frag.glsl b/shaders/anaglyph_frag.glsl
new file mode 100644
index 0000000..114e311
--- /dev/null
+++ b/shaders/anaglyph_frag.glsl
@@ -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 ) );
+
+}
diff --git a/shaders/anaglyph_vert.glsl b/shaders/anaglyph_vert.glsl
new file mode 100644
index 0000000..610d70b
--- /dev/null
+++ b/shaders/anaglyph_vert.glsl
@@ -0,0 +1,5 @@
+varying vec2 vUv;
+void main() {
+ vUv = vec2( uv.x, uv.y );
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
+}
diff --git a/src/actions/index.js b/src/actions/index.js
index 9c6469d..64285fe 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -1,6 +1,5 @@
import { ActionCreators as undo } from 'redux-undo';
import * as notification from 'react-notification-system-redux';
-import { routerActions as router } from 'react-router-redux';
import * as selectionUtils from '../utils/selectionUtils.js';
import { calculatePointInImage, decomposeMatrix } from '../utils/matrixUtils.js';
import { loadImage, prepareImage } from '../utils/imageUtils.js';
@@ -74,7 +73,6 @@ export const ALIGN = 'ALIGN';
export const ADD_IMAGE = 'ADD_IMAGE';
export const D2_TEXT_INIT = 'D2_TEXT_INIT';
export const D2_TEXT_INPUT_CHANGE = 'D2_TEXT_INPUT_CHANGE';
-export const D2_TEXT_ADD = 'D2_TEXT_ADD';
export const UNION = 'UNION';
export const INTERSECT = 'INTERSECT';
export const MOVE_SELECTION = 'MOVE_SELECTION';
@@ -390,14 +388,10 @@ export function addImage(file) {
export function d2textInit(position, textId, screenMatrixContainer, screenMatrixZoom) {
return (dispatch) => {
dispatch({ type: D2_TEXT_INIT, position, textId, screenMatrixContainer, screenMatrixZoom });
- dispatch(router.push('/sketch/inputtext'));
};
}
-export function d2textInputChange(text, family, weight, style, fill) {
- return { type: D2_TEXT_INPUT_CHANGE, text, family, weight, style, fill };
-}
-export function d2textAdd() {
- return { type: D2_TEXT_ADD };
+export function d2textInputChange(text) {
+ return { type: D2_TEXT_INPUT_CHANGE, text };
}
const traceDragThrottle = createThrottle();
diff --git a/src/components/D2Panel.js b/src/components/D2Panel.js
index 83ce0a6..cd9438d 100644
--- a/src/components/D2Panel.js
+++ b/src/components/D2Panel.js
@@ -23,6 +23,7 @@ import ShapesManager from '../d2/ShapesManager.js';
import EventGroup from '../d2/EventGroup.js';
import ReactResizeDetector from 'react-resize-detector';
import { load as loadPattern } from '../d2/Shape.js';
+import InputText from './InputText.js';
// import createDebug from 'debug';
// const debug = createDebug('d3d:d2');
@@ -63,6 +64,9 @@ class D2Panel extends React.Component {
dispatch: PropTypes.func.isRequired,
classes: PropTypes.objectOf(PropTypes.string)
};
+ state = {
+ screenMatrix: new CAL.Matrix()
+ };
activeNeedRender = false;
inactiveNeedRender = false;
@@ -117,7 +121,7 @@ class D2Panel extends React.Component {
}
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
if (this.tool) {
this.tool.destroy();
@@ -135,18 +139,18 @@ class D2Panel extends React.Component {
this.objectContainerActive.add(this.tool);
}
- update(newState) {
- if (this.state === newState) return;
+ update(state) {
+ 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.inactive) this.inactiveNeedRender = true;
// Update Objects Container Space with zoom & panning
- const newCanvasMatrix = newState.d2.canvasMatrix;
- if (this.state && newCanvasMatrix !== this.state.d2.canvasMatrix) {
+ const newCanvasMatrix = state.d2.canvasMatrix;
+ if (this.state.state && newCanvasMatrix !== this.state.state.d2.canvasMatrix) {
this.objectContainerActive.copyMatrix(newCanvasMatrix);
this.objectContainerInactive.copyMatrix(newCanvasMatrix);
@@ -154,9 +158,9 @@ class D2Panel extends React.Component {
this.inactiveNeedRender = true;
}
- const selection = (this.state) ? this.state.selection : null;
- const newSelection = newState.selection;
- if (!this.state || newSelection !== selection) {
+ const selection = (this.state.state) ? this.state.state.selection : null;
+ const newSelection = state.selection;
+ if (!this.state.state || newSelection !== selection) {
const newSelectedObjects = newSelection.objects;
if (!selection || selection.objects !== newSelectedObjects) {
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 newDragSelect = newState.d2.transform.dragSelect;
+ const dragSelect = (this.state.state) ? this.state.state.d2.transform.dragSelect : null;
+ const newDragSelect = state.d2.transform.dragSelect;
if (!dragSelect || dragSelect !== newDragSelect) {
this.activeNeedRender = true;
}
- this.state = newState;
+ this.setState({ state });
}
resizeHandler = (width, height) => {
this.sceneActive.setSize(width, height, PIXEL_RATIO);
this.sceneInactive.setSize(width, height, PIXEL_RATIO);
- this.sceneInactive.x = this.sceneActive.x = Math.round(width / 2 * PIXEL_RATIO);
- this.sceneInactive.y = this.sceneActive.y = Math.round(height / 2 * PIXEL_RATIO);
-
+ const x = Math.round(width / 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);
- 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.renderRequest();
@@ -210,12 +218,14 @@ class D2Panel extends React.Component {
render() {
// debug('this.props.state: ', this.props.state);
const { state, classes } = this.props;
+ const { screenMatrix } = this.state;
this.update(state);
this.renderCanvas();
return (
);
}
diff --git a/src/components/D3Panel.js b/src/components/D3Panel.js
index 52f18fc..2a043dd 100644
--- a/src/components/D3Panel.js
+++ b/src/components/D3Panel.js
@@ -16,7 +16,7 @@ import TwistTransformer from '../d3/transformers/TwistTransformer.js';
import SculptTransformer from '../d3/transformers/SculptTransformer.js';
import StampTransformer from '../d3/transformers/StampTransformer.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 Camera from '../d3/Camera.js';
import ReactResizeDetector from 'react-resize-detector';
@@ -62,8 +62,8 @@ class D3Panel extends React.Component {
componentWillMount() {
this.createScene();
- const toonShader = hasExtensionsFor.toonShaderPreview;
- this.renderChain = new RenderChain(this.renderer, this.scene, this.camera, toonShader, {
+ const shader = hasExtensionsFor.toonShaderPreview ? TOONSHADER_OUTLINE : TOONSHADER;
+ this.renderChain = new RenderChain(this.renderer, this.scene, this.camera, shader, {
UI: this.UIContainer,
shapes: this.shapesManager,
boundingBox: this.selectionBox,
diff --git a/src/components/DoodlePreview.js b/src/components/DoodlePreview.js
index 8003515..4d1af00 100644
--- a/src/components/DoodlePreview.js
+++ b/src/components/DoodlePreview.js
@@ -45,7 +45,7 @@ class DoodlePreview extends React.Component {
scene: null
};
- async componentDidMount() {
+ async componentWillMount() {
let { docData, sketchData } = this.props;
if (docData) sketchData = await JSONToSketchData(this.props.docData);
diff --git a/src/components/InputText.js b/src/components/InputText.js
new file mode 100644
index 0000000..73e74bd
--- /dev/null
+++ b/src/components/InputText.js
@@ -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 (
+
+
+
+ );
+ }
+ return null;
+ }
+}
+
+export default injectSheet(styles)(connect(state => ({
+ state: state.sketcher.present
+}), {
+ changeText: actions.d2textInputChange
+})(InputText));
diff --git a/src/components/Menu.js b/src/components/Menu.js
index 0b08362..3e1a6f7 100644
--- a/src/components/Menu.js
+++ b/src/components/Menu.js
@@ -1,7 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
-import initialMenuStructure from '../constants/menu.js';
-import { connect } from 'react-redux';
// import createDebug from 'debug';
// const debug = createDebug('d3d:menu');
@@ -14,7 +12,7 @@ class Menu extends React.Component {
value: PropTypes.string,
className: PropTypes.string,
children: PropTypes.node,
- stateMenu: PropTypes.object
+ id: PropTypes.string
};
onSelect = (event) => {
const { onSelect, value } = this.props;
@@ -23,7 +21,7 @@ class Menu extends React.Component {
if (onSelect) onSelect({ ...event, menuValue });
};
render() {
- const { className = '', id, selectedValue, onOpen, onClose, value, children, stateMenu } = this.props;
+ const { className = '', id, selectedValue, onOpen, onClose, children } = this.props;
return (
{React.Children.map(children, (child) => {
@@ -39,6 +37,4 @@ class Menu extends React.Component {
}
}
-export default connect(state => ({
- stateMenu: state.sketcher.present.menus
-}))(Menu);
+export default Menu;
diff --git a/src/components/SketcherToolbars.js b/src/components/SketcherToolbars.js
index 708bb80..b22ea96 100644
--- a/src/components/SketcherToolbars.js
+++ b/src/components/SketcherToolbars.js
@@ -9,6 +9,8 @@ import * as contextTools from '../constants/contextTools.js';
import * as d2Tools from '../constants/d2Tools.js';
import { createSelector } from 'reselect';
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
import '../../styles/styles.css';
// import createDebug from 'debug';
@@ -120,6 +122,7 @@ function renderChildren(children) {
for (const child of children) {
let component;
+
if (child.children.length > 0) {
component = (
);
+ } else if (FONT_TOOLS.includes(child.value)) {
+ component = (
+
+ );
} else {
component = (