diff --git a/modules/bus/index.js b/modules/bus/index.js
index 50294b6b..5f9244a5 100644
--- a/modules/bus/index.js
+++ b/modules/bus/index.js
@@ -5,19 +5,16 @@ export default class Bus {
this.state = {};
this.keepStateFor = new Set();
this.lock = new Set();
+ this.stateConnections = new Map();
}
- subscribe(key, callback) {
- let listenerList = this.listeners[key];
+ subscribe(token, callback) {
+ let listenerList = this.listeners[token];
if (listenerList === undefined) {
listenerList = [];
- this.listeners[key] = listenerList;
+ this.listeners[token] = listenerList;
}
listenerList.push(callback);
-
- if (this.keepStateFor.has(key)) {
- callback(this.state[key]);
- }
return callback;
};
@@ -30,7 +27,49 @@ export default class Bus {
}
}
};
+
+ tokensToStates(tokens) {
+ return tokens.map( token => this.state[token] );
+ }
+ connectToState(tokens, callback) {
+ if (!Array.isArray(tokens)) {
+ tokens = [tokens];
+ }
+
+ let connection = () => {
+ callback(this.tokensToStates(tokens));
+ };
+ tokens.forEach(token => {
+ if (!this.keepStateFor.has(token)) {
+ throw `unable to connect to stateless token ${token}. state for a token should be initialized before connecting`;
+ }
+ this.subscribe(token, connection);
+ });
+ this.stateConnections.set(connection, tokens);
+ return connection;
+ }
+
+ disconnectFromState(connection) {
+ this.stateConnections.get(connection).forEach(token => this.unSubscribe(token, connection));
+ this.stateConnections.delete(connection);
+ }
+
+ updateStates(tokens, updater) {
+ let updated = updater(this.tokensToStates(tokens));
+ for (let i = 0; i < tokens.length; ++i) {
+ this.dispatch(tokens[i], updated[i]);
+ }
+ }
+
+ updateState(token, updater) {
+ this.dispatch(token, updater(this.state[token]));
+ }
+
+ setState(token, partialState) {
+ this.dispatch(token, Object.assign({}, this.state[token], partialState));
+ }
+
dispatch(key, data) {
if (this.lock.has(key)) {
console.warn('recursive dispatch');
@@ -57,16 +96,14 @@ export default class Bus {
}
};
- enableState(forEvent, initValue) {
- this.keepStateFor.add(forEvent);
- this.state[forEvent] = initValue;
+ enableState(forToken, initValue) {
+ this.keepStateFor.add(forToken);
+ this.state[forToken] = initValue;
}
- disableState(forEvent) {
- this.keepStateFor.delete(forEvent);
- }
}
-
-
+export function createToken(...fqn) {
+ return fqn.join('.');
+}
diff --git a/modules/ui/WindowSystem.jsx b/modules/ui/WindowSystem.jsx
new file mode 100644
index 00000000..8df09111
--- /dev/null
+++ b/modules/ui/WindowSystem.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+
+export default class WindowSystem extends React.Component {
+
+ constructor() {
+ super();
+ this.state = {
+ windows: []
+ }
+ }
+
+ render() {
+ return this.state.windows;
+ }
+
+ addWindow(window) {
+ this.setState({windows: [...this.state.windows, window]});
+ }
+
+ removeWindow(window) {
+ let windows = [...this.state.windows];
+ windows.splice(windows.indexOf(window), 1);
+ this.setState({windows});
+ }
+}
diff --git a/modules/ui/components/Abs.jsx b/modules/ui/components/Abs.jsx
new file mode 100644
index 00000000..55cd633b
--- /dev/null
+++ b/modules/ui/components/Abs.jsx
@@ -0,0 +1,51 @@
+import React from 'react';
+
+export default class Abs extends React.Component {
+
+ fit() {
+ if (!this.el) {
+ return;
+ }
+ let w = this.el.offsetWidth;
+ let h = this.el.offsetHeight;
+ let holder = this.el.parentNode;
+
+ const fit = (prop, dim, holderDim) => {
+ let pos = this.props[prop];
+ if (pos !== undefined) {
+ if (pos + dim > holderDim) {
+ pos = holderDim - dim;
+ this.el.style[prop] = pos + 'px';
+ }
+ }
+ };
+
+ fit('left', w, holder.offsetWidth);
+ fit('right', w, holder.offsetWidth);
+ fit('top', h, holder.offsetHeight);
+ fit('bottom', h, holder.offsetHeight);
+ }
+
+ componentDidMount() {
+ this.fit();
+ }
+
+ componentDidUpdate() {
+ this.fit();
+ }
+
+ componentWillUnmount() {
+ this.el = undefined;
+ }
+
+ render() {
+ let {left, top, right, bottom, children, style, ...props} = this.props;
+ return
this.el = el}
+ style={{position: 'absolute', left, top, right, bottom, zIndex: 999, ...style}} {...props}>
+ {children}
+
;
+ }
+}
+
+
+
diff --git a/modules/ui/components/AuxWidget.jsx b/modules/ui/components/AuxWidget.jsx
new file mode 100644
index 00000000..a22e28db
--- /dev/null
+++ b/modules/ui/components/AuxWidget.jsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import Abs from "./Abs";
+import cx from 'classnames';
+
+import ls from './AuxWidget.less';
+
+export default function AuxWidget({flatTop, flatBottom, children, className, ...props}) {
+ return
+ {children}
+
+}
\ No newline at end of file
diff --git a/modules/ui/components/AuxWidget.less b/modules/ui/components/AuxWidget.less
new file mode 100644
index 00000000..795da87d
--- /dev/null
+++ b/modules/ui/components/AuxWidget.less
@@ -0,0 +1,16 @@
+@border-radius: 3px;
+
+.root {
+ color: #fff;
+ background-color: rgba(40, 40, 40, 0.95);
+ border: solid 1px #000;
+ border-radius: @border-radius;
+}
+
+.flatBottom {
+ border-radius: @border-radius @border-radius 0 0;
+}
+
+.flatTop {
+ border-radius: 0 0 @border-radius @border-radius;
+}
diff --git a/modules/ui/components/Card.jsx b/modules/ui/components/Card.jsx
new file mode 100644
index 00000000..76bfffa4
--- /dev/null
+++ b/modules/ui/components/Card.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+
+export default function Card({visible, children, ...props}) {
+ return
+ {children}
+
+}
\ No newline at end of file
diff --git a/modules/ui/components/Fa.jsx b/modules/ui/components/Fa.jsx
new file mode 100644
index 00000000..1865a972
--- /dev/null
+++ b/modules/ui/components/Fa.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import cx from 'classnames';
+
+export default function Fa({icon, fw, fa, stack, className, ...props}) {
+ let faCss = fa ? fa.map(s => 'fa-' + s) : [];
+ if (icon) {
+ icon = 'fa-' + icon;
+ }
+ if (fw) {
+ faCss.push('fa-fw');
+ }
+ if (stack) {
+ faCss.push('fa-stack-' + stack);
+ }
+ return
+}
\ No newline at end of file
diff --git a/modules/ui/components/Filler.jsx b/modules/ui/components/Filler.jsx
new file mode 100644
index 00000000..0245ea49
--- /dev/null
+++ b/modules/ui/components/Filler.jsx
@@ -0,0 +1,12 @@
+import React from 'react';
+
+export default function Filler({width, height, children, style, ...props}) {
+ return
+ {children}
+
+}
\ No newline at end of file
diff --git a/modules/ui/components/Folder.jsx b/modules/ui/components/Folder.jsx
new file mode 100644
index 00000000..b408bbea
--- /dev/null
+++ b/modules/ui/components/Folder.jsx
@@ -0,0 +1,35 @@
+import React from 'react';
+
+import ls from './Folder.less'
+import Fa from "./Fa";
+
+export default class Folder extends React.Component{
+
+ constructor() {
+ super();
+ this.state = {
+ closed: null
+ }
+ }
+
+ isClosed() {
+ let {closable, defaultClosed} = this.props;
+ if (!closable) return false;
+ return closable && (this.state.closed === null ? defaultClosed : this.state.closed)
+ }
+
+ tweakClose = () => {
+ this.setState({closed: !this.isClosed()});
+ };
+
+ render() {
+ let {title, closable, children} = this.props;
+ return
+
+
+ {title}
+
+ {!this.isClosed() && children}
+
+ }
+}
diff --git a/modules/ui/components/Folder.less b/modules/ui/components/Folder.less
new file mode 100644
index 00000000..bf681ead
--- /dev/null
+++ b/modules/ui/components/Folder.less
@@ -0,0 +1,16 @@
+.root {
+ color: #eee;
+ font: 11px 'Lucida Grande', sans-serif;
+ background-color: #1a1a1a;
+}
+
+.title {
+ height: 27px;
+ line-height: 27px;
+ overflow: hidden;
+ padding: 0 4px 0 5px;
+ border-bottom: 1px solid #2c2c2c;
+
+ padding-left: 16px;
+
+}
\ No newline at end of file
diff --git a/modules/ui/components/ImgIcon.jsx b/modules/ui/components/ImgIcon.jsx
new file mode 100644
index 00000000..b2f2528a
--- /dev/null
+++ b/modules/ui/components/ImgIcon.jsx
@@ -0,0 +1,13 @@
+import React from 'react';
+
+export default function ImgIcon({url, size, style, ...props}) {
+ return
+};
\ No newline at end of file
diff --git a/modules/ui/components/Menu.jsx b/modules/ui/components/Menu.jsx
new file mode 100644
index 00000000..e00b1e27
--- /dev/null
+++ b/modules/ui/components/Menu.jsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import ls from './Menu.less';
+import AuxWidget from "./AuxWidget";
+import cx from 'classnames';
+
+export default function Menu({children, visible, x, y, orientationUp, style, ...props}) {
+ return
+ {children}
+ ;
+}
+
+export function MenuSeparator() {
+ return
+}
+
+
+export function MenuItem({icon, label, hotKey, style, disabled, onClick}, {closeAllUpPopups}) {
+
+ if (hotKey) {
+ hotKey = hotKey.replace(/\s/g, '');
+ if (hotKey.length > 15) {
+ hotKey = null;
+ }
+ }
+ let clickHandler = disabled ? undefined : () => {
+ closeAllUpPopups();
+ onClick();
+ };
+ return e.stopPropagation()} style={style} onClick={clickHandler}>
+ {icon}
+ {label}
+ {hotKey && {hotKey}}
+
;
+}
+
+MenuItem.contextTypes = {
+ closeAllUpPopups: PropTypes.func
+};
diff --git a/modules/ui/components/Menu.less b/modules/ui/components/Menu.less
new file mode 100644
index 00000000..dfad12d9
--- /dev/null
+++ b/modules/ui/components/Menu.less
@@ -0,0 +1,48 @@
+@import '../styles/theme';
+
+.root {
+ padding: 0.45em 0;
+}
+
+.item {
+ padding: 0.45em 0.55em 0.45em 0.45em;
+ cursor: pointer;
+ text-transform: capitalize;
+ white-space: nowrap;
+ display: flex;
+ align-items: baseline;
+
+ &:hover {
+ background-color: #0074D9;
+ }
+ &:active {
+ background-color: #000d7f;
+ }
+
+ &.disabled:hover, &.disbaled:active {
+ background-color: #545454;
+ }
+
+
+ & .hotKey {
+ color: @font-color-suppressed;
+ font-size: 0.8em;
+ padding-left: 1.5em;
+ flex-grow: 1;
+ text-align: right;
+ }
+
+ & .label {
+ padding-left: 0.45em;
+ padding-right: 0.25em;
+ }
+}
+
+.separator {
+ border-top: solid 1px #777;
+}
+
+.disabled {
+ color: @font-color-suppressed;
+}
+
diff --git a/modules/ui/components/Row.jsx b/modules/ui/components/Row.jsx
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/ui/components/TabSwitcher.less b/modules/ui/components/TabSwitcher.less
new file mode 100644
index 00000000..0c95022d
--- /dev/null
+++ b/modules/ui/components/TabSwitcher.less
@@ -0,0 +1,35 @@
+@import "../styles/theme";
+
+.root {
+ color: @font-color-suppressed;
+}
+
+.tab {
+ @border: 1px solid @border-color;
+
+ padding: 0.3em 0.3em 0.4em 0.6em;
+ cursor: pointer;
+ border-right: @border;
+ display: inline-block;
+ &:hover {
+ color: @font-color;
+ }
+ &.active {
+ background-color: @bg-color-alt;
+ color: @font-color;
+ }
+ &:first-child {
+ border-left: @border;
+ }
+}
+
+.expand:hover {
+ color: green;
+}
+.close:hover {
+ color: red;
+}
+
+
+
+
diff --git a/modules/ui/components/TabSwticher.jsx b/modules/ui/components/TabSwticher.jsx
new file mode 100644
index 00000000..763e356a
--- /dev/null
+++ b/modules/ui/components/TabSwticher.jsx
@@ -0,0 +1,21 @@
+import React, {Fragment} from 'react';
+import Fa from 'ui/components/Fa';
+import cx from 'classnames';
+
+import ls from './TabSwitcher.less';
+
+export default function TabSwitcher({children, className}) {
+
+ return
+ {children}
+
+}
+
+export function Tab({id, label, active, closable, onSwitch}) {
+ return onSwitch(id)}>
+ {label}
+ {closable &&
+
+ }
+ ;
+}
\ No newline at end of file
diff --git a/modules/ui/components/Toolbar.jsx b/modules/ui/components/Toolbar.jsx
new file mode 100644
index 00000000..a3ba7b7e
--- /dev/null
+++ b/modules/ui/components/Toolbar.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import cx from 'classnames';
+
+import ls from './Toolbar.less';
+
+export default function Toolbar({children, className, ...props}) {
+ return
+ {children}
+
;
+}
+
+export function ToolbarButton({children, disabled}) {
+ return
+ {children}
+
;
+}
diff --git a/modules/ui/components/Toolbar.less b/modules/ui/components/Toolbar.less
new file mode 100644
index 00000000..d2cd18ca
--- /dev/null
+++ b/modules/ui/components/Toolbar.less
@@ -0,0 +1,36 @@
+@import "../styles/theme";
+
+.root {
+ background-color: rgba(255, 255, 255, 0.5);
+ padding: 0.3em;
+ border-radius: 5px;
+}
+
+.button {
+ border-radius: 5px;
+ text-align: center;
+ white-space: nowrap;
+ font-size: 0.8em;
+ padding: 0.3em 0.1em;
+ color: #555;
+
+ &:hover {
+ cursor: pointer;
+ background-color: #333;
+ color: #fff;
+ }
+
+ &.disabled {
+ color: #999;
+ }
+
+ &.disabled:hover {
+ color: #aaa;
+ background-color: #888;
+ }
+}
+
+
+
+
+
diff --git a/modules/ui/components/Window.jsx b/modules/ui/components/Window.jsx
new file mode 100644
index 00000000..b927b83c
--- /dev/null
+++ b/modules/ui/components/Window.jsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import ls from './Window.less'
+import Fa from "./Fa";
+
+export default class Window extends React.Component {
+
+ constructor({initWidth}) {
+ super();
+ this.state = {
+ width: initWidth
+ }
+ }
+
+ render() {
+ let {children, title, minimizable } = this.props;
+ return
+
+ {title}
+
+ {minimizable && _}
+
+
+
+ {children}
+
+
+ }
+
+ getStyle() {
+ return {
+ width: toPx(this.state.width),
+ height: toPx(this.state.height),
+ left: toPx(this.state.left),
+ top: toPx(this.state.top)
+ }
+ }
+}
+
+Window.defaultProps = {
+ minimizable: false,
+};
+
+function toPx(val) {
+ return val === undefined ? undefined : val + 'px';
+}
+
diff --git a/modules/ui/components/Window.less b/modules/ui/components/Window.less
new file mode 100644
index 00000000..4e5dcbdb
--- /dev/null
+++ b/modules/ui/components/Window.less
@@ -0,0 +1,8 @@
+.root {
+ position: absolute;
+}
+
+.bar {
+ display: flex;
+ justify-content: space-between;
+}
diff --git a/modules/ui/components/controls/Button.jsx b/modules/ui/components/controls/Button.jsx
new file mode 100644
index 00000000..97469a94
--- /dev/null
+++ b/modules/ui/components/controls/Button.jsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+import ls from './Button.less'
+
+export default function Button({text}) {
+
+ return {text}
+
+}
diff --git a/modules/ui/components/controls/Button.less b/modules/ui/components/controls/Button.less
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/ui/components/controls/ButtonGroup.jsx b/modules/ui/components/controls/ButtonGroup.jsx
new file mode 100644
index 00000000..c9435677
--- /dev/null
+++ b/modules/ui/components/controls/ButtonGroup.jsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+import ls from './ButtonGroup.less'
+
+export default function ButtonGroup({children}) {
+
+ return {children}
+
+}
diff --git a/modules/ui/components/controls/ButtonGroup.less b/modules/ui/components/controls/ButtonGroup.less
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/ui/components/controls/Field.jsx b/modules/ui/components/controls/Field.jsx
new file mode 100644
index 00000000..4c1c8b3b
--- /dev/null
+++ b/modules/ui/components/controls/Field.jsx
@@ -0,0 +1,12 @@
+import React from 'react';
+
+import ls from './Label.less'
+
+export default function Field({children}) {
+
+ return
+ {children[0]} {children[1]}
+
;
+
+
+}
diff --git a/modules/ui/components/controls/Field.less b/modules/ui/components/controls/Field.less
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/ui/components/controls/Label.jsx b/modules/ui/components/controls/Label.jsx
new file mode 100644
index 00000000..cbd2594f
--- /dev/null
+++ b/modules/ui/components/controls/Label.jsx
@@ -0,0 +1,8 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import ls from './Label.less'
+
+export default function Label({children}) {
+ return {children}
+}
diff --git a/modules/ui/components/controls/Label.less b/modules/ui/components/controls/Label.less
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/ui/components/controls/NumberControl.jsx b/modules/ui/components/controls/NumberControl.jsx
new file mode 100644
index 00000000..097add1c
--- /dev/null
+++ b/modules/ui/components/controls/NumberControl.jsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import ls from './NumberControl.less'
+
+export default class NumberControl extends React.Component{
+
+ render() {
+ let {initValue, } = this.props;
+
+ return
+ this.input = input}
+ onWheel={this.onWheel}
+ onChange={e => onChange(e.target.value)} />
+
;
+ }
+
+ onWheel = (e) => {
+ let {baseStep, round, min, max, onChange, accelerator} = this.props;
+ let delta = 0;
+ if ( e.wheelDelta ) { // WebKit / Opera / Explorer 9
+ delta = e.wheelDelta;
+ } else if ( e.detail ) { // Firefox
+ delta = - e.detail;
+ }
+ let val = e.target.value;
+ if (!val) val = 0;
+ let step = baseStep * (e.shiftKey ? accelerator : 1);
+ val = parseFloat(val) + (delta < 0 ? -step : step);
+ if (min !== undefined && val < min) {
+ val = min;
+ }
+ if (max !== undefined && val > max) {
+ val = max;
+ }
+ if (round !== 0) {
+ val = val.toFixed(round);
+ }
+ this.input.value = val;
+ onChange(val);
+ e.preventDefault();
+ e.stopPropagation();
+ }
+}
+
+NumberControl.propTypes = {
+ baseStep: PropTypes.number,
+ round: PropTypes.number,
+ min: PropTypes.number,
+ max: PropTypes.number,
+ accelerator: PropTypes.number,
+ initValue: PropTypes.number.isRequired,
+ onChange: PropTypes.func.isRequired
+};
+
+NumberControl.defaultProps = {
+ baseStep: 1,
+ round: 0,
+ accelerator: 100
+};
\ No newline at end of file
diff --git a/modules/ui/components/controls/NumberControl.less b/modules/ui/components/controls/NumberControl.less
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/ui/connect.jsx b/modules/ui/connect.jsx
new file mode 100644
index 00000000..456061d9
--- /dev/null
+++ b/modules/ui/connect.jsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+export default function connect(tokens, WrappedComponent, staticProps, mapper, dispatchMapper) {
+
+ if (!Array.isArray(tokens)) {
+ tokens = [tokens];
+ }
+
+ mapper = createMapper(mapper);
+
+ dispatchMapper = dispatchMapper || function(dispatch) {
+ return dispatch;
+ };
+
+ return class StateConnector extends React.Component {
+
+ constructor(context) {
+ super();
+ this.mounted = false;
+ this.stateProps = {};
+ this.dispatchProps = dispatchMapper(this.dispatch);
+ }
+
+ componentWillMount() {
+ this.externalStateConnection = this.context.bus.connectToState(tokens, this.setExternalState);
+ this.externalStateConnection();
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ this.context.bus.disconnectFromState(this.externalStateConnection);
+ }
+
+ setExternalState = (state) => {
+ this.stateProps = mapper(state);
+ if (this.mounted) {
+ this.forceUpdate();
+ }
+ };
+
+ dispatch = (event, data) => {
+ this.context.bus.dispatch(event, data);
+ };
+
+ render() {
+ return
+ }
+
+ static contextTypes = {
+ bus: PropTypes.object
+ };
+ }
+}
+
+function createMapper(mapper) {
+ if (!mapper) {
+ return function (state) {
+ let props = {};
+ state.forEach(stateItem => Object.assign(props, stateItem));
+ return props;
+ };
+ } else if (Array.isArray(mapper)) {
+ return function (state) {
+ let props = {};
+ for (let i = 0; i < state.length; i++) {
+ let stateItem = state[i];
+ let mapperItem = mapper[i];
+ Object.assign(props, mapperItem ? mapperItem(stateItem) : stateItem)
+ }
+ return props;
+ };
+ }
+ return mapper;
+}
+
+
+
diff --git a/modules/ui/genId.js b/modules/ui/genId.js
new file mode 100644
index 00000000..d7c35001
--- /dev/null
+++ b/modules/ui/genId.js
@@ -0,0 +1,7 @@
+
+let COUNTER = 0;
+const PREFIX = 'id_';
+
+export default function genId() {
+ return `${PREFIX}_${COUNTER++}`;
+}
\ No newline at end of file
diff --git a/modules/ui/styles/index.less b/modules/ui/styles/index.less
new file mode 100644
index 00000000..e8aa69fd
--- /dev/null
+++ b/modules/ui/styles/index.less
@@ -0,0 +1 @@
+@import "constants";
\ No newline at end of file
diff --git a/modules/ui/styles/init/main.less b/modules/ui/styles/init/main.less
new file mode 100644
index 00000000..63527263
--- /dev/null
+++ b/modules/ui/styles/init/main.less
@@ -0,0 +1,28 @@
+@import "../theme";
+
+body {
+ background-color: @bg-color;
+ color: @font-color;
+ font: 11px 'Lucida Grande', sans-serif;
+}
+
+table {
+ font-size: 1em;
+}
+
+iframe {
+ border: 0;
+}
+
+:global(.disable-selection) {
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+}
+
+:global(.compact-font) {
+ font-family: 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif;
+}
+
+
diff --git a/web/css/minireset.css b/modules/ui/styles/init/minireset.css
similarity index 100%
rename from web/css/minireset.css
rename to modules/ui/styles/init/minireset.css
diff --git a/modules/ui/styles/theme.less b/modules/ui/styles/theme.less
new file mode 100644
index 00000000..17fd8398
--- /dev/null
+++ b/modules/ui/styles/theme.less
@@ -0,0 +1,20 @@
+@bg-color: #000;
+@bg-color-alt: #1a1a1a;
+
+@font-color: #eee;
+@font-color-minor: #ccc;
+
+@font-color-suppressed: #aaa;
+@font-color-disabled: #888;
+
+@border-color: #2c2c2c;
+
+@work-area-color: #808080;
+
+@work-area-control-bar-bg-color: rgba(0, 0, 0, 0.5);
+@work-area-control-bar-bg-color-active: #555;
+@work-area-control-bar-font-color: @font-color-minor;
+
+
+//@work-area-toolbar-bg-color: ;
+//@work-area-toolbar-font-color: ;
diff --git a/modules/ui/wizard/declarativeWizard.js b/modules/ui/wizard/declarativeWizard.js
new file mode 100644
index 00000000..e69de29b
diff --git a/package.json b/package.json
index 993f867b..8ed1ab1c 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
"dependencies": {
"react": "16.2.0",
"react-dom": "16.2.0",
+ "prop-types": "15.6.0",
"clipper-lib": "6.2.1",
"diff-match-patch": "1.0.0",
"earcut": "2.1.1",
diff --git a/web/app/3d/actions/all-actions.js b/web/app/3d/actions/all-actions.js
deleted file mode 100644
index 5be41da0..00000000
--- a/web/app/3d/actions/all-actions.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './core-actions'
-export * from './operation-actions'
-export * from './history-actions'
diff --git a/web/app/3d/actions/core-actions.js b/web/app/3d/actions/core-actions.js
deleted file mode 100644
index e2149a18..00000000
--- a/web/app/3d/actions/core-actions.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import * as ActionHelpers from './action-helpers'
-
-export const EditFace = {
- cssIcons: ['file-picture-o'],
- label: 'sketch',
- icon96: 'img/3d/face-edit96.png',
- info: 'open sketcher for a face/plane',
- listens: ['selection_face'],
- update: ActionHelpers.checkForSelectedFaces(1),
- invoke: (app) => app.editFace()
-};
-
-export const Save = {
- cssIcons: ['floppy-o'],
- label: 'save',
- info: 'save project to storage',
- invoke: (app) => app.save()
-};
-
-export const StlExport = {
- cssIcons: ['upload', 'flip-vertical'],
- label: 'STL Export',
- info: 'export model to STL file',
- invoke: (app) => app.stlExport()
-};
-
-export const RefreshSketches = {
- cssIcons: ['refresh'],
- label: 'Refresh Sketches',
- info: 'refresh all visible sketches',
- invoke: (app) => app.refreshSketches()
-};
-
-export const DeselectAll = {
- cssIcons: ['square-o'],
- label: 'deselect all',
- info: 'deselect everything',
- invoke: (app) => app.viewer.selectionMgr.deselectAll()
-};
-
-export const ToggleCameraMode = {
- cssIcons: ['video-camera'],
- label: 'toggle camera',
- info: 'switch camera mode between perspective and orthographic',
- invoke: (app) => {
- app.context.services.viewer.toggleCamera();
- app.context.services.viewer.render();
- }
-};
-
-export const Info = {
- cssIcons: ['info-circle'],
- label: 'info',
- info: 'opens help dialog',
- invoke: (app) => app.showInfo()
-};
-
-export const Donate = {
- cssIcons: ['paypal'],
- label: 'donate',
- info: 'open paypal donate page',
- invoke: (app, e) => window.open('https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=WADW7V7CC32CY&lc=US&item_name=web%2dcad%2eorg¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted', '_blank')
-};
-
-export const GitHub = {
- cssIcons: ['github'],
- label: 'GitHub',
- info: 'open GitHub project page',
- invoke: (app, e) => window.open('https://github.com/xibyte/jsketcher', '_blank')
-};
-
-export const ShowSketches = {
- type: 'binary',
- property: 'showSketches',
- cssIcons: ['image'],
- label: 'show sketches',
- info: 'toggle whether to show sketches on a solid face'
-};
-
-export const LookAtSolid = {
- cssIcons: ['crosshairs'],
- label: 'look at solid',
- info: 'position camera at the solid at zoom to fit it',
- invoke: (app, e) => app.lookAtSolid(app.inputManager.context.attr('data-id'))
-};
-
-export const noIcon = {
- label: 'no icon'
-};
\ No newline at end of file
diff --git a/web/app/3d/actions/history-actions.js b/web/app/3d/actions/history-actions.js
deleted file mode 100644
index ecfdd808..00000000
--- a/web/app/3d/actions/history-actions.js
+++ /dev/null
@@ -1,57 +0,0 @@
-export const SetHistoryPointer = {
- label: 'set history',
- info: 'set history pointer to this modification item',
- invoke: (app) => {
- const mIndex = parseInt(modificationIndex(app));
- app.craft.historyPointer = mIndex;
- }
-};
-
-export const OpenHistoryWizard = {
- label: 'edit operation',
- info: 'open wizard to change parameters of this operation',
- invoke: (app) => {
- const mIndex = parseInt(modificationIndex(app));
- if (mIndex != app.craft.historyPointer) {
- app.craft.historyPointer = mIndex;
- } else {
- const modification = app.craft.history[mIndex];
- app.ui.createWizardForOperation(modification);
- }
- }
-};
-
-export const EditOperationSketch = {
- cssIcons: ['image'],
- label: 'sketch',
- info: 'edit the sketch assigned to this operation',
- invoke: (app) => {
-
- const mIndex = parseInt(modificationIndex(app));
- const modification = app.craft.history[mIndex];
- if (!modification.face) {
- return;
- }
- if (mIndex != app.craft.historyPointer) {
- app.craft.historyPointer = mIndex;
- }
- const face = app.findFace(modification.face);
- app.sketchFace(face);
- }
-};
-
-export const RemoveModification = {
- label: 'remove modification',
- info: 'remove this modification',
- invoke: (app) => {
- if (!confirm("This modification and all following modifications will be removed. Continue?")) {
- return;
- }
- const mIndex = parseInt(modificationIndex(app));
- app.craft.remove(mIndex);
- }
-};
-
-function modificationIndex(app) {
- return app.inputManager.context.data('modification')
-}
\ No newline at end of file
diff --git a/web/app/3d/actions/operation-actions.js b/web/app/3d/actions/operation-actions.js
deleted file mode 100644
index 2bd7dd51..00000000
--- a/web/app/3d/actions/operation-actions.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import * as Operations from '../craft/operations'
-import * as ActionHelpers from './action-helpers'
-
-function mergeInfo(opName, action) {
- const op = Operations[opName];
- action.label = op.label;
- action.icon32 = op.icon + '32.png';
- action.icon96 = op.icon + '96.png';
- action.invoke = (app) => app.ui.initOperation(opName);
- return action;
-}
-
-export const CUT = mergeInfo('CUT', {
- info: 'makes a cut based on 2D sketch'
-});
-
-export const EXTRUDE = mergeInfo('EXTRUDE', {
- info: 'extrudes 2D sketch'
-});
-
-export const REVOLVE = mergeInfo('REVOLVE', {
- info: 'revolve 2D sketch'
-});
-
-export const SHELL = mergeInfo('SHELL', {
- info: 'makes shell using borders'
-});
-
-export const BOX = mergeInfo('BOX', {
- info: 'creates new object box'
-});
-
-export const PLANE = mergeInfo('PLANE', {
- info: 'creates new object plane'
-});
-
-export const SPHERE = mergeInfo('SPHERE', {
- info: 'creates new object sphere'
-});
-
-export const INTERSECTION = mergeInfo('INTERSECTION', {
- info: 'intersection operation on two solids'
-});
-
-export const DIFFERENCE = mergeInfo('DIFFERENCE', {
- info: 'difference operation on two solids'
-});
-
-export const UNION = mergeInfo('UNION', {
- info: 'union operation on two solids'
-});
-
-export const IMPORT_STL = mergeInfo('IMPORT_STL', {
- info: 'import stl from external location'
-});
-
-requiresFaceSelection(CUT, 1);
-requiresFaceSelection(EXTRUDE, 1);
-requiresFaceSelection(REVOLVE, 1);
-
-requiresSolidSelection(INTERSECTION, 2);
-requiresSolidSelection(DIFFERENCE, 2);
-requiresSolidSelection(UNION, 2);
-
-function requiresFaceSelection(action, amount) {
- action.listens = ['selection_face'];
- action.update = ActionHelpers.checkForSelectedFaces(amount)
-}
-
-function requiresSolidSelection(action, amount) {
- action.listens = ['selection_face'];
- action.update = ActionHelpers.checkForSelectedSolids(amount)
-}
diff --git a/web/app/3d/dom/WebApplication.jsx b/web/app/3d/dom/WebApplication.jsx
deleted file mode 100644
index 3dc0a4ea..00000000
--- a/web/app/3d/dom/WebApplication.jsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React, {Fragment} from 'react';
-
-export default class WebApplication extends React.Component {
-
- render() {
- return
-
-
-
-
- }
-}
\ No newline at end of file
diff --git a/web/app/3d/dom/startReact.js b/web/app/3d/dom/startReact.js
deleted file mode 100644
index f379893f..00000000
--- a/web/app/3d/dom/startReact.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import WebApplication from './WebApplication';
-
-export default function startReact(callback) {
- return ReactDOM.render(
- ,
- document.getElementById('app'),
- callback
- );
-}
\ No newline at end of file
diff --git a/web/app/3d/menu/menu-config.js b/web/app/3d/menu/menu-config.js
deleted file mode 100644
index 38c39eb4..00000000
--- a/web/app/3d/menu/menu-config.js
+++ /dev/null
@@ -1,40 +0,0 @@
-export const file = {
- label: 'file',
- cssIcons: ['file'],
- actions: ['Save', 'StlExport', '-', 'IMPORT_STL']
-};
-
-export const craft = {
- label: 'craft',
- cssIcons: ['magic'],
- info: 'set of available craft operations on a solid',
- actions: ['EXTRUDE', 'CUT', 'REVOLVE', 'SHELL']
-};
-
-export const primitives = {
- label: 'add',
- cssIcons: ['cube', 'plus'],
- info: 'set of available solid creation operations',
- actions: ['PLANE', 'BOX', 'SPHERE']
-};
-
-export const boolean = {
- label: 'bool',
- cssIcons: ['pie-chart'],
- info: 'set of available boolean operations',
- actions: ['INTERSECTION', 'DIFFERENCE', 'UNION']
-};
-
-export const main = {
- label: 'start',
- cssIcons: ['rocket'],
- info: 'common set of actions',
- actions: ['EXTRUDE', 'CUT', 'SHELL', '-', 'INTERSECTION', 'DIFFERENCE', 'UNION', '-', 'PLANE', 'BOX', 'SPHERE', '-',
- 'EditFace', '-', 'DeselectAll', 'RefreshSketches']
-};
-
-export const SolidContext = {
- label: 'solid-context',
- info: 'solid context actions',
- actions: ['LookAtSolid']
-};
diff --git a/web/app/brep/brep-builder.js b/web/app/brep/brep-builder.js
index 71611bf8..85338a5a 100644
--- a/web/app/brep/brep-builder.js
+++ b/web/app/brep/brep-builder.js
@@ -6,7 +6,7 @@ import {Face} from './topo/face';
import {Loop} from './topo/loop';
import {Edge} from './topo/edge';
import {Vertex} from './topo/vertex';
-import {normalOfCCWSeq} from '../3d/cad-utils';
+import {normalOfCCWSeq} from '../cad/cad-utils';
import BBox from "../math/bbox";
export default class BrepBuilder {
diff --git a/web/app/brep/brep-enclose.js b/web/app/brep/brep-enclose.js
index eb69b74c..2ca4173e 100644
--- a/web/app/brep/brep-enclose.js
+++ b/web/app/brep/brep-enclose.js
@@ -8,7 +8,7 @@ import {NurbsSurface, NurbsCurve} from './geom/impl/nurbs'
import {Plane} from './geom/impl/plane'
import {Point} from './geom/point'
import {BasisForPlane, Matrix3} from '../math/l3space'
-import * as cad_utils from '../3d/cad-utils'
+import * as cad_utils from '../cad/cad-utils'
import * as math from '../math/math'
import mergeNullFace from './null-face-merge'
import {invert} from './operations/boolean'
diff --git a/web/app/brep/brep-primitives.js b/web/app/brep/brep-primitives.js
index 7e808099..b25f379e 100644
--- a/web/app/brep/brep-primitives.js
+++ b/web/app/brep/brep-primitives.js
@@ -2,7 +2,7 @@ import {Point} from './geom/point'
import {Plane} from './geom/impl/plane'
import {createPrism, enclose} from './brep-enclose'
import {AXIS, Matrix3} from '../math/l3space'
-import {Circle} from '../3d/craft/sketch/sketch-model'
+import {Circle} from '../cad/craft/sketch/sketch-model'
export function box(w, h, d, tr) {
const wh = w * 0.5;
diff --git a/web/app/brep/debug/debugger/brepDebugger.less b/web/app/brep/debug/debugger/brepDebugger.less
index eb14d1a1..394fd8d8 100644
--- a/web/app/brep/debug/debugger/brepDebugger.less
+++ b/web/app/brep/debug/debugger/brepDebugger.less
@@ -1,3 +1,4 @@
+:global
.brep-debugger {
& .strike {
diff --git a/web/app/brep/operations/evolve-face.js b/web/app/brep/operations/evolve-face.js
index 03ef0565..37efa045 100644
--- a/web/app/brep/operations/evolve-face.js
+++ b/web/app/brep/operations/evolve-face.js
@@ -2,7 +2,7 @@ import {Face} from '../topo/face';
import {Vertex} from '../topo/vertex';
import Vector from 'math/vector';
import {isCCW} from '../../math/math';
-import PIP from '../../3d/tess/pip';
+import PIP from '../../cad/tess/pip';
export function evolveFace(originFace, loops) {
let out = [];
diff --git a/web/app/brep/operations/polyhedronify.js b/web/app/brep/operations/polyhedronify.js
index b80a2f9c..d21f08a4 100644
--- a/web/app/brep/operations/polyhedronify.js
+++ b/web/app/brep/operations/polyhedronify.js
@@ -1,4 +1,4 @@
-import {TriangulateFace} from '../../3d/tess/triangulation'
+import {TriangulateFace} from '../../cad/tess/triangulation'
import {Shell} from '../topo/shell'
import {HalfEdge} from '../topo/edge'
import {Loop} from '../topo/loop'
diff --git a/web/app/brep/topo/face.js b/web/app/brep/topo/face.js
index 7c7be6d0..0b187a71 100644
--- a/web/app/brep/topo/face.js
+++ b/web/app/brep/topo/face.js
@@ -1,6 +1,6 @@
import {TopoObject} from './topo-object'
import {Loop} from './loop'
-import PIP from '../../3d/tess/pip';
+import PIP from '../../cad/tess/pip';
import {NurbsCurve} from "../geom/impl/nurbs";
import {eqSqTol, veq, veqNeg} from "../geom/tolerance";
import {
diff --git a/web/app/3d/actions/action-helpers.js b/web/app/cad/actions/action-helpers.js
similarity index 66%
rename from web/app/3d/actions/action-helpers.js
rename to web/app/cad/actions/action-helpers.js
index eec3fe14..faf81169 100644
--- a/web/app/3d/actions/action-helpers.js
+++ b/web/app/cad/actions/action-helpers.js
@@ -1,6 +1,6 @@
export function checkForSelectedFaces(amount) {
- return (state, app) => {
- state.enabled = app.getFaceSelection().length >= amount;
+ return (state, context) => {
+ state.enabled = context.services.selection.face().length >= amount;
if (!state.enabled) {
state.hint = amount === 1 ? 'requires a face to be selected' : 'requires ' + amount + ' faces to be selected';
}
@@ -8,8 +8,8 @@ export function checkForSelectedFaces(amount) {
}
export function checkForSelectedSolids(amount) {
- return (state, app) => {
- state.enabled = app.getFaceSelection().length >= amount;
+ return (state, context) => {
+ state.enabled = context.services.selection.face().length >= amount;
if (!state.enabled) {
state.hint = amount === 1 ? 'requires a solid to be selected' : 'requires ' + amount + ' solids to be selected';
}
diff --git a/web/app/cad/actions/actionRef.jsx b/web/app/cad/actions/actionRef.jsx
new file mode 100644
index 00000000..64eddc30
--- /dev/null
+++ b/web/app/cad/actions/actionRef.jsx
@@ -0,0 +1,8 @@
+
+export function toIdAndOverrides(ref) {
+ if (Array.isArray(ref)) {
+ return ref;
+ } else {
+ return [ref, undefined]
+ }
+}
\ No newline at end of file
diff --git a/web/app/cad/actions/actionSystemPlugin.js b/web/app/cad/actions/actionSystemPlugin.js
new file mode 100644
index 00000000..bea2637d
--- /dev/null
+++ b/web/app/cad/actions/actionSystemPlugin.js
@@ -0,0 +1,63 @@
+import {createToken} from 'bus';
+
+export function activate(context) {
+
+ let {bus} = context;
+
+ function run(id, data) {
+ bus.dispatch(TOKENS.actionRun(id), data);
+ }
+
+ function register(action) {
+ bus.enableState(TOKENS.actionAppearance(action.id), action.appearance);
+
+ let stateToken = TOKENS.actionState(action.id);
+ let initialState = {
+ hint: '',
+ enabled: true,
+ visible: true
+ };
+ if (action.update) {
+ action.update(initialState, context);
+ }
+ bus.enableState(stateToken, initialState);
+
+ if (action.update && action.listens) {
+
+ const stateUpdater = () => {
+ bus.updateState(stateToken, (actionState) => {
+ actionState.hint = '';
+ actionState.enabled = true;
+ actionState.visible = true;
+ action.update(actionState, context);
+ return actionState;
+ });
+ };
+
+ for (let event of action.listens) {
+ bus.subscribe(event, stateUpdater);
+ }
+ }
+ bus.subscribe(TOKENS.actionRun(action.id), (data) => action.invoke(context, data));
+ }
+
+ function registerAction(action) {
+ register(action);
+ }
+
+ function registerActions(actions) {
+ actions.forEach(action => register(action));
+ }
+
+ context.services.action = {run, registerAction, registerActions}
+}
+
+export const TOKENS = {
+ ACTION_STATE_NS: 'action.state',
+ ACTION_APPEARANCE_NS: 'action.appearance',
+ ACTION_RUN_NS: 'action.run',
+
+ actionState: (actionId) => createToken(TOKENS.ACTION_STATE_NS, actionId),
+ actionAppearance: (actionId) => createToken(TOKENS.ACTION_APPEARANCE_NS, actionId),
+ actionRun: (actionId) => createToken(TOKENS.ACTION_RUN_NS, actionId),
+};
\ No newline at end of file
diff --git a/web/app/3d/actions/actions.js b/web/app/cad/actions/actions.js
similarity index 100%
rename from web/app/3d/actions/actions.js
rename to web/app/cad/actions/actions.js
diff --git a/web/app/cad/actions/coreActions.js b/web/app/cad/actions/coreActions.js
new file mode 100644
index 00000000..7d4efacd
--- /dev/null
+++ b/web/app/cad/actions/coreActions.js
@@ -0,0 +1,130 @@
+import * as ActionHelpers from './action-helpers'
+
+export default [
+ {
+ id: 'EditFace',
+ appearance: {
+ cssIcons: ['file-picture-o'],
+ label: 'sketch',
+ icon96: 'img/cad/face-edit96.png',
+ info: 'open sketcher for a face/plane',
+ },
+ listens: ['selection_face'],
+ update: ActionHelpers.checkForSelectedFaces(1),
+ invoke: (context) => context.services.sketcher.editFace(),
+ },
+
+ {
+ id: 'Save',
+ appearance: {
+ cssIcons: ['floppy-o'],
+ label: 'save',
+ info: 'save project to storage',
+ },
+ invoke: (context) => context.services.project.save()
+ },
+
+ {
+ id: 'StlExport',
+ appearance: {
+ cssIcons: ['upload', 'flip-vertical'],
+ label: 'STL Export',
+ info: 'export model to STL file',
+ },
+ invoke: (context) => context.services.project.stlExport()
+ },
+
+
+ {
+ id: 'RefreshSketches',
+ appearance: {
+ cssIcons: ['refresh'],
+ label: 'Refresh Sketches',
+ info: 'refresh all visible sketches',
+ },
+ invoke: (context) => context.services.sketcher.refreshSketches()
+ },
+
+ {
+ id: 'DeselectAll',
+ appearance: {
+ cssIcons: ['square-o'],
+ label: 'deselect all',
+ info: 'deselect everything',
+ },
+ invoke: (context) => context.services.selection.deselectAll()
+ },
+
+ {
+ id: 'ToggleCameraMode',
+ appearance: {
+ cssIcons: ['video-camera'],
+ label: 'toggle camera',
+ info: 'switch camera mode between perspective and orthographic',
+ },
+ invoke: (app) => {
+ let viewer = app.context.services.viewer;
+ viewer.toggleCamera();
+ viewer.render();
+ }
+ },
+
+ {
+ id: 'Info',
+ appearance: {
+ cssIcons: ['info-circle'],
+ label: 'info',
+ info: 'opens help dialog',
+ },
+ invoke: (context) => context.services.help.showInfo()
+ },
+
+ {
+ id: 'Donate',
+ appearance: {
+ cssIcons: ['paypal'],
+ label: 'donate',
+ info: 'open paypal donate page',
+ },
+ invoke: (context) => window.open('https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=WADW7V7CC32CY&lc=US&item_name=web%2dcad%2eorg¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted', '_blank')
+ },
+
+ {
+ id: 'GitHub',
+ appearance: {
+ cssIcons: ['github'],
+ label: 'GitHub',
+ info: 'open GitHub project page',
+ },
+ invoke: (context) => window.open('https://github.com/xibyte/jsketcher', '_blank')
+ },
+
+ {
+ id: 'ShowSketches',
+ type: 'binary',
+ property: 'showSketches',
+ appearance: {
+ cssIcons: ['image'],
+ label: 'show sketches',
+ info: 'toggle whether to show sketches on a solid face'
+ }
+ },
+
+ {
+ id: 'LookAtSolid',
+ appearance: {
+ cssIcons: ['crosshairs'],
+ label: 'look at solid',
+ info: 'position camera at the solid at zoom to fit it',
+ },
+ invoke: (context) => app.lookAtSolid(app.inputManager.context.attr('data-id'))
+ },
+
+ {
+ id: 'noIcon',
+ appearance: {
+ label: 'no icon'
+ }
+ }
+]
+
diff --git a/web/app/cad/actions/historyActions.js b/web/app/cad/actions/historyActions.js
new file mode 100644
index 00000000..34e9cc1f
--- /dev/null
+++ b/web/app/cad/actions/historyActions.js
@@ -0,0 +1,68 @@
+export default [
+ {
+ id: 'SetHistoryPointer',
+ appearance: {
+ label: 'set history',
+ info: 'set history pointer to this modification item',
+ },
+ invoke: (app) => {
+ const mIndex = parseInt(modificationIndex(app));
+ app.craft.historyPointer = mIndex;
+ }
+ },
+ {
+ id: 'OpenHistoryWizard',
+ appearance: {
+ label: 'edit operation',
+ info: 'open wizard to change parameters of this operation',
+ },
+ invoke: (app) => {
+ const mIndex = parseInt(modificationIndex(app));
+ if (mIndex != app.craft.historyPointer) {
+ app.craft.historyPointer = mIndex;
+ } else {
+ const modification = app.craft.history[mIndex];
+ app.ui.createWizardForOperation(modification);
+ }
+ }
+ },
+ {
+ id: 'EditOperationSketch',
+ appearance: {
+ cssIcons: ['image'],
+ label: 'sketch',
+ info: 'edit the sketch assigned to this operation',
+ },
+ invoke: (app) => {
+
+ const mIndex = parseInt(modificationIndex(app));
+ const modification = app.craft.history[mIndex];
+ if (!modification.face) {
+ return;
+ }
+ if (mIndex != app.craft.historyPointer) {
+ app.craft.historyPointer = mIndex;
+ }
+ const face = app.findFace(modification.face);
+ app.sketchFace(face);
+ }
+ },
+ {
+ id: 'RemoveModification',
+ appearance: {
+ label: 'remove modification',
+ info: 'remove this modification',
+ },
+ invoke: (app) => {
+ if (!confirm("This modification and all following modifications will be removed. Continue?")) {
+ return;
+ }
+ const mIndex = parseInt(modificationIndex(app));
+ app.craft.remove(mIndex);
+ }
+ }
+]
+
+function modificationIndex(app) {
+ return app.inputManager.context.data('modification')
+}
\ No newline at end of file
diff --git a/web/app/cad/actions/operationActions.js b/web/app/cad/actions/operationActions.js
new file mode 100644
index 00000000..88fbee07
--- /dev/null
+++ b/web/app/cad/actions/operationActions.js
@@ -0,0 +1,105 @@
+import * as Operations from '../craft/operations'
+import * as ActionHelpers from './action-helpers'
+
+const OPERATION_ACTIONS = [
+ {
+ id: 'CUT',
+ appearance: {
+ info: 'makes a cut based on 2D sketch',
+ },
+ ...requiresFaceSelection(1)
+ },
+ {
+ id: 'EXTRUDE',
+ appearance: {
+ info: 'extrudes 2D sketch',
+ },
+ },
+ {
+ id: 'REVOLVE',
+ appearance: {
+ info: 'revolve 2D sketch',
+ },
+ ...requiresFaceSelection(1)
+ },
+ {
+ id: 'SHELL',
+ appearance: {
+ info: 'makes shell using borders',
+ },
+ ...requiresFaceSelection(1)
+ },
+ {
+ id: 'BOX',
+ appearance: {
+ info: 'creates new object box'
+ },
+ },
+ {
+ id: 'PLANE',
+ appearance: {
+ info: 'creates new object plane'
+ },
+ },
+ {
+ id: 'SPHERE',
+ appearance: {
+ info: 'creates new object sphere'
+ },
+ },
+ {
+ id: 'INTERSECTION',
+ appearance: {
+ info: 'intersection operation on two solids',
+ },
+ ...requiresSolidSelection(2)
+ },
+ {
+ id: 'DIFFERENCE',
+ appearance: {
+ info: 'difference operation on two solids',
+ },
+ ...requiresSolidSelection(2)
+ },
+ {
+ id: 'UNION',
+ appearance: {
+ info: 'union operation on two solids',
+ },
+ ...requiresSolidSelection(2)
+ },
+ {
+ id: 'IMPORT_STL',
+ appearance: {
+ info: 'import stl from external location'
+ }
+ }
+];
+
+function mergeInfo(action) {
+ const op = Operations[action.id];
+ action.invoke = app => app.ui.initOperation(action.id);
+ Object.assign(action.appearance, {
+ label: op.label,
+ icon32: op.icon + '32.png',
+ icon96: op.icon + '96.png',
+ });
+}
+
+OPERATION_ACTIONS.forEach(action => mergeInfo(action));
+
+function requiresFaceSelection(amount) {
+ return {
+ listens: ['selection_face'],
+ update: ActionHelpers.checkForSelectedFaces(amount)
+ }
+}
+
+function requiresSolidSelection(amount) {
+ return {
+ listens: ['selection_face'],
+ update: ActionHelpers.checkForSelectedSolids(amount)
+ }
+}
+
+export default OPERATION_ACTIONS;
\ No newline at end of file
diff --git a/web/app/3d/cad-utils.js b/web/app/cad/cad-utils.js
similarity index 100%
rename from web/app/3d/cad-utils.js
rename to web/app/cad/cad-utils.js
diff --git a/web/app/3d/counters.js b/web/app/cad/counters.js
similarity index 100%
rename from web/app/3d/counters.js
rename to web/app/cad/counters.js
diff --git a/web/app/3d/craft/brep/boolean-operation.js b/web/app/cad/craft/brep/boolean-operation.js
similarity index 100%
rename from web/app/3d/craft/brep/boolean-operation.js
rename to web/app/cad/craft/brep/boolean-operation.js
diff --git a/web/app/3d/craft/brep/cut-extrude.js b/web/app/cad/craft/brep/cut-extrude.js
similarity index 100%
rename from web/app/3d/craft/brep/cut-extrude.js
rename to web/app/cad/craft/brep/cut-extrude.js
diff --git a/web/app/3d/craft/brep/revolve.js b/web/app/cad/craft/brep/revolve.js
similarity index 100%
rename from web/app/3d/craft/brep/revolve.js
rename to web/app/cad/craft/brep/revolve.js
diff --git a/web/app/3d/craft/brep/wizards/box.js b/web/app/cad/craft/brep/wizards/box.js
similarity index 100%
rename from web/app/3d/craft/brep/wizards/box.js
rename to web/app/cad/craft/brep/wizards/box.js
diff --git a/web/app/3d/craft/brep/wizards/cut-extrude-wizard.js b/web/app/cad/craft/brep/wizards/cut-extrude-wizard.js
similarity index 100%
rename from web/app/3d/craft/brep/wizards/cut-extrude-wizard.js
rename to web/app/cad/craft/brep/wizards/cut-extrude-wizard.js
diff --git a/web/app/3d/craft/brep/wizards/plane-wizard.js b/web/app/cad/craft/brep/wizards/plane-wizard.js
similarity index 100%
rename from web/app/3d/craft/brep/wizards/plane-wizard.js
rename to web/app/cad/craft/brep/wizards/plane-wizard.js
diff --git a/web/app/3d/craft/brep/wizards/preview-wizard.js b/web/app/cad/craft/brep/wizards/preview-wizard.js
similarity index 100%
rename from web/app/3d/craft/brep/wizards/preview-wizard.js
rename to web/app/cad/craft/brep/wizards/preview-wizard.js
diff --git a/web/app/3d/craft/brep/wizards/revolve-wizard.js b/web/app/cad/craft/brep/wizards/revolve-wizard.js
similarity index 100%
rename from web/app/3d/craft/brep/wizards/revolve-wizard.js
rename to web/app/cad/craft/brep/wizards/revolve-wizard.js
diff --git a/web/app/3d/craft/brep/wizards/wizard.js b/web/app/cad/craft/brep/wizards/wizard.js
similarity index 100%
rename from web/app/3d/craft/brep/wizards/wizard.js
rename to web/app/cad/craft/brep/wizards/wizard.js
diff --git a/web/app/3d/craft/craft.js b/web/app/cad/craft/craft.js
similarity index 100%
rename from web/app/3d/craft/craft.js
rename to web/app/cad/craft/craft.js
diff --git a/web/app/3d/craft/mesh/revolve.js b/web/app/cad/craft/mesh/revolve.js
similarity index 100%
rename from web/app/3d/craft/mesh/revolve.js
rename to web/app/cad/craft/mesh/revolve.js
diff --git a/web/app/3d/craft/mesh/wizards/box.js b/web/app/cad/craft/mesh/wizards/box.js
similarity index 100%
rename from web/app/3d/craft/mesh/wizards/box.js
rename to web/app/cad/craft/mesh/wizards/box.js
diff --git a/web/app/3d/craft/mesh/wizards/extrude.js b/web/app/cad/craft/mesh/wizards/extrude.js
similarity index 100%
rename from web/app/3d/craft/mesh/wizards/extrude.js
rename to web/app/cad/craft/mesh/wizards/extrude.js
diff --git a/web/app/3d/craft/mesh/wizards/import.js b/web/app/cad/craft/mesh/wizards/import.js
similarity index 100%
rename from web/app/3d/craft/mesh/wizards/import.js
rename to web/app/cad/craft/mesh/wizards/import.js
diff --git a/web/app/3d/craft/mesh/wizards/plane.js b/web/app/cad/craft/mesh/wizards/plane.js
similarity index 100%
rename from web/app/3d/craft/mesh/wizards/plane.js
rename to web/app/cad/craft/mesh/wizards/plane.js
diff --git a/web/app/3d/craft/mesh/wizards/revolve.js b/web/app/cad/craft/mesh/wizards/revolve.js
similarity index 100%
rename from web/app/3d/craft/mesh/wizards/revolve.js
rename to web/app/cad/craft/mesh/wizards/revolve.js
diff --git a/web/app/3d/craft/mesh/wizards/sphere.js b/web/app/cad/craft/mesh/wizards/sphere.js
similarity index 100%
rename from web/app/3d/craft/mesh/wizards/sphere.js
rename to web/app/cad/craft/mesh/wizards/sphere.js
diff --git a/web/app/3d/craft/mesh/wizards/transform.js b/web/app/cad/craft/mesh/wizards/transform.js
similarity index 100%
rename from web/app/3d/craft/mesh/wizards/transform.js
rename to web/app/cad/craft/mesh/wizards/transform.js
diff --git a/web/app/3d/craft/mesh/wizards/wizard-commons.js b/web/app/cad/craft/mesh/wizards/wizard-commons.js
similarity index 100%
rename from web/app/3d/craft/mesh/wizards/wizard-commons.js
rename to web/app/cad/craft/mesh/wizards/wizard-commons.js
diff --git a/web/app/3d/craft/mesh/workbench.js b/web/app/cad/craft/mesh/workbench.js
similarity index 100%
rename from web/app/3d/craft/mesh/workbench.js
rename to web/app/cad/craft/mesh/workbench.js
diff --git a/web/app/3d/craft/operations.js b/web/app/cad/craft/operations.js
similarity index 71%
rename from web/app/3d/craft/operations.js
rename to web/app/cad/craft/operations.js
index b6b600d6..de88447d 100644
--- a/web/app/3d/craft/operations.js
+++ b/web/app/cad/craft/operations.js
@@ -1,39 +1,39 @@
-import {MESH_OPERATIONS} from './mesh/workbench'
-import {Extrude, Cut} from './brep/cut-extrude'
-import {Revolve} from './brep/revolve'
-import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject'
-import {PlaneSceneObject} from '../scene/wrappers/planeSceneObject'
-import {box} from '../../brep/brep-primitives'
+// import {MESH_OPERATIONS} from './mesh/workbench'
+// import {Extrude, Cut} from './brep/cut-extrude'
+// import {Revolve} from './brep/revolve'
+// import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject'
+// import {PlaneSceneObject} from '../scene/wrappers/planeSceneObject'
+// import {box} from '../../brep/brep-primitives'
export const CUT = {
- icon: 'img/3d/cut',
+ icon: 'img/cad/cut',
label: 'Cut',
info: (p) => '(' + r(p.value) + ')',
action: (app, params) => Cut(app, params)
};
export const EXTRUDE = {
- icon: 'img/3d/extrude',
+ icon: 'img/cad/extrude',
label: 'Extrude',
info: (p) => '(' + r(p.value) + ')',
action: (app, params) => Extrude(app, params)
};
export const REVOLVE = {
- icon: 'img/3d/revolve',
+ icon: 'img/cad/revolve',
label: 'Revolve',
info: (p) => '(' + p.angle + ')',
action: (app, params) => Revolve(app, params)
};
export const SHELL = {
- icon: 'img/3d/shell',
+ icon: 'img/cad/shell',
label: 'Shell',
info: (p) => '(' + p.d + ')'
};
export const BOX = {
- icon: 'img/3d/cube',
+ icon: 'img/cad/cube',
label: 'Box',
info: (p) => '(' + p.width + ', ' + p.height + ', ' + p.depth + ')',
action: (app, request) => {
@@ -45,7 +45,7 @@ export const BOX = {
};
export const PLANE = {
- icon: 'img/3d/plane',
+ icon: 'img/cad/plane',
label: 'Plane',
info: (p) => '(' + p.depth + ')',
action: (app, request) => {
@@ -57,7 +57,7 @@ export const PLANE = {
};
export const SPHERE = {
- icon: 'img/3d/sphere',
+ icon: 'img/cad/sphere',
label: 'Sphere',
info: (p) => '(' + p.radius + ')',
action: (app, request) => {
@@ -66,25 +66,25 @@ export const SPHERE = {
};
export const INTERSECTION = {
- icon: 'img/3d/intersection',
+ icon: 'img/cad/intersection',
label: 'Intersection',
info: (p) => null
};
export const DIFFERENCE = {
- icon: 'img/3d/difference',
+ icon: 'img/cad/difference',
label: 'Difference',
info: (p) => null
};
export const UNION = {
- icon: 'img/3d/union',
+ icon: 'img/cad/union',
label: 'Union',
info: (p) => null
};
export const IMPORT_STL = {
- icon: 'img/3d/stl',
+ icon: 'img/cad/stl',
label: 'STL Import',
info: (p) => '(' + p.url.substring(p.url.lastIndexOf('/') + 1 ) + ')',
action: (app, request) => {
diff --git a/web/app/3d/craft/sketch/sketch-model.js b/web/app/cad/craft/sketch/sketch-model.js
similarity index 100%
rename from web/app/3d/craft/sketch/sketch-model.js
rename to web/app/cad/craft/sketch/sketch-model.js
diff --git a/web/app/3d/craft/sketch/sketch-reader.js b/web/app/cad/craft/sketch/sketch-reader.js
similarity index 100%
rename from web/app/3d/craft/sketch/sketch-reader.js
rename to web/app/cad/craft/sketch/sketch-reader.js
diff --git a/web/app/3d/debug.js b/web/app/cad/debug.js
similarity index 100%
rename from web/app/3d/debug.js
rename to web/app/cad/debug.js
diff --git a/web/app/cad/dom/components/ControlBar.jsx b/web/app/cad/dom/components/ControlBar.jsx
new file mode 100644
index 00000000..d7f3ca35
--- /dev/null
+++ b/web/app/cad/dom/components/ControlBar.jsx
@@ -0,0 +1,23 @@
+import React from 'react';
+
+import ls from './ControlBar.less';
+import cx from 'classnames';
+
+export default function ControlBar({left, right}) {
+
+ return
+
+ {right}
+
+
+ {left}
+
+
+}
+
+export function ControlBarButton({onClick, onElement, disabled, children}) {
+ return
+ {children}
+
+}
\ No newline at end of file
diff --git a/web/app/cad/dom/components/ControlBar.less b/web/app/cad/dom/components/ControlBar.less
new file mode 100644
index 00000000..95161e0c
--- /dev/null
+++ b/web/app/cad/dom/components/ControlBar.less
@@ -0,0 +1,36 @@
+@import "~ui/styles/theme";
+
+.root {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ justify-content: space-between;
+ background-color: @work-area-control-bar-bg-color;
+ color: @work-area-control-bar-font-color;
+}
+
+.button {
+ display: inline-block;
+ cursor: pointer;
+ padding: 0.3em 0.3em 0.4em 0.6em;
+
+ @border: 1px solid @border-color;
+ &.left {
+ border-right: @border;
+ }
+ &.right {
+ border-left: @border;
+ }
+ &:hover {
+ background-color: @work-area-control-bar-bg-color-active;
+ }
+}
+
+.button.disabled {
+ color: @font-color-suppressed;
+ &:hover {
+ background-color: @work-area-control-bar-bg-color;
+ }
+}
diff --git a/web/app/cad/dom/components/ObjectExplorer.jsx b/web/app/cad/dom/components/ObjectExplorer.jsx
new file mode 100644
index 00000000..9bf2ded7
--- /dev/null
+++ b/web/app/cad/dom/components/ObjectExplorer.jsx
@@ -0,0 +1,10 @@
+import React from 'react';
+
+export default class ObjectExplorer extends React.Component {
+
+ render() {
+ return
+ ObjectExplorer
+
+ }
+}
\ No newline at end of file
diff --git a/web/app/cad/dom/components/OperationHistory.jsx b/web/app/cad/dom/components/OperationHistory.jsx
new file mode 100644
index 00000000..4a00b8df
--- /dev/null
+++ b/web/app/cad/dom/components/OperationHistory.jsx
@@ -0,0 +1,10 @@
+import React from 'react';
+
+export default class OperationHistory extends React.Component {
+
+ render() {
+ return
+ OperationHistory
+
+ }
+}
\ No newline at end of file
diff --git a/web/app/cad/dom/components/PlugableControlBar.jsx b/web/app/cad/dom/components/PlugableControlBar.jsx
new file mode 100644
index 00000000..b64259a4
--- /dev/null
+++ b/web/app/cad/dom/components/PlugableControlBar.jsx
@@ -0,0 +1,56 @@
+import React, {Fragment} from 'react';
+import ControlBar, {ControlBarButton} from './ControlBar';
+import connect from 'ui/connect';
+import Fa from 'ui/components/Fa';
+import {TOKENS as UI_TOKENS} from '../uiEntryPointsPlugin';
+import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin';
+import {toIdAndOverrides} from "../../actions/actionRef";
+
+
+export default function PlugableControlBar() {
+ return } right={}/>;
+}
+
+function ButtonGroup({actions}) {
+ return actions.map(actionRef => {
+ let [id, overrides] = toIdAndOverrides(actionRef);
+ let actionRunToken = ACTION_TOKENS.actionRun(id);
+ let Comp = connect([ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
+ ActionButton, {actionId: id},
+ ([appearance, state]) => Object.assign({}, appearance, state, overrides),
+ dispatch => ({runAction: (data) => dispatch(actionRunToken, data)})
+ );
+ return ;
+ });
+}
+
+function isMenuAction(actionId) {
+ return actionId.startsWith('menu.');
+}
+
+class ActionButton extends React.Component {
+
+ render() {
+ let {label, cssIcons, runAction, enabled, visible, actionId} = this.props;
+ if (!visible) {
+ return null;
+ }
+ const onClick = e => runAction(isMenuAction(actionId) ? getMenuData(this.el) : undefined);
+ return this.el = el}>
+ {cssIcons && } {label}
+ ;
+ }
+}
+
+const LeftGroup = connect(UI_TOKENS.CONTROL_BAR_LEFT, ButtonGroup, undefined, ([actions]) => ({actions}));
+const RightGroup = connect(UI_TOKENS.CONTROL_BAR_RIGHT, ButtonGroup, undefined, ([actions]) => ({actions}));
+
+function getMenuData(el) {
+ //TODO: make more generic
+ return {
+ orientationUp: true,
+ flatBottom: true,
+ x: el.offsetParent.offsetParent.offsetLeft + el.offsetLeft,
+ y: el.offsetParent.offsetHeight - el.offsetTop
+ };
+}
diff --git a/web/app/cad/dom/components/PlugableToolbar.jsx b/web/app/cad/dom/components/PlugableToolbar.jsx
new file mode 100644
index 00000000..7838fb31
--- /dev/null
+++ b/web/app/cad/dom/components/PlugableToolbar.jsx
@@ -0,0 +1,44 @@
+import React, {Fragment} from 'react';
+import connect from 'ui/connect';
+import Fa from 'ui/components/Fa';
+import {TOKENS as UI_TOKENS} from '../uiEntryPointsPlugin';
+import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin';
+import Toolbar, {ToolbarButton} from "../../../../../modules/ui/components/Toolbar";
+import ImgIcon from "../../../../../modules/ui/components/ImgIcon";
+import {toIdAndOverrides} from "../../actions/actionRef";
+
+
+function ConfigurableToolbar({actions, small}) {
+
+ return
+ {actions.map(actionRef => {
+ let [id, overrides] = toIdAndOverrides(actionRef);
+ let Comp = connect(
+ [ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
+ ActionButton, {small, },
+ ([appearance, state]) => Object.assign({}, appearance, state, overrides));
+ return
+ })}
+
+}
+
+function ActionButton({label, icon96, cssIcons, small, enabled, visible, onClick}) {
+ if (!visible) {
+ return null;
+ }
+
+ let icon = small ? : ;
+
+ return
+ {icon}
+ {!small && {label}
}
+
+}
+
+export function createPlugableToolbar(configToken, small) {
+ return connect(configToken, ConfigurableToolbar, {small}, ([actions]) => ({actions}) );
+}
+
+export const PlugableToolbarLeft = createPlugableToolbar(UI_TOKENS.TOOLBAR_BAR_LEFT);
+export const PlugableToolbarLeftSecondary = createPlugableToolbar(UI_TOKENS.TOOLBAR_BAR_LEFT_SECONDARY);
+export const PlugableToolbarRight = createPlugableToolbar(UI_TOKENS.TOOLBAR_BAR_RIGHT, true);
\ No newline at end of file
diff --git a/web/app/cad/dom/components/View3d.jsx b/web/app/cad/dom/components/View3d.jsx
new file mode 100644
index 00000000..d1445863
--- /dev/null
+++ b/web/app/cad/dom/components/View3d.jsx
@@ -0,0 +1,63 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import PlugableControlBar from './PlugableControlBar';
+
+import ls from './View3d.less';
+import ObjectExplorer from './ObjectExplorer';
+import OperationHistory from './OperationHistory';
+import Toolbar, {ToolbarButton} from 'ui/components/Toolbar';
+import ImgIcon from 'ui/components/ImgIcon';
+import Fa from 'ui/components/Fa';
+import Abs from 'ui/components/Abs';
+import {PlugableToolbarLeft, PlugableToolbarLeftSecondary, PlugableToolbarRight} from "./PlugableToolbar";
+import MenuHolder from "../menu/MenuHolder";
+import {TOKENS as MENU_TOKENS} from '../menu/menuPlugin';
+
+
+export default class View3d extends React.PureComponent {
+
+ render() {
+ return
+
+
+
+
+
+
+ {/*
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+ closeAllUpPopups = () => {
+ let openedMenus = this.context.bus.state[MENU_TOKENS.OPENED];
+ if (openedMenus && openedMenus.length !== 0) {
+ this.context.bus.dispatch(MENU_TOKENS.CLOSE_ALL);
+ }
+
+ };
+
+ getChildContext() {
+ return {
+ closeAllUpPopups: this.closeAllUpPopups
+ }
+ }
+
+ static contextTypes = {
+ bus: PropTypes.object
+ };
+
+ static childContextTypes = {
+ closeAllUpPopups: PropTypes.func
+ };
+}
\ No newline at end of file
diff --git a/web/app/cad/dom/components/View3d.less b/web/app/cad/dom/components/View3d.less
new file mode 100644
index 00000000..b8d79fb2
--- /dev/null
+++ b/web/app/cad/dom/components/View3d.less
@@ -0,0 +1,23 @@
+.root {
+ height: 100%;
+ display: flex;
+}
+
+.viewer {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ position: relative;
+}
+
+.sideBar {
+ flex-basis: 250px;
+}
+
+.leftToolbarGroup > * {
+ margin-bottom: 0.8em;
+}
+
+.smallToolbar > * {
+ font-size: 2em;
+}
diff --git a/web/app/cad/dom/components/WebApplication.jsx b/web/app/cad/dom/components/WebApplication.jsx
new file mode 100644
index 00000000..52268df0
--- /dev/null
+++ b/web/app/cad/dom/components/WebApplication.jsx
@@ -0,0 +1,88 @@
+import React, {Fragment} from 'react';
+import PropTypes from 'prop-types';
+
+import 'ui/styles/init/minireset.css';
+import 'ui/styles/init/main.less';
+import '../../../../css/app3d-legacy.less';
+
+import View3d from './View3d';
+
+import WindowSystem from 'ui/WindowSystem';
+import Window from "ui/components/Window";
+import Folder from "ui/components/Folder";
+import Field from "ui/components/controls/Field";
+import Label from "ui/components/controls/Label";
+import NumberControl from "ui/components/controls/NumberControl";
+import ButtonGroup from "ui/components/controls/ButtonGroup";
+import Button from "ui/components/controls/Button";
+
+import ls from './WebApplication.less';
+import TabSwitcher, {Tab} from 'ui/components/TabSwticher';
+import Card from 'ui/components/Card';
+
+const DEFAULT_VIEW = {id: 'view3d', label: '3D View', Component: View3d};
+
+export default class WebApplication extends React.Component {
+
+ constructor({bus}) {
+ super();
+ this.bus = bus;
+ this.views = [DEFAULT_VIEW, {id: 'XXX', label: '3D View2', Component: Fragment}];
+ this.state = {
+ activeView: DEFAULT_VIEW
+ };
+ }
+
+ switchTab = (viewId) => {
+ this.setState({activeView: this.views.find(v => v.id === viewId)});
+ };
+
+ render() {
+ let activeView = this.state.activeView;
+ return
+
+
+ {this.views.map(({id, Component}) =>
+
+ )}
+
+
+
+ {this.views.map(({label, id}) => )}
+
+
+ {/*
*/}
+ {/*
*/}
+ {/**/}
+ {/**/}
+ {/**/}
+ {/* console.log(val)}/>*/}
+ {/**/}
+ {/**/}
+ {/**/}
+ {/* console.log(val)}/>*/}
+ {/**/}
+ {/**/}
+ {/**/}
+ {/**/}
+ {/**/}
+ {/**/}
+ {/**/}
+
+ }
+
+ getChildContext() {
+ return {bus: this.bus};
+ }
+
+ static childContextTypes = {
+ bus: PropTypes.object
+ };
+}
+
+function render(Component) {
+ return ;
+}
\ No newline at end of file
diff --git a/web/app/cad/dom/components/WebApplication.less b/web/app/cad/dom/components/WebApplication.less
new file mode 100644
index 00000000..02df60cf
--- /dev/null
+++ b/web/app/cad/dom/components/WebApplication.less
@@ -0,0 +1,21 @@
+@import "~ui/styles/theme";
+
+.root {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.content {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ & > * {
+ flex: 1;
+ }
+ position: relative;
+}
+
+.contentSwitcher {
+ border-top: 1px solid @border-color;
+}
\ No newline at end of file
diff --git a/web/app/3d/dom/domPlugin.js b/web/app/cad/dom/domPlugin.js
similarity index 100%
rename from web/app/3d/dom/domPlugin.js
rename to web/app/cad/dom/domPlugin.js
diff --git a/web/app/cad/dom/menu/MenuHolder.jsx b/web/app/cad/dom/menu/MenuHolder.jsx
new file mode 100644
index 00000000..fafdc003
--- /dev/null
+++ b/web/app/cad/dom/menu/MenuHolder.jsx
@@ -0,0 +1,66 @@
+import React, {Fragment} from 'react';
+import connect from 'ui/connect';
+import {TOKENS as MENU_TOKENS} from './menuPlugin';
+import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin';
+import Menu, {MenuItem, MenuSeparator} from "../../../../../modules/ui/components/Menu";
+import Fa from "../../../../../modules/ui/components/Fa";
+import Filler from "../../../../../modules/ui/components/Filler";
+import {TOKENS as KeyboardTokens} from "../../keyboard/keyboardPlugin";
+
+function MenuHolder({menus}) {
+ return menus.map(({id, actions}) => {
+ let menuToken = MENU_TOKENS.menuState(id);
+ return React.createElement(connect([menuToken, KeyboardTokens.KEYMAP],
+ ActionMenu, {actions}, [,keymap => ({keymap})]), {key: id});
+ });
+}
+
+function ActionMenu({actions, keymap, ...props}) {
+ return ;
+}
+
+function ActionMenuItem({label, cssIcons, icon32, icon96, onClick, enabled, hotKey, visible}) {
+ if (!visible) {
+ return null;
+ }
+ let icon, style;
+ if (icon32 || icon96) {
+ let size = 16;
+ icon = ;
+ style = {
+ backgroundImage: `url(${icon32 || icon96})`,
+ backgroundRepeat: 'no-repeat',
+ backgroundSize: `${size}px ${size}px`,
+ backgroundPositionY: 6,
+ backgroundPositionX: 5,
+ };
+ if (!enabled) {
+ style.filter = 'grayscale(90%)';
+ }
+ } else if (cssIcons) {
+ icon = ;
+ }
+
+ return ;
+}
+
+
+export default connect(MENU_TOKENS.MENUS, MenuHolder, undefined, ([menus]) => ({menus}));
+
+
+
diff --git a/web/app/cad/dom/menu/menuPlugin.js b/web/app/cad/dom/menu/menuPlugin.js
new file mode 100644
index 00000000..ea109858
--- /dev/null
+++ b/web/app/cad/dom/menu/menuPlugin.js
@@ -0,0 +1,50 @@
+import {createToken} from 'bus';
+
+export function activate({bus, services}) {
+
+ bus.enableState(TOKENS.MENUS, []);
+ bus.enableState(TOKENS.OPENED, []);
+
+ function registerMenus(menus) {
+ let menusToAdd = [];
+ let showMenuActions = [];
+ menus.forEach(({id, actions, ...appearance}) => {
+ let stateToken = TOKENS.menuState(id);
+ bus.enableState(stateToken, {
+ visible: false,
+ orientationUp: false,
+ x: undefined,
+ y: undefined
+ });
+ if (!appearance) {
+ appearance.label = id;
+ }
+ showMenuActions.push({
+ id: 'menu.' + id,
+ appearance,
+ invoke: (ctx, hints) => bus.updateStates([stateToken, TOKENS.OPENED],
+ ([state, opened]) => [Object.assign(state, {visible: true}, hints), [id, ...opened]]
+ )
+ });
+
+ menusToAdd.push({id, actions});
+ });
+ services.action.registerActions(showMenuActions);
+ bus.updateState(TOKENS.MENUS, menus => [...menus, ...menusToAdd]);
+ }
+
+ bus.subscribe(TOKENS.CLOSE_ALL, () => {
+ bus.state[TOKENS.OPENED].forEach(openedMenu => bus.setState(TOKENS.menuState(openedMenu), {visible: false}));
+ bus.updateState(TOKENS.OPENED, () => []);
+ });
+
+ services.menu = { registerMenus }
+}
+
+export const TOKENS = {
+ menuState: id => createToken('menu', 'state', id),
+ MENUS: createToken('menus'),
+ CLOSE_ALL: createToken('menus', 'closeAll'),
+ OPENED: createToken('menus', 'opened')
+};
+
diff --git a/web/app/cad/dom/startReact.jsx b/web/app/cad/dom/startReact.jsx
new file mode 100644
index 00000000..a16cce39
--- /dev/null
+++ b/web/app/cad/dom/startReact.jsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import WebApplication from './components/WebApplication';
+
+export default function startReact(bus, callback) {
+ return ReactDOM.render(
+ ,
+ document.getElementById('app'),
+ callback
+ );
+}
\ No newline at end of file
diff --git a/web/app/cad/dom/uiEntryPointsPlugin.js b/web/app/cad/dom/uiEntryPointsPlugin.js
new file mode 100644
index 00000000..c4e9cb06
--- /dev/null
+++ b/web/app/cad/dom/uiEntryPointsPlugin.js
@@ -0,0 +1,25 @@
+import {createToken} from 'bus';
+
+
+export function activate({bus}) {
+
+ bus.enableState(TOKENS.CONTROL_BAR_LEFT, []);
+ bus.enableState(TOKENS.CONTROL_BAR_RIGHT, []);
+
+ bus.enableState(TOKENS.TOOLBAR_BAR_LEFT, []);
+ bus.enableState(TOKENS.TOOLBAR_BAR_LEFT_SECONDARY, []);
+ bus.enableState(TOKENS.TOOLBAR_BAR_RIGHT, []);
+
+}
+
+const NS = 'ui.config';
+
+export const TOKENS = {
+ CONTROL_BAR_LEFT: createToken(NS, 'controlBar.left'),
+ CONTROL_BAR_RIGHT: createToken(NS, 'controlBar.right'),
+
+ TOOLBAR_BAR_LEFT: createToken(NS, 'toolbar.left'),
+ TOOLBAR_BAR_LEFT_SECONDARY: createToken(NS, 'toolbar.left.secondary'),
+ TOOLBAR_BAR_RIGHT: createToken(NS, 'toolbar.right'),
+};
+
diff --git a/web/app/cad/init/startApplication.js b/web/app/cad/init/startApplication.js
new file mode 100644
index 00000000..cd4f2048
--- /dev/null
+++ b/web/app/cad/init/startApplication.js
@@ -0,0 +1,53 @@
+import Bus from 'bus';
+import * as DomPlugin from '../dom/domPlugin';
+import * as PickControlPlugin from '../scene/controls/pickControlPlugin';
+import * as ScenePlugin from '../scene/scenePlugin';
+import * as SelectionMarkerPlugin from '../scene/selectionMarker/selectionMarkerPlugin';
+import * as ActionSystemPlugin from '../actions/actionSystemPlugin';
+import * as UiEntryPointsPlugin from '../dom/uiEntryPointsPlugin';
+import * as MenuPlugin from '../dom/menu/menuPlugin';
+import * as KeyboardPlugin from '../keyboard/keyboardPlugin';
+
+import * as PartModellerPlugin from '../part/partModellerPlugin';
+
+import startReact from "../dom/startReact";
+
+export default function startApplication(callback) {
+
+ let applicationPlugins = [PartModellerPlugin];
+
+ let preUIPlugins = [
+ ActionSystemPlugin,
+ MenuPlugin,
+ UiEntryPointsPlugin,
+ KeyboardPlugin
+ ];
+
+ let plugins = [
+ DomPlugin,
+ ScenePlugin,
+ PickControlPlugin,
+ SelectionMarkerPlugin,
+ ...applicationPlugins,
+ ];
+
+ let context = {
+ bus: new Bus(),
+ services: {}
+ };
+
+ activatePlugins(preUIPlugins, context);
+
+ startReact(context.bus, () => {
+ activatePlugins(plugins, context);
+ context.services.viewer.render();
+ callback(context);
+ });
+}
+
+function activatePlugins(plugins, context) {
+ for (let plugin of plugins) {
+ plugin.activate(context);
+ }
+}
+
diff --git a/web/app/3d/io.js b/web/app/cad/io.js
similarity index 100%
rename from web/app/3d/io.js
rename to web/app/cad/io.js
diff --git a/web/app/cad/keyboard/keyboardPlugin.js b/web/app/cad/keyboard/keyboardPlugin.js
new file mode 100644
index 00000000..d1803843
--- /dev/null
+++ b/web/app/cad/keyboard/keyboardPlugin.js
@@ -0,0 +1,11 @@
+import DefaultKeymap from './keymaps/default';
+
+import {createToken} from "../../../../modules/bus/index";
+
+export function activate({bus}) {
+ bus.enableState(TOKENS.KEYMAP, DefaultKeymap);
+}
+
+export const TOKENS = {
+ KEYMAP: createToken('keymap')
+};
\ No newline at end of file
diff --git a/web/app/3d/ui/keymaps/default.js b/web/app/cad/keyboard/keymaps/default.js
similarity index 90%
rename from web/app/3d/ui/keymaps/default.js
rename to web/app/cad/keyboard/keymaps/default.js
index 2a7ca0fa..adc090f7 100644
--- a/web/app/3d/ui/keymaps/default.js
+++ b/web/app/cad/keyboard/keymaps/default.js
@@ -1,4 +1,4 @@
-export const keymap = {
+export default {
'CUT': 'C',
'EXTRUDE': 'E',
'ZoomIn': '+',
diff --git a/web/app/3d/menu/menu.js b/web/app/cad/menu/menu.js
similarity index 100%
rename from web/app/3d/menu/menu.js
rename to web/app/cad/menu/menu.js
diff --git a/web/app/3d/mesh.js b/web/app/cad/mesh.js
similarity index 100%
rename from web/app/3d/mesh.js
rename to web/app/cad/mesh.js
diff --git a/web/app/3d/modeler-app.js b/web/app/cad/modeler-app.js
similarity index 98%
rename from web/app/3d/modeler-app.js
rename to web/app/cad/modeler-app.js
index 7f4d2ac8..57e54d71 100644
--- a/web/app/3d/modeler-app.js
+++ b/web/app/cad/modeler-app.js
@@ -6,7 +6,7 @@ import TabSwitcher from './ui/tab-switcher'
import ControlBar from './ui/control-bar'
import {InputManager} from './ui/input-manager'
import {ActionManager} from './actions/actions'
-import * as AllActions from './actions/all-actions'
+import * as AllActions from './actions/allActions'
import Vector from 'math/vector';
import {Matrix3, AXIS, ORIGIN, IDENTITY_BASIS} from '../math/l3space'
import {Craft} from './craft/craft'
@@ -17,7 +17,6 @@ import * as math from '../math/math'
import {IO} from '../sketcher/io'
import {AddDebugSupport} from './debug'
import {init as initSample} from './sample'
-import '../../css/app3d.less'
import BrepBuilder from '../brep/brep-builder'
import * as BREPPrimitives from '../brep/brep-primitives'
@@ -49,7 +48,7 @@ function App() {
// this.viewer.workGroup = this.context.services.cadScene.workGroup;
this.actionManager.registerActions(AllActions);
- this.tabSwitcher = new TabSwitcher($('#tab-switcher'), $('#view-3d'));
+ // this.tabSwitcher = new TabSwitcher($('#tab-switcher'), $('#view-3d'));
this.controlBar = new ControlBar(this, $('#control-bar'));
this.TPI = TPI;
@@ -94,7 +93,7 @@ function App() {
App.prototype.createPluginContext = function() {
return {
bus: this.bus,
- services: {}
+ services: {}
};
};
@@ -104,6 +103,14 @@ App.prototype.initPlugins = function() {
}
};
+//temporary workaround before extracted to plugin
+App.prototype.activateSketcherPlugin = function(context) {
+ context.services.sketcher = {
+ sketchFace: (sceneFace) => this.sketchFace(sceneFace),
+ editFace: () => this.editFace()
+ }
+};
+
App.prototype.getFaceSelection = function() {
let selection = this.context.bus.state['selection_face'];
return selection;
diff --git a/web/app/cad/part/menuConfig.js b/web/app/cad/part/menuConfig.js
new file mode 100644
index 00000000..2b415a7e
--- /dev/null
+++ b/web/app/cad/part/menuConfig.js
@@ -0,0 +1,41 @@
+export default [
+ {
+ id: 'file',
+ cssIcons: ['file'],
+ actions: ['Save', 'StlExport', '-', 'IMPORT_STL']
+ },
+ {
+ id: 'craft',
+ cssIcons: ['magic'],
+ info: 'set of available craft operations on a solid',
+ actions: ['EXTRUDE', 'CUT', 'REVOLVE', 'SHELL']
+ },
+ {
+ id: 'primitives',
+ label: 'add',
+ cssIcons: ['cube', 'plus'],
+ info: 'set of available solid creation operations',
+ actions: ['PLANE', 'BOX', 'SPHERE']
+ },
+ {
+ id: 'boolean',
+ label: 'bool',
+ cssIcons: ['pie-chart'],
+ info: 'set of available boolean operations',
+ actions: ['INTERSECTION', 'DIFFERENCE', 'UNION']
+ },
+ {
+ id: 'main',
+ label: 'start',
+ cssIcons: ['rocket'],
+ info: 'common set of actions',
+ actions: ['EXTRUDE', 'CUT', 'SHELL', '-', 'INTERSECTION', 'DIFFERENCE', 'UNION', '-', 'PLANE', 'BOX', 'SPHERE', '-',
+ 'EditFace', '-', 'DeselectAll', 'RefreshSketches']
+ },
+ {
+ id: 'SolidContext',
+ label: 'solid-context',
+ info: 'solid context actions',
+ actions: ['LookAtSolid']
+ }
+];
diff --git a/web/app/cad/part/partModellerPlugin.js b/web/app/cad/part/partModellerPlugin.js
new file mode 100644
index 00000000..8ac5b70c
--- /dev/null
+++ b/web/app/cad/part/partModellerPlugin.js
@@ -0,0 +1,25 @@
+import CoreActions from '../actions/coreActions';
+import OperationActions from '../actions/operationActions';
+import HistoryActions from '../actions/historyActions';
+import {TOKENS as UI_TOKENS} from '../dom/uiEntryPointsPlugin';
+import menuConfig from "./menuConfig";
+
+export function activate({bus, services}) {
+
+ services.action.registerActions(CoreActions);
+ services.action.registerActions(OperationActions);
+ services.action.registerActions(HistoryActions);
+
+ services.menu.registerMenus(menuConfig);
+
+ bus.dispatch(UI_TOKENS.CONTROL_BAR_LEFT, ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'Donate', 'GitHub']);
+ bus.dispatch(UI_TOKENS.CONTROL_BAR_RIGHT, [
+ ['Info', {label: null}],
+ ['RefreshSketches', {label: null}],
+ ['ShowSketches', {label: 'sketches'}], ['DeselectAll', null], ['ToggleCameraMode', null]
+ ]);
+
+ bus.dispatch(UI_TOKENS.TOOLBAR_BAR_LEFT, ['PLANE', 'EditFace', 'EXTRUDE', 'CUT', 'REVOLVE']);
+ bus.dispatch(UI_TOKENS.TOOLBAR_BAR_LEFT_SECONDARY, ['INTERSECTION', 'DIFFERENCE', 'UNION']);
+ bus.dispatch(UI_TOKENS.TOOLBAR_BAR_RIGHT, ['Save', 'StlExport']);
+}
\ No newline at end of file
diff --git a/web/app/3d/plugins.js b/web/app/cad/plugins.js
similarity index 100%
rename from web/app/3d/plugins.js
rename to web/app/cad/plugins.js
diff --git a/web/app/3d/sample.js b/web/app/cad/sample.js
similarity index 100%
rename from web/app/3d/sample.js
rename to web/app/cad/sample.js
diff --git a/web/app/3d/scene/cadScene.js b/web/app/cad/scene/cadScene.js
similarity index 100%
rename from web/app/3d/scene/cadScene.js
rename to web/app/cad/scene/cadScene.js
diff --git a/web/app/3d/scene/controls/pickControlPlugin.js b/web/app/cad/scene/controls/pickControlPlugin.js
similarity index 91%
rename from web/app/3d/scene/controls/pickControlPlugin.js
rename to web/app/cad/scene/controls/pickControlPlugin.js
index 9ba960a9..973154ef 100644
--- a/web/app/3d/scene/controls/pickControlPlugin.js
+++ b/web/app/cad/scene/controls/pickControlPlugin.js
@@ -9,13 +9,9 @@ export const PICK_KIND = {
export function activate(context) {
- let {bus} = context;
+ initStateAndServices(context);
let domElement = context.services.viewer.sceneSetup.domElement();
- bus.enableState('selection_solid', []);
- bus.enableState('selection_face', []);
- bus.enableState('selection_edge', []);
- bus.enableState('selection_sketchObject', []);
-
+
domElement.addEventListener('mousedown', mousedown, false);
domElement.addEventListener('mouseup', mouseup, false);
@@ -113,6 +109,19 @@ export function activate(context) {
}
}
+function initStateAndServices({bus, services}) {
+ bus.enableState('selection_solid', []);
+ bus.enableState('selection_face', []);
+ bus.enableState('selection_edge', []);
+ bus.enableState('selection_sketchObject', []);
+
+ services.selection = {
+ solid: () => bus.state.selection_solid,
+ face: () => bus.state.selection_face,
+ edge: () => bus.state.selection_edge,
+ sketchObject: () => bus.state.selection_sketchObject
+ };
+}
diff --git a/web/app/3d/scene/scenePlugin.js b/web/app/cad/scene/scenePlugin.js
similarity index 100%
rename from web/app/3d/scene/scenePlugin.js
rename to web/app/cad/scene/scenePlugin.js
diff --git a/web/app/3d/scene/selectionMarker/abstractSelectionMarker.js b/web/app/cad/scene/selectionMarker/abstractSelectionMarker.js
similarity index 100%
rename from web/app/3d/scene/selectionMarker/abstractSelectionMarker.js
rename to web/app/cad/scene/selectionMarker/abstractSelectionMarker.js
diff --git a/web/app/3d/scene/selectionMarker/edgeSelectionMarker.js b/web/app/cad/scene/selectionMarker/edgeSelectionMarker.js
similarity index 100%
rename from web/app/3d/scene/selectionMarker/edgeSelectionMarker.js
rename to web/app/cad/scene/selectionMarker/edgeSelectionMarker.js
diff --git a/web/app/3d/scene/selectionMarker/lineMarker.js b/web/app/cad/scene/selectionMarker/lineMarker.js
similarity index 100%
rename from web/app/3d/scene/selectionMarker/lineMarker.js
rename to web/app/cad/scene/selectionMarker/lineMarker.js
diff --git a/web/app/3d/scene/selectionMarker/selectionMarker.js b/web/app/cad/scene/selectionMarker/selectionMarker.js
similarity index 100%
rename from web/app/3d/scene/selectionMarker/selectionMarker.js
rename to web/app/cad/scene/selectionMarker/selectionMarker.js
diff --git a/web/app/3d/scene/selectionMarker/selectionMarkerPlugin.js b/web/app/cad/scene/selectionMarker/selectionMarkerPlugin.js
similarity index 100%
rename from web/app/3d/scene/selectionMarker/selectionMarkerPlugin.js
rename to web/app/cad/scene/selectionMarker/selectionMarkerPlugin.js
diff --git a/web/app/3d/scene/selectionMarker/sketchSelectionMarker.js b/web/app/cad/scene/selectionMarker/sketchSelectionMarker.js
similarity index 100%
rename from web/app/3d/scene/selectionMarker/sketchSelectionMarker.js
rename to web/app/cad/scene/selectionMarker/sketchSelectionMarker.js
diff --git a/web/app/3d/scene/viewer.js b/web/app/cad/scene/viewer.js
similarity index 100%
rename from web/app/3d/scene/viewer.js
rename to web/app/cad/scene/viewer.js
diff --git a/web/app/3d/scene/wrappers/brepSceneObject.js b/web/app/cad/scene/wrappers/brepSceneObject.js
similarity index 100%
rename from web/app/3d/scene/wrappers/brepSceneObject.js
rename to web/app/cad/scene/wrappers/brepSceneObject.js
diff --git a/web/app/3d/scene/wrappers/meshSceneObject.js b/web/app/cad/scene/wrappers/meshSceneObject.js
similarity index 100%
rename from web/app/3d/scene/wrappers/meshSceneObject.js
rename to web/app/cad/scene/wrappers/meshSceneObject.js
diff --git a/web/app/3d/scene/wrappers/planeSceneObject.js b/web/app/cad/scene/wrappers/planeSceneObject.js
similarity index 100%
rename from web/app/3d/scene/wrappers/planeSceneObject.js
rename to web/app/cad/scene/wrappers/planeSceneObject.js
diff --git a/web/app/3d/scene/wrappers/sceneObject.js b/web/app/cad/scene/wrappers/sceneObject.js
similarity index 100%
rename from web/app/3d/scene/wrappers/sceneObject.js
rename to web/app/cad/scene/wrappers/sceneObject.js
diff --git a/web/app/3d/stl/stl-ascii-reader.js b/web/app/cad/stl/stl-ascii-reader.js
similarity index 100%
rename from web/app/3d/stl/stl-ascii-reader.js
rename to web/app/cad/stl/stl-ascii-reader.js
diff --git a/web/app/3d/stl/stl-binary-reader.js b/web/app/cad/stl/stl-binary-reader.js
similarity index 100%
rename from web/app/3d/stl/stl-binary-reader.js
rename to web/app/cad/stl/stl-binary-reader.js
diff --git a/web/app/3d/stl/stl-data-structure.js b/web/app/cad/stl/stl-data-structure.js
similarity index 100%
rename from web/app/3d/stl/stl-data-structure.js
rename to web/app/cad/stl/stl-data-structure.js
diff --git a/web/app/3d/stl/stl-reader.js b/web/app/cad/stl/stl-reader.js
similarity index 100%
rename from web/app/3d/stl/stl-reader.js
rename to web/app/cad/stl/stl-reader.js
diff --git a/web/app/3d/tess/brep-tess.js b/web/app/cad/tess/brep-tess.js
similarity index 100%
rename from web/app/3d/tess/brep-tess.js
rename to web/app/cad/tess/brep-tess.js
diff --git a/web/app/3d/tess/nested-loops.js b/web/app/cad/tess/nested-loops.js
similarity index 100%
rename from web/app/3d/tess/nested-loops.js
rename to web/app/cad/tess/nested-loops.js
diff --git a/web/app/3d/tess/pip.js b/web/app/cad/tess/pip.js
similarity index 100%
rename from web/app/3d/tess/pip.js
rename to web/app/cad/tess/pip.js
diff --git a/web/app/3d/tess/triangulation.js b/web/app/cad/tess/triangulation.js
similarity index 100%
rename from web/app/3d/tess/triangulation.js
rename to web/app/cad/tess/triangulation.js
diff --git a/web/app/3d/tpi.js b/web/app/cad/tpi.js
similarity index 100%
rename from web/app/3d/tpi.js
rename to web/app/cad/tpi.js
diff --git a/web/app/3d/ui/bind.js b/web/app/cad/ui/bind.js
similarity index 100%
rename from web/app/3d/ui/bind.js
rename to web/app/cad/ui/bind.js
diff --git a/web/app/3d/ui/control-bar.js b/web/app/cad/ui/control-bar.js
similarity index 100%
rename from web/app/3d/ui/control-bar.js
rename to web/app/cad/ui/control-bar.js
diff --git a/web/app/3d/ui/ctrl.js b/web/app/cad/ui/ctrl.js
similarity index 100%
rename from web/app/3d/ui/ctrl.js
rename to web/app/cad/ui/ctrl.js
diff --git a/web/app/3d/ui/input-manager.js b/web/app/cad/ui/input-manager.js
similarity index 98%
rename from web/app/3d/ui/input-manager.js
rename to web/app/cad/ui/input-manager.js
index ee7d131e..e805a8c3 100644
--- a/web/app/3d/ui/input-manager.js
+++ b/web/app/cad/ui/input-manager.js
@@ -1,5 +1,5 @@
import {jwerty} from 'jwerty'
-import {keymap} from './keymaps/default'
+import {keymap} from '../keyboard/keymaps/default'
import {Bind} from './bind'
import {MessageSink} from './message-sink'
import {LoadTemplate, DefaultMouseEvent, EventData, fit} from './utils'
diff --git a/web/app/3d/ui/message-sink.js b/web/app/cad/ui/message-sink.js
similarity index 100%
rename from web/app/3d/ui/message-sink.js
rename to web/app/cad/ui/message-sink.js
diff --git a/web/app/3d/ui/modifications-panel.js b/web/app/cad/ui/modifications-panel.js
similarity index 100%
rename from web/app/3d/ui/modifications-panel.js
rename to web/app/cad/ui/modifications-panel.js
diff --git a/web/app/3d/ui/solid-list.js b/web/app/cad/ui/solid-list.js
similarity index 100%
rename from web/app/3d/ui/solid-list.js
rename to web/app/cad/ui/solid-list.js
diff --git a/web/app/3d/ui/tab-switcher.js b/web/app/cad/ui/tab-switcher.js
similarity index 100%
rename from web/app/3d/ui/tab-switcher.js
rename to web/app/cad/ui/tab-switcher.js
diff --git a/web/app/3d/ui/tmpl/action-info.html b/web/app/cad/ui/tmpl/action-info.html
similarity index 100%
rename from web/app/3d/ui/tmpl/action-info.html
rename to web/app/cad/ui/tmpl/action-info.html
diff --git a/web/app/3d/ui/tmpl/modifications.html b/web/app/cad/ui/tmpl/modifications.html
similarity index 100%
rename from web/app/3d/ui/tmpl/modifications.html
rename to web/app/cad/ui/tmpl/modifications.html
diff --git a/web/app/3d/ui/tmpl/solid-list.html b/web/app/cad/ui/tmpl/solid-list.html
similarity index 100%
rename from web/app/3d/ui/tmpl/solid-list.html
rename to web/app/cad/ui/tmpl/solid-list.html
diff --git a/web/app/3d/ui/toolbar.js b/web/app/cad/ui/toolbar.js
similarity index 100%
rename from web/app/3d/ui/toolbar.js
rename to web/app/cad/ui/toolbar.js
diff --git a/web/app/3d/ui/utils.js b/web/app/cad/ui/utils.js
similarity index 100%
rename from web/app/3d/ui/utils.js
rename to web/app/cad/ui/utils.js
diff --git a/web/app/index.js b/web/app/index.js
index 16d30e60..0c492d14 100644
--- a/web/app/index.js
+++ b/web/app/index.js
@@ -1,7 +1,6 @@
-//import './utils/jqueryfy'
-import App from './3d/modeler-app'
-import startReact from './3d/dom/startReact';
-startReact(() =>{
- window._TCAD_APP = new App();
+import startApplication from "./cad/init/startApplication";
+
+startApplication(context => {
+ window.__CAD_APP = context;
});
diff --git a/web/app/sketcher.js b/web/app/sketcher.js
index 9141f342..76aa78fc 100644
--- a/web/app/sketcher.js
+++ b/web/app/sketcher.js
@@ -10,7 +10,7 @@ import '../css/app.less'
function initializeSketcherApplication() {
var app = new App2D();
- window._TCAD_APP = app;
+ window.__CAD_APP = app;
var sketchId = app.getSketchId();
if (sketchId == App2D.STORAGE_PREFIX + '__sample2D__') {
var sample = '{"layers":[{"name":"_dim","style":{"lineWidth":1,"strokeStyle":"#bcffc1","fillStyle":"#00FF00"},"data":[{"id":0,"_class":"TCAD.TWO.DiameterDimension","obj":90},{"id":1,"_class":"TCAD.TWO.DiameterDimension","obj":95},{"id":2,"_class":"TCAD.TWO.DiameterDimension","obj":42},{"id":3,"_class":"TCAD.TWO.Dimension","a":5,"b":8,"flip":false},{"id":4,"_class":"TCAD.TWO.DiameterDimension","obj":105}]},{"name":"sketch","style":{"lineWidth":2,"strokeStyle":"#ffffff","fillStyle":"#000000"},"data":[{"id":11,"_class":"TCAD.TWO.Segment","points":[[5,[6,110.1295615870824],[7,313.66509156975803]],[8,[9,419.44198895058975],[10,516.7065215258621]]]},{"id":18,"_class":"TCAD.TWO.Segment","points":[[12,[13,489.1218947877601],[14,477.98601743930897]],[15,[16,481.90945628911174],[17,182.9391540301952]]]},{"id":25,"_class":"TCAD.TWO.Segment","points":[[19,[20,427.6872468325118],[21,163.96220645927505]],[22,[23,349.9023145352797],[24,256.7344291384989]]]},{"id":32,"_class":"TCAD.TWO.Segment","points":[[26,[27,306.81261277555075],[28,273.1404656521002]],[29,[30,135.09050734792822],[31,247.98348666778958]]]},{"id":42,"_class":"TCAD.TWO.Arc","points":[[33,[34,489.1218947877601],[35,477.98601743930897]],[36,[37,419.44198895058975],[38,516.7065215258621]],[39,[40,444.1353623657045],[41,479.08688157090376]]]},{"id":53,"_class":"TCAD.TWO.Arc","points":[[44,[45,427.6872468325118],[46,163.96220645927505]],[47,[48,481.90945628911174],[49,182.9391540301952]],[50,[51,451.2148840882273],[52,183.68960424767275]]]},{"id":64,"_class":"TCAD.TWO.Arc","points":[[55,[56,349.9023145352797],[57,256.7344291384989]],[58,[59,306.81261277555075],[60,273.1404656521002]],[61,[62,313.6665992835383],[63,226.35256652594512]]]},{"id":75,"_class":"TCAD.TWO.Arc","points":[[66,[67,110.1295615870824],[68,313.66509156975803]],[69,[70,135.09050734792822],[71,247.98348666778958]],[72,[73,129.8749213918784],[74,283.58516027516237]]]},{"id":80,"_class":"TCAD.TWO.Circle","c":[77,[78,444.1353623657045],[79,479.08688157090376]],"r":17},{"id":85,"_class":"TCAD.TWO.Circle","c":[82,[83,451.2148840882273],[84,183.68960424767275]],"r":17},{"id":90,"_class":"TCAD.TWO.Circle","c":[87,[88,129.8749213918784],[89,283.58516027516237]],"r":17},{"id":95,"_class":"TCAD.TWO.Circle","c":[92,[93,364.7627927122075],[94,358.27520724354514]],"r":50},{"id":100,"_class":"TCAD.TWO.Circle","c":[97,[98,450.6425914465028],[99,356.1758703461729]],"r":13},{"id":105,"_class":"TCAD.TWO.Circle","c":[102,[103,281.1241663120215],[104,360.3197585470608]],"r":13}]},{"name":"_construction_","style":{"lineWidth":1,"strokeStyle":"#aaaaaa","fillStyle":"#000000"},"data":[{"id":113,"_class":"TCAD.TWO.Segment","points":[[107,[108,366.96497096679207],[109,448.36204633886825]],[110,[111,362.6842565514955],[112,273.2463262825022]]]},{"id":120,"_class":"TCAD.TWO.Segment","points":[[114,[115,254.60331148100178],[116,360.9680624545806]],[117,[118,474.9222739434132],[119,355.5823520325097]]]}]}],"constraints":[["Tangent",[42,18]],["Tangent",[42,11]],["coi",[33,12]],["coi",[36,8]],["Tangent",[53,25]],["Tangent",[53,18]],["coi",[44,19]],["coi",[47,15]],["Tangent",[64,25]],["Tangent",[64,32]],["coi",[55,22]],["coi",[58,26]],["Tangent",[75,11]],["Tangent",[75,32]],["coi",[66,5]],["coi",[69,29]],["coi",[77,39]],["coi",[82,50]],["coi",[87,72]],["RR",[80,85]],["RR",[85,90]],["parallel",[113,18]],["perpendicular",[120,113]],["Symmetry",[92,120]],["PointOnLine",[92,113]],["PointOnLine",[102,120]],["PointOnLine",[97,120]],["RR",[105,100]]]}';
diff --git a/web/app/utils/jqueryfy.js b/web/app/utils/jqueryfy.js
index 4b250de0..74ce9c95 100644
--- a/web/app/utils/jqueryfy.js
+++ b/web/app/utils/jqueryfy.js
@@ -1,2 +1,6 @@
import $ from 'jquery'
+
+// Usage of jquery is totally deprecated. It exists only to support legacy code and
+// will be gone soon after complete transition to React.
+
window.jQuery = window.$ = $;
diff --git a/web/css/app3d.less b/web/css/app3d-legacy.less
similarity index 57%
rename from web/css/app3d.less
rename to web/css/app3d-legacy.less
index 090bcaac..86cf651b 100644
--- a/web/css/app3d.less
+++ b/web/css/app3d-legacy.less
@@ -1,16 +1,7 @@
@import 'brep-debugger.less';
-@tab-switcher-inner-height: 20px;
-@tab-switcher-top-border: 1px;
-@tab-switcher-height: @tab-switcher-inner-height + @tab-switcher-top-border;
-
@tab-border-color: #2c2c2c;
-
-@right-panel-width: 250px;
-
-@control-button-border: 1px solid #2c2c2c;
@menu-border-radius: 3px;
-
@suppressed-color: #888;
.no-selection {
@@ -20,136 +11,14 @@
-ms-user-select: none;
}
-body {
- background-color: #808080;
- .main-font;
-}
-
-iframe {
- border: 0;
-}
-
-.main-font {
- font: 11px 'Lucida Grande', sans-serif;
-}
-
.history-selected, .history-selected:hover {
background-color: #780000;
}
-.app-tab-view {
- position: absolute;
- top: 0;
- bottom: @tab-switcher-height;
- width: 100%;
-}
-
-#tab-switcher {
- position: absolute;
- height: @tab-switcher-inner-height;
- bottom: 0;
- background-color: #000;
- width: 100%;
- border-top: 1px solid @tab-border-color;
- color: #eee;
- text-align: center;
- .no-selection;
-}
-
-#tab-switcher .tab {
- padding: 2px 5px 0 5px;
- height: 100%;
- float: left;
- cursor: pointer;
- border-right: 1px solid #2c2c2c;
- color: #aaa;
-}
-
-#tab-switcher .tab:hover {
- color: #eee;
-}
-
-#tab-switcher .tab-selected {
- background-color: #222;
- color: #eee;
-}
-
-.tab .expand:hover {
- color: green;
-}
-
-.tab .close:hover {
- color: red;
-}
-
-#viewer-container {
- position: absolute;
- left: @right-panel-width;
- right: 0;
- top: 0;
- bottom: 0;
-}
-
-#control-bar {
- position: absolute;
- left: @right-panel-width;
- right: 0;
- bottom: 0;
- height: 20px;
- background-color: rgba(0, 0, 0, 0.5);
- color: #ccc;
-}
-
-#control-bar .left-group {
- text-align: left;
- float: left;
-}
-
-#control-bar .right-group {
- text-align: right;
-}
-
-#control-bar .left-group .button {
- float: left;
- border-right: @control-button-border;
-}
-
-#control-bar .right-group .button {
- float: right;
- border-left: @control-button-border;
-}
-
.button .fa {
line-height: 1.5;
}
-#control-bar .button {
- padding: 3px 7px 0 5px;
- height: 100%;
- vertical-align: baseline;
- cursor: pointer;
- .no-selection
-}
-
-#control-bar .button:hover {
- background-color: #555;
-}
-
-#control-bar .button-selected {
- background-color: #666;
-}
-
-#control-bar .button-selected:hover {
- background-color: #666;
-}
-
-#right-panel {
- position: absolute;
- height: 100%;
- background-color: #000;
- width: @right-panel-width;
-}
-
.aux-win {
color: #fff;
background-color: rgba(40,40,40,0.95);
diff --git a/web/img/3d/cube32.png b/web/img/cad/cube32.png
similarity index 100%
rename from web/img/3d/cube32.png
rename to web/img/cad/cube32.png
diff --git a/web/img/3d/cube96.png b/web/img/cad/cube96.png
similarity index 100%
rename from web/img/3d/cube96.png
rename to web/img/cad/cube96.png
diff --git a/web/img/3d/cut32.png b/web/img/cad/cut32.png
similarity index 100%
rename from web/img/3d/cut32.png
rename to web/img/cad/cut32.png
diff --git a/web/img/3d/cut96.png b/web/img/cad/cut96.png
similarity index 100%
rename from web/img/3d/cut96.png
rename to web/img/cad/cut96.png
diff --git a/web/img/3d/difference32.png b/web/img/cad/difference32.png
similarity index 100%
rename from web/img/3d/difference32.png
rename to web/img/cad/difference32.png
diff --git a/web/img/3d/difference96.png b/web/img/cad/difference96.png
similarity index 100%
rename from web/img/3d/difference96.png
rename to web/img/cad/difference96.png
diff --git a/web/img/3d/extrude32.png b/web/img/cad/extrude32.png
similarity index 100%
rename from web/img/3d/extrude32.png
rename to web/img/cad/extrude32.png
diff --git a/web/img/3d/extrude96.png b/web/img/cad/extrude96.png
similarity index 100%
rename from web/img/3d/extrude96.png
rename to web/img/cad/extrude96.png
diff --git a/web/img/3d/face-edit96.png b/web/img/cad/face-edit96.png
similarity index 100%
rename from web/img/3d/face-edit96.png
rename to web/img/cad/face-edit96.png
diff --git a/web/img/3d/intersection32.png b/web/img/cad/intersection32.png
similarity index 100%
rename from web/img/3d/intersection32.png
rename to web/img/cad/intersection32.png
diff --git a/web/img/3d/intersection96.png b/web/img/cad/intersection96.png
similarity index 100%
rename from web/img/3d/intersection96.png
rename to web/img/cad/intersection96.png
diff --git a/web/img/3d/plane32.png b/web/img/cad/plane32.png
similarity index 100%
rename from web/img/3d/plane32.png
rename to web/img/cad/plane32.png
diff --git a/web/img/3d/plane96.png b/web/img/cad/plane96.png
similarity index 100%
rename from web/img/3d/plane96.png
rename to web/img/cad/plane96.png
diff --git a/web/img/3d/revolve32.png b/web/img/cad/revolve32.png
similarity index 100%
rename from web/img/3d/revolve32.png
rename to web/img/cad/revolve32.png
diff --git a/web/img/3d/revolve96.png b/web/img/cad/revolve96.png
similarity index 100%
rename from web/img/3d/revolve96.png
rename to web/img/cad/revolve96.png
diff --git a/web/img/3d/shell32.png b/web/img/cad/shell32.png
similarity index 100%
rename from web/img/3d/shell32.png
rename to web/img/cad/shell32.png
diff --git a/web/img/3d/shell96.png b/web/img/cad/shell96.png
similarity index 100%
rename from web/img/3d/shell96.png
rename to web/img/cad/shell96.png
diff --git a/web/img/3d/sketch32.png b/web/img/cad/sketch32.png
similarity index 100%
rename from web/img/3d/sketch32.png
rename to web/img/cad/sketch32.png
diff --git a/web/img/3d/sketch96.png b/web/img/cad/sketch96.png
similarity index 100%
rename from web/img/3d/sketch96.png
rename to web/img/cad/sketch96.png
diff --git a/web/img/3d/solid32.png b/web/img/cad/solid32.png
similarity index 100%
rename from web/img/3d/solid32.png
rename to web/img/cad/solid32.png
diff --git a/web/img/3d/sphere32.png b/web/img/cad/sphere32.png
similarity index 100%
rename from web/img/3d/sphere32.png
rename to web/img/cad/sphere32.png
diff --git a/web/img/3d/sphere96.png b/web/img/cad/sphere96.png
similarity index 100%
rename from web/img/3d/sphere96.png
rename to web/img/cad/sphere96.png
diff --git a/web/img/3d/stl32.png b/web/img/cad/stl32.png
similarity index 100%
rename from web/img/3d/stl32.png
rename to web/img/cad/stl32.png
diff --git a/web/img/3d/stl96.png b/web/img/cad/stl96.png
similarity index 100%
rename from web/img/3d/stl96.png
rename to web/img/cad/stl96.png
diff --git a/web/img/3d/union32.png b/web/img/cad/union32.png
similarity index 100%
rename from web/img/3d/union32.png
rename to web/img/cad/union32.png
diff --git a/web/img/3d/union96.png b/web/img/cad/union96.png
similarity index 100%
rename from web/img/3d/union96.png
rename to web/img/cad/union96.png
diff --git a/web/index.html b/web/index.html
index f3a7a1a2..d5e605dc 100644
--- a/web/index.html
+++ b/web/index.html
@@ -1,13 +1,6 @@
TCAD
-
@@ -16,7 +9,7 @@
-
+
diff --git a/web/test/menu.js b/web/test/menu.js
index f1509e5b..7c7a8952 100644
--- a/web/test/menu.js
+++ b/web/test/menu.js
@@ -1,4 +1,4 @@
-//import {DefaultMouseEvent} from '../app/3d/ui/utils'
+//import {DefaultMouseEvent} from '../app/cad/ui/utils'
export class Menu {
diff --git a/web/test/test.js b/web/test/test.js
index 1b2f587e..03310f98 100644
--- a/web/test/test.js
+++ b/web/test/test.js
@@ -147,7 +147,7 @@ export function load(url, callback) {
$(function() { // fire event when iframe is ready
frame.load(function() {
const win = frame.get(0).contentWindow;
- callback(win, win._TCAD_APP)
+ callback(win, win.__CAD_APP)
});
});
frame.attr('src', window.location.origin + url)
diff --git a/webpack.config.js b/webpack.config.js
index b93baccb..364e2bf6 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -4,7 +4,9 @@ const generateCSSScopedName = require('./build/cssScoopeGenerator')();
const WEB_APP = path.join(__dirname, 'web/app');
const MODULES = path.join(__dirname, 'modules');
-const INTEGRATION_TESTS = path.join(__dirname, 'web/test');
+const INTEGRATION_TESTS = path.join(__dirname, 'web/test');
+
+const GLOBAL_CSS = path.join(__dirname, 'web/css');
module.exports = {
devtool: 'source-map',
@@ -34,14 +36,17 @@ module.exports = {
loader: 'babel-loader',
include: [MODULES, WEB_APP, INTEGRATION_TESTS]
}, {
- test: /\.css$/,
+ test: /\.(less|css)$/,
+ include: GLOBAL_CSS,
use: [
'style-loader',
- 'css-loader',
- ]
+ 'css-loader?-url',
+ 'less-loader',
+ ]
},
{
- test: /\.less$/,
+ test: /\.(less|css)$/,
+ include: [MODULES, WEB_APP],
use: [
'style-loader',
{