assembly infrastructure

This commit is contained in:
Val Erastov (xibyte) 2020-06-20 21:53:23 -07:00
parent 06682ab003
commit 4ddf018fc6
85 changed files with 1951 additions and 504 deletions

View file

@ -52,7 +52,7 @@ export function distinctState<T>(initialValue: T): StateStream<T>;
export function externalState<T>(get: any, set: any): StateStream<T>; export function externalState<T>(get: any, set: any): StateStream<T>;
export function never<T>(): Stream<T> export function never<T>(): Emitter<T>
export function constant<T>(value: T): Stream<T> export function constant<T>(value: T): Stream<T>

View file

@ -6,6 +6,9 @@ export class NeverStream extends StreamBase {
attach(observer) { attach(observer) {
return NOOP; return NOOP;
} }
next(value) {
}
} }
NeverStream.INSTANCE = new NeverStream(); NeverStream.INSTANCE = new NeverStream();

View file

@ -30,25 +30,27 @@ export default class CSys {
w() { w() {
return this.z.dot(this.origin); return this.z.dot(this.origin);
} }
get outTransformation3x3() {
return new Matrix3().setBasisAxises(this.x, this.y, this.z);
}
get outTransformation() { get outTransformation() {
if (!this._outTr) { const mx = new Matrix3().setBasisAxises(this.x, this.y, this.z);
const mx = new Matrix3().setBasisAxises(this.x, this.y, this.z); mx.tx = this.origin.x;
mx.tx = this.origin.x; mx.ty = this.origin.y;
mx.ty = this.origin.y; mx.tz = this.origin.z;
mx.tz = this.origin.z; return mx;
this._outTr = mx; }
}
return this._outTr; get inTransformation3x3() {
return this.outTransformation3x3.invert();
} }
get inTransformation() { get inTransformation() {
if (!this._inTr) { return this.outTransformation.invert();
this._inTr = this.outTransformation.invert();
}
return this._inTr;
} }
copy(csys) { copy(csys) {
this.origin.setV(csys.origin); this.origin.setV(csys.origin);
this.x.setV(csys.x); this.x.setV(csys.x);
@ -65,7 +67,16 @@ export default class CSys {
this.origin.set(x, y, z); this.origin.set(x, y, z);
return this; 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(); CSys.ORIGIN = CSys.origin();

371
modules/math/l3space.ts Normal file
View file

@ -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};

View file

@ -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;
}
}

View file

@ -2,7 +2,7 @@ import {AmbientLight, PerspectiveCamera, Scene, SpotLight, WebGLRenderer} from '
import DPR from '../dpr'; import DPR from '../dpr';
import {MeshArrow} from './objects/auxiliary'; import {MeshArrow} from './objects/auxiliary';
import * as SceneGraph from './sceneGraph'; import * as SceneGraph from './sceneGraph';
import {AXIS} from '../../web/app/math/l3space'; import {AXIS} from '../math/l3space';
export default function(container) { export default function(container) {

View file

@ -3,7 +3,7 @@ import {advancePseudoFrenetFrame, frenetFrame, pseudoFrenetFrame} from '../../..
import * as vec from 'math/vec'; import * as vec from 'math/vec';
import {viewScaleFactor} from '../scaleHelper'; import {viewScaleFactor} from '../scaleHelper';
import {arrToThree} from 'math/vectorAdapters'; import {arrToThree} from 'math/vectorAdapters';
import {ORIGIN} from '../../../web/app/math/l3space'; import {ORIGIN} from '../../math/l3space';
import {getSceneSetup} from '../sceneSetup'; import {getSceneSetup} from '../sceneSetup';
import calcFaceNormal from '../utils/calcFaceNormal'; import calcFaceNormal from '../utils/calcFaceNormal';

View file

@ -1,7 +1,6 @@
@import "~ui/styles/theme.less"; @import "~ui/styles/theme.less";
.root { .root {
background-color: @bg-color-2;
} }
.title { .title {

View file

@ -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 <Window initWidth={250}
initLeft={left || 15}
title={(title||'').toUpperCase()}
className={cx('mid-typography', className)}
controlButtons={<>
<WindowControlButton title='help' onClick={(e) => DocumentationTopic$.next({
topic: topicId,
x: e.pageX + 40,
y: e.pageY
})}>
<IoMdHelp />
</WindowControlButton>
</>} {...props} >
{children}
<Stack>
<ButtonGroup>
<Button className='dialog-cancel' onClick={onCancel}>Cancel</Button>
<Button className='dialog-ok' type='accent' onClick={onOK}>OK</Button>
</ButtonGroup>
{infoText}
</Stack>
</Window>
}

View file

@ -323,7 +323,7 @@ function _maskTest(mask, value) {
export function WindowControlButton({danger, ...props}: { export function WindowControlButton({danger, ...props}: {
danger?: boolean; danger?: boolean;
children: any; children: any;
onClick: () => void; onClick: (e: any) => any;
} & JSX.IntrinsicAttributes) { } & React.HTMLAttributes<HTMLSpanElement>) {
return <span className={cx(ls.button, danger&&ls.danger)} {...props} />; return <span className={cx(ls.button, danger&&ls.danger)} {...props} />;
} }

View file

@ -3,7 +3,7 @@ import React from 'react';
import ls from './ButtonGroup.less' import ls from './ButtonGroup.less'
import cx from 'classnames'; import cx from 'classnames';
export default function ButtonGroup({className, ...props}) { export default function ButtonGroup({className, ...props}: React.HTMLAttributes<HTMLSpanElement>) {
return <div className={cx(ls.root, className)} {...props}/>; return <div className={cx(ls.root, className)} {...props}/>;

View file

@ -1,52 +1,59 @@
import React from 'react'; import React, {useCallback, useRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import InputControl from './InputControl'; import InputControl from './InputControl';
export default class NumberControl extends React.Component { export default function NumberControl(props) {
render() { let {onChange, onFocus, value} = props;
let {onChange, onFocus, value} = this.props;
return <InputControl type='number' const onChangeFromTarget = e => {
onWheel={this.onWheel} onChange(e.target.value);
value={ value }
onChange={this.onChange}
onFocus={onFocus}
inputRef={input => this.input = input} />
}
onChange = e => {
this.props.onChange(e.target.value);
}; };
onWheel = (e) => { const attachWheelListener = useCallback((input) => {
let {baseStep, round, min, max, onChange, accelerator} = this.props; if (!input) {
let delta = e.deltaY; return;
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) { const onWheel = (e) => {
val = max; let {baseStep, round, min, max, onChange, accelerator} = props;
} let delta = e.shiftKey ? e.deltaX : e.deltaY;
if (round !== 0) { let step = baseStep * (e.shiftKey ? accelerator : 1);
val = val.toFixed(round); let val = parseFloat(e.target.value);
} if (isNaN(val)) val = 0;
this.input.value = val; val = val + (delta < 0 ? -step : step);
onChange(val); if (min !== undefined && val < min) {
// e.preventDefault(); val = min;
e.stopPropagation(); }
} 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 <InputControl type='number'
value={value}
onChange={onChangeFromTarget}
onFocus={onFocus}
inputRef={attachWheelListener}/>
} }
NumberControl.propTypes = { NumberControl.propTypes = {
baseStep: PropTypes.number, baseStep: PropTypes.number,
round: PropTypes.number, round: PropTypes.number,
min: PropTypes.number, min: PropTypes.number,
max: PropTypes.number, max: PropTypes.number,
accelerator: PropTypes.number, accelerator: PropTypes.number,
onChange: PropTypes.func.isRequired onChange: PropTypes.func.isRequired
}; };

View file

@ -8,14 +8,14 @@ export function useStream<T>(getStream: (ctx: ApplicationContext) => Stream<T>)
const basicStreams = useContext(StreamsContext); const basicStreams = useContext(StreamsContext);
const [state, setState] = useState<{data: T}>(); const [state, setState] = useState<{data: T}>();
const stream = typeof getStream === 'function' ? getStream(basicStreams) : getStream; const stream = resolveStream(getStream, basicStreams);
if (!stream) { if (!stream) {
console.log(getStream); console.log(getStream);
throw "no stream ^"; throw "no stream ^";
} }
useEffect(() => stream.attach(data => setState({data})), EMPTY_ARR); useEffect(() => stream.attach(data => setState({data})), [stream]);
// @ts-ignore // @ts-ignore
return state ? state.data : (stream.value !== undefined ? stream.value : null); return state ? state.data : (stream.value !== undefined ? stream.value : null);
@ -26,7 +26,7 @@ export function useStreamWithUpdater<T>(getStream: (ctx: ApplicationContext) =>
const data = useStream(getStream); const data = useStream(getStream);
const basicStreams = useContext(StreamsContext); const basicStreams = useContext(StreamsContext);
const stream = typeof getStream === 'function' ? getStream(basicStreams) : getStream; const stream = resolveStream(getStream, basicStreams);
const updater = useCallback((val) => { const updater = useCallback((val) => {
@ -41,5 +41,6 @@ export function useStreamWithUpdater<T>(getStream: (ctx: ApplicationContext) =>
} }
function resolveStream(getStream, basicStreams) {
const EMPTY_ARR = []; return typeof getStream === 'function' ? getStream(basicStreams) : getStream
}

View file

@ -1,6 +1,6 @@
import * as test from '../../test' import * as test from '../../test'
import {deepMerge} from '../coreTests/utils/deep-merge' import {deepMerge} from '../coreTests/utils/deep-merge'
import {Matrix3} from '../../../../web/app/math/l3space' import {Matrix3} from '../../../../modules/math/l3space'
const OPERANDS_MODE = false; const OPERANDS_MODE = false;

View file

@ -3,7 +3,7 @@ import {Face} from './topo/face';
import {Edge} from './topo/edge'; import {Edge} from './topo/edge';
import BrepCurve from './geom/curves/brepCurve'; import BrepCurve from './geom/curves/brepCurve';
import {Plane} from './geom/impl/plane'; 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 cad_utils from '../cad/cad-utils';
import * as math from '../math/math'; import * as math from '../math/math';
import {createBoundingSurface} from './brep-builder'; import {createBoundingSurface} from './brep-builder';

View file

@ -1,9 +1,9 @@
import {Point} from './geom/point' import {Point} from './geom/point'
import {Plane} from './geom/impl/plane' import {Plane} from './geom/impl/plane'
import {createPrism, enclose} from './brep-enclose' 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 {Circle} from '../cad/sketch/sketchModel'
import CSys from '../math/csys'; import CSys from 'math/csys';
export function box(w, h, d, tr) { export function box(w, h, d, tr) {
const wh = w * 0.5; const wh = w * 0.5;

View file

@ -1,5 +1,5 @@
import NurbsCurve from "./nurbsCurve"; import NurbsCurve from "./nurbsCurve";
import {Matrix3} from '../../../math/l3space' import {Matrix3} from '../../../../../modules/math/l3space'
import {areEqual} from '../../../math/math' import {areEqual} from '../../../math/math'
import {eqSqTol, ueq, veq, veq3, veqNeg} from "../tolerance"; import {eqSqTol, ueq, veq, veq3, veqNeg} from "../tolerance";

View file

@ -1,6 +1,6 @@
import {Point} from '../point'; import {Point} from '../point';
import {Line} from './line'; import {Line} from './line';
import {AXIS, BasisForPlane, Matrix3} from '../../../math/l3space'; import {AXIS, BasisForPlane, Matrix3} from '../../../../../modules/math/l3space';
import {eqTol, veq} from '../tolerance'; import {eqTol, veq} from '../tolerance';
export class Plane { export class Plane {

View file

@ -1,4 +1,4 @@
import {AXIS, ORIGIN} from '../../math/l3space'; import {AXIS, ORIGIN} from '../../../../modules/math/l3space';
import Vector from 'math/vector'; import Vector from 'math/vector';
import {RiCamera2Line} from "react-icons/ri"; import {RiCamera2Line} from "react-icons/ri";

View file

@ -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<AlgNumConstraint> = 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])
];
}
}

View file

@ -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<AssemblyNode>();
constraints.forEach(c => c.objects.forEach(o => objects.add(o)));
const stage: ISolveStage = {
objects: objects,
index: 0
};
const roots = new Set<MShell>();
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);
// });
}

View file

@ -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);
}

View file

@ -3,7 +3,7 @@ import BBox from '../math/bbox'
import {HashTable} from '../utils/hashmap' import {HashTable} from '../utils/hashmap'
import {Graph} from '../math/graph' import {Graph} from '../math/graph'
import * as math from '../math/math' 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' import {MeshSceneSolid} from './scene/wrappers/meshSceneObject'
export const FACE_COLOR = 0xB0C4DE; export const FACE_COLOR = 0xB0C4DE;

View file

@ -56,9 +56,13 @@ export function activate(ctx: ApplicationContext) {
function findEntity(entity, id) { function findEntity(entity, id) {
return index().get(id); return index().get(id);
} }
function find(id) {
return index().get(id);
}
services.cadRegistry = { services.cadRegistry = {
getAllShells, findShell, findFace, findEdge, findSketchObject, findEntity, findDatum, findDatumAxis, findLoop, getAllShells, findShell, findFace, findEdge, findSketchObject, findEntity, findDatum, findDatumAxis, findLoop, find,
get modelIndex() { get modelIndex() {
return streams.cadRegistry.modelIndex.value; return streams.cadRegistry.modelIndex.value;
}, },
@ -85,6 +89,7 @@ export interface CadRegistry {
findDatum(id: string): MObject; findDatum(id: string): MObject;
findDatumAxis(id: string): MObject; findDatumAxis(id: string): MObject;
findLoop(id: string): MObject; findLoop(id: string): MObject;
find(id: string): MObject;
modelIndex: Map<String, MObject>; modelIndex: Map<String, MObject>;
models: MObject[]; models: MObject[];
shells: MObject[]; shells: MObject[];

View file

@ -1,4 +1,4 @@
import {Matrix3} from '../../../math/l3space' import {Matrix3} from '../../../../../modules/math/l3space'
import * as math from '../../../math/math' import * as math from '../../../math/math'
import {enclose} from '../../../brep/brep-enclose' import {enclose} from '../../../brep/brep-enclose'
import {BooleanOperation, combineShells} from '../booleanOperation' import {BooleanOperation, combineShells} from '../booleanOperation'

View file

@ -2,7 +2,7 @@ import DatumWizard from './CreateDatumWizard';
import schema from './createDatumOpSchema'; import schema from './createDatumOpSchema';
import DatumObject3D from '../datumObject'; import DatumObject3D from '../datumObject';
import * as SceneGraph from 'scene/sceneGraph'; import * as SceneGraph from 'scene/sceneGraph';
import CSys from '../../../../math/csys'; import CSys from '../../../../../../modules/math/csys';
import {MDatum} from '../../../model/mdatum'; import {MDatum} from '../../../model/mdatum';
import {roundInteractiveInput} from '../../wizard/roundUtils'; import {roundInteractiveInput} from '../../wizard/roundUtils';
import {DatumParamsRenderer} from '../DatumParamsRenderer'; import {DatumParamsRenderer} from '../DatumParamsRenderer';

View file

@ -1,5 +1,5 @@
import {MeshLambertMaterial, Object3D} from 'three'; import {MeshLambertMaterial, Object3D} from 'three';
import {AXIS} from '../../../math/l3space'; import {AXIS} from '../../../../../modules/math/l3space';
import {MeshArrow} from 'scene/objects/auxiliary'; import {MeshArrow} from 'scene/objects/auxiliary';
import {viewScaleFactor} from '../../../../../modules/scene/scaleHelper'; import {viewScaleFactor} from '../../../../../modules/scene/scaleHelper';

View file

@ -1,7 +1,7 @@
import schema from './rotateDatumOpSchema'; import schema from './rotateDatumOpSchema';
import {MDatum} from '../../../model/mdatum'; import {MDatum} from '../../../model/mdatum';
import RotateDatumWizard from './RotateDatumWizard'; import RotateDatumWizard from './RotateDatumWizard';
import {Matrix3, ORIGIN} from '../../../../math/l3space'; import {Matrix3, ORIGIN} from '../../../../../../modules/math/l3space';
import {DEG_RAD} from '../../../../math/math'; import {DEG_RAD} from '../../../../math/math';

View file

@ -2,7 +2,7 @@ import {Mesh, ConeGeometry, Matrix4, CylinderGeometry} from 'three';
import schema from './coneOpSchema'; import schema from './coneOpSchema';
import ConeWizard from './ConeWizard'; import ConeWizard from './ConeWizard';
import {IMAGINARY_SURFACE_MATERIAL} from '../../../preview/scenePreviewer'; 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 * as SceneGraph from '../../../../../../modules/scene/sceneGraph';
import datumConsumingOperation from '../datumConsumingOperation'; import datumConsumingOperation from '../datumConsumingOperation';
import {assignBooleanParams} from '../booleanOptionHelper'; import {assignBooleanParams} from '../booleanOptionHelper';

View file

@ -1,4 +1,4 @@
import CSys from '../../../math/csys'; import CSys from '../../../../../modules/math/csys';
export default function datumConsumingOperation(params, services, run) { export default function datumConsumingOperation(params, services, run) {
let mDatum = params.datum && services.cadRegistry.findDatum(params.datum); let mDatum = params.datum && services.cadRegistry.findDatum(params.datum);

View file

@ -1,6 +1,6 @@
import {BoxGeometry, Matrix4, Mesh} from 'three'; import {BoxGeometry, Matrix4, Mesh} from 'three';
import {IMAGINARY_SURFACE_MATERIAL} from '../../preview/scenePreviewer'; 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 * as SceneGraph from '../../../../../modules/scene/sceneGraph';
export default function primitivePreviewer(createThreePrimitiveGeometry, paramsToScales, shift) { export default function primitivePreviewer(createThreePrimitiveGeometry, paramsToScales, shift) {

View file

@ -1,5 +1,5 @@
import {createMeshGeometry} from 'scene/geoms'; 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 {Plane} from '../../../../brep/geom/impl/plane';
import Vector from 'math/vector'; import Vector from 'math/vector';
import PlaneWizard from './SimplePlaneWizard'; import PlaneWizard from './SimplePlaneWizard';

View file

@ -2,7 +2,7 @@ import {Mesh, TorusGeometry} from 'three';
import schema from './torusOpSchema'; import schema from './torusOpSchema';
import TorusWizard from './TorusWizard'; import TorusWizard from './TorusWizard';
import {IMAGINARY_SURFACE_MATERIAL} from '../../../preview/scenePreviewer'; 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 * as SceneGraph from '../../../../../../modules/scene/sceneGraph';
import datumConsumingOperation from '../datumConsumingOperation'; import datumConsumingOperation from '../datumConsumingOperation';
import {assignBooleanParams} from '../booleanOptionHelper'; import {assignBooleanParams} from '../booleanOptionHelper';

View file

@ -1,4 +1,4 @@
import CSys from '../../../math/csys'; import CSys from '../../../../../modules/math/csys';
import {MDatum} from '../../model/mdatum'; import {MDatum} from '../../model/mdatum';
import spatialCurveOpSchema from './spatialCurveOpSchema'; import spatialCurveOpSchema from './spatialCurveOpSchema';

View file

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import Window, {WindowControlButton} from 'ui/components/Window';
import Stack from 'ui/components/Stack'; import Stack from 'ui/components/Stack';
import Button from 'ui/components/controls/Button'; import Button from 'ui/components/controls/Button';
import ButtonGroup from 'ui/components/controls/ButtonGroup'; import ButtonGroup from 'ui/components/controls/ButtonGroup';
@ -9,8 +8,7 @@ import CadError from '../../../../utils/errors';
import {FormContext} from './form/Form'; import {FormContext} from './form/Form';
import connect from 'ui/connect'; import connect from 'ui/connect';
import {combine} from 'lstream'; import {combine} from 'lstream';
import {DocumentationTopic$} from "../../../../../../modules/doc/DocumentationWindow"; import {GenericWizard} from "ui/components/GenericWizard";
import {IoMdHelp} from "react-icons/io";
@connect((streams, props) => combine(props.context.workingRequest$, props.context.state$) @connect((streams, props) => combine(props.context.workingRequest$, props.context.state$)
.map(([workingRequest, state]) => ({ .map(([workingRequest, state]) => ({
@ -56,42 +54,31 @@ export default class Wizard extends React.Component {
let Form = operation.form; let Form = operation.form;
const error = this.props.error; const error = this.props.error;
return <Window initWidth={250} return <GenericWizard
initLeft={left || 15} left={left}
title={title} title={title}
onClose={this.cancel} onClose={this.cancel}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
setFocus={this.focusFirstInput} setFocus={this.focusFirstInput}
className='Wizard mid-typography' className='Wizard'
data-operation-id={operation.id} data-operation-id={operation.id}
controlButtons={<> topicId={operation.id}
<WindowControlButton title='help' onClick={(e) => DocumentationTopic$.next({ onCancel={this.cancel}
topic: operation.id, onOK={this.onOK}
x: e.pageX + 40, infoText={error && <div className={ls.errorMessage}>
y: e.pageY {CadError.ALGORITHM_ERROR_KINDS.includes(error.kind) && <span>
})}> performing operation with current parameters leads to an invalid object
<IoMdHelp /> (self-intersecting / zero-thickness / complete degeneration or unsupported cases)
</WindowControlButton> </span>}
</>}> {error.code && <div className={ls.errorCode}>{error.code}</div>}
{error.userMessage && <div className={ls.userErrorMessage}>{error.userMessage}</div>}
{!error.userMessage && <div>internal error processing operation, check the log</div>}
</div>}
>
<FormContext.Provider value={formContext}> <FormContext.Provider value={formContext}>
<Form/> <Form/>
</FormContext.Provider> </FormContext.Provider>
<Stack> </GenericWizard>;
<ButtonGroup>
<Button className='dialog-cancel' onClick={this.cancel}>Cancel</Button>
<Button className='dialog-ok' type='accent' onClick={this.onOK}>OK</Button>
</ButtonGroup>
{error && <div className={ls.errorMessage}>
{CadError.ALGORITMTHM_ERROR_KINDS.includes(error.kind) && <span>
performing operation with current parameters leads to an invalid object
(self-intersecting / zero-thickness / complete degeneration or unsupported cases)
</span>}
{error.code && <div className={ls.errorCode}>{error.code}</div>}
{error.userMessage && <div className={ls.userErrorMessage}>{error.userMessage}</div>}
{!error.userMessage && <div>internal error processing operation, check the log</div>}
</div>}
</Stack>
</Window>;
} }
onKeyDown = e => { onKeyDown = e => {

View file

@ -181,6 +181,12 @@ function addGlobalDebugActions({viewer, cadScene, cadRegistry}) {
AddSurfaceNormal: (surface) => { AddSurfaceNormal: (surface) => {
__DEBUG__.AddNormal(surface.pointInMiddle(), surface.normalInMiddle()); __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) => { AddTessDump: (triangles, color) => {
const vec = arr => new THREE.Vector3().fromArray(arr); const vec = arr => new THREE.Vector3().fromArray(arr);
color = color || 0xffffff; color = color || 0xffffff;

View file

@ -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(<button key='faceParallel' onClick={() => faceParallel(ctx, faceSelection)}>Face Parallel</button>);
}
return <Dialog initRight={50} title='AVAILABLE ACTIONS' onClose={() => {}}>
{actions}
</Dialog>;
}
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),
]
}

View file

@ -4,7 +4,6 @@ import connect from 'ui/connect';
import Fa from 'ui/components/Fa'; import Fa from 'ui/components/Fa';
import {toIdAndOverrides} from '../../actions/actionRef'; import {toIdAndOverrides} from '../../actions/actionRef';
import {isMenuAction} from '../menu/menuPlugin'; import {isMenuAction} from '../menu/menuPlugin';
import {combine} from 'lstream';
import {menuAboveElementHint} from '../menu/menuUtils'; import {menuAboveElementHint} from '../menu/menuUtils';
import {useStream} from "../../../../../modules/ui/effects"; import {useStream} from "../../../../../modules/ui/effects";
import {ActionButtonBehavior} from "../../actions/ActionButtonBehavior"; import {ActionButtonBehavior} from "../../actions/ActionButtonBehavior";
@ -44,11 +43,13 @@ const RightGroup = connect(streams => streams.ui.controlBars.right.map(actions =
function ConnectedActionButton(props) { function ConnectedActionButton(props) {
const actionId = props.actionId; 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; return null;
} }
const [actionAppearance, actionState] = stream;
return <ActionButtonBehavior actionId={actionId}> return <ActionButtonBehavior actionId={actionId}>
{behaviourProps => <ActionButton {...behaviourProps} {...actionAppearance} {...actionState} {...props} />} {behaviourProps => <ActionButton {...behaviourProps} {...actionAppearance} {...actionState} {...props} />}

View file

@ -67,11 +67,11 @@ function ActionButton({label, icon, icon96, icon32, cssIcons, symbol, size, noLa
export function ConnectedActionButton(props) { export function ConnectedActionButton(props) {
const actionId = props.actionId; const actionId = props.actionId;
const stream = useStream(ctx => combine(ctx.streams.action.appearance[actionId], ctx.streams.action.state[actionId])); const actionAppearance = useStream(ctx => ctx.streams.action.appearance[actionId]);
if (!stream) { const actionState = useStream(ctx => ctx.streams.action.state[actionId]);
if (!actionAppearance || !actionState) {
return null; return null;
} }
const [actionAppearance, actionState] = stream;
return<ActionButtonBehavior actionId={actionId}> return<ActionButtonBehavior actionId={actionId}>
{behaviourProps => <ActionButton {...behaviourProps} {...actionAppearance} {...actionState} {...props} />} {behaviourProps => <ActionButton {...behaviourProps} {...actionAppearance} {...actionState} {...props} />}

View file

@ -31,7 +31,6 @@ export default function UISystem({children, ...props}) {
<MenuHolder/> <MenuHolder/>
<ActionInfo/> <ActionInfo/>
{children} {children}
<ContributedComponents/>
<Scope><DocumentationWindow/></Scope> <Scope><DocumentationWindow/></Scope>
</div> </div>
</UISystemContext.Provider>; </UISystemContext.Provider>;

View file

@ -19,6 +19,8 @@ import {ConstraintEditor} from "../../../sketcher/components/ConstraintEditor";
import SketcherOperationWizard from "../../../sketcher/components/SketcherOperationWizard"; import SketcherOperationWizard from "../../../sketcher/components/SketcherOperationWizard";
import {ToastContainer} from "react-toastify"; import {ToastContainer} from "react-toastify";
import 'react-toastify/dist/ReactToastify.css'; import 'react-toastify/dist/ReactToastify.css';
import {ContributedComponents} from "./ContributedComponents";
import {ModellerContextualActions} from "./ModellerContextualActions";
export default class View3d extends React.Component { export default class View3d extends React.Component {
@ -56,6 +58,8 @@ export default class View3d extends React.Component {
<div className={ls.wizardArea} > <div className={ls.wizardArea} >
<WizardManager/> <WizardManager/>
</div> </div>
<ContributedComponents/>
<Scope><ModellerContextualActions /></Scope>
</div> </div>
<div className={ls.bottomStack}> <div className={ls.bottomStack}>

View file

@ -57,11 +57,14 @@ const ConnectedActionMenu = connect((streams, props) =>
export function ConnectedMenuItem(props) { export function ConnectedMenuItem(props) {
const actionId = props.actionId; 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; return null;
} }
const [actionAppearance, actionState] = stream;
return <ActionButtonBehavior actionId={actionId}> return <ActionButtonBehavior actionId={actionId}>
{behaviourProps => <ActionMenuItem {...behaviourProps} {...actionAppearance} {...actionState} {...props} />} {behaviourProps => <ActionMenuItem {...behaviourProps} {...actionAppearance} {...actionState} {...props} />}

View file

@ -36,7 +36,7 @@ import * as UIConfigPlugin from "../part/uiConfigPlugin";
import * as DebugPlugin from "../debugPlugin"; import * as DebugPlugin from "../debugPlugin";
import * as ExpressionsPlugin from "../expressions/expressionsPlugin"; import * as ExpressionsPlugin from "../expressions/expressionsPlugin";
import * as PartOperationsPlugin from "../part/partOperationsPlugin"; import * as PartOperationsPlugin from "../part/partOperationsPlugin";
import * as LocationPlugin from "../location/locationPlugin";
export default function startApplication(callback) { export default function startApplication(callback) {
@ -75,6 +75,7 @@ export default function startApplication(callback) {
UIConfigPlugin, UIConfigPlugin,
DebugPlugin, DebugPlugin,
PartOperationsPlugin, PartOperationsPlugin,
LocationPlugin,
RemotePartsPlugin, RemotePartsPlugin,
ViewSyncPlugin, ViewSyncPlugin,
WizardSelectionPlugin WizardSelectionPlugin

View file

@ -1,4 +1,4 @@
import {Matrix3} from '../../../math/l3space' import {Matrix3} from '../../../../../modules/math/l3space'
import Vector from 'math/vector'; import Vector from 'math/vector';
import * as math from '../../../math/math' import * as math from '../../../math/math'
import {createShared} from '../../cad-utils' import {createShared} from '../../cad-utils'

View file

@ -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 * as tk from '../../../../ui/toolkit.js'
import {FACE_COLOR} from '../../../cad-utils' import {FACE_COLOR} from '../../../cad-utils'
import {Wizard} from './wizard-commons' import {Wizard} from './wizard-commons'

View file

@ -2,7 +2,7 @@ import * as tk from '../../../../ui/toolkit.js'
import * as workbench from '../workbench' import * as workbench from '../workbench'
import * as cad_utils from '../../../cad-utils' import * as cad_utils from '../../../cad-utils'
import Vector from 'math/vector'; 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' import {OpWizard, IMAGINE_MATERIAL, BASE_MATERIAL} from './wizard-commons'
export function ExtrudeWizard(app, face, invert, initParams) { export function ExtrudeWizard(app, face, invert, initParams) {

View file

@ -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 * as tk from '../../../../ui/toolkit.js'
import {FACE_COLOR} from '../../../cad-utils' import {FACE_COLOR} from '../../../cad-utils'
import {Wizard} from './wizard-commons' import {Wizard} from './wizard-commons'
import {Matrix3} from '../../../../math/l3space' import {Matrix3} from '../../../../../../modules/math/l3space'
export function PlaneWizard(app, initParams) { export function PlaneWizard(app, initParams) {
Wizard.call(this, app.viewer, initParams); Wizard.call(this, app.viewer, initParams);

View file

@ -2,7 +2,7 @@ import * as tk from '../../../../ui/toolkit.js'
import * as workbench from '../workbench' import * as workbench from '../workbench'
import * as cad_utils from '../../../cad-utils' import * as cad_utils from '../../../cad-utils'
import Vector from 'math/vector'; import Vector from 'math/vector';
import {Matrix3, ORIGIN} from '../../../../math/l3space' import {Matrix3, ORIGIN} from '../../../../../../modules/math/l3space'
import {revolveToTriangles} from '../revolve' import {revolveToTriangles} from '../revolve'
import {OpWizard, IMAGINARY_SURFACE_MATERIAL, } from './wizard-commons' import {OpWizard, IMAGINARY_SURFACE_MATERIAL, } from './wizard-commons'

View file

@ -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 * as tk from '../../../../ui/toolkit.js'
import {FACE_COLOR} from '../../../cad-utils' import {FACE_COLOR} from '../../../cad-utils'
import {Wizard} from './wizard-commons' import {Wizard} from './wizard-commons'

View file

@ -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 * as tk from '../../../../ui/toolkit.js'
import {FACE_COLOR} from '../../../cad-utils' import {FACE_COLOR} from '../../../cad-utils'
import {Wizard} from './wizard-commons' import {Wizard} from './wizard-commons'

View file

@ -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<CSys>());
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 <GenericWizard
left={15}
title='PART LOCATION'
onClose={close}
className='location-dialog'
topicId='entity-location'
infoText={null}
onCancel={revertAndClose}
onOK={close} >
<Folder title='Position'>
<Group>
<Field active={false} name='X'>
<Label>X:</Label>
<NumberControl onChange={setX} value={location.origin.x} />
</Field>
<Field active={false} name='Y'>
<Label>Y:</Label>
<NumberControl onChange={setY} value={location.origin.y} />
</Field>
<Field active={false} name='Z'>
<Label>Z:</Label>
<NumberControl onChange={setZ} value={location.origin.z} />
</Field>
</Group>
</Folder>
<Folder title='Rotation'>
<Group>
{/*<Field active={false} name='Azimuth'>*/}
{/* <Label>Axis Azimuth:</Label>*/}
{/* <NumberControl onChange={setAzimuth} value={location.rotationAxisAzimuth / DEG_RAD} />*/}
{/*</Field>*/}
{/*<Field active={false} name='Inclination'>*/}
{/* <Label>Axis Inclination:</Label>*/}
{/* <NumberControl onChange={setInclination} value={location.rotationAxisInclination / DEG_RAD} />*/}
{/*</Field>*/}
{/*<Field active={false} name='Angle'>*/}
{/* <Label>Angle:</Label>*/}
{/* <NumberControl onChange={setAngle} value={location.rotationAxisAngle / DEG_RAD} />*/}
{/*</Field>*/}
</Group>
</Folder>
</GenericWizard>
}

View file

@ -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<EditLocationRequest>;
edit(shell);
}
declare module 'context' {
interface ApplicationContext {
locationService: LocationService;
}
}

View file

@ -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;
}
}

View file

@ -1,5 +1,5 @@
import {MObject, MObjectIdGenerator} from './mobject'; import {MObject, MObjectIdGenerator} from './mobject';
import CSys from "../../math/csys"; import CSys from "math/csys";
import Vector from "math/vector"; import Vector from "math/vector";
export class MDatum extends MObject { export class MDatum extends MObject {
@ -13,9 +13,9 @@ export class MDatum extends MObject {
constructor(csys) { constructor(csys) {
super(MDatum.TYPE, MObjectIdGenerator.next(MDatum.TYPE, 'D')); super(MDatum.TYPE, MObjectIdGenerator.next(MDatum.TYPE, 'D'));
this.csys = csys; this.csys = csys;
this.xAxis = new MDatumAxis(this.id + '/X', this.csys.origin, this.csys.x); 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.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.zAxis = new MDatumAxis(this.id + '/Z', this.csys.origin, this.csys.z, this);
} }
getAxisByLiteral(literal) { getAxisByLiteral(literal) {
@ -33,6 +33,10 @@ export class MDatum extends MObject {
this.yAxis.traverse(callback); this.yAxis.traverse(callback);
this.zAxis.traverse(callback); this.zAxis.traverse(callback);
} }
get parent() {
return null;
}
} }
export class MDatumAxis extends MObject { export class MDatumAxis extends MObject {
@ -40,10 +44,16 @@ export class MDatumAxis extends MObject {
static TYPE = 'datumAxis'; static TYPE = 'datumAxis';
origin: Vector; origin: Vector;
dir: Vector; dir: Vector;
holder: MObject;
constructor(id, origin, dir) { constructor(id, origin, dir, holder) {
super(MDatumAxis.TYPE, id); super(MDatumAxis.TYPE, id);
this.origin = origin; this.origin = origin;
this.dir = dir; this.dir = dir;
this.holder = holder;
}
get parent() {
return this.holder;
} }
} }

View file

@ -1,5 +1,5 @@
import {MObject} from './mobject'; import {MObject} from './mobject';
import {MBrepShell, MShell} from "./mshell"; import {MBrepShell} from "./mshell";
export class MEdge extends MObject { export class MEdge extends MObject {
@ -25,4 +25,8 @@ export class MEdge extends MObject {
} }
return out; return out;
} }
get parent() {
return this.shell;
}
} }

View file

@ -1,12 +1,13 @@
import {MObject} from './mobject'; import {MObject} from './mobject';
import Vector from 'math/vector'; import Vector from 'math/vector';
import {BasisForPlane} from '../../math/l3space'; import {BasisForPlane} from 'math/l3space';
import {MSketchObject} from './msketchObject'; import {MSketchObject} from './msketchObject';
import {EMPTY_ARRAY} from 'gems/iterables'; import {EMPTY_ARRAY} from 'gems/iterables';
import CSys from '../../math/csys'; import CSys from 'math/csys';
import {MSketchLoop} from './mloop'; import {MSketchLoop} from './mloop';
import {ProductionInfo} from './productionInfo'; import {ProductionInfo} from './productionInfo';
import {MBrepShell, MShell} from "./mshell"; import {MBrepShell, MShell} from "./mshell";
import {AssemblyUnitVectorNode} from "../assembly/assembly";
export class MFace extends MObject { export class MFace extends MObject {
@ -18,6 +19,10 @@ export class MFace extends MObject {
sketch: any; sketch: any;
brepFace: any; brepFace: any;
assemblyNodes: {
normal: AssemblyUnitVectorNode
};
private _csys: any; private _csys: any;
private w: number; private w: number;
private _basis: [Vector, Vector, Vector]; private _basis: [Vector, Vector, Vector];
@ -31,7 +36,10 @@ export class MFace extends MObject {
this.surface = surface; this.surface = surface;
this.sketchObjects = []; this.sketchObjects = [];
this.sketchLoops = []; this.sketchLoops = [];
this._csys = csys this._csys = csys;
this.assemblyNodes = {
normal: new AssemblyUnitVectorNode(this, () => this.normal())
};
} }
normal() { normal() {
@ -166,6 +174,10 @@ export class MFace extends MObject {
this.sketchLoops.forEach(i => i.traverse(callback)); this.sketchLoops.forEach(i => i.traverse(callback));
} }
get parent() {
return this.shell;
}
} }
export class MBrepFace extends MFace { export class MBrepFace extends MFace {

View file

@ -10,6 +10,9 @@ export class MLoop extends MObject {
super(MLoop.TYPE, id); super(MLoop.TYPE, id);
} }
get parent() {
return null;
}
} }
export class MSketchLoop extends MLoop { export class MSketchLoop extends MLoop {
@ -24,5 +27,9 @@ export class MSketchLoop extends MLoop {
this.contour = contour; this.contour = contour;
} }
get parent() {
return this.face;
}
} }

View file

@ -1,11 +1,10 @@
export abstract class MObject { export abstract class MObject {
TYPE: string; TYPE: string;
id: string; id: string;
ext: any = {}; ext: any = {};
constructor(TYPE, id) { constructor(TYPE, id) {
this.TYPE = TYPE; this.TYPE = TYPE;
this.id = id; this.id = id;
@ -14,6 +13,16 @@ export abstract class MObject {
traverse(callback: (obj: MObject) => void): void { traverse(callback: (obj: MObject) => void): void {
callback(this); callback(this);
} }
abstract get parent();
get root(): MObject {
let obj = this;
while (obj.parent) {
obj = obj.parent;
}
return obj;
}
} }
export const MObjectIdGenerator = { export const MObjectIdGenerator = {
@ -50,3 +59,4 @@ export const MObjectIdGenerator = {
} }
}; };

View file

@ -16,5 +16,8 @@ export class MOpenFaceShell extends MShell {
get face() { get face() {
return this.faces[0]; return this.faces[0];
} }
get parent() {
return null;
}
} }

View file

@ -1,8 +1,11 @@
import {MObject, MObjectIdGenerator} from './mobject'; import {MObject, MObjectIdGenerator} from './mobject';
import {MBrepFace, MFace} from './mface'; import {MBrepFace} from './mface';
import {MEdge} from './medge'; import {MEdge} from './medge';
import {MVertex} from './mvertex'; 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 { export class MShell extends MObject {
@ -15,8 +18,20 @@ export class MShell extends MObject {
edges = []; edges = [];
vertices = []; vertices = [];
location$: StateStream<CSys> = state(CSys.origin());
locationMatrix$ = this.location$.map((csys: CSys) => csys.outTransformation).remember();
assemblyNodes: {
location: AssemblyCSysNode
};
constructor() { 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 { traverse(callback: (obj: MObject) => void): void {
@ -25,6 +40,10 @@ export class MShell extends MObject {
this.edges.forEach(i => i.traverse(callback)); this.edges.forEach(i => i.traverse(callback));
this.vertices.forEach(i => i.traverse(callback)); this.vertices.forEach(i => i.traverse(callback));
} }
get parent() {
return null;
}
} }
export class MBrepShell extends MShell { export class MBrepShell extends MShell {

View file

@ -15,4 +15,8 @@ export class MSketchObject extends MObject {
this.construction = false; this.construction = false;
} }
get parent() {
return this.face;
}
} }

View file

@ -13,4 +13,8 @@ export class MVertex extends MObject {
this.brepVertex = brepVertex; this.brepVertex = brepVertex;
} }
get parent() {
return this.shell;
}
} }

View file

@ -11,7 +11,7 @@ import {SelectionView} from "../dom/components/SelectionView";
import {GrSelect} from "react-icons/gr"; import {GrSelect} from "react-icons/gr";
export const STANDARD_MODE_HEADS_UP_TOOLBAR = ['DATUM_CREATE', 'PLANE', 'EditFace', 'EXTRUDE', 'CUT', 'REVOLVE', 'LOFT', 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}) { export function activate({services, streams}) {
streams.ui.controlBars.left.value = ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'menu.views', 'Donate', 'GitHub']; streams.ui.controlBars.left.value = ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'menu.views', 'Donate', 'GitHub'];

View file

@ -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 * as vec from 'math/vec'
import Vector from 'math/vector'; import Vector from 'math/vector';
import BrepBuilder from '../brep/brep-builder' 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 {closestToCurveParam} from '../brep/geom/curves/closestPoint';
import NurbsSurface from '../brep/geom/surfaces/nurbsSurface'; import NurbsSurface from '../brep/geom/surfaces/nurbsSurface';
import DatumObject3D from './craft/datum/datumObject'; 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"; import {createOctreeFromSurface, sphereOctree, traverseOctree} from "../../../modules/voxels/octree";
export function runSandbox({bus, services, services: { viewer, cadScene, cadRegistry, exposure, exposure: {addShellOnScene} }}) { export function runSandbox({bus, services, services: { viewer, cadScene, cadRegistry, exposure, exposure: {addShellOnScene} }}) {

View file

@ -1,4 +1,4 @@
import {AXIS} from '../../math/l3space' import {AXIS} from '../../../../modules/math/l3space'
import {createArrow} from 'scene/objects/auxiliary'; import {createArrow} from 'scene/objects/auxiliary';
import Vector from 'math/vector'; import Vector from 'math/vector';
import {OnTopOfAll} from 'scene/materialMixins'; import {OnTopOfAll} from 'scene/materialMixins';

View file

@ -69,6 +69,7 @@ export function activate(context) {
domElement.addEventListener('mousedown', mousedown, false); domElement.addEventListener('mousedown', mousedown, false);
domElement.addEventListener('mouseup', mouseup, false); domElement.addEventListener('mouseup', mouseup, false);
domElement.addEventListener('dblclick', mousedblclick, false);
let mouseState = { let mouseState = {
startX: 0, startX: 0,
@ -93,6 +94,10 @@ export function activate(context) {
} }
} }
function mousedblclick(e) {
handleSolidPick(e);
}
function setPickHandler(handler) { function setPickHandler(handler) {
pickHandler = handler || defaultHandler; pickHandler = handler || defaultHandler;
services.marker.clear(); services.marker.clear();
@ -136,8 +141,7 @@ export function activate(context) {
function handleSolidPick(e) { function handleSolidPick(e) {
let pickResults = services.viewer.raycast(e, services.cadScene.workGroup.children); let pickResults = services.viewer.raycast(e, services.cadScene.workGroup.children);
traversePickResults(e, pickResults, PICK_KIND.FACE, (sketchFace) => { traversePickResults(e, pickResults, PICK_KIND.FACE, (sketchFace) => {
streams.selection.solid.next([sketchFace.solid]); context.locationService.edit(sketchFace.shell);
services.viewer.render();
return false; return false;
}); });
} }

View file

@ -38,7 +38,7 @@ function sceneSynchronizer({services: {cadScene, cadRegistry, viewer, wizard, ac
if (model instanceof MOpenFaceShell) { if (model instanceof MOpenFaceShell) {
modelView = new OpenFaceShellView(model); modelView = new OpenFaceShellView(model);
} else if (model instanceof MShell) { } else if (model instanceof MShell) {
modelView = new ShellView(model); modelView = new ShellView(model, undefined, viewer);
} else if (model instanceof MDatum) { } else if (model instanceof MDatum) {
modelView = new DatumView(model, viewer, modelView = new DatumView(model, viewer,
wizard.open, wizard.open,

View file

@ -9,7 +9,7 @@ import {Mesh} from 'three';
export class ShellView extends View { export class ShellView extends View {
constructor(shell, skin) { constructor(shell, skin, viewer) {
super(shell); super(shell);
this.material = createSolidMaterial(skin); this.material = createSolidMaterial(skin);
@ -45,6 +45,15 @@ export class ShellView extends View {
SceneGraph.addToGroup(this.edgeGroup, edgeView.rootGroup); SceneGraph.addToGroup(this.edgeGroup, edgeView.rootGroup);
this.edgeViews.push(edgeView); 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) { mark(color) {

View file

@ -1,6 +1,6 @@
import {readBrep} from '../../../brep/io/brepIO'; import {readBrep} from '../../../brep/io/brepIO';
import {MBrepShell} from '../../model/mshell'; import {MBrepShell} from '../../model/mshell';
import CSys from '../../../math/csys'; import CSys from '../../../../../modules/math/csys';
export function readShellEntityFromJson(data, consumed, csys) { export function readShellEntityFromJson(data, consumed, csys) {

View file

@ -1,5 +1,5 @@
import Vector from 'math/vector'; import Vector from 'math/vector';
import {BasisForPlane} from '../../../math/l3space' import {BasisForPlane} from '../../../../../modules/math/l3space'
import DPR from 'dpr' import DPR from 'dpr'
import {setAttribute} from "scene/objectData"; import {setAttribute} from "scene/objectData";
import {genSolidId} from "../../craft/cadRegistryPlugin"; import {genSolidId} from "../../craft/cadRegistryPlugin";

View file

@ -1,6 +1,6 @@
import {DelegatingPanTool} from '../../sketcher/tools/pan'; import {DelegatingPanTool} from '../../sketcher/tools/pan';
import {Matrix4} from 'three/src/math/Matrix4'; 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 {CAMERA_MODE} from '../scene/viewer';
import DPR from 'dpr'; import DPR from 'dpr';
import {SKETCHER_MODE_HEADS_UP_ACTIONS} from "./sketcherUIContrib"; import {SKETCHER_MODE_HEADS_UP_ACTIONS} from "./sketcherUIContrib";

View file

@ -6,7 +6,7 @@ import {LUT} from '../../math/bezier-cubic'
import {distanceAB, isCCW, makeAngle0_360} from '../../math/math' import {distanceAB, isCCW, makeAngle0_360} from '../../math/math'
import {normalizeCurveEnds} from '../../brep/geom/impl/nurbs-ext'; import {normalizeCurveEnds} from '../../brep/geom/impl/nurbs-ext';
import Vector from '../../../../modules/math/vector'; import Vector from '../../../../modules/math/vector';
import {AXIS, ORIGIN} from '../../math/l3space'; import {AXIS, ORIGIN} from '../../../../modules/math/l3space';
const RESOLUTION = 20; const RESOLUTION = 20;

View file

@ -1,5 +1,5 @@
import * as sm from './sketchModel' 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 Vector from 'math/vector';
import {Graph} from '../../math/graph' import {Graph} from '../../math/graph'
import * as math from '../../math/math' import * as math from '../../math/math'

View file

@ -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};

View file

@ -9,12 +9,13 @@ import {SketcherAppContext} from "./SketcherAppContext";
export function SketchObjectExplorer() { export function SketchObjectExplorer() {
const [modification, setModification] = useState(0); 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); const ctx = useContext(SketcherAppContext);
if (!stream) {
if (!objects || !selection) {
return null return null
} }
const [objects, selection] = stream;
const select = (obj, exclusive) => { const select = (obj, exclusive) => {
let viewer = ctx.viewer; let viewer = ctx.viewer;

View file

@ -33,8 +33,15 @@ import {
LengthAnnotation, LengthAnnotation,
RadiusLengthAnnotation RadiusLengthAnnotation
} from "../shapes/annotations/angleAnnotation"; } 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 : { PCoincident : {
id: 'PCoincident', id: 'PCoincident',
@ -338,8 +345,8 @@ export const ConstraintDefinitions = {
.monomial(-1); .monomial(-1);
polynomials.push(ellipsePoly()); // polynomials.push(ellipsePoly());
polynomials.push(ellipsePoly()); // polynomials.push(ellipsePoly());
}, },
}, },
@ -814,12 +821,12 @@ export const ConstraintDefinitions = {
pt.visitParams(callback); 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(-x).monomial().term(px, POW_1_FN));
polynomials.push(new Polynomial(-y).monomial().term(py, 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.x = pt.x + '';
constants.y = pt.y + ''; constants.y = pt.y + '';
} }
@ -940,11 +947,51 @@ const bezier3Polynomial = (p, t, p0, p1, p2, p3) => new Polynomial()
.monomial(-1) .monomial(-1)
.term(p, POW_1_FN); .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 { export class AlgNumConstraint {
static Counter = 0; 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.id = schema.id + ':' + (AlgNumConstraint.Counter ++); // only for debug purposes - not persisted
this.objects = objects; this.objects = objects;
this.constants = constants; this.constants = constants;
@ -958,18 +1005,6 @@ export class AlgNumConstraint {
this.schema.defineParamsScope(this.objects, p => this.params.push(p)); 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) { if (!this.internal && this.schema.createAnnotations) {
this.annotations = this.schema.createAnnotations(this.objects, this); this.annotations = this.schema.createAnnotations(this.objects, this);
} else { } else {
@ -977,8 +1012,8 @@ export class AlgNumConstraint {
} }
} }
collectPolynomials(polynomials) { collectPolynomials(polynomials: Polynomial[]) {
this.schema.collectPolynomials(polynomials, this.params, this.resolvedConstants); this.schema.collectPolynomials(polynomials, this.params, this.resolvedConstants, this.objects);
} }
resolveConstants(expressionResolver) { resolveConstants(expressionResolver) {
@ -988,7 +1023,7 @@ export class AlgNumConstraint {
} }
Object.keys(this.constants).map(name => { Object.keys(this.constants).map(name => {
let def = this.schema.constants[name]; let def = this.schema.constants[name];
let val = this.constants[name]; let val: any = this.constants[name];
val = expressionResolver(val); val = expressionResolver(val);
if (def.type === 'number') { if (def.type === 'number') {
val = parseFloat(val); val = parseFloat(val);
@ -1071,4 +1106,3 @@ export class AlgNumConstraint {
this.constants[key] = value + ''; // only string are allowed here this.constants[key] = value + ''; // only string are allowed here
} }
} }

View file

@ -2,8 +2,10 @@ import {prepare} from "./solver";
import {eqEps} from "../../brep/geom/tolerance"; import {eqEps} from "../../brep/geom/tolerance";
import {Polynomial, POW_1_FN} from "./polynomial"; import {Polynomial, POW_1_FN} from "./polynomial";
import {compositeFn} from "gems/func"; import {compositeFn} from "gems/func";
import {AlgNumConstraint} from "./ANConstraints";
import {SolverParam} from "./solverParam";
const DEBUG = false; const DEBUG = true;
export class AlgNumSubSystem { export class AlgNumSubSystem {
@ -34,6 +36,18 @@ export class AlgNumSubSystem {
stage = null; stage = null;
dof: number = 0;
requiresHardSolve: boolean = false;
polynomialIsolations: Isolation[];
calcVisualLimit: () => number;
expressionResolver: (string) => any;
solveStatus: SolveStatus;
constructor(calcVisualLimit, expressionResolver, stage) { constructor(calcVisualLimit, expressionResolver, stage) {
this.calcVisualLimit = calcVisualLimit; this.calcVisualLimit = calcVisualLimit;
@ -182,7 +196,7 @@ export class AlgNumSubSystem {
}); });
if (DEBUG) { if (DEBUG) {
console.log('reducing system:'); console.log('reducing system(of', this.polynomials.length, '):');
this.polynomials.forEach(p => console.log(p.toString())); this.polynomials.forEach(p => console.log(p.toString()));
} }
@ -502,6 +516,13 @@ export class AlgNumSubSystem {
class Isolation { class Isolation {
polynomials: Polynomial[];
system: AlgNumSubSystem;
beingSolvedParams: Set<SolverParam>;
beingSolvedConstraints: Set<AlgNumConstraint>;
dof: number;
solveStatus: SolveStatus;
numericalSolver: { system; diagnose; solveSystem; error; updateLock };
constructor(polynomials, system) { constructor(polynomials, system) {
this.system = system; this.system = system;
@ -605,3 +626,7 @@ class PolynomialResidual {
} }
interface SolveStatus {
success: boolean;
error: number
}

View file

@ -0,0 +1,12 @@
import {Param} from "../shapes/param";
export interface SolvableObject {
id: string;
}
export interface ISolveStage {
objects: Set<SolvableObject>;
index: number;
}

View file

@ -6,28 +6,6 @@ import {ConstantWrapper, EqualsTo} from './solverConstraints'
import {dog_leg} from '../../math/optim' import {dog_leg} from '../../math/optim'
import {newVector} from 'math/vec'; 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 */ /** @constructor */
function System(constraints) { function System(constraints) {
@ -313,4 +291,4 @@ var solve_lm = function(sys, model, jacobian, rough) {
}; };
}; };
export {Param, prepare} export {prepare}

View file

@ -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;
}
}

View file

@ -3,6 +3,8 @@ import {AlgNumConstraint, ConstraintDefinitions} from "./constr/ANConstraints";
import {AlgNumSubSystem} from "./constr/AlgNumSystem"; import {AlgNumSubSystem} from "./constr/AlgNumSystem";
import {state, stream} from 'lstream'; import {state, stream} from 'lstream';
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {ISolveStage, SolvableObject} from "./constr/solvableObject";
import {Viewer} from "./viewer2d";
export {Constraints, ParametricManager} export {Constraints, ParametricManager}
@ -31,6 +33,8 @@ class ParametricManager {
$constantDefinition = state(''); $constantDefinition = state('');
groundStage = new SolveStage(this); groundStage = new SolveStage(this);
viewer: Viewer;
messageSink: (msg) => void;
constructor(viewer) { constructor(viewer) {
this.viewer = viewer; this.viewer = viewer;
@ -240,7 +244,7 @@ class ParametricManager {
}); });
}; };
_removeObject = (obj, force) => { _removeObject = (obj, force?) => {
if (obj.__disposed) { if (obj.__disposed) {
return; return;
} }
@ -398,10 +402,12 @@ class ParametricManager {
} }
} }
class SolveStage { class SolveStage implements ISolveStage{
generators = new Set(); generators = new Set();
objects = new Set(); objects = new Set<SolvableObject>();
private parametricManager: ParametricManager;
private algNumSystem: AlgNumSubSystem;
constructor(parametricManager) { constructor(parametricManager) {
this.parametricManager = parametricManager; this.parametricManager = parametricManager;
@ -460,6 +466,7 @@ class SolveStage {
solve(rough) { solve(rough) {
this.algNumSystem.solve(rough); this.algNumSystem.solve(rough);
this.generators.forEach(gen => { this.generators.forEach(gen => {
// @ts-ignore
gen.regenerate(this.viewer); gen.regenerate(this.viewer);
}) })
} }

View file

@ -1,15 +1,18 @@
import {Generator} from "../id-generator"; import {Generator} from "../id-generator";
import {Param as SolverParam} from '../constr/solver'; import {SolverParam} from "../constr/solverParam";
export class Param { export class Param {
id: number; id: number;
value: number; value: number;
solverParam: any; solverParam: SolverParam;
private readonly debugSymbol: string; private readonly debugSymbol: string;
normalizer: (number) => any; normalizer: (number) => any;
enforceVisualLimit: boolean = false; enforceVisualLimit: boolean = false;
//penalty function constraints
constraints?: any[];
constructor(value, debugSymbol) { constructor(value, debugSymbol) {
this.id = Generator.genID(); this.id = Generator.genID();
this.value = value; this.value = value;

View file

@ -4,8 +4,9 @@ import {Styles} from "../styles";
import {NoIcon} from "../icons/NoIcon"; import {NoIcon} from "../icons/NoIcon";
import {Layer, Viewer} from "../viewer2d"; import {Layer, Viewer} from "../viewer2d";
import {NOOP} from "gems/func"; 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; ref: string;
id: string; id: string;

View file

@ -9,7 +9,7 @@ import {BasisOrigin} from './shapes/basis-origin';
import Vector from 'math/vector'; import Vector from 'math/vector';
import * as draw_utils from './shapes/draw-utils'; import * as draw_utils from './shapes/draw-utils';
import {Matrix3} from '../math/l3space'; import {Matrix3} from 'math/l3space';
import sketcherStreams, {SketcherStreams} from './sketcherStreams'; import sketcherStreams, {SketcherStreams} from './sketcherStreams';
import {BBox, IO} from "./io"; import {BBox, IO} from "./io";
import {NOOP} from "../../../modules/gems/func"; import {NOOP} from "../../../modules/gems/func";
@ -358,13 +358,14 @@ export class Viewer {
}; };
fit() { fit() {
let count = 0;
const bbox = new BBox(); const bbox = new BBox();
this.accept(obj => { this.accept(obj => {
count ++;
bbox.check(obj); bbox.check(obj);
return true; return true;
}); });
if (!bbox.isValid()) { if (count < 2 || !bbox.isValid()) {
return; return;
} }

View file

@ -22,5 +22,5 @@ CadError.KIND = {
INVALID_PARAMS: 'INVALID_PARAMS' INVALID_PARAMS: 'INVALID_PARAMS'
}; };
CadError.ALGORITMTHM_ERROR_KINDS = ['INTERNAL_ERROR', 'UNSUPPORTED_CASE', 'INVALID_INPUT']; CadError.ALGORITHM_ERROR_KINDS = ['INTERNAL_ERROR', 'UNSUPPORTED_CASE', 'INVALID_INPUT'];