mirror of
https://github.com/xibyte/jsketcher
synced 2026-02-12 10:30:54 +01:00
elliptical arc basic support(no DXF or SVG export)
This commit is contained in:
parent
af85e3e77b
commit
787dc07925
12 changed files with 131 additions and 30 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 = [];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -35,10 +35,6 @@ export class SketchObject extends Shape {
|
|||
return false;
|
||||
}
|
||||
|
||||
getDefaultTool(viewer) {
|
||||
return new DragTool(this, viewer);
|
||||
}
|
||||
|
||||
isAuxOrLinkedTo() {
|
||||
if (!!this.aux) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -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 () {
|
||||
|
|
|
|||
24
web/app/sketcher/tools/edit-tools-map.js
Normal file
24
web/app/sketcher/tools/edit-tools-map.js
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,4 +33,10 @@ export function constRef(value) {
|
|||
return function() {
|
||||
return value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function swap(arr, i1, i2) {
|
||||
const tmp = arr[i1];
|
||||
arr[i1] = arr[i2];
|
||||
arr[i2] = tmp;
|
||||
}
|
||||
|
|
@ -29,7 +29,8 @@
|
|||
--><button class="btn tbtn act-addMultiSegment" style="background-image: url(img/mline.png);" type="submit" value=""></button><!--
|
||||
--><button class="btn tbtn act-addCircle" style="background-image: url(img/circle.png);" type="submit" value=""></button><!--
|
||||
--><button class="btn tbtn act-addArc" style="background-image: url(img/arc.png);" type="submit" value=""></button><!--
|
||||
--><button class="btn tbtn act-addEllipse sep" type="submit" value="">E</button><!--
|
||||
--><button class="btn tbtn act-addEllipse" type="submit" value="">E</button><!--
|
||||
--><button class="btn tbtn act-addEllipticalArc sep" type="submit" value="">EA</button><!--
|
||||
--><button class="btn tbtn act-addHDim" style="background-image: url(img/hdim.png);" type="submit" value=""></button><!--
|
||||
--><button class="btn tbtn act-addVDim" style="background-image: url(img/vdim.png);" type="submit" value=""></button><!--
|
||||
--><button class="btn tbtn act-addDim" style="background-image: url(img/dim.png);" type="submit" value=""></button><!--
|
||||
|
|
|
|||
Loading…
Reference in a new issue