diff --git a/modules/lstream/index.d.ts b/modules/lstream/index.d.ts index e3af16eb..47aa20e1 100644 --- a/modules/lstream/index.d.ts +++ b/modules/lstream/index.d.ts @@ -52,7 +52,7 @@ export function distinctState(initialValue: T): StateStream; export function externalState(get: any, set: any): StateStream; -export function never(): Stream +export function never(): Emitter export function constant(value: T): Stream diff --git a/modules/lstream/never.js b/modules/lstream/never.js index 0b3a6880..6865dbae 100644 --- a/modules/lstream/never.js +++ b/modules/lstream/never.js @@ -6,6 +6,9 @@ export class NeverStream extends StreamBase { attach(observer) { return NOOP; } + + next(value) { + } } NeverStream.INSTANCE = new NeverStream(); diff --git a/web/app/math/csys.ts b/modules/math/csys.ts similarity index 64% rename from web/app/math/csys.ts rename to modules/math/csys.ts index 33ffc5e6..f6f16291 100644 --- a/web/app/math/csys.ts +++ b/modules/math/csys.ts @@ -30,25 +30,27 @@ export default class CSys { w() { return this.z.dot(this.origin); } - + + get outTransformation3x3() { + return new Matrix3().setBasisAxises(this.x, this.y, this.z); + } + get outTransformation() { - if (!this._outTr) { - const mx = new Matrix3().setBasisAxises(this.x, this.y, this.z); - mx.tx = this.origin.x; - mx.ty = this.origin.y; - mx.tz = this.origin.z; - this._outTr = mx; - } - return this._outTr; + const mx = new Matrix3().setBasisAxises(this.x, this.y, this.z); + mx.tx = this.origin.x; + mx.ty = this.origin.y; + mx.tz = this.origin.z; + return mx; + } + + get inTransformation3x3() { + return this.outTransformation3x3.invert(); } get inTransformation() { - if (!this._inTr) { - this._inTr = this.outTransformation.invert(); - } - return this._inTr; + return this.outTransformation.invert(); } - + copy(csys) { this.origin.setV(csys.origin); this.x.setV(csys.x); @@ -65,7 +67,16 @@ export default class CSys { this.origin.set(x, y, z); return this; } - + + invert() { + const tr = this.inTransformation; + return new CSys( + new Vector(tr.tx, tr.ty, tr.tz), + new Vector(tr.mxx, tr.myx, tr.mzx), + new Vector(tr.mxy, tr.myy, tr.mzy), + new Vector(tr.mxz, tr.myz, tr.mzz) + ); + } } CSys.ORIGIN = CSys.origin(); diff --git a/modules/math/l3space.ts b/modules/math/l3space.ts new file mode 100644 index 00000000..207f851f --- /dev/null +++ b/modules/math/l3space.ts @@ -0,0 +1,371 @@ +import Vector from 'math/vector'; + +export type Basis = [Vector, Vector, Vector]; + +export type Vec3 = [number, number, number]; + +const freeze = Object.freeze; + +const ORIGIN = freeze(new Vector(0, 0, 0)); + +const AXIS = freeze({ + X: freeze(new Vector(1, 0, 0)), + Y: freeze(new Vector(0, 1, 0)), + Z: freeze(new Vector(0, 0, 1)) +}); + +// @ts-ignore +const IDENTITY_BASIS: Basis = Object.freeze([AXIS.X, AXIS.Y, AXIS.Z]); + +export const STANDARD_BASES = freeze({ + 'XY': IDENTITY_BASIS, + 'XZ': [AXIS.X, AXIS.Z, AXIS.Y], + 'ZY': [AXIS.Z, AXIS.Y, AXIS.X] +}); + +export class Matrix3 { + + mxx: number = 1; + mxy: number = 0; + mxz: number = 0; + tx: number = 0; + myx: number = 0; + myy: number = 1; + myz: number = 0; + ty: number = 0; + mzx: number = 0; + mzy: number = 0; + mzz: number = 1; + tz: number = 0; + + reset(): Matrix3 { + this.mxx = 1; + this.mxy = 0; + this.mxz = 0; + this.tx = 0; + this.myx = 0; + this.myy = 1; + this.myz = 0; + this.ty = 0; + this.mzx = 0; + this.mzy = 0; + this.mzz = 1; + this.tz = 0; + return this; + }; + + setBasis(basis: [Vector, Vector, Vector]): Matrix3 { + var b = basis; + this.mxx = b[0].x; + this.mxy = b[1].x; + this.mxz = b[2].x; + this.tx = 0; + this.myx = b[0].y; + this.myy = b[1].y; + this.myz = b[2].y; + this.ty = 0; + this.mzx = b[0].z; + this.mzy = b[1].z; + this.mzz = b[2].z; + this.tz = 0; + return this; + }; + + setBasisAxises(x: Vector, y: Vector, z: Vector): Matrix3 { + this.mxx = x.x; + this.mxy = y.x; + this.mxz = z.x; + this.tx = 0; + this.myx = x.y; + this.myy = y.y; + this.myz = z.y; + this.ty = 0; + this.mzx = x.z; + this.mzy = y.z; + this.mzz = z.z; + this.tz = 0; + return this; + }; + + setBasisAndTranslation(basis: [Vector, Vector, Vector], translation: Vector): Matrix3 { + this.setBasis(basis); + this.tx = translation.x; + this.ty = translation.y; + this.tz = translation.z; + return this; + }; + + scale(dx: number, dy: number, dz: number): Matrix3{ + this.mxx *= dx; + this.myy *= dy; + this.mzz *= dz; + return this; + }; + + translate(dx: number, dy: number, dz: number): Matrix3 { + this.tx += dx; + this.ty += dy; + this.tz += dz; + return this; + }; + + set3( + mxx: number, mxy: number, mxz: number, + myx: number, myy: number, myz: number, + mzx: number, mzy: number, mzz: number + ): Matrix3 { + this.mxx = mxx; + this.mxy = mxy; + this.mxz = mxz; + this.myx = myx; + this.myy = myy; + this.myz = myz; + this.mzx = mzx; + this.mzy = mzy; + this.mzz = mzz; + return this; + }; + + set34( + mxx: number, mxy: number, mxz: number, tx: number, + myx: number, myy: number, myz: number, ty: number, + mzx: number, mzy: number, mzz: number, tz: number + ): Matrix3 { + this.mxx = mxx; + this.mxy = mxy; + this.mxz = mxz; + this.tx = tx; + this.myx = myx; + this.myy = myy; + this.myz = myz; + this.ty = ty; + this.mzx = mzx; + this.mzy = mzy; + this.mzz = mzz; + this.tz = tz; + return this; + }; + + setMatrix(m: Matrix3): Matrix3 { + this.mxx = m.mxx; + this.mxy = m.mxy; + this.mxz = m.mxz; + this.tx = m.tx; + this.myx = m.myx; + this.myy = m.myy; + this.myz = m.myz; + this.ty = m.ty; + this.mzx = m.mzx; + this.mzy = m.mzy; + this.mzz = m.mzz; + this.tz = m.tz; + return this; + }; + + setToMatrix(m: any): void { + m.set( + this.mxx, this.mxy, this.mxz, this.tx, + this.myx, this.myy, this.myz, this.ty, + this.mzx, this.mzy, this.mzz, this.tz, + 0, 0, 0, 1 + ); + }; + + toArray(): [[number, number, number, number],[number, number, number, number],[number, number, number, number]] { + return [ + [this.mxx, this.mxy, this.mxz, this.tx], + [this.myx, this.myy, this.myz, this.ty], + [this.mzx, this.mzy, this.mzz, this.tz] + ]; + }; + + invert(): Matrix3 { + return this.__invert(new Matrix3()); + }; + + _invert(): Matrix3 { + return this.__invert(this); + }; + + __invert(out: Matrix3): Matrix3 { + + var det = + this.mxx * (this.myy * this.mzz - this.mzy * this.myz) + + this.mxy * (this.myz * this.mzx - this.mzz * this.myx) + + this.mxz * (this.myx * this.mzy - this.mzx * this.myy); + + if (det == 0.0) { + return null; + } + + var cxx = this.myy * this.mzz - this.myz * this.mzy; + var cyx = -this.myx * this.mzz + this.myz * this.mzx; + var czx = this.myx * this.mzy - this.myy * this.mzx; + var cxt = -this.mxy * (this.myz * this.tz - this.mzz * this.ty) + - this.mxz * (this.ty * this.mzy - this.tz * this.myy) + - this.tx * (this.myy * this.mzz - this.mzy * this.myz); + var cxy = -this.mxy * this.mzz + this.mxz * this.mzy; + var cyy = this.mxx * this.mzz - this.mxz * this.mzx; + var czy = -this.mxx * this.mzy + this.mxy * this.mzx; + var cyt = this.mxx * (this.myz * this.tz - this.mzz * this.ty) + + this.mxz * (this.ty * this.mzx - this.tz * this.myx) + + this.tx * (this.myx * this.mzz - this.mzx * this.myz); + var cxz = this.mxy * this.myz - this.mxz * this.myy; + var cyz = -this.mxx * this.myz + this.mxz * this.myx; + var czz = this.mxx * this.myy - this.mxy * this.myx; + var czt = -this.mxx * (this.myy * this.tz - this.mzy * this.ty) + - this.mxy * (this.ty * this.mzx - this.tz * this.myx) + - this.tx * (this.myx * this.mzy - this.mzx * this.myy); + + out.mxx = cxx / det; + out.mxy = cxy / det; + out.mxz = cxz / det; + out.tx = cxt / det; + out.myx = cyx / det; + out.myy = cyy / det; + out.myz = cyz / det; + out.ty = cyt / det; + out.mzx = czx / det; + out.mzy = czy / det; + out.mzz = czz / det; + out.tz = czt / det; + return out; + }; + + combine(transform: Matrix3, out?: Matrix3): Matrix3 { + var txx = transform.mxx; + var txy = transform.mxy; + var txz = transform.mxz; + var ttx = transform.tx; + var tyx = transform.myx; + var tyy = transform.myy; + var tyz = transform.myz; + var tty = transform.ty; + var tzx = transform.mzx; + var tzy = transform.mzy; + var tzz = transform.mzz; + var ttz = transform.tz; + + var m = out || new Matrix3(); + m.mxx = (this.mxx * txx + this.mxy * tyx + this.mxz * tzx); + m.mxy = (this.mxx * txy + this.mxy * tyy + this.mxz * tzy); + m.mxz = (this.mxx * txz + this.mxy * tyz + this.mxz * tzz); + m.tx = (this.mxx * ttx + this.mxy * tty + this.mxz * ttz + this.tx); + m.myx = (this.myx * txx + this.myy * tyx + this.myz * tzx); + m.myy = (this.myx * txy + this.myy * tyy + this.myz * tzy); + m.myz = (this.myx * txz + this.myy * tyz + this.myz * tzz); + m.ty = (this.myx * ttx + this.myy * tty + this.myz * ttz + this.ty); + m.mzx = (this.mzx * txx + this.mzy * tyx + this.mzz * tzx); + m.mzy = (this.mzx * txy + this.mzy * tyy + this.mzz * tzy); + m.mzz = (this.mzx * txz + this.mzy * tyz + this.mzz * tzz); + m.tz = (this.mzx * ttx + this.mzy * tty + this.mzz * ttz + this.tz); + + return m; + }; + + _apply(vector: Vector): Vector { + return this.__apply(vector, vector); + }; + + __apply(vector: Vector, out: Vector): Vector { + let x = vector.x; + let y = vector.y; + let z = vector.z; + out.x = this.mxx * x + this.mxy * y + this.mxz * z + this.tx; + out.y = this.myx * x + this.myy * y + this.myz * z + this.ty; + out.z = this.mzx * x + this.mzy * y + this.mzz * z + this.tz; + return out; + }; + + apply3(data: Vec3): Vec3 { + return this.__apply3(data, [0,0,0]) + }; + + _apply3(data: Vec3): Vec3 { + return this.__apply3(data, data); + }; + + __apply3([x, y, z]: Vec3, out: Vec3): Vec3 { + out[0] = this.mxx * x + this.mxy * y + this.mxz * z + this.tx; + out[1] = this.myx * x + this.myy * y + this.myz * z + this.ty; + out[2] = this.mzx * x + this.mzy * y + this.mzz * z + this.tz; + return out; + }; + + rotateWithSphericalAxis(axisAzimuth: number, axisInclination: number, angle: number, pivot: Vector) { + + const axis = new Vector( + Math.sin(axisAzimuth) * Math.cos(axisInclination), + Math.sin(axisAzimuth) * Math.sin(axisInclination), + Math.cos(axisAzimuth) + ); + + return Matrix3.rotateMatrix(angle, axis, pivot, this); + }; + + rotate(angle: number, axis: Vector, pivot: Vector) { + return Matrix3.rotateMatrix(angle, axis, pivot, this); + }; + + static rotateMatrix(angle: number, axis: Vector, pivot: Vector, matrix: Matrix3): Matrix3 { + var sin = Math.sin(angle); + var cos = Math.cos(angle); + var axisX, axisY, axisZ; + var m = matrix || new Matrix3(); + + if (axis === AXIS.X || axis === AXIS.Y || axis === AXIS.Z) { + axisX = axis.x; + axisY = axis.y; + axisZ = axis.z; + } else { + // normalize + var mag = axis.length(); + + if (mag == 0.0) { + return m; + } else { + axisX = axis.x / mag; + axisY = axis.y / mag; + axisZ = axis.z / mag; + } + } + + var px = pivot.x; + var py = pivot.y; + var pz = pivot.z; + + m.mxx = cos + axisX * axisX * (1 - cos); + m.mxy = axisX * axisY * (1 - cos) - axisZ * sin; + m.mxz = axisX * axisZ * (1 - cos) + axisY * sin; + + m.tx = px * (1 - m.mxx) - py * m.mxy - pz * m.mxz; + + m.myx = axisY * axisX * (1 - cos) + axisZ * sin; + m.myy = cos + axisY * axisY * (1 - cos); + m.myz = axisY * axisZ * (1 - cos) - axisX * sin; + m.ty = py * (1 - m.myy) - px * m.myx - pz * m.myz; + + m.mzx = axisZ * axisX * (1 - cos) - axisY * sin; + m.mzy = axisZ * axisY * (1 - cos) + axisX * sin; + m.mzz = cos + axisZ * axisZ * (1 - cos); + m.tz = pz * (1 - m.mzz) - px * m.mzx - py * m.mzy; + return m; + }; + + apply = vector => this.__apply(vector, new Vector()); +} + + +function BasisForPlane(normal: Vector, alignY: Vector = AXIS.Y, alignZ: Vector = AXIS.Z): [number, number, Vector] { + let alignPlane, x, y; + if (Math.abs(normal.dot(alignY)) < 0.5) { + alignPlane = normal.cross(alignY); + } else { + alignPlane = normal.cross(alignZ); + } + y = alignPlane.cross(normal); + x = y.cross(normal); + return [x, y, normal]; +} + +export {ORIGIN, IDENTITY_BASIS, AXIS, BasisForPlane}; \ No newline at end of file diff --git a/modules/math/quaternion.ts b/modules/math/quaternion.ts new file mode 100644 index 00000000..3849c3b2 --- /dev/null +++ b/modules/math/quaternion.ts @@ -0,0 +1,34 @@ + + +export class Quaternion { + + x: number = 0; + y: number = 0; + z: number = 0; + w: number = 1; + + + setAxisAndAngle(axisX, axisY, axisZ, angle) { + const halfAngle = angle/2; + const sin = Math.sin(halfAngle); + this.x = axisX*sin; + this.y = axisY*sin; + this.z = axisZ*sin; + this.w = Math.cos(halfAngle); + + return this; + } + + setSphericalAxisAndAngle(azimuth, inclination, angle) { + + this.setAxisAndAngle( + Math.sin(azimuth) * Math.cos(inclination), + Math.sin(azimuth) * Math.sin(inclination), + Math.cos(azimuth), + angle + ); + + return this; + } + +} \ No newline at end of file diff --git a/modules/scene/cameraControlRenderer.js b/modules/scene/cameraControlRenderer.js index df67e668..8d787938 100644 --- a/modules/scene/cameraControlRenderer.js +++ b/modules/scene/cameraControlRenderer.js @@ -2,7 +2,7 @@ import {AmbientLight, PerspectiveCamera, Scene, SpotLight, WebGLRenderer} from ' import DPR from '../dpr'; import {MeshArrow} from './objects/auxiliary'; import * as SceneGraph from './sceneGraph'; -import {AXIS} from '../../web/app/math/l3space'; +import {AXIS} from '../math/l3space'; export default function(container) { diff --git a/modules/scene/objects/scalableLine.js b/modules/scene/objects/scalableLine.js index 285f1592..5a5368f2 100644 --- a/modules/scene/objects/scalableLine.js +++ b/modules/scene/objects/scalableLine.js @@ -3,7 +3,7 @@ import {advancePseudoFrenetFrame, frenetFrame, pseudoFrenetFrame} from '../../.. import * as vec from 'math/vec'; import {viewScaleFactor} from '../scaleHelper'; import {arrToThree} from 'math/vectorAdapters'; -import {ORIGIN} from '../../../web/app/math/l3space'; +import {ORIGIN} from '../../math/l3space'; import {getSceneSetup} from '../sceneSetup'; import calcFaceNormal from '../utils/calcFaceNormal'; diff --git a/modules/ui/components/Folder.less b/modules/ui/components/Folder.less index f22b07ae..a83f2352 100644 --- a/modules/ui/components/Folder.less +++ b/modules/ui/components/Folder.less @@ -1,7 +1,6 @@ @import "~ui/styles/theme.less"; .root { - background-color: @bg-color-2; } .title { diff --git a/modules/ui/components/GenericWizard.tsx b/modules/ui/components/GenericWizard.tsx new file mode 100644 index 00000000..58f1a800 --- /dev/null +++ b/modules/ui/components/GenericWizard.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import Window, {WindowControlButton, WindowProps} from 'ui/components/Window'; +import {DocumentationTopic$} from "doc/DocumentationWindow"; +import {IoMdHelp} from "react-icons/io"; +import cx from 'classnames'; +import Stack from "ui/components/Stack"; +import ButtonGroup from "ui/components/controls/ButtonGroup"; +import Button from "ui/components/controls/Button"; + +export function GenericWizard({topicId, title, left, className, children, onCancel, onOK, infoText, ...props}: { + topicId: string, + title: string, + left?: number, + onCancel: () => any, + onOK: () => any, + infoText: string +} & WindowProps ) { + + return + DocumentationTopic$.next({ + topic: topicId, + x: e.pageX + 40, + y: e.pageY + })}> + + + } {...props} > + {children} + + + + + + {infoText} + + + +} + + diff --git a/modules/ui/components/Window.tsx b/modules/ui/components/Window.tsx index ed53f000..556c5de0 100644 --- a/modules/ui/components/Window.tsx +++ b/modules/ui/components/Window.tsx @@ -323,7 +323,7 @@ function _maskTest(mask, value) { export function WindowControlButton({danger, ...props}: { danger?: boolean; children: any; - onClick: () => void; - } & JSX.IntrinsicAttributes) { + onClick: (e: any) => any; + } & React.HTMLAttributes) { return ; } \ No newline at end of file diff --git a/modules/ui/components/controls/ButtonGroup.jsx b/modules/ui/components/controls/ButtonGroup.tsx similarity index 61% rename from modules/ui/components/controls/ButtonGroup.jsx rename to modules/ui/components/controls/ButtonGroup.tsx index 85f888de..ad5a10fe 100644 --- a/modules/ui/components/controls/ButtonGroup.jsx +++ b/modules/ui/components/controls/ButtonGroup.tsx @@ -3,7 +3,7 @@ import React from 'react'; import ls from './ButtonGroup.less' import cx from 'classnames'; -export default function ButtonGroup({className, ...props}) { +export default function ButtonGroup({className, ...props}: React.HTMLAttributes) { return
; diff --git a/modules/ui/components/controls/NumberControl.jsx b/modules/ui/components/controls/NumberControl.jsx index d56f9db9..91226655 100644 --- a/modules/ui/components/controls/NumberControl.jsx +++ b/modules/ui/components/controls/NumberControl.jsx @@ -1,52 +1,59 @@ -import React from 'react'; +import React, {useCallback, useRef} from 'react'; import PropTypes from 'prop-types'; import InputControl from './InputControl'; -export default class NumberControl extends React.Component { - - render() { - let {onChange, onFocus, value} = this.props; - return this.input = input} /> - } - - onChange = e => { - this.props.onChange(e.target.value); +export default function NumberControl(props) { + + let {onChange, onFocus, value} = props; + + const onChangeFromTarget = e => { + onChange(e.target.value); }; - - onWheel = (e) => { - let {baseStep, round, min, max, onChange, accelerator} = this.props; - let delta = e.deltaY; - let step = baseStep * (e.shiftKey ? accelerator : 1); - let val = parseFloat(e.target.value); - if (isNaN(val)) val = 0; - val = val + (delta < 0 ? -step : step); - if (min !== undefined && val < min) { - val = min; + + const attachWheelListener = useCallback((input) => { + if (!input) { + return; } - if (max !== undefined && val > max) { - val = max; - } - if (round !== 0) { - val = val.toFixed(round); - } - this.input.value = val; - onChange(val); - // e.preventDefault(); - e.stopPropagation(); - } + const onWheel = (e) => { + let {baseStep, round, min, max, onChange, accelerator} = props; + let delta = e.shiftKey ? e.deltaX : e.deltaY; + let step = baseStep * (e.shiftKey ? accelerator : 1); + let val = parseFloat(e.target.value); + if (isNaN(val)) val = 0; + val = 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); + } + input.value = val; + onChange(val); + e.preventDefault(); + e.stopPropagation(); + }; + input.addEventListener('wheel', onWheel, {passive: false}); + }, []); + + return + } + + NumberControl.propTypes = { - baseStep: PropTypes.number, - round: PropTypes.number, - min: PropTypes.number, - max: PropTypes.number, - accelerator: PropTypes.number, + baseStep: PropTypes.number, + round: PropTypes.number, + min: PropTypes.number, + max: PropTypes.number, + accelerator: PropTypes.number, onChange: PropTypes.func.isRequired }; diff --git a/modules/ui/effects.ts b/modules/ui/effects.ts index e5bb9ddb..d358636a 100644 --- a/modules/ui/effects.ts +++ b/modules/ui/effects.ts @@ -8,14 +8,14 @@ export function useStream(getStream: (ctx: ApplicationContext) => Stream) const basicStreams = useContext(StreamsContext); const [state, setState] = useState<{data: T}>(); - const stream = typeof getStream === 'function' ? getStream(basicStreams) : getStream; + const stream = resolveStream(getStream, basicStreams); if (!stream) { console.log(getStream); throw "no stream ^"; } - useEffect(() => stream.attach(data => setState({data})), EMPTY_ARR); + useEffect(() => stream.attach(data => setState({data})), [stream]); // @ts-ignore return state ? state.data : (stream.value !== undefined ? stream.value : null); @@ -26,7 +26,7 @@ export function useStreamWithUpdater(getStream: (ctx: ApplicationContext) => const data = useStream(getStream); const basicStreams = useContext(StreamsContext); - const stream = typeof getStream === 'function' ? getStream(basicStreams) : getStream; + const stream = resolveStream(getStream, basicStreams); const updater = useCallback((val) => { @@ -41,5 +41,6 @@ export function useStreamWithUpdater(getStream: (ctx: ApplicationContext) => } - -const EMPTY_ARR = []; \ No newline at end of file +function resolveStream(getStream, basicStreams) { + return typeof getStream === 'function' ? getStream(basicStreams) : getStream +} diff --git a/test/coreTests/testCases/legacy/brep-bool.js b/test/coreTests/testCases/legacy/brep-bool.js index c1d47683..594d8f9f 100644 --- a/test/coreTests/testCases/legacy/brep-bool.js +++ b/test/coreTests/testCases/legacy/brep-bool.js @@ -1,6 +1,6 @@ import * as test from '../../test' import {deepMerge} from '../coreTests/utils/deep-merge' -import {Matrix3} from '../../../../web/app/math/l3space' +import {Matrix3} from '../../../../modules/math/l3space' const OPERANDS_MODE = false; diff --git a/web/app/brep/brep-enclose.js b/web/app/brep/brep-enclose.js index 43c4c993..63cbc2b3 100644 --- a/web/app/brep/brep-enclose.js +++ b/web/app/brep/brep-enclose.js @@ -3,7 +3,7 @@ import {Face} from './topo/face'; import {Edge} from './topo/edge'; import BrepCurve from './geom/curves/brepCurve'; import {Plane} from './geom/impl/plane'; -import {BasisForPlane, Matrix3} from '../math/l3space'; +import {BasisForPlane, Matrix3} from 'math/l3space'; import * as cad_utils from '../cad/cad-utils'; import * as math from '../math/math'; import {createBoundingSurface} from './brep-builder'; diff --git a/web/app/brep/brep-primitives.js b/web/app/brep/brep-primitives.js index 37aabcb6..29996701 100644 --- a/web/app/brep/brep-primitives.js +++ b/web/app/brep/brep-primitives.js @@ -1,9 +1,9 @@ import {Point} from './geom/point' import {Plane} from './geom/impl/plane' import {createPrism, enclose} from './brep-enclose' -import {AXIS, Matrix3} from '../math/l3space' +import {AXIS, Matrix3} from 'math/l3space' import {Circle} from '../cad/sketch/sketchModel' -import CSys from '../math/csys'; +import CSys from 'math/csys'; export function box(w, h, d, tr) { const wh = w * 0.5; diff --git a/web/app/brep/geom/curves/brepCurve.js b/web/app/brep/geom/curves/brepCurve.js index 8b5284a8..0f17933b 100644 --- a/web/app/brep/geom/curves/brepCurve.js +++ b/web/app/brep/geom/curves/brepCurve.js @@ -1,5 +1,5 @@ import NurbsCurve from "./nurbsCurve"; -import {Matrix3} from '../../../math/l3space' +import {Matrix3} from '../../../../../modules/math/l3space' import {areEqual} from '../../../math/math' import {eqSqTol, ueq, veq, veq3, veqNeg} from "../tolerance"; diff --git a/web/app/brep/geom/impl/plane.js b/web/app/brep/geom/impl/plane.js index a4867fd4..826b9039 100644 --- a/web/app/brep/geom/impl/plane.js +++ b/web/app/brep/geom/impl/plane.js @@ -1,6 +1,6 @@ import {Point} from '../point'; import {Line} from './line'; -import {AXIS, BasisForPlane, Matrix3} from '../../../math/l3space'; +import {AXIS, BasisForPlane, Matrix3} from '../../../../../modules/math/l3space'; import {eqTol, veq} from '../tolerance'; export class Plane { diff --git a/web/app/cad/actions/usabilityActions.js b/web/app/cad/actions/usabilityActions.js index e559a27a..af2a52f8 100644 --- a/web/app/cad/actions/usabilityActions.js +++ b/web/app/cad/actions/usabilityActions.js @@ -1,4 +1,4 @@ -import {AXIS, ORIGIN} from '../../math/l3space'; +import {AXIS, ORIGIN} from '../../../../modules/math/l3space'; import Vector from 'math/vector'; import {RiCamera2Line} from "react-icons/ri"; diff --git a/web/app/cad/assembly/assembly.ts b/web/app/cad/assembly/assembly.ts new file mode 100644 index 00000000..8140e211 --- /dev/null +++ b/web/app/cad/assembly/assembly.ts @@ -0,0 +1,145 @@ +import {MObject} from "../model/mobject"; +import {Param} from "../../sketcher/shapes/param"; +import Vector from "math/vector"; +import {Matrix3} from "math/l3space"; +import {ISolveStage, SolvableObject} from "../../sketcher/constr/solvableObject"; +import {AlgNumConstraint} from "../../sketcher/constr/ANConstraints"; +import {Constraints3D} from "./constraints3d"; + +export abstract class AssemblyNode implements SolvableObject { + + constraints: Set = new Set(); + + model: MObject; + + stage: ISolveStage; + + id: string; + + protected constructor(model: MObject) { + this.model = model; + this.id = 'assembly-node:' + model.id; + } + + abstract visitParams(cb); + + abstract reset(); + + createConsistencyConstraints(): AlgNumConstraint[] { + return []; + } + + createRigidBodyLink(body: AssemblyCSysNode): AlgNumConstraint[] { + return []; + } + + get params(): Param[] { + const paramArray = []; + this.visitParams(p => paramArray.push(p)); + return paramArray; + } + +} + +export class AssemblyUnitVectorNode extends AssemblyNode { + + x = new Param(0, 'X'); + y = new Param(0, 'Y'); + z = new Param(0, 'Z'); + getVector: () => Vector; + + constructor(model: MObject, getVector: () => Vector) { + super(model); + this.getVector = getVector; + } + + visitParams(cb) { + cb(this.x); + cb(this.y); + cb(this.z); + } + + reset() { + const {x, y, z} = this.getVector(); + this.x.set(x); + this.y.set(y); + this.z.set(z); + } + + createConsistencyConstraints() { + return [ + new AlgNumConstraint(Constraints3D.UnitVectorConsistency, [this]) + ]; + } + + createRigidBodyLink(body: AssemblyCSysNode) { + return [ + new AlgNumConstraint(Constraints3D.RigidBodyLink3x3, [body, this]) + ]; + } + +} + +export class AssemblyCSysNode extends AssemblyNode { + + ox = new Param(0, 'X'); + oy = new Param(0, 'Y'); + oz = new Param(0, 'Z'); + ix = new Param(1, 'X'); + iy = new Param(0, 'Y'); + iz = new Param(0, 'Z'); + jx = new Param(0, 'X'); + jy = new Param(1, 'Y'); + jz = new Param(0, 'Z'); + kx = new Param(0, 'X'); + ky = new Param(0, 'Y'); + kz = new Param(1, 'Z'); + getTransformation: () => Matrix3; + + constructor(model: MObject, getTransformation: () => Matrix3) { + super(model); + this.getTransformation = getTransformation; + } + + visitParams(cb) { + cb(this.ox); + cb(this.oy); + cb(this.oz); + cb(this.ix); + cb(this.iy); + cb(this.iz); + cb(this.jx); + cb(this.jy); + cb(this.jz); + cb(this.kx); + cb(this.ky); + cb(this.kz); + } + + reset() { + const mx = this.getTransformation(); + this.ox.set(mx.tx); + this.oy.set(mx.ty); + this.oz.set(mx.tz); + + this.ix.set(mx.mxx); + this.iy.set(mx.myx); + this.iz.set(mx.mzx); + + this.jx.set(mx.mxy); + this.jy.set(mx.myy); + this.jz.set(mx.mzy); + + this.kx.set(mx.mxz); + this.ky.set(mx.myz); + this.kz.set(mx.mzz); + + } + + createConsistencyConstraints() { + return [ + new AlgNumConstraint(Constraints3D.CSysConsistency, [this]) + ]; + } + +} \ No newline at end of file diff --git a/web/app/cad/assembly/assemblySolver.ts b/web/app/cad/assembly/assemblySolver.ts new file mode 100644 index 00000000..aa0b40e5 --- /dev/null +++ b/web/app/cad/assembly/assemblySolver.ts @@ -0,0 +1,111 @@ +import {AlgNumConstraint} from "../../sketcher/constr/ANConstraints"; +import {AlgNumSubSystem} from "../../sketcher/constr/AlgNumSystem"; +import Vector from "math/vector"; +import CSys from "math/csys"; +import {AssemblyCSysNode, AssemblyNode} from "./assembly"; +import {ISolveStage} from "../../sketcher/constr/solvableObject"; +import {MObject} from "../model/mobject"; +import {MShell} from "../model/mshell"; +import {Constraints3D} from "./constraints3d"; + +export function solveAssembly(constraints: AlgNumConstraint[]) { + + const objects = new Set(); + + constraints.forEach(c => c.objects.forEach(o => objects.add(o))); + + const stage: ISolveStage = { + objects: objects, + index: 0 + }; + + const roots = new Set(); + objects.forEach(o => { + const root = o.model.root; + if (root instanceof MShell) { + roots.add(root); + objects.add(root.assemblyNodes.location) + } + }); + + objects.forEach(o => { + o.stage = stage; + o.reset(); + }); + + // const algNumConstraint = new AlgNumConstraint(Constraints3D.FaceParallel, objects); + + const system = new AlgNumSubSystem(() => 0.001, val => val, stage); + // __DEBUG__.AddNormal(face1.csys.origin, new Vector().set3(objects[0].normal.map(p => p.get()))) + // __DEBUG__.AddNormal(face2.csys.origin, new Vector().set3(objects[1].normal.map(p => p.get()))) + + system.startTransaction(); + constraints.forEach(c => system.addConstraint(c)); + objects.forEach(assemblyNode => { + const internalConstraints = assemblyNode.createConsistencyConstraints(); + internalConstraints.forEach(c => { + c.internal = true; + system.addConstraint(c); + }); + + const root = assemblyNode.model.root; + if (root instanceof MShell) { + const rigidBodyLinks = assemblyNode.createRigidBodyLink(root.assemblyNodes.location); + rigidBodyLinks.forEach(c => { + c.internal = true; + system.addConstraint(c); + }); + } else { + throw 'unsupported: needs location implementation'; + } + }); + + system.prepare(); + system.solveFine(); + system.finishTransaction(); + + // __DEBUG__.AddNormal(face1.csys.origin, new Vector().set3(objects[0].normal.map(p => p.get()))) + // __DEBUG__.AddNormal(face2.csys.origin, new Vector().set3(objects[1].normal.map(p => p.get()))) + + roots.forEach(root => { + applyResults(root, root.assemblyNodes.location); + }); + +} + + +function applyResults(shell: MShell, targetCsysParams: AssemblyCSysNode): void { + const [ + ox, oy, oz, ix, iy, iz, jx, jy, jz, kx, ky, kz + ] = targetCsysParams.params.map(p => p.get()); + + const targetCsys = new CSys( + new Vector(ox, oy, oz), + new Vector(ix, iy, iz), + new Vector(jx, jy, jz), + new Vector(kx, ky, kz), + ); + + const basis = [ + new Vector(ix, iy, iz), + new Vector(jx, jy, jz), + new Vector(kx, ky, kz), + ]; + + // __DEBUG__.AddCSys(shell.csys); + // __DEBUG__.AddCSys(targetCsys); + + const tr = shell.csys.inTransformation3x3; + basis.forEach(r => tr._apply(r)); + + shell.location$.update(csys => { + return targetCsys; + }); + // shell.location$.mutate(csys => { + // csys.x = basis[0]; + // csys.y = basis[1]; + // csys.z = basis[2]; + // csys.origin = new Vector(ox, oy, oz)._minus(shell.csys.origin); + // }); + +} \ No newline at end of file diff --git a/web/app/cad/assembly/constraints3d.ts b/web/app/cad/assembly/constraints3d.ts new file mode 100644 index 00000000..08ac3f94 --- /dev/null +++ b/web/app/cad/assembly/constraints3d.ts @@ -0,0 +1,248 @@ +import {Polynomial, POW_1_FN, POW_2_FN} from "../../sketcher/constr/polynomial"; +import {NoIcon} from "../../sketcher/icons/NoIcon"; +import {AlgNumConstraint, ConstantsDefinitions, ConstraintSchema} from "../../sketcher/constr/ANConstraints"; +import {MObject} from "../model/mobject"; +import {SolvableObject} from "../../sketcher/constr/solvableObject"; + +export const Constraints3D = { + + FaceParallel: { + id: 'FaceParallel', + name: 'Face Parallel', + icon: NoIcon, + + defineAssemblyScope: ([face1, face2]) => { + return [ + face1.assemblyNodes.normal, + face2.assemblyNodes.normal + ]; + }, + + defineParamsScope: ([n1, n2], cb) => { + n1.visitParams(cb); + n2.visitParams(cb); + }, + + collectPolynomials: (polynomials, params) => { + + const [ + nx1, ny1, nz1, nx2, ny2, nz2, + ] = params; + + polynomials.push( + new Polynomial(1) + .monomial() + .term(nx1, POW_1_FN) + .term(nx2, POW_1_FN) + .monomial() + .term(ny1, POW_1_FN) + .term(ny2, POW_1_FN) + .monomial() + .term(nz1, POW_1_FN) + .term(nz2, POW_1_FN) + + ); + + } + + }, + + UnitVectorConsistency: { + id: 'UnitVectorConsistency', + name: 'UnitVectorConsistency', + icon: NoIcon, + + defineParamsScope: ([vec], cb) => { + vec.visitParams(cb); + }, + + collectPolynomials: (polynomials, params) => { + + const [x, y, z] = params; + + polynomials.push( + new Polynomial(-1) + .monomial() + .term(x, POW_2_FN) + .monomial() + .term(y, POW_2_FN) + .monomial() + .term(z, POW_2_FN) + ); + } + }, + + CSysConsistency: { + id: 'CSysConsistency', + name: 'CSysConsistency', + icon: NoIcon, + + defineParamsScope: ([csys], cb) => { + cb(csys.ix); + cb(csys.iy); + cb(csys.iz); + cb(csys.jx); + cb(csys.jy); + cb(csys.jz); + cb(csys.kx); + cb(csys.ky); + cb(csys.kz); + }, + + collectPolynomials: (polynomials, params) => { + + const [ + ix, + iy, + iz, + jx, + jy, + jz, + kx, + ky, + kz] = params; + + //let's keep matrix orthogonal and unit basis + polynomials.push(new Polynomial(0) + .monomial() + .term(ix, POW_1_FN) + .term(jx, POW_1_FN) + .monomial() + .term(iy, POW_1_FN) + .term(jy, POW_1_FN) + .monomial() + .term(iz, POW_1_FN) + .term(jz, POW_1_FN)); + + polynomials.push(new Polynomial(0) + .monomial() + .term(ix, POW_1_FN) + .term(kx, POW_1_FN) + .monomial() + .term(iy, POW_1_FN) + .term(ky, POW_1_FN) + .monomial() + .term(iz, POW_1_FN) + .term(kz, POW_1_FN)); + + polynomials.push(new Polynomial(0) + .monomial() + .term(jx, POW_1_FN) + .term(kx, POW_1_FN) + .monomial() + .term(jy, POW_1_FN) + .term(ky, POW_1_FN) + .monomial() + .term(jz, POW_1_FN) + .term(kz, POW_1_FN)); + + polynomials.push(new Polynomial(-1) + .monomial() + .term(ix, POW_2_FN) + .monomial() + .term(iy, POW_2_FN) + .monomial() + .term(iz, POW_2_FN)); + + polynomials.push(new Polynomial(-1) + .monomial() + .term(jx, POW_2_FN) + .monomial() + .term(jy, POW_2_FN) + .monomial() + .term(jz, POW_2_FN)); + + polynomials.push(new Polynomial(-1) + .monomial() + .term(kx, POW_2_FN) + .monomial() + .term(ky, POW_2_FN) + .monomial() + .term(kz, POW_2_FN)); + }, + }, + + RigidBodyLink3x3: { + id: 'RigidBodyLink3x3', + name: 'RigidBodyLink3x3', + icon: NoIcon, + + defineParamsScope: ([csys, vec], cb) => { + cb(csys.ix); + cb(csys.iy); + cb(csys.iz); + cb(csys.jx); + cb(csys.jy); + cb(csys.jz); + cb(csys.kx); + cb(csys.ky); + cb(csys.kz); + vec.visitParams(cb); + }, + + collectPolynomials: (polynomials, params, _, objects) => { + const [csys, vec] = objects; + + const {x: nStarX, y: nStarY, z: nStarZ} = vec.getVector(); + + const [ix, iy, iz, jx, jy, jz, kx, ky, kz, x, y, z] = params; + + // out.x = this.mxx * x + this.mxy * y + this.mxz * z + this.tx; + // out.y = this.myx * x + this.myy * y + this.myz * z + this.ty; + // out.z = this.mzx * x + this.mzy * y + this.mzz * z + this.tz; + + polynomials.push( + new Polynomial(0) + .monomial(-1) + .term(x, POW_1_FN) + .monomial(nStarX) + .term(ix, POW_1_FN) + .monomial(nStarY) + .term(jx, POW_1_FN) + .monomial(nStarZ) + .term(kx, POW_1_FN) + ); + + polynomials.push( + new Polynomial(0) + .monomial(-1) + .term(y, POW_1_FN) + .monomial(nStarX) + .term(iy, POW_1_FN) + .monomial(nStarY) + .term(jy, POW_1_FN) + .monomial(nStarZ) + .term(ky, POW_1_FN), + + ); + + polynomials.push( + new Polynomial(0) + .monomial(-1) + .term(z, POW_1_FN) + .monomial(nStarX) + .term(iz, POW_1_FN) + .monomial(nStarY) + .term(jz, POW_1_FN) + .monomial(nStarZ) + .term(kz, POW_1_FN) + ); + + } + }, + +}; + + +export interface AssemblyConstraintSchema extends ConstraintSchema { + defineAssemblyScope: (objects: MObject[]) => SolvableObject[], +} + + +export function createAssemblyConstraint(schema: AssemblyConstraintSchema, + objects: MObject[], + constants?: ConstantsDefinitions, + internal?: boolean = false) { + + return new AlgNumConstraint(schema, schema.defineAssemblyScope(objects), constants, internal); +} \ No newline at end of file diff --git a/web/app/cad/cad-utils.js b/web/app/cad/cad-utils.js index 2ae6dc00..bee31e11 100644 --- a/web/app/cad/cad-utils.js +++ b/web/app/cad/cad-utils.js @@ -3,7 +3,7 @@ import BBox from '../math/bbox' import {HashTable} from '../utils/hashmap' import {Graph} from '../math/graph' import * as math from '../math/math' -import {Matrix3, AXIS, ORIGIN} from '../math/l3space' +import {Matrix3, AXIS, ORIGIN} from '../../../modules/math/l3space' import {MeshSceneSolid} from './scene/wrappers/meshSceneObject' export const FACE_COLOR = 0xB0C4DE; diff --git a/web/app/cad/craft/cadRegistryPlugin.ts b/web/app/cad/craft/cadRegistryPlugin.ts index 76e250ef..6fcb5276 100644 --- a/web/app/cad/craft/cadRegistryPlugin.ts +++ b/web/app/cad/craft/cadRegistryPlugin.ts @@ -56,9 +56,13 @@ export function activate(ctx: ApplicationContext) { function findEntity(entity, id) { return index().get(id); } - + + function find(id) { + return index().get(id); + } + services.cadRegistry = { - getAllShells, findShell, findFace, findEdge, findSketchObject, findEntity, findDatum, findDatumAxis, findLoop, + getAllShells, findShell, findFace, findEdge, findSketchObject, findEntity, findDatum, findDatumAxis, findLoop, find, get modelIndex() { return streams.cadRegistry.modelIndex.value; }, @@ -85,6 +89,7 @@ export interface CadRegistry { findDatum(id: string): MObject; findDatumAxis(id: string): MObject; findLoop(id: string): MObject; + find(id: string): MObject; modelIndex: Map; models: MObject[]; shells: MObject[]; diff --git a/web/app/cad/craft/cutExtrude/cutExtrude.js b/web/app/cad/craft/cutExtrude/cutExtrude.js index 095cf473..0acbd44e 100644 --- a/web/app/cad/craft/cutExtrude/cutExtrude.js +++ b/web/app/cad/craft/cutExtrude/cutExtrude.js @@ -1,4 +1,4 @@ -import {Matrix3} from '../../../math/l3space' +import {Matrix3} from '../../../../../modules/math/l3space' import * as math from '../../../math/math' import {enclose} from '../../../brep/brep-enclose' import {BooleanOperation, combineShells} from '../booleanOperation' diff --git a/web/app/cad/craft/datum/create/createDatumOperation.js b/web/app/cad/craft/datum/create/createDatumOperation.js index 62f937ad..f5b0bdef 100644 --- a/web/app/cad/craft/datum/create/createDatumOperation.js +++ b/web/app/cad/craft/datum/create/createDatumOperation.js @@ -2,7 +2,7 @@ import DatumWizard from './CreateDatumWizard'; import schema from './createDatumOpSchema'; import DatumObject3D from '../datumObject'; import * as SceneGraph from 'scene/sceneGraph'; -import CSys from '../../../../math/csys'; +import CSys from '../../../../../../modules/math/csys'; import {MDatum} from '../../../model/mdatum'; import {roundInteractiveInput} from '../../wizard/roundUtils'; import {DatumParamsRenderer} from '../DatumParamsRenderer'; diff --git a/web/app/cad/craft/datum/csysObject.js b/web/app/cad/craft/datum/csysObject.js index eff38ba6..90b969a6 100644 --- a/web/app/cad/craft/datum/csysObject.js +++ b/web/app/cad/craft/datum/csysObject.js @@ -1,5 +1,5 @@ import {MeshLambertMaterial, Object3D} from 'three'; -import {AXIS} from '../../../math/l3space'; +import {AXIS} from '../../../../../modules/math/l3space'; import {MeshArrow} from 'scene/objects/auxiliary'; import {viewScaleFactor} from '../../../../../modules/scene/scaleHelper'; diff --git a/web/app/cad/craft/datum/rotate/rotateDatumOperation.js b/web/app/cad/craft/datum/rotate/rotateDatumOperation.js index 7187c0c4..7cbff395 100644 --- a/web/app/cad/craft/datum/rotate/rotateDatumOperation.js +++ b/web/app/cad/craft/datum/rotate/rotateDatumOperation.js @@ -1,7 +1,7 @@ import schema from './rotateDatumOpSchema'; import {MDatum} from '../../../model/mdatum'; import RotateDatumWizard from './RotateDatumWizard'; -import {Matrix3, ORIGIN} from '../../../../math/l3space'; +import {Matrix3, ORIGIN} from '../../../../../../modules/math/l3space'; import {DEG_RAD} from '../../../../math/math'; diff --git a/web/app/cad/craft/primitives/cone/coneOperation.js b/web/app/cad/craft/primitives/cone/coneOperation.js index 8c5931c4..bc1ee8b0 100644 --- a/web/app/cad/craft/primitives/cone/coneOperation.js +++ b/web/app/cad/craft/primitives/cone/coneOperation.js @@ -2,7 +2,7 @@ import {Mesh, ConeGeometry, Matrix4, CylinderGeometry} from 'three'; import schema from './coneOpSchema'; import ConeWizard from './ConeWizard'; import {IMAGINARY_SURFACE_MATERIAL} from '../../../preview/scenePreviewer'; -import CSys from '../../../../math/csys'; +import CSys from '../../../../../../modules/math/csys'; import * as SceneGraph from '../../../../../../modules/scene/sceneGraph'; import datumConsumingOperation from '../datumConsumingOperation'; import {assignBooleanParams} from '../booleanOptionHelper'; diff --git a/web/app/cad/craft/primitives/datumConsumingOperation.js b/web/app/cad/craft/primitives/datumConsumingOperation.js index a9e67e6d..1e6adc99 100644 --- a/web/app/cad/craft/primitives/datumConsumingOperation.js +++ b/web/app/cad/craft/primitives/datumConsumingOperation.js @@ -1,4 +1,4 @@ -import CSys from '../../../math/csys'; +import CSys from '../../../../../modules/math/csys'; export default function datumConsumingOperation(params, services, run) { let mDatum = params.datum && services.cadRegistry.findDatum(params.datum); diff --git a/web/app/cad/craft/primitives/primitivePreviewer.js b/web/app/cad/craft/primitives/primitivePreviewer.js index 34dffad6..d39b7e08 100644 --- a/web/app/cad/craft/primitives/primitivePreviewer.js +++ b/web/app/cad/craft/primitives/primitivePreviewer.js @@ -1,6 +1,6 @@ import {BoxGeometry, Matrix4, Mesh} from 'three'; import {IMAGINARY_SURFACE_MATERIAL} from '../../preview/scenePreviewer'; -import CSys from '../../../math/csys'; +import CSys from '../../../../../modules/math/csys'; import * as SceneGraph from '../../../../../modules/scene/sceneGraph'; export default function primitivePreviewer(createThreePrimitiveGeometry, paramsToScales, shift) { diff --git a/web/app/cad/craft/primitives/simplePlane/simplePlaneOperation.js b/web/app/cad/craft/primitives/simplePlane/simplePlaneOperation.js index ecc0beaa..dc132e3a 100644 --- a/web/app/cad/craft/primitives/simplePlane/simplePlaneOperation.js +++ b/web/app/cad/craft/primitives/simplePlane/simplePlaneOperation.js @@ -1,5 +1,5 @@ import {createMeshGeometry} from 'scene/geoms'; -import {STANDARD_BASES} from '../../../../math/l3space'; +import {STANDARD_BASES} from '../../../../../../modules/math/l3space'; import {Plane} from '../../../../brep/geom/impl/plane'; import Vector from 'math/vector'; import PlaneWizard from './SimplePlaneWizard'; diff --git a/web/app/cad/craft/primitives/torus/torusOperation.js b/web/app/cad/craft/primitives/torus/torusOperation.js index 7b7232e4..502068e3 100644 --- a/web/app/cad/craft/primitives/torus/torusOperation.js +++ b/web/app/cad/craft/primitives/torus/torusOperation.js @@ -2,7 +2,7 @@ import {Mesh, TorusGeometry} from 'three'; import schema from './torusOpSchema'; import TorusWizard from './TorusWizard'; import {IMAGINARY_SURFACE_MATERIAL} from '../../../preview/scenePreviewer'; -import CSys from '../../../../math/csys'; +import CSys from '../../../../../../modules/math/csys'; import * as SceneGraph from '../../../../../../modules/scene/sceneGraph'; import datumConsumingOperation from '../datumConsumingOperation'; import {assignBooleanParams} from '../booleanOptionHelper'; diff --git a/web/app/cad/craft/spatialCurve/spatialCurveOperation.js b/web/app/cad/craft/spatialCurve/spatialCurveOperation.js index aff7d087..f0a06d94 100644 --- a/web/app/cad/craft/spatialCurve/spatialCurveOperation.js +++ b/web/app/cad/craft/spatialCurve/spatialCurveOperation.js @@ -1,4 +1,4 @@ -import CSys from '../../../math/csys'; +import CSys from '../../../../../modules/math/csys'; import {MDatum} from '../../model/mdatum'; import spatialCurveOpSchema from './spatialCurveOpSchema'; diff --git a/web/app/cad/craft/wizard/components/Wizard.jsx b/web/app/cad/craft/wizard/components/Wizard.jsx index 40896ffa..78516e04 100644 --- a/web/app/cad/craft/wizard/components/Wizard.jsx +++ b/web/app/cad/craft/wizard/components/Wizard.jsx @@ -1,5 +1,4 @@ import React from 'react'; -import Window, {WindowControlButton} from 'ui/components/Window'; import Stack from 'ui/components/Stack'; import Button from 'ui/components/controls/Button'; import ButtonGroup from 'ui/components/controls/ButtonGroup'; @@ -9,8 +8,7 @@ import CadError from '../../../../utils/errors'; import {FormContext} from './form/Form'; import connect from 'ui/connect'; import {combine} from 'lstream'; -import {DocumentationTopic$} from "../../../../../../modules/doc/DocumentationWindow"; -import {IoMdHelp} from "react-icons/io"; +import {GenericWizard} from "ui/components/GenericWizard"; @connect((streams, props) => combine(props.context.workingRequest$, props.context.state$) .map(([workingRequest, state]) => ({ @@ -56,42 +54,31 @@ export default class Wizard extends React.Component { let Form = operation.form; const error = this.props.error; - return - DocumentationTopic$.next({ - topic: operation.id, - x: e.pageX + 40, - y: e.pageY - })}> - - - }> + topicId={operation.id} + onCancel={this.cancel} + onOK={this.onOK} + infoText={error &&
+ {CadError.ALGORITHM_ERROR_KINDS.includes(error.kind) && + performing operation with current parameters leads to an invalid object + (self-intersecting / zero-thickness / complete degeneration or unsupported cases) + } + {error.code &&
{error.code}
} + {error.userMessage &&
{error.userMessage}
} + {!error.userMessage &&
internal error processing operation, check the log
} +
} + >
- - - - - - {error &&
- {CadError.ALGORITMTHM_ERROR_KINDS.includes(error.kind) && - performing operation with current parameters leads to an invalid object - (self-intersecting / zero-thickness / complete degeneration or unsupported cases) - } - {error.code &&
{error.code}
} - {error.userMessage &&
{error.userMessage}
} - {!error.userMessage &&
internal error processing operation, check the log
} -
} -
- ; + ; } onKeyDown = e => { diff --git a/web/app/cad/debugPlugin.js b/web/app/cad/debugPlugin.js index 9dfe27ff..818e829e 100644 --- a/web/app/cad/debugPlugin.js +++ b/web/app/cad/debugPlugin.js @@ -181,6 +181,12 @@ function addGlobalDebugActions({viewer, cadScene, cadRegistry}) { AddSurfaceNormal: (surface) => { __DEBUG__.AddNormal(surface.pointInMiddle(), surface.normalInMiddle()); }, + AddCSys: (csys, scale) => { + scale = scale || 100; + __DEBUG__.AddNormal(csys.origin, csys.x, 0xff0000, scale) + __DEBUG__.AddNormal(csys.origin, csys.y, 0x00ff00, scale) + __DEBUG__.AddNormal(csys.origin, csys.z, 0x0000ff, scale) + }, AddTessDump: (triangles, color) => { const vec = arr => new THREE.Vector3().fromArray(arr); color = color || 0xffffff; diff --git a/web/app/cad/dom/components/ModellerContextualActions.tsx b/web/app/cad/dom/components/ModellerContextualActions.tsx new file mode 100644 index 00000000..f1a66172 --- /dev/null +++ b/web/app/cad/dom/components/ModellerContextualActions.tsx @@ -0,0 +1,377 @@ +import React, {useContext} from 'react'; +import {AppContext} from "./AppContext"; +import {useStream} from "ui/effects"; +import {ApplicationContext} from "context"; +import {AlgNumSubSystem} from "../../../sketcher/constr/AlgNumSystem"; +import {ParallelConstraintIcon} from "../../../sketcher/icons/constraints/ConstraintIcons"; +import {DEG_RAD, makeAngle0_360} from "../../../math/math"; +import {AlgNumConstraint} from "../../../sketcher/constr/ANConstraints"; +import {Param} from "../../../sketcher/shapes/param"; +import {COS_FN, Polynomial, POW_1_FN, POW_2_FN, SIN_FN} from "../../../sketcher/constr/polynomial"; +import {Matrix3} from "math/l3space"; +import CSys from "math/csys"; +import Vector from "math/vector"; +import {Dialog} from "ui/components/Dialog"; +import {MObject} from "../../model/mobject"; +import {MBrepFace} from "../../model/mface"; +import {solveAssembly} from "../../assembly/assemblySolver"; +import {Constraints3D, createAssemblyConstraint} from "../../assembly/constraints3d"; + +export function ModellerContextualActions({}) { + + const ctx = useContext(AppContext); + + const faceSelection: string[] = useStream(ctx => ctx.streams.selection.face); + + if (faceSelection.length === 0) { + return null; + } + + const actions = []; + + if (faceSelection.length === 2) { + + actions.push(); + + } + + return {}}> + {actions} + ; +} + +const XConstraints3D = { + FaceParallel: { + id: 'FaceParallel', + name: 'FaceParallel', + icon: ParallelConstraintIcon, + + defineAssemblyScope: ([face1, face2], cb) => { + cb(face1.assemblyNodes.normal); + cb(face2.assemblyNodes.normal); + }, + + defineParamsScope: (objects, cb) => { + + const [face1W, face2W, csys1W, csys2W] = objects; + + const n1 = face1W.normal; + const n2 = face2W.normal; + + + const [nx1, ny1, nz1] = n1; + const [nx2, ny2, nz2] = n2; + + const csysParams1 = csys1W.params; + const [ + ox1, oy1, oz1, ix1, iy1, iz1, jx1, jy1, jz1, kx1, ky1, kz1 + ] = csysParams1; + + const csysParams2 = csys2W.params; + const [ + ox2, oy2, oz2, ix2, iy2, iz2, jx2, jy2, jz2, kx2, ky2, kz2 + ] = csysParams2; + + [ + nx1, ny1, nz1, nx2, ny2, nz2, + ox1, oy1, oz1, ix1, iy1, iz1, jx1, jy1, jz1, kx1, ky1, kz1, + ox2, oy2, oz2, ix2, iy2, iz2, jx2, jy2, jz2, kx2, ky2, kz2 + ].forEach(cb); + + }, + + collectPolynomials: (polynomials, params, constants, [face1, face2, csys1, csys2]) => { + + const [ + nx1, ny1, nz1, nx2, ny2, nz2, + ox1, oy1, oz1, ix1, iy1, iz1, jx1, jy1, jz1, kx1, ky1, kz1, + ox2, oy2, oz2, ix2, iy2, iz2, jx2, jy2, jz2, kx2, ky2, kz2 + ] = params; + + polynomials.push( + new Polynomial(1) + .monomial() + .term(nx1, POW_1_FN) + .term(nx2, POW_1_FN) + .monomial() + .term(ny1, POW_1_FN) + .term(ny2, POW_1_FN) + .monomial() + .term(nz1, POW_1_FN) + .term(nz2, POW_1_FN) + ); + + rigidBodyLink3x3( + [ix1, iy1, iz1, jx1, jy1, jz1, kx1, ky1, kz1], + csys1.csys, + face1.normal + ).forEach(p => polynomials.push(p)); + + rigidBodyLink3x3( + [ix2, iy2, iz2, jx2, jy2, jz2, kx2, ky2, kz2], + csys2.csys, + face2.normal + ).forEach(p => polynomials.push(p)); + + polynomials.push( + new Polynomial(-1) + .monomial() + .term(nx1, POW_2_FN) + .monomial() + .term(ny1, POW_2_FN) + .monomial() + .term(nz1, POW_2_FN) + ); + + polynomials.push( + new Polynomial(-1) + .monomial() + .term(nx2, POW_2_FN) + .monomial() + .term(ny2, POW_2_FN) + .monomial() + .term(nz2, POW_2_FN) + ); + + } + + }, + +}; + +function vectorParams(vec) { + const {x, y, z} = vec; + return [new Param(x, 'X'), new Param(y, 'Y'), new Param(z, 'Z')]; +} + +function csysParams(csys) { + const {x, y, z} = csys.origin; + return [ + new Param(x, 'X'), + new Param(y, 'Y'), + new Param(z, 'Z'), + new Param(csys.x.x, 'X'), + new Param(csys.x.y, 'Y'), + new Param(csys.x.z, 'Z'), + new Param(csys.y.x, 'X'), + new Param(csys.y.y, 'Y'), + new Param(csys.y.z, 'Z'), + new Param(csys.z.x, 'X'), + new Param(csys.z.y, 'Y'), + new Param(csys.z.z, 'Z') + ]; +} + +function faceWrapper(face: MBrepFace) { + + return { + constraints: new Set(), + normal: vectorParams(face.normal()), + face, + visitParams(cb) { + this.normal.forEach(cb); + } + + } + +} + + +function csysWrapper(csys: CSys) { + + return { + constraints: new Set(), + params: csysParams(csys), + csys, + visitParams(cb) { + this.params.forEach(cb); + } + } + +} +function faceParallel(ctx: ApplicationContext, faceSelection: string[]) { + + const [face1, face2] = faceSelection.map(id => ctx.cadRegistry.find(id)); + + const constraints = [ + createAssemblyConstraint(Constraints3D.FaceParallel, [face1, face2]) + ]; + + solveAssembly(constraints); +} + +function faceParallelLegacy(ctx: ApplicationContext, faceSelection: string[]) { + + const [face1, face2] = faceSelection.map(id => ctx.cadRegistry.find(id)); + + const stage = {}; + const objects = [ + faceWrapper(face1), + faceWrapper(face2), + csysWrapper(face1.shell.csys), + csysWrapper(face2.shell.csys), + ]; + objects.forEach(o => o.stage = stage); + stage.objects = objects; + const algNumConstraint = new AlgNumConstraint(XConstraints3D.FaceParallel, objects); + + const system = new AlgNumSubSystem(() => 0.001, val => val, stage); + // __DEBUG__.AddNormal(face1.csys.origin, new Vector().set3(objects[0].normal.map(p => p.get()))) + // __DEBUG__.AddNormal(face2.csys.origin, new Vector().set3(objects[1].normal.map(p => p.get()))) + + system.startTransaction(); + system.addConstraint(algNumConstraint); + system.prepare(); + system.solveFine(); + system.finishTransaction(); + + __DEBUG__.AddNormal(face1.csys.origin, new Vector().set3(objects[0].normal.map(p => p.get()))) + __DEBUG__.AddNormal(face2.csys.origin, new Vector().set3(objects[1].normal.map(p => p.get()))) + + + + function applyResults(shell, targetCsysParams, normal) { + const [ + ox, oy, oz, ix, iy, iz, jx, jy, jz, kx, ky, kz + ] = targetCsysParams.map(p => p.get()); + + const targetCsys = new CSys( + new Vector(ox, oy, oz), + new Vector(ix, iy, iz), + new Vector(jx, jy, jz), + new Vector(kx, ky, kz), + ); + + const basis = [ + new Vector(ix, iy, iz), + new Vector(jx, jy, jz), + new Vector(kx, ky, kz), + ]; + + // __DEBUG__.AddCSys(shell.csys); + __DEBUG__.AddCSys(targetCsys); + + const tr = shell.csys.inTransformation3x3; + basis.forEach(r => tr._apply(r)); + + shell.location$.update(csys => { + return targetCsys; + }); + // shell.location$.mutate(csys => { + // csys.x = basis[0]; + // csys.y = basis[1]; + // csys.z = basis[2]; + // csys.origin = new Vector(ox, oy, oz)._minus(shell.csys.origin); + // }); + + } + + applyResults(face1.shell, objects[2].params, new Vector().set3(objects[0].normal.map(p => p.get()))); + applyResults(face2.shell, objects[3].params, new Vector().set3(objects[1].normal.map(p => p.get()))); + + +} + + +function rigidBodyLink3x3(csysParams, csys, vector) { + const [ix, iy, iz, jx, jy, jz, kx, ky, kz] = csysParams; + const [x, y, z] = vector; + + // const [nStarX, nStarY, nStarZ] = csys.inTransformation3x3.apply3(vector.map(p => p.get())); + const [nStarX, nStarY, nStarZ] = vector.map(p => p.get()); + // out.x = this.mxx * x + this.mxy * y + this.mxz * z + this.tx; + // out.y = this.myx * x + this.myy * y + this.myz * z + this.ty; + // out.z = this.mzx * x + this.mzy * y + this.mzz * z + this.tz; + + return [ + new Polynomial(0) + .monomial(-1) + .term(x, POW_1_FN) + .monomial(nStarX) + .term(ix, POW_1_FN) + .monomial(nStarY) + .term(jx, POW_1_FN) + .monomial(nStarZ) + .term(kx, POW_1_FN), + + new Polynomial(0) + .monomial(-1) + .term(y, POW_1_FN) + .monomial(nStarX) + .term(iy, POW_1_FN) + .monomial(nStarY) + .term(jy, POW_1_FN) + .monomial(nStarZ) + .term(ky, POW_1_FN), + + new Polynomial(0) + .monomial(-1) + .term(z, POW_1_FN) + .monomial(nStarX) + .term(iz, POW_1_FN) + .monomial(nStarY) + .term(jz, POW_1_FN) + .monomial(nStarZ) + .term(kz, POW_1_FN), + + + new Polynomial(0) + .monomial() + .term(ix, POW_1_FN) + .term(jx, POW_1_FN) + .monomial() + .term(iy, POW_1_FN) + .term(jy, POW_1_FN) + .monomial() + .term(iz, POW_1_FN) + .term(jz, POW_1_FN), + + new Polynomial(0) + .monomial() + .term(ix, POW_1_FN) + .term(kx, POW_1_FN) + .monomial() + .term(iy, POW_1_FN) + .term(ky, POW_1_FN) + .monomial() + .term(iz, POW_1_FN) + .term(kz, POW_1_FN), + + new Polynomial(0) + .monomial() + .term(jx, POW_1_FN) + .term(kx, POW_1_FN) + .monomial() + .term(jy, POW_1_FN) + .term(ky, POW_1_FN) + .monomial() + .term(jz, POW_1_FN) + .term(kz, POW_1_FN), + + new Polynomial(-1) + .monomial() + .term(ix, POW_2_FN) + .monomial() + .term(iy, POW_2_FN) + .monomial() + .term(iz, POW_2_FN), + + new Polynomial(-1) + .monomial() + .term(jx, POW_2_FN) + .monomial() + .term(jy, POW_2_FN) + .monomial() + .term(jz, POW_2_FN), + + new Polynomial(-1) + .monomial() + .term(kx, POW_2_FN) + .monomial() + .term(ky, POW_2_FN) + .monomial() + .term(kz, POW_2_FN), + + + ] +} diff --git a/web/app/cad/dom/components/PlugableControlBar.jsx b/web/app/cad/dom/components/PlugableControlBar.jsx index 5f28f491..34dc58cd 100644 --- a/web/app/cad/dom/components/PlugableControlBar.jsx +++ b/web/app/cad/dom/components/PlugableControlBar.jsx @@ -4,7 +4,6 @@ import connect from 'ui/connect'; import Fa from 'ui/components/Fa'; import {toIdAndOverrides} from '../../actions/actionRef'; import {isMenuAction} from '../menu/menuPlugin'; -import {combine} from 'lstream'; import {menuAboveElementHint} from '../menu/menuUtils'; import {useStream} from "../../../../../modules/ui/effects"; import {ActionButtonBehavior} from "../../actions/ActionButtonBehavior"; @@ -44,11 +43,13 @@ const RightGroup = connect(streams => streams.ui.controlBars.right.map(actions = function ConnectedActionButton(props) { const actionId = props.actionId; - const stream = useStream(ctx => combine(ctx.streams.action.appearance[actionId], ctx.streams.action.state[actionId])); - if (!stream) { + + const actionAppearance = useStream(ctx => ctx.streams.action.appearance[actionId]); + const actionState = useStream(ctx => ctx.streams.action.state[actionId]); + + if (!actionAppearance || !actionState) { return null; } - const [actionAppearance, actionState] = stream; return {behaviourProps => } diff --git a/web/app/cad/dom/components/PlugableToolbar.jsx b/web/app/cad/dom/components/PlugableToolbar.jsx index 3c455af9..8cea9664 100644 --- a/web/app/cad/dom/components/PlugableToolbar.jsx +++ b/web/app/cad/dom/components/PlugableToolbar.jsx @@ -67,11 +67,11 @@ function ActionButton({label, icon, icon96, icon32, cssIcons, symbol, size, noLa export function ConnectedActionButton(props) { const actionId = props.actionId; - const stream = useStream(ctx => combine(ctx.streams.action.appearance[actionId], ctx.streams.action.state[actionId])); - if (!stream) { + const actionAppearance = useStream(ctx => ctx.streams.action.appearance[actionId]); + const actionState = useStream(ctx => ctx.streams.action.state[actionId]); + if (!actionAppearance || !actionState) { return null; } - const [actionAppearance, actionState] = stream; return {behaviourProps => } diff --git a/web/app/cad/dom/components/UISystem.tsx b/web/app/cad/dom/components/UISystem.tsx index fc07ab21..59a11ecd 100644 --- a/web/app/cad/dom/components/UISystem.tsx +++ b/web/app/cad/dom/components/UISystem.tsx @@ -31,7 +31,6 @@ export default function UISystem({children, ...props}) { {children} -
; diff --git a/web/app/cad/dom/components/View3d.jsx b/web/app/cad/dom/components/View3d.jsx index bd2c8e69..d8e80c96 100644 --- a/web/app/cad/dom/components/View3d.jsx +++ b/web/app/cad/dom/components/View3d.jsx @@ -19,6 +19,8 @@ import {ConstraintEditor} from "../../../sketcher/components/ConstraintEditor"; import SketcherOperationWizard from "../../../sketcher/components/SketcherOperationWizard"; import {ToastContainer} from "react-toastify"; import 'react-toastify/dist/ReactToastify.css'; +import {ContributedComponents} from "./ContributedComponents"; +import {ModellerContextualActions} from "./ModellerContextualActions"; export default class View3d extends React.Component { @@ -56,6 +58,8 @@ export default class View3d extends React.Component {
+ +
diff --git a/web/app/cad/dom/menu/MenuHolder.jsx b/web/app/cad/dom/menu/MenuHolder.jsx index b4f50c42..1d517cdc 100644 --- a/web/app/cad/dom/menu/MenuHolder.jsx +++ b/web/app/cad/dom/menu/MenuHolder.jsx @@ -57,11 +57,14 @@ const ConnectedActionMenu = connect((streams, props) => export function ConnectedMenuItem(props) { const actionId = props.actionId; - const stream = useStream(ctx => combine(ctx.streams.action.appearance[actionId], ctx.streams.action.state[actionId])); - if (!stream) { + + const actionAppearance = useStream(ctx => ctx.streams.action.appearance[actionId]); + const actionState = useStream(ctx => ctx.streams.action.state[actionId]); + + + if (!actionAppearance || !actionState) { return null; } - const [actionAppearance, actionState] = stream; return {behaviourProps => } diff --git a/web/app/cad/init/startApplication.js b/web/app/cad/init/startApplication.js index efc2031d..f8524154 100644 --- a/web/app/cad/init/startApplication.js +++ b/web/app/cad/init/startApplication.js @@ -36,7 +36,7 @@ import * as UIConfigPlugin from "../part/uiConfigPlugin"; import * as DebugPlugin from "../debugPlugin"; import * as ExpressionsPlugin from "../expressions/expressionsPlugin"; import * as PartOperationsPlugin from "../part/partOperationsPlugin"; - +import * as LocationPlugin from "../location/locationPlugin"; export default function startApplication(callback) { @@ -75,6 +75,7 @@ export default function startApplication(callback) { UIConfigPlugin, DebugPlugin, PartOperationsPlugin, + LocationPlugin, RemotePartsPlugin, ViewSyncPlugin, WizardSelectionPlugin diff --git a/web/app/cad/legacy/mesh/revolve.js b/web/app/cad/legacy/mesh/revolve.js index 3feb4560..a0eb7bec 100644 --- a/web/app/cad/legacy/mesh/revolve.js +++ b/web/app/cad/legacy/mesh/revolve.js @@ -1,4 +1,4 @@ -import {Matrix3} from '../../../math/l3space' +import {Matrix3} from '../../../../../modules/math/l3space' import Vector from 'math/vector'; import * as math from '../../../math/math' import {createShared} from '../../cad-utils' diff --git a/web/app/cad/legacy/mesh/wizards/box.js b/web/app/cad/legacy/mesh/wizards/box.js index e70adc8b..1dec1a03 100644 --- a/web/app/cad/legacy/mesh/wizards/box.js +++ b/web/app/cad/legacy/mesh/wizards/box.js @@ -1,4 +1,4 @@ -import {AXIS, IDENTITY_BASIS} from '../../../../math/l3space' +import {AXIS, IDENTITY_BASIS} from '../../../../../../modules/math/l3space' import * as tk from '../../../../ui/toolkit.js' import {FACE_COLOR} from '../../../cad-utils' import {Wizard} from './wizard-commons' diff --git a/web/app/cad/legacy/mesh/wizards/extrude.js b/web/app/cad/legacy/mesh/wizards/extrude.js index 4f41a9d2..79566f34 100644 --- a/web/app/cad/legacy/mesh/wizards/extrude.js +++ b/web/app/cad/legacy/mesh/wizards/extrude.js @@ -2,7 +2,7 @@ import * as tk from '../../../../ui/toolkit.js' import * as workbench from '../workbench' import * as cad_utils from '../../../cad-utils' import Vector from 'math/vector'; -import {Matrix3, ORIGIN} from '../../../../math/l3space' +import {Matrix3, ORIGIN} from '../../../../../../modules/math/l3space' import {OpWizard, IMAGINE_MATERIAL, BASE_MATERIAL} from './wizard-commons' export function ExtrudeWizard(app, face, invert, initParams) { diff --git a/web/app/cad/legacy/mesh/wizards/plane.js b/web/app/cad/legacy/mesh/wizards/plane.js index 31416e93..3fad1d3a 100644 --- a/web/app/cad/legacy/mesh/wizards/plane.js +++ b/web/app/cad/legacy/mesh/wizards/plane.js @@ -1,8 +1,8 @@ -import {AXIS, IDENTITY_BASIS} from '../../../../math/l3space' +import {AXIS, IDENTITY_BASIS} from '../../../../../../modules/math/l3space' import * as tk from '../../../../ui/toolkit.js' import {FACE_COLOR} from '../../../cad-utils' import {Wizard} from './wizard-commons' -import {Matrix3} from '../../../../math/l3space' +import {Matrix3} from '../../../../../../modules/math/l3space' export function PlaneWizard(app, initParams) { Wizard.call(this, app.viewer, initParams); diff --git a/web/app/cad/legacy/mesh/wizards/revolve.js b/web/app/cad/legacy/mesh/wizards/revolve.js index 8ac52aff..dc1097e8 100644 --- a/web/app/cad/legacy/mesh/wizards/revolve.js +++ b/web/app/cad/legacy/mesh/wizards/revolve.js @@ -2,7 +2,7 @@ import * as tk from '../../../../ui/toolkit.js' import * as workbench from '../workbench' import * as cad_utils from '../../../cad-utils' import Vector from 'math/vector'; -import {Matrix3, ORIGIN} from '../../../../math/l3space' +import {Matrix3, ORIGIN} from '../../../../../../modules/math/l3space' import {revolveToTriangles} from '../revolve' import {OpWizard, IMAGINARY_SURFACE_MATERIAL, } from './wizard-commons' diff --git a/web/app/cad/legacy/mesh/wizards/sphere.js b/web/app/cad/legacy/mesh/wizards/sphere.js index f1a7e998..2e263a67 100644 --- a/web/app/cad/legacy/mesh/wizards/sphere.js +++ b/web/app/cad/legacy/mesh/wizards/sphere.js @@ -1,4 +1,4 @@ -import {AXIS, IDENTITY_BASIS} from '../../../../math/l3space' +import {AXIS, IDENTITY_BASIS} from '../../../../../../modules/math/l3space' import * as tk from '../../../../ui/toolkit.js' import {FACE_COLOR} from '../../../cad-utils' import {Wizard} from './wizard-commons' diff --git a/web/app/cad/legacy/mesh/wizards/transform.js b/web/app/cad/legacy/mesh/wizards/transform.js index 4d89caf9..c7404197 100644 --- a/web/app/cad/legacy/mesh/wizards/transform.js +++ b/web/app/cad/legacy/mesh/wizards/transform.js @@ -1,4 +1,4 @@ -import {AXIS, IDENTITY_BASIS} from '../../../../math/l3space' +import {AXIS, IDENTITY_BASIS} from '../../../../../../modules/math/l3space' import * as tk from '../../../../ui/toolkit.js' import {FACE_COLOR} from '../../../cad-utils' import {Wizard} from './wizard-commons' diff --git a/web/app/cad/location/LocationDialog.tsx b/web/app/cad/location/LocationDialog.tsx new file mode 100644 index 00000000..5cdcd023 --- /dev/null +++ b/web/app/cad/location/LocationDialog.tsx @@ -0,0 +1,116 @@ +import React, {useCallback, useContext} from 'react'; +import {useStreamWithUpdater} from "ui/effects"; +import {AppContext} from "../dom/components/AppContext"; +import {GenericWizard} from "ui/components/GenericWizard"; +import Field from "ui/components/controls/Field"; +import {Group} from "../craft/wizard/components/form/Form"; +import Label from "ui/components/controls/Label"; +import Folder from "ui/components/Folder"; +import {never} from "lstream"; +import NumberControl from "ui/components/controls/NumberControl"; +import {Location} from "../model/location"; +import {DEG_RAD} from "../../math/math"; +import CSys from "math/csys"; + +export function LocationDialog() { + + const [req, setReq] = useStreamWithUpdater(ctx => ctx.locationService.editLocationRequest$); + + + const [location, setLocation] = useStreamWithUpdater(() => req ? req.shell.location$ : never()); + + const setX = useCallback(x => { + location.origin.x = parseFloat(x); + setLocation(location); + }, [setLocation]); + + const setY = useCallback(y => { + location.origin.y = parseFloat(y); + setLocation(location); + }, [setLocation]); + + const setZ = useCallback(z => { + location.origin.z = parseFloat(z); + setLocation(location); + }, [setLocation]); + // + // const setAzimuth = useCallback(angle => { + // location.rotationAxisAzimuth = parseFloat(angle) * DEG_RAD; + // setLocation(location); + // }, [setLocation]); + // + // const setInclination = useCallback(angle => { + // location.rotationAxisInclination = parseFloat(angle) * DEG_RAD; + // setLocation(location); + // }, [setLocation]); + // + // const setAngle = useCallback(angle => { + // location.rotationAxisAngle = parseFloat(angle) * DEG_RAD; + // setLocation(location); + // }, [setLocation]); + + const ctx = useContext(AppContext); + + if (!req) { + return null; + } + + const revert = () => { + // close(); + }; + + const close = () => { + setReq(null); + }; + + const revertAndClose = () => { + close(); + }; + + return + + + + + + + + + + + + + + + + + + + + {/**/} + {/* */} + {/* */} + {/**/} + {/**/} + {/* */} + {/* */} + {/**/} + {/**/} + {/* */} + {/* */} + {/**/} + + + + + + +} \ No newline at end of file diff --git a/web/app/cad/location/LocationPlugin.ts b/web/app/cad/location/LocationPlugin.ts new file mode 100644 index 00000000..14b6f285 --- /dev/null +++ b/web/app/cad/location/LocationPlugin.ts @@ -0,0 +1,44 @@ +import {state, StateStream} from "lstream"; +import {ApplicationContext} from "context"; +import {MShell} from "../model/mshell"; +import {LocationDialog} from "./LocationDialog"; + +export function activate(ctx: ApplicationContext) { + + ctx.domService.contributeComponent(LocationDialog); + + const editLocationRequest$ = state(null); + + ctx.locationService = { + editLocationRequest$, + + edit: shell => { + editLocationRequest$.next({ + shell + }) + } + }; +} + + +export interface EditLocationRequest { + + shell: MShell; + +} + +export interface LocationService { + + editLocationRequest$: StateStream; + + edit(shell); + +} + +declare module 'context' { + interface ApplicationContext { + + locationService: LocationService; + } +} + diff --git a/web/app/cad/model/location.ts b/web/app/cad/model/location.ts new file mode 100644 index 00000000..7565d7ca --- /dev/null +++ b/web/app/cad/model/location.ts @@ -0,0 +1,24 @@ +import Vector from "math/vector"; +import {Matrix3} from "math/l3space"; + +export class Location { + + rotationAxisAzimuth: number = 0; + rotationAxisInclination: number = 0; + rotationAxisAngle: number = 0; + + translation: Vector = new Vector(); + + __mx = new Matrix3(); + + toTransformationMatrix() { + this.__mx.rotateWithSphericalAxis( + this.rotationAxisAzimuth, + this.rotationAxisInclination, + this.rotationAxisAngle, + this.translation + ); + return this.__mx; + } + +} \ No newline at end of file diff --git a/web/app/cad/model/mdatum.ts b/web/app/cad/model/mdatum.ts index 43be347f..72245511 100644 --- a/web/app/cad/model/mdatum.ts +++ b/web/app/cad/model/mdatum.ts @@ -1,5 +1,5 @@ import {MObject, MObjectIdGenerator} from './mobject'; -import CSys from "../../math/csys"; +import CSys from "math/csys"; import Vector from "math/vector"; export class MDatum extends MObject { @@ -13,9 +13,9 @@ export class MDatum extends MObject { constructor(csys) { super(MDatum.TYPE, MObjectIdGenerator.next(MDatum.TYPE, 'D')); this.csys = csys; - this.xAxis = new MDatumAxis(this.id + '/X', this.csys.origin, this.csys.x); - this.yAxis = new MDatumAxis(this.id + '/Y', this.csys.origin, this.csys.y); - this.zAxis = new MDatumAxis(this.id + '/Z', this.csys.origin, this.csys.z); + this.xAxis = new MDatumAxis(this.id + '/X', this.csys.origin, this.csys.x, this); + this.yAxis = new MDatumAxis(this.id + '/Y', this.csys.origin, this.csys.y, this); + this.zAxis = new MDatumAxis(this.id + '/Z', this.csys.origin, this.csys.z, this); } getAxisByLiteral(literal) { @@ -33,6 +33,10 @@ export class MDatum extends MObject { this.yAxis.traverse(callback); this.zAxis.traverse(callback); } + + get parent() { + return null; + } } export class MDatumAxis extends MObject { @@ -40,10 +44,16 @@ export class MDatumAxis extends MObject { static TYPE = 'datumAxis'; origin: Vector; dir: Vector; + holder: MObject; - constructor(id, origin, dir) { + constructor(id, origin, dir, holder) { super(MDatumAxis.TYPE, id); this.origin = origin; this.dir = dir; + this.holder = holder; + } + + get parent() { + return this.holder; } } \ No newline at end of file diff --git a/web/app/cad/model/medge.ts b/web/app/cad/model/medge.ts index d995a6fe..b2fa0dcf 100644 --- a/web/app/cad/model/medge.ts +++ b/web/app/cad/model/medge.ts @@ -1,5 +1,5 @@ import {MObject} from './mobject'; -import {MBrepShell, MShell} from "./mshell"; +import {MBrepShell} from "./mshell"; export class MEdge extends MObject { @@ -25,4 +25,8 @@ export class MEdge extends MObject { } return out; } + + get parent() { + return this.shell; + } } \ No newline at end of file diff --git a/web/app/cad/model/mface.ts b/web/app/cad/model/mface.ts index 319b9bde..fc9bb0c4 100644 --- a/web/app/cad/model/mface.ts +++ b/web/app/cad/model/mface.ts @@ -1,12 +1,13 @@ import {MObject} from './mobject'; import Vector from 'math/vector'; -import {BasisForPlane} from '../../math/l3space'; +import {BasisForPlane} from 'math/l3space'; import {MSketchObject} from './msketchObject'; import {EMPTY_ARRAY} from 'gems/iterables'; -import CSys from '../../math/csys'; +import CSys from 'math/csys'; import {MSketchLoop} from './mloop'; import {ProductionInfo} from './productionInfo'; import {MBrepShell, MShell} from "./mshell"; +import {AssemblyUnitVectorNode} from "../assembly/assembly"; export class MFace extends MObject { @@ -18,6 +19,10 @@ export class MFace extends MObject { sketch: any; brepFace: any; + assemblyNodes: { + normal: AssemblyUnitVectorNode + }; + private _csys: any; private w: number; private _basis: [Vector, Vector, Vector]; @@ -31,7 +36,10 @@ export class MFace extends MObject { this.surface = surface; this.sketchObjects = []; this.sketchLoops = []; - this._csys = csys + this._csys = csys; + this.assemblyNodes = { + normal: new AssemblyUnitVectorNode(this, () => this.normal()) + }; } normal() { @@ -166,6 +174,10 @@ export class MFace extends MObject { this.sketchLoops.forEach(i => i.traverse(callback)); } + get parent() { + return this.shell; + } + } export class MBrepFace extends MFace { diff --git a/web/app/cad/model/mloop.ts b/web/app/cad/model/mloop.ts index 5ab92b18..3f6ce612 100644 --- a/web/app/cad/model/mloop.ts +++ b/web/app/cad/model/mloop.ts @@ -10,6 +10,9 @@ export class MLoop extends MObject { super(MLoop.TYPE, id); } + get parent() { + return null; + } } export class MSketchLoop extends MLoop { @@ -24,5 +27,9 @@ export class MSketchLoop extends MLoop { this.contour = contour; } + get parent() { + return this.face; + } + } diff --git a/web/app/cad/model/mobject.ts b/web/app/cad/model/mobject.ts index f60655fd..f2ba9493 100644 --- a/web/app/cad/model/mobject.ts +++ b/web/app/cad/model/mobject.ts @@ -1,11 +1,10 @@ - export abstract class MObject { TYPE: string; id: string; ext: any = {}; - + constructor(TYPE, id) { this.TYPE = TYPE; this.id = id; @@ -14,6 +13,16 @@ export abstract class MObject { traverse(callback: (obj: MObject) => void): void { callback(this); } + + abstract get parent(); + + get root(): MObject { + let obj = this; + while (obj.parent) { + obj = obj.parent; + } + return obj; + } } export const MObjectIdGenerator = { @@ -50,3 +59,4 @@ export const MObjectIdGenerator = { } }; + diff --git a/web/app/cad/model/mopenFace.ts b/web/app/cad/model/mopenFace.ts index f5da3404..3961630e 100644 --- a/web/app/cad/model/mopenFace.ts +++ b/web/app/cad/model/mopenFace.ts @@ -16,5 +16,8 @@ export class MOpenFaceShell extends MShell { get face() { return this.faces[0]; } - + + get parent() { + return null; + } } diff --git a/web/app/cad/model/mshell.ts b/web/app/cad/model/mshell.ts index c9e9c5fe..76362c9c 100644 --- a/web/app/cad/model/mshell.ts +++ b/web/app/cad/model/mshell.ts @@ -1,8 +1,11 @@ import {MObject, MObjectIdGenerator} from './mobject'; -import {MBrepFace, MFace} from './mface'; +import {MBrepFace} from './mface'; import {MEdge} from './medge'; import {MVertex} from './mvertex'; -import CSys from '../../math/csys'; +import CSys from 'math/csys'; +import {Matrix3} from "math/l3space"; +import {state, StateStream} from "lstream"; +import {AssemblyCSysNode} from "../assembly/assembly"; export class MShell extends MObject { @@ -15,8 +18,20 @@ export class MShell extends MObject { edges = []; vertices = []; + location$: StateStream = state(CSys.origin()); + locationMatrix$ = this.location$.map((csys: CSys) => csys.outTransformation).remember(); + + assemblyNodes: { + location: AssemblyCSysNode + }; + constructor() { - super(MShell.TYPE, MObjectIdGenerator.next(MShell.TYPE, 'S')) + super(MShell.TYPE, MObjectIdGenerator.next(MShell.TYPE, 'S')); + // @ts-ignore + this.assemblyNodes = { + location: new AssemblyCSysNode( this, () => new Matrix3() ) + }; + } traverse(callback: (obj: MObject) => void): void { @@ -25,6 +40,10 @@ export class MShell extends MObject { this.edges.forEach(i => i.traverse(callback)); this.vertices.forEach(i => i.traverse(callback)); } + + get parent() { + return null; + } } export class MBrepShell extends MShell { diff --git a/web/app/cad/model/msketchObject.ts b/web/app/cad/model/msketchObject.ts index c5ba0444..8578f708 100644 --- a/web/app/cad/model/msketchObject.ts +++ b/web/app/cad/model/msketchObject.ts @@ -15,4 +15,8 @@ export class MSketchObject extends MObject { this.construction = false; } + get parent() { + return this.face; + } + } \ No newline at end of file diff --git a/web/app/cad/model/mvertex.ts b/web/app/cad/model/mvertex.ts index fafce99a..25a56317 100644 --- a/web/app/cad/model/mvertex.ts +++ b/web/app/cad/model/mvertex.ts @@ -13,4 +13,8 @@ export class MVertex extends MObject { this.brepVertex = brepVertex; } + get parent() { + return this.shell; + } + } \ No newline at end of file diff --git a/web/app/cad/part/uiConfigPlugin.js b/web/app/cad/part/uiConfigPlugin.js index db7518ce..6371d5a5 100644 --- a/web/app/cad/part/uiConfigPlugin.js +++ b/web/app/cad/part/uiConfigPlugin.js @@ -11,7 +11,7 @@ import {SelectionView} from "../dom/components/SelectionView"; import {GrSelect} from "react-icons/gr"; export const STANDARD_MODE_HEADS_UP_TOOLBAR = ['DATUM_CREATE', 'PLANE', 'EditFace', 'EXTRUDE', 'CUT', 'REVOLVE', 'LOFT', - '-', 'FILLET', '-', 'INTERSECTION', 'SUBTRACT', 'UNION', '-', 'IMPORT_PART']; + '-', 'FILLET', '-', 'INTERSECTION', 'SUBTRACT', 'UNION', '-', 'IMPORT_PART', "IMPORT_STEP_FILE"]; export function activate({services, streams}) { streams.ui.controlBars.left.value = ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'menu.views', 'Donate', 'GitHub']; diff --git a/web/app/cad/sandbox.js b/web/app/cad/sandbox.js index 4af12548..82d96bfd 100644 --- a/web/app/cad/sandbox.js +++ b/web/app/cad/sandbox.js @@ -1,4 +1,4 @@ -import {AXIS, Matrix3, ORIGIN} from '../math/l3space' +import {AXIS, Matrix3, ORIGIN} from '../../../modules/math/l3space' import * as vec from 'math/vec' import Vector from 'math/vector'; import BrepBuilder from '../brep/brep-builder' @@ -9,7 +9,7 @@ import {surfaceIntersect} from '../brep/geom/intersection/surfaceSurface'; import {closestToCurveParam} from '../brep/geom/curves/closestPoint'; import NurbsSurface from '../brep/geom/surfaces/nurbsSurface'; import DatumObject3D from './craft/datum/datumObject'; -import CSys from '../math/csys'; +import CSys from '../../../modules/math/csys'; import {createOctreeFromSurface, sphereOctree, traverseOctree} from "../../../modules/voxels/octree"; export function runSandbox({bus, services, services: { viewer, cadScene, cadRegistry, exposure, exposure: {addShellOnScene} }}) { diff --git a/web/app/cad/scene/cadScene.js b/web/app/cad/scene/cadScene.js index 3e612f86..41308eb0 100644 --- a/web/app/cad/scene/cadScene.js +++ b/web/app/cad/scene/cadScene.js @@ -1,4 +1,4 @@ -import {AXIS} from '../../math/l3space' +import {AXIS} from '../../../../modules/math/l3space' import {createArrow} from 'scene/objects/auxiliary'; import Vector from 'math/vector'; import {OnTopOfAll} from 'scene/materialMixins'; diff --git a/web/app/cad/scene/controls/pickControlPlugin.js b/web/app/cad/scene/controls/pickControlPlugin.js index ff906568..6dce7d86 100644 --- a/web/app/cad/scene/controls/pickControlPlugin.js +++ b/web/app/cad/scene/controls/pickControlPlugin.js @@ -69,6 +69,7 @@ export function activate(context) { domElement.addEventListener('mousedown', mousedown, false); domElement.addEventListener('mouseup', mouseup, false); + domElement.addEventListener('dblclick', mousedblclick, false); let mouseState = { startX: 0, @@ -93,6 +94,10 @@ export function activate(context) { } } + function mousedblclick(e) { + handleSolidPick(e); + } + function setPickHandler(handler) { pickHandler = handler || defaultHandler; services.marker.clear(); @@ -136,8 +141,7 @@ export function activate(context) { function handleSolidPick(e) { let pickResults = services.viewer.raycast(e, services.cadScene.workGroup.children); traversePickResults(e, pickResults, PICK_KIND.FACE, (sketchFace) => { - streams.selection.solid.next([sketchFace.solid]); - services.viewer.render(); + context.locationService.edit(sketchFace.shell); return false; }); } diff --git a/web/app/cad/scene/viewSyncPlugin.js b/web/app/cad/scene/viewSyncPlugin.js index 1f03896c..ec9699c6 100644 --- a/web/app/cad/scene/viewSyncPlugin.js +++ b/web/app/cad/scene/viewSyncPlugin.js @@ -38,7 +38,7 @@ function sceneSynchronizer({services: {cadScene, cadRegistry, viewer, wizard, ac if (model instanceof MOpenFaceShell) { modelView = new OpenFaceShellView(model); } else if (model instanceof MShell) { - modelView = new ShellView(model); + modelView = new ShellView(model, undefined, viewer); } else if (model instanceof MDatum) { modelView = new DatumView(model, viewer, wizard.open, diff --git a/web/app/cad/scene/views/shellView.js b/web/app/cad/scene/views/shellView.js index f6ddf181..356f8d49 100644 --- a/web/app/cad/scene/views/shellView.js +++ b/web/app/cad/scene/views/shellView.js @@ -9,7 +9,7 @@ import {Mesh} from 'three'; export class ShellView extends View { - constructor(shell, skin) { + constructor(shell, skin, viewer) { super(shell); this.material = createSolidMaterial(skin); @@ -45,6 +45,15 @@ export class ShellView extends View { SceneGraph.addToGroup(this.edgeGroup, edgeView.rootGroup); this.edgeViews.push(edgeView); } + + this.rootGroup.matrixAutoUpdate = false; + + this.model.locationMatrix$.attach(loc => { + loc.setToMatrix(this.rootGroup.matrix); + this.rootGroup.matrixWorldNeedsUpdate = true; + viewer.requestRender(); + }); + } mark(color) { diff --git a/web/app/cad/scene/wrappers/entityIO.js b/web/app/cad/scene/wrappers/entityIO.js index 314e1d9d..8ac37f4c 100644 --- a/web/app/cad/scene/wrappers/entityIO.js +++ b/web/app/cad/scene/wrappers/entityIO.js @@ -1,6 +1,6 @@ import {readBrep} from '../../../brep/io/brepIO'; import {MBrepShell} from '../../model/mshell'; -import CSys from '../../../math/csys'; +import CSys from '../../../../../modules/math/csys'; export function readShellEntityFromJson(data, consumed, csys) { diff --git a/web/app/cad/scene/wrappers/sceneObject.js b/web/app/cad/scene/wrappers/sceneObject.js index cb43c8e2..d74d8303 100644 --- a/web/app/cad/scene/wrappers/sceneObject.js +++ b/web/app/cad/scene/wrappers/sceneObject.js @@ -1,5 +1,5 @@ import Vector from 'math/vector'; -import {BasisForPlane} from '../../../math/l3space' +import {BasisForPlane} from '../../../../../modules/math/l3space' import DPR from 'dpr' import {setAttribute} from "scene/objectData"; import {genSolidId} from "../../craft/cadRegistryPlugin"; diff --git a/web/app/cad/sketch/inPlaceSketcher.js b/web/app/cad/sketch/inPlaceSketcher.js index 1cf9a87e..c62fd4e5 100644 --- a/web/app/cad/sketch/inPlaceSketcher.js +++ b/web/app/cad/sketch/inPlaceSketcher.js @@ -1,6 +1,6 @@ import {DelegatingPanTool} from '../../sketcher/tools/pan'; import {Matrix4} from 'three/src/math/Matrix4'; -import {ORIGIN} from '../../math/l3space'; +import {ORIGIN} from '../../../../modules/math/l3space'; import {CAMERA_MODE} from '../scene/viewer'; import DPR from 'dpr'; import {SKETCHER_MODE_HEADS_UP_ACTIONS} from "./sketcherUIContrib"; diff --git a/web/app/cad/sketch/sketchModel.js b/web/app/cad/sketch/sketchModel.js index fc59b8cc..051ddb56 100644 --- a/web/app/cad/sketch/sketchModel.js +++ b/web/app/cad/sketch/sketchModel.js @@ -6,7 +6,7 @@ import {LUT} from '../../math/bezier-cubic' import {distanceAB, isCCW, makeAngle0_360} from '../../math/math' import {normalizeCurveEnds} from '../../brep/geom/impl/nurbs-ext'; import Vector from '../../../../modules/math/vector'; -import {AXIS, ORIGIN} from '../../math/l3space'; +import {AXIS, ORIGIN} from '../../../../modules/math/l3space'; const RESOLUTION = 20; diff --git a/web/app/cad/sketch/sketchReader.js b/web/app/cad/sketch/sketchReader.js index 4682f8da..45db93da 100644 --- a/web/app/cad/sketch/sketchReader.js +++ b/web/app/cad/sketch/sketchReader.js @@ -1,5 +1,5 @@ import * as sm from './sketchModel' -import {Matrix3, AXIS, ORIGIN} from '../../math/l3space' +import {Matrix3, AXIS, ORIGIN} from '../../../../modules/math/l3space' import Vector from 'math/vector'; import {Graph} from '../../math/graph' import * as math from '../../math/math' diff --git a/web/app/math/l3space.js b/web/app/math/l3space.js deleted file mode 100644 index 78e20e1e..00000000 --- a/web/app/math/l3space.js +++ /dev/null @@ -1,277 +0,0 @@ -import Vector from 'math/vector'; - -const freeze = Object.freeze; - -const ORIGIN = freeze(new Vector(0, 0, 0)); - -const AXIS = freeze({ - X : freeze(new Vector(1, 0, 0)), - Y : freeze(new Vector(0, 1, 0)), - Z : freeze(new Vector(0, 0, 1)) -}); - -const IDENTITY_BASIS = Object.freeze([AXIS.X, AXIS.Y, AXIS.Z]); - -export const STANDARD_BASES = freeze({ - 'XY': IDENTITY_BASIS, - 'XZ': [AXIS.X, AXIS.Z, AXIS.Y], - 'ZY': [AXIS.Z, AXIS.Y, AXIS.X] -}); - - -class Matrix3 { - constructor() { - this.reset(); - } - - apply = vector => this.__apply(vector, new Vector()); -} - -Matrix3.prototype.reset = function() { - this.mxx = 1; this.mxy = 0; this.mxz = 0; this.tx = 0; - this.myx = 0; this.myy = 1; this.myz = 0; this.ty = 0; - this.mzx = 0; this.mzy = 0; this.mzz = 1; this.tz = 0; - return this; -}; - -Matrix3.prototype.setBasis = function(basis) { - var b = basis; - this.mxx = b[0].x; this.mxy = b[1].x; this.mxz = b[2].x; this.tx = 0; - this.myx = b[0].y; this.myy = b[1].y; this.myz = b[2].y; this.ty = 0; - this.mzx = b[0].z; this.mzy = b[1].z; this.mzz = b[2].z; this.tz = 0; - return this; -}; - -Matrix3.prototype.setBasisAxises = function(x, y, z) { - this.mxx = x.x; this.mxy = y.x; this.mxz = z.x; this.tx = 0; - this.myx = x.y; this.myy = y.y; this.myz = z.y; this.ty = 0; - this.mzx = x.z; this.mzy = y.z; this.mzz = z.z; this.tz = 0; - return this; -}; - -Matrix3.prototype.scale = function(dx, dy, dz) { - this.mxx *= dx; - this.myy *= dy; - this.mzz *= dz; - return this; -}; - -Matrix3.prototype.translate = function(dx, dy, dz) { - this.tx += dx; - this.ty += dy; - this.tz += dz; - return this; -}; - -Matrix3.prototype.set3 = function( - mxx, mxy, mxz, - myx, myy, myz, - mzx, mzy, mzz -) { - this.mxx = mxx; this.mxy = mxy; this.mxz = mxz; - this.myx = myx; this.myy = myy; this.myz = myz; - this.mzx = mzx; this.mzy = mzy; this.mzz = mzz; - return this; -}; - -Matrix3.prototype.set34 = function( - mxx, mxy, mxz, tx, - myx, myy, myz, ty, - mzx, mzy, mzz, tz -) { - this.mxx = mxx; this.mxy = mxy; this.mxz = mxz; this.tx = tx; - this.myx = myx; this.myy = myy; this.myz = myz; this.ty = ty; - this.mzx = mzx; this.mzy = mzy; this.mzz = mzz; this.tz = tz; - return this; -}; - -Matrix3.prototype.setMatrix = function(m) { - this.mxx = m.mxx; this.mxy = m.mxy; this.mxz = m.mxz; this.tx = m.tx; - this.myx = m.myx; this.myy = m.myy; this.myz = m.myz; this.ty = m.ty; - this.mzx = m.mzx; this.mzy = m.mzy; this.mzz = m.mzz; this.tz = m.tz; - return this; -}; - -Matrix3.prototype.toArray = function() { - return [ - [this.mxx, this.mxy, this.mxz, this.tx], - [this.myx, this.myy, this.myz, this.ty], - [this.mzx, this.mzy, this.mzz, this.tz] - ]; -}; - -Matrix3.prototype.invert = function() { - return this.__invert(new Matrix3()); -}; - -Matrix3.prototype._invert = function() { - return this.__invert(this); -}; - -Matrix3.prototype.__invert = function(out) { - - var det = - this.mxx * (this.myy * this.mzz - this.mzy * this.myz) + - this.mxy * (this.myz * this.mzx - this.mzz * this.myx) + - this.mxz * (this.myx * this.mzy - this.mzx * this.myy); - - if (det == 0.0) { - return null; - } - - var cxx = this.myy * this.mzz - this.myz * this.mzy; - var cyx = - this.myx * this.mzz + this.myz * this.mzx; - var czx = this.myx * this.mzy - this.myy * this.mzx; - var cxt = - this.mxy * (this.myz * this.tz - this.mzz * this.ty) - - this.mxz * (this.ty * this.mzy - this.tz * this.myy) - - this.tx * (this.myy * this.mzz - this.mzy * this.myz); - var cxy = - this.mxy * this.mzz + this.mxz * this.mzy; - var cyy = this.mxx * this.mzz - this.mxz * this.mzx; - var czy = - this.mxx * this.mzy + this.mxy * this.mzx; - var cyt = this.mxx * (this.myz * this.tz - this.mzz * this.ty) - + this.mxz * (this.ty * this.mzx - this.tz * this.myx) - + this.tx * (this.myx * this.mzz - this.mzx * this.myz); - var cxz = this.mxy * this.myz - this.mxz * this.myy; - var cyz = - this.mxx * this.myz + this.mxz * this.myx; - var czz = this.mxx * this.myy - this.mxy * this.myx; - var czt = - this.mxx * (this.myy * this.tz - this.mzy * this.ty) - - this.mxy * (this.ty * this.mzx - this.tz * this.myx) - - this.tx * (this.myx * this.mzy - this.mzx * this.myy); - - out.mxx = cxx / det; - out.mxy = cxy / det; - out.mxz = cxz / det; - out.tx = cxt / det; - out.myx = cyx / det; - out.myy = cyy / det; - out.myz = cyz / det; - out.ty = cyt / det; - out.mzx = czx / det; - out.mzy = czy / det; - out.mzz = czz / det; - out.tz = czt / det; - return out; -}; - -Matrix3.prototype.combine = function(transform, out) { - var txx = transform.mxx; - var txy = transform.mxy; - var txz = transform.mxz; - var ttx = transform.tx; - var tyx = transform.myx; - var tyy = transform.myy; - var tyz = transform.myz; - var tty = transform.ty; - var tzx = transform.mzx; - var tzy = transform.mzy; - var tzz = transform.mzz; - var ttz = transform.tz; - - var m = out || new Matrix3(); - m.mxx = (this.mxx * txx + this.mxy * tyx + this.mxz * tzx); - m.mxy = (this.mxx * txy + this.mxy * tyy + this.mxz * tzy); - m.mxz = (this.mxx * txz + this.mxy * tyz + this.mxz * tzz); - m.tx = (this.mxx * ttx + this.mxy * tty + this.mxz * ttz + this.tx); - m.myx = (this.myx * txx + this.myy * tyx + this.myz * tzx); - m.myy = (this.myx * txy + this.myy * tyy + this.myz * tzy); - m.myz = (this.myx * txz + this.myy * tyz + this.myz * tzz); - m.ty = (this.myx * ttx + this.myy * tty + this.myz * ttz + this.ty); - m.mzx = (this.mzx * txx + this.mzy * tyx + this.mzz * tzx); - m.mzy = (this.mzx * txy + this.mzy * tyy + this.mzz * tzy); - m.mzz = (this.mzx * txz + this.mzy * tyz + this.mzz * tzz); - m.tz = (this.mzx * ttx + this.mzy * tty + this.mzz * ttz + this.tz); - - return m; -}; - -Matrix3.prototype._apply = function(vector) { - return this.__apply(vector, vector); -}; - -Matrix3.prototype.__apply = function(vector, out) { - let x = vector.x; - let y = vector.y; - let z = vector.z; - out.x = this.mxx * x + this.mxy * y + this.mxz * z + this.tx; - out.y = this.myx * x + this.myy * y + this.myz * z + this.ty; - out.z = this.mzx * x + this.mzy * y + this.mzz * z + this.tz; - return out; -}; - -Matrix3.prototype.apply3 = function(data) { - return this.__apply3(data, []) -}; - -Matrix3.prototype._apply3 = function(data) { - return this.__apply3(data, data); -}; - -Matrix3.prototype.__apply3 = function([x, y, z], out) { - out[0] = this.mxx * x + this.mxy * y + this.mxz * z + this.tx; - out[1] = this.myx * x + this.myy * y + this.myz * z + this.ty; - out[2] = this.mzx * x + this.mzy * y + this.mzz * z + this.tz; - return out; -}; - -Matrix3.prototype.rotate = function(angle, axis, pivot) { - return Matrix3.rotateMatrix(angle, axis, pivot, this); -}; - -Matrix3.rotateMatrix = function(angle, axis, pivot, matrix) { - var sin = Math.sin(angle); - var cos = Math.cos(angle); - var axisX, axisY, axisZ; - var m = matrix || new Matrix3(); - - if (axis === AXIS.X || axis === AXIS.Y || axis === AXIS.Z) { - axisX = axis.x; - axisY = axis.y; - axisZ = axis.z; - } else { - // normalize - var mag = axis.length(); - - if (mag == 0.0) { - return m; - } else { - axisX = axis.x / mag; - axisY = axis.y / mag; - axisZ = axis.z / mag; - } - } - - var px = pivot.x; - var py = pivot.y; - var pz = pivot.z; - - m.mxx = cos + axisX * axisX * (1 - cos); - m.mxy = axisX * axisY * (1 - cos) - axisZ * sin; - m.mxz = axisX * axisZ * (1 - cos) + axisY * sin; - - m.tx = px * (1 - m.mxx) - py * m.mxy - pz * m.mxz; - - m.myx = axisY * axisX * (1 - cos) + axisZ * sin; - m.myy = cos + axisY * axisY * (1 - cos); - m.myz = axisY * axisZ * (1 - cos) - axisX * sin; - m.ty = py * (1 - m.myy) - px * m.myx - pz * m.myz; - - m.mzx = axisZ * axisX * (1 - cos) - axisY * sin; - m.mzy = axisZ * axisY * (1 - cos) + axisX * sin; - m.mzz = cos + axisZ * axisZ * (1 - cos); - m.tz = pz * (1 - m.mzz) - px * m.mzx - py * m.mzy; - return m; -}; - -function BasisForPlane(normal, alignY = AXIS.Y, alignZ = AXIS.Z) { - let alignPlane, x, y; - if (Math.abs(normal.dot(alignY)) < 0.5) { - alignPlane = normal.cross(alignY); - } else { - alignPlane = normal.cross(alignZ); - } - y = alignPlane.cross(normal); - x = y.cross(normal); - return [x, y, normal]; -} - -export {Matrix3, ORIGIN, IDENTITY_BASIS, AXIS, BasisForPlane}; \ No newline at end of file diff --git a/web/app/sketcher/components/SketchObjectExplorer.jsx b/web/app/sketcher/components/SketchObjectExplorer.jsx index ba3c335f..4c59deb0 100644 --- a/web/app/sketcher/components/SketchObjectExplorer.jsx +++ b/web/app/sketcher/components/SketchObjectExplorer.jsx @@ -9,12 +9,13 @@ import {SketcherAppContext} from "./SketcherAppContext"; export function SketchObjectExplorer() { const [modification, setModification] = useState(0); - const stream = useStream(ctx => combine(ctx.viewer.streams.objects, ctx.viewer.streams.selection)); + const objects = useStream(ctx => ctx.viewer.streams.objects); + const selection = useStream(ctx.viewer.streams.selection); const ctx = useContext(SketcherAppContext); - if (!stream) { + + if (!objects || !selection) { return null } - const [objects, selection] = stream; const select = (obj, exclusive) => { let viewer = ctx.viewer; diff --git a/web/app/sketcher/constr/ANConstraints.js b/web/app/sketcher/constr/ANConstraints.ts similarity index 93% rename from web/app/sketcher/constr/ANConstraints.js rename to web/app/sketcher/constr/ANConstraints.ts index 8b8c5f82..25325956 100644 --- a/web/app/sketcher/constr/ANConstraints.js +++ b/web/app/sketcher/constr/ANConstraints.ts @@ -33,8 +33,15 @@ import { LengthAnnotation, RadiusLengthAnnotation } from "../shapes/annotations/angleAnnotation"; +import {ISolveStage, SolvableObject} from "./solvableObject"; +import {SketchObject} from "../shapes/sketch-object"; +import {IconType} from "react-icons"; -export const ConstraintDefinitions = { +export const ConstraintDefinitions + // : { + // [key: string]: ConstraintSchema + // } += { PCoincident : { id: 'PCoincident', @@ -338,8 +345,8 @@ export const ConstraintDefinitions = { .monomial(-1); - polynomials.push(ellipsePoly()); - polynomials.push(ellipsePoly()); + // polynomials.push(ellipsePoly()); + // polynomials.push(ellipsePoly()); }, }, @@ -814,12 +821,12 @@ export const ConstraintDefinitions = { pt.visitParams(callback); }, - collectPolynomials: (polynomials, [px, py], {x, y}) => { + collectPolynomials: (polynomials, [px, py], {x, y}: ResolvedConstants) => { polynomials.push(new Polynomial(-x).monomial().term(px, POW_1_FN)); polynomials.push(new Polynomial(-y).monomial().term(py, POW_1_FN)); }, - setConstantsFromGeometry: ([pt], constants) => { + setConstantsFromGeometry: ([pt], constants: ConstantsDefinitions) => { constants.x = pt.x + ''; constants.y = pt.y + ''; } @@ -940,11 +947,51 @@ const bezier3Polynomial = (p, t, p0, p1, p2, p3) => new Polynomial() .monomial(-1) .term(p, POW_1_FN); + +export type ResolvedConstants = { [p: string]: any }; +export type ConstantsDefinitions = { [p: string]: string }; + +export interface ConstraintSchema { + + id: string; + name: string, + icon: IconType, + constants?: { + [name: string]: { + readOnly?: boolean; + type: string, + description?: string, + transform?: (string) => any, + initialValue(objects: SolvableObject[]): any; + } + }; + + createAnnotations?: (objects: SolvableObject[], constraintInstance: AlgNumConstraint) => SketchObject[]; + + defineParamsScope: (object: SolvableObject[], cb: (param: Param) => void) => void; + + collectPolynomials(polynomials: Polynomial[], params: Param[], resolvedConstants: ResolvedConstants, objects: SolvableObject[]): void; + + setConstantsFromGeometry?: (object: SolvableObject[], resolvedConstants: ConstantsDefinitions) => void; + + initialGuess?(params: Param[], resolvedConstants: ResolvedConstants): void; +} + export class AlgNumConstraint { static Counter = 0; - constructor(schema, objects, constants, internal = false) { + id: string; + objects: SolvableObject[]; + constants: ConstantsDefinitions; + resolvedConstants: ResolvedConstants; + internal: boolean; + schema: ConstraintSchema; + params: Param[]; + stage: ISolveStage; + private annotations: SketchObject[]; + + constructor(schema: ConstraintSchema, objects: SolvableObject[], constants?: ConstantsDefinitions, internal?: boolean = false) { this.id = schema.id + ':' + (AlgNumConstraint.Counter ++); // only for debug purposes - not persisted this.objects = objects; this.constants = constants; @@ -958,18 +1005,6 @@ export class AlgNumConstraint { this.schema.defineParamsScope(this.objects, p => this.params.push(p)); } - this.modifier = this.schema.modify !== undefined; - if (this.modifier) { - this.referenceObjects = this.schema.referenceObjects(this.objects); - this.managedObjects = this.schema.managedObjects(this.objects); - this.managedObjects.forEach(o => { - if (o.managedBy) { - throw 'there can be only one managing modifier for an object'; - } - o.managedBy = this; - }); - } - if (!this.internal && this.schema.createAnnotations) { this.annotations = this.schema.createAnnotations(this.objects, this); } else { @@ -977,8 +1012,8 @@ export class AlgNumConstraint { } } - collectPolynomials(polynomials) { - this.schema.collectPolynomials(polynomials, this.params, this.resolvedConstants); + collectPolynomials(polynomials: Polynomial[]) { + this.schema.collectPolynomials(polynomials, this.params, this.resolvedConstants, this.objects); } resolveConstants(expressionResolver) { @@ -988,7 +1023,7 @@ export class AlgNumConstraint { } Object.keys(this.constants).map(name => { let def = this.schema.constants[name]; - let val = this.constants[name]; + let val: any = this.constants[name]; val = expressionResolver(val); if (def.type === 'number') { val = parseFloat(val); @@ -1071,4 +1106,3 @@ export class AlgNumConstraint { this.constants[key] = value + ''; // only string are allowed here } } - diff --git a/web/app/sketcher/constr/AlgNumSystem.js b/web/app/sketcher/constr/AlgNumSystem.ts similarity index 95% rename from web/app/sketcher/constr/AlgNumSystem.js rename to web/app/sketcher/constr/AlgNumSystem.ts index d3361cd1..bb31e3a6 100644 --- a/web/app/sketcher/constr/AlgNumSystem.js +++ b/web/app/sketcher/constr/AlgNumSystem.ts @@ -2,8 +2,10 @@ import {prepare} from "./solver"; import {eqEps} from "../../brep/geom/tolerance"; import {Polynomial, POW_1_FN} from "./polynomial"; import {compositeFn} from "gems/func"; +import {AlgNumConstraint} from "./ANConstraints"; +import {SolverParam} from "./solverParam"; -const DEBUG = false; +const DEBUG = true; export class AlgNumSubSystem { @@ -34,6 +36,18 @@ export class AlgNumSubSystem { stage = null; + dof: number = 0; + + requiresHardSolve: boolean = false; + + polynomialIsolations: Isolation[]; + + calcVisualLimit: () => number; + + expressionResolver: (string) => any; + + solveStatus: SolveStatus; + constructor(calcVisualLimit, expressionResolver, stage) { this.calcVisualLimit = calcVisualLimit; @@ -182,7 +196,7 @@ export class AlgNumSubSystem { }); if (DEBUG) { - console.log('reducing system:'); + console.log('reducing system(of', this.polynomials.length, '):'); this.polynomials.forEach(p => console.log(p.toString())); } @@ -502,6 +516,13 @@ export class AlgNumSubSystem { class Isolation { + polynomials: Polynomial[]; + system: AlgNumSubSystem; + beingSolvedParams: Set; + beingSolvedConstraints: Set; + dof: number; + solveStatus: SolveStatus; + numericalSolver: { system; diagnose; solveSystem; error; updateLock }; constructor(polynomials, system) { this.system = system; @@ -605,3 +626,7 @@ class PolynomialResidual { } +interface SolveStatus { + success: boolean; + error: number +} \ No newline at end of file diff --git a/web/app/sketcher/constr/solvableObject.ts b/web/app/sketcher/constr/solvableObject.ts new file mode 100644 index 00000000..30eefe44 --- /dev/null +++ b/web/app/sketcher/constr/solvableObject.ts @@ -0,0 +1,12 @@ +import {Param} from "../shapes/param"; + +export interface SolvableObject { + + id: string; + +} + +export interface ISolveStage { + objects: Set; + index: number; +} \ No newline at end of file diff --git a/web/app/sketcher/constr/solver.js b/web/app/sketcher/constr/solver.js index 0b2ece45..37ebefa3 100644 --- a/web/app/sketcher/constr/solver.js +++ b/web/app/sketcher/constr/solver.js @@ -6,28 +6,6 @@ import {ConstantWrapper, EqualsTo} from './solverConstraints' import {dog_leg} from '../../math/optim' import {newVector} from 'math/vec'; -/** @constructor */ -function Param(value, objectParam) { - this.reset(value); - this.objectParam = objectParam; -} - -Param.prototype.reset = function(value) { - this.set(value); - this.constant = false; - this.j = -1; -}; - -Param.prototype.set = function(value) { - if (this.constant) return; - this.value = value; -}; - -Param.prototype.get = function() { - return this.value; -}; - -Param.prototype.nop = function() {}; /** @constructor */ function System(constraints) { @@ -313,4 +291,4 @@ var solve_lm = function(sys, model, jacobian, rough) { }; }; -export {Param, prepare} \ No newline at end of file +export {prepare} \ No newline at end of file diff --git a/web/app/sketcher/constr/solverParam.ts b/web/app/sketcher/constr/solverParam.ts new file mode 100644 index 00000000..93f59d67 --- /dev/null +++ b/web/app/sketcher/constr/solverParam.ts @@ -0,0 +1,35 @@ +import {Param} from "../shapes/param"; + +export class SolverParam { + + value: number; + objectParam: Param; + constant: boolean; + + /** + * Jacobian position + */ + j: number; + + constructor(value, objectParam) { + this.reset(value); + this.objectParam = objectParam; + } + + reset(value) { + this.set(value); + this.constant = false; + this.j = -1; + }; + + set(value) { + if (this.constant) return; + this.value = value; + }; + + get() { + return this.value; + } + +} + diff --git a/web/app/sketcher/parametric.js b/web/app/sketcher/parametric.ts similarity index 96% rename from web/app/sketcher/parametric.js rename to web/app/sketcher/parametric.ts index f07b4f74..be7fa03b 100644 --- a/web/app/sketcher/parametric.js +++ b/web/app/sketcher/parametric.ts @@ -3,6 +3,8 @@ import {AlgNumConstraint, ConstraintDefinitions} from "./constr/ANConstraints"; import {AlgNumSubSystem} from "./constr/AlgNumSystem"; import {state, stream} from 'lstream'; import {toast} from "react-toastify"; +import {ISolveStage, SolvableObject} from "./constr/solvableObject"; +import {Viewer} from "./viewer2d"; export {Constraints, ParametricManager} @@ -31,6 +33,8 @@ class ParametricManager { $constantDefinition = state(''); groundStage = new SolveStage(this); + viewer: Viewer; + messageSink: (msg) => void; constructor(viewer) { this.viewer = viewer; @@ -240,7 +244,7 @@ class ParametricManager { }); }; - _removeObject = (obj, force) => { + _removeObject = (obj, force?) => { if (obj.__disposed) { return; } @@ -398,10 +402,12 @@ class ParametricManager { } } -class SolveStage { +class SolveStage implements ISolveStage{ generators = new Set(); - objects = new Set(); + objects = new Set(); + private parametricManager: ParametricManager; + private algNumSystem: AlgNumSubSystem; constructor(parametricManager) { this.parametricManager = parametricManager; @@ -460,6 +466,7 @@ class SolveStage { solve(rough) { this.algNumSystem.solve(rough); this.generators.forEach(gen => { + // @ts-ignore gen.regenerate(this.viewer); }) } diff --git a/web/app/sketcher/shapes/param.ts b/web/app/sketcher/shapes/param.ts index 2cc164dd..4697dea1 100644 --- a/web/app/sketcher/shapes/param.ts +++ b/web/app/sketcher/shapes/param.ts @@ -1,15 +1,18 @@ import {Generator} from "../id-generator"; -import {Param as SolverParam} from '../constr/solver'; +import {SolverParam} from "../constr/solverParam"; export class Param { id: number; value: number; - solverParam: any; + solverParam: SolverParam; private readonly debugSymbol: string; normalizer: (number) => any; enforceVisualLimit: boolean = false; + //penalty function constraints + constraints?: any[]; + constructor(value, debugSymbol) { this.id = Generator.genID(); this.value = value; diff --git a/web/app/sketcher/shapes/sketch-object.ts b/web/app/sketcher/shapes/sketch-object.ts index e9834e92..0d774ce1 100644 --- a/web/app/sketcher/shapes/sketch-object.ts +++ b/web/app/sketcher/shapes/sketch-object.ts @@ -4,8 +4,9 @@ import {Styles} from "../styles"; import {NoIcon} from "../icons/NoIcon"; import {Layer, Viewer} from "../viewer2d"; import {NOOP} from "gems/func"; +import {SolvableObject} from "../constr/solveObject"; -export abstract class SketchObject extends Shape { +export abstract class SketchObject extends Shape implements SolvableObject { ref: string; id: string; diff --git a/web/app/sketcher/viewer2d.ts b/web/app/sketcher/viewer2d.ts index c05e71da..621a7c5f 100644 --- a/web/app/sketcher/viewer2d.ts +++ b/web/app/sketcher/viewer2d.ts @@ -9,7 +9,7 @@ import {BasisOrigin} from './shapes/basis-origin'; import Vector from 'math/vector'; import * as draw_utils from './shapes/draw-utils'; -import {Matrix3} from '../math/l3space'; +import {Matrix3} from 'math/l3space'; import sketcherStreams, {SketcherStreams} from './sketcherStreams'; import {BBox, IO} from "./io"; import {NOOP} from "../../../modules/gems/func"; @@ -358,13 +358,14 @@ export class Viewer { }; fit() { - + let count = 0; const bbox = new BBox(); this.accept(obj => { + count ++; bbox.check(obj); return true; }); - if (!bbox.isValid()) { + if (count < 2 || !bbox.isValid()) { return; } diff --git a/web/app/utils/errors.js b/web/app/utils/errors.js index c6d3fcd1..eca451c1 100644 --- a/web/app/utils/errors.js +++ b/web/app/utils/errors.js @@ -22,5 +22,5 @@ CadError.KIND = { INVALID_PARAMS: 'INVALID_PARAMS' }; -CadError.ALGORITMTHM_ERROR_KINDS = ['INTERNAL_ERROR', 'UNSUPPORTED_CASE', 'INVALID_INPUT']; +CadError.ALGORITHM_ERROR_KINDS = ['INTERNAL_ERROR', 'UNSUPPORTED_CASE', 'INVALID_INPUT'];