From 547ec02b01864862706d6a9af8385331dd9ff60f Mon Sep 17 00:00:00 2001 From: "Val Erastov (xibyte)" Date: Mon, 9 Mar 2020 23:46:16 -0700 Subject: [PATCH] object highlight mode, reuse styles --- modules/ui/WindowSystem.jsx | 10 +- modules/ui/components/Window.jsx | 27 ++-- modules/ui/components/controls/Button.jsx | 3 +- modules/ui/components/controls/Button.less | 30 ---- .../ui/components/controls/ButtonGroup.jsx | 4 +- .../ui/components/controls/ButtonGroup.less | 2 +- .../ui/components/controls/InputControl.jsx | 4 +- .../ui/components/controls/InputControl.less | 20 --- modules/ui/components/controls/Label.jsx | 3 - modules/ui/components/controls/Label.less | 0 modules/ui/styles/common.less | 9 -- modules/ui/styles/init/form.less | 64 ++++++++ modules/ui/styles/init/index.less | 7 + modules/ui/styles/init/links.less | 7 + modules/ui/styles/init/main.less | 24 +-- .../init/{minireset.css => minireset.less} | 0 .../styles/{table.less => init/tables.less} | 8 +- modules/ui/styles/theme.less | 2 + web/app/cad/dom/components/WebApplication.jsx | 3 +- web/app/cad/expressions/Expressions.jsx | 2 +- web/app/sketcher.js | 2 + .../sketcher/components/ConstraintEditor.jsx | 46 ++++-- .../components/ConstraintExplorer.jsx | 57 ++++--- .../components/ContextualControls.jsx | 26 +++- .../components/ContextualControls.less | 3 + web/app/sketcher/constr/ANConstraints.js | 4 +- web/app/sketcher/constr/AlgNumSystem.js | 14 +- web/app/sketcher/parametric.js | 23 ++- web/app/sketcher/shapes/arc.js | 4 +- web/app/sketcher/shapes/circle.js | 4 +- web/app/sketcher/shapes/segment.js | 2 +- web/app/sketcher/shapes/sketch-object.js | 32 ++-- web/app/sketcher/styles.js | 10 +- web/app/sketcher/tools/fillet.js | 3 +- web/app/sketcher/viewer2d.js | 140 ++++++++++++------ web/css/app.less | 10 +- web/index.html | 2 + web/sketcher.html | 4 +- webpack.config.js | 37 +++-- 39 files changed, 409 insertions(+), 243 deletions(-) delete mode 100644 modules/ui/components/controls/Button.less delete mode 100644 modules/ui/components/controls/InputControl.less delete mode 100644 modules/ui/components/controls/Label.less create mode 100644 modules/ui/styles/init/form.less create mode 100644 modules/ui/styles/init/index.less create mode 100644 modules/ui/styles/init/links.less rename modules/ui/styles/init/{minireset.css => minireset.less} (100%) rename modules/ui/styles/{table.less => init/tables.less} (89%) diff --git a/modules/ui/WindowSystem.jsx b/modules/ui/WindowSystem.jsx index 9c92c0f6..fcb0d90f 100644 --- a/modules/ui/WindowSystem.jsx +++ b/modules/ui/WindowSystem.jsx @@ -1,19 +1,15 @@ import React from 'react'; import PropTypes from 'prop-types'; +import {NOOP} from "../gems/func"; +//TODO: remove it export default class WindowSystem extends React.Component { constructor() { super(); - this.moveHandler = null; } componentDidMount() { - document.body.onmousemove = e => { - if (this.moveHandler !== null) { - this.moveHandler(e); - } - }; } componentWillUnMount() { @@ -24,7 +20,7 @@ export default class WindowSystem extends React.Component { } childContext = { - setWindowMoveHandler: moveHandler => this.moveHandler = moveHandler + setWindowMoveHandler: NOOP }; getChildContext() { diff --git a/modules/ui/components/Window.jsx b/modules/ui/components/Window.jsx index 4e6234c7..c72f0253 100644 --- a/modules/ui/components/Window.jsx +++ b/modules/ui/components/Window.jsx @@ -5,16 +5,16 @@ import Fa from "./Fa"; import WindowSystem from '../WindowSystem'; import cx from 'classnames'; - export default class Window extends React.Component { - constructor({initWidth, initLeft, initTop, initHeight}) { + constructor({initWidth, initLeft, initTop, initRight, initHeight}) { super(); this.state = { width: initWidth, height: initHeight, left: initLeft, - top: initTop + top: initTop, + right: initRight }; this.dragOrigin = null; } @@ -56,11 +56,22 @@ export default class Window extends React.Component { startDrag = e => { this.dragOrigin = {x : e.pageX, y : e.pageY}; + let left = this.state.left; + let top = this.state.top; + if (left === undefined) { + left = this.el.offsetLeft; + } + if (top === undefined) { + top = this.el.offsetTop; + } this.originLocation = { - left: this.state.left, - top: this.state.top + left, + top, + right: undefined }; - this.context.setWindowMoveHandler(this.doDrag); + + this.handlerToRestore = document.body.onmousemove; + document.body.onmousemove = this.doDrag; }; doDrag = e => { @@ -73,13 +84,11 @@ export default class Window extends React.Component { stopDrag = e => { this.dragOrigin = null; - this.context.setWindowMoveHandler(null); + document.body.onmousemove = this.handlerToRestore; }; keepRef = el => this.el = el; - static contextTypes = WindowSystem.childContextTypes; - } Window.defaultProps = { diff --git a/modules/ui/components/controls/Button.jsx b/modules/ui/components/controls/Button.jsx index 2bcec788..813ee749 100644 --- a/modules/ui/components/controls/Button.jsx +++ b/modules/ui/components/controls/Button.jsx @@ -1,11 +1,10 @@ import React from 'react'; -import ls from './Button.less' import cx from 'classnames'; export default function Button({type, onClick, className, children}) { - return + return } diff --git a/modules/ui/components/controls/Button.less b/modules/ui/components/controls/Button.less deleted file mode 100644 index 8fab0f2b..00000000 --- a/modules/ui/components/controls/Button.less +++ /dev/null @@ -1,30 +0,0 @@ -@import '../../styles/theme.less'; -@import '../../styles/mixins.less'; - -.button { - line-height: 1.5; -} - -.neutral, .accent, .danger { - background-color: darken(@color-neutral, 10%); -} - -.neutral { - .button-behavior(@color-neutral) -} - -.accent { - .button-behavior(@color-accent) -} - -.highlihgt { - .button-behavior(@color-highlight) -} - -.danger { - .button-behavior(@color-danger) -} - -.minor { - .button-behavior(@color-neutral) -} diff --git a/modules/ui/components/controls/ButtonGroup.jsx b/modules/ui/components/controls/ButtonGroup.jsx index c0d4adff..7b3e257a 100644 --- a/modules/ui/components/controls/ButtonGroup.jsx +++ b/modules/ui/components/controls/ButtonGroup.jsx @@ -2,8 +2,8 @@ import React from 'react'; import ls from './ButtonGroup.less' -export default function ButtonGroup({children}) { +export default function ButtonGroup(props) { - return
{children}
+ return
; } diff --git a/modules/ui/components/controls/ButtonGroup.less b/modules/ui/components/controls/ButtonGroup.less index b09c6edb..b26a1bb7 100644 --- a/modules/ui/components/controls/ButtonGroup.less +++ b/modules/ui/components/controls/ButtonGroup.less @@ -1,7 +1,7 @@ .root { display: flex; justify-content: flex-end; - & > * { + & > *:not(:first-child) { margin-left: 5px; } } diff --git a/modules/ui/components/controls/InputControl.jsx b/modules/ui/components/controls/InputControl.jsx index 499e1d8d..cca42838 100644 --- a/modules/ui/components/controls/InputControl.jsx +++ b/modules/ui/components/controls/InputControl.jsx @@ -1,14 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import ls from './InputControl.less' - export default class InputControl extends React.Component { render() { let {type, inputRef, ...props} = this.props; - return
+ return
; } diff --git a/modules/ui/components/controls/InputControl.less b/modules/ui/components/controls/InputControl.less deleted file mode 100644 index cf870dd5..00000000 --- a/modules/ui/components/controls/InputControl.less +++ /dev/null @@ -1,20 +0,0 @@ -@import '../../styles/theme.less'; - -.number input { - .colorStyling(@control-color-number, @control-bg); -} - -.text input { - .colorStyling(@control-color-text, @control-bg); -} - -.colorStyling(@color, @bg) { - color: @color; - background: @bg; - outline: none; - border: @bg 1px solid; - padding: 2px; - &:focus { - border: #444 1px solid; - } -} \ No newline at end of file diff --git a/modules/ui/components/controls/Label.jsx b/modules/ui/components/controls/Label.jsx index cbd2594f..d44abc23 100644 --- a/modules/ui/components/controls/Label.jsx +++ b/modules/ui/components/controls/Label.jsx @@ -1,7 +1,4 @@ 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 deleted file mode 100644 index e69de29b..00000000 diff --git a/modules/ui/styles/common.less b/modules/ui/styles/common.less index 32d9a287..3d918690 100644 --- a/modules/ui/styles/common.less +++ b/modules/ui/styles/common.less @@ -1,5 +1,4 @@ @import "./theme.less"; -@import "./table.less"; *.autoMarginLeft { margin-left: auto !important; @@ -25,14 +24,6 @@ display: inline-block; } -a { - color: @font-color; - text-decoration: underline; - &:hover { - color: @color-text-highlight - } -} - .scrollable { overflow: auto; } \ No newline at end of file diff --git a/modules/ui/styles/init/form.less b/modules/ui/styles/init/form.less new file mode 100644 index 00000000..6ffd8692 --- /dev/null +++ b/modules/ui/styles/init/form.less @@ -0,0 +1,64 @@ +button, input, select, textarea { + font-family: inherit; + font-size: 100%; +} + +button { + padding: 4px 8px; + border: 0; + background-color: darken(@color-neutral, 10%); + color: @font-color; + white-space: nowrap; + .button-behavior(@color-neutral) + //line-height: 1.5; +} + +button.neutral { + .button-behavior(@color-neutral) +} + +button.accent { + .button-behavior(@color-accent) +} + +button.highlight { + .button-behavior(@color-highlight) +} + +button.danger { + .button-behavior(@color-danger) +} + +button.minor { + .button-behavior(@color-neutral) +} + +//-------------- + +input, textarea { + outline: none; + background: @control-bg; + border: @control-bg 1px solid; + padding: 2px; + color: @control-color-text; + &::selection { + color: white; + background: blue; + + } + &:focus { + border: #326da3 1px solid; + background: #173851; + } +} + +input.number { + border-color: @control-color-number; + color: @control-color-number; +} + +input.text { + border-color: @control-color-text; + color: @control-color-text; +} + diff --git a/modules/ui/styles/init/index.less b/modules/ui/styles/init/index.less new file mode 100644 index 00000000..a9f06e32 --- /dev/null +++ b/modules/ui/styles/init/index.less @@ -0,0 +1,7 @@ +@import "./minireset.less"; +@import "../theme.less"; +@import "../mixins.less"; +@import "./main.less"; +@import "./links.less"; +@import "./form.less"; +@import "./tables.less"; diff --git a/modules/ui/styles/init/links.less b/modules/ui/styles/init/links.less new file mode 100644 index 00000000..2dc807a2 --- /dev/null +++ b/modules/ui/styles/init/links.less @@ -0,0 +1,7 @@ +a { + color: @font-color; + text-decoration: underline; + &:hover { + color: @color-text-highlight + } +} \ No newline at end of file diff --git a/modules/ui/styles/init/main.less b/modules/ui/styles/init/main.less index 72fcca75..34b9291f 100644 --- a/modules/ui/styles/init/main.less +++ b/modules/ui/styles/init/main.less @@ -1,39 +1,25 @@ -@import "../theme.less"; -@import "../mixins.less"; - -@fontSize: 11px; - html, pre { - font: @fontSize 'Lucida Grande', sans-serif; + font: @font-size 'Lucida Grande', sans-serif; } body { background-color: @bg-color; color: @font-color; + font-family: 'Roboto', 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif; } iframe { border: 0; } -:global(.disable-selection) { +.disable-selection { .no-selection(); } -:global(.compact-font) { - font-family: 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif; -} - -button { - border: 0; - background-color: inherit; - color: inherit; +.compact-font, .condensed { + font-family: 'Roboto Condensed', 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif; } pre { line-height: 1.5; -} - -table { - font-size: @fontSize; } \ No newline at end of file diff --git a/modules/ui/styles/init/minireset.css b/modules/ui/styles/init/minireset.less similarity index 100% rename from modules/ui/styles/init/minireset.css rename to modules/ui/styles/init/minireset.less diff --git a/modules/ui/styles/table.less b/modules/ui/styles/init/tables.less similarity index 89% rename from modules/ui/styles/table.less rename to modules/ui/styles/init/tables.less index 5693501a..e2de3f88 100644 --- a/modules/ui/styles/table.less +++ b/modules/ui/styles/init/tables.less @@ -1,6 +1,8 @@ -@import "./theme.less"; +table { + font-size: @font-size; +} -.stripedTable { +table.striped { & tr:nth-child(even), & th { background-color: @bg-color } @@ -12,7 +14,7 @@ } } -.delineatedTable { +table.delineated { border-collapse: collapse; border-spacing: 0; & td, & th { diff --git a/modules/ui/styles/theme.less b/modules/ui/styles/theme.less index 77e47c97..17e70c59 100644 --- a/modules/ui/styles/theme.less +++ b/modules/ui/styles/theme.less @@ -26,5 +26,7 @@ @color-text-highlight: #9cdaf7; +@font-size: 11px; + //@work-area-toolbar-bg-color: ; //@work-area-toolbar-font-color: ; diff --git a/web/app/cad/dom/components/WebApplication.jsx b/web/app/cad/dom/components/WebApplication.jsx index 852e90ad..a57a796b 100644 --- a/web/app/cad/dom/components/WebApplication.jsx +++ b/web/app/cad/dom/components/WebApplication.jsx @@ -1,8 +1,7 @@ import React, {Fragment} from 'react'; import PropTypes from 'prop-types'; -import 'ui/styles/init/minireset.css'; -import 'ui/styles/init/main.less'; +import 'ui/styles/init/index.less'; import AppTabs from "./AppTabs"; diff --git a/web/app/cad/expressions/Expressions.jsx b/web/app/cad/expressions/Expressions.jsx index fc85bd62..1afe7bbf 100644 --- a/web/app/cad/expressions/Expressions.jsx +++ b/web/app/cad/expressions/Expressions.jsx @@ -74,7 +74,7 @@ const Script = bind(streams => streams.expressions.script)( const VarTable = bind(streams => streams.expressions.list)( function VarTable({value}) { - return + return
diff --git a/web/app/sketcher.js b/web/app/sketcher.js index 9328a417..d4fa9cc2 100644 --- a/web/app/sketcher.js +++ b/web/app/sketcher.js @@ -5,6 +5,8 @@ import * as toolkit from './ui/toolkit'; import {Constraints} from './sketcher/parametric' import './utils/jqueryfy' import '../css/app.less' +import 'ui/styles/init/index.less'; + import ReactDOM from "react-dom"; import {SketcherApp} from "./sketcher/components/SketcherApp"; import React from "react"; diff --git a/web/app/sketcher/components/ConstraintEditor.jsx b/web/app/sketcher/components/ConstraintEditor.jsx index 008b8923..6fbaf5c6 100644 --- a/web/app/sketcher/components/ConstraintEditor.jsx +++ b/web/app/sketcher/components/ConstraintEditor.jsx @@ -1,11 +1,14 @@ -import React, {useEffect, useState} from 'react'; -import Widget from "ui/components/Widget"; +import React, {useCallback, useContext, useEffect, useState} from 'react'; import NumberControl from "ui/components/controls/NumberControl"; import Stack from "ui/components/Stack"; import ButtonGroup from "ui/components/controls/ButtonGroup"; import Button from "ui/components/controls/Button"; -import {useStream} from "../../../../modules/ui/effects"; -import CheckboxControl from "../../../../modules/ui/components/controls/CheckboxControl"; +import {useStream} from "ui/effects"; +import CheckboxControl from "ui/components/controls/CheckboxControl"; +import Window from "ui/components/Window"; +import Field from "ui/components/controls/Field"; +import Label from "../../../../modules/ui/components/controls/Label"; +import {SketcherAppContext} from "./SketcherApp"; export function ConstraintEditor() { @@ -13,7 +16,17 @@ export function ConstraintEditor() { const [values, setValues] = useState(null); - useEffect(() => setValues(req && {...req.constraint.constants}), [req]); + useEffect(() => { + setValues(req && {...req.constraint.constants}) + return () => { + if (req) { + viewer.unHighlight(req.constraint.objects); + viewer.refresh(); + } + } + }, [req]); + + const {viewer} = useContext(SketcherAppContext); const setValue = (name, value) => { setValues({...value, [name]: value}); @@ -25,6 +38,17 @@ export function ConstraintEditor() { const {constraint, onCancel, onApply} = req; + const highlight = () => { + viewer.highlight(constraint.objects, true); + viewer.refresh(); + }; + + const unHighlight = () => { + viewer.unHighlightAll(); + viewer.refresh(); + }; + + const apply = () => { Object.keys(constraint.schema.constants).map(name => { const val = values[name]; @@ -35,12 +59,14 @@ export function ConstraintEditor() { onApply(); }; - return + return - {Object.keys(constraint.schema.constants).sort().map(name =>
- + {Object.keys(constraint.schema.constants).sort().map(name => + { (() => { const def = constraint.schema.constants[name]; @@ -57,7 +83,7 @@ export function ConstraintEditor() { } -
)} + )} @@ -67,7 +93,7 @@ export function ConstraintEditor() {
-
; + ; } diff --git a/web/app/sketcher/components/ConstraintExplorer.jsx b/web/app/sketcher/components/ConstraintExplorer.jsx index 162b3468..41c6e166 100644 --- a/web/app/sketcher/components/ConstraintExplorer.jsx +++ b/web/app/sketcher/components/ConstraintExplorer.jsx @@ -1,11 +1,10 @@ -import React, {useContext} from 'react'; +import React, {useContext, useEffect} from 'react'; import ls from './ConstraintExplorer.less'; import Fa from 'ui/components/Fa'; -import {useStream} from "../../../../modules/ui/effects"; +import {useStream} from "ui/effects"; import {SketcherAppContext} from "./SketcherApp"; import cx from 'classnames'; import {editConstraint} from "./ConstraintEditor"; -import {NOOP} from "../../../../modules/gems/func"; export function ConstraintExplorer(props) { @@ -21,11 +20,25 @@ export function ConstraintList() { const constraints = useStream(ctx => ctx.viewer.parametricManager.$constraints); + let i = 0; + return constraints.map((c) => { + if (c.internal) { + return null; + } + i ++; + return + }) +} + +export function ConstraintButton({prefix='', constraint: c, ...props}) { + const {viewer, ui} = useContext(SketcherAppContext); const edit = (constraint) => { if (constraint.editable) { - editConstraint(ui.$constraintEditRequest, constraint, NOOP); + editConstraint(ui.$constraintEditRequest, constraint, () => { + viewer.parametricManager.reSolve(); + }); } }; @@ -35,33 +48,31 @@ export function ConstraintList() { }; const highlight = constr => { - viewer.select(constr.objects, true); + viewer.capture('highlight', constr.objects, true); viewer.refresh(); }; - const withdraw = constr => { - viewer.deselectAll(); + const withdraw = () => { + viewer.withdrawAll('highlight'); viewer.refresh(); }; + useEffect(() => withdraw, [c]); - return constraints.map((c, i) => { - if (c.internal) { - return null; - } - const conflicting = viewer.parametricManager.algNumSystem.conflicting.has(c); - const redundant = viewer.parametricManager.algNumSystem.redundant.has(c); + const conflicting = viewer.parametricManager.algNumSystem.conflicting.has(c); + const redundant = viewer.parametricManager.algNumSystem.redundant.has(c); - return
c.schema.constants && edit(c)} - onMouseEnter={() => highlight(c)} - onMouseLeave={() => withdraw(c)}> - - - {i}. {c.schema.name} + return
c.schema.constants && edit(c)} + onMouseEnter={() => highlight(c)} + onMouseLeave={() => withdraw(c)} + {...props}> + + + {prefix} {c.schema.name} - remove(c)}> + remove(c)}> + +
-
- }) } \ No newline at end of file diff --git a/web/app/sketcher/components/ContextualControls.jsx b/web/app/sketcher/components/ContextualControls.jsx index 0a4bcdf0..3f948150 100644 --- a/web/app/sketcher/components/ContextualControls.jsx +++ b/web/app/sketcher/components/ContextualControls.jsx @@ -4,10 +4,12 @@ import {matchAvailableActions} from "../actions"; import {useStream} from "../../../../modules/ui/effects"; import {SketcherAppContext} from "./SketcherApp"; import {MatchIndex, matchSelection} from "../selectionMatcher"; +import {ConstraintButton} from "./ConstraintExplorer"; export function ContextualControls() { const selection = useStream(ctx => ctx.viewer.streams.selection); + const ___ = useStream(ctx => ctx.viewer.parametricManager.$constraints); const ctx = useContext(SketcherAppContext); @@ -15,8 +17,12 @@ export function ContextualControls() { return null; } + const obj = selection.length === 1 ? selection[0] : null; + const availableActions = matchAvailableActions(selection); + const nonInternalConstraints = obj && Array.from(obj.constraints).filter(c => !c.internal); + return
{ @@ -25,9 +31,25 @@ export function ContextualControls() {
AVAILABLE ACTIONS:
+
+ { + availableActions.map(a => ) + } +
{ - availableActions.map(a => ) + nonInternalConstraints && nonInternalConstraints.length !== 0 && <> +
PARTICIPATES IN CONSTRAINTS:
+ {nonInternalConstraints.map(c => )} + }
; diff --git a/web/app/sketcher/components/ContextualControls.less b/web/app/sketcher/components/ContextualControls.less index 37ae7ee7..30d21ac3 100644 --- a/web/app/sketcher/components/ContextualControls.less +++ b/web/app/sketcher/components/ContextualControls.less @@ -1,5 +1,8 @@ .root { + position: absolute; + top: 0; + right: 0; margin: 5px; padding: 3px 5px; background-color: #000D; diff --git a/web/app/sketcher/constr/ANConstraints.js b/web/app/sketcher/constr/ANConstraints.js index 85f502fe..f711ad17 100644 --- a/web/app/sketcher/constr/ANConstraints.js +++ b/web/app/sketcher/constr/ANConstraints.js @@ -286,7 +286,7 @@ export const ConstraintDefinitions = { angle: { type: 'number', description: 'line angle', - internal: true, + readOnly: true, initialValue: ([segment1, segment2]) => { const a1 = segment1.params.ang.get(); const a2 = segment2.params.ang.get(); @@ -315,7 +315,7 @@ export const ConstraintDefinitions = { angle: { type: 'number', description: 'line angle', - internal: true, + readOnly: true, initialValue: ([segment1, segment2]) => { const a1 = segment1.params.ang.get(); const a2 = segment2.params.ang.get(); diff --git a/web/app/sketcher/constr/AlgNumSystem.js b/web/app/sketcher/constr/AlgNumSystem.js index 12d37db1..95a5ba1b 100644 --- a/web/app/sketcher/constr/AlgNumSystem.js +++ b/web/app/sketcher/constr/AlgNumSystem.js @@ -3,7 +3,7 @@ import {eqEps} from "../../brep/geom/tolerance"; import {Polynomial, POW_1_FN} from "./polynomial"; import {compositeFn} from "gems/func"; -const DEBUG = true; +const DEBUG = false; export class AlgNumSubSystem { @@ -32,7 +32,11 @@ export class AlgNumSubSystem { inTransaction = false; - constructor() { + visualLimit = 100; + + constructor(calcVisualLimit) { + + this.calcVisualLimit = calcVisualLimit; this.solveStatus = { error: 0, @@ -274,6 +278,8 @@ export class AlgNumSubSystem { iso.beingSolvedParams.forEach(solverParam => this.paramToIsolation.set(solverParam.objectParam, iso)) }); + this.visualLimit = this.calcVisualLimit(); + if (DEBUG) { console.log('solving system:'); this.polynomialIsolations.forEach((iso, i) => { @@ -490,8 +496,8 @@ class Isolation { let val = solverParam.objectParam.get(); if (this.system.controlBounds) { - if (solverParam.objectParam.min && val < solverParam.objectParam.min) { - val = solverParam.objectParam.min; + if (solverParam.objectParam.enforceVisualLimit && val < this.system.visualLimit) { + val = this.system.visualLimit; } } solverParam.set(val); diff --git a/web/app/sketcher/parametric.js b/web/app/sketcher/parametric.js index 71c1df29..b43492fc 100644 --- a/web/app/sketcher/parametric.js +++ b/web/app/sketcher/parametric.js @@ -1,14 +1,13 @@ -import {askNumber} from '../utils/utils'; import {Constraints} from './constraints'; import {AlgNumConstraint, ConstraintDefinitions} from "./constr/ANConstraints"; import {AlgNumSubSystem} from "./constr/AlgNumSystem"; -import {state, stream} from "../../../modules/lstream"; +import {stream} from "../../../modules/lstream"; export {Constraints, ParametricManager} class ParametricManager { - algNumSystem = new AlgNumSubSystem(); + algNumSystem = null;; constantTable = {}; externalConstantResolver = null; @@ -26,7 +25,7 @@ class ParametricManager { this.viewer.params.subscribe('constantDefinition', 'parametricManager', this.onConstantsExternalChange, this)(); this.constantResolver = this.createConstantResolver(); this.messageSink = msg => alert(msg); - + this.reset(); } get allConstraints() { @@ -34,7 +33,16 @@ class ParametricManager { } reset() { - this.algNumSystem = new AlgNumSubSystem(); + const pt = {x:0,y:0}; + const limit = 30; //px + this.algNumSystem = new AlgNumSubSystem(() => { + //100 px limit + this.viewer.screenToModel2(0, 0, pt); + const x1 = pt.x; + this.viewer.screenToModel2(limit, 0, pt); + const x2 = pt.x; + return Math.abs(x2 - x1); + }); } addAlgNum(constr) { @@ -161,6 +169,11 @@ class ParametricManager { this.algNumSystem.solve(rough); } + reSolve() { + this.prepare(); + this.solve(false); + } + addModifier(modifier) { this.algNumSystem.addModifier(modifier); this.refresh(); diff --git a/web/app/sketcher/shapes/arc.js b/web/app/sketcher/shapes/arc.js index 79a55da4..8d90d6ad 100644 --- a/web/app/sketcher/shapes/arc.js +++ b/web/app/sketcher/shapes/arc.js @@ -19,9 +19,9 @@ export class Arc extends SketchObject { c.parent = this; this.children.push(a, b, c); - this.r = new Param(MIN_RADIUS + 0.001, 'R'); + this.r = new Param(0, 'R'); this.r.constraints = [greaterThanConstraint(MIN_RADIUS)]; - this.r.min = MIN_RADIUS; + this.r.enforceVisualLimit = true; this.ang1 = new Param(0, 'A'); this.ang2 = new Param(0, 'A'); diff --git a/web/app/sketcher/shapes/circle.js b/web/app/sketcher/shapes/circle.js index e4b23a4d..0f2ab71c 100644 --- a/web/app/sketcher/shapes/circle.js +++ b/web/app/sketcher/shapes/circle.js @@ -12,9 +12,9 @@ export class Circle extends SketchObject { this.c = c; c.parent = this; this.children.push(c); - this.r = new Param(MIN_RADIUS + 0.001, 'R'); + this.r = new Param(0, 'R'); this.r.constraints = [greaterThanConstraint(MIN_RADIUS)]; - this.r.min = MIN_RADIUS; + this.r.enforceVisualLimit = true; } visitParams(callback) { diff --git a/web/app/sketcher/shapes/segment.js b/web/app/sketcher/shapes/segment.js index c9551dfd..1c4073b2 100644 --- a/web/app/sketcher/shapes/segment.js +++ b/web/app/sketcher/shapes/segment.js @@ -21,7 +21,7 @@ export class Segment extends SketchObject { t: new Param(undefined, 'T') }; this.params.ang.normalizer = makeAngle0_360; - this.params.t.min = 100; + this.params.t.enforceVisualLimit = true; this.syncGeometry(); } diff --git a/web/app/sketcher/shapes/sketch-object.js b/web/app/sketcher/shapes/sketch-object.js index 359556b6..2a8146f8 100644 --- a/web/app/sketcher/shapes/sketch-object.js +++ b/web/app/sketcher/shapes/sketch-object.js @@ -9,12 +9,12 @@ export class SketchObject extends Shape { constructor() { super(); this.id = Generator.genID(); - this.marked = null; + this.markers = []; this.children = []; this.layer = null; - this.fullyConstrained = false; this.constraints = new Set(); this.readOnly = false; + this.fullyConstrained = false; } normalDistance(aim, scale) { @@ -68,19 +68,33 @@ export class SketchObject extends Shape { return true; }); } - + + addMarker(style) { + this.markers.push(style); + this.markers.sort((a, b) => (a.priority||99999) - (b.priority||99999)) + } + + removeMarker(style) { + const index = this.markers.indexOf(style); + if (index !== -1) { + this.markers.splice(index, 1); + } + } + + get marked() { + return this.markers.length !== 0; + } + draw(ctx, scale, viewer) { if (!this.visible) return; - if (this.marked != null) { + const customStyle = this.markers.length !== 0 ? this.markers[0] : (this.fullyConstrained ? Styles.FULLY_CONSTRAINED : null); + if (customStyle !== null) { ctx.save(); - viewer.setStyle(this.marked, ctx); - } else if (this.fullyConstrained) { - ctx.save(); - viewer.setStyle(Styles.FULLY_CONSTRAINED, ctx); + viewer.setStyle(customStyle, ctx); } this.drawImpl(ctx, scale, viewer); - if (this.marked != null || this.fullyConstrained) ctx.restore(); + if (customStyle !== null) ctx.restore(); } copy() { diff --git a/web/app/sketcher/styles.js b/web/app/sketcher/styles.js index 7026050e..f36f694d 100644 --- a/web/app/sketcher/styles.js +++ b/web/app/sketcher/styles.js @@ -15,13 +15,19 @@ export const Styles = { fillStyle : "#FF0000" }, - MARK : { + SELECTION : { lineWidth : 2, strokeStyle : "#ff0000", fillStyle : "#FF0000" }, - SNAP : { + HIGHLIGHT : { + lineWidth : 2, + strokeStyle : "#f1ff3a", + fillStyle : "#f1ff3a" + }, + + TOOL_HELPER : { lineWidth : 2, strokeStyle : "#00FF00", fillStyle : "#00FF00" diff --git a/web/app/sketcher/tools/fillet.js b/web/app/sketcher/tools/fillet.js index f36e6a4c..fe88686b 100644 --- a/web/app/sketcher/tools/fillet.js +++ b/web/app/sketcher/tools/fillet.js @@ -105,7 +105,6 @@ export class FilletTool extends Tool { const point2 = candi[1]; this.breakLinkAndMakeFillet(point1, point2) } - breakLinkAndMakeFillet(point1, point2) { const pm = this.viewer.parametricManager; let coi = null; @@ -168,7 +167,7 @@ export class FilletTool extends Tool { } var candi = this.getCandidate(e); if (candi != null) { - this.viewer.mark(candi[0], Styles.SNAP); + this.lastCandidate.addStyle(Styles.SNAP); needRefresh = true; } if (needRefresh) { diff --git a/web/app/sketcher/viewer2d.js b/web/app/sketcher/viewer2d.js index d07b10d7..fbd208bd 100644 --- a/web/app/sketcher/viewer2d.js +++ b/web/app/sketcher/viewer2d.js @@ -15,7 +15,6 @@ import * as draw_utils from './shapes/draw-utils'; import {Matrix3} from '../math/l3space'; import sketcherStreams from './sketcherStreams'; - class Viewer { constructor(canvas, IO) { @@ -80,8 +79,10 @@ class Viewer { this.translate = {x: 0.0, y: 0.0}; this.scale = 1.0; - this.selected = []; - this.snapped = null; + this.captured = { + }; + Object.keys(CAPTURES).forEach(key => this.captured[key] = []); + this.historyManager = new HistoryManager(this); this.transformation = null; @@ -89,6 +90,14 @@ class Viewer { this.refresh(); } + get selected() { + return this.captured.selection; + } + + get snapped() { + return this.captured.tool[0] || null; + } + dispose() { window.removeEventListener('resize', this.onWindowResize, false); this.canvas = null; @@ -280,19 +289,15 @@ class Viewer { snap(x, y, excl) { this.cleanSnap(); - var snapTo = this.search(x, y, 20 / this.scale, true, true, excl); + const snapTo = this.search(x, y, 20 / this.scale, true, true, excl); if (snapTo.length > 0) { - this.snapped = snapTo[0]; - this.mark(this.snapped, Styles.SNAP); + this.capture('tool', [snapTo[0]], true); } return this.snapped; }; cleanSnap() { - if (this.snapped != null) { - this.deselect(this.snapped); - this.snapped = null; - } + this.withdrawAll('tool') }; showBounds(x1, y1, x2, y2, offset) { @@ -328,7 +333,7 @@ class Viewer { }; _screenToModel(x, y) { - var out = {x: 0, y: 0}; + const out = {x: 0, y: 0}; this.screenToModel2(x, y, out); return out; }; @@ -365,29 +370,68 @@ class Viewer { }; select(objs, exclusive) { - if (exclusive) this.deselectAll(); - for (var i = 0; i < objs.length; i++) { - this.mark(objs[i]); + this.capture('selection', objs, exclusive); + this.streams.selection.next(this.selected); + } + + capture(type, objs, exclusive) { + if (exclusive) this.withdrawAll(type); + const captured = this.captured[type]; + for (let i = 0; i < objs.length; i++) { + const obj = objs[i]; + if (captured.indexOf(obj) === -1) { + captured.push(obj); + obj.addMarker(CAPTURES[type]); + } } }; + + withdraw(type, obj) { + let captured = this.captured[type]; + for (let i = 0; i < captured.length; i++) { + if (obj === captured[i]) { + captured.splice(i, 1)[0].removeMarker(CAPTURES[type]); + break; + } + } + }; + + withdrawAll(type) { + const captured = this.captured[type]; + for (let i = 0; i < captured.length; i++) { + captured[i].removeMarker(CAPTURES[type]); + } + while (captured.length > 0) captured.pop(); + }; + + deselect(obj) { + this.withdraw('selection', obj); + this.streams.selection.next(this.selected); + }; + + deselectAll() { + this.withdrawAll('selection'); + this.streams.selection.next(this.selected); + }; + + highlight(objs, exclusive) { + this.capture('highlight', objs, exclusive); + } + + unHighlightAll(objs) { + this.withdrawAll('highlight'); + } + + unHighlight(objs) { + this.withdrawAll('highlight', objs); + } + pick(e) { var m = this.screenToModel(e); return this.search(m.x, m.y, 20 / this.scale, true, false, []); }; - mark(obj, style) { - if (style === undefined) { - style = Styles.MARK; - } - obj.marked = style; - - if (this.selected.indexOf(obj) == -1) { - this.selected.push(obj); - this.streams.selection.next(this.selected); - } - }; - getActiveLayer() { var layer = this._activeLayer; if (layer == null || layer.readOnly) { @@ -423,24 +467,6 @@ class Viewer { } }; - deselect(obj) { - for (var i = 0; i < this.selected.length; i++) { - if (obj === this.selected[i]) { - this.selected.splice(i, 1)[0].marked = null; - break; - } - } - this.streams.selection.next(this.selected); - }; - - deselectAll() { - for (var i = 0; i < this.selected.length; i++) { - this.selected[i].marked = null; - } - while (this.selected.length > 0) this.selected.pop(); - this.streams.selection.next(this.selected); - }; - equalizeLinkedEndpoints() { const visited = new Set(); @@ -484,11 +510,11 @@ class Viewer { } static __SKETCH_DRAW_PIPELINE = [ - (obj) => !isEndPoint(obj) && obj.marked === null && isConstruction(obj), - (obj) => !isEndPoint(obj) && obj.marked === null && !isConstruction(obj), - (obj) => !isEndPoint(obj) && obj.marked !== null, - (obj) => isEndPoint(obj) && obj.marked === null, - (obj) => isEndPoint(obj) && obj.marked !== null + (obj) => !isEndPoint(obj) && !obj.marked && isConstruction(obj), + (obj) => !isEndPoint(obj) && !obj.marked && !isConstruction(obj), + (obj) => !isEndPoint(obj) && obj.marked, + (obj) => isEndPoint(obj) && !obj.marked, + (obj) => isEndPoint(obj) && obj.marked ]; static __SIMPLE_DRAW_PIPELINE = [ @@ -547,4 +573,20 @@ class Layer { } } +const CAPTURES = { + tool: { + ...Styles.TOOL_HELPER, + priority: 1 + }, + highlight: { + ...Styles.HIGHLIGHT, + priority: 2 + + }, + selection: { + ...Styles.SELECTION, + priority: 3 + }, +}; + export {Viewer, Styles} \ No newline at end of file diff --git a/web/css/app.less b/web/css/app.less index a4369bb8..fc1ed267 100644 --- a/web/css/app.less +++ b/web/css/app.less @@ -1,6 +1,4 @@ body { - .sans-serif; - font-size: 11px; overflow: hidden; } @@ -11,6 +9,10 @@ html, body { margin: 0; } +.sans-serif { + +} + .helvetica { font-family: 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif; } @@ -19,10 +21,6 @@ html, body { font-family: Monaco, monospace; } -.sans-serif { - font-family: sans-serif; -} - .logo { color: #bbb; font-size: 16px; diff --git a/web/index.html b/web/index.html index 40a35a91..ae50f2e2 100644 --- a/web/index.html +++ b/web/index.html @@ -1,6 +1,8 @@ Web CAD / Part Designer + + diff --git a/web/sketcher.html b/web/sketcher.html index a996d478..343b16e1 100644 --- a/web/sketcher.html +++ b/web/sketcher.html @@ -3,6 +3,8 @@ sketcher.js + + @@ -70,7 +72,7 @@
-
+
diff --git a/webpack.config.js b/webpack.config.js index 329491e7..f0c5f86f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -69,20 +69,33 @@ module.exports = { ] }, { - test: /\.(less|css)$/, - include: [MODULES, WEB_APP], - use: [ - 'style-loader', + oneOf: [ { - loader: 'css-loader', - options: { - getLocalIdent: (context, localIdentName, localName) => generateCSSScopedName(localName, context.resourcePath), - modules: true, - url: false - } + test: /\.(less|css)$/, + include: [path.resolve(MODULES, 'ui/styles/init')], + use: [ + 'style-loader', + 'css-loader', + 'less-loader' + ] }, - 'less-loader' - ] + { + test: /\.(less|css)$/, + include: [MODULES, WEB_APP], + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + getLocalIdent: (context, localIdentName, localName) => generateCSSScopedName(localName, context.resourcePath), + modules: true, + url: false + } + }, + 'less-loader' + ] + } + ], }, { test: /\.html$/,
Name