mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-08 01:13:27 +01:00
offset tool
This commit is contained in:
parent
bb6ff8610b
commit
d9e61e273e
5 changed files with 140 additions and 72 deletions
|
|
@ -17,6 +17,8 @@ function createByConstraintName(name, params, values) {
|
|||
return new Perpendicular(params);
|
||||
case "parallel":
|
||||
return new Parallel(params);
|
||||
case "P2LDistanceSigned":
|
||||
return new P2LDistanceSigned(params, values[0]);
|
||||
case "P2LDistance":
|
||||
return new P2LDistance(params, values[0]);
|
||||
case "P2LDistanceV":
|
||||
|
|
@ -140,9 +142,8 @@ function ConstantWrapper(constr, mask) {
|
|||
|
||||
this.params = [];
|
||||
this.grad = [];
|
||||
var j;
|
||||
|
||||
for (j = 0; j < constr.params.length; j++) {
|
||||
for (let j = 0; j < constr.params.length; j++) {
|
||||
if (!mask[j]) {
|
||||
this.params.push(constr.params[j]);
|
||||
}
|
||||
|
|
@ -157,7 +158,7 @@ function ConstantWrapper(constr, mask) {
|
|||
fillArray(this.grad, 0, this.grad.length, 0);
|
||||
constr.gradient(this.grad);
|
||||
var jj = 0;
|
||||
for (j = 0; j < mask.length; j++) {
|
||||
for (let j = 0; j < mask.length; j++) {
|
||||
if (!mask[j]) {
|
||||
out[jj ++] = this.grad[j];
|
||||
}
|
||||
|
|
@ -212,6 +213,29 @@ function Diff(params, value) {
|
|||
};
|
||||
}
|
||||
|
||||
function P2LDistanceSigned(params, value) {
|
||||
|
||||
const TX = 0;
|
||||
const TY = 1;
|
||||
const AX = 2;
|
||||
const AY = 3;
|
||||
const BX = 4;
|
||||
const BY = 5;
|
||||
|
||||
this.params = params;
|
||||
this.value = value;
|
||||
|
||||
this.error = function() {
|
||||
const tx = params[TX].get(), ax = params[AX].get(), bx = params[BX].get();
|
||||
const ty = params[TY].get(), ay = params[AY].get(), by = params[BY].get();
|
||||
const d = Math.sqrt(sq(by - ay) + sq(bx - ax));
|
||||
|
||||
return (-(by - ay) * (tx - ax) ) / d + ((bx - ax) * (ty - ay)) / d - this.value;
|
||||
};
|
||||
|
||||
this.gradient = NumericGradient;
|
||||
}
|
||||
|
||||
function P2LDistance(params, distance) {
|
||||
|
||||
this.params = params;
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ function Param(id, value, readOnly) {
|
|||
Param.prototype.reset = function(value) {
|
||||
this.set(value);
|
||||
this.j = -1;
|
||||
this.aux = false;
|
||||
};
|
||||
|
||||
Param.prototype.set = function(value) {
|
||||
if (this.aux) return;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
|
|
@ -187,28 +189,27 @@ System.prototype.getValues = function() {
|
|||
return values;
|
||||
};
|
||||
|
||||
var wrapAux = function(constrs, locked) {
|
||||
|
||||
var i, lockedSet = {};
|
||||
for (i = 0; i < locked.length; i++) {
|
||||
lockedSet[locked[i].j] = true;
|
||||
}
|
||||
|
||||
for (i = 0; i < constrs.length; i++) {
|
||||
var c = constrs[i];
|
||||
var mask = [];
|
||||
var needWrap = false;
|
||||
for (var j = 0; j < c.params.length; j++) {
|
||||
var param = c.params[j];
|
||||
mask[j] = lockedSet[param.j] === true;
|
||||
function wrapAux(constrs) {
|
||||
for (let i = 0; i < constrs.length; i++) {
|
||||
const c = constrs[i];
|
||||
const mask = [];
|
||||
let needWrap = false;
|
||||
for (let j = 0; j < c.params.length; j++) {
|
||||
const param = c.params[j];
|
||||
mask[j] = param.aux === true;
|
||||
needWrap = needWrap || mask[j];
|
||||
}
|
||||
if (needWrap) {
|
||||
var wrapper = new ConstantWrapper(c, mask);
|
||||
constrs[i] = wrapper;
|
||||
constrs[i] = new ConstantWrapper(c, mask);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (let constr of constrs) {
|
||||
if (constr.params.length == 0) {
|
||||
return constrs.filter(c => c.params.length != 0);
|
||||
}
|
||||
}
|
||||
return constrs;
|
||||
}
|
||||
|
||||
var lock2Equals2 = function(constrs, locked) {
|
||||
var _locked = [];
|
||||
|
|
@ -233,18 +234,17 @@ var diagnose = function(sys) {
|
|||
}
|
||||
};
|
||||
|
||||
var prepare = function(constrs, locked, aux, alg) {
|
||||
var prepare = function(constrs, locked) {
|
||||
|
||||
var simpleMode = true;
|
||||
if (!simpleMode) {
|
||||
var lockingConstrs = lock2Equals2(constrs, locked);
|
||||
Array.prototype.push.apply( constrs, lockingConstrs );
|
||||
}
|
||||
|
||||
|
||||
constrs = wrapAux(constrs);
|
||||
var sys = new System(constrs);
|
||||
|
||||
wrapAux(constrs, aux);
|
||||
|
||||
var model = function(point) {
|
||||
sys.setParams(point);
|
||||
return sys.getValues();
|
||||
|
|
|
|||
|
|
@ -485,29 +485,23 @@ ParametricManager.prototype.__getSolveData = function(constraints, out) {
|
|||
return out;
|
||||
};
|
||||
|
||||
ParametricManager.prototype.solve = function() {
|
||||
var solver = this.prepare([]);
|
||||
ParametricManager.prototype.solve = function(lock, extraConstraints, disabledObjects) {
|
||||
const solver = this.prepare(lock, extraConstraints, disabledObjects);
|
||||
solver.solve(false);
|
||||
solver.sync();
|
||||
};
|
||||
|
||||
ParametricManager.prototype.solveWithLock = function(lock) {
|
||||
var solver = this.prepare(lock);
|
||||
solver.solve(false);
|
||||
solver.sync();
|
||||
ParametricManager.prototype.prepare = function(locked, extraConstraints, disabledObjects) {
|
||||
return this._prepare(locked, this.subSystems, extraConstraints, disabledObjects);
|
||||
};
|
||||
|
||||
ParametricManager.prototype.prepare = function(locked, extraConstraints) {
|
||||
return this._prepare(locked, this.subSystems, extraConstraints);
|
||||
};
|
||||
|
||||
ParametricManager.prototype._prepare = function(locked, subSystems, extraConstraints) {
|
||||
ParametricManager.prototype._prepare = function(locked, subSystems, extraConstraints, disabledObjects) {
|
||||
var solvers = [];
|
||||
for (var i = 0; i < subSystems.length; i++) {
|
||||
solvers.push(this.prepareForSubSystem(locked, subSystems[i].constraints, extraConstraints));
|
||||
solvers.push(this.prepareForSubSystem(locked, subSystems[i].constraints, extraConstraints, disabledObjects));
|
||||
}
|
||||
if (subSystems.length == 0 && locked.length != 0) {
|
||||
solvers.push(this.prepareForSubSystem(locked, [], extraConstraints));
|
||||
solvers.push(this.prepareForSubSystem(locked, [], extraConstraints, disabledObjects));
|
||||
}
|
||||
return {
|
||||
solvers : solvers,
|
||||
|
|
@ -545,9 +539,9 @@ ParametricManager.prototype._prepare = function(locked, subSystems, extraConstra
|
|||
}
|
||||
};
|
||||
|
||||
ParametricManager.isAux = function(obj) {
|
||||
ParametricManager.isAux = function(obj, disabledObjects) {
|
||||
while (!!obj) {
|
||||
if (!!obj.aux) {
|
||||
if (!!obj.aux || (disabledObjects !== undefined && disabledObjects.has(obj))) {
|
||||
return true;
|
||||
}
|
||||
obj = obj.parent;
|
||||
|
|
@ -555,12 +549,13 @@ ParametricManager.isAux = function(obj) {
|
|||
return false;
|
||||
};
|
||||
|
||||
ParametricManager.fetchAuxParams = function(system, auxParams, auxDict) {
|
||||
ParametricManager.fetchAuxParams = function(system, auxParams, auxDict, disabledObjects) {
|
||||
disabledObjects = disabledObjects != undefined ? new Set(disabledObjects) : undefined;
|
||||
for (var i = 0; i < system.length; ++i) {
|
||||
for (var p = 0; p < system[i][1].length; ++p) {
|
||||
var parameter = system[i][1][p];
|
||||
if (parameter.obj !== undefined) {
|
||||
if (ParametricManager.isAux(parameter.obj)) {
|
||||
if (ParametricManager.isAux(parameter.obj, disabledObjects)) {
|
||||
if (auxDict[parameter.id] === undefined) {
|
||||
auxDict[parameter.id] = parameter;
|
||||
auxParams.push(parameter);
|
||||
|
|
@ -726,7 +721,9 @@ ParametricManager.reduceSystem = function(system, readOnlyParams) {
|
|||
return info;
|
||||
};
|
||||
|
||||
ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemConstraints, extraConstraints) {
|
||||
ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemConstraints, extraConstraints, disabledObjects) {
|
||||
|
||||
locked = locked || [];
|
||||
|
||||
var constrs = [];
|
||||
var solverParamsDict = {};
|
||||
|
|
@ -737,7 +734,7 @@ ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemCons
|
|||
this.__getSolveData(subSystemConstraints, system);
|
||||
if (!!extraConstraints) this.__getSolveData(extraConstraints, system);
|
||||
|
||||
ParametricManager.fetchAuxParams(system, auxParams, auxDict);
|
||||
ParametricManager.fetchAuxParams(system, auxParams, auxDict, disabledObjects);
|
||||
var readOnlyParams = auxParams.concat(locked);
|
||||
var reduceInfo = ParametricManager.reduceSystem(system, readOnlyParams);
|
||||
|
||||
|
|
@ -777,19 +774,16 @@ ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemCons
|
|||
}
|
||||
})();
|
||||
|
||||
var aux = [];
|
||||
for (var i = 0; i < system.length; ++i) {
|
||||
|
||||
var sdata = system[i];
|
||||
var params = [];
|
||||
|
||||
for (var p = 0; p < sdata[1].length; ++p) {
|
||||
var param = sdata[1][p];
|
||||
var solverParam = getSolverParam(param);
|
||||
for (let p = 0; p < sdata[1].length; ++p) {
|
||||
const param = sdata[1][p];
|
||||
const solverParam = getSolverParam(param);
|
||||
solverParam.aux = auxDict[param.id] !== undefined;
|
||||
params.push(solverParam);
|
||||
if (auxDict[param.id] !== undefined) {
|
||||
aux.push(solverParam);
|
||||
}
|
||||
}
|
||||
if (reduceInfo.reducedConstraints[i] === true) continue;
|
||||
|
||||
|
|
@ -798,11 +792,11 @@ ParametricManager.prototype.prepareForSubSystem = function(locked, subSystemCons
|
|||
}
|
||||
|
||||
var lockedSolverParams = [];
|
||||
for (p = 0; p < locked.length; ++p) {
|
||||
for (let p = 0; p < locked.length; ++p) {
|
||||
lockedSolverParams[p] = getSolverParam(locked[p]);
|
||||
}
|
||||
|
||||
var solver = prepare(constrs, lockedSolverParams, aux);
|
||||
var solver = prepare(constrs, lockedSolverParams);
|
||||
function solve(rough, alg) {
|
||||
return solver.solveSystem(rough, alg);
|
||||
}
|
||||
|
|
@ -1015,6 +1009,45 @@ Constraints.Perpendicular.prototype.getObjects = function() {
|
|||
|
||||
// ------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
/** @constructor */
|
||||
Constraints.P2LDistanceSigned = function(p, a, b, d) {
|
||||
this.p = p;
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.d = d;
|
||||
};
|
||||
|
||||
Constraints.P2LDistanceSigned.prototype.NAME = 'P2LDistanceSigned';
|
||||
Constraints.P2LDistanceSigned.prototype.UI_NAME = 'Distance P & L';
|
||||
|
||||
Constraints.P2LDistanceSigned.prototype.getSolveData = function(resolver) {
|
||||
var params = [];
|
||||
this.p.collectParams(params);
|
||||
this.a.collectParams(params);
|
||||
this.b.collectParams(params);
|
||||
return [[this.NAME, params, [resolver(this.d)]]];
|
||||
};
|
||||
|
||||
Constraints.P2LDistanceSigned.prototype.serialize = function() {
|
||||
return [this.NAME, [this.p.id, this.a.id, this.b.id, this.d]];
|
||||
};
|
||||
|
||||
Constraints.Factory[Constraints.P2LDistanceSigned.prototype.NAME] = function(refs, data) {
|
||||
return new Constraints.P2LDistanceSigned(refs(data[0]), refs(data[1]), refs(data[2]), data[3]);
|
||||
};
|
||||
|
||||
Constraints.P2LDistanceSigned.prototype.getObjects = function() {
|
||||
const collector = new Constraints.ParentsCollector();
|
||||
collector.check(this.a);
|
||||
collector.check(this.b);
|
||||
collector.parents.push(this.p);
|
||||
return collector.parents;
|
||||
};
|
||||
|
||||
Constraints.P2LDistanceSigned.prototype.SettableFields = {'d' : "Enter the distance"};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
/** @constructor */
|
||||
Constraints.P2LDistance = function(p, l, d) {
|
||||
this.p = p;
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ export class FilletTool extends Tool {
|
|||
//pm._add(new Constraints.LockConvex(arc.c, arc.a, otherEnd(point1)));
|
||||
//pm._add(new Constraints.LockConvex(otherEnd(point2), arc.b, arc.c));
|
||||
|
||||
var solver = pm.solveWithLock([]);
|
||||
// var solver = pm.solveWithLock([point1._x, point1._y, point2._x, point2._y]);
|
||||
var solver = pm.solve();
|
||||
// var solver = pm.solve([point1._x, point1._y, point2._x, point2._y]);
|
||||
pm.notify();
|
||||
this.viewer.refresh();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,11 @@ export class OffsetTool extends LoopPickTool {
|
|||
}
|
||||
}
|
||||
let delta = parseInt(prompt('offset distance?', 100));
|
||||
if (isNaN(delta)) {
|
||||
return;
|
||||
}
|
||||
const absDelta = Math.abs(delta);
|
||||
|
||||
|
||||
const edges = [];
|
||||
const startPoint = findLowestPoint(loopPoints);
|
||||
const start = loopPoints.indexOf(startPoint);
|
||||
|
|
@ -38,15 +41,14 @@ export class OffsetTool extends LoopPickTool {
|
|||
return (i + start) % length;
|
||||
}
|
||||
|
||||
if (!math.isCCW([loopPoints[pos(0)], loopPoints[pos(1)], loopPoints[pos(length - 1)]])) {
|
||||
const inverse = this.twoConnectedArcs() || !math.isCCW([loopPoints[pos(0)], loopPoints[pos(1)], loopPoints[pos(length - 1)]]);
|
||||
if (inverse) {
|
||||
delta *= -1;
|
||||
}
|
||||
|
||||
const arcs = [];
|
||||
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const a = loopPoints[pos(i)];
|
||||
const b = loopPoints[pos(i + 1)];
|
||||
let a = loopPoints[pos(i)];
|
||||
let b = loopPoints[pos(i + 1)];
|
||||
const normal = new Vector(-(b.y - a.y), (b.x - a.x))._normalize();
|
||||
const offVector = normal._multiply(delta);
|
||||
const origEdge = loopEdges[pos(i)];
|
||||
|
|
@ -58,33 +60,39 @@ export class OffsetTool extends LoopPickTool {
|
|||
const segment = this.viewer.addSegment(aOffX, aOffY,
|
||||
bOffX, bOffY, this.viewer.activeLayer);
|
||||
this.viewer.parametricManager._add(new Constraints.Parallel(origEdge, segment));
|
||||
this.viewer.parametricManager._add(new Constraints.P2LDistance(a, segment, absDelta));
|
||||
this.viewer.parametricManager._add(new Constraints.P2LDistanceSigned(a, segment.b, segment.a, delta));
|
||||
edges.push(segment);
|
||||
} else if (origEdge._class == 'TCAD.TWO.Arc') {
|
||||
const connectionEdge = new SimpleEdge(new EndPoint(aOffX, aOffY), new EndPoint(bOffX, bOffY));
|
||||
edges.push(connectionEdge);
|
||||
const arcEdge = inverse ? connectionEdge.reverse() : connectionEdge;
|
||||
const arc = new Arc(
|
||||
new EndPoint(aOffX, aOffY),
|
||||
new EndPoint(bOffX, bOffY),
|
||||
arcEdge.a,
|
||||
arcEdge.b,
|
||||
new EndPoint(origEdge.c.x + offVector.x, origEdge.c.y + offVector.y)
|
||||
);
|
||||
arc.stabilize(this.viewer);
|
||||
this.viewer.parametricManager._linkObjects([origEdge.c, arc.c]);
|
||||
this.viewer.parametricManager._add(new Constraints.RadiusOffset(origEdge, arc, delta));
|
||||
this.viewer.parametricManager._linkObjects([arc.c, origEdge.c]);
|
||||
this.viewer.parametricManager._add(new Constraints.RadiusOffset(inverse?arc:origEdge, inverse?origEdge:arc, delta));
|
||||
this.viewer.activeLayer.add(arc);
|
||||
edges.push(arc);
|
||||
arcs.push(arc);
|
||||
}
|
||||
}
|
||||
|
||||
arcs.forEach(e => e.c.aux = true);
|
||||
for (let i = 0; i < edges.length; i++) {
|
||||
this.viewer.parametricManager._linkObjects([edges[i].b, edges[(i + 1) % edges.length].a]);
|
||||
}
|
||||
loopEdges.forEach(e => e.aux = true);
|
||||
this.viewer.parametricManager.solve(undefined, undefined, loopEdges);
|
||||
this.viewer.parametricManager.refresh();
|
||||
loopEdges.forEach(e => e.aux = false);
|
||||
arcs.forEach(e => e.c.aux = false);
|
||||
this.viewer.toolManager.releaseControl();
|
||||
}
|
||||
|
||||
twoConnectedArcs() {
|
||||
function isArc(edge) {
|
||||
return edge._class == 'TCAD.TWO.Arc';
|
||||
}
|
||||
const edges = this.pickedLoop.edges;
|
||||
return edges.length == 2 && isArc(edges[0]) && isArc(edges[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -96,9 +104,12 @@ const SUPPORTED_OBJECTS = new Set();
|
|||
SUPPORTED_OBJECTS.add('TCAD.TWO.Segment');
|
||||
SUPPORTED_OBJECTS.add('TCAD.TWO.Arc');
|
||||
|
||||
function SimpleSegment(a, b) {
|
||||
function SimpleEdge(a, b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.reverse = function() {
|
||||
return new SimpleEdge(b, a);
|
||||
}
|
||||
}
|
||||
|
||||
function findLowestPoint(poly) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue