remove intermediate parameter W / vertical constraint

This commit is contained in:
Val Erastov (xibyte) 2020-03-02 22:47:46 -08:00
parent b5d21fa1cb
commit e999181db1
11 changed files with 153 additions and 110 deletions

View file

@ -150,11 +150,10 @@ function initializeSketcherApplication() {
app.dock.views['Dimensions'].node.append(constantTextArea);
startReact(app.viewer);
startReact(app.context);
}
function startReact(viewer) {
const appCtx = createAppContext(viewer)
function startReact(appCtx) {
let reactControls = document.getElementById('react-controls');
reactControls.onkeydown = e => {
@ -167,14 +166,4 @@ function startReact(viewer) {
);
}
function createAppContext(viewer) {
return {
viewer,
ui: {
$constraintEditRequest: stream()
}
};
}
$( () => initializeSketcherApplication() );

View file

@ -12,6 +12,7 @@ export default [
{
id: 'Coincident',
shortName: 'Coincident',
description: 'Point Coincident',
selectionMatcher: (selection, sortedByType) => matchAll(selection, EndPoint, 2),
@ -30,6 +31,7 @@ export default [
},
{
id: 'Tangent',
shortName: 'Tangent',
description: 'Tangent Between Line And Circle',
selectionMatcher: [
@ -51,7 +53,8 @@ export default [
},
{
shortName: 'EqualRadius',
id: 'EqualRadius',
shortName: 'Equal Radius',
description: 'Equal Radius Between Two Circle',
selectionMatcher: selection => {
for (let obj of selection) {
@ -76,7 +79,8 @@ export default [
},
{
shortName: 'EqualLength',
id: 'EqualLength',
shortName: 'Equal Length',
description: 'Equal Length Between Two Segments',
selectionMatcher: selection => matchAll(selection, Segment, 2),
@ -92,6 +96,7 @@ export default [
},
{
id: 'PointOnLine',
shortName: 'Point On Line',
description: 'Point On Line',
selectionMatcher: (selection, sortedByType) => matchTypes(sortedByType, EndPoint, 1, Segment, 1),
@ -105,6 +110,7 @@ export default [
},
{
id: 'Angle',
shortName: 'Angle',
description: 'Angle',
selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 1),
@ -129,6 +135,26 @@ export default [
},
{
id: 'Vertical',
shortName: 'Vertical',
description: 'Vertical',
selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 1),
invoke: ctx => {
const {viewer} = ctx;
const pm = viewer.parametricManager;
viewer.selected.forEach(obj => {
const constr = new AlgNumConstraint(ConstraintDefinitions.Vertical, [obj]);
constr.initConstants();
pm._add(constr);
});
pm.commit();
}
},
{
id: 'AngleBetween',
shortName: 'Angle Between',
description: 'Angle Between Lines',
selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 2),
@ -154,6 +180,7 @@ export default [
},
{
id: 'Perpendicular',
shortName: 'Perpendicular',
description: 'Perpendicularity between two lines',
selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 2),
@ -180,6 +207,7 @@ export default [
},
{
id: 'Length',
shortName: 'Length',
description: 'Segment Length',
selectionMatcher: (selection) => matchAll(selection, Segment, 1),
@ -204,6 +232,7 @@ export default [
},
{
id: 'Lock',
shortName: 'Lock',
description: 'Lock Point',
selectionMatcher: (selection) => matchTypes(selection, EndPoint, 1),
@ -220,6 +249,7 @@ export default [
},
{
id: 'Fillet',
shortName: 'Fillet',
description: 'Add a Fillet',
selectionMatcher: (selection) => {
@ -255,34 +285,6 @@ export default [
}
},
{
shortName: 'Mirror',
description: 'Mirror Objects',
selectionMatcher: selection => isInstanceOf(selection[0], Segment) && selection.length > 1,
invoke: ctx => {
const {viewer} = ctx;
const objects = viewer.selected;
const managedObjects = [];
for (let i = 1; i < objects.length; i++) {
let obj = objects[i];
const copy = obj.copy();
obj.layer.add(copy);
managedObjects.push(copy);
}
ConstraintDefinitions.Mirror.modify(objects, managedObjects);
// const constr = new AlgNumConstraint(ConstraintDefinitions.Mirror, [...objects, ...managedObjects]);
// viewer.parametricManager.addModifier(constr);
}
}
];
function editConstraint(ctx, constraint, onApply) {

View file

@ -5,6 +5,7 @@ export default [
{
id: 'Mirror',
shortName: 'Mirror',
description: 'Mirror Objects',
selectionMatcher: selection => isInstanceOf(selection[0]) && selection.length > 1,

View file

@ -32,4 +32,14 @@ export function matchAvailableActions(selection) {
return matched;
}
//For backward compatibility
export function getActionIfAvailable(actionId, selection, cb) {
matchAvailableActions(selection).forEach(a => {
if (a.id === actionId) {
cb(a);
}
})
}

View file

@ -43,23 +43,21 @@ export const ConstraintDefinitions = {
type: 'boolean',
description: 'whether the circle attached from the opposite side',
initialValue: ([line, circle]) => {
const ang = line.params.ang.get();
const w = line.params.w.get();
return Math.cos(ang) * circle.c.x + Math.sin(ang) * circle.c.y < w;
return line.nx * circle.c.x + line.ny * circle.c.y < line.w;
}
}
},
defineParamsScope: ([segment, circle], callback) => {
callback(segment.params.ang);
callback(segment.params.w);
segment.a.visitParams(callback);
circle.c.visitParams(callback);
callback(circle.r);
},
collectPolynomials: (polynomials, [ang, w, cx, cy, r], {inverted}) => {
polynomials.push(tangentLCPolynomial(ang, w, cx, cy, r, inverted));
collectPolynomials: (polynomials, [ang, ax, ay, cx, cy, r], {inverted}) => {
polynomials.push(tangentLCPolynomial(ang, ax, ay, cx, cy, r, inverted));
},
},
@ -69,15 +67,11 @@ export const ConstraintDefinitions = {
defineParamsScope: ([pt, segment], callback) => {
pt.visitParams(callback);
segment.a.visitParams(callback);
callback(segment.params.ang);
callback(segment.params.w);
},
collectResiduals: (residuals, params) => {
residuals.push([R_PointOnLine, params, []]);
},
collectPolynomials: (polynomials, [x, y, ang, w]) => {
collectPolynomials: (polynomials, [x, y, ax, ay, ang]) => {
polynomials.push(new Polynomial(0)
.monomial(1)
.term(x, POW_1_FN)
@ -85,8 +79,12 @@ export const ConstraintDefinitions = {
.monomial(1)
.term(y, POW_1_FN)
.term(ang, SIN_FN)
.monomial(1)
.term(ax, POW_1_FN)
.term(ang, SIN_FN)
.monomial(-1)
.term(w, POW_1_FN)
.term(ay, POW_1_FN)
.term(ang, COS_FN)
);
},
@ -141,7 +139,7 @@ export const ConstraintDefinitions = {
type: 'number',
description: 'line angle',
initialValue: ([seg]) => seg.getAngleFromNormal(),
transform: degree => ( (degree + 90) % 360 ) * DEG_RAD
transform: degree => ( (degree) % 360 ) * DEG_RAD
}
},
@ -158,6 +156,31 @@ export const ConstraintDefinitions = {
}
},
Vertical: {
id: 'Vertical',
name: 'Line Verticality',
constants: {
angle: {
readOnly: true,
type: 'number',
description: 'line angle',
initialValue: ([seg]) => {
const angleFromNormal = seg.getAngleFromNormal();
return Math.abs(270 - angleFromNormal) > Math.abs(90 - angleFromNormal) ? 90 : 270;
},
transform: degree => ( (degree ) % 360 ) * DEG_RAD
}
},
defineParamsScope: (objs, cb) => {
ConstraintDefinitions.Angle.defineParamsScope(objs, cb);
},
collectPolynomials: (polynomials, params, constants) => {
ConstraintDefinitions.Angle.collectPolynomials(polynomials, params, constants);
}
},
AngleBetween: {
id: 'AngleBetween',
name: 'Angle Between Two Lines',
@ -255,16 +278,8 @@ export const ConstraintDefinitions = {
},
collectPolynomials: (polynomials, [ang, t, x1, y1, x2, y2]) => {
// v = [sin(ang), - cos(ang)]
// v * t = pt2 - pt1
//sin(ang) * t - x2 + x1
//-cos(ang) * t - y2 + y1
polynomials.push(new Polynomial().monomial() .term(ang, SIN_FN).term(t, POW_1_FN).monomial(-1).term(x2, POW_1_FN).monomial(1).term(x1, POW_1_FN));
polynomials.push(new Polynomial().monomial(-1).term(ang, COS_FN).term(t, POW_1_FN).monomial(-1).term(y2, POW_1_FN).monomial(1).term(y1, POW_1_FN));
polynomials.push(new Polynomial().monomial(1).term(x1, POW_1_FN).monomial(1).term(ang, COS_FN).term(t, POW_1_FN).monomial(-1).term(x2, POW_1_FN));
polynomials.push(new Polynomial().monomial(1).term(y1, POW_1_FN).monomial(1).term(ang, SIN_FN).term(t, POW_1_FN).monomial(-1).term(y2, POW_1_FN));
},
},
@ -369,16 +384,16 @@ export const ConstraintDefinitions = {
defineParamsScope: ([l1, l2, arc], callback) => {
callback(l1.params.ang);
callback(l1.params.w);
l1.a.visitParams(callback);
callback(l2.params.ang);
callback(l2.params.w);
l2.a.visitParams(callback);
arc.c.visitParams(callback);
callback(arc.r);
},
collectPolynomials: (polynomials, [ang1, w1, ang2, w2, cx, cy, r], {inverted1, inverted2}) => {
polynomials.push(tangentLCPolynomial(ang1, w1, cx, cy, r, inverted1));
polynomials.push(tangentLCPolynomial(ang2, w2, cx, cy, r, inverted2));
collectPolynomials: (polynomials, [ang1, ax1, ay1, ang2, ax2, ay2, cx, cy, r], {inverted1, inverted2}) => {
polynomials.push(tangentLCPolynomial(ang1, ax1, ay1, cx, cy, r, inverted1));
polynomials.push(tangentLCPolynomial(ang2, ax2, ay2, cx, cy, r, inverted2));
},
},
@ -415,7 +430,7 @@ export const ConstraintDefinitions = {
};
function tangentLCPolynomial(ang, w, cx, cy, r, inverted) {
function tangentLCPolynomial(ang, ax, ay, cx, cy, r, inverted) {
return new Polynomial(0)
.monomial(1)
.term(cx, POW_1_FN)
@ -423,8 +438,12 @@ function tangentLCPolynomial(ang, w, cx, cy, r, inverted) {
.monomial(1)
.term(cy, POW_1_FN)
.term(ang, SIN_FN)
.monomial(1)
.term(ax, POW_1_FN)
.term(ang, SIN_FN)
.monomial(-1)
.term(w, POW_1_FN)
.term(ay, POW_1_FN)
.term(ang, COS_FN)
.monomial(- (inverted ? -1 : 1))
.term(r, POW_1_FN);
}

View file

@ -24,6 +24,8 @@ export class AlgNumSubSystem {
conflicting = new Set();
redundant = new Set();
interactiveParams = new Set();
snapshot = new Map();
constructor() {
@ -126,6 +128,7 @@ export class AlgNumSubSystem {
this.eliminatedParams.clear();
this.polyToConstr.clear();
this.paramToIsolation.clear();
this.interactiveParams.clear();
}
evaluatePolynomials() {
@ -182,7 +185,14 @@ export class AlgNumSubSystem {
this.polynomials[i] = null;
} else if (polynomial.monomials.length === 2 && polynomial.isLinear) {
const [m1, m2] = polynomial.monomials;
let [m1, m2] = polynomial.monomials;
if (this.interactiveParams.has(m1.linearParam)) {
const t = m1;
m1 = m2;
m2 = t;
}
let p1 = m1.linearParam;
let p2 = m2.linearParam;
@ -240,9 +250,10 @@ export class AlgNumSubSystem {
}
prepare() {
prepare(interactiveObjects = []) {
this.reset();
interactiveObjects.forEach(obj => obj.visitParams(p => this.interactiveParams.add(p)));
this.validConstraints(c => c.params.forEach(p => p.normalizer && p.set(p.normalizer(p.get()))));

View file

@ -153,8 +153,8 @@ class ParametricManager {
this.refresh();
};
prepare() {
this.algNumSystem.prepare();
prepare(interactiveObjects) {
this.algNumSystem.prepare(interactiveObjects);
}
solve(rough) {

View file

@ -18,7 +18,6 @@ export class Segment extends SketchObject {
this.children.push(a, b);
this.params = {
ang: new Param(undefined),
w: new Param(undefined),
t: new Param(undefined)
};
this.params.ang.normalizer = makeAngle0_360;
@ -31,16 +30,24 @@ export class Segment extends SketchObject {
}
get w() {
return this.params.w.get();
return this.nx*this.a.x + this.ny*this.a.y;
}
get t() {
return this.params.t.get();
}
get nx() {
return -Math.sin(this.ang);
}
get ny() {
return Math.cos(this.ang);
}
getAngleFromNormal() {
const degrees = this.params.ang.get() / DEG_RAD;
return (degrees + 360 - 90) % 360;
return (degrees + 360) % 360;
}
syncGeometry() {
@ -48,24 +55,20 @@ export class Segment extends SketchObject {
const dy = this.b.y - this.a.y;
const l = Math.sqrt(dx*dx + dy*dy);
let nx = (- dy / l) || 0;
let ny = (dx / l) || 0;
let ux = (dx / l) || 0;
let uy = (dy / l) || 0;
let ang = Math.atan2(ny, nx);
let ang = Math.atan2(uy, ux);
this.params.ang.set(makeAngle0_360(ang||0));
this.params.w.set(nx * this.a.x + ny * this.a.y);
this.params.t.set(l);
}
stabilize(viewer) {
this.syncGeometry();
const c1 = new AlgNumConstraint(ConstraintDefinitions.PointOnLine, [this.a, this]);
const c2 = new AlgNumConstraint(ConstraintDefinitions.Polar, [this, this.a, this.b]);
c1.internal = true;
c2.internal = true;
viewer.parametricManager._add(c1);
viewer.parametricManager._add(c2);
const c = new AlgNumConstraint(ConstraintDefinitions.Polar, [this, this.a, this.b]);
c.internal = true;
viewer.parametricManager._add(c);
}
recoverIfNecessary() {
@ -83,7 +86,7 @@ export class Segment extends SketchObject {
this.a.visitParams(callback);
this.b.visitParams(callback);
callback(this.params.ang);
callback(this.params.w);
callback(this.params.t);
}
normalDistance(aim) {
@ -116,15 +119,14 @@ export class Segment extends SketchObject {
translateImpl(dx, dy) {
this.a.translate(dx, dy);
this.b.translate(dx, dy);
this.params.w.set(Math.cos(this.ang) * this.a.x + Math.sin(this.ang) * this.a.y);
}
drawImpl(ctx, scale) {
let ang = this.params.ang.get();
let nx = Math.cos(ang) ;
let ny = Math.sin(ang) ;
let w = this.params.w.get();
let nx = -Math.sin(ang);
let ny = Math.cos(ang);
let w = this.w;
ctx.save();
draw_utils.SetStyle(Styles.CONSTRUCTION_OF_OBJECT, ctx, scale );

View file

@ -15,15 +15,15 @@ import {OffsetTool} from './tools/offset'
import {ReferencePointTool} from './tools/origin'
import {InputManager} from './input-manager'
import genSerpinski from '../utils/genSerpinski';
import context from 'context';
import ReactDOM from "react-dom";
import React from "react";
import {SketcherApp} from "./components/SketcherApp";
import {getActionIfAvailable} from "./actions";
import {stream} from "../../../modules/lstream";
function App2D() {
var app = this;
this.viewer = new Viewer(document.getElementById('viewer'), IO);
this.context = createAppContext(this.viewer);
this.winManager = new ui.WinManager();
this.inputManager = new InputManager(this);
@ -198,11 +198,11 @@ function App2D() {
});
this.registerAction('coincident', "Coincident", function () {
app.viewer.parametricManager.coincident(app.viewer.selected);
getActionIfAvailable('Coincident', app.viewer.selected, action => action.invoke(app.context));
});
this.registerAction('verticalConstraint', "Vertical Constraint", function () {
app.viewer.parametricManager.vertical(app.viewer.selected);
getActionIfAvailable('Vertical', app.viewer.selected, action => action.invoke(app.context));
});
this.registerAction('horizontalConstraint', "Horizontal Constraint", function () {
@ -473,6 +473,16 @@ App2D.prototype.handleTerminalInput = function(commandStr) {
}
};
function createAppContext(viewer) {
return {
viewer,
ui: {
$constraintEditRequest: stream()
}
};
}
App2D.STORAGE_PREFIX = "TCAD.projects.";
export default App2D;
export default App2D;

View file

@ -26,7 +26,7 @@ export class DragTool extends Tool {
this.obj.translate(dx, dy);
// this.viewer.parametricManager.setConstantsFromGeometry(this.obj);
if (!Tool.dumbMode(e)) {
this.viewer.parametricManager.prepare();
// this.viewer.parametricManager.prepare();
this.viewer.parametricManager.solve(true);
}
this.viewer.refresh();
@ -37,8 +37,7 @@ export class DragTool extends Tool {
this.origin.y = e.offsetY;
this.viewer.screenToModel2(e.offsetX, e.offsetY, this._point);
this.viewer.parametricManager.prepare();
this.viewer.parametricManager.prepare([this.obj]);
}
mouseup(e) {

View file

@ -292,12 +292,12 @@ class Viewer {
};
showBounds(x1, y1, x2, y2, offset) {
var dx = Math.max(x2 - x1, 1);
var dy = Math.max(y2 - y1, 1);
if (this.canvas.width > this.canvas.height) {
this.scale = this.canvas.height / dy;
const dx = Math.max(x2 - x1, 1);
const dy = Math.max(y2 - y1, 1);
if (dx > dy) {
this.scale = this.canvas.height / dx;
} else {
this.scale = this.canvas.width / dx;
this.scale = this.canvas.width / dy;
}
this.translate.x = -x1 * this.scale;
this.translate.y = -y1 * this.scale;