working local file save
This commit is contained in:
parent
138d8ec091
commit
5d782cf9a9
148
dist/fs-helpers.js
vendored
Normal file
148
dist/fs-helpers.js
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2019 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* exported getFileHandle, getNewFileHandle, readFile, verifyPermission,
|
||||||
|
writeFile */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a handle to an existing file on the local file system.
|
||||||
|
*
|
||||||
|
* @return {!Promise<FileSystemFileHandle>} Handle to the existing file.
|
||||||
|
*/
|
||||||
|
function getFileHandle() {
|
||||||
|
// For Chrome 86 and later...
|
||||||
|
if ('showOpenFilePicker' in window) {
|
||||||
|
return window.showOpenFilePicker().then((handles) => handles[0]);
|
||||||
|
}
|
||||||
|
// For Chrome 85 and earlier...
|
||||||
|
return window.chooseFileSystemEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a handle to a new (text) file on the local file system.
|
||||||
|
*
|
||||||
|
* @return {!Promise<FileSystemFileHandle>} Handle to the new file.
|
||||||
|
*/
|
||||||
|
function getNewFileHandle() {
|
||||||
|
// For Chrome 86 and later...
|
||||||
|
if ('showSaveFilePicker' in window) {
|
||||||
|
const opts = {
|
||||||
|
types: [{
|
||||||
|
description: 'Text file',
|
||||||
|
accept: {'text/plain': ['.txt']},
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
return window.showSaveFilePicker(opts);
|
||||||
|
}
|
||||||
|
// For Chrome 85 and earlier...
|
||||||
|
const opts = {
|
||||||
|
type: 'save-file',
|
||||||
|
accepts: [{
|
||||||
|
description: 'Text file',
|
||||||
|
extensions: ['txt'],
|
||||||
|
mimeTypes: ['text/plain'],
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
return window.chooseFileSystemEntries(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the raw text from a file.
|
||||||
|
*
|
||||||
|
* @param {File} file
|
||||||
|
* @return {!Promise<string>} A promise that resolves to the parsed string.
|
||||||
|
*/
|
||||||
|
function readFile(file) {
|
||||||
|
// If the new .text() reader is available, use it.
|
||||||
|
if (file.text) {
|
||||||
|
return file.text();
|
||||||
|
}
|
||||||
|
// Otherwise use the traditional file reading technique.
|
||||||
|
return _readFileLegacy(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the raw text from a file.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {File} file
|
||||||
|
* @return {Promise<string>} A promise that resolves to the parsed string.
|
||||||
|
*/
|
||||||
|
function _readFileLegacy(file) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener('loadend', (e) => {
|
||||||
|
const text = e.srcElement.result;
|
||||||
|
resolve(text);
|
||||||
|
});
|
||||||
|
reader.readAsText(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the contents to disk.
|
||||||
|
*
|
||||||
|
* @param {FileSystemFileHandle} fileHandle File handle to write to.
|
||||||
|
* @param {string} contents Contents to write.
|
||||||
|
*/
|
||||||
|
async function writeFile(fileHandle, contents) {
|
||||||
|
// Support for Chrome 82 and earlier.
|
||||||
|
if (fileHandle.createWriter) {
|
||||||
|
// Create a writer (request permission if necessary).
|
||||||
|
const writer = await fileHandle.createWriter();
|
||||||
|
// Write the full length of the contents
|
||||||
|
await writer.write(0, contents);
|
||||||
|
// Close the file and write the contents to disk
|
||||||
|
await writer.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// For Chrome 83 and later.
|
||||||
|
// Create a FileSystemWritableFileStream to write to.
|
||||||
|
const writable = await fileHandle.createWritable();
|
||||||
|
// Write the contents of the file to the stream.
|
||||||
|
await writable.write(contents);
|
||||||
|
// Close the file and write the contents to disk.
|
||||||
|
await writable.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the user has granted permission to read or write to the file, if
|
||||||
|
* permission hasn't been granted, request permission.
|
||||||
|
*
|
||||||
|
* @param {FileSystemFileHandle} fileHandle File handle to check.
|
||||||
|
* @param {boolean} withWrite True if write permission should be checked.
|
||||||
|
* @return {boolean} True if the user has granted read/write permission.
|
||||||
|
*/
|
||||||
|
async function verifyPermission(fileHandle, withWrite) {
|
||||||
|
const opts = {};
|
||||||
|
if (withWrite) {
|
||||||
|
opts.writable = true;
|
||||||
|
// For Chrome 86 and later...
|
||||||
|
opts.mode = 'readwrite';
|
||||||
|
}
|
||||||
|
// Check if we already have permission, if so, return true.
|
||||||
|
if (await fileHandle.queryPermission(opts) === 'granted') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Request permission to the file, if the user grants permission, return true.
|
||||||
|
if (await fileHandle.requestPermission(opts) === 'granted') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// The user did nt grant permission, return false.
|
||||||
|
return false;
|
||||||
|
}
|
1
dist/index.html
vendored
1
dist/index.html
vendored
@ -25,6 +25,7 @@
|
|||||||
<script src="app.bundle.js"></script>
|
<script src="app.bundle.js"></script>
|
||||||
<script src="scene.bundle.js"></script>
|
<script src="scene.bundle.js"></script>
|
||||||
<script src="solver.js"></script>
|
<script src="solver.js"></script>
|
||||||
|
<script src="fs-helpers.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
1
example_parts/qwre
Normal file
1
example_parts/qwre
Normal file
File diff suppressed because one or more lines are too long
0
example_parts/test.json
Normal file
0
example_parts/test.json
Normal file
1
example_parts/test2.json
Normal file
1
example_parts/test2.json
Normal file
File diff suppressed because one or more lines are too long
0
example_parts/testt.json
Normal file
0
example_parts/testt.json
Normal file
1
example_parts/uncomp_2021-04-18T00-32-36.json
Normal file
1
example_parts/uncomp_2021-04-18T00-32-36.json
Normal file
File diff suppressed because one or more lines are too long
1
example_parts/uncomp_2021-04-18T00-33-34.json
Normal file
1
example_parts/uncomp_2021-04-18T00-33-34.json
Normal file
File diff suppressed because one or more lines are too long
10
example_parts/webpack.prod.js
Normal file
10
example_parts/webpack.prod.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const { merge } = require('webpack-merge');
|
||||||
|
|
||||||
|
const common = require('./webpack.common.js');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
|
||||||
|
mode: 'production',
|
||||||
|
|
||||||
|
});
|
28
localhost-key.pem
Normal file
28
localhost-key.pem
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDyMGwhoISvPn3Q
|
||||||
|
QBJbSHZSTM8mGgK4DwY0REvnzvZPycGOdqBRumPqDExUmDGZehDvPB9KS5rITIac
|
||||||
|
sDe/IkNB438tyvjxpwT959G0DaGf282/KsT1V7NlkHp6voP5NfzJLl4h2suzzTrF
|
||||||
|
bYiftcaTn86PhaTpHnInBOiNrDcOv3vJmDn0fUICiGD3m0q6i7G3dsNuJDCMOXMi
|
||||||
|
oCL46g4WIUiLU3uTfhHaKzDsUW+gKu6Q0sEwVd1GzTz7S4Xde77hUAjapHOFuFT8
|
||||||
|
D8WF4Ze7pZNfKx3CnQmY3dyHWjZu0GgQuypF2IXk93Q9IYts3U3phq0WzPcq4L3d
|
||||||
|
6qVsZsbbAgMBAAECggEAa5QUxA8gQROaIUIEpWWXoVEbBsqxAH8z+02HBg4JnUF6
|
||||||
|
Z8TLy+HmddVGpqEADzOIiCwFniPdOjG77afc61rV01Oxb27ki7rr3bj2jmsrqu2h
|
||||||
|
A9SErpJpTqkRrqonxzAy/E5LY/BjYZe9DmtsL7032uU2hMwRh7eNb0Wf4yZnQnXw
|
||||||
|
2aGajqF7i1R50B1uBlh/EeS+3+nR9O5YSi9abyHsYxGz8l+wSvA5uLv0dkP7xWBb
|
||||||
|
8PmMN87wEbZDV4jJyyJw4VhaFvMgjwwboTDcOgOnrGOAN31991LtBAt6ypSeOndo
|
||||||
|
Tvl4xfYA1Gs6mgCx+LtRnSoPVKj3ziLw/tdezgsT4QKBgQD4YizViNoD4HqwnDdQ
|
||||||
|
NopSMSw0gJGRw4NFvAm1Ci2WjOv1cBQaorRwu+nYfyaRlkaHcG3dHr24DW4VcUY8
|
||||||
|
VnZBRq6SvmmuXCenHXiXOykWFXFfxZSJT1svfllpAXdAPcQm5luIIYV/eGg8gBJt
|
||||||
|
OjECNFYObDMGrM03A2G3i+jxUQKBgQD5nZ8Lz5ANhZd9DEu7hJcIiBh6m7Z3JujW
|
||||||
|
U9DTDvZ51MzThgJRC2A6dU8mcJfZo3Wa+Pv1xsz0OjjG+83bMNJB0OVff5laaU50
|
||||||
|
eApOp5XsNHGzcjYoxSxxztCd7UFtf4Bu33yGDN91B8WQY1c++BpZQ4XCG8AKFzdI
|
||||||
|
HqQAAaDKawKBgQCzRvFDYxaxK6qCpQ6LmAI4lwNoFdB8HFk40SNUh7cl7is1qSLp
|
||||||
|
oryIjimYORZWiNf5VB4INvMK0K6/TVY7oNCUBvdkNYnD7wIz7eKnjWz3YpzFWq/+
|
||||||
|
d8fCPPk+AG/Zb3uP9D7mwANCYV8jI/Go4xKSm8HtgQ1HaRxp88fpGlQVMQKBgQD3
|
||||||
|
uAxR/VAZiz2WtPAnjWMR7XZVn1iKkQu7P/zaqFvE9oG7XZ/I7EA4Y5kELfMU4tpg
|
||||||
|
zL3H4N4fdfRIzTYzVBUliflIN+ppxl48ybB49Gmdu0Incq368gq0eymfwQgQcdt0
|
||||||
|
rMf4hKfyjZ7sNxorfK8xbQg+ZanEmdub8ASTmQoINwKBgHnT+FJyuhvrcnMMhS4Q
|
||||||
|
G8wvL1gbnQCBUd8+ouc9vbfE8eXFQVyGuqjToPQcKxl9rjha4X9gNw9yWa5zxOhX
|
||||||
|
5LEmewUMo1LfaryhBt08Mc2CeIbFTEveqj3Ajhn1t9wW03X7+8A8E0sPBLzpDnr0
|
||||||
|
yRyKZKdn91/5P6r1MztY2LQh
|
||||||
|
-----END PRIVATE KEY-----
|
24
localhost.pem
Normal file
24
localhost.pem
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIID9DCCAlygAwIBAgIQKfKuWsppHxEm8FuAqgxhpjANBgkqhkiG9w0BAQsFADBP
|
||||||
|
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExEjAQBgNVBAsMCWhvd2Fy
|
||||||
|
ZEBocDEZMBcGA1UEAwwQbWtjZXJ0IGhvd2FyZEBocDAeFw0yMTA0MTgwNjIxMjZa
|
||||||
|
Fw0yMzA3MTgwNjIxMjZaMD0xJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBj
|
||||||
|
ZXJ0aWZpY2F0ZTESMBAGA1UECwwJaG93YXJkQGhwMIIBIjANBgkqhkiG9w0BAQEF
|
||||||
|
AAOCAQ8AMIIBCgKCAQEA8jBsIaCErz590EASW0h2UkzPJhoCuA8GNERL5872T8nB
|
||||||
|
jnagUbpj6gxMVJgxmXoQ7zwfSkuayEyGnLA3vyJDQeN/Lcr48acE/efRtA2hn9vN
|
||||||
|
vyrE9VezZZB6er6D+TX8yS5eIdrLs806xW2In7XGk5/Oj4Wk6R5yJwTojaw3Dr97
|
||||||
|
yZg59H1CAohg95tKuouxt3bDbiQwjDlzIqAi+OoOFiFIi1N7k34R2isw7FFvoCru
|
||||||
|
kNLBMFXdRs08+0uF3Xu+4VAI2qRzhbhU/A/FheGXu6WTXysdwp0JmN3ch1o2btBo
|
||||||
|
ELsqRdiF5Pd0PSGLbN1N6YatFsz3KuC93eqlbGbG2wIDAQABo14wXDAOBgNVHQ8B
|
||||||
|
Af8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAU1LipvY+p
|
||||||
|
DhxjE8UP8QpCiAfmI3kwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEB
|
||||||
|
CwUAA4IBgQAaGHY+H63/Q7MQmRyKG3eTaW8al2pzd7MejiNSDt9R+2KeiMwmzvC8
|
||||||
|
OlslfNviatLfMqaOA5WrdpifblVRqjXZyblqeaswlA5S0gzx4JLr7yKtb4FVlwpa
|
||||||
|
Iv3/cOJB0HXEhJoWKTSKAFXgjqxUSaRbSX2tQRiRNE72shc9J5cYP3AMCloC3PUG
|
||||||
|
pbEyo+08cEHIUoZBMmjSFB2cB8JQijs3iDSCCGILCzjV0/33I0RfoFWIZZPIYFON
|
||||||
|
1zDYmaXWmpd7GAsl1rHE9o835HnyqGFy+IOovxVufSqzzRX8JlOzCzKioIfq4EjC
|
||||||
|
FLpNlZ0snDovZ6AmmH3UL1Nk2N/0WaLrrgnr58qtBqao1x1DdhcjGymjPFRyqItI
|
||||||
|
4yNnlKpaYHk5W/Z8zm8Pb1b/tCBmdlFyYyBmqkRSOqcwS/eGmV9BFZJrdU1XM4fH
|
||||||
|
0kyIOt9bDZcZWvwOcD94TUz9FNu18GDUhqBrqPAYSMlaJpe60pPiPcsE21/2usqi
|
||||||
|
H6s/L19rZxE=
|
||||||
|
-----END CERTIFICATE-----
|
65
src/Scene.js
65
src/Scene.js
@ -24,8 +24,8 @@ window.loader = new THREE.ObjectLoader();
|
|||||||
window.STLexp = new STLExporter();
|
window.STLexp = new STLExporter();
|
||||||
|
|
||||||
window.id = 0
|
window.id = 0
|
||||||
window.sid = 1
|
// window.sid = 1
|
||||||
window.mid = 1
|
// window.mid = 1
|
||||||
|
|
||||||
|
|
||||||
const pointMaterial = new THREE.PointsMaterial({
|
const pointMaterial = new THREE.PointsMaterial({
|
||||||
@ -62,7 +62,14 @@ export class Scene {
|
|||||||
controls.target.set(0, 0, 0);
|
controls.target.set(0, 0, 0);
|
||||||
controls.update();
|
controls.update();
|
||||||
|
|
||||||
this.obj3d = new THREE.Scene()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.obj3d = new THREE.Scene() ///////
|
||||||
|
|
||||||
// this.obj3d.background = new THREE.Color(color.background);
|
// this.obj3d.background = new THREE.Color(color.background);
|
||||||
const helpersGroup = new THREE.Group();
|
const helpersGroup = new THREE.Group();
|
||||||
@ -170,21 +177,30 @@ export class Scene {
|
|||||||
return needResize;
|
return needResize;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveState() {
|
|
||||||
|
|
||||||
localStorage.setItem(
|
saveScene() {
|
||||||
'sv2', JSON.stringify([id, this.sid, this.mid, this.store.getState().treeEntries])
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
saveString() {
|
|
||||||
return JSON.stringify([id, this.sid, this.mid, this.store.getState().treeEntries])
|
return JSON.stringify([id, this.sid, this.mid, this.store.getState().treeEntries])
|
||||||
}
|
}
|
||||||
|
|
||||||
loadState() { //uglyyy
|
|
||||||
|
clearScene() {
|
||||||
|
const deleted = this.obj3d.children.splice(1)
|
||||||
|
console.log(deleted)
|
||||||
|
|
||||||
|
for (let i = 0; i < deleted.length; i++) {
|
||||||
|
deleted[i].traverse((obj) => {
|
||||||
|
if (obj.geometry) obj.geometry.dispose()
|
||||||
|
if (obj.material) obj.material.dispose()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadState(file) { //uglyyy
|
||||||
|
|
||||||
|
this.clearScene()
|
||||||
|
|
||||||
const [curid, cursid, curmid, state] = JSON.parse(
|
const [curid, cursid, curmid, state] = JSON.parse(
|
||||||
localStorage.getItem('sv2')
|
file
|
||||||
)
|
)
|
||||||
|
|
||||||
window.id = curid
|
window.id = curid
|
||||||
@ -234,27 +250,6 @@ export class Scene {
|
|||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
|
|
||||||
// clearSelection() {
|
|
||||||
// for (let x = 0, obj; x < this.selected.length; x++) {
|
|
||||||
// obj = this.selected[x]
|
|
||||||
// if (obj.userData.type == 'selpoint') {
|
|
||||||
// obj.visible = false
|
|
||||||
// } else {
|
|
||||||
// setHover(obj, 0)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// this.selected = []
|
|
||||||
|
|
||||||
// for (let x = 0; x < this.hovered.length; x++) {
|
|
||||||
|
|
||||||
// const obj = this.hovered[x]
|
|
||||||
// setHover(obj, 0)
|
|
||||||
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
boolOp(m1, m2, op, refresh = false) {
|
boolOp(m1, m2, op, refresh = false) {
|
||||||
let bspA = CSG.fromMesh(m1)
|
let bspA = CSG.fromMesh(m1)
|
||||||
@ -425,7 +420,7 @@ async function addSketch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.sc = new Scene(store)
|
window.sc = new Scene(store)
|
||||||
sc.loadState()
|
// sc.loadState()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import ReactDOM from 'react-dom'
|
|||||||
import React, { } from 'react'
|
import React, { } from 'react'
|
||||||
|
|
||||||
import { createStore, applyMiddleware } from 'redux'
|
import { createStore, applyMiddleware } from 'redux'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider, useSelector } from 'react-redux'
|
||||||
import { reducer } from './reducer'
|
import { reducer } from './reducer'
|
||||||
import logger from 'redux-logger'
|
import logger from 'redux-logger'
|
||||||
|
|
||||||
@ -32,13 +32,13 @@ const store = createStore(reducer, {}, applyMiddleware(logger))
|
|||||||
// const store = createStore(reducer, sc.loadState(), applyMiddleware(logger))
|
// const store = createStore(reducer, sc.loadState(), applyMiddleware(logger))
|
||||||
|
|
||||||
|
|
||||||
const App = ({ store }) => (
|
const App = ({ store }) => {
|
||||||
<Provider store={store}>
|
return <Provider store={store}>
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<Tree />
|
<Tree />
|
||||||
<ToolTip />
|
<ToolTip />
|
||||||
</Provider>
|
</Provider>
|
||||||
)
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
ReactDOM.render(<App store={store} />, document.getElementById('react'));
|
ReactDOM.render(<App store={store} />, document.getElementById('react'));
|
||||||
|
@ -44,7 +44,7 @@ export const Dialog = () => {
|
|||||||
switch (dialog.action) {
|
switch (dialog.action) {
|
||||||
case 'extrude':
|
case 'extrude':
|
||||||
return <>
|
return <>
|
||||||
<input className='w-16 border-t-0 border-l-0 border-r-0 border-b border-gray-50 text-gray-50 mr-6' type="number" defaultValue="1" step="0.1" ref={ref} />
|
<input className='w-16 border-t-0 border-l-0 border-r-0 border-b border-gray-50 text-gray-50 mr-2' type="number" defaultValue="1" step="0.1" ref={ref} />
|
||||||
<Icon.Flip className="btn w-auto h-full p-3.5"
|
<Icon.Flip className="btn w-auto h-full p-3.5"
|
||||||
onClick={() => ref.current.value *= -1}
|
onClick={() => ref.current.value *= -1}
|
||||||
/>
|
/>
|
||||||
@ -58,7 +58,7 @@ export const Dialog = () => {
|
|||||||
</>
|
</>
|
||||||
case 'extrude-edit':
|
case 'extrude-edit':
|
||||||
return <>
|
return <>
|
||||||
<input className='w-16 border-t-0 border-l-0 border-r-0 border-b border-gray-50 text-gray-50 mr-6' type="number" defaultValue={dialog.target.userData.featureInfo[1]} step="0.1" ref={ref} />
|
<input className='w-16 border-t-0 border-l-0 border-r-0 border-b border-gray-50 text-gray-50 mr-2' type="number" defaultValue={dialog.target.userData.featureInfo[1]} step="0.1" ref={ref} />
|
||||||
<Icon.Flip className="btn w-auto h-full p-3.5"
|
<Icon.Flip className="btn w-auto h-full p-3.5"
|
||||||
onClick={() => ref.current.value *= -1}
|
onClick={() => ref.current.value *= -1}
|
||||||
/>
|
/>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const link = document.createElement( 'a' );
|
const link = document.createElement('a');
|
||||||
link.style.display = 'none';
|
link.style.display = 'none';
|
||||||
document.body.appendChild( link );
|
document.body.appendChild(link);
|
||||||
|
|
||||||
function save(blob, filename) {
|
function save(blob, filename) {
|
||||||
|
|
||||||
@ -11,30 +11,147 @@ function save(blob, filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function saveArrayBuffer( buffer, filename ) {
|
function saveArrayBuffer(buffer, filename) {
|
||||||
|
|
||||||
// save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
|
// save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
|
||||||
save( new Blob( [ buffer ], { type: 'model/stl' } ), filename );
|
save(new Blob([buffer], { type: 'model/stl' }), filename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveString( text, filename ) {
|
function saveString(text, filename) {
|
||||||
|
|
||||||
// save( new Blob( [ text ], { type: 'text/plain' } ), filename );
|
// save( new Blob( [ text ], { type: 'text/plain' } ), filename );
|
||||||
save( new Blob( [ text ], { type: 'application/json' } ), filename );
|
save(new Blob([text], { type: 'application/json' }), filename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function STLExport() {
|
export function STLExport() {
|
||||||
if (sc.selected[0] && sc.selected[0].userData.type == 'mesh') {
|
if (sc.selected[0] && sc.selected[0].userData.type == 'mesh') {
|
||||||
const result = STLexp.parse( sc.selected[0], { binary: true } );
|
const result = STLexp.parse(sc.selected[0], { binary: true });
|
||||||
saveArrayBuffer( result, 'box.stl' );
|
saveArrayBuffer(result, 'box.stl');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function savePart() {
|
export async function saveFile(fileHandle, file, dispatch) {
|
||||||
|
try {
|
||||||
|
if (!fileHandle) {
|
||||||
|
return await saveFileAs(file, dispatch);
|
||||||
|
}
|
||||||
|
await writeFile(fileHandle, file);
|
||||||
|
|
||||||
saveString( sc.saveString(), 'uncomp.json' );
|
dispatch({ type: 'set-modified', status: false })
|
||||||
|
} catch (ex) {
|
||||||
|
const msg = 'Unable to save file';
|
||||||
|
console.error(msg, ex);
|
||||||
|
alert(msg);
|
||||||
|
}
|
||||||
|
// app.setFocus();
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function saveFileAs(file, dispatch) {
|
||||||
|
let fileHandle;
|
||||||
|
try {
|
||||||
|
|
||||||
|
const opts = {
|
||||||
|
types: [{
|
||||||
|
// description: 'Text file',
|
||||||
|
accept: { 'application/json': ['.json'] },
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
fileHandle = await showSaveFilePicker(opts)
|
||||||
|
|
||||||
|
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex.name === 'AbortError') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const msg = 'An error occured trying to open the file.';
|
||||||
|
console.error(msg, ex);
|
||||||
|
alert(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const writable = await fileHandle.createWritable();
|
||||||
|
// Write the contents of the file to the stream.
|
||||||
|
await writable.write(file);
|
||||||
|
// Close the file and write the contents to disk.
|
||||||
|
await writable.close()
|
||||||
|
|
||||||
|
dispatch({ type: 'set-file-handle', fileHandle, modified: false })
|
||||||
|
|
||||||
|
} catch (ex) {
|
||||||
|
|
||||||
|
const msg = 'Unable to save file.';
|
||||||
|
console.error(msg, ex);
|
||||||
|
alert(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// app.setFocus();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
async function verifyPermission(fileHandle, withWrite) {
|
||||||
|
const opts = {};
|
||||||
|
if (withWrite) {
|
||||||
|
opts.writable = true;
|
||||||
|
// For Chrome 86 and later...
|
||||||
|
opts.mode = 'readwrite';
|
||||||
|
}
|
||||||
|
// Check if we already have permission, if so, return true.
|
||||||
|
if (await fileHandle.queryPermission(opts) === 'granted') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Request permission to the file, if the user grants permission, return true.
|
||||||
|
if (await fileHandle.requestPermission(opts) === 'granted') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// The user did nt grant permission, return false.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function openFile(dispatch) {
|
||||||
|
// if (!app.confirmDiscard()) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
let fileHandle
|
||||||
|
|
||||||
|
// If a fileHandle is provided, verify we have permission to read/write it,
|
||||||
|
// otherwise, show the file open prompt and allow the user to select the file.
|
||||||
|
try {
|
||||||
|
fileHandle = await getFileHandle();
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex.name === 'AbortError') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const msg = 'An error occured trying to open the file.';
|
||||||
|
console.error(msg, ex);
|
||||||
|
alert(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fileHandle) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const file = await fileHandle.getFile();
|
||||||
|
|
||||||
|
readFile(file, fileHandle, dispatch);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
const text = await readFile(file);
|
||||||
|
sc.loadState(text)
|
||||||
|
dispatch({ type: 'set-file-handle', fileHandle })
|
||||||
|
// app.setModified(false);
|
||||||
|
// app.setFocus(true);
|
||||||
|
} catch (ex) {
|
||||||
|
const msg = `An error occured reading ${fileHandle}`;
|
||||||
|
console.error(msg, ex);
|
||||||
|
alert(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
@ -4,33 +4,13 @@ import React, { useEffect, useReducer } from 'react';
|
|||||||
|
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
|
||||||
import { FaEdit } from 'react-icons/fa'
|
import { FaEdit, FaFileDownload } from 'react-icons/fa'
|
||||||
import { MdSave } from 'react-icons/md'
|
import { MdSave, MdFolder, MdFileUpload, MdInsertDriveFile } from 'react-icons/md'
|
||||||
import { FaFolderOpen } from 'react-icons/fa'
|
import { FaRegFolderOpen, FaFile } from 'react-icons/fa'
|
||||||
|
|
||||||
import * as Icon from "./icons";
|
import * as Icon from "./icons";
|
||||||
import { Dialog } from './dialog'
|
import { Dialog } from './dialog'
|
||||||
import { STLExport, savePart } from './fileExporter'
|
import { STLExport, savePart, saveFile, openFile } from './fileExporter'
|
||||||
|
|
||||||
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.style.display = 'none';
|
|
||||||
document.body.appendChild(link);
|
|
||||||
|
|
||||||
function save(blob, filename) {
|
|
||||||
|
|
||||||
link.href = URL.createObjectURL(blob);
|
|
||||||
link.download = filename;
|
|
||||||
link.click();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function saveArrayBuffer(buffer, filename) {
|
|
||||||
|
|
||||||
save(new Blob([buffer], { type: 'application/octet-stream' }), filename);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -39,6 +19,7 @@ export const NavBar = () => {
|
|||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const activeSketchId = useSelector(state => state.treeEntries.activeSketchId)
|
const activeSketchId = useSelector(state => state.treeEntries.activeSketchId)
|
||||||
const treeEntriesById = useSelector(state => state.treeEntries.byId)
|
const treeEntriesById = useSelector(state => state.treeEntries.byId)
|
||||||
|
const fileHandle = useSelector(state => state.ui.fileHandle)
|
||||||
|
|
||||||
const boolOp = (code) => {
|
const boolOp = (code) => {
|
||||||
if (sc.selected.length != 2 || !sc.selected.every(e => e.userData.type == 'mesh')) return
|
if (sc.selected.length != 2 || !sc.selected.every(e => e.userData.type == 'mesh')) return
|
||||||
@ -55,6 +36,9 @@ export const NavBar = () => {
|
|||||||
forceUpdate()
|
forceUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => { // hacky way to handle mounting and unmounting mouse listeners for feature mode
|
useEffect(() => { // hacky way to handle mounting and unmounting mouse listeners for feature mode
|
||||||
if (!activeSketchId) {
|
if (!activeSketchId) {
|
||||||
sc.canvas.addEventListener('pointermove', sc.onHover)
|
sc.canvas.addEventListener('pointermove', sc.onHover)
|
||||||
@ -79,6 +63,7 @@ export const NavBar = () => {
|
|||||||
[Icon.Vertical, () => sc.activeSketch.command('v'), 'Vertical [v]'],
|
[Icon.Vertical, () => sc.activeSketch.command('v'), 'Vertical [v]'],
|
||||||
[Icon.Horizontal, () => sc.activeSketch.command('h'), 'Horizontal [h]'],
|
[Icon.Horizontal, () => sc.activeSketch.command('h'), 'Horizontal [h]'],
|
||||||
[Icon.Tangent, () => sc.activeSketch.command('t'), 'Tangent [t]'],
|
[Icon.Tangent, () => sc.activeSketch.command('t'), 'Tangent [t]'],
|
||||||
|
[Icon.Tangent, () => sc.activeSketch.command('t'), 'Tangent [t]'],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -91,8 +76,11 @@ export const NavBar = () => {
|
|||||||
[Icon.Union, () => boolOp('u'), 'Union'],
|
[Icon.Union, () => boolOp('u'), 'Union'],
|
||||||
[Icon.Subtract, () => boolOp('s'), 'Subtract'],
|
[Icon.Subtract, () => boolOp('s'), 'Subtract'],
|
||||||
[Icon.Intersect, () => boolOp('i'), 'Intersect'],
|
[Icon.Intersect, () => boolOp('i'), 'Intersect'],
|
||||||
[MdSave, savePart, 'Save [ctrl+s]'],
|
[MdInsertDriveFile, savePart, 'New [ctrl+n]'],
|
||||||
[FaFolderOpen, () => boolOp('i'), 'Load'],
|
[MdSave, () => {
|
||||||
|
saveFile(fileHandle, sc.saveScene(), dispatch)
|
||||||
|
}, 'Save [ctrl+s]'],
|
||||||
|
[MdFolder, () => openFile(dispatch), 'Open'],
|
||||||
[Icon.Stl, STLExport, 'Export STL'],
|
[Icon.Stl, STLExport, 'Export STL'],
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -124,3 +112,25 @@ export const NavBar = () => {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// app.saveFile = async () => {
|
||||||
|
// try {
|
||||||
|
// if (!app.file.handle) {
|
||||||
|
// return await app.saveFileAs();
|
||||||
|
// }
|
||||||
|
// gaEvent('FileAction', 'Save');
|
||||||
|
// await writeFile(app.file.handle, app.getText());
|
||||||
|
// app.setModified(false);
|
||||||
|
// } catch (ex) {
|
||||||
|
// gaEvent('Error', 'FileSave', ex.name);
|
||||||
|
// const msg = 'Unable to save file';
|
||||||
|
// console.error(msg, ex);
|
||||||
|
// alert(msg);
|
||||||
|
// }
|
||||||
|
// app.setFocus();
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ export function treeEntries(state = defaultState, action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ui(state = { dialog: {} }, action) {
|
export function ui(state = { dialog: {}, filePane: false }, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
||||||
case 'set-dialog':
|
case 'set-dialog':
|
||||||
@ -116,6 +116,11 @@ export function ui(state = { dialog: {} }, action) {
|
|||||||
return update(state, {
|
return update(state, {
|
||||||
dialog: { $set: {} },
|
dialog: { $set: {} },
|
||||||
})
|
})
|
||||||
|
case 'set-file-handle':
|
||||||
|
return update(state, {
|
||||||
|
fileHandle: { $set: action.fileHandle },
|
||||||
|
modified: { $set: false },
|
||||||
|
})
|
||||||
default:
|
default:
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
import React, { useReducer, useState } from 'react';
|
import React, { useReducer, useState, useRef } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
import { MdVisibilityOff, MdVisibility, MdDelete, MdRefresh } from 'react-icons/md'
|
import { MdVisibilityOff, MdVisibility, MdDelete, MdRefresh } from 'react-icons/md'
|
||||||
|
|
||||||
@ -8,8 +8,10 @@ import { FaCube, FaEdit } from 'react-icons/fa'
|
|||||||
|
|
||||||
export const Tree = () => {
|
export const Tree = () => {
|
||||||
const treeEntries = useSelector(state => state.treeEntries)
|
const treeEntries = useSelector(state => state.treeEntries)
|
||||||
|
const ref = useRef()
|
||||||
|
|
||||||
return <div className='sideNav flex flex-col bg-gray-800'>
|
return <div className='sideNav flex flex-col bg-gray-800'>
|
||||||
|
<input className='w-16 text-gray-50 h-7 mx-1 border-0 focus:outline-none bg-transparent' type="text" defaultValue="untitled" step="0.1" ref={ref} />
|
||||||
{treeEntries.allIds.map((entId, idx) => (
|
{treeEntries.allIds.map((entId, idx) => (
|
||||||
<TreeEntry key={idx} entId={entId} />
|
<TreeEntry key={idx} entId={entId} />
|
||||||
))}
|
))}
|
||||||
|
7
todo.txt
7
todo.txt
@ -34,17 +34,19 @@ loopfind especially arc, // fixed for single looop, good enough, maybe stretch g
|
|||||||
dim tag delete //resolved
|
dim tag delete //resolved
|
||||||
auto update extrude // done
|
auto update extrude // done
|
||||||
extrude edit dialog // done
|
extrude edit dialog // done
|
||||||
|
file save, stl export// done
|
||||||
|
|
||||||
|
|
||||||
-sometimes unable to hit return and change dimensionk
|
-sometimes unable to hit return and change dimensionk
|
||||||
-unable to delete arc
|
-unable to delete arc
|
||||||
hover not clearing sometimes in sketch
|
hover not clearing sometimes in sketch
|
||||||
0.000 artifact
|
0.000 artifact
|
||||||
|
lighting messed up
|
||||||
|
|
||||||
|
seperate scene from init logic only init cam and rendere
|
||||||
file save, stl export
|
|
||||||
|
|
||||||
reattach sketch
|
reattach sketch
|
||||||
|
auto snap
|
||||||
|
|
||||||
highlight button to indicate active mode
|
highlight button to indicate active mode
|
||||||
|
|
||||||
@ -53,7 +55,6 @@ highlight button to indicate active mode
|
|||||||
add cancle soft button for line arc
|
add cancle soft button for line arc
|
||||||
|
|
||||||
|
|
||||||
auto snap
|
|
||||||
constraint labels,equal
|
constraint labels,equal
|
||||||
|
|
||||||
add download button, different from save button
|
add download button, different from save button
|
||||||
|
@ -2,6 +2,7 @@ const { merge } = require('webpack-merge');
|
|||||||
|
|
||||||
const common = require('./webpack.common.js');
|
const common = require('./webpack.common.js');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
|
|
||||||
@ -12,5 +13,11 @@ module.exports = merge(common, {
|
|||||||
contentBase: path.join(__dirname, 'dist'),
|
contentBase: path.join(__dirname, 'dist'),
|
||||||
compress: true,
|
compress: true,
|
||||||
port: 9000,
|
port: 9000,
|
||||||
|
https: {
|
||||||
|
key: fs.readFileSync('./localhost-key.pem'),
|
||||||
|
cert: fs.readFileSync('./localhost.pem'),
|
||||||
|
ca: fs.readFileSync('/home/howard/.local/share/mkcert/rootCA.pem'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
})
|
})
|
Loading…
Reference in New Issue
Block a user