mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 16:33:15 +01:00
object highlight mode, reuse styles
This commit is contained in:
parent
279db19809
commit
547ec02b01
39 changed files with 409 additions and 243 deletions
|
|
@ -1,19 +1,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import {NOOP} from "../gems/func";
|
||||||
|
|
||||||
|
//TODO: remove it
|
||||||
export default class WindowSystem extends React.Component {
|
export default class WindowSystem extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.moveHandler = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
document.body.onmousemove = e => {
|
|
||||||
if (this.moveHandler !== null) {
|
|
||||||
this.moveHandler(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnMount() {
|
componentWillUnMount() {
|
||||||
|
|
@ -24,7 +20,7 @@ export default class WindowSystem extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
childContext = {
|
childContext = {
|
||||||
setWindowMoveHandler: moveHandler => this.moveHandler = moveHandler
|
setWindowMoveHandler: NOOP
|
||||||
};
|
};
|
||||||
|
|
||||||
getChildContext() {
|
getChildContext() {
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,16 @@ import Fa from "./Fa";
|
||||||
import WindowSystem from '../WindowSystem';
|
import WindowSystem from '../WindowSystem';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
|
||||||
|
|
||||||
export default class Window extends React.Component {
|
export default class Window extends React.Component {
|
||||||
|
|
||||||
constructor({initWidth, initLeft, initTop, initHeight}) {
|
constructor({initWidth, initLeft, initTop, initRight, initHeight}) {
|
||||||
super();
|
super();
|
||||||
this.state = {
|
this.state = {
|
||||||
width: initWidth,
|
width: initWidth,
|
||||||
height: initHeight,
|
height: initHeight,
|
||||||
left: initLeft,
|
left: initLeft,
|
||||||
top: initTop
|
top: initTop,
|
||||||
|
right: initRight
|
||||||
};
|
};
|
||||||
this.dragOrigin = null;
|
this.dragOrigin = null;
|
||||||
}
|
}
|
||||||
|
|
@ -56,11 +56,22 @@ export default class Window extends React.Component {
|
||||||
|
|
||||||
startDrag = e => {
|
startDrag = e => {
|
||||||
this.dragOrigin = {x : e.pageX, y : e.pageY};
|
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 = {
|
this.originLocation = {
|
||||||
left: this.state.left,
|
left,
|
||||||
top: this.state.top
|
top,
|
||||||
|
right: undefined
|
||||||
};
|
};
|
||||||
this.context.setWindowMoveHandler(this.doDrag);
|
|
||||||
|
this.handlerToRestore = document.body.onmousemove;
|
||||||
|
document.body.onmousemove = this.doDrag;
|
||||||
};
|
};
|
||||||
|
|
||||||
doDrag = e => {
|
doDrag = e => {
|
||||||
|
|
@ -73,13 +84,11 @@ export default class Window extends React.Component {
|
||||||
|
|
||||||
stopDrag = e => {
|
stopDrag = e => {
|
||||||
this.dragOrigin = null;
|
this.dragOrigin = null;
|
||||||
this.context.setWindowMoveHandler(null);
|
document.body.onmousemove = this.handlerToRestore;
|
||||||
};
|
};
|
||||||
|
|
||||||
keepRef = el => this.el = el;
|
keepRef = el => this.el = el;
|
||||||
|
|
||||||
static contextTypes = WindowSystem.childContextTypes;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Window.defaultProps = {
|
Window.defaultProps = {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import ls from './Button.less'
|
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
|
||||||
export default function Button({type, onClick, className, children}) {
|
export default function Button({type, onClick, className, children}) {
|
||||||
|
|
||||||
return <button onClick={onClick} className={cx(ls[type], ls.button, className)}>{children}</button>
|
return <button onClick={onClick} className={cx(type, className)}>{children}</button>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -2,8 +2,8 @@ import React from 'react';
|
||||||
|
|
||||||
import ls from './ButtonGroup.less'
|
import ls from './ButtonGroup.less'
|
||||||
|
|
||||||
export default function ButtonGroup({children}) {
|
export default function ButtonGroup(props) {
|
||||||
|
|
||||||
return <div className={ls.root}>{children}</div>
|
return <div className={ls.root} {...props}/>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
.root {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
& > * {
|
& > *:not(:first-child) {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import ls from './InputControl.less'
|
|
||||||
|
|
||||||
export default class InputControl extends React.Component {
|
export default class InputControl extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {type, inputRef, ...props} = this.props;
|
let {type, inputRef, ...props} = this.props;
|
||||||
|
|
||||||
return <div className={ls[type]}>
|
return <div className={type}>
|
||||||
<input type='text' ref={inputRef} {...props} spellCheck='false' />
|
<input type='text' ref={inputRef} {...props} spellCheck='false' />
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import ls from './Label.less'
|
|
||||||
|
|
||||||
export default function Label({children}) {
|
export default function Label({children}) {
|
||||||
return <span>{children}</span>
|
return <span>{children}</span>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
@import "./theme.less";
|
@import "./theme.less";
|
||||||
@import "./table.less";
|
|
||||||
|
|
||||||
*.autoMarginLeft {
|
*.autoMarginLeft {
|
||||||
margin-left: auto !important;
|
margin-left: auto !important;
|
||||||
|
|
@ -25,14 +24,6 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
color: @font-color;
|
|
||||||
text-decoration: underline;
|
|
||||||
&:hover {
|
|
||||||
color: @color-text-highlight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollable {
|
.scrollable {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
64
modules/ui/styles/init/form.less
Normal file
64
modules/ui/styles/init/form.less
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
7
modules/ui/styles/init/index.less
Normal file
7
modules/ui/styles/init/index.less
Normal file
|
|
@ -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";
|
||||||
7
modules/ui/styles/init/links.less
Normal file
7
modules/ui/styles/init/links.less
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
a {
|
||||||
|
color: @font-color;
|
||||||
|
text-decoration: underline;
|
||||||
|
&:hover {
|
||||||
|
color: @color-text-highlight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,39 +1,25 @@
|
||||||
@import "../theme.less";
|
|
||||||
@import "../mixins.less";
|
|
||||||
|
|
||||||
@fontSize: 11px;
|
|
||||||
|
|
||||||
html, pre {
|
html, pre {
|
||||||
font: @fontSize 'Lucida Grande', sans-serif;
|
font: @font-size 'Lucida Grande', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: @bg-color;
|
background-color: @bg-color;
|
||||||
color: @font-color;
|
color: @font-color;
|
||||||
|
font-family: 'Roboto', 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.disable-selection) {
|
.disable-selection {
|
||||||
.no-selection();
|
.no-selection();
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.compact-font) {
|
.compact-font, .condensed {
|
||||||
font-family: 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif;
|
font-family: 'Roboto Condensed', 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif;
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border: 0;
|
|
||||||
background-color: inherit;
|
|
||||||
color: inherit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
|
||||||
font-size: @fontSize;
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
@import "./theme.less";
|
table {
|
||||||
|
font-size: @font-size;
|
||||||
|
}
|
||||||
|
|
||||||
.stripedTable {
|
table.striped {
|
||||||
& tr:nth-child(even), & th {
|
& tr:nth-child(even), & th {
|
||||||
background-color: @bg-color
|
background-color: @bg-color
|
||||||
}
|
}
|
||||||
|
|
@ -12,7 +14,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.delineatedTable {
|
table.delineated {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
& td, & th {
|
& td, & th {
|
||||||
|
|
@ -26,5 +26,7 @@
|
||||||
|
|
||||||
@color-text-highlight: #9cdaf7;
|
@color-text-highlight: #9cdaf7;
|
||||||
|
|
||||||
|
@font-size: 11px;
|
||||||
|
|
||||||
//@work-area-toolbar-bg-color: ;
|
//@work-area-toolbar-bg-color: ;
|
||||||
//@work-area-toolbar-font-color: ;
|
//@work-area-toolbar-font-color: ;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import React, {Fragment} from 'react';
|
import React, {Fragment} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import 'ui/styles/init/minireset.css';
|
import 'ui/styles/init/index.less';
|
||||||
import 'ui/styles/init/main.less';
|
|
||||||
import AppTabs from "./AppTabs";
|
import AppTabs from "./AppTabs";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ const Script = bind(streams => streams.expressions.script)(
|
||||||
|
|
||||||
const VarTable = bind(streams => streams.expressions.list)(
|
const VarTable = bind(streams => streams.expressions.list)(
|
||||||
function VarTable({value}) {
|
function VarTable({value}) {
|
||||||
return <table className={cx(cmn.fullWidth, cmn.stripedTable, cmn.delineatedTable)}>
|
return <table className={cx(cmn.fullWidth, 'striped', 'delineated')}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import * as toolkit from './ui/toolkit';
|
||||||
import {Constraints} from './sketcher/parametric'
|
import {Constraints} from './sketcher/parametric'
|
||||||
import './utils/jqueryfy'
|
import './utils/jqueryfy'
|
||||||
import '../css/app.less'
|
import '../css/app.less'
|
||||||
|
import 'ui/styles/init/index.less';
|
||||||
|
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import {SketcherApp} from "./sketcher/components/SketcherApp";
|
import {SketcherApp} from "./sketcher/components/SketcherApp";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
import React, {useEffect, useState} from 'react';
|
import React, {useCallback, useContext, useEffect, useState} from 'react';
|
||||||
import Widget from "ui/components/Widget";
|
|
||||||
import NumberControl from "ui/components/controls/NumberControl";
|
import NumberControl from "ui/components/controls/NumberControl";
|
||||||
import Stack from "ui/components/Stack";
|
import Stack from "ui/components/Stack";
|
||||||
import ButtonGroup from "ui/components/controls/ButtonGroup";
|
import ButtonGroup from "ui/components/controls/ButtonGroup";
|
||||||
import Button from "ui/components/controls/Button";
|
import Button from "ui/components/controls/Button";
|
||||||
import {useStream} from "../../../../modules/ui/effects";
|
import {useStream} from "ui/effects";
|
||||||
import CheckboxControl from "../../../../modules/ui/components/controls/CheckboxControl";
|
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() {
|
export function ConstraintEditor() {
|
||||||
|
|
||||||
|
|
@ -13,7 +16,17 @@ export function ConstraintEditor() {
|
||||||
|
|
||||||
const [values, setValues] = useState(null);
|
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) => {
|
const setValue = (name, value) => {
|
||||||
setValues({...value, [name]: value});
|
setValues({...value, [name]: value});
|
||||||
|
|
@ -25,6 +38,17 @@ export function ConstraintEditor() {
|
||||||
|
|
||||||
const {constraint, onCancel, onApply} = req;
|
const {constraint, onCancel, onApply} = req;
|
||||||
|
|
||||||
|
const highlight = () => {
|
||||||
|
viewer.highlight(constraint.objects, true);
|
||||||
|
viewer.refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
const unHighlight = () => {
|
||||||
|
viewer.unHighlightAll();
|
||||||
|
viewer.refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const apply = () => {
|
const apply = () => {
|
||||||
Object.keys(constraint.schema.constants).map(name => {
|
Object.keys(constraint.schema.constants).map(name => {
|
||||||
const val = values[name];
|
const val = values[name];
|
||||||
|
|
@ -35,12 +59,14 @@ export function ConstraintEditor() {
|
||||||
onApply();
|
onApply();
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Widget>
|
return <Window initWidth={250} initLeft={5} initTop={5} title={constraint.schema.name} onClose={onCancel}
|
||||||
|
onMouseEnter={highlight}
|
||||||
|
onMouseLeave={unHighlight}>
|
||||||
|
|
||||||
<Stack>
|
<Stack>
|
||||||
|
|
||||||
{Object.keys(constraint.schema.constants).sort().map(name => <div key={name}>
|
{Object.keys(constraint.schema.constants).sort().map(name => <Field key={name}>
|
||||||
|
<Label>{name}</Label>
|
||||||
{
|
{
|
||||||
(() => {
|
(() => {
|
||||||
const def = constraint.schema.constants[name];
|
const def = constraint.schema.constants[name];
|
||||||
|
|
@ -57,7 +83,7 @@ export function ConstraintEditor() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>)}
|
</Field>)}
|
||||||
|
|
||||||
|
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
|
|
@ -67,7 +93,7 @@ export function ConstraintEditor() {
|
||||||
|
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
</Widget>;
|
</Window>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import React, {useContext} from 'react';
|
import React, {useContext, useEffect} from 'react';
|
||||||
import ls from './ConstraintExplorer.less';
|
import ls from './ConstraintExplorer.less';
|
||||||
import Fa from 'ui/components/Fa';
|
import Fa from 'ui/components/Fa';
|
||||||
import {useStream} from "../../../../modules/ui/effects";
|
import {useStream} from "ui/effects";
|
||||||
import {SketcherAppContext} from "./SketcherApp";
|
import {SketcherAppContext} from "./SketcherApp";
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import {editConstraint} from "./ConstraintEditor";
|
import {editConstraint} from "./ConstraintEditor";
|
||||||
import {NOOP} from "../../../../modules/gems/func";
|
|
||||||
|
|
||||||
|
|
||||||
export function ConstraintExplorer(props) {
|
export function ConstraintExplorer(props) {
|
||||||
|
|
@ -21,11 +20,25 @@ export function ConstraintList() {
|
||||||
|
|
||||||
const constraints = useStream(ctx => ctx.viewer.parametricManager.$constraints);
|
const constraints = useStream(ctx => ctx.viewer.parametricManager.$constraints);
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
return constraints.map((c) => {
|
||||||
|
if (c.internal) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
i ++;
|
||||||
|
return <ConstraintButton prefix={i+'.'} constraint={c} />
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ConstraintButton({prefix='', constraint: c, ...props}) {
|
||||||
|
|
||||||
const {viewer, ui} = useContext(SketcherAppContext);
|
const {viewer, ui} = useContext(SketcherAppContext);
|
||||||
|
|
||||||
const edit = (constraint) => {
|
const edit = (constraint) => {
|
||||||
if (constraint.editable) {
|
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 => {
|
const highlight = constr => {
|
||||||
viewer.select(constr.objects, true);
|
viewer.capture('highlight', constr.objects, true);
|
||||||
viewer.refresh();
|
viewer.refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
const withdraw = constr => {
|
const withdraw = () => {
|
||||||
viewer.deselectAll();
|
viewer.withdrawAll('highlight');
|
||||||
viewer.refresh();
|
viewer.refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => withdraw, [c]);
|
||||||
|
|
||||||
return constraints.map((c, i) => {
|
const conflicting = viewer.parametricManager.algNumSystem.conflicting.has(c);
|
||||||
if (c.internal) {
|
const redundant = viewer.parametricManager.algNumSystem.redundant.has(c);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const conflicting = viewer.parametricManager.algNumSystem.conflicting.has(c);
|
|
||||||
const redundant = viewer.parametricManager.algNumSystem.redundant.has(c);
|
|
||||||
|
|
||||||
return <div key={c.id} className={cx(ls.objectItem, conflicting&&ls.conflicting, redundant&&ls.redundant)}
|
return <div key={c.id} className={cx(ls.objectItem, conflicting&&ls.conflicting, redundant&&ls.redundant)}
|
||||||
onClick={() => c.schema.constants && edit(c)}
|
onClick={() => c.schema.constants && edit(c)}
|
||||||
onMouseEnter={() => highlight(c)}
|
onMouseEnter={() => highlight(c)}
|
||||||
onMouseLeave={() => withdraw(c)}>
|
onMouseLeave={() => withdraw(c)}
|
||||||
<span className={ls.objectIcon}><img width="15px" src='img/vec/pointOnArc.svg'/></span>
|
{...props}>
|
||||||
<span className={ls.objectTag}>
|
<span className={ls.objectIcon}><img width="15px" src='img/vec/pointOnArc.svg'/></span>
|
||||||
{i}. {c.schema.name}
|
<span className={ls.objectTag}>
|
||||||
|
{prefix} {c.schema.name}
|
||||||
</span>
|
</span>
|
||||||
<span className={ls.removeButton} onClick={() => remove(c)}><Fa icon='times'/></span>
|
<span className={ls.removeButton} onClick={() => remove(c)}><Fa icon='times'/></span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
@ -4,10 +4,12 @@ import {matchAvailableActions} from "../actions";
|
||||||
import {useStream} from "../../../../modules/ui/effects";
|
import {useStream} from "../../../../modules/ui/effects";
|
||||||
import {SketcherAppContext} from "./SketcherApp";
|
import {SketcherAppContext} from "./SketcherApp";
|
||||||
import {MatchIndex, matchSelection} from "../selectionMatcher";
|
import {MatchIndex, matchSelection} from "../selectionMatcher";
|
||||||
|
import {ConstraintButton} from "./ConstraintExplorer";
|
||||||
|
|
||||||
export function ContextualControls() {
|
export function ContextualControls() {
|
||||||
|
|
||||||
const selection = useStream(ctx => ctx.viewer.streams.selection);
|
const selection = useStream(ctx => ctx.viewer.streams.selection);
|
||||||
|
const ___ = useStream(ctx => ctx.viewer.parametricManager.$constraints);
|
||||||
|
|
||||||
const ctx = useContext(SketcherAppContext);
|
const ctx = useContext(SketcherAppContext);
|
||||||
|
|
||||||
|
|
@ -15,8 +17,12 @@ export function ContextualControls() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const obj = selection.length === 1 ? selection[0] : null;
|
||||||
|
|
||||||
const availableActions = matchAvailableActions(selection);
|
const availableActions = matchAvailableActions(selection);
|
||||||
|
|
||||||
|
const nonInternalConstraints = obj && Array.from(obj.constraints).filter(c => !c.internal);
|
||||||
|
|
||||||
return <div className={ls.root}>
|
return <div className={ls.root}>
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -25,9 +31,25 @@ export function ContextualControls() {
|
||||||
|
|
||||||
<div className={ls.hr}>AVAILABLE ACTIONS:</div>
|
<div className={ls.hr}>AVAILABLE ACTIONS:</div>
|
||||||
|
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
maxWidth: 200,
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
availableActions.map(a => <button
|
||||||
|
style={{
|
||||||
|
margin: 3
|
||||||
|
}}
|
||||||
|
onClick={() => a.invoke(ctx, matchSelection(a.selectionMatcher, new MatchIndex(selection), false))}
|
||||||
|
title={a.description}>{a.shortName}</button>)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
{
|
{
|
||||||
availableActions.map(a => <button onClick={() => a.invoke(ctx, matchSelection(a.selectionMatcher, new MatchIndex(selection), false))}
|
nonInternalConstraints && nonInternalConstraints.length !== 0 && <>
|
||||||
title={a.description}>{a.shortName}</button>)
|
<div className={ls.hr}>PARTICIPATES IN CONSTRAINTS:</div>
|
||||||
|
{nonInternalConstraints.map(c => <ConstraintButton constraint={c} key={c.id} style={{borderColor: 'white'}}/>)}
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
padding: 3px 5px;
|
padding: 3px 5px;
|
||||||
background-color: #000D;
|
background-color: #000D;
|
||||||
|
|
|
||||||
|
|
@ -286,7 +286,7 @@ export const ConstraintDefinitions = {
|
||||||
angle: {
|
angle: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: 'line angle',
|
description: 'line angle',
|
||||||
internal: true,
|
readOnly: true,
|
||||||
initialValue: ([segment1, segment2]) => {
|
initialValue: ([segment1, segment2]) => {
|
||||||
const a1 = segment1.params.ang.get();
|
const a1 = segment1.params.ang.get();
|
||||||
const a2 = segment2.params.ang.get();
|
const a2 = segment2.params.ang.get();
|
||||||
|
|
@ -315,7 +315,7 @@ export const ConstraintDefinitions = {
|
||||||
angle: {
|
angle: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: 'line angle',
|
description: 'line angle',
|
||||||
internal: true,
|
readOnly: true,
|
||||||
initialValue: ([segment1, segment2]) => {
|
initialValue: ([segment1, segment2]) => {
|
||||||
const a1 = segment1.params.ang.get();
|
const a1 = segment1.params.ang.get();
|
||||||
const a2 = segment2.params.ang.get();
|
const a2 = segment2.params.ang.get();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import {eqEps} from "../../brep/geom/tolerance";
|
||||||
import {Polynomial, POW_1_FN} from "./polynomial";
|
import {Polynomial, POW_1_FN} from "./polynomial";
|
||||||
import {compositeFn} from "gems/func";
|
import {compositeFn} from "gems/func";
|
||||||
|
|
||||||
const DEBUG = true;
|
const DEBUG = false;
|
||||||
|
|
||||||
export class AlgNumSubSystem {
|
export class AlgNumSubSystem {
|
||||||
|
|
||||||
|
|
@ -32,7 +32,11 @@ export class AlgNumSubSystem {
|
||||||
|
|
||||||
inTransaction = false;
|
inTransaction = false;
|
||||||
|
|
||||||
constructor() {
|
visualLimit = 100;
|
||||||
|
|
||||||
|
constructor(calcVisualLimit) {
|
||||||
|
|
||||||
|
this.calcVisualLimit = calcVisualLimit;
|
||||||
|
|
||||||
this.solveStatus = {
|
this.solveStatus = {
|
||||||
error: 0,
|
error: 0,
|
||||||
|
|
@ -274,6 +278,8 @@ export class AlgNumSubSystem {
|
||||||
iso.beingSolvedParams.forEach(solverParam => this.paramToIsolation.set(solverParam.objectParam, iso))
|
iso.beingSolvedParams.forEach(solverParam => this.paramToIsolation.set(solverParam.objectParam, iso))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.visualLimit = this.calcVisualLimit();
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
console.log('solving system:');
|
console.log('solving system:');
|
||||||
this.polynomialIsolations.forEach((iso, i) => {
|
this.polynomialIsolations.forEach((iso, i) => {
|
||||||
|
|
@ -490,8 +496,8 @@ class Isolation {
|
||||||
let val = solverParam.objectParam.get();
|
let val = solverParam.objectParam.get();
|
||||||
|
|
||||||
if (this.system.controlBounds) {
|
if (this.system.controlBounds) {
|
||||||
if (solverParam.objectParam.min && val < solverParam.objectParam.min) {
|
if (solverParam.objectParam.enforceVisualLimit && val < this.system.visualLimit) {
|
||||||
val = solverParam.objectParam.min;
|
val = this.system.visualLimit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
solverParam.set(val);
|
solverParam.set(val);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import {askNumber} from '../utils/utils';
|
|
||||||
import {Constraints} from './constraints';
|
import {Constraints} from './constraints';
|
||||||
import {AlgNumConstraint, ConstraintDefinitions} from "./constr/ANConstraints";
|
import {AlgNumConstraint, ConstraintDefinitions} from "./constr/ANConstraints";
|
||||||
import {AlgNumSubSystem} from "./constr/AlgNumSystem";
|
import {AlgNumSubSystem} from "./constr/AlgNumSystem";
|
||||||
import {state, stream} from "../../../modules/lstream";
|
import {stream} from "../../../modules/lstream";
|
||||||
|
|
||||||
export {Constraints, ParametricManager}
|
export {Constraints, ParametricManager}
|
||||||
|
|
||||||
class ParametricManager {
|
class ParametricManager {
|
||||||
|
|
||||||
algNumSystem = new AlgNumSubSystem();
|
algNumSystem = null;;
|
||||||
|
|
||||||
constantTable = {};
|
constantTable = {};
|
||||||
externalConstantResolver = null;
|
externalConstantResolver = null;
|
||||||
|
|
@ -26,7 +25,7 @@ class ParametricManager {
|
||||||
this.viewer.params.subscribe('constantDefinition', 'parametricManager', this.onConstantsExternalChange, this)();
|
this.viewer.params.subscribe('constantDefinition', 'parametricManager', this.onConstantsExternalChange, this)();
|
||||||
this.constantResolver = this.createConstantResolver();
|
this.constantResolver = this.createConstantResolver();
|
||||||
this.messageSink = msg => alert(msg);
|
this.messageSink = msg => alert(msg);
|
||||||
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
get allConstraints() {
|
get allConstraints() {
|
||||||
|
|
@ -34,7 +33,16 @@ class ParametricManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
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) {
|
addAlgNum(constr) {
|
||||||
|
|
@ -161,6 +169,11 @@ class ParametricManager {
|
||||||
this.algNumSystem.solve(rough);
|
this.algNumSystem.solve(rough);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reSolve() {
|
||||||
|
this.prepare();
|
||||||
|
this.solve(false);
|
||||||
|
}
|
||||||
|
|
||||||
addModifier(modifier) {
|
addModifier(modifier) {
|
||||||
this.algNumSystem.addModifier(modifier);
|
this.algNumSystem.addModifier(modifier);
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ export class Arc extends SketchObject {
|
||||||
c.parent = this;
|
c.parent = this;
|
||||||
this.children.push(a, b, c);
|
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.constraints = [greaterThanConstraint(MIN_RADIUS)];
|
||||||
this.r.min = MIN_RADIUS;
|
this.r.enforceVisualLimit = true;
|
||||||
|
|
||||||
this.ang1 = new Param(0, 'A');
|
this.ang1 = new Param(0, 'A');
|
||||||
this.ang2 = new Param(0, 'A');
|
this.ang2 = new Param(0, 'A');
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ export class Circle extends SketchObject {
|
||||||
this.c = c;
|
this.c = c;
|
||||||
c.parent = this;
|
c.parent = this;
|
||||||
this.children.push(c);
|
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.constraints = [greaterThanConstraint(MIN_RADIUS)];
|
||||||
this.r.min = MIN_RADIUS;
|
this.r.enforceVisualLimit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitParams(callback) {
|
visitParams(callback) {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export class Segment extends SketchObject {
|
||||||
t: new Param(undefined, 'T')
|
t: new Param(undefined, 'T')
|
||||||
};
|
};
|
||||||
this.params.ang.normalizer = makeAngle0_360;
|
this.params.ang.normalizer = makeAngle0_360;
|
||||||
this.params.t.min = 100;
|
this.params.t.enforceVisualLimit = true;
|
||||||
this.syncGeometry();
|
this.syncGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ export class SketchObject extends Shape {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.id = Generator.genID();
|
this.id = Generator.genID();
|
||||||
this.marked = null;
|
this.markers = [];
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.layer = null;
|
this.layer = null;
|
||||||
this.fullyConstrained = false;
|
|
||||||
this.constraints = new Set();
|
this.constraints = new Set();
|
||||||
this.readOnly = false;
|
this.readOnly = false;
|
||||||
|
this.fullyConstrained = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
normalDistance(aim, scale) {
|
normalDistance(aim, scale) {
|
||||||
|
|
@ -69,18 +69,32 @@ export class SketchObject extends Shape {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
draw(ctx, scale, viewer) {
|
||||||
if (!this.visible) return;
|
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();
|
ctx.save();
|
||||||
viewer.setStyle(this.marked, ctx);
|
viewer.setStyle(customStyle, ctx);
|
||||||
} else if (this.fullyConstrained) {
|
|
||||||
ctx.save();
|
|
||||||
viewer.setStyle(Styles.FULLY_CONSTRAINED, ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.drawImpl(ctx, scale, viewer);
|
this.drawImpl(ctx, scale, viewer);
|
||||||
if (this.marked != null || this.fullyConstrained) ctx.restore();
|
if (customStyle !== null) ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
copy() {
|
copy() {
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,19 @@ export const Styles = {
|
||||||
fillStyle : "#FF0000"
|
fillStyle : "#FF0000"
|
||||||
},
|
},
|
||||||
|
|
||||||
MARK : {
|
SELECTION : {
|
||||||
lineWidth : 2,
|
lineWidth : 2,
|
||||||
strokeStyle : "#ff0000",
|
strokeStyle : "#ff0000",
|
||||||
fillStyle : "#FF0000"
|
fillStyle : "#FF0000"
|
||||||
},
|
},
|
||||||
|
|
||||||
SNAP : {
|
HIGHLIGHT : {
|
||||||
|
lineWidth : 2,
|
||||||
|
strokeStyle : "#f1ff3a",
|
||||||
|
fillStyle : "#f1ff3a"
|
||||||
|
},
|
||||||
|
|
||||||
|
TOOL_HELPER : {
|
||||||
lineWidth : 2,
|
lineWidth : 2,
|
||||||
strokeStyle : "#00FF00",
|
strokeStyle : "#00FF00",
|
||||||
fillStyle : "#00FF00"
|
fillStyle : "#00FF00"
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,6 @@ export class FilletTool extends Tool {
|
||||||
const point2 = candi[1];
|
const point2 = candi[1];
|
||||||
this.breakLinkAndMakeFillet(point1, point2)
|
this.breakLinkAndMakeFillet(point1, point2)
|
||||||
}
|
}
|
||||||
|
|
||||||
breakLinkAndMakeFillet(point1, point2) {
|
breakLinkAndMakeFillet(point1, point2) {
|
||||||
const pm = this.viewer.parametricManager;
|
const pm = this.viewer.parametricManager;
|
||||||
let coi = null;
|
let coi = null;
|
||||||
|
|
@ -168,7 +167,7 @@ export class FilletTool extends Tool {
|
||||||
}
|
}
|
||||||
var candi = this.getCandidate(e);
|
var candi = this.getCandidate(e);
|
||||||
if (candi != null) {
|
if (candi != null) {
|
||||||
this.viewer.mark(candi[0], Styles.SNAP);
|
this.lastCandidate.addStyle(Styles.SNAP);
|
||||||
needRefresh = true;
|
needRefresh = true;
|
||||||
}
|
}
|
||||||
if (needRefresh) {
|
if (needRefresh) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import * as draw_utils from './shapes/draw-utils';
|
||||||
import {Matrix3} from '../math/l3space';
|
import {Matrix3} from '../math/l3space';
|
||||||
import sketcherStreams from './sketcherStreams';
|
import sketcherStreams from './sketcherStreams';
|
||||||
|
|
||||||
|
|
||||||
class Viewer {
|
class Viewer {
|
||||||
|
|
||||||
constructor(canvas, IO) {
|
constructor(canvas, IO) {
|
||||||
|
|
@ -80,8 +79,10 @@ class Viewer {
|
||||||
this.translate = {x: 0.0, y: 0.0};
|
this.translate = {x: 0.0, y: 0.0};
|
||||||
this.scale = 1.0;
|
this.scale = 1.0;
|
||||||
|
|
||||||
this.selected = [];
|
this.captured = {
|
||||||
this.snapped = null;
|
};
|
||||||
|
Object.keys(CAPTURES).forEach(key => this.captured[key] = []);
|
||||||
|
|
||||||
|
|
||||||
this.historyManager = new HistoryManager(this);
|
this.historyManager = new HistoryManager(this);
|
||||||
this.transformation = null;
|
this.transformation = null;
|
||||||
|
|
@ -89,6 +90,14 @@ class Viewer {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get selected() {
|
||||||
|
return this.captured.selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
get snapped() {
|
||||||
|
return this.captured.tool[0] || null;
|
||||||
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
window.removeEventListener('resize', this.onWindowResize, false);
|
window.removeEventListener('resize', this.onWindowResize, false);
|
||||||
this.canvas = null;
|
this.canvas = null;
|
||||||
|
|
@ -280,19 +289,15 @@ class Viewer {
|
||||||
|
|
||||||
snap(x, y, excl) {
|
snap(x, y, excl) {
|
||||||
this.cleanSnap();
|
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) {
|
if (snapTo.length > 0) {
|
||||||
this.snapped = snapTo[0];
|
this.capture('tool', [snapTo[0]], true);
|
||||||
this.mark(this.snapped, Styles.SNAP);
|
|
||||||
}
|
}
|
||||||
return this.snapped;
|
return this.snapped;
|
||||||
};
|
};
|
||||||
|
|
||||||
cleanSnap() {
|
cleanSnap() {
|
||||||
if (this.snapped != null) {
|
this.withdrawAll('tool')
|
||||||
this.deselect(this.snapped);
|
|
||||||
this.snapped = null;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
showBounds(x1, y1, x2, y2, offset) {
|
showBounds(x1, y1, x2, y2, offset) {
|
||||||
|
|
@ -328,7 +333,7 @@ class Viewer {
|
||||||
};
|
};
|
||||||
|
|
||||||
_screenToModel(x, y) {
|
_screenToModel(x, y) {
|
||||||
var out = {x: 0, y: 0};
|
const out = {x: 0, y: 0};
|
||||||
this.screenToModel2(x, y, out);
|
this.screenToModel2(x, y, out);
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|
@ -365,29 +370,68 @@ class Viewer {
|
||||||
};
|
};
|
||||||
|
|
||||||
select(objs, exclusive) {
|
select(objs, exclusive) {
|
||||||
if (exclusive) this.deselectAll();
|
this.capture('selection', objs, exclusive);
|
||||||
for (var i = 0; i < objs.length; i++) {
|
this.streams.selection.next(this.selected);
|
||||||
this.mark(objs[i]);
|
}
|
||||||
|
|
||||||
|
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) {
|
pick(e) {
|
||||||
var m = this.screenToModel(e);
|
var m = this.screenToModel(e);
|
||||||
return this.search(m.x, m.y, 20 / this.scale, true, false, []);
|
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() {
|
getActiveLayer() {
|
||||||
var layer = this._activeLayer;
|
var layer = this._activeLayer;
|
||||||
if (layer == null || layer.readOnly) {
|
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() {
|
equalizeLinkedEndpoints() {
|
||||||
const visited = new Set();
|
const visited = new Set();
|
||||||
|
|
||||||
|
|
@ -484,11 +510,11 @@ class Viewer {
|
||||||
}
|
}
|
||||||
|
|
||||||
static __SKETCH_DRAW_PIPELINE = [
|
static __SKETCH_DRAW_PIPELINE = [
|
||||||
(obj) => !isEndPoint(obj) && obj.marked === null && isConstruction(obj),
|
(obj) => !isEndPoint(obj) && !obj.marked && isConstruction(obj),
|
||||||
(obj) => !isEndPoint(obj) && obj.marked === null && !isConstruction(obj),
|
(obj) => !isEndPoint(obj) && !obj.marked && !isConstruction(obj),
|
||||||
(obj) => !isEndPoint(obj) && obj.marked !== null,
|
(obj) => !isEndPoint(obj) && obj.marked,
|
||||||
(obj) => isEndPoint(obj) && obj.marked === null,
|
(obj) => isEndPoint(obj) && !obj.marked,
|
||||||
(obj) => isEndPoint(obj) && obj.marked !== null
|
(obj) => isEndPoint(obj) && obj.marked
|
||||||
];
|
];
|
||||||
|
|
||||||
static __SIMPLE_DRAW_PIPELINE = [
|
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}
|
export {Viewer, Styles}
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
body {
|
body {
|
||||||
.sans-serif;
|
|
||||||
font-size: 11px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -11,6 +9,10 @@ html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sans-serif {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.helvetica {
|
.helvetica {
|
||||||
font-family: 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif;
|
font-family: 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
@ -19,10 +21,6 @@ html, body {
|
||||||
font-family: Monaco, monospace;
|
font-family: Monaco, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sans-serif {
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
color: #bbb;
|
color: #bbb;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Web CAD / Part Designer</title>
|
<title>Web CAD / Part Designer</title>
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Condensed:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext">
|
||||||
<link rel="stylesheet" href="lib/font-awesome/css/font-awesome.min.css?modeler">
|
<link rel="stylesheet" href="lib/font-awesome/css/font-awesome.min.css?modeler">
|
||||||
<link rel="shortcut icon" href="img/cad/cube96.png" />
|
<link rel="shortcut icon" href="img/cad/cube96.png" />
|
||||||
<script src="lib/pnltri.js"></script>
|
<script src="lib/pnltri.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>sketcher.js</title>
|
<title>sketcher.js</title>
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Condensed:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext">
|
||||||
<link rel="shortcut icon" href="img/tgn.png" />
|
<link rel="shortcut icon" href="img/tgn.png" />
|
||||||
<link rel="stylesheet" href="css/toolkit.css">
|
<link rel="stylesheet" href="css/toolkit.css">
|
||||||
<link rel="stylesheet" href="lib/font-awesome/css/font-awesome.min.css">
|
<link rel="stylesheet" href="lib/font-awesome/css/font-awesome.min.css">
|
||||||
|
|
@ -70,7 +72,7 @@
|
||||||
<div id="viewer-container" style="background: #808080; overflow: hidden; height: 100%; position: relative">
|
<div id="viewer-container" style="background: #808080; overflow: hidden; height: 100%; position: relative">
|
||||||
<div class="tool-hint" style="position: absolute; bottom: 5px; right: 5px;"></div>
|
<div class="tool-hint" style="position: absolute; bottom: 5px; right: 5px;"></div>
|
||||||
<canvas width="300" height="300" id="viewer"></canvas>
|
<canvas width="300" height="300" id="viewer"></canvas>
|
||||||
<div id="react-controls" style="position: absolute; top: 0; right: 0; bottom: 0;"></div>
|
<div id="react-controls" style="position: absolute; top: 0; right: 0; left: 0; height: 0;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,20 +69,33 @@ module.exports = {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(less|css)$/,
|
oneOf: [
|
||||||
include: [MODULES, WEB_APP],
|
|
||||||
use: [
|
|
||||||
'style-loader',
|
|
||||||
{
|
{
|
||||||
loader: 'css-loader',
|
test: /\.(less|css)$/,
|
||||||
options: {
|
include: [path.resolve(MODULES, 'ui/styles/init')],
|
||||||
getLocalIdent: (context, localIdentName, localName) => generateCSSScopedName(localName, context.resourcePath),
|
use: [
|
||||||
modules: true,
|
'style-loader',
|
||||||
url: false
|
'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$/,
|
test: /\.html$/,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue