From 787dc079253adff0c0fdfc103b452b1d2482634d Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Thu, 8 Dec 2016 01:25:28 -0800 Subject: [PATCH] elliptical arc basic support(no DXF or SVG export) --- web/app/sketcher/io.js | 17 +++++++- web/app/sketcher/parametric.js | 5 ++- web/app/sketcher/shapes/circle.js | 3 -- web/app/sketcher/shapes/ellipse.js | 5 --- web/app/sketcher/shapes/elliptical-arc.js | 49 +++++++++++++++++++---- web/app/sketcher/shapes/sketch-object.js | 4 -- web/app/sketcher/sketcher-app.js | 6 ++- web/app/sketcher/tools/edit-tools-map.js | 24 +++++++++++ web/app/sketcher/tools/ellipse.js | 34 ++++++++++++++-- web/app/sketcher/tools/pan.js | 3 +- web/app/utils/utils.js | 8 +++- web/sketcher.html | 3 +- 12 files changed, 131 insertions(+), 30 deletions(-) create mode 100644 web/app/sketcher/tools/edit-tools-map.js diff --git a/web/app/sketcher/io.js b/web/app/sketcher/io.js index cc4c88d8..8109e59c 100644 --- a/web/app/sketcher/io.js +++ b/web/app/sketcher/io.js @@ -5,6 +5,7 @@ import {EndPoint} from './shapes/point' import {Segment} from './shapes/segment' import {Circle} from './shapes/circle' import {Ellipse} from './shapes/ellipse' +import {EllipticalArc} from './shapes/elliptical-arc' import {HDimension, VDimension, Dimension, DiameterDimension} from './shapes/dim' import {Constraints} from './parametric' import Vector from '../math/vector' @@ -14,7 +15,8 @@ var Types = { SEGMENT : 'TCAD.TWO.Segment', ARC : 'TCAD.TWO.Arc', CIRCLE : 'TCAD.TWO.Circle', - ELLIPSE : 'TCAD.TWO.Ellipse', + ELLIPSE : 'TCAD.TWO.Ellipse', + ELL_ARC : 'TCAD.TWO.EllipticalArc', DIM : 'TCAD.TWO.Dimension', HDIM : 'TCAD.TWO.HDimension', VDIM : 'TCAD.TWO.VDimension', @@ -120,6 +122,13 @@ IO.prototype._loadSketch = function(sketch) { const ep2 = endPoint(obj['ep2']); skobj = new Ellipse(ep1, ep2); skobj.r.set(obj['r']); + } else if (_class === T.ELL_ARC) { + const ep1 = endPoint(obj['ep1']); + const ep2 = endPoint(obj['ep2']); + const a = endPoint(obj['a']); + const b = endPoint(obj['b']); + skobj = new EllipticalArc(ep1, ep2, a, b); + skobj.r.set(obj['r']); } else if (_class === T.HDIM) { skobj = new HDimension(obj['a'], obj['b']); skobj.flip = obj['flip']; @@ -299,6 +308,12 @@ IO.prototype._serializeSketch = function() { to['ep1'] = point(obj.ep1); to['ep2'] = point(obj.ep2); to['r'] = obj.r.get(); + } else if (obj._class === T.ELL_ARC) { + to['ep1'] = point(obj.ep1); + to['ep2'] = point(obj.ep2); + to['a'] = point(obj.a); + to['b'] = point(obj.b); + to['r'] = obj.r.get(); } else if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) { to['a'] = obj.a.id; to['b'] = obj.b.id; diff --git a/web/app/sketcher/parametric.js b/web/app/sketcher/parametric.js index be1d500e..bfdec522 100644 --- a/web/app/sketcher/parametric.js +++ b/web/app/sketcher/parametric.js @@ -267,7 +267,7 @@ ParametricManager.prototype.lockConvex = function(objs, warnCallback) { }; ParametricManager.prototype.tangent = function(objs) { - const ellipses = fetch.generic(objs, ['TCAD.TWO.Ellipse'], 0); + const ellipses = fetch.generic(objs, ['TCAD.TWO.Ellipse', 'TCAD.TWO.EllipticalArc'], 0); const lines = fetch.generic(objs, ['TCAD.TWO.Segment'], 1); if (ellipses.length > 0) { this.add(new Constraints.EllipseTangent(lines[0], ellipses[0])); @@ -337,7 +337,7 @@ ParametricManager.prototype.pointOnArc = function(objs) { ParametricManager.prototype.pointOnEllipse = function(objs) { const points = fetch.generic(objs, ['TCAD.TWO.EndPoint'], 1); - const ellipses = fetch.generic(objs, ['TCAD.TWO.Ellipse'], 1); + const ellipses = fetch.generic(objs, ['TCAD.TWO.Ellipse', 'TCAD.TWO.EllipticalArc'], 1); this.add(new Constraints.PointOnEllipse(points[0], ellipses[0])); }; @@ -1374,6 +1374,7 @@ Constraints.PointOnEllipseInternal = function(point, ellipse) { Constraints.PointOnEllipseInternal.prototype.NAME = 'PointOnEllipseI'; Constraints.PointOnEllipseInternal.prototype.UI_NAME = 'Point On Ellipse'; +Constraints.PointOnEllipseInternal.prototype.aux = true; Constraints.PointOnEllipseInternal.prototype.getSolveData = function() { var params = []; diff --git a/web/app/sketcher/shapes/circle.js b/web/app/sketcher/shapes/circle.js index 6953c0a9..8b4ccff5 100644 --- a/web/app/sketcher/shapes/circle.js +++ b/web/app/sketcher/shapes/circle.js @@ -44,9 +44,6 @@ export class Circle extends SketchObject { if (alternative) { return super.getDefaultTool(viewer, alternative); } else { - const editTool = new EditCircleTool(viewer, null); - editTool.circle = this; - return editTool; } } } diff --git a/web/app/sketcher/shapes/ellipse.js b/web/app/sketcher/shapes/ellipse.js index 0619fb38..807b1025 100644 --- a/web/app/sketcher/shapes/ellipse.js +++ b/web/app/sketcher/shapes/ellipse.js @@ -1,6 +1,5 @@ import {Ref} from './ref' import {SketchObject} from './sketch-object' -import {EllipseTool, STATE_RADIUS} from '../tools/ellipse' import {Constraints} from '../parametric' import * as math from '../../math/math'; @@ -95,10 +94,6 @@ export class Ellipse extends SketchObject { if (alternative) { return super.getDefaultTool(viewer, alternative); } else { - const editTool = new EllipseTool(viewer); - editTool.ellipse = this; - editTool.state = STATE_RADIUS; - return editTool; } } } diff --git a/web/app/sketcher/shapes/elliptical-arc.js b/web/app/sketcher/shapes/elliptical-arc.js index 70d7b1fc..0114bd32 100644 --- a/web/app/sketcher/shapes/elliptical-arc.js +++ b/web/app/sketcher/shapes/elliptical-arc.js @@ -1,19 +1,54 @@ -import {Ref} from './ref' -import {SketchObject} from './sketch-object' +import {Ellipse} from './ellipse' +import {Constraints} from '../parametric' import * as math from '../../math/math'; +import {swap} from '../../utils/utils' -export class EllipticalArc extends SketchObject { +export class EllipticalArc extends Ellipse { constructor(ep1, ep2, a, b) { + super(ep1, ep2); + this.a = a; + this.b = b; + this.addChild(a); + this.addChild(b); + + //we'd like to have angles points have higher selection order + swap(this.children, 0, this.children.length - 2); + swap(this.children, 1, this.children.length - 1); + } + + stabilize(viewer) { + viewer.parametricManager._add(new Constraints.PointOnEllipseInternal(this.b, this)); + viewer.parametricManager._add(new Constraints.PointOnEllipseInternal(this.a, this)); } drawImpl(ctx, scale) { - } - - - normalDistance(aim) { + ctx.beginPath(); + const radiusX = Math.max(this.radiusX, 1e-8); + const radiusY = Math.max(this.radiusY, 1e-8); + let aAngle = this.drawAngle(this.a); + let bAngle; + if (math.areEqual(this.a.x, this.b.x, math.TOLERANCE) && + math.areEqual(this.a.y, this.b.y, math.TOLERANCE)) { + bAngle = aAngle + 2 * Math.PI; + } else { + bAngle = this.drawAngle(this.b) + } + ctx.ellipse(this.centerX, this.centerY, radiusX, radiusY, this.rotation, aAngle, bAngle ); + ctx.stroke(); } + drawAngle(point) { + let deformScale = this.radiusY / this.radiusX; + let x = point.x - this.centerX; + let y = point.y - this.centerY; + const rotation = - this.rotation; + let xx = x * Math.cos(rotation) - y * Math.sin(rotation); + let yy = x * Math.sin(rotation) + y * Math.cos(rotation); + xx *= deformScale; + return Math.atan2(yy, xx); + } } + EllipticalArc.prototype._class = 'TCAD.TWO.EllipticalArc'; diff --git a/web/app/sketcher/shapes/sketch-object.js b/web/app/sketcher/shapes/sketch-object.js index 2c87a0cf..859fbd5e 100644 --- a/web/app/sketcher/shapes/sketch-object.js +++ b/web/app/sketcher/shapes/sketch-object.js @@ -35,10 +35,6 @@ export class SketchObject extends Shape { return false; } - getDefaultTool(viewer) { - return new DragTool(this, viewer); - } - isAuxOrLinkedTo() { if (!!this.aux) { return true; diff --git a/web/app/sketcher/sketcher-app.js b/web/app/sketcher/sketcher-app.js index 4739fb16..3684c707 100644 --- a/web/app/sketcher/sketcher-app.js +++ b/web/app/sketcher/sketcher-app.js @@ -136,7 +136,11 @@ function App2D() { }, 'circle'); this.registerAction('addEllipse', "Add Ellipse", function () { - app.viewer.toolManager.takeControl(new EllipseTool(app.viewer)); + app.viewer.toolManager.takeControl(new EllipseTool(app.viewer, false)); + }); + + this.registerAction('addEllipticalArc', "Add Elliptical Arc", function () { + app.viewer.toolManager.takeControl(new EllipseTool(app.viewer, true)); }); this.registerAction('pan', "Pan", function () { diff --git a/web/app/sketcher/tools/edit-tools-map.js b/web/app/sketcher/tools/edit-tools-map.js new file mode 100644 index 00000000..2361784e --- /dev/null +++ b/web/app/sketcher/tools/edit-tools-map.js @@ -0,0 +1,24 @@ +import {Ellipse} from '../shapes/ellipse' +import {EllipticalArc} from '../shapes/elliptical-arc' +import {Circle} from '../shapes/circle' +import {EditCircleTool} from './circle' +import {DragTool} from './drag' +import {EllipseTool, STATE_RADIUS} from './ellipse' + +export function GetShapeEditTool(viewer, obj, alternative) { + if (obj instanceof Circle && !alternative) { + const tool = new EditCircleTool(viewer); + tool.circle = obj; + return tool; + } else if (obj instanceof Ellipse && !alternative) { + // even for an ell-arc we should act as it would be an ellipse to + // avoid stabilize constraints added and demoing B point on move + // so second arg must be FALSE! + const tool = new EllipseTool(viewer, false); + tool.ellipse = obj; + tool.state = STATE_RADIUS; + return tool; + } else { + return new DragTool(obj, viewer); + } +} diff --git a/web/app/sketcher/tools/ellipse.js b/web/app/sketcher/tools/ellipse.js index f9cf59f8..d07ce0bb 100644 --- a/web/app/sketcher/tools/ellipse.js +++ b/web/app/sketcher/tools/ellipse.js @@ -1,6 +1,7 @@ import {Tool} from './tool' import {EndPoint} from '../shapes/point' import {Ellipse} from '../shapes/ellipse' +import {EllipticalArc} from '../shapes/elliptical-arc' import Vector from '../../math/vector' export const STATE_POINT1 = 0; @@ -9,8 +10,9 @@ export const STATE_RADIUS = 2; export class EllipseTool extends Tool { - constructor(viewer) { - super('ellipse', viewer); + constructor(viewer, arc) { + super(arc ? 'ellipse' : 'elliptical arc', viewer); + this.arc = arc; this.ellipse = null; this.state = STATE_POINT1; } @@ -29,11 +31,25 @@ export class EllipseTool extends Tool { return this.viewer.snapped ? this.viewer.snapped : this.viewer.screenToModel(e); } + newEllipse(p) { + const ep = () => new EndPoint(p.x, p.y); + return this.arc ? new EllipticalArc(ep(), ep(), ep(), ep()) : new Ellipse(ep(), ep()); + } + + demoBPoint() { + const arc = this.ellipse; + let ang = Math.atan2(arc.a.y - arc.centerY, arc.a.x - arc.centerX) + (2 * Math.PI - 0.3); + ang %= 2 * Math.PI; + const r = arc.radiusAtAngle(ang - arc.rotation); + arc.b.x = arc.centerX + r * Math.cos(ang); + arc.b.y = arc.centerY + r * Math.sin(ang); + } + mouseup(e) { switch (this.state) { case STATE_POINT1: { const p = this.point(e); - this.ellipse = new Ellipse(new EndPoint(p.x, p.y), new EndPoint(p.x, p.y)); + this.ellipse = this.newEllipse(p); this.snapIfNeed(this.ellipse.ep1); this.viewer.activeLayer.objects.push(this.ellipse); this.viewer.refresh(); @@ -51,6 +67,9 @@ export class EllipseTool extends Tool { break; } case STATE_RADIUS: + if (this.arc) { + this.ellipse.stabilize(this.viewer); + } this.viewer.toolManager.releaseControl(); } } @@ -64,7 +83,11 @@ export class EllipseTool extends Tool { case STATE_POINT2: this.ellipse.ep2.setFromPoint(this.viewer.screenToModel(e)); this.ellipse.r.value = this.ellipse.radiusX * 0.5; - this.viewer.snap(p.x, p.y, [this.ellipse.ep1, this.ellipse.ep2]); + this.viewer.snap(p.x, p.y, this.ellipse.children); + if (this.arc) { + this.ellipse.a.setFromPoint(this.ellipse.ep2); + this.demoBPoint(); + } break; case STATE_RADIUS: const polarPoint = this.ellipse.toEllipseCoordinateSystem(p); @@ -79,6 +102,9 @@ export class EllipseTool extends Tool { if (!Tool.dumbMode(e)) { this.solveRequest(true); } + if (this.arc) { + this.demoBPoint(); + } break; } this.viewer.refresh(); diff --git a/web/app/sketcher/tools/pan.js b/web/app/sketcher/tools/pan.js index 4628465e..3302526e 100644 --- a/web/app/sketcher/tools/pan.js +++ b/web/app/sketcher/tools/pan.js @@ -1,4 +1,5 @@ import {Tool} from './tool' +import {GetShapeEditTool} from './edit-tools-map' export class PanTool extends Tool { constructor(viewer) { @@ -57,7 +58,7 @@ export class PanTool extends Tool { } this.viewer.select([toSelect], true); if (!toSelect.isAuxOrLinkedTo()) { - var tool = toSelect.getDefaultTool(this.viewer, e.altKey); + const tool = GetShapeEditTool(this.viewer, toSelect, e.altKey); tool.mousedown(e); this.viewer.toolManager.switchTool(tool); } diff --git a/web/app/utils/utils.js b/web/app/utils/utils.js index f5cc5c6b..d9d2240c 100644 --- a/web/app/utils/utils.js +++ b/web/app/utils/utils.js @@ -33,4 +33,10 @@ export function constRef(value) { return function() { return value; }; -} \ No newline at end of file +} + +export function swap(arr, i1, i2) { + const tmp = arr[i1]; + arr[i1] = arr[i2]; + arr[i2] = tmp; +} \ No newline at end of file diff --git a/web/sketcher.html b/web/sketcher.html index 967b2e51..4f5473dd 100644 --- a/web/sketcher.html +++ b/web/sketcher.html @@ -29,7 +29,8 @@ -->