Compare commits
105 Commits
Author | SHA1 | Date |
---|---|---|
Rick Companje | 35e4877326 | |
Rick Companje | d519033bd6 | |
Rick Companje | 7418cce606 | |
Rick Companje | de19534d9d | |
Rick Companje | 9f84cf406b | |
casper | 83d03e8a1a | |
casper | 011d9904a7 | |
Rick Companje | 353793d867 | |
Rick Companje | 395a116760 | |
Rick Companje | e11de849dd | |
Rick Companje | a0307f8ead | |
casper | 80c08d7b2c | |
casper | bb1b2b73c0 | |
casper | f613c2b68d | |
Casper Lamboo | 87b6e9851b | |
Casper Lamboo | 31e9ec735f | |
Casper Lamboo | 1a5f0fd9d0 | |
casper | a6eeb3f3f6 | |
Rick Companje | f514c9da45 | |
Rick Companje | d569a4f5b7 | |
Casper Lamboo | d43c55906b | |
Casper Lamboo | 5f7e70027a | |
Casper Lamboo | 4879f47d4c | |
Casper Lamboo | fed4146aea | |
Casper Lamboo | 9e5c929da5 | |
Casper Lamboo | 0c1cdb1580 | |
Casper Lamboo | 6260e21c6d | |
Casper Lamboo | 02e312247c | |
Casper Lamboo | 3670802bea | |
Casper Lamboo | b5e0d809c7 | |
Casper Lamboo | 023c416b5d | |
Casper Lamboo | c55750613f | |
Casper Lamboo | 853d8a5c7e | |
Casper Lamboo | b5bf5eb8c7 | |
Casper Lamboo | ac2256d2c5 | |
Casper Lamboo | b0c0063039 | |
Casper Lamboo | ca4b886c93 | |
Casper Lamboo | 582b8f8c37 | |
Casper Lamboo | fcd6bdbbf8 | |
Casper Lamboo | 0a3686d8df | |
Casper Lamboo | 24abade4b9 | |
Casper Lamboo | 7a3ee5888a | |
Casper Lamboo | ab581cf79b | |
Casper Lamboo | efb1efe3d9 | |
Rick Companje | 7360cdc85a | |
Rick Companje | d98b90bb2e | |
Casper Lamboo | 3d63e349fe | |
Casper Lamboo | 6e1660504b | |
Casper Lamboo | 2b84cd0b11 | |
Casper Lamboo | 5032018257 | |
Casper Lamboo | 6b6b860c4a | |
Casper Lamboo | 63b33c69b9 | |
Casper Lamboo | 7d34f916ac | |
Casper Lamboo | 42c2b1243f | |
Casper Lamboo | 5564e8b533 | |
Casper Lamboo | eb19034e86 | |
casperlamboo | b9ed59c8d9 | |
casperlamboo | 9996728f9c | |
casperlamboo | 02a7f64d5f | |
casperlamboo | 1d4562e2df | |
casperlamboo | 7fd9220851 | |
casperlamboo | be8a49e5c7 | |
casperlamboo | f387297195 | |
casperlamboo | 07c5d0635c | |
casperlamboo | 9101a6325a | |
casperlamboo | 68a36c29e3 | |
casperlamboo | ec22dc0cf7 | |
casperlamboo | dd8ab3263c | |
casperlamboo | da12939253 | |
casperlamboo | 12ba7f624f | |
casperlamboo | 4bd5c2f679 | |
Rick Companje | 4080f0151c | |
Rick Companje | d300a31929 | |
casperlamboo | ff09b3942a | |
casperlamboo | 4cb60940f8 | |
Rick Companje | ad94402400 | |
Rick Companje | 908050bbd0 | |
Rick Companje | 509cff2eae | |
casperlamboo | 36a73c233e | |
casperlamboo | 1b66fc21e0 | |
casperlamboo | b674769ef8 | |
casperlamboo | 83113e65b2 | |
casperlamboo | 9ce8594856 | |
casperlamboo | 9ca5fd5205 | |
casperlamboo | e8243d84e3 | |
casperlamboo | 8621ff9755 | |
casperlamboo | 87acf98f51 | |
casperlamboo | b007ff3f33 | |
casperlamboo | 7b9f647f11 | |
casperlamboo | a4a5afeb08 | |
casperlamboo | b42c7829ca | |
casperlamboo | 01bd55796e | |
casperlamboo | 2b5d240487 | |
casperlamboo | 1464a4b74f | |
casperlamboo | 04186fd56c | |
casperlamboo | 834ca531fd | |
casperlamboo | ab27e7c8c2 | |
casperlamboo | 9d1d51d357 | |
casperlamboo | e3cda119ce | |
casperlamboo | 0e6fd5fb1c | |
casperlamboo | 0c4e410a27 | |
casperlamboo | 01f4b4c1f8 | |
casperlamboo | 65592d6f48 | |
casperlamboo | c31115aa0b | |
casperlamboo | 162016bbc9 |
|
@ -13,3 +13,4 @@ plugins
|
|||
platforms
|
||||
|
||||
config.xml
|
||||
.DS_Store
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2015-2021 Doodle3D
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
14
README.md
|
@ -0,0 +1,14 @@
|
|||
This repository contains the core functionality of Doodle3D Transform.
|
||||
|
||||
## Build & run locally
|
||||
```bash
|
||||
git clone git@github.com:Doodle3D/Doodle3D-Core.git
|
||||
cd Doodle3D-Core
|
||||
npm install # Make sure you have installed NPM / NodeJS first
|
||||
npm run start
|
||||
# visit http://localhost:8080
|
||||
```
|
||||
|
||||
## Doodle3D-Transform
|
||||
The Doodle3D-Core repo just contains the core drawing features of Doodle3D Transform. To get the other features like file management, exporting and slicing you should visit the Doodle3D Transform repository: https://github.com/Doodle3D/Doodle3D-Transform
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 113 KiB |
BIN
img/bannerBg.png
Before Width: | Height: | Size: 14 KiB |
BIN
img/btnCross.png
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 630 B |
Before Width: | Height: | Size: 524 B |
Before Width: | Height: | Size: 639 B |
Before Width: | Height: | Size: 930 B |
BIN
img/btnPlus.png
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 219 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 10 KiB |
BIN
img/logo.png
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 7.0 KiB |
BIN
img/menu/new.png
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 11 KiB |
BIN
img/platform.png
Before Width: | Height: | Size: 22 KiB |
BIN
img/trace.jpg
Before Width: | Height: | Size: 146 KiB |
31
index.js
|
@ -11,6 +11,7 @@ import thunkMiddleware from 'redux-thunk';
|
|||
import promiseMiddleware from 'redux-promise-middleware';
|
||||
import { createLogger } from 'redux-logger';
|
||||
import sketcherReducer from './src/reducer/index.js';
|
||||
|
||||
const reducer = combineReducers({ sketcher: sketcherReducer });
|
||||
const enhancer = compose(applyMiddleware(thunkMiddleware, promiseMiddleware(), createLogger({ collapsed: true })));
|
||||
const store = createStore(reducer, enhancer);
|
||||
|
@ -23,16 +24,37 @@ import { saveAs as saveAsLib } from 'file-saver';
|
|||
|
||||
// download file
|
||||
import { createFile } from './src/utils/exportUtils.js';
|
||||
window.downloadStl = () => {
|
||||
import sketchDataToJSON from './src/shape/sketchDataToJSON.js';
|
||||
import { JSONToBlob } from './src/utils/binaryUtils.js';
|
||||
|
||||
// usage: downloadStl({lineWidth:20})
|
||||
window.downloadStl = (options) => {
|
||||
store.dispatch(async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const blob = await createFile(state.sketcher.present, 'stl-blob');
|
||||
const blob = await createFile(state.sketcher.present, 'stl-blob', options);
|
||||
saveAsLib(blob, 'doodle.stl');
|
||||
});
|
||||
};
|
||||
|
||||
window.downloadSketch = () => {
|
||||
store.dispatch((dispatch, getState) => {
|
||||
const state = getState();
|
||||
const json = sketchDataToJSON(state.sketcher.present);
|
||||
const blob = JSONToBlob(json);
|
||||
saveAsLib(blob, 'doodle.doodle3d');
|
||||
});
|
||||
};
|
||||
|
||||
// import keycode from 'keycode';
|
||||
// window.addEventListener('keydown', (event) => {
|
||||
// // downloadSketch
|
||||
// const key = keycode(event);
|
||||
// if (key === 's') window.downloadSketch();
|
||||
// });
|
||||
|
||||
|
||||
// add model to store
|
||||
import modelData from './models/simple_hole.d3sketch';
|
||||
import modelData from './models/circle_error.d3sketch';
|
||||
import JSONToSketchData from './src/shape/JSONToSketchData.js';
|
||||
JSONToSketchData(JSON.parse(modelData)).then(data => {
|
||||
store.dispatch(actions.openSketch({ data }));
|
||||
|
@ -42,10 +64,11 @@ JSONToSketchData(JSON.parse(modelData)).then(data => {
|
|||
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': {
|
||||
'@global': {
|
||||
'*': { margin: 0, padding: 0 },
|
||||
'#app, body, html': { height: '100%', fontFamily: 'sans-serif' },
|
||||
body: { overflow: 'auto' },
|
||||
|
|
|
@ -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"}
|
29
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@doodle3d/doodle3d-core",
|
||||
"version": "0.17.5",
|
||||
"version": "0.23.0",
|
||||
"description": "Core functions of Doodle3D Transform",
|
||||
"main": "lib",
|
||||
"module": "module",
|
||||
|
@ -10,46 +10,50 @@
|
|||
"ios": "TARGET=app webpack -p && cordova run ios",
|
||||
"prepare": "npm run build",
|
||||
"lint": "eslint src",
|
||||
"dist": "NODE_ENV=production webpack -p",
|
||||
"build": "npm run build:main && npm run build:module ",
|
||||
"build:main": "BABEL_ENV=main babel src -s -d lib",
|
||||
"build:module": "BABEL_ENV=module babel src -s -d module"
|
||||
},
|
||||
"dependencies": {
|
||||
"@doodle3d/cal": "0.0.8",
|
||||
"@doodle3d/clipper-js": "^1.0.7",
|
||||
"@doodle3d/fill-path": "^1.0.7",
|
||||
"@doodle3d/clipper-js": "^1.0.11",
|
||||
"@doodle3d/potrace-js": "0.0.6",
|
||||
"@doodle3d/threejs-export-obj": "0.0.8",
|
||||
"@doodle3d/threejs-export-stl": "0.0.5",
|
||||
"@doodle3d/touch-events": "0.0.7",
|
||||
"@doodle3d/touch-events": "0.0.9",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"bezier-js": "^2.2.3",
|
||||
"blueimp-canvas-to-blob": "^3.14.0",
|
||||
"bowser": "^1.8.1",
|
||||
"file-loader": "^0.11.2",
|
||||
"fill-path": "^1.0.11",
|
||||
"fit-curve": "^0.1.6",
|
||||
"font-loaded": "^1.0.0",
|
||||
"imports-loader": "^0.7.1",
|
||||
"jss": "^9.4.0",
|
||||
"keycode": "^2.1.9",
|
||||
"lodash": "^4.17.4",
|
||||
"memoizee": "^0.3.9",
|
||||
"pouchdb": "^6.3.4",
|
||||
"normalize-wheel": "^1.0.1",
|
||||
"proptypes": "^1.1.0",
|
||||
"raf": "^3.4.0",
|
||||
"ramda": "^0.21.0",
|
||||
"react": "^16.0.0",
|
||||
"react": "^16.0.1",
|
||||
"react-addons-update": "^15.6.2",
|
||||
"react-jss": "^7.0.2",
|
||||
"react-notification-system-redux": "^1.2.0",
|
||||
"react-redux": "^5.0.6",
|
||||
"react-resize-detector": "^1.1.0",
|
||||
"react-svg-inline": "^2.0.1",
|
||||
"react-tap-event-plugin": "^3.0.3",
|
||||
"redux-form": "^7.1.2",
|
||||
"redux-undo": "^1.0.0-beta9-9-7",
|
||||
"reselect": "^3.0.1",
|
||||
"semver": "^5.4.1",
|
||||
"shortid": "^2.2.8",
|
||||
"three": "^0.88.0",
|
||||
"three-js-csg": "github:Doodle3D/three-js-csg",
|
||||
"valid-url": "^1.0.9"
|
||||
"three-js-csg": "github:Doodle3D/three-js-csg"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "6.24.1",
|
||||
|
@ -81,8 +85,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",
|
||||
"redux-logger": "^3.0.6",
|
||||
|
@ -94,16 +96,15 @@
|
|||
"webpack-bundle-analyzer": "^2.9.1",
|
||||
"webpack-cordova-plugin": "^0.1.6",
|
||||
"webpack-dev-server": "^2.9.4",
|
||||
"worker-loader": "^1.1.0",
|
||||
"yml-loader": "^2.1.0"
|
||||
"worker-loader": "^1.1.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Doodle3D/Doodle3D-Core.git"
|
||||
},
|
||||
"author": "Casper @Doodle3D",
|
||||
"license": "UNLICENSED",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"bugs": {
|
||||
"url": "https://github.com/Doodle3D/Doodle3D-Core/issues"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
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));
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
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';
|
||||
|
@ -9,7 +8,7 @@ import { createThrottle } from '../utils/async.js';
|
|||
import { tween } from '../utils/tweenUtils.js';
|
||||
import { DEFAULT_TRACE_TOLERANCE, MAX_TRACE_TOLERANCE } from '../constants/d2Constants.js';
|
||||
import * as d2Tools from '../constants/d2Tools.js';
|
||||
import { Matrix } from 'cal';
|
||||
import { Matrix } from '@doodle3d/cal';
|
||||
// import createDebug from 'debug';
|
||||
// const debug = createDebug('d3d:actions');
|
||||
|
||||
|
@ -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';
|
||||
|
@ -86,6 +84,8 @@ export const TRACE_FLOOD_FILL = 'TRACE_FLOOD_FILL';
|
|||
export const MENU_OPEN = 'MENU_OPEN';
|
||||
export const MENU_CLOSE = 'MENU_CLOSE';
|
||||
export const OPEN_SKETCH = 'OPEN_SKETCH';
|
||||
export const SET_PREVENT_SCROLL = 'SET_PREVENT_SCROLL';
|
||||
export const SET_DISABLE_SCROLL = 'SET_DISABLE_SCROLL';
|
||||
|
||||
// CATEGORIES
|
||||
// actions that influence selected objects
|
||||
|
@ -390,14 +390,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();
|
||||
|
@ -422,25 +418,25 @@ export function traceDrag(position, start, id, screenMatrixContainer, screenMatr
|
|||
});
|
||||
};
|
||||
}
|
||||
export function traceTap(position, objects, screenMatrixContainer, screenMatrixZoom) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({ type: TRACE_TAP });
|
||||
|
||||
const state = getState();
|
||||
const id = objects.find(_id => state.sketcher.present.objectsById[_id].type === 'IMAGE_GUIDE');
|
||||
|
||||
if (id) {
|
||||
const shapeData = state.sketcher.present.objectsById[id];
|
||||
const traceStart = calculatePointInImage(position, shapeData, screenMatrixZoom);
|
||||
|
||||
const { value: traceData } = await dispatch(floodFill(DEFAULT_TRACE_TOLERANCE, shapeData, traceStart));
|
||||
|
||||
return dispatch(traceFloodFill(traceData, shapeData));
|
||||
} else {
|
||||
return dispatch(importImage());
|
||||
}
|
||||
};
|
||||
}
|
||||
// export function traceTap(position, objects, screenMatrixContainer, screenMatrixZoom) {
|
||||
// return async (dispatch, getState) => {
|
||||
// dispatch({ type: TRACE_TAP });
|
||||
//
|
||||
// const state = getState();
|
||||
// const id = objects.find(_id => state.sketcher.present.objectsById[_id].type === 'IMAGE_GUIDE');
|
||||
//
|
||||
// if (id) {
|
||||
// const shapeData = state.sketcher.present.objectsById[id];
|
||||
// const traceStart = calculatePointInImage(position, shapeData, screenMatrixZoom);
|
||||
//
|
||||
// const { value: traceData } = await dispatch(floodFill(DEFAULT_TRACE_TOLERANCE, shapeData, traceStart));
|
||||
//
|
||||
// return dispatch(traceFloodFill(traceData, shapeData));
|
||||
// } else {
|
||||
// return dispatch(importImage());
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
export function importImage() {
|
||||
return dispatch => {
|
||||
|
@ -502,3 +498,11 @@ export function menuClose(menuValue) {
|
|||
export function openSketch(data) {
|
||||
return { type: OPEN_SKETCH, data };
|
||||
}
|
||||
|
||||
export function setPreventScroll(preventScroll) {
|
||||
return { type: SET_PREVENT_SCROLL, preventScroll };
|
||||
}
|
||||
|
||||
export function setDisableScroll(disableScroll) {
|
||||
return { type: SET_DISABLE_SCROLL, disableScroll };
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@ import JSONToSketchData from '../shape/JSONToSketchData.js';
|
|||
import keycode from 'keycode';
|
||||
import bowser from 'bowser';
|
||||
import * as d2Tools from '../constants/d2Tools.js';
|
||||
import { isLoaded, load } from '../utils/loaded.js';
|
||||
import { setConfig } from '@doodle3d/touch-events';
|
||||
|
||||
setConfig({ DRAG_THRESHOLD: 0 });
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
|
@ -71,23 +75,45 @@ class App extends React.Component {
|
|||
selectAll: PropTypes.func.isRequired,
|
||||
d2ChangeTool: PropTypes.func.isRequired,
|
||||
moveSelection: PropTypes.func.isRequired,
|
||||
selectedPen: PropTypes.string.isRequired
|
||||
selectedPen: PropTypes.string.isRequired,
|
||||
preventScroll: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.container = React.createRef();
|
||||
}
|
||||
|
||||
state = {
|
||||
loaded: isLoaded()
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { container } = this.refs;
|
||||
if (!this.state.loaded) load.then(() => this.setState({ loaded: true }));
|
||||
|
||||
container.addEventListener('dragover', event => event.preventDefault());
|
||||
container.addEventListener('drop', this.onDrop);
|
||||
window.addEventListener('keydown', this.onKeyDown);
|
||||
if (this.container.current) {
|
||||
this.container.current.addEventListener('dragover', this.dragOver);
|
||||
this.container.current.addEventListener('drop', this.onDrop);
|
||||
this.container.current.addEventListener('wheel', this.onWheel);
|
||||
window.addEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { container } = this.refs;
|
||||
container.removeEventListener('drop', this.onDrop);
|
||||
window.removeEventListener('keydown', this.onKeyDown);
|
||||
if (this.container.current) {
|
||||
|
||||
this.container.current.addEventListener('dragover', this.dragOver);
|
||||
this.container.current.removeEventListener('drop', this.onDrop);
|
||||
this.container.current.addEventListener('wheel', this.onWheel);
|
||||
window.removeEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
}
|
||||
|
||||
dragOver = event => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
onDrop = async event => {
|
||||
const { openSketch, addImage } = this.props;
|
||||
event.preventDefault();
|
||||
|
@ -96,12 +122,13 @@ class App extends React.Component {
|
|||
const extentions = file.name.split('.').pop();
|
||||
|
||||
switch (extentions.toUpperCase()) {
|
||||
case 'DOODLE3D':
|
||||
case 'D3SKETCH':
|
||||
case 'JSON':
|
||||
const url = URL.createObjectURL(file);
|
||||
const data = await fetch(url).then(result => result.json());
|
||||
const sketchData = await JSONToSketchData(data);
|
||||
openSketch({ data: sketchData });
|
||||
const json = await fetch(url).then(result => result.json());
|
||||
const data = await JSONToSketchData(json);
|
||||
openSketch({ data });
|
||||
break;
|
||||
case 'JPG':
|
||||
case 'JPEG':
|
||||
|
@ -145,6 +172,29 @@ class App extends React.Component {
|
|||
}
|
||||
break;
|
||||
|
||||
case 's': {
|
||||
if (!commandKey) d2ChangeTool(d2Tools.TRANSFORM);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'e': {
|
||||
if (!commandKey) {
|
||||
event.preventDefault();
|
||||
d2ChangeTool(d2Tools.ERASER);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'c': {
|
||||
if (!commandKey) d2ChangeTool(d2Tools.CIRCLE);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'l': {
|
||||
if (!commandKey) d2ChangeTool(d2Tools.POLYGON);
|
||||
break;
|
||||
}
|
||||
|
||||
case 't': {
|
||||
if (!commandKey) d2ChangeTool(d2Tools.TEXT);
|
||||
break;
|
||||
|
@ -176,16 +226,27 @@ class App extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
onWheel = (event) => {
|
||||
const { preventScroll } = this.props;
|
||||
if (preventScroll) event.preventDefault();
|
||||
};
|
||||
|
||||
dragover = (event) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { classes, undo, redo } = this.props;
|
||||
const { loaded } = this.state;
|
||||
|
||||
return (
|
||||
<div ref="container" className={classes.container}>
|
||||
<div ref={this.container} className={classes.container}>
|
||||
<InlineIconsLoader />
|
||||
<div className={classes.appContainer}>
|
||||
{loaded && <div className={classes.appContainer}>
|
||||
<D2Panel />
|
||||
<div className={classes.vLine} />
|
||||
<D3Panel />
|
||||
</div>
|
||||
</div>}
|
||||
<Logo />
|
||||
<div className={classes.undoMenu}>
|
||||
<Button onSelect={undo} className={classes.undo} />
|
||||
|
@ -198,6 +259,7 @@ class App extends React.Component {
|
|||
}
|
||||
|
||||
export default injectSheet(styles)(connect(state => ({
|
||||
preventScroll: state.sketcher.present.preventScroll,
|
||||
selectedPen: state.sketcher.present.menus['pen-tools'].selected
|
||||
}), {
|
||||
undo: actions.undo.undo,
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
import { isWebUri } from 'valid-url';
|
||||
import PouchDB from 'pouchdb';
|
||||
// import createDebug from 'debug';
|
||||
// const debug = createDebug('d3d:FileThumb');
|
||||
|
||||
class AuthImage extends React.Component {
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func,
|
||||
src: PropTypes.string.isRequired,
|
||||
token: PropTypes.string,
|
||||
password: PropTypes.string
|
||||
};
|
||||
|
||||
state = {
|
||||
src: null
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
const { src, token, password } = this.props;
|
||||
|
||||
if (src.startsWith('/') || src.startsWith('./')) {
|
||||
this.setState({ src });
|
||||
} else if (isWebUri(src)) {
|
||||
if (token && password) {
|
||||
const filteredSrc = src.replace(/:\/\/.+?@/, '://');
|
||||
const headers = {
|
||||
Authorization: `Basic ${btoa(`${token}:${password}`)}`
|
||||
};
|
||||
fetch(filteredSrc, { headers })
|
||||
.then(response => response.blob())
|
||||
.then(blob => this.setState({ src: URL.createObjectURL(blob) }));
|
||||
} else {
|
||||
this.setState({ src });
|
||||
}
|
||||
} else {
|
||||
const [dbName, docName, attachmentName] = src.split('/');
|
||||
const db = new PouchDB(dbName);
|
||||
db.getAttachment(docName, attachmentName).then((blob) => {
|
||||
this.setState({ src: URL.createObjectURL(blob) });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
URL.revokeObjectURL(this.state.src);
|
||||
}
|
||||
|
||||
render() {
|
||||
// filter props before passing them to img element
|
||||
const props = { ...this.props };
|
||||
delete props.dispatch;
|
||||
delete props.token;
|
||||
delete props.password;
|
||||
delete props.src;
|
||||
return (<img { ...props } src={this.state.src} />);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(state => ({
|
||||
token: state.user.session.token,
|
||||
password: state.user.session.password
|
||||
}))(AuthImage);
|
|
@ -37,7 +37,7 @@ export default class Button extends React.Component {
|
|||
if (className) combinedClassName += ` ${className}`;
|
||||
|
||||
return (
|
||||
<div id={id} className={combinedClassName} onTouchTap={this.onClick}>
|
||||
<div id={id} className={combinedClassName} onClick={this.onClick}>
|
||||
{svg && <svg>
|
||||
<use xlinkHref={svg}/>
|
||||
</svg>}
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import injectSheet from 'react-jss';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import * as CAL from 'cal';
|
||||
import * as CAL from '@doodle3d/cal';
|
||||
import * as toolNames from '../constants/d2Tools';
|
||||
import { PIPETTE } from '../constants/contextTools.js';
|
||||
import { CANVAS_SIZE } from '../constants/d2Constants';
|
||||
|
@ -22,7 +22,7 @@ import { PIXEL_RATIO } from '../constants/general';
|
|||
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,9 +63,17 @@ class D2Panel extends React.Component {
|
|||
dispatch: PropTypes.func.isRequired,
|
||||
classes: PropTypes.objectOf(PropTypes.string)
|
||||
};
|
||||
state = {
|
||||
screenMatrix: new CAL.Matrix()
|
||||
};
|
||||
activeNeedRender = false;
|
||||
inactiveNeedRender = false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.canvasContainer = React.createRef();
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
// Scene space
|
||||
this.sceneActive = new EventGroup({
|
||||
|
@ -89,22 +97,17 @@ class D2Panel extends React.Component {
|
|||
this.objectContainerInactive.add(new Grid(new CAL.Color(0xdddddd)));
|
||||
|
||||
this.shapesManager = new ShapesManager(this.objectContainerActive, this.objectContainerInactive);
|
||||
loadPattern.then(() => {
|
||||
this.activeNeedRender = true;
|
||||
this.inactiveNeedRender = true;
|
||||
this.renderRequest();
|
||||
});
|
||||
|
||||
this.DOM = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { canvasContainer } = this.refs;
|
||||
this.container = canvasContainer;
|
||||
|
||||
this.container.appendChild(this.sceneInactive.image);
|
||||
this.container.appendChild(this.sceneActive.image);
|
||||
if (this.canvasContainer.current) {
|
||||
this.container = this.canvasContainer.current;
|
||||
|
||||
this.container.appendChild(this.sceneInactive.image);
|
||||
this.container.appendChild(this.sceneActive.image);
|
||||
}
|
||||
this.sceneActive.onClick = (event) => event.stopPropagation();
|
||||
}
|
||||
|
||||
|
@ -117,7 +120,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 +138,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 +157,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 +170,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 +217,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" />
|
||||
<div className={classes.canvasContainer} ref={this.canvasContainer} />
|
||||
<InputText screenMatrix={screenMatrix} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,11 +16,10 @@ 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';
|
||||
import { load as loadMatcapMaterial } from '../d3/MatcapMaterial.js';
|
||||
// import createDebug from 'debug';
|
||||
// const debug = createDebug('d3d:d3');
|
||||
|
||||
|
@ -51,6 +50,10 @@ const styles = {
|
|||
};
|
||||
|
||||
class D3Panel extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.canvasContainer = React.createRef();
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
state: PropTypes.object.isRequired,
|
||||
|
@ -62,23 +65,23 @@ 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,
|
||||
plane: this.plane
|
||||
});
|
||||
|
||||
loadMatcapMaterial.then(this.renderRequest);
|
||||
this.DOM = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.container = this.refs.canvasContainer;
|
||||
this.container.appendChild(this.renderer.domElement);
|
||||
|
||||
this.renderScene(); // immidiatly render because when THREE.JS inits, a black screen is generated
|
||||
if (this.canvasContainer.current) {
|
||||
this.container = this.canvasContainer.current;
|
||||
this.container.appendChild(this.renderer.domElement);
|
||||
this.renderScene(); // immidiatly render because when THREE.JS inits, a black screen is generated
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -223,7 +226,7 @@ class D3Panel extends React.Component {
|
|||
return (
|
||||
<div className={classes.container}>
|
||||
<ReactResizeDetector handleWidth handleHeight onResize={this.resizeHandler} />
|
||||
<div className={classes.canvasContainer} ref="canvasContainer"/>
|
||||
<div className={classes.canvasContainer} ref={this.canvasContainer}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import createScene from '../d3/createScene.js';
|
|||
import injectSheet from 'react-jss';
|
||||
import ReactResizeDetector from 'react-resize-detector';
|
||||
import requestAnimationFrame from 'raf';
|
||||
import { load as loadMatcapMaterial } from '../d3/MatcapMaterial.js';
|
||||
import createRAFOnce from '../utils/rafOnce.js';
|
||||
|
||||
const rafOnce = createRAFOnce();
|
||||
|
@ -29,6 +28,11 @@ class DoodlePreview extends React.Component {
|
|||
pixelRatio: 1
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.canvas = React.createRef();
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
classes: PropTypes.objectOf(PropTypes.string),
|
||||
width: PropTypes.number.isRequired,
|
||||
|
@ -37,7 +41,7 @@ class DoodlePreview extends React.Component {
|
|||
sketchData: PropTypes.object, // TODO
|
||||
docData: PropTypes.shape({
|
||||
appVersion: PropTypes.string,
|
||||
data: PropTypes.string
|
||||
data: PropTypes.oneOf([PropTypes.string, PropTypes.object])
|
||||
})
|
||||
};
|
||||
|
||||
|
@ -48,19 +52,15 @@ class DoodlePreview extends React.Component {
|
|||
async componentDidMount() {
|
||||
let { docData, sketchData } = this.props;
|
||||
|
||||
if (docData) sketchData = await JSONToSketchData(this.props.docData);
|
||||
if (docData) sketchData = await JSONToSketchData(docData);
|
||||
|
||||
const { canvas } = this.refs;
|
||||
const sceneData = await createSceneData(sketchData);
|
||||
|
||||
const sceneData = createSceneData(sketchData);
|
||||
|
||||
const scene = createScene(sceneData, canvas);
|
||||
const scene = createScene(sceneData, this.canvas.current);
|
||||
this.setState(scene);
|
||||
|
||||
this.editorControls = new THREE.EditorControls(scene.camera, canvas);
|
||||
this.editorControls = new THREE.EditorControls(scene.camera, this.canvas.current);
|
||||
this.editorControls.addEventListener('change', () => rafOnce(scene.render));
|
||||
|
||||
loadMatcapMaterial.then(scene.render);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -81,7 +81,7 @@ class DoodlePreview extends React.Component {
|
|||
return (
|
||||
<div className={classes.container}>
|
||||
<ReactResizeDetector handleWidth handleHeight onResize={this.resizeHandler} />
|
||||
<canvas className={classes.canvas} ref="canvas" />
|
||||
<canvas className={classes.canvas} ref={this.canvas} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
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 '@doodle3d/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 {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.text = React.createRef();
|
||||
}
|
||||
|
||||
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.text.current.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.text.current) this.text.current.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={this.text}
|
||||
spellCheck="false"
|
||||
autoFocus
|
||||
onChange={this.onInputChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(connect(state => ({
|
||||
state: state.sketcher.present
|
||||
}), {
|
||||
changeText: actions.d2textInputChange
|
||||
})(InputText));
|
|
@ -8,11 +8,12 @@ const styles = {
|
|||
top: '0px',
|
||||
right: '15%',
|
||||
width: '19%',
|
||||
backgroundImage: `url("${doodleSignImageURL}")`,
|
||||
maxWidth: '290px',
|
||||
height: '140px',
|
||||
backgroundSize: '100%',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
pointerEvents: 'none', // enable clicking through logo
|
||||
'& img': {
|
||||
width: '100%',
|
||||
maxWidth: '290px'
|
||||
}
|
||||
},
|
||||
'@media (max-width: 555px)': {
|
||||
container: {
|
||||
|
@ -22,8 +23,6 @@ const styles = {
|
|||
};
|
||||
|
||||
const Logo = ({ classes }) => (
|
||||
<div className={classes.container}>
|
||||
<img src={doodleSignImageURL} />
|
||||
</div>
|
||||
<div className={classes.container} />
|
||||
);
|
||||
export default injectSheet(styles)(Logo);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -8,6 +8,11 @@ import { hexToStyle } from '../utils/colorUtils.js';
|
|||
// const debug = createDebug('d3d:ui:submenu');
|
||||
|
||||
class SubMenu extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.li = React.createRef();
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
onSelect: PropTypes.func,
|
||||
onOpen: PropTypes.func,
|
||||
|
@ -61,13 +66,15 @@ class SubMenu extends React.Component {
|
|||
const { open, onClose, value } = this.props;
|
||||
if (open && onClose) {
|
||||
// was click on this submenu?
|
||||
const onSubmenu = this.refs.li.contains(event.target);
|
||||
const onSubmenu = this.li.current.contains(event.target);
|
||||
// debug(`onDocumentClick ${event.type} ${onSubmenu ? 'onSubmenu' : ''}`);
|
||||
if (!onSubmenu) onClose({ menuValue: value });
|
||||
}
|
||||
};
|
||||
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') {
|
||||
|
@ -77,7 +84,7 @@ class SubMenu extends React.Component {
|
|||
let className = 'submenu';
|
||||
if (open) className += ' open';
|
||||
return (
|
||||
<li id={id} className={className} ref="li" style={style}>
|
||||
<li id={id} className={className} ref={this.li} style={style}>
|
||||
<Button
|
||||
id={`${selectedValue}-menu`}
|
||||
value={selectedValue}
|
||||
|
|
|
@ -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
|
||||
];
|
||||
|
|
|
@ -21,13 +21,6 @@ export const SNAPPING_DISTANCE = 7.0;
|
|||
export const MAX_TRACE_TOLERANCE = 256;
|
||||
export const SELECTION_VIEW_MIN_SCALE = 50;
|
||||
export const SELECTION_VIEW_MIN_AXIS_SCALE = 80;
|
||||
export const POTRACE_OPTIONS = {
|
||||
turnpolicy: 'black',
|
||||
turdsize: 5.0,
|
||||
optcurve: false,
|
||||
alphamax: 0.5,
|
||||
opttolerance: 0.2
|
||||
};
|
||||
export const ERASER_SIZES = {
|
||||
[contextTools.ERASER_SIZE_SMALL]: 10,
|
||||
[contextTools.ERASER_SIZE_MEDIUM]: 30,
|
||||
|
@ -41,3 +34,4 @@ export const BRUSH_SIZES = {
|
|||
};
|
||||
|
||||
export const CLIPPER_PRECISION = 100; // accurate to the hundredth
|
||||
export const TEXT_TOOL_FONT_SIZE = 40;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as contextTools from './contextTools.js';
|
||||
|
||||
export const VERSION = '0.17.4';
|
||||
export const VERSION = '0.23.0';
|
||||
export const SHAPE_CACHE_LIMIT = 50;
|
||||
export const PIXEL_RATIO = 1.0;
|
||||
|
||||
|
@ -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'
|
||||
};
|
||||
|
|