UNPKG

awv3

Version:
246 lines (218 loc) 10.8 kB
import { addCommand, removeCommands, updateCommand } from './command/highlevel'; import { Assign, Member, Sequence, Variable } from './command/lowlevel'; import * as Constraints from './constraint/type'; import { drawArcBy_S_E_CP, getTangent, intersectLines } from './geomutils'; import {ReplaceEntityInConstraints} from "./command/lowlevel"; export default class FilletProcessor { constructor(sketch) { this.sketch = sketch; } // check if the given position is at the angle that can be filleted // return full information about the angle or null recognizeFilletableAngle(pos) { // find exactly matching points - children of lines const samePoints = this.findPointsAt(pos); // exactly two curves must end at pos if (samePoints.length !== 2) return null; //both of them must be lines const lines = samePoints.map(pnt => pnt.parent); if (!lines.every(ln => ln && ln.isLine())) return null; // these two points must be marked as incident const incidenceConstr = this.findConstraints(Constraints.incidence.type, samePoints[0], samePoints[1]); if (incidenceConstr.length !== 1) return null; // check that parent lines are not collinear const dirA = getTangent(lines[0].geomParams), dirB = getTangent(lines[1].geomParams); if (Math.abs(dirA.cross(dirB).z) <= FilletProcessor.angularTolerance) return null; // find outer endpoints of the lines let lineEnds = []; for (let i = 0; i < 2; i++) { const vertexAtStart = (lines[i].startPoint.id === samePoints[i].id); lineEnds[i] = vertexAtStart ? lines[i].endPoint : lines[i].startPoint; } //return all the found data return { lines: lines, lineStarts: samePoints, lineEnds: lineEnds, incidence: incidenceConstr[0], control: pos, }; } // checks if the given arc is a part of a correct fillet // return full information about the fillet or null recognizeFilletByArcOrEdge(obj) { let res = { obj, objEnds: [], lines: [], lineStarts: [], lineEnds: [], vertex: null }; let lineDirs = []; for (let objEnd of [obj.startPoint, obj.endPoint]) { // find incident line let samePoints = this.findPointsAt(objEnd.pos); samePoints = samePoints.filter( point => point.parent.id !== obj.id && point.parent.state.class === 'CC_Line' ); if (samePoints.length !== 1) return null; const lineStart = samePoints[0]; const line = lineStart.parent; const lineEnd = line.startPoint.id === lineStart.id ? line.endPoint : line.startPoint; // check that all constraints are in objEnd if (this.findConstraints(Constraints.incidence.type, objEnd, lineStart).length !== 1) return null; if (obj.state.class === 'CC_Arc' && this.findConstraints(Constraints.tangency.type, obj, line).length !== 1) return null; // check that a line is tangent at a common point const lineDir = lineEnd.pos.sub(lineStart.pos).normalize(); if (obj.state.class === 'CC_Arc') { const arcDir = getTangent(obj.geomParams, objEnd.pos); if (objEnd.state.name === 'startPoint') arcDir.negate(); if (lineDir.distanceTo(arcDir) > FilletProcessor.angularTolerance) return null; } // save found objects res.objEnds.push(objEnd); res.lines.push(line); res.lineStarts.push(lineStart); res.lineEnds.push(lineEnd); lineDirs.push(lineDir); } // check that the fillet angle is in range (0; pi) if (obj.state.class === 'CC_Arc' && Math.abs(obj.state.members.bulge.value) >= 1.0) return null; //arc's angle > pi let angle = lineDirs[0].angleTo(lineDirs[1]); if (angle <= FilletProcessor.angularTolerance || angle >= Math.PI - FilletProcessor.angularTolerance) return null; // calculate the location of the fillet's vertex const vertexPos = intersectLines( res.lineStarts[0].pos, lineDirs[0], res.lineStarts[1].pos, lineDirs[1], FilletProcessor.angularTolerance ); if (!vertexPos) return null; // check for a point there const vertices = this.findPointsAt(vertexPos); if (vertices.length !== 1) return null; res.vertex = vertices[0]; res.control = res.vertex.pos; // check incidence of the vertex to lines for (let i = 0; i < 2; ++i) if (this.findConstraints(Constraints.incidence.type, res.vertex, res.lines[i]).length !== 1) return null; return res; } getAngle(info) { let dirs = []; for (let i = 0; i < 2; i++) dirs[i] = info.lineEnds[i].pos.sub(info.lineStarts[i].pos).normalize(); return dirs[0].angleTo(dirs[1]); } offsetToRadius(info, offset) { let angle = this.getAngle(info); return offset * Math.tan(angle / 2); } radiusToOffset(info, radius) { let angle = this.getAngle(info); return radius / Math.tan(angle / 2); } // calculate the filleting arc params (center, touch points) by specified radius calculateFilletParams(info, options) { const touch = this.getTouchPoints(info, options.radius, options.offset); if (!touch) return null; return { ...drawArcBy_S_E_CP([...touch, info.control], {}), control: info.control }; } calculateChamferParams(info, options) { const touch = this.getTouchPoints(info, options.radius, options.offset); if (!touch) return null; return { start: touch[0], end: touch[1], control: info.control }; } findPointsAt(pos) { let samePoints = []; for (let obj of this.sketch.descendants) { if (obj.state.class === 'CC_Point' && obj.pos.distanceTo(pos) <= FilletProcessor.linearTolerance) samePoints.push(obj); } return samePoints; } findConstraints(type, objA, objB) { return this.sketch.children.filter( constr => constr.state.class === type && (constr.entities[0].id === objA.id && constr.entities[1].id === objB.id || constr.entities[0].id === objB.id && constr.entities[1].id === objA.id) ); } getTouchPoints(info, radius, distance) { const vertexPos = info.control; let dirs = [], maxDists = []; for (let i = 0; i < 2; i++) { let dir = info.lineEnds[i].pos.sub(info.lineStarts[i].pos).normalize(); let maxDist = info.lineEnds[i].pos.distanceTo(vertexPos); dirs.push(dir); maxDists.push(maxDist); } const angle = dirs[0].angleTo(dirs[1]); const dist = distance !== undefined ? distance : radius / Math.tan(angle / 2); if (dist + FilletProcessor.linearTolerance >= Math.min(maxDists[0], maxDists[1])) return null; // too large fillet return dirs.map(dir => dir.clone().multiplyScalar(dist).add(vertexPos)); } createNewFilletStatement(info, params) { const vertex = Variable(), curve = Variable(); return Sequence( ...removeCommands(this.sketch, info.incidence), updateCommand(this.sketch, info.lineStarts[0], { start: params.start }), updateCommand(this.sketch, info.lineStarts[1], { start: params.end }), Assign(vertex, addCommand(this.sketch, { start: info.control })), Assign(curve, addCommand(this.sketch, params)), ReplaceEntityInConstraints(this.sketch, undefined, info.lineStarts[0], vertex), ReplaceEntityInConstraints(this.sketch, undefined, info.lineStarts[1], vertex), addCommand(this.sketch, { class: Constraints.incidence.type, entities: [info.lines[0], vertex], value: {} }), addCommand(this.sketch, { class: Constraints.incidence.type, entities: [info.lines[1], vertex], value: {} }), addCommand(this.sketch, { class: Constraints.incidence.type, entities: [info.lineStarts[0], Member(curve, 'startPoint')], value: {} }), addCommand(this.sketch, { class: Constraints.incidence.type, entities: [info.lineStarts[1], Member(curve, 'endPoint')], value: {} }), addCommand(this.sketch, { class: Constraints.tangency.type, entities: [info.lines[0], curve], value: {} }), addCommand(this.sketch, { class: Constraints.tangency.type, entities: [info.lines[1], curve], value: {} }), addCommand(this.sketch, { class: Constraints.radius.type, entities: [curve], value: {} }) ); } deleteFilletStatement(info) { let constraints = Array.from(this.sketch.descendants).filter(obj => obj.isConstraint()); //note: when moving constraints from fillet's vertex to the resulting angle's vertex, //ignore everything that involves removed geometry, filleting lines and their closer endpoints //the ignored constraints would be removed automatically let blacklist = [...info.lines, ...info.lineStarts, info.obj, info.obj.startPoint, info.obj.endPoint, info.obj.center]; let involves = (constr, obj) => constr.entities.some(e => e.id === obj.id); constraints = constraints.filter(constr => !blacklist.some(t => involves(constr, t))); return Sequence( ...removeCommands(this.sketch, info.obj), ReplaceEntityInConstraints(this.sketch, constraints, info.vertex, info.lineStarts[0]), ...removeCommands(this.sketch, info.vertex), updateCommand(this.sketch, info.lineStarts[0], { start: info.vertex.pos }), updateCommand(this.sketch, info.lineStarts[1], { start: info.vertex.pos }), addCommand(this.sketch, { class: Constraints.incidence.type, entities: [info.lineStarts[0], info.lineStarts[1]], value: {} }) ); } updateFilletStatement(info, params) { return Sequence( updateCommand(this.sketch, info.lineStarts[0], { start: params.start }), updateCommand(this.sketch, info.lineStarts[1], { start: params.end }), updateCommand(this.sketch, info.obj, params) ); } static linearTolerance = 1e-3; static angularTolerance = 1e-3; }