mirror of
https://github.com/Doodle3D/Doodle3D-Core.git
synced 2024-12-22 11:03:48 +01:00
Merge branch 'master' into feature-cordova
This commit is contained in:
commit
e8243d84e3
25
.babelrc
Normal file
25
.babelrc
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"module": {
|
||||||
|
"presets": [
|
||||||
|
["env", {
|
||||||
|
"targets": { "node": "6" },
|
||||||
|
"modules": false
|
||||||
|
}],
|
||||||
|
"stage-0",
|
||||||
|
"react"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
"presets": ["env", "stage-0", "react"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"babel-plugin-transform-regenerator",
|
||||||
|
"babel-plugin-transform-object-rest-spread",
|
||||||
|
"babel-plugin-inline-import",
|
||||||
|
"babel-plugin-transform-class-properties",
|
||||||
|
"babel-plugin-transform-es2015-classes",
|
||||||
|
"babel-plugin-syntax-dynamic-import"
|
||||||
|
]
|
||||||
|
}
|
33
.eslintrc
Normal file
33
.eslintrc
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"extends": "eslint-config-airbnb",
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"experimentalObjectRestSpread": true,
|
||||||
|
"modules": true,
|
||||||
|
"jsx": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"comma-dangle": [1, "never"],
|
||||||
|
"no-else-return": 0,
|
||||||
|
"no-use-before-define": [2, "nofunc"],
|
||||||
|
"no-param-reassign": 0,
|
||||||
|
"no-var": 1,
|
||||||
|
"no-labels": 0,
|
||||||
|
"guard-for-in": 0,
|
||||||
|
"prefer-const": 0,
|
||||||
|
"no-unused-vars": 1,
|
||||||
|
"key-spacing": [1, {"beforeColon": false, "afterColon": true, "mode": "minimum"}],
|
||||||
|
"no-loop-func": 1,
|
||||||
|
"react/sort-comp": [0],
|
||||||
|
"max-len": [1, 110, 4],
|
||||||
|
"camelcase": 1,
|
||||||
|
"new-cap": 0
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"THREE": false
|
||||||
|
}
|
||||||
|
}
|
43
index.js
43
index.js
@ -15,24 +15,43 @@ const reducer = combineReducers({ sketcher: sketcherReducer });
|
|||||||
const enhancer = compose(applyMiddleware(thunkMiddleware, promiseMiddleware(), createLogger({ collapsed: true })));
|
const enhancer = compose(applyMiddleware(thunkMiddleware, promiseMiddleware(), createLogger({ collapsed: true })));
|
||||||
const store = createStore(reducer, enhancer);
|
const store = createStore(reducer, enhancer);
|
||||||
|
|
||||||
// prepare html (SHOULDN'T BE DONE LIKE THIS)
|
// add actions to window
|
||||||
document.body.style.margin = 0;
|
|
||||||
document.body.style.padding = 0;
|
|
||||||
document.body.style.height = '100%';
|
|
||||||
document.documentElement.style.height = '100%';
|
|
||||||
document.documentElement.style.overflow = 'hidden';
|
|
||||||
document.getElementById('app').style.height = '100%';
|
|
||||||
|
|
||||||
import actionWrapper from 'redux-action-wrapper';
|
import actionWrapper from 'redux-action-wrapper';
|
||||||
import * as actions from './src/actions/index.js';
|
import * as actions from './src/actions/index.js';
|
||||||
window.actions = actionWrapper(actions, store.dispatch);
|
window.actions = actionWrapper(actions, store.dispatch);
|
||||||
|
import { saveAs as saveAsLib } from 'file-saver';
|
||||||
|
|
||||||
import modelData from './models/noodlebot.d3sketch';
|
// download file
|
||||||
|
import { createFile } from './src/utils/exportUtils.js';
|
||||||
|
window.downloadStl = () => {
|
||||||
|
store.dispatch(async (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const blob = await createFile(state.sketcher.present, 'stl-blob');
|
||||||
|
saveAsLib(blob, 'doodle.stl');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// add model to store
|
||||||
|
import modelData from './models/circle_error.d3sketch';
|
||||||
import JSONToSketchData from './src/shape/JSONToSketchData.js';
|
import JSONToSketchData from './src/shape/JSONToSketchData.js';
|
||||||
(async () => {
|
JSONToSketchData(JSON.parse(modelData)).then(data => {
|
||||||
const data = await JSONToSketchData(JSON.parse(modelData));
|
|
||||||
store.dispatch(actions.openSketch({ data }));
|
store.dispatch(actions.openSketch({ data }));
|
||||||
})();
|
});
|
||||||
|
|
||||||
|
// default css
|
||||||
|
import jss from 'jss';
|
||||||
|
import preset from 'jss-preset-default';
|
||||||
|
import normalize from 'normalize-jss';
|
||||||
|
jss.setup(preset());
|
||||||
|
jss.createStyleSheet(normalize).attach();
|
||||||
|
jss.createStyleSheet({
|
||||||
|
'@global': {
|
||||||
|
'*': { margin: 0, padding: 0 },
|
||||||
|
'#app, body, html': { height: '100%', fontFamily: 'sans-serif' },
|
||||||
|
body: { overflow: 'auto' },
|
||||||
|
html: { overflow: 'hidden' }
|
||||||
|
}
|
||||||
|
}).attach();
|
||||||
|
|
||||||
// render dom
|
// render dom
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
1
models/Doodle.d3sketch
Normal file
1
models/Doodle.d3sketch
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"data":"{\"spaces\":[{\"matrix\":{\"metadata\":{\"type\":\"Matrix4\",\"library\":\"three.js\"},\"elements\":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},\"objects\":[{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[-0.181725074441806,0,77.79195055196584,0,0.1712971976655113,-50.224936115703166]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":true,\"points\":{\"metadata\":{\"type\":\"VectorArray\",\"size\":\"Float32\"},\"data\":\"PUrTQ3sUzENmxklErkeMQ3tU0EPXI7FDAIDQQwAAskMAANBDAAC2QwCAzkMAgLlDAADMQwCAvEMAAMlDAAC/QwCAxUMAgMBDAADCQwAAwUMAAL5DAIDAQwCAukMAAL9DAAC3QwAAvUOFS7ZD7PG7QwAAtUMAgL1DAACyQwAAwEMAgK5DAIDBQwAAq0MAAMJDAACnQwCAwUMAgKNDAADAQwAAoEMAAL5DAACeQwAAu0MAAJxDAIC3QwCAm0MAgLNDM9ObQwo3sUNSOJ/CrkeMQ67HhEPNjMVDrueFQ1J4xENcb4pDPWrCQwr3jkOPYsFD1yOUQ7jewEPNrJhDZubBQ+wxnUOkcMNDwxWhQ9cDxkOkUKRDmhnJQ+GapUNSOM1DSEGmQ+zR0EPhmqVDXO/UQx8Fo0M9ithDXG+gQwCg20Ou55tDXK/dQwBgl0OaOd9DMzOSQ3G930OFq41DmjnfQxRujEMUzt5D4XqzQnvELURSmKxDUjjlQ3E9q0PN7ONDUripQ+Ea4UPDNalDj+LdQ5o5qkMp3NlDuL6rQz0K10NmRq5DAKDUQ+zRsUPXA9NDcV21Q8M10kM9arlDFM7RQ3v0vENxndJDAIDAQ+zR00M9isNDw9XVQ+wRxkMAQNhDexTHQ1J420PDlcdD9kjeQ3sUx0OPguFDXA/FQzNT5EOuB8NDuL7mQ2aGwUPNbOdDAHAfRHvELUT2iNRD4brQQ6Qw1EMKN9FDSIHTQ64H0kNcT9JDM5PSQ3Ed0UPh+tJDAMDPQ7ge00Ncj85D4frSQylczUMzk9JDw1XMQ2bm0UMpfMtDwxXRQ5r5ykOPItBDXM/KQ80Mz0NmJstDpLDNQ66ny0O4vsxDj4LMQxTuy0N7tM1Dj2LLQ2bmzkNxHctD10PQQ5r5ykN7dNFDSEHLQ66n0kP2qMtDPUrTQ3sUzEM=\"},\"holes\":[],\"color\":6939133,\"star\":{\"rays\":5,\"innerRadius\":7.898894154818322,\"outerRadius\":63.507109004739334},\"type\":\"COMPOUND_PATH\"}]}]}","appVersion":"0.17.4"}
|
1
models/circle_error.d3sketch
Normal file
1
models/circle_error.d3sketch
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"data":"{\"spaces\":[{\"matrix\":{\"metadata\":{\"type\":\"Matrix4\",\"library\":\"three.js\"},\"elements\":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},\"objects\":[{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-16.42969984202213,0,1,-10.74249605055293]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":true,\"circle\":{\"radius\":31.061842611644483,\"segment\":6.283185307179586},\"color\":6873597,\"type\":\"CIRCLE\"},{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-3.1595576619273373,0,1,-2.5276461295418784]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":false,\"circle\":{\"radius\":30.41228120273779,\"segment\":6.283185307179586},\"color\":6873597,\"type\":\"CIRCLE\"}]}]}","appVersion":"0.17.4"}
|
1
models/simple_hole.d3sketch
Normal file
1
models/simple_hole.d3sketch
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"data":"{\"spaces\":[{\"matrix\":{\"metadata\":{\"type\":\"Matrix4\",\"library\":\"three.js\"},\"elements\":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},\"objects\":[{\"height\":21.005096748926302,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,8.214849921011051,0,1,-3.7914691943127927]},\"z\":7.243224684956081,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":false,\"star\":{\"rays\":5,\"innerRadius\":18.00947867298578,\"outerRadius\":38.54660347551342},\"color\":6873597,\"type\":\"STAR\"},{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-14.533965244865726,0,1,7.266982622432863]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":true,\"star\":{\"rays\":5,\"innerRadius\":18.00947867298578,\"outerRadius\":38.54660347551342},\"color\":6873597,\"type\":\"STAR\"}]}]}","appVersion":"0.17.4"}
|
1549
package-lock.json
generated
1549
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@doodle3d/doodle3d-core",
|
"name": "@doodle3d/doodle3d-core",
|
||||||
"version": "0.17.4",
|
"version": "0.17.5",
|
||||||
"description": "Core functions of Doodle3D Transform",
|
"description": "Core functions of Doodle3D Transform",
|
||||||
"main": "lib",
|
"main": "lib",
|
||||||
"module": "module",
|
"module": "module",
|
||||||
@ -9,6 +9,7 @@
|
|||||||
"start": "webpack-dev-server -w",
|
"start": "webpack-dev-server -w",
|
||||||
"ios": "TARGET=app webpack -p && cordova run ios",
|
"ios": "TARGET=app webpack -p && cordova run ios",
|
||||||
"prepare": "npm run build",
|
"prepare": "npm run build",
|
||||||
|
"lint": "eslint src",
|
||||||
"build": "npm run build:main && npm run build:module ",
|
"build": "npm run build:main && npm run build:module ",
|
||||||
"build:main": "BABEL_ENV=main babel src -s -d lib",
|
"build:main": "BABEL_ENV=main babel src -s -d lib",
|
||||||
"build:module": "BABEL_ENV=module babel src -s -d module"
|
"build:module": "BABEL_ENV=module babel src -s -d module"
|
||||||
@ -27,6 +28,9 @@
|
|||||||
"bowser": "^1.8.1",
|
"bowser": "^1.8.1",
|
||||||
"fit-curve": "^0.1.6",
|
"fit-curve": "^0.1.6",
|
||||||
"imports-loader": "^0.7.1",
|
"imports-loader": "^0.7.1",
|
||||||
|
"jss": "^9.4.0",
|
||||||
|
"keycode": "^2.1.9",
|
||||||
|
"lodash": "^4.17.4",
|
||||||
"memoizee": "^0.3.9",
|
"memoizee": "^0.3.9",
|
||||||
"pouchdb": "^6.3.4",
|
"pouchdb": "^6.3.4",
|
||||||
"proptypes": "^1.1.0",
|
"proptypes": "^1.1.0",
|
||||||
@ -38,6 +42,7 @@
|
|||||||
"react-notification-system-redux": "^1.2.0",
|
"react-notification-system-redux": "^1.2.0",
|
||||||
"react-redux": "^5.0.6",
|
"react-redux": "^5.0.6",
|
||||||
"react-resize-detector": "^1.1.0",
|
"react-resize-detector": "^1.1.0",
|
||||||
|
"react-svg-inline": "^2.0.1",
|
||||||
"redux-form": "^7.1.2",
|
"redux-form": "^7.1.2",
|
||||||
"redux-undo": "^1.0.0-beta9-9-7",
|
"redux-undo": "^1.0.0-beta9-9-7",
|
||||||
"reselect": "^3.0.1",
|
"reselect": "^3.0.1",
|
||||||
@ -50,6 +55,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-cli": "6.24.1",
|
"babel-cli": "6.24.1",
|
||||||
"babel-core": "6.24.1",
|
"babel-core": "6.24.1",
|
||||||
|
"babel-eslint": "^5.0.4",
|
||||||
"babel-loader": "^7.0.0",
|
"babel-loader": "^7.0.0",
|
||||||
"babel-plugin-add-module-exports": "0.2.1",
|
"babel-plugin-add-module-exports": "0.2.1",
|
||||||
"babel-plugin-inline-import": "^2.0.6",
|
"babel-plugin-inline-import": "^2.0.6",
|
||||||
@ -66,12 +72,17 @@
|
|||||||
"babel-preset-stage-0": "^6.24.1",
|
"babel-preset-stage-0": "^6.24.1",
|
||||||
"cordova": "^7.1.0",
|
"cordova": "^7.1.0",
|
||||||
"css-loader": "^0.28.7",
|
"css-loader": "^0.28.7",
|
||||||
|
"eslint": "^1.10.3",
|
||||||
|
"eslint-config-airbnb": "^3.1.0",
|
||||||
|
"eslint-plugin-react": "^3.16.1",
|
||||||
|
"file-saver": "^1.3.3",
|
||||||
"html-webpack-plugin": "^2.30.1",
|
"html-webpack-plugin": "^2.30.1",
|
||||||
"html-webpack-template": "^6.0.2",
|
"html-webpack-template": "^6.0.2",
|
||||||
|
"jss-preset-default": "^4.0.1",
|
||||||
|
"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-router-redux": "^4.0.8",
|
||||||
"react-svg-inline": "^2.0.1",
|
|
||||||
"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 );
|
||||||
|
}
|
@ -228,13 +228,13 @@ export function d3ChangeTool(tool) {
|
|||||||
export function contextChangeTool(tool) {
|
export function contextChangeTool(tool) {
|
||||||
return { type: CONTEXT_CHANGE_TOOL, tool };
|
return { type: CONTEXT_CHANGE_TOOL, tool };
|
||||||
}
|
}
|
||||||
export function heightStart(handle) {
|
export function changeHeightStart(handle) {
|
||||||
return { type: HEIGHT_START, handle };
|
return { type: HEIGHT_START, handle };
|
||||||
}
|
}
|
||||||
export function height(delta) {
|
export function changeHeight(delta) {
|
||||||
return { type: HEIGHT, delta, log: false };
|
return { type: HEIGHT, delta, log: false };
|
||||||
}
|
}
|
||||||
export function heightEnd() {
|
export function changeHeightEnd() {
|
||||||
return { type: HEIGHT_END };
|
return { type: HEIGHT_END };
|
||||||
}
|
}
|
||||||
export function twistStart() {
|
export function twistStart() {
|
||||||
@ -378,7 +378,10 @@ export function addImage(file) {
|
|||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
dispatch(notification.error({ position: 'tc', title: 'Error loading image, please try again with another image' }));
|
dispatch(notification.error({
|
||||||
|
position: 'tc',
|
||||||
|
title: 'Error loading image, please try again with another image'
|
||||||
|
}));
|
||||||
|
|
||||||
throw error; // rethrow for other listeners
|
throw error; // rethrow for other listeners
|
||||||
});
|
});
|
||||||
|
@ -13,6 +13,9 @@ import btnUndoImageURL from '../../img/mainmenu/btnUndo.png';
|
|||||||
import btnRedoImageURL from '../../img/mainmenu/btnRedo.png';
|
import btnRedoImageURL from '../../img/mainmenu/btnRedo.png';
|
||||||
import InlineIconsLoader from './InlineIconsLoader.js';
|
import InlineIconsLoader from './InlineIconsLoader.js';
|
||||||
import JSONToSketchData from '../shape/JSONToSketchData.js';
|
import JSONToSketchData from '../shape/JSONToSketchData.js';
|
||||||
|
import keycode from 'keycode';
|
||||||
|
import bowser from 'bowser';
|
||||||
|
import * as d2Tools from '../constants/d2Tools.js';
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
container: {
|
container: {
|
||||||
@ -63,7 +66,12 @@ class App extends React.Component {
|
|||||||
addImage: PropTypes.func.isRequired,
|
addImage: PropTypes.func.isRequired,
|
||||||
undo: PropTypes.func.isRequired,
|
undo: PropTypes.func.isRequired,
|
||||||
redo: PropTypes.func.isRequired,
|
redo: PropTypes.func.isRequired,
|
||||||
classes: PropTypes.objectOf(PropTypes.string)
|
classes: PropTypes.objectOf(PropTypes.string),
|
||||||
|
deleteSelection: PropTypes.func.isRequired,
|
||||||
|
selectAll: PropTypes.func.isRequired,
|
||||||
|
d2ChangeTool: PropTypes.func.isRequired,
|
||||||
|
moveSelection: PropTypes.func.isRequired,
|
||||||
|
selectedPen: PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -71,6 +79,13 @@ class App extends React.Component {
|
|||||||
|
|
||||||
container.addEventListener('dragover', event => event.preventDefault());
|
container.addEventListener('dragover', event => event.preventDefault());
|
||||||
container.addEventListener('drop', this.onDrop);
|
container.addEventListener('drop', this.onDrop);
|
||||||
|
window.addEventListener('keydown', this.onKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const { container } = this.refs;
|
||||||
|
container.removeEventListener('drop', this.onDrop);
|
||||||
|
window.removeEventListener('keydown', this.onKeyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDrop = async event => {
|
onDrop = async event => {
|
||||||
@ -78,9 +93,9 @@ class App extends React.Component {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
for (const file of event.dataTransfer.files) {
|
for (const file of event.dataTransfer.files) {
|
||||||
const [name, ...extentions] = file.name.split('.');
|
const extentions = file.name.split('.').pop();
|
||||||
|
|
||||||
switch (extentions.pop().toUpperCase()) {
|
switch (extentions.toUpperCase()) {
|
||||||
case 'D3SKETCH':
|
case 'D3SKETCH':
|
||||||
case 'JSON':
|
case 'JSON':
|
||||||
const url = URL.createObjectURL(file);
|
const url = URL.createObjectURL(file);
|
||||||
@ -100,8 +115,65 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillUnmount() {
|
onKeyDown = (event) => {
|
||||||
container.removeEventListener('drop', this.onDrop);
|
const { undo, redo, deleteSelection, selectAll, d2ChangeTool, moveSelection, selectedPen } = this.props;
|
||||||
|
const { metaKey, ctrlKey, shiftKey } = event;
|
||||||
|
const key = keycode(event);
|
||||||
|
const commandKey = bowser.mac ? metaKey : ctrlKey;
|
||||||
|
|
||||||
|
const targetTag = event.target.tagName.toLowerCase();
|
||||||
|
if (targetTag === 'input' || targetTag === 'textarea') return;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 'backspace':
|
||||||
|
case 'delete':
|
||||||
|
event.preventDefault();
|
||||||
|
deleteSelection();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
if (commandKey) selectAll();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'z':
|
||||||
|
if (commandKey) {
|
||||||
|
if (shiftKey) {
|
||||||
|
redo();
|
||||||
|
} else {
|
||||||
|
undo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't': {
|
||||||
|
if (!commandKey) d2ChangeTool(d2Tools.TEXT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'b': {
|
||||||
|
if (!commandKey) d2ChangeTool(selectedPen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'left':
|
||||||
|
case 'right':
|
||||||
|
case 'up':
|
||||||
|
case 'down': {
|
||||||
|
const delta = shiftKey ? 10 : 1;
|
||||||
|
const deltas = {
|
||||||
|
left: { deltaX: -delta, deltaY: 0 },
|
||||||
|
right: { deltaX: delta, deltaY: 0 },
|
||||||
|
up: { deltaX: 0, deltaY: -delta },
|
||||||
|
down: { deltaX: 0, deltaY: delta }
|
||||||
|
};
|
||||||
|
|
||||||
|
const { deltaX, deltaY } = deltas[key];
|
||||||
|
moveSelection(deltaX, deltaY);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -125,9 +197,15 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectSheet(styles)(connect(null, {
|
export default injectSheet(styles)(connect(state => ({
|
||||||
|
selectedPen: state.sketcher.present.menus['pen-tools'].selected
|
||||||
|
}), {
|
||||||
undo: actions.undo.undo,
|
undo: actions.undo.undo,
|
||||||
redo: actions.undo.redo,
|
redo: actions.undo.redo,
|
||||||
openSketch: actions.openSketch,
|
openSketch: actions.openSketch,
|
||||||
addImage: actions.addImage,
|
addImage: actions.addImage,
|
||||||
|
deleteSelection: actions.deleteSelection,
|
||||||
|
selectAll: actions.selectAll,
|
||||||
|
d2ChangeTool: actions.d2ChangeTool,
|
||||||
|
moveSelection: actions.moveSelection
|
||||||
})(App));
|
})(App));
|
||||||
|
@ -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,7 +62,8 @@ class D3Panel extends React.Component {
|
|||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.createScene();
|
this.createScene();
|
||||||
|
|
||||||
this.renderChain = new RenderChain(this.renderer, this.scene, this.camera, hasExtensionsFor.toonShaderPreview, {
|
const shader = hasExtensionsFor.toonShaderPreview ? TOONSHADER_OUTLINE : 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,
|
||||||
|
@ -51,7 +51,6 @@ class DoodlePreview extends React.Component {
|
|||||||
if (docData) sketchData = await JSONToSketchData(this.props.docData);
|
if (docData) sketchData = await JSONToSketchData(this.props.docData);
|
||||||
|
|
||||||
const { canvas } = this.refs;
|
const { canvas } = this.refs;
|
||||||
const { pixelRatio } = this.props
|
|
||||||
|
|
||||||
const sceneData = createSceneData(sketchData);
|
const sceneData = createSceneData(sketchData);
|
||||||
|
|
||||||
@ -70,7 +69,7 @@ class DoodlePreview extends React.Component {
|
|||||||
|
|
||||||
resizeHandler = (width, height) => {
|
resizeHandler = (width, height) => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const { setSize, render } = this.state;
|
const { setSize } = this.state;
|
||||||
const { pixelRatio } = this.props;
|
const { pixelRatio } = this.props;
|
||||||
setSize(width, height, pixelRatio);
|
setSize(width, height, pixelRatio);
|
||||||
});
|
});
|
||||||
|
@ -20,20 +20,20 @@ const CONTEXT = 'context';
|
|||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
contextMenuContainer: {
|
contextMenuContainer: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: 0,
|
left: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
pointerEvents: 'none', /* enable clicking through object */
|
pointerEvents: 'none', /* enable clicking through object */
|
||||||
'& *': {
|
'& *': {
|
||||||
pointerEvents: 'visible' /* enable clicking in children */
|
pointerEvents: 'visible' /* enable clicking in children */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
class SketcherToolbars extends React.Component {
|
class SketcherToolbars extends React.Component {
|
||||||
|
|
||||||
@ -41,7 +41,8 @@ class SketcherToolbars extends React.Component {
|
|||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
toolbar2d: PropTypes.object, // TODO: specify further
|
toolbar2d: PropTypes.object, // TODO: specify further
|
||||||
toolbar3d: PropTypes.object, // TODO: specify further
|
toolbar3d: PropTypes.object, // TODO: specify further
|
||||||
context: PropTypes.object // TODO: specify further
|
context: PropTypes.object, // TODO: specify further
|
||||||
|
classes: PropTypes.objectOf(PropTypes.string)
|
||||||
};
|
};
|
||||||
onSelect2D = ({ value }) => {
|
onSelect2D = ({ value }) => {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Button from './Button.js';
|
import Button from './Button.js';
|
||||||
import Menu from './Menu.js';
|
import Menu from './Menu.js';
|
||||||
import bowser from 'bowser';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { hexToStyle } from '../utils/colorUtils.js';
|
import { hexToStyle } from '../utils/colorUtils.js';
|
||||||
// import createDebug from 'debug';
|
// import createDebug from 'debug';
|
||||||
@ -21,7 +20,9 @@ class SubMenu extends React.Component {
|
|||||||
svg: PropTypes.string,
|
svg: PropTypes.string,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
selectOnOpen: PropTypes.bool,
|
selectOnOpen: PropTypes.bool,
|
||||||
toggleBehavior: PropTypes.bool
|
toggleBehavior: PropTypes.bool,
|
||||||
|
color: PropTypes.number,
|
||||||
|
solid: PropTypes.bool
|
||||||
};
|
};
|
||||||
componentWillMount = () => {
|
componentWillMount = () => {
|
||||||
// Listeners to close the submenu when anywhere else is clicked
|
// Listeners to close the submenu when anywhere else is clicked
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
import DoodlePreview from './DoodlePreview.js';
|
|
||||||
import AuthImage from './AuthImage.js';
|
|
||||||
|
|
||||||
export { DoodlePreview, AuthImage };
|
|
@ -36,33 +36,6 @@ export const BLACK_C = 'color-black-c';
|
|||||||
export const HOLE_MATERIAL = 'color-hole-material';
|
export const HOLE_MATERIAL = 'color-hole-material';
|
||||||
export const PIPETTE = 'pipette-tool';
|
export const PIPETTE = 'pipette-tool';
|
||||||
|
|
||||||
export const COLORS = [
|
|
||||||
LIGHT_BLUE_A,
|
|
||||||
LIGHT_BLUE_B,
|
|
||||||
LIGHT_BLUE_C,
|
|
||||||
DARK_BLUE_A,
|
|
||||||
DARK_BLUE_B,
|
|
||||||
DARK_BLUE_C,
|
|
||||||
PURPLE_A,
|
|
||||||
PURPLE_B,
|
|
||||||
PURPLE_C,
|
|
||||||
PINK_A,
|
|
||||||
PINK_B,
|
|
||||||
PINK_C,
|
|
||||||
RED_A,
|
|
||||||
RED_B,
|
|
||||||
RED_C,
|
|
||||||
YELLOW_A,
|
|
||||||
YELLOW_B,
|
|
||||||
YELLOW_C,
|
|
||||||
GREEN_A,
|
|
||||||
GREEN_B,
|
|
||||||
GREEN_C,
|
|
||||||
BLACK_A,
|
|
||||||
BLACK_B,
|
|
||||||
BLACK_C
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ERASER_SIZE_SMALL = 'eraser-size-small';
|
export const ERASER_SIZE_SMALL = 'eraser-size-small';
|
||||||
export const ERASER_SIZE_MEDIUM = 'eraser-size-medium';
|
export const ERASER_SIZE_MEDIUM = 'eraser-size-medium';
|
||||||
export const ERASER_SIZE_LARGE = 'eraser-size-large';
|
export const ERASER_SIZE_LARGE = 'eraser-size-large';
|
||||||
|
@ -5,28 +5,35 @@ export const SHAPE_CACHE_LIMIT = 50;
|
|||||||
export const PIXEL_RATIO = 1.0;
|
export const PIXEL_RATIO = 1.0;
|
||||||
|
|
||||||
export const COLOR_STRING_TO_HEX = {
|
export const COLOR_STRING_TO_HEX = {
|
||||||
[contextTools.LIGHT_BLUE_A]: 0xbcffff,
|
[contextTools.LIGHT_BLUE_A]: 0xBCFFFF,
|
||||||
[contextTools.LIGHT_BLUE_B]: 0x69e1fd,
|
[contextTools.LIGHT_BLUE_B]: 0x68E1FD,
|
||||||
[contextTools.LIGHT_BLUE_C]: 0x00b8ff,
|
[contextTools.LIGHT_BLUE_C]: 0x01B8FF,
|
||||||
[contextTools.DARK_BLUE_A]: 0xc8e2ff,
|
|
||||||
[contextTools.DARK_BLUE_B]: 0x7dacfc,
|
[contextTools.DARK_BLUE_A]: 0xC8E3FF,
|
||||||
[contextTools.DARK_BLUE_C]: 0x0357ff,
|
[contextTools.DARK_BLUE_B]: 0x7DACFC,
|
||||||
[contextTools.PURPLE_A]: 0xefc9ff,
|
[contextTools.DARK_BLUE_C]: 0x0256FF,
|
||||||
[contextTools.PURPLE_B]: 0xc57efc,
|
|
||||||
[contextTools.PURPLE_C]: 0x820ef9,
|
[contextTools.PURPLE_A]: 0xEFC9FF,
|
||||||
[contextTools.PINK_A]: 0xffc7ee,
|
[contextTools.PURPLE_B]: 0xC57EFC,
|
||||||
[contextTools.PINK_B]: 0xfd7cc1,
|
[contextTools.PURPLE_C]: 0x820FF9,
|
||||||
[contextTools.PINK_C]: 0xfa047b,
|
|
||||||
[contextTools.RED_A]: 0xffcdce,
|
[contextTools.PINK_A]: 0xFFC7EE,
|
||||||
[contextTools.RED_B]: 0xfd898a,
|
[contextTools.PINK_B]: 0xFD7BC1,
|
||||||
[contextTools.RED_C]: 0xfd898a,
|
[contextTools.PINK_C]: 0xFA047B,
|
||||||
[contextTools.YELLOW_A]: 0xfffea0,
|
|
||||||
[contextTools.YELLOW_B]: 0xfffb39,
|
[contextTools.RED_A]: 0xFFCDCE,
|
||||||
[contextTools.YELLOW_C]: 0xfdac05,
|
[contextTools.RED_B]: 0xFD898A,
|
||||||
[contextTools.GREEN_A]: 0xdaffd4,
|
[contextTools.RED_C]: 0xFF2600,
|
||||||
[contextTools.GREEN_B]: 0x97f194,
|
|
||||||
[contextTools.GREEN_C]: 0x31d22d,
|
[contextTools.YELLOW_A]: 0xFFF76B,
|
||||||
[contextTools.BLACK_A]: 0xf4f4f4,
|
[contextTools.YELLOW_B]: 0xFF9201,
|
||||||
[contextTools.BLACK_B]: 0x7f7f7f,
|
[contextTools.YELLOW_C]: 0xAA7942,
|
||||||
[contextTools.BLACK_C]: 0x1f1f1f
|
|
||||||
|
[contextTools.GREEN_A]: 0xDAFFD5,
|
||||||
|
[contextTools.GREEN_B]: 0x97F294,
|
||||||
|
[contextTools.GREEN_C]: 0x00EA01,
|
||||||
|
|
||||||
|
[contextTools.BLACK_A]: 0xF4F4F4,
|
||||||
|
[contextTools.BLACK_B]: 0xAAAAAA,
|
||||||
|
[contextTools.BLACK_C]: 0x444444
|
||||||
};
|
};
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import * as contextTools from './contextTools.js';
|
|
||||||
import * as d2Constants from './d2Constants.js';
|
|
||||||
import * as d2Tools from './d2Tools.js';
|
|
||||||
import * as d3Constants from './d3Constants.js';
|
|
||||||
import * as d3Tools from './d3Tools.js';
|
|
||||||
import * as exportConstants from './exportConstants.js';
|
|
||||||
import * as general from './general.js';
|
|
||||||
import * as saveConstants from './saveConstants.js';
|
|
||||||
import * as shapeTypeProperties from './shapeTypeProperties.js';
|
|
||||||
import * as mainMenuItems from './mainMenuItems.js';
|
|
||||||
import * as menu from './menu.js';
|
|
||||||
|
|
||||||
export {
|
|
||||||
contextTool,
|
|
||||||
d2Constant,
|
|
||||||
d2Tool,
|
|
||||||
d3Constant,
|
|
||||||
d3Tool,
|
|
||||||
exportConstant,
|
|
||||||
genera,
|
|
||||||
saveConstant,
|
|
||||||
shapeTypePropertie,
|
|
||||||
mainMenuItems,
|
|
||||||
menu
|
|
||||||
};
|
|
@ -46,7 +46,7 @@ const toolbar3d = {
|
|||||||
children: [
|
children: [
|
||||||
{ value: d3Tools.HEIGHT },
|
{ value: d3Tools.HEIGHT },
|
||||||
{ value: d3Tools.SCULPT },
|
{ value: d3Tools.SCULPT },
|
||||||
{ value: d3Tools.TWIST },
|
{ value: d3Tools.TWIST }
|
||||||
// { value: d3Tools.STAMP }
|
// { value: d3Tools.STAMP }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { shapeDataToShape, determineActiveShape2d } from '../shape/shapeDataUtils.js';
|
import { shapeDataToShape, determineActiveShape2d } from '../shape/shapeDataUtils.js';
|
||||||
// import R from 'ramda';
|
import _ from 'lodash';
|
||||||
|
|
||||||
export default class ShapesManager {
|
export default class ShapesManager {
|
||||||
constructor(objectContainerActive, objectContainerInactive) {
|
constructor(objectContainerActive, objectContainerInactive) {
|
||||||
@ -21,11 +21,9 @@ export default class ShapesManager {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
this._objectsById === objectsById &&
|
this._objectsById === objectsById &&
|
||||||
true &&
|
_.isEqual(activeShapes, this._activeShapes) &&
|
||||||
state.activeSpace === this._activeSpace
|
state.activeSpace === this._activeSpace
|
||||||
) {
|
) return needRender;
|
||||||
return needRender;
|
|
||||||
}
|
|
||||||
|
|
||||||
// object ids that are in the current space
|
// object ids that are in the current space
|
||||||
const spaceObjectIds = state.spaces[state.activeSpace].objectIds;
|
const spaceObjectIds = state.spaces[state.activeSpace].objectIds;
|
||||||
|
9
src/d3/MatcapMaterial.js
vendored
9
src/d3/MatcapMaterial.js
vendored
@ -12,9 +12,9 @@ export default class MatcapMaterial extends THREE.ShaderMaterial {
|
|||||||
constructor({ color = new THREE.Color(), opacity = 1 }) {
|
constructor({ color = new THREE.Color(), opacity = 1 }) {
|
||||||
super({
|
super({
|
||||||
uniforms: {
|
uniforms: {
|
||||||
"opacity": { type: 'f', value: opacity },
|
opacity: { type: 'f', value: opacity },
|
||||||
"tMatcap": { type: 't', value: matcapTexture },
|
tMatcap: { type: 't', value: matcapTexture },
|
||||||
"color": { type: 'vec3', value: new THREE.Vector3() }
|
color: { type: 'vec3', value: new THREE.Vector3() }
|
||||||
},
|
},
|
||||||
vertexShader: matcapVert,
|
vertexShader: matcapVert,
|
||||||
fragmentShader: matcapFrag
|
fragmentShader: matcapFrag
|
||||||
@ -35,7 +35,8 @@ export default class MatcapMaterial extends THREE.ShaderMaterial {
|
|||||||
|
|
||||||
set opacity(opacity) {
|
set opacity(opacity) {
|
||||||
if (!this.uniforms) return opacity;
|
if (!this.uniforms) return opacity;
|
||||||
return this.uniforms.opacity.value = opacity;
|
this.uniforms.opacity.value = opacity;
|
||||||
|
return opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
|
75
src/d3/RenderChain.js
vendored
75
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() {
|
||||||
@ -62,7 +81,7 @@ export default class RenderChain extends THREE.EffectComposer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSize(width, height, pixelRatio, rerender = true) {
|
setSize(width, height, pixelRatio, render = true) {
|
||||||
this._renderer.setPixelRatio(pixelRatio);
|
this._renderer.setPixelRatio(pixelRatio);
|
||||||
this._renderer.setSize(width, height);
|
this._renderer.setSize(width, height);
|
||||||
super.setSize(width * pixelRatio, height * pixelRatio);
|
super.setSize(width * pixelRatio, height * pixelRatio);
|
||||||
@ -70,17 +89,17 @@ export default class RenderChain extends THREE.EffectComposer {
|
|||||||
this._camera.aspect = width / height;
|
this._camera.aspect = width / height;
|
||||||
this._camera.updateProjectionMatrix();
|
this._camera.updateProjectionMatrix();
|
||||||
|
|
||||||
if (rerender) this.render();
|
if (render) this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
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]);
|
||||||
}
|
}
|
||||||
|
71
src/d3/ShapeMesh.js
vendored
71
src/d3/ShapeMesh.js
vendored
@ -37,6 +37,7 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
this._holeMesh.name = shapeData.UID;
|
this._holeMesh.name = shapeData.UID;
|
||||||
this._holeMesh.isShapeMesh = true;
|
this._holeMesh.isShapeMesh = true;
|
||||||
|
|
||||||
|
this._reverse = shapeData.transform.sx > 0 !== shapeData.transform.sy > 0;
|
||||||
this._sculpt = sculpt;
|
this._sculpt = sculpt;
|
||||||
this._rotate = rotate;
|
this._rotate = rotate;
|
||||||
this._twist = twist;
|
this._twist = twist;
|
||||||
@ -47,12 +48,8 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
this._color = color;
|
this._color = color;
|
||||||
this.updateSolid(solid, active);
|
this.updateSolid(solid, active);
|
||||||
this.updatePoints(shapeData);
|
this.updatePoints(shapeData);
|
||||||
}
|
|
||||||
|
|
||||||
_isReverse() {
|
this._shapeData = shapeData;
|
||||||
const sx = this._transform.sx > 0;
|
|
||||||
const sy = this._transform.sy > 0;
|
|
||||||
return sx !== sy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
add(object) {
|
add(object) {
|
||||||
@ -66,22 +63,34 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
if (!this._solid) return false;
|
if (!this._solid) return false;
|
||||||
if (holes === this._holes && !this._changedGeometry) return false;
|
if (holes === this._holes && !this._changedGeometry) return false;
|
||||||
|
|
||||||
this._holeMesh.geometry.dispose();
|
const fill = this._shapeData.type === 'EXPORT_SHAPE' ? !this._shapeData.originalFill : !this._fill;
|
||||||
|
if (holes.length === 0 || fill) {
|
||||||
|
if (this._holeMeshIsOriginal && !this._changedGeometry) return false;
|
||||||
|
|
||||||
if (holes === null || !this._fill || this._type === 'EXPORT_SHAPE') {
|
this._holeMesh.geometry.dispose();
|
||||||
this._holeMesh.geometry = new THREE.Geometry().fromBufferGeometry(this._mesh.geometry);
|
this._holeMesh.geometry = new THREE.Geometry().fromBufferGeometry(this._mesh.geometry);
|
||||||
|
this._holeMeshIsOriginal = true;
|
||||||
|
this._changedGeometry = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const objectGeometry = new THREE.Geometry().fromBufferGeometry(this._mesh.geometry);
|
const objectGeometry = new THREE.Geometry().fromBufferGeometry(this._mesh.geometry);
|
||||||
objectGeometry.mergeVertices();
|
objectGeometry.mergeVertices();
|
||||||
let objectBSP = new THREE_BSP(objectGeometry);
|
const box = new THREE.Box3().setFromPoints(objectGeometry.vertices);
|
||||||
|
let bsp = new THREE_BSP(objectGeometry);
|
||||||
objectGeometry.dispose();
|
objectGeometry.dispose();
|
||||||
objectBSP = objectBSP.subtract(holes);
|
|
||||||
this._holeMesh.geometry = objectBSP.toMesh().geometry;
|
for (const hole of holes) {
|
||||||
|
if (hole.box.intersectsBox(box)) {
|
||||||
|
bsp = bsp.subtract(hole.bsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._holeMesh.geometry = bsp.toMesh().geometry;
|
||||||
|
|
||||||
this._holes = holes;
|
this._holes = holes;
|
||||||
this._changedGeometry = false;
|
this._changedGeometry = false;
|
||||||
|
this._holeMeshIsOriginal = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,6 +203,12 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
throw new Error(`Cannot update object ${this.name}: transform contains invalid values.`);
|
throw new Error(`Cannot update object ${this.name}: transform contains invalid values.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reverse = this._transform.sx > 0 !== this._transform.sy > 0;
|
||||||
|
if (reverse !== this._reverse) {
|
||||||
|
this._reverse = reverse;
|
||||||
|
this._updateFaces();
|
||||||
|
}
|
||||||
|
|
||||||
this._transform = transform;
|
this._transform = transform;
|
||||||
this._z = z;
|
this._z = z;
|
||||||
this._updateVertices();
|
this._updateVertices();
|
||||||
@ -258,7 +273,6 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
_updateVerticesHorizontal(heightStep, paths, center, indexCounter) {
|
_updateVerticesHorizontal(heightStep, paths, center, indexCounter) {
|
||||||
for (let pathindex = 0; pathindex < paths.length; pathindex ++) {
|
for (let pathindex = 0; pathindex < paths.length; pathindex ++) {
|
||||||
const path = applyMatrixOnPath(paths[pathindex], this._transform);
|
const path = applyMatrixOnPath(paths[pathindex], this._transform);
|
||||||
if (this._isReverse()) path.reverse();
|
|
||||||
|
|
||||||
for (let pathIndex = 0; pathIndex < path.length; pathIndex ++) {
|
for (let pathIndex = 0; pathIndex < path.length; pathIndex ++) {
|
||||||
let point = path[pathIndex];
|
let point = path[pathIndex];
|
||||||
@ -291,7 +305,6 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
|
|
||||||
for (let pathsIndex = 0; pathsIndex < paths.length; pathsIndex ++) {
|
for (let pathsIndex = 0; pathsIndex < paths.length; pathsIndex ++) {
|
||||||
const path = applyMatrixOnPath(paths[pathsIndex], this._transform);
|
const path = applyMatrixOnPath(paths[pathsIndex], this._transform);
|
||||||
if (this._isReverse()) path.reverse();
|
|
||||||
|
|
||||||
for (let pathIndex = 0; pathIndex < path.length; pathIndex ++) {
|
for (let pathIndex = 0; pathIndex < path.length; pathIndex ++) {
|
||||||
const point = path[pathIndex];
|
const point = path[pathIndex];
|
||||||
@ -405,11 +418,6 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
})
|
})
|
||||||
.map(path => path.map(({ x, y }) => new THREE.Vector2(x, y)));
|
.map(path => path.map(({ x, y }) => new THREE.Vector2(x, y)));
|
||||||
|
|
||||||
if (this._isReverse()) {
|
|
||||||
points.reverse();
|
|
||||||
holes.map(hole => hole.reverse());
|
|
||||||
}
|
|
||||||
|
|
||||||
// triangulate
|
// triangulate
|
||||||
const triangulatedTop = THREE.ShapeUtils.triangulateShape(points, holes)
|
const triangulatedTop = THREE.ShapeUtils.triangulateShape(points, holes)
|
||||||
.reduce((a, b) => a.concat(b), [])
|
.reduce((a, b) => a.concat(b), [])
|
||||||
@ -419,12 +427,7 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
// reverse index order for bottom so faces are flipped
|
// reverse index order for bottom so faces are flipped
|
||||||
const triangulatedBottom = triangulatedTop
|
const triangulatedBottom = triangulatedTop
|
||||||
.map(value => value + numPoints)
|
.map(value => value + numPoints)
|
||||||
|
.reverse();
|
||||||
if (this._isReverse()) {
|
|
||||||
triangulatedTop.reverse();
|
|
||||||
} else {
|
|
||||||
triangulatedBottom.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
triangulatedIndexes.push(triangulatedBottom.concat(triangulatedTop));
|
triangulatedIndexes.push(triangulatedBottom.concat(triangulatedTop));
|
||||||
|
|
||||||
@ -453,6 +456,7 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
const { shape } = this._shapes[i];
|
const { shape } = this._shapes[i];
|
||||||
|
|
||||||
if (this._fill) {
|
if (this._fill) {
|
||||||
|
if (this._reverse) triangulatedIndexes[i].reverse();
|
||||||
for (let j = 0; j < triangulatedIndexes[i].length; j ++) {
|
for (let j = 0; j < triangulatedIndexes[i].length; j ++) {
|
||||||
indexes[indexCounter ++] = triangulatedIndexes[i][j];
|
indexes[indexCounter ++] = triangulatedIndexes[i][j];
|
||||||
}
|
}
|
||||||
@ -466,14 +470,23 @@ class ShapeMesh extends THREE.Object3D {
|
|||||||
let base = (pointIndexOffset + pointIndex) * numHeightSteps + vertexOffsets[i];
|
let base = (pointIndexOffset + pointIndex) * numHeightSteps + vertexOffsets[i];
|
||||||
|
|
||||||
for (let heightStep = 0; heightStep < (numHeightSteps - 1); heightStep ++) {
|
for (let heightStep = 0; heightStep < (numHeightSteps - 1); heightStep ++) {
|
||||||
indexes[indexCounter ++] = base;
|
if (this._reverse) {
|
||||||
indexes[indexCounter ++] = base + numHeightSteps;
|
indexes[indexCounter ++] = base + 1;
|
||||||
indexes[indexCounter ++] = base + 1;
|
indexes[indexCounter ++] = base + numHeightSteps;
|
||||||
|
indexes[indexCounter ++] = base;
|
||||||
|
|
||||||
indexes[indexCounter ++] = base + 1;
|
indexes[indexCounter ++] = base + numHeightSteps + 1;
|
||||||
indexes[indexCounter ++] = base + numHeightSteps;
|
indexes[indexCounter ++] = base + numHeightSteps;
|
||||||
indexes[indexCounter ++] = base + numHeightSteps + 1;
|
indexes[indexCounter ++] = base + 1;
|
||||||
|
} else {
|
||||||
|
indexes[indexCounter ++] = base;
|
||||||
|
indexes[indexCounter ++] = base + numHeightSteps;
|
||||||
|
indexes[indexCounter ++] = base + 1;
|
||||||
|
|
||||||
|
indexes[indexCounter ++] = base + 1;
|
||||||
|
indexes[indexCounter ++] = base + numHeightSteps;
|
||||||
|
indexes[indexCounter ++] = base + numHeightSteps + 1;
|
||||||
|
}
|
||||||
base ++;
|
base ++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
40
src/d3/ShapesManager.js
vendored
40
src/d3/ShapesManager.js
vendored
@ -14,7 +14,7 @@ export default class ShapesManager extends THREE.Object3D {
|
|||||||
this._spaces = {};
|
this._spaces = {};
|
||||||
this.name = 'shapes-manager';
|
this.name = 'shapes-manager';
|
||||||
|
|
||||||
this._holes = null;
|
this._holes = [];
|
||||||
|
|
||||||
// this._edges = {};
|
// this._edges = {};
|
||||||
}
|
}
|
||||||
@ -77,23 +77,33 @@ export default class ShapesManager extends THREE.Object3D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (holesChanged) {
|
if (holesChanged) {
|
||||||
this._holes = null;
|
this._holes = [];
|
||||||
for (let i = 0; i < ids.length; i ++) {
|
for (let i = 0; i < ids.length; i ++) {
|
||||||
const id = ids[i];
|
const id = ids[i];
|
||||||
|
if (activeShapes[id]) continue;
|
||||||
|
|
||||||
const { solid, type } = state.objectsById[id];
|
const { solid, type } = state.objectsById[id];
|
||||||
const d3Visible = SHAPE_TYPE_PROPERTIES[type].D3Visible;
|
const d3Visible = SHAPE_TYPE_PROPERTIES[type].D3Visible;
|
||||||
if (!solid && d3Visible) {
|
if (solid || !d3Visible) continue;
|
||||||
const hole = this._meshes[id].mesh._mesh;
|
|
||||||
const holeGeometry = new THREE.Geometry().fromBufferGeometry(hole.geometry);
|
const holeMesh = this._meshes[id].mesh._mesh;
|
||||||
if (holeGeometry.vertices.length === 0) continue;
|
const geometry = new THREE.Geometry().fromBufferGeometry(holeMesh.geometry);
|
||||||
const holeBSP = new THREE_BSP(holeGeometry);
|
if (geometry.vertices.length === 0) continue;
|
||||||
if (!this._holes) {
|
|
||||||
this._holes = holeBSP;
|
const box = new THREE.Box3().setFromPoints(geometry.vertices);
|
||||||
} else {
|
const intersectHole = this._holes.find(hole => hole.box.intersectsBox(box));
|
||||||
this._holes = this._holes.union(holeBSP);
|
|
||||||
}
|
const bsp = new THREE_BSP(geometry);
|
||||||
holeGeometry.dispose();
|
|
||||||
|
if (intersectHole) {
|
||||||
|
intersectHole.bsp = intersectHole.bsp.union(bsp);
|
||||||
|
const min = box.min.min(intersectHole.box.min);
|
||||||
|
const max = box.max.max(intersectHole.box.max);
|
||||||
|
intersectHole.box = new THREE.Box3(min, max);
|
||||||
|
} else {
|
||||||
|
this._holes.push({ bsp, box });
|
||||||
}
|
}
|
||||||
|
geometry.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,9 +114,7 @@ export default class ShapesManager extends THREE.Object3D {
|
|||||||
const d3Visible = SHAPE_TYPE_PROPERTIES[type].D3Visible;
|
const d3Visible = SHAPE_TYPE_PROPERTIES[type].D3Visible;
|
||||||
if (!active && solid && d3Visible) {
|
if (!active && solid && d3Visible) {
|
||||||
const shape = this._meshes[id].mesh;
|
const shape = this._meshes[id].mesh;
|
||||||
if (shape.updateHoleGeometry(this._holes)) {
|
if (shape.updateHoleGeometry(this._holes)) render = true;
|
||||||
render = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, delta, maskActive) {
|
||||||
|
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) {
|
||||||
@ -26,8 +26,8 @@ export default class OutlinePass {
|
|||||||
|
|
||||||
this._edgeMaterial = new THREE.ShaderMaterial({
|
this._edgeMaterial = new THREE.ShaderMaterial({
|
||||||
uniforms: {
|
uniforms: {
|
||||||
"tDiffuse": { type: 't', value: this._depthNormalRenderTarget.texture },
|
tDiffuse: { type: 't', value: this._depthNormalRenderTarget.texture },
|
||||||
"resolution": { type: 'v2', value: new THREE.Vector2() }
|
resolution: { type: 'v2', value: new THREE.Vector2() }
|
||||||
},
|
},
|
||||||
vertexShader: edgeVert,
|
vertexShader: edgeVert,
|
||||||
fragmentShader: edgeFrag
|
fragmentShader: edgeFrag
|
||||||
@ -35,12 +35,12 @@ export default class OutlinePass {
|
|||||||
|
|
||||||
this._copyEdge = new THREE.ShaderMaterial({
|
this._copyEdge = new THREE.ShaderMaterial({
|
||||||
uniforms: {
|
uniforms: {
|
||||||
"tDiffuse": { type: 't', value: null },
|
tDiffuse: { type: 't', value: null },
|
||||||
"uTexArray" : { type: 'tv', value: [this._edgeRenderTarget.texture] }
|
uTexArray : { type: 'tv', value: [this._edgeRenderTarget.texture] }
|
||||||
},
|
},
|
||||||
vertexShader: combineVert,
|
vertexShader: combineVert,
|
||||||
fragmentShader: combineFrag
|
fragmentShader: combineFrag
|
||||||
})
|
});
|
||||||
|
|
||||||
this._camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
this._camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
||||||
this._scene = new THREE.Scene();
|
this._scene = new THREE.Scene();
|
||||||
@ -55,7 +55,7 @@ export default class OutlinePass {
|
|||||||
this._edgeMaterial.uniforms.resolution.value.set(width, height);
|
this._edgeMaterial.uniforms.resolution.value.set(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(renderer, writeBuffer, readBuffer, delta, maskActive) {
|
render(renderer, writeBuffer, readBuffer) {
|
||||||
if (this._callbackBeforeRender) this._callbackBeforeRender();
|
if (this._callbackBeforeRender) this._callbackBeforeRender();
|
||||||
|
|
||||||
this._copyEdge.uniforms.tDiffuse.value = readBuffer.texture;
|
this._copyEdge.uniforms.tDiffuse.value = readBuffer.texture;
|
7
src/d3/index.js
vendored
7
src/d3/index.js
vendored
@ -1,7 +0,0 @@
|
|||||||
import createSceneData from './createSceneData.js';
|
|
||||||
import createScene from './createScene.js';
|
|
||||||
import RenderChain from './RenderChain.js';
|
|
||||||
import ShapeMesh from './ShapeMesh.js';
|
|
||||||
import ShapesManager from './ShapesManager.js';
|
|
||||||
|
|
||||||
export { createSceneData, createScene, RenderChain, ToonShaderRenderChain, ShapeMesh, ShapesManager };
|
|
6
src/d3/transformers/HeightTransformer.js
vendored
6
src/d3/transformers/HeightTransformer.js
vendored
@ -38,7 +38,7 @@ export default class HeightTransformer extends BaseTransformer {
|
|||||||
dragStart(event) {
|
dragStart(event) {
|
||||||
const handle = this.includesHandle(event.intersections);
|
const handle = this.includesHandle(event.intersections);
|
||||||
if (handle) {
|
if (handle) {
|
||||||
this.dispatch(actions.heightStart(handle));
|
this.dispatch(actions.changeHeightStart(handle));
|
||||||
} else {
|
} else {
|
||||||
super.dragStart(event);
|
super.dragStart(event);
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ export default class HeightTransformer extends BaseTransformer {
|
|||||||
drag(event) {
|
drag(event) {
|
||||||
if (this._active) {
|
if (this._active) {
|
||||||
const delta = event.position.subtract(event.previousPosition);
|
const delta = event.position.subtract(event.previousPosition);
|
||||||
this.dispatch(actions.height(delta));
|
this.dispatch(actions.changeHeight(delta));
|
||||||
} else {
|
} else {
|
||||||
super.drag(event);
|
super.drag(event);
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ export default class HeightTransformer extends BaseTransformer {
|
|||||||
|
|
||||||
dragEnd(event) {
|
dragEnd(event) {
|
||||||
if (this._active) {
|
if (this._active) {
|
||||||
this.dispatch(actions.heightEnd());
|
this.dispatch(actions.changeHeightEnd());
|
||||||
} else {
|
} else {
|
||||||
super.dragEnd(event);
|
super.dragEnd(event);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
import * as shape from './shape/index.js';
|
|
||||||
import * as utils from './utils/index.js';
|
|
||||||
import * as d3 from './d3/index.js';
|
|
||||||
import * as components from './components/index.js';
|
|
||||||
import * as constants from './constants/index.js';
|
|
||||||
import * as actions from './actions/index.js';
|
|
||||||
import * as reducer from './reducer/index.js';
|
|
||||||
|
|
||||||
export { shape, utils, d3, components, constants, actions, reducer };
|
|
@ -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, COLOR_HEX_TO_STRING } from '../constants/general.js';
|
import { COLOR_STRING_TO_HEX } 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';
|
||||||
|
@ -139,6 +139,16 @@ export default function penReducer(state, action) {
|
|||||||
.filter(({ distance }) => distance < snappingDistance)
|
.filter(({ distance }) => distance < snappingDistance)
|
||||||
.sort((a, b) => a.distance - b.distance);
|
.sort((a, b) => a.distance - b.distance);
|
||||||
|
|
||||||
|
const hits = snappingPoints.map(snappingPoint => snappingPoint.hit);
|
||||||
|
if (hits.includes('start-end') && hits.includes('end-end')) {
|
||||||
|
const index = Math.max(hits.indexOf('start-end') && hits.indexOf('end-end'));
|
||||||
|
snappingPoints.splice(index, 1);
|
||||||
|
}
|
||||||
|
if (hits.includes('start-start') && hits.includes('end-start')) {
|
||||||
|
const index = Math.max(hits.indexOf('start-start') && hits.indexOf('end-start'));
|
||||||
|
snappingPoints.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
// the active shape's start and end points can only be connected to one other shape,
|
// the active shape's start and end points can only be connected to one other shape,
|
||||||
// this variable can be used to check if the start or end point is already been snapped to
|
// this variable can be used to check if the start or end point is already been snapped to
|
||||||
// when the point has a connection it stores the shape UID of the connected shape
|
// when the point has a connection it stores the shape UID of the connected shape
|
||||||
|
@ -8,7 +8,7 @@ import { COLOR_STRING_TO_HEX } 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';
|
||||||
import { defaultCamera, cameraReducer } from './d3/tools/cameraReducer.js';
|
import { defaultCamera } from './d3/tools/cameraReducer.js';
|
||||||
import d2AddImageReducer from './d2/addImageReducer.js';
|
import d2AddImageReducer from './d2/addImageReducer.js';
|
||||||
import d2ToolReducer from './d2/toolReducer.js';
|
import d2ToolReducer from './d2/toolReducer.js';
|
||||||
import d3ToolReducer from './d3/toolReducer.js';
|
import d3ToolReducer from './d3/toolReducer.js';
|
||||||
@ -19,9 +19,7 @@ import selectionReducer from './selectionReducer.js';
|
|||||||
import selectionOperationReducer from './selectionOperationReducer.js';
|
import selectionOperationReducer from './selectionOperationReducer.js';
|
||||||
import contextReducer from './contextReducer.js';
|
import contextReducer from './contextReducer.js';
|
||||||
import { Matrix, Vector } from 'cal';
|
import { Matrix, Vector } from 'cal';
|
||||||
import {
|
import { setActiveSpace, addSpaceActive, setActive2D, getActive2D, addObject } from './objectReducers.js';
|
||||||
setActiveSpace, addSpaceActive, setActive2D, removeAllObjects, getActive2D, addObject
|
|
||||||
} from './objectReducers.js';
|
|
||||||
import menusReducer from './menusReducer.js';
|
import menusReducer from './menusReducer.js';
|
||||||
// import createDebug from 'debug';
|
// import createDebug from 'debug';
|
||||||
// const debug = createDebug('d3d:reducer:sketcher');
|
// const debug = createDebug('d3d:reducer:sketcher');
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
import docToFile from './docToFile.js';
|
|
||||||
import * as shapeToPoints from './shapeToPoints.js';
|
|
||||||
import JSONToSketchData from './JSONToSketchData.js';
|
|
||||||
import SketchDataToJSON from './SketchDataToJSON.js';
|
|
||||||
import * as shapeDataUtils from './shapeDataUtils.js';
|
|
||||||
|
|
||||||
export { docToFile, shapeToPoints, JSONToSketchData, SketchDataToJSON, shapeDataUtils };
|
|
@ -96,7 +96,7 @@ export const determineActiveShape3d = (state) => {
|
|||||||
state.d3.sculpt.activeHandle !== null ||
|
state.d3.sculpt.activeHandle !== null ||
|
||||||
state.d3.twist.active;
|
state.d3.twist.active;
|
||||||
|
|
||||||
const selectedObjects = state.selection.objects.map(({ id }) => id);
|
// const selectedObjects = state.selection.objects.map(({ id }) => id);
|
||||||
const activeShapes = {};
|
const activeShapes = {};
|
||||||
for (const id in state.objectsById) {
|
for (const id in state.objectsById) {
|
||||||
activeShapes[id] = activeTransformer || state.d2.activeShape === id;
|
activeShapes[id] = activeTransformer || state.d2.activeShape === id;
|
||||||
|
@ -83,8 +83,8 @@ function shapeToPointsRaw(shapeData) {
|
|||||||
const { radius, segment } = shapeData.circle;
|
const { radius, segment } = shapeData.circle;
|
||||||
const points = [];
|
const points = [];
|
||||||
const circumference = 2 * radius * Math.PI;
|
const circumference = 2 * radius * Math.PI;
|
||||||
const numSegments = Math.min(circumference * 2, 64);
|
const numSegments = Math.max(3, Math.min(circumference * 2, 32));
|
||||||
for (let rad = 0; rad <= segment; rad += Math.PI * 2 / numSegments) {
|
for (let rad = 0; rad < segment; rad += Math.PI * 2 / numSegments) {
|
||||||
const x = Math.sin(rad) * radius;
|
const x = Math.sin(rad) * radius;
|
||||||
const y = -Math.cos(rad) * radius;
|
const y = -Math.cos(rad) * radius;
|
||||||
points.push(new Vector(x, y));
|
points.push(new Vector(x, y));
|
||||||
|
@ -52,13 +52,13 @@ export function createThrottle() {
|
|||||||
|
|
||||||
if (!startLoop) return null;
|
if (!startLoop) return null;
|
||||||
|
|
||||||
return function loop() {
|
return (function loop() {
|
||||||
const promise = next().then(() => {
|
const promise = next().then(() => {
|
||||||
if (typeof next === 'function') return loop();
|
if (typeof next === 'function') return loop();
|
||||||
});
|
});
|
||||||
next = true;
|
next = true;
|
||||||
return promise;
|
return promise;
|
||||||
}().then(() => {
|
})().then(() => {
|
||||||
next = null;
|
next = null;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,7 @@ export function getDbUrl(db) {
|
|||||||
} else {
|
} else {
|
||||||
return `${db.protocol}${db.host}`;
|
return `${db.protocol}${db.host}`;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const dbs = {};
|
const dbs = {};
|
||||||
export function getDb(dbUrl) {
|
export function getDb(dbUrl) {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'blueimp-canvas-to-blob'; // canvas toBlob polyfill
|
|
||||||
import { Matrix } from '@doodle3d/cal';
|
import { Matrix } from '@doodle3d/cal';
|
||||||
import * as exportSTL from '@doodle3d/threejs-export-stl';
|
import * as exportSTL from '@doodle3d/threejs-export-stl';
|
||||||
import * as exportOBJ from '@doodle3d/threejs-export-obj';
|
import * as exportOBJ from '@doodle3d/threejs-export-obj';
|
||||||
@ -11,8 +10,6 @@ import { shapeToPoints } from '../shape/shapeToPoints.js';
|
|||||||
import { SHAPE_TYPE_PROPERTIES } from '../constants/shapeTypeProperties.js';
|
import { SHAPE_TYPE_PROPERTIES } from '../constants/shapeTypeProperties.js';
|
||||||
import { LINE_WIDTH } from '../constants/exportConstants.js';
|
import { LINE_WIDTH } from '../constants/exportConstants.js';
|
||||||
import { bufferToBase64 } from '../utils/binaryUtils.js';
|
import { bufferToBase64 } from '../utils/binaryUtils.js';
|
||||||
import { IMAGE_TYPE, IMAGE_QUALITY } from '../constants/saveConstants.js';
|
|
||||||
import createScene from '../d3/createScene.js';
|
|
||||||
|
|
||||||
const THREE_BSP = ThreeBSP(THREE);
|
const THREE_BSP = ThreeBSP(THREE);
|
||||||
|
|
||||||
@ -60,6 +57,7 @@ function createExportShapeData(shapeData, offsetSingleWalls, lineWidth) {
|
|||||||
...shapeData,
|
...shapeData,
|
||||||
transform: new Matrix(),
|
transform: new Matrix(),
|
||||||
type: 'EXPORT_SHAPE',
|
type: 'EXPORT_SHAPE',
|
||||||
|
originalFill: shapeData.fill,
|
||||||
fill,
|
fill,
|
||||||
shapes
|
shapes
|
||||||
};
|
};
|
||||||
@ -79,38 +77,42 @@ export function generateExportMesh(state, options = {}) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (const id in state.objectsById) {
|
for (const id in state.objectsById) {
|
||||||
exportState.objectsById[id] = createExportShapeData(state.objectsById[id], offsetSingleWalls || unionGeometry, lineWidth);
|
const shapeData = state.objectsById[id];
|
||||||
|
if (!SHAPE_TYPE_PROPERTIES[shapeData.type].D3Visible) continue;
|
||||||
|
const exportShapeData = createExportShapeData(shapeData, offsetSingleWalls || unionGeometry, lineWidth);
|
||||||
|
exportState.objectsById[id] = exportShapeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(exportState.objectsById).length === 0) {
|
||||||
|
throw new Error('sketch is empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
const shapesManager = new ShapesManager({ toonShader: false });
|
const shapesManager = new ShapesManager({ toonShader: false });
|
||||||
shapesManager.update(exportState);
|
shapesManager.update(exportState);
|
||||||
|
|
||||||
const materials = [];
|
const materials = [];
|
||||||
const objectMatrix = new THREE.Matrix4();
|
|
||||||
let exportGeometry;
|
let exportGeometry;
|
||||||
shapesManager.traverse(mesh => {
|
shapesManager.traverse(mesh => {
|
||||||
const shapeData = exportState.objectsById[mesh.name];
|
const shapeData = exportState.objectsById[mesh.name];
|
||||||
if (mesh instanceof THREE.Mesh && shapeData.solid) {
|
if (mesh instanceof THREE.Mesh && shapeData.solid) {
|
||||||
const { geometry, material } = mesh;
|
const { geometry, material } = mesh;
|
||||||
|
const objectMatrix = state.spaces[shapeData.space].matrix;
|
||||||
|
|
||||||
let objectGeometry = geometry.clone();
|
let objectGeometry = geometry.clone();
|
||||||
objectGeometry.mergeVertices();
|
objectGeometry.mergeVertices();
|
||||||
objectGeometry.applyMatrix(objectMatrix.multiplyMatrices(state.spaces[shapeData.space].matrix, matrix));
|
objectGeometry.applyMatrix(new THREE.Matrix4().multiplyMatrices(objectMatrix, matrix));
|
||||||
|
|
||||||
const colorHex = material.color.getHex();
|
const materialIndex = materials.length;
|
||||||
let materialIndex = materials.findIndex(exportMaterial => exportMaterial.color.getHex() === colorHex);
|
const exportMaterial = new THREE.MeshBasicMaterial({ color: material.color.getHex() });
|
||||||
if (materialIndex === -1) {
|
exportMaterial.side = shapeData.fill ? THREE.FrontSide : THREE.DoubleSide;
|
||||||
materialIndex = materials.length;
|
materials.push(exportMaterial);
|
||||||
materials.push(material);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unionGeometry) objectGeometry = new THREE_BSP(objectGeometry, materialIndex);
|
|
||||||
|
|
||||||
if (unionGeometry) {
|
if (unionGeometry) {
|
||||||
if (!exportGeometry) {
|
objectGeometry = new THREE_BSP(objectGeometry, materialIndex);
|
||||||
exportGeometry = objectGeometry;
|
if (exportGeometry) {
|
||||||
} else {
|
|
||||||
exportGeometry = exportGeometry.union(objectGeometry);
|
exportGeometry = exportGeometry.union(objectGeometry);
|
||||||
|
} else {
|
||||||
|
exportGeometry = objectGeometry;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!exportGeometry) exportGeometry = new THREE.Geometry();
|
if (!exportGeometry) exportGeometry = new THREE.Geometry();
|
||||||
@ -151,7 +153,7 @@ export async function createFile(state, type, options) {
|
|||||||
}
|
}
|
||||||
case 'stl-blob': {
|
case 'stl-blob': {
|
||||||
const buffer = exportSTL.fromMesh(exportMesh, true);
|
const buffer = exportSTL.fromMesh(exportMesh, true);
|
||||||
return new Blob([buffer], { type: 'application/vnd.ms-pki.stl' })
|
return new Blob([buffer], { type: 'application/vnd.ms-pki.stl' });
|
||||||
}
|
}
|
||||||
case 'obj-blob': {
|
case 'obj-blob': {
|
||||||
const buffer = await exportOBJ.fromMesh(exportMesh);
|
const buffer = await exportOBJ.fromMesh(exportMesh);
|
||||||
@ -166,34 +168,3 @@ export async function createFile(state, type, options) {
|
|||||||
throw new Error(`did not regonize type ${type}`);
|
throw new Error(`did not regonize type ${type}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateThumb(state, width, height, responseType = 'blob') {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const { render, renderer, setSize } = createScene(state);
|
|
||||||
|
|
||||||
setSize(width, height, 1.0);
|
|
||||||
render();
|
|
||||||
|
|
||||||
// possible to add encoder options for smaller file setSize
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
|
|
||||||
switch (responseType) {
|
|
||||||
case 'base64':
|
|
||||||
const base64 = renderer.domElement.toDataURL(IMAGE_TYPE, IMAGE_QUALITY);
|
|
||||||
resolve(base64);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'objectURL':
|
|
||||||
renderer.domElement.toCanvas((blob) => {
|
|
||||||
const objectURL = URL.createObjectURL(blob);
|
|
||||||
resolve(objectURL);
|
|
||||||
}, IMAGE_TYPE, IMAGE_QUALITY);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
renderer.domElement.toBlob((blob) => {
|
|
||||||
resolve(blob);
|
|
||||||
}, IMAGE_TYPE, IMAGE_QUALITY);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
34
src/utils/generateThumb.js
Normal file
34
src/utils/generateThumb.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import 'blueimp-canvas-to-blob'; // canvas toBlob polyfill
|
||||||
|
import createScene from '../d3/createScene.js';
|
||||||
|
import { IMAGE_TYPE, IMAGE_QUALITY } from '../constants/saveConstants.js';
|
||||||
|
|
||||||
|
export function generateThumb(state, width, height, responseType = 'blob') {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const { render, renderer, setSize } = createScene(state);
|
||||||
|
|
||||||
|
setSize(width, height, 1.0);
|
||||||
|
render();
|
||||||
|
|
||||||
|
// possible to add encoder options for smaller file setSize
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
|
||||||
|
switch (responseType) {
|
||||||
|
case 'base64':
|
||||||
|
const base64 = renderer.domElement.toDataURL(IMAGE_TYPE, IMAGE_QUALITY);
|
||||||
|
resolve(base64);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'objectURL':
|
||||||
|
renderer.domElement.toCanvas((blob) => {
|
||||||
|
const objectURL = URL.createObjectURL(blob);
|
||||||
|
resolve(objectURL);
|
||||||
|
}, IMAGE_TYPE, IMAGE_QUALITY);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
renderer.domElement.toBlob((blob) => {
|
||||||
|
resolve(blob);
|
||||||
|
}, IMAGE_TYPE, IMAGE_QUALITY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
import * as asyncUtils from './async.js';
|
|
||||||
import * as binaryUtils from './async.js';
|
|
||||||
import * as dbUtils from './dbUtils.js';
|
|
||||||
import * as imageUtils from './imageUtils.js';
|
|
||||||
import * as exportUtils from './exportUtils.js';
|
|
||||||
import * as webGLSupport from './webGLSupport.js';
|
|
||||||
import * as vectorUtils from './vectorUtils.js';
|
|
||||||
import * as textUtils from './textUtils.js';
|
|
||||||
import * as reactUtils from './reactUtils.js';
|
|
||||||
|
|
||||||
export { dbUtils, asyncUtils, imageUtils, exportUtils, webGLSupport, binaryUtils, vectorUtils, textUtils, reactUtils };
|
|
@ -45,5 +45,5 @@ export function asyncValidateForm(dispatch, form, asyncValidate, formData) {
|
|||||||
dispatch(stopAsyncValidation(form));
|
dispatch(stopAsyncValidation(form));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(stopAsyncValidation(form, error));
|
dispatch(stopAsyncValidation(form, error));
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { shapeToPoints } from '../shape/shapeToPoints.js'
|
import { shapeToPoints } from '../shape/shapeToPoints.js';
|
||||||
import { getPointsBounds } from '../shape/shapeDataUtils.js'
|
import { getPointsBounds } from '../shape/shapeDataUtils.js';
|
||||||
import { Vector } from 'cal';
|
import { Vector } from 'cal';
|
||||||
import arrayMemoizer from './arrayMemoizer.js';
|
import arrayMemoizer from './arrayMemoizer.js';
|
||||||
import memoize from 'memoizee';
|
import memoize from 'memoizee';
|
||||||
// import createDebug from 'debug';
|
// import createDebug from 'debug';
|
||||||
// const debug = createDebug('d3d:util:selection');
|
// const debug = createDebug('d3d:util:selection');
|
||||||
|
|
||||||
// Memoized selector that returns the same array of shapeSata's when
|
// Memoized selector that returns the same array of shapeData's when
|
||||||
// - the selection array didn't change
|
// - the selection array didn't change
|
||||||
// - the objects in the resulting array didn't change
|
// - the objects in the resulting array didn't change
|
||||||
// enables memoization of utils that use this array
|
// enables memoization of utils that use this array
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { loadImage } from './imageUtils.js'
|
import { loadImage } from './imageUtils.js';
|
||||||
import createDebug from 'debug';
|
import createDebug from 'debug';
|
||||||
const debug = createDebug('d3d:threeUtils');
|
const debug = createDebug('d3d:threeUtils');
|
||||||
|
|
||||||
|
@ -249,78 +249,37 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#color-light-blue-a {
|
#color-light-blue-a { fill: #BCFFFF; }
|
||||||
fill: #bcffff;
|
#color-light-blue-b { fill: #68E1FD; }
|
||||||
}
|
#color-light-blue-c { fill: #01B8FF; }
|
||||||
#color-light-blue-b {
|
|
||||||
fill: #69e1fd;
|
#color-dark-blue-a { fill: #C8E3FF; }
|
||||||
}
|
#color-dark-blue-b { fill: #7DACFC; }
|
||||||
#color-light-blue-c {
|
#color-dark-blue-c { fill: #0256FF; }
|
||||||
fill: #00b8ff;
|
|
||||||
}
|
#color-purple-a { fill: #EFC9FF; }
|
||||||
#color-dark-blue-a {
|
#color-purple-b { fill: #C57EFC; }
|
||||||
fill: #c8e2ff;
|
#color-purple-c { fill: #820FF9; }
|
||||||
}
|
|
||||||
#color-dark-blue-b {
|
#color-pink-a { fill: #FFC7EE; }
|
||||||
fill: #7dacfc;
|
#color-pink-b { fill: #FD7BC1; }
|
||||||
}
|
#color-pink-c { fill: #FA047B; }
|
||||||
#color-dark-blue-c {
|
|
||||||
fill: #0357ff;
|
#color-red-a { fill: #FFCDCE; }
|
||||||
}
|
#color-red-b { fill: #FD898A; }
|
||||||
#color-purple-a {
|
#color-red-c { fill: #FF2600; }
|
||||||
fill: #efc9ff;
|
|
||||||
}
|
#color-yellow-a { fill: #FFF76B; }
|
||||||
#color-purple-b {
|
#color-yellow-b { fill: #FF9201; }
|
||||||
fill: #c57efc;
|
#color-yellow-c { fill: #AA7942; }
|
||||||
}
|
|
||||||
#color-purple-c {
|
#color-green-a { fill: #DAFFD5; }
|
||||||
fill: #820ef9;
|
#color-green-b { fill: #97F294; }
|
||||||
}
|
#color-green-c { fill: #00EA01; }
|
||||||
#color-pink-a {
|
|
||||||
fill: #ffc7ee;
|
#color-black-a { fill: #F4F4F4; }
|
||||||
}
|
#color-black-b { fill: #AAAAAA; }
|
||||||
#color-pink-b {
|
#color-black-c { fill: #444444; }
|
||||||
fill: #fd7cc1;
|
|
||||||
}
|
|
||||||
#color-pink-c {
|
|
||||||
fill: #fa047b;
|
|
||||||
}
|
|
||||||
#color-red-a {
|
|
||||||
fill: #ffcdce;
|
|
||||||
}
|
|
||||||
#color-red-b {
|
|
||||||
fill: #fd898a;
|
|
||||||
}
|
|
||||||
#color-red-c {
|
|
||||||
fill: #fd898a;
|
|
||||||
}
|
|
||||||
#color-yellow-a {
|
|
||||||
fill: #fffea0;
|
|
||||||
}
|
|
||||||
#color-yellow-b {
|
|
||||||
fill: #fffb39;
|
|
||||||
}
|
|
||||||
#color-yellow-c {
|
|
||||||
fill: #fdac05;
|
|
||||||
}
|
|
||||||
#color-green-a {
|
|
||||||
fill: #daffd4;
|
|
||||||
}
|
|
||||||
#color-green-b {
|
|
||||||
fill: #97f194;
|
|
||||||
}
|
|
||||||
#color-green-c {
|
|
||||||
fill: #31d22d;
|
|
||||||
}
|
|
||||||
#color-black-a {
|
|
||||||
fill: #f4f4f4;
|
|
||||||
}
|
|
||||||
#color-black-b {
|
|
||||||
fill: #7f7f7f;
|
|
||||||
}
|
|
||||||
#color-black-c {
|
|
||||||
fill: #1f1f1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
#color-hole-material {
|
#color-hole-material {
|
||||||
fill: url(#holepattern);
|
fill: url(#holepattern);
|
||||||
|
@ -69,7 +69,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
use: [ 'style-loader', 'css-loader' ]
|
use: ['style-loader', 'css-loader']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -74,9 +74,7 @@ function floodFill(imageData, start, tolerance) {
|
|||||||
for (let i = 0; i < neighbours.length; i ++) {
|
for (let i = 0; i < neighbours.length; i ++) {
|
||||||
const neighbourIndex = neighbours[i];
|
const neighbourIndex = neighbours[i];
|
||||||
|
|
||||||
if (!done[neighbourIndex]) {
|
if (!done[neighbourIndex]) stack.push(neighbourIndex);
|
||||||
stack.push(neighbourIndex);
|
|
||||||
}
|
|
||||||
done[neighbourIndex] = true;
|
done[neighbourIndex] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user