mirror of
https://github.com/Doodle3D/Doodle3D-Core.git
synced 2024-12-22 02:53:50 +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"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
|
@ -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",
|
||||
|
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 * 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();
|
||||
|
@ -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 (
|
||||
<div className={classes.container}>
|
||||
<ReactResizeDetector handleWidth handleHeight onResize={this.resizeHandler} />
|
||||
<div className={classes.canvasContainer} ref="canvasContainer" />
|
||||
<InputText screenMatrix={screenMatrix} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
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 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 (
|
||||
<ul id={id} className={`menu ${className}`}>
|
||||
{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;
|
||||
|
@ -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 = (
|
||||
<SubMenu
|
||||
@ -135,6 +138,18 @@ function renderChildren(children) {
|
||||
{renderChildren(child.children)}
|
||||
</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 {
|
||||
component = (
|
||||
<MenuItem
|
||||
@ -151,7 +166,7 @@ function renderChildren(children) {
|
||||
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 showIntersect = activeTool === d2Tools.TRANSFORM && numSelectedObjects >= 1;
|
||||
return {
|
||||
@ -191,11 +206,14 @@ function filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidO
|
||||
case contextTools.HOLE_TOGGLE:
|
||||
return numSelectedObjects > 0;
|
||||
|
||||
case contextTools.FONT:
|
||||
return selectionIncludesText || activeTool === d2Tools.TEXT;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}).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.d2.tool,
|
||||
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.sketcher.present.objectsById[id].solid).length
|
||||
], (menus, activeTool, numSelectedObjects, numFilledObjects, numSolidObjects) => ({
|
||||
toolbar2d: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, nestChildren(menus, menus[TOOLBAR2D])),
|
||||
toolbar3d: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, nestChildren(menus, menus[TOOLBAR3D])),
|
||||
context: filterMenus(activeTool, numSelectedObjects, numFilledObjects, numSolidObjects, nestChildren(menus, menus[CONTEXT]))
|
||||
state => state.sketcher.present.selection.objects.filter(({ id }) => {
|
||||
return state.sketcher.present.objectsById[id].fill;
|
||||
}).length,
|
||||
state => state.sketcher.present.selection.objects.filter(({ id }) => {
|
||||
return state.sketcher.present.objectsById[id].solid;
|
||||
}).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));
|
||||
|
@ -67,7 +67,9 @@ class SubMenu extends React.Component {
|
||||
}
|
||||
};
|
||||
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 = {};
|
||||
if (id === 'color-picker-tool') {
|
||||
|
@ -8,6 +8,7 @@ export const HOLE_TOGGLE = 'hole-toggle-tool';
|
||||
export const ALIGN = 'align-tool';
|
||||
export const UNION = 'union-tool';
|
||||
export const INTERSECT = 'intersect-tool';
|
||||
export const FONT = 'font-tool';
|
||||
|
||||
export const LIGHT_BLUE_A = 'color-light-blue-a';
|
||||
export const LIGHT_BLUE_B = 'color-light-blue-b';
|
||||
@ -79,3 +80,23 @@ export const ALIGN_TOOLS = [
|
||||
ALIGN_VERTICAL,
|
||||
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 TEXT_TOOL_FONT_SIZE = 40;
|
||||
|
@ -37,3 +37,14 @@ export const COLOR_STRING_TO_HEX = {
|
||||
[contextTools.BLACK_B]: 0xAAAAAA,
|
||||
[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,
|
||||
children: contextTools.ALIGN_TOOLS.map(value => ({ value })),
|
||||
...selectorBehavior
|
||||
}, {
|
||||
value: contextTools.FONT,
|
||||
selected: contextTools.OSWALD,
|
||||
children: contextTools.FONT_TOOLS.map(value => ({ value })),
|
||||
...selectorBehavior
|
||||
},
|
||||
{ value: contextTools.UNION },
|
||||
{ value: contextTools.INTERSECT }
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Vector, Matrix } from '@doodle3d/cal';
|
||||
import * as d2Tools from './d2Tools';
|
||||
import * as d3Tools from './d3Tools';
|
||||
import * as contextTools from './contextTools';
|
||||
import { FONT_FACE } from '../constants/general.js';
|
||||
|
||||
const SHAPE = {
|
||||
D3Visible: true,
|
||||
@ -58,7 +60,7 @@ export const SHAPE_TYPE_PROPERTIES = {
|
||||
...SHAPE,
|
||||
defaultProperties: {
|
||||
...defaultProperties,
|
||||
text: { text: '', family: 'Arial', weight: 'normal', style: 'normal' },
|
||||
text: { text: '', family: FONT_FACE[contextTools.OSWALD], weight: 'normal', style: 'normal' },
|
||||
fill: true
|
||||
}
|
||||
},
|
||||
|
@ -2,6 +2,7 @@ import { Group, Utils } from 'cal';
|
||||
import createListener from 'touch-events';
|
||||
import bowser from 'bowser';
|
||||
import { convertEvent, isMouseEvent } from '../utils/pointerUtils.js';
|
||||
import normalizeWheel from 'normalize-wheel';
|
||||
|
||||
const events = [
|
||||
'wheel', 'tap',
|
||||
@ -64,7 +65,9 @@ export default class EventGroup extends Group {
|
||||
if (event.event) gestureEvent.position = _convertEvent(event.event);
|
||||
if (event.events) gestureEvent.positions = event.events.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);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import { shapeToPoints } from '../../shape/shapeToPoints';
|
||||
import { LINE_COLLISION_MARGIN } from '../../constants/d2Constants';
|
||||
import { LINE_WIDTH } from '../../constants/d2Constants';
|
||||
import { PIXEL_RATIO } from '../../constants/general';
|
||||
import { Matrix } from 'cal';
|
||||
import { Matrix, Vector } from 'cal';
|
||||
|
||||
const HIT_ORDER = {
|
||||
RECT: 0,
|
||||
@ -132,13 +132,27 @@ export default class BaseTool extends EventGroup {
|
||||
const objects = shapeDatas
|
||||
.filter(shapeData => {
|
||||
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 }) => {
|
||||
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
|
||||
.fixOrientation()
|
||||
.pointInShape(position, true, true);
|
||||
|
@ -58,24 +58,6 @@ export default class PolygonTool extends BaseTool {
|
||||
context.restore();
|
||||
|
||||
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 bowser from 'bowser';
|
||||
import { convertEvent, isMouseEvent } from '../utils/pointerUtils.js';
|
||||
import normalizeWheel from 'normalize-wheel';
|
||||
|
||||
const events = ['wheel', 'tap', 'dragstart', 'drag', 'dragend', 'seconddragstart', 'seconddrag',
|
||||
'seconddragend', 'multitouchstart', 'multitouch', 'multitouchend'];
|
||||
@ -41,7 +42,9 @@ export class EventScene extends THREE.Scene {
|
||||
if (event.event) gestureEvent.position = _convertEvent(event.event);
|
||||
if (event.events) gestureEvent.positions = event.events.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);
|
||||
};
|
||||
|
71
src/d3/RenderChain.js
vendored
71
src/d3/RenderChain.js
vendored
@ -1,43 +1,62 @@
|
||||
import * as THREE from 'three';
|
||||
import OutlinePass from './OutlinePass.js';
|
||||
import RenderPass from './RenderPass.js';
|
||||
import OutlinePass from './effects/OutlinePass.js';
|
||||
import RenderPass from './effects/RenderPass.js';
|
||||
import AnaglyphPass from './effects/AnaglyphPass.js';
|
||||
import 'three/examples/js/shaders/CopyShader.js';
|
||||
import 'three/examples/js/postprocessing/EffectComposer.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 {
|
||||
constructor(renderer, scene, camera, toonShader, groups) {
|
||||
constructor(renderer, scene, camera, shader, groups) {
|
||||
super(renderer);
|
||||
this._groups = groups;
|
||||
|
||||
if (toonShader) {
|
||||
const renderPass = new RenderPass(scene, camera, () => {
|
||||
this._setVisible(this._initalValues, [groups.shapes, groups.plane, groups.boundingBox]);
|
||||
});
|
||||
this.addPass(renderPass);
|
||||
switch (shader) {
|
||||
case TOONSHADER_OUTLINE: {
|
||||
const renderPass = new RenderPass(scene, camera, () => {
|
||||
this._setVisible(this._initalValues, [groups.shapes, groups.plane, groups.boundingBox]);
|
||||
});
|
||||
this.addPass(renderPass);
|
||||
|
||||
const outlinePass = new OutlinePass(scene, camera, () => {
|
||||
this._setVisible(this._initalValues, [groups.shapes]);
|
||||
});
|
||||
outlinePass.renderToScreen = true;
|
||||
this.addPass(outlinePass);
|
||||
const outlinePass = new OutlinePass(scene, camera, () => {
|
||||
this._setVisible(this._initalValues, [groups.shapes]);
|
||||
});
|
||||
outlinePass.renderToScreen = true;
|
||||
this.addPass(outlinePass);
|
||||
|
||||
const renderPassUI = new RenderPass(scene, camera, () => {
|
||||
this._setVisible(this._initalValues, [groups.UI]);
|
||||
});
|
||||
renderPassUI.clear = false;
|
||||
renderPassUI.renderToScreen = true;
|
||||
this.addPass(renderPassUI);
|
||||
} else {
|
||||
const renderPass = new RenderPass(scene, camera);
|
||||
renderPass.renderToScreen = true;
|
||||
this.addPass(renderPass);
|
||||
const renderPassUI = new RenderPass(scene, camera, () => {
|
||||
this._setVisible(this._initalValues, [groups.UI]);
|
||||
});
|
||||
renderPassUI.clear = false;
|
||||
renderPassUI.renderToScreen = true;
|
||||
this.addPass(renderPassUI);
|
||||
break;
|
||||
}
|
||||
|
||||
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._camera = camera;
|
||||
this._scene = scene;
|
||||
this._toonShader = toonShader;
|
||||
this._shader = shader;
|
||||
}
|
||||
|
||||
_getCurrentVisibleValues() {
|
||||
@ -74,13 +93,13 @@ export default class RenderChain extends THREE.EffectComposer {
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this._toonShader) {
|
||||
if (this._shader === TOONSHADER_OUTLINE) {
|
||||
this._initalValues = this._getCurrentVisibleValues();
|
||||
}
|
||||
|
||||
super.render();
|
||||
|
||||
if (this._toonShader) {
|
||||
if (this._shader === TOONSHADER_OUTLINE) {
|
||||
const { shapes, UI, plane, boundingBox } = this._groups;
|
||||
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();
|
||||
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) });
|
||||
|
||||
|
7
src/d3/createScene.js
vendored
7
src/d3/createScene.js
vendored
@ -1,6 +1,6 @@
|
||||
import * as THREE from 'three';
|
||||
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 { CANVAS_SIZE } from '../constants/d2Constants.js';
|
||||
|
||||
@ -17,7 +17,7 @@ export default function createScene(state, canvas) {
|
||||
|
||||
scene.add(camera);
|
||||
|
||||
const shapesManager = new ShapesManager({ toonShader: hasExtensionsFor.toonShaderThumbnail });
|
||||
const shapesManager = new ShapesManager();
|
||||
shapesManager.update(state);
|
||||
|
||||
scene.add(shapesManager);
|
||||
@ -37,7 +37,8 @@ export default function createScene(state, canvas) {
|
||||
|
||||
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,
|
||||
UI: new THREE.Object3D(),
|
||||
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 normalDepthVert from '../../shaders/normal_depth_vert.glsl';
|
||||
import normalDepthFrag from '../../shaders/normal_depth_frag.glsl';
|
||||
import edgeVert from '../../shaders/edge_vert.glsl';
|
||||
import edgeFrag from '../../shaders/edge_frag.glsl';
|
||||
import combineVert from '../../shaders/combine_vert.glsl';
|
||||
import combineFrag from '../../shaders/combine_frag.glsl';
|
||||
import normalDepthVert from '../../../shaders/normal_depth_vert.glsl';
|
||||
import normalDepthFrag from '../../../shaders/normal_depth_frag.glsl';
|
||||
import edgeVert from '../../../shaders/edge_vert.glsl';
|
||||
import edgeFrag from '../../../shaders/edge_frag.glsl';
|
||||
import combineVert from '../../../shaders/combine_vert.glsl';
|
||||
import combineFrag from '../../../shaders/combine_frag.glsl';
|
||||
|
||||
export default class OutlinePass {
|
||||
constructor(scene, camera, callbackBeforeRender) {
|
@ -1,5 +1,3 @@
|
||||
import * as THREE from 'three';
|
||||
|
||||
export default class RenderPass {
|
||||
constructor(scene, camera, callbackBeforeRender) {
|
||||
this.scene = scene;
|
||||
@ -10,10 +8,10 @@ export default class RenderPass {
|
||||
this.renderToScreen = false;
|
||||
}
|
||||
|
||||
setSize(width, height) {
|
||||
setSize() {
|
||||
}
|
||||
|
||||
render(renderer, writeBuffer, readBuffer, delta, maskActive) {
|
||||
render(renderer, writeBuffer, readBuffer) {
|
||||
if (this._callbackBeforeRender) this._callbackBeforeRender();
|
||||
|
||||
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 { EventObject3D } from '../EventScene.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 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 ShapeMesh from '../ShapeMesh.js';
|
||||
import * as THREE from 'three';
|
||||
import BaseTransformer from './BaseTransformer.js';
|
||||
import * as actions from '../../actions/index.js';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import update from 'react-addons-update';
|
||||
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 { SHAPE_TYPE_PROPERTIES } from '../constants/shapeTypeProperties.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_B:
|
||||
case contextTools.LIGHT_BLUE_C:
|
||||
@ -94,6 +121,11 @@ export default function (state, action) {
|
||||
case contextTools.BLACK_B:
|
||||
case contextTools.BLACK_C: {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import update from 'react-addons-update';
|
||||
import { Matrix } from 'cal';
|
||||
import constrainMatrix from './constrainMatrix.js';
|
||||
import { calculateGestureMatrix } from '../../utils/matrixUtils.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 bucketReducer from './tools/bucketReducer.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 { transformReducer } from './tools/transformReducer.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
|
||||
if (action.type === actions.D2_CHANGE_TOOL) {
|
||||
state = updateTool(state, action.tool);
|
||||
state = removeEmptyText(state);
|
||||
}
|
||||
if (action.category === actions.CAT_SELECTION) {
|
||||
state = updateTool(state, tools.TRANSFORM);
|
||||
|
@ -31,5 +31,8 @@ function handleEraser(state, path, screenMatrixZoom) {
|
||||
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 { getColor } from '../../../utils/objectSelectors.js';
|
||||
import { select } from '../../menusReducer.js';
|
||||
import update from 'react-addons-update';
|
||||
import { updateTool as updateTool2d } from '../toolReducer.js';
|
||||
|
||||
export default function pipetteReducer(state, action) {
|
||||
|
@ -8,10 +8,10 @@ const debug = createDebug('d3d:reducer:text');
|
||||
export default function textReducer(state, action) {
|
||||
if (action.log !== false) debug(action.type);
|
||||
|
||||
const activeShape = state.d2.activeShape;
|
||||
|
||||
switch (action.type) {
|
||||
case actions.D2_TEXT_INIT: {
|
||||
state = removeEmptyText(state);
|
||||
|
||||
const { position, textId, screenMatrixZoom } = action;
|
||||
const screenPosition = (position && screenMatrixZoom) ?
|
||||
position.applyMatrix(screenMatrixZoom.inverseMatrix()) :
|
||||
@ -22,37 +22,40 @@ export default function textReducer(state, action) {
|
||||
} else {
|
||||
return addObjectActive2D(state, {
|
||||
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;
|
||||
}
|
||||
case actions.D2_TEXT_INPUT_CHANGE: {
|
||||
const { text, family, style, weight, fill } = action;
|
||||
const { text } = action;
|
||||
const { activeShape } = state.d2;
|
||||
return update(state, {
|
||||
objectsById: {
|
||||
[activeShape]: {
|
||||
text: {
|
||||
text: { $set: text },
|
||||
family: { $set: family },
|
||||
style: { $set: style },
|
||||
weight: { $set: weight }
|
||||
},
|
||||
fill: { $set: fill }
|
||||
text: { $set: text }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
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:
|
||||
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;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import undoFilter from '../utils/undoFilter.js';
|
||||
import * as actions from '../actions/index.js';
|
||||
import * as d2Tools from '../constants/d2Tools.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 { ERASER_SIZES, BRUSH_SIZES } from '../constants/d2Constants.js';
|
||||
import update from 'react-addons-update';
|
||||
@ -36,7 +36,8 @@ const initialState = {
|
||||
objectIdCounter: 0,
|
||||
context: {
|
||||
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: {
|
||||
transform: new Matrix(),
|
||||
@ -142,7 +143,6 @@ function sketcherReducer(state = initialState, action) {
|
||||
case actions.MULTITOUCH_TRANSFORM_END:
|
||||
case actions.D2_TEXT_INIT:
|
||||
case actions.D2_TEXT_INPUT_CHANGE:
|
||||
case actions.D2_TEXT_ADD:
|
||||
case actions.MOVE_SELECTION:
|
||||
return d2ToolReducer(state, action);
|
||||
|
||||
@ -188,11 +188,11 @@ function sketcherReducer(state = initialState, action) {
|
||||
return d3ToolReducer(state, action);
|
||||
|
||||
case actions.D2_CHANGE_TOOL:
|
||||
state = setActive2D(state, null);
|
||||
state = selectionReducer(state, action);
|
||||
state = d2ToolReducer(state, action); // switch and initialize tool
|
||||
state = updateMenus(state, action);
|
||||
state = contextReducer(state, action);
|
||||
state = setActive2D(state, null);
|
||||
return state;
|
||||
|
||||
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 { createText } from '../utils/textUtils.js';
|
||||
import { segmentBezierPath } from '../utils/curveUtils.js';
|
||||
import { TEXT_TOOL_FONT_SIZE } from '../constants/d2Constants.js';
|
||||
|
||||
const setDirection = (clockwise) => (path) => {
|
||||
return (THREE.ShapeUtils.isClockWise(path) === clockwise) ? path : path.reverse();
|
||||
@ -64,7 +65,7 @@ function shapeToPointsRaw(shapeData) {
|
||||
const points = [];
|
||||
let even = false;
|
||||
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?
|
||||
points.push(points[0].clone()); // go to first point
|
||||
} else {
|
||||
@ -72,7 +73,6 @@ function shapeToPointsRaw(shapeData) {
|
||||
let x = Math.sin(rad) * radius;
|
||||
let y = -Math.cos(rad) * radius;
|
||||
points.push(new Vector(x, y));
|
||||
even = !even;
|
||||
}
|
||||
}
|
||||
shapes.push({ points, holes: [] });
|
||||
@ -120,7 +120,7 @@ function shapeToPointsRaw(shapeData) {
|
||||
}
|
||||
case '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 }));
|
||||
|
||||
shapes.push(...textShapes);
|
||||
|
@ -9,19 +9,17 @@ import memoize from 'memoizee';
|
||||
const MARGIN = 200;
|
||||
|
||||
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 [];
|
||||
|
||||
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
|
||||
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 }) => ({
|
||||
x: (x - halfWidth) / 10,
|
||||
y: (y - halfHeight) / 10
|
||||
x: (x - MARGIN) / precision,
|
||||
y: (y - MARGIN) / precision
|
||||
})));
|
||||
|
||||
const shapes = new ClipperShape(pathsOffset, true, true, false)
|
||||
@ -33,7 +31,7 @@ export function createTextRaw(text, size, family, style, weight) {
|
||||
return shapes;
|
||||
}
|
||||
|
||||
const textContext = new Text();
|
||||
const textContext = new Text({ baseline: 'top' });
|
||||
export function createTextCanvas(text, size, family, style, weight) {
|
||||
textContext.size = size;
|
||||
textContext.family = family;
|
||||
@ -52,7 +50,7 @@ export function createTextCanvas(text, size, family, style, weight) {
|
||||
context.fillStyle = 'white';
|
||||
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.STAMP,
|
||||
actions.SELECT,
|
||||
actions.D2_TEXT_ADD,
|
||||
actions.CLEAR,
|
||||
actions.TWIST_END,
|
||||
actions.SCULPT_END,
|
||||
|
@ -249,6 +249,24 @@
|
||||
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-b { fill: #68E1FD; }
|
||||
#color-light-blue-c { fill: #01B8FF; }
|
||||
|
@ -3,6 +3,7 @@ const path = require('path');
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
const HTMLWebpackPlugin = require('html-webpack-plugin');
|
||||
const CordovaPlugin = require('webpack-cordova-plugin');
|
||||
const GoogleFontsPlugin = require('google-fonts-webpack-plugin');
|
||||
|
||||
const devMode = process.env.NODE_ENV !== 'production';
|
||||
const appMode = process.env.TARGET === 'app';
|
||||
@ -75,9 +76,7 @@ module.exports = {
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
'TARGET': JSON.stringify(process.env.TARGET)
|
||||
}
|
||||
'process.env.TARGET': JSON.stringify(process.env.TARGET)
|
||||
}),
|
||||
new HTMLWebpackPlugin({
|
||||
title: 'Doodle3D Core - Simple example',
|
||||
@ -86,6 +85,18 @@ module.exports = {
|
||||
scripts: appMode ? ['cordova.js'] : null,
|
||||
appMountId: 'app'
|
||||
}),
|
||||
new GoogleFontsPlugin({
|
||||
fonts: [
|
||||
{ family: 'Oswald' },
|
||||
{ family: 'Ranga' },
|
||||
{ family: 'Joti One' },
|
||||
{ family: 'Bellefair' },
|
||||
{ family: 'Lobster' },
|
||||
{ family: 'Abril Fatface' },
|
||||
{ family: 'Play' },
|
||||
{ family: 'Fascinate' }
|
||||
]
|
||||
}),
|
||||
...(appMode ? [
|
||||
new CordovaPlugin({
|
||||
config: 'config.xml',
|
||||
@ -95,7 +106,7 @@ module.exports = {
|
||||
})
|
||||
] : [])
|
||||
],
|
||||
devtool: "source-map",
|
||||
devtool: 'source-map',
|
||||
devServer: {
|
||||
contentBase: 'dist'
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user