mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 16:33:15 +01:00
angular measure tool
This commit is contained in:
parent
b86933b32a
commit
c082c13ed5
17 changed files with 653 additions and 64 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
import Vector from 'math/vector';
|
import Vector from 'math/vector';
|
||||||
import BBox from './bbox'
|
import BBox from './bbox'
|
||||||
import * as vec from './vec';
|
import * as vec from './vec';
|
||||||
|
import {perp2d} from "./vec";
|
||||||
|
import {eqTol} from "../brep/geom/tolerance";
|
||||||
|
|
||||||
export const TOLERANCE = 1E-6;
|
export const TOLERANCE = 1E-6;
|
||||||
export const TOLERANCE_SQ = TOLERANCE * TOLERANCE;
|
export const TOLERANCE_SQ = TOLERANCE * TOLERANCE;
|
||||||
|
|
@ -284,6 +286,20 @@ export function pointToLineSignedDistance(ax, ay, bx, by, px, py) {
|
||||||
return vx * nx + vy * ny;
|
return vx * nx + vy * ny;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function lineLineIntersection2d(p1, p2, v1, v2) {
|
||||||
|
|
||||||
|
// const n1 = perp2d(v1);
|
||||||
|
const n2 = perp2d(v2);
|
||||||
|
const cos = vec.dot(n2, v1);
|
||||||
|
if (eqTol(cos, 0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const u1 = vec.dot(n2, vec.sub(p2, p1)) / cos;
|
||||||
|
// const u2 = vec.dot(n1, vec.sub(p1, p2)) / vec.dot(n1, v2);
|
||||||
|
|
||||||
|
return [p1[0] + v1[0] * u1, p1[1] + v1[1] * u1];
|
||||||
|
}
|
||||||
|
|
||||||
export const DEG_RAD = Math.PI / 180.0;
|
export const DEG_RAD = Math.PI / 180.0;
|
||||||
|
|
||||||
export const sq = (a) => a * a;
|
export const sq = (a) => a * a;
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,10 @@ export function __normalize(v, out) {
|
||||||
return __div(v, mag, out)
|
return __div(v, mag, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function cross2d(v1, v2) {
|
||||||
|
return v1[0] * v2[1] - v1[1] * v2[0];
|
||||||
|
}
|
||||||
|
|
||||||
export function _normalize(v) {
|
export function _normalize(v) {
|
||||||
return __normalize(v, v);
|
return __normalize(v, v);
|
||||||
}
|
}
|
||||||
|
|
@ -155,6 +159,20 @@ export function distance(v1, v2) {
|
||||||
return Math.sqrt(distanceSq(v1, v2));
|
return Math.sqrt(distanceSq(v1, v2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function perp2d(v) {
|
||||||
|
return __perp2d(v, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function _perp2d(v) {
|
||||||
|
return __perp2d(v, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function __perp2d([x, y], out) {
|
||||||
|
out[0] = -y;
|
||||||
|
out[1] = x;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
export function normal3(ccwSequence) {
|
export function normal3(ccwSequence) {
|
||||||
let a = ccwSequence[0];
|
let a = ccwSequence[0];
|
||||||
let b = ccwSequence[1];
|
let b = ccwSequence[1];
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
import {MirrorGeneratorIcon} from "../icons/generators/GeneratorIcons";
|
|
||||||
import {MirrorGeneratorSchema} from "../generators/mirrorGenerator";
|
|
||||||
import {AddCircleDimTool, AddFreeDimTool, AddHorizontalDimTool, AddVerticalDimTool} from "../tools/dim";
|
|
||||||
import {
|
import {
|
||||||
|
AddAngleBetweenDimTool,
|
||||||
|
AddCircleDimTool,
|
||||||
|
AddFreeDimTool,
|
||||||
|
AddHorizontalDimTool,
|
||||||
|
AddVerticalDimTool
|
||||||
|
} from "../tools/dim";
|
||||||
|
import {
|
||||||
|
MeasureAngleBetweenAngle,
|
||||||
MeasureCircleToolIcon,
|
MeasureCircleToolIcon,
|
||||||
MeasureFreeToolIcon,
|
MeasureFreeToolIcon,
|
||||||
MeasureHorizontalToolIcon,
|
MeasureHorizontalToolIcon,
|
||||||
|
|
@ -62,4 +67,17 @@ export default [
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 'MeasureAngleBetween',
|
||||||
|
shortName: 'Measure Angle Between',
|
||||||
|
kind: 'Tool',
|
||||||
|
description: 'Measure angle between',
|
||||||
|
icon: MeasureAngleBetweenAngle,
|
||||||
|
|
||||||
|
invoke: (ctx) => {
|
||||||
|
ctx.viewer.toolManager.takeControl(new AddAngleBetweenDimTool(ctx.viewer, ctx.viewer.dimLayer));
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -10,7 +10,6 @@ import Field from "ui/components/controls/Field";
|
||||||
import Label from "../../../../modules/ui/components/controls/Label";
|
import Label from "../../../../modules/ui/components/controls/Label";
|
||||||
import {SketcherAppContext} from "./SketcherApp";
|
import {SketcherAppContext} from "./SketcherApp";
|
||||||
import {EMPTY_OBJECT} from "../../../../modules/gems/objects";
|
import {EMPTY_OBJECT} from "../../../../modules/gems/objects";
|
||||||
import identity from 'lodash';
|
|
||||||
|
|
||||||
export function ConstraintEditor() {
|
export function ConstraintEditor() {
|
||||||
|
|
||||||
|
|
@ -19,7 +18,7 @@ export function ConstraintEditor() {
|
||||||
const [values, setValues] = useState(null);
|
const [values, setValues] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValues(req && {...req.constraint.constants})
|
setValues(req && {...req.constraint.constants});
|
||||||
return () => {
|
return () => {
|
||||||
if (req) {
|
if (req) {
|
||||||
viewer.unHighlight(req.constraint.objects);
|
viewer.unHighlight(req.constraint.objects);
|
||||||
|
|
@ -72,10 +71,10 @@ export function ConstraintEditor() {
|
||||||
.sort().map(name => {
|
.sort().map(name => {
|
||||||
const def = constraint.schema.constants[name];
|
const def = constraint.schema.constants[name];
|
||||||
const presentation = def.presentation || EMPTY_OBJECT;
|
const presentation = def.presentation || EMPTY_OBJECT;
|
||||||
const onChange = value => setValue(name, (presentation.transformIn||identity)(value));
|
|
||||||
|
|
||||||
const val = (presentation.transformOut||identity)(values[name]);
|
|
||||||
const type = presentation.type || def.type;
|
const type = presentation.type || def.type;
|
||||||
|
const onChange = value => setValue(name, (presentation.transformIn||transformInByType(type))(value));
|
||||||
|
|
||||||
|
const val = (presentation.transformOut||transformOutByType(type))(values[name]);
|
||||||
return <Field key={presentation.label||name}>
|
return <Field key={presentation.label||name}>
|
||||||
<Label>{name}</Label>
|
<Label>{name}</Label>
|
||||||
{
|
{
|
||||||
|
|
@ -113,4 +112,21 @@ export function editConstraint(rqStream, constraint, onApply) {
|
||||||
onApply();
|
onApply();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NO_TR = v => v;
|
||||||
|
|
||||||
|
function transformInByType(type) {
|
||||||
|
if ('boolean' === type) {
|
||||||
|
return value => value ? 'true' : 'false';
|
||||||
|
}
|
||||||
|
return NO_TR;
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformOutByType(type) {
|
||||||
|
if ('boolean' === type) {
|
||||||
|
return value => value === 'true';
|
||||||
|
}
|
||||||
|
return NO_TR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -964,6 +964,8 @@ export class AlgNumConstraint {
|
||||||
val = expressionResolver(val);
|
val = expressionResolver(val);
|
||||||
if (def.type === 'number') {
|
if (def.type === 'number') {
|
||||||
val = parseFloat(val);
|
val = parseFloat(val);
|
||||||
|
} else if (def.type === 'boolean') {
|
||||||
|
val = val === 'true' || val === true;
|
||||||
}
|
}
|
||||||
if (def.transform) {
|
if (def.transform) {
|
||||||
val = def.transform(val);
|
val = def.transform(val);
|
||||||
|
|
@ -996,9 +998,9 @@ export class AlgNumConstraint {
|
||||||
this.constantKeys.map(name => {
|
this.constantKeys.map(name => {
|
||||||
let val = this.schema.constants[name].initialValue(this.objects);
|
let val = this.schema.constants[name].initialValue(this.objects);
|
||||||
if (typeof val === 'number') {
|
if (typeof val === 'number') {
|
||||||
val = val.toFixed(2) + '';
|
val = val.toFixed(2);
|
||||||
}
|
}
|
||||||
this.updateConstant(name, val);
|
this.updateConstant(name, val + '');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import measureCircleContent from './measure-circle-tool.svg';
|
||||||
import measureFreeContent from './measure-free-tool.svg';
|
import measureFreeContent from './measure-free-tool.svg';
|
||||||
import measureHorizontalContent from './measure-horizontal-tool.svg';
|
import measureHorizontalContent from './measure-horizontal-tool.svg';
|
||||||
import measureVerticalContent from './measure-vertical-tool.svg';
|
import measureVerticalContent from './measure-vertical-tool.svg';
|
||||||
|
import measureAngleBetweenContent from './measure-angle-between.svg';
|
||||||
import rectangleContent from './rectangle-tool.svg';
|
import rectangleContent from './rectangle-tool.svg';
|
||||||
import bezierContent from './bezier-tool.svg';
|
import bezierContent from './bezier-tool.svg';
|
||||||
import ellipseContent from './ellipse-tool.svg';
|
import ellipseContent from './ellipse-tool.svg';
|
||||||
|
|
@ -34,6 +35,11 @@ export function MeasureVerticalToolIcon(props) {
|
||||||
return <SvgIcon content={measureVerticalContent} {...props}/>
|
return <SvgIcon content={measureVerticalContent} {...props}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function MeasureAngleBetweenAngle(props) {
|
||||||
|
|
||||||
|
return <SvgIcon content={measureAngleBetweenContent} {...props}/>
|
||||||
|
}
|
||||||
|
|
||||||
export function RectangleToolIcon(props) {
|
export function RectangleToolIcon(props) {
|
||||||
|
|
||||||
return <SvgIcon content={rectangleContent} {...props}/>
|
return <SvgIcon content={rectangleContent} {...props}/>
|
||||||
|
|
|
||||||
26
web/app/sketcher/icons/tools/measure-angle-between.svg
Normal file
26
web/app/sketcher/icons/tools/measure-angle-between.svg
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="64"
|
||||||
|
height="64"
|
||||||
|
viewBox="0 0 64 64"
|
||||||
|
stroke="black"
|
||||||
|
fill="gray">
|
||||||
|
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<path
|
||||||
|
class="annotation"
|
||||||
|
fill="none"
|
||||||
|
d="M 19 26 A 25 25 0 0 1 40 45"
|
||||||
|
/>
|
||||||
|
<polygon class="annotation arrow" points="0,0 -7,-4 -7,4"
|
||||||
|
transform="translate(19 26) rotate(195 0 0)" />
|
||||||
|
|
||||||
|
<polygon class="annotation arrow" points="0,0 -7,-4 -7,4"
|
||||||
|
transform="translate(40 45) rotate(70 0 0)" />
|
||||||
|
|
||||||
|
<path class="line" d="M 10 55 L 23 10" />
|
||||||
|
<path class="line" d="M 10 55 L 55 40" />
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 583 B |
|
|
@ -8,7 +8,7 @@ import {Circle} from './shapes/circle'
|
||||||
import {Ellipse} from './shapes/ellipse'
|
import {Ellipse} from './shapes/ellipse'
|
||||||
import {EllipticalArc} from './shapes/elliptical-arc'
|
import {EllipticalArc} from './shapes/elliptical-arc'
|
||||||
import {BezierCurve} from './shapes/bezier-curve'
|
import {BezierCurve} from './shapes/bezier-curve'
|
||||||
import {DiameterDimension, Dimension, HDimension, VDimension} from './shapes/dim'
|
import {AngleBetweenDimension, DiameterDimension, Dimension, HDimension, VDimension} from './shapes/dim'
|
||||||
import {Constraints} from './parametric'
|
import {Constraints} from './parametric'
|
||||||
import Vector from 'math/vector';
|
import Vector from 'math/vector';
|
||||||
import exportTextData from 'gems/exportTextData';
|
import exportTextData from 'gems/exportTextData';
|
||||||
|
|
@ -78,31 +78,32 @@ IO.prototype._loadSketch = function(sketch) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var layer = viewer.createLayer(name, Styles.DEFAULT);
|
let layer = viewer.createLayer(name, Styles.DEFAULT);
|
||||||
viewer.layers.push(layer);
|
viewer.layers.push(layer);
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
const version = sketch.version || 1;
|
const version = sketch.version || 1;
|
||||||
var T = Types;
|
let T = Types;
|
||||||
var maxEdge = 0;
|
let maxEdge = 0;
|
||||||
var sketchLayers = sketch.layers;
|
let sketchLayers = sketch.layers;
|
||||||
var boundary = sketch.boundary;
|
let boundary = sketch.boundary;
|
||||||
var boundaryNeedsUpdate = !(boundary === undefined || boundary == null);
|
let boundaryNeedsUpdate = !(boundary === undefined || boundary == null);
|
||||||
|
const dimensions = [];
|
||||||
if (sketchLayers !== undefined) {
|
if (sketchLayers !== undefined) {
|
||||||
for (var l = 0; l < sketchLayers.length; ++l) {
|
for (let l = 0; l < sketchLayers.length; ++l) {
|
||||||
var ioLayer = sketchLayers[l];
|
let ioLayer = sketchLayers[l];
|
||||||
var layerName = ioLayer.name;
|
let layerName = ioLayer.name;
|
||||||
var boundaryProcessing = layerName === IO.BOUNDARY_LAYER_NAME && boundaryNeedsUpdate;
|
let boundaryProcessing = layerName === IO.BOUNDARY_LAYER_NAME && boundaryNeedsUpdate;
|
||||||
var layer = getLayer(this.viewer, layerName);
|
let layer = getLayer(this.viewer, layerName);
|
||||||
// if (!!ioLayer.style) layer.style = ioLayer.style;
|
// if (!!ioLayer.style) layer.style = ioLayer.style;
|
||||||
layer.readOnly = !!ioLayer.readOnly;
|
layer.readOnly = !!ioLayer.readOnly;
|
||||||
var layerData = ioLayer.data;
|
let layerData = ioLayer.data;
|
||||||
for (let i = 0; i < layerData.length; ++i) {
|
for (let i = 0; i < layerData.length; ++i) {
|
||||||
var obj = layerData[i];
|
let obj = layerData[i];
|
||||||
var skobj = null;
|
let skobj = null;
|
||||||
var _class = obj._class;
|
let _class = obj._class;
|
||||||
var aux = !!obj.aux;
|
let aux = !!obj.aux;
|
||||||
var role = obj.role;
|
let role = obj.role;
|
||||||
|
|
||||||
//support legacy format
|
//support legacy format
|
||||||
if (!role) {
|
if (!role) {
|
||||||
|
|
@ -150,17 +151,8 @@ IO.prototype._loadSketch = function(sketch) {
|
||||||
const cp1 = endPoint(obj.cp1);
|
const cp1 = endPoint(obj.cp1);
|
||||||
const cp2 = endPoint(obj.cp2);
|
const cp2 = endPoint(obj.cp2);
|
||||||
skobj = new BezierCurve(a, b, cp1, cp2);
|
skobj = new BezierCurve(a, b, cp1, cp2);
|
||||||
} else if (_class === T.HDIM) {
|
} else {
|
||||||
skobj = new HDimension(obj.a, obj.b);
|
dimensions.push(obj);
|
||||||
obj.offset !== undefined && (skobj.offset = obj.offset);
|
|
||||||
} else if (_class === T.VDIM) {
|
|
||||||
skobj = new VDimension(obj.a, obj.b);
|
|
||||||
obj.offset !== undefined && (skobj.offset = obj.offset);
|
|
||||||
} else if (_class === T.DIM) {
|
|
||||||
skobj = new Dimension(obj.a, obj.b);
|
|
||||||
obj.offset !== undefined && (skobj.offset = obj.offset);
|
|
||||||
} else if (_class === T.DDIM) {
|
|
||||||
skobj = new DiameterDimension(obj.obj);
|
|
||||||
}
|
}
|
||||||
if (skobj != null) {
|
if (skobj != null) {
|
||||||
skobj.role = role;
|
skobj.role = role;
|
||||||
|
|
@ -192,14 +184,27 @@ IO.prototype._loadSketch = function(sketch) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this.viewer.dimLayer.objects.length; ++i) {
|
for (let obj of dimensions) {
|
||||||
obj = this.viewer.dimLayer.objects[i];
|
let _class = obj._class;
|
||||||
if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) {
|
let skobj = null;
|
||||||
obj.a = index[obj.a];
|
if (_class === T.HDIM) {
|
||||||
obj.b = index[obj.b];
|
skobj = new HDimension(index[obj.a], index[obj.b]);
|
||||||
} else if (obj._class === T.DDIM) {
|
obj.offset !== undefined && (skobj.offset = obj.offset);
|
||||||
obj.obj = index[obj.obj];
|
} else if (_class === T.VDIM) {
|
||||||
|
skobj = new VDimension(index[obj.a], index[obj.b]);
|
||||||
|
obj.offset !== undefined && (skobj.offset = obj.offset);
|
||||||
|
} else if (_class === T.DIM) {
|
||||||
|
skobj = new Dimension(index[obj.a], index[obj.b]);
|
||||||
|
obj.offset !== undefined && (skobj.offset = obj.offset);
|
||||||
|
} else if (_class === T.DDIM) {
|
||||||
|
skobj = new DiameterDimension(index[obj.obj]);
|
||||||
|
skobj.angle = obj.angle;
|
||||||
|
} else if (_class === T.ANGLE_BW) {
|
||||||
|
skobj = new AngleBetweenDimension(index[obj.a], index[obj.b]);
|
||||||
|
skobj.offset = obj.offset;
|
||||||
}
|
}
|
||||||
|
this.viewer.dimLayer.add(skobj);
|
||||||
|
index[obj.id] = skobj;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (boundaryNeedsUpdate) {
|
if (boundaryNeedsUpdate) {
|
||||||
|
|
@ -391,6 +396,11 @@ IO.prototype._serializeSketch = function(metadata) {
|
||||||
to.offset = obj.offset;
|
to.offset = obj.offset;
|
||||||
} else if (obj._class === T.DDIM) {
|
} else if (obj._class === T.DDIM) {
|
||||||
to.obj = obj.obj.id;
|
to.obj = obj.obj.id;
|
||||||
|
to.angle = obj.obj.angle;
|
||||||
|
} else if (obj._class === T.ANGLE_BW) {
|
||||||
|
to.a = obj.a.id;
|
||||||
|
to.b = obj.b.id;
|
||||||
|
to.offset = obj.offset;
|
||||||
}
|
}
|
||||||
const children = nonPointChildren(obj).map(c => c.id);
|
const children = nonPointChildren(obj).map(c => c.id);
|
||||||
if (children.length !== 0) {
|
if (children.length !== 0) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import * as math from '../../math/math'
|
import * as math from '../../math/math'
|
||||||
import {pointToLineSignedDistance} from '../../math/math'
|
import * as vec from '../../math/vec'
|
||||||
|
import {DEG_RAD, lineLineIntersection2d, makeAngle0_360, pointToLineSignedDistance} from '../../math/math'
|
||||||
import Vector from 'math/vector';
|
import Vector from 'math/vector';
|
||||||
import {SketchObject} from './sketch-object'
|
import {SketchObject} from './sketch-object'
|
||||||
import {Styles} from "../styles";
|
import {Styles} from "../styles";
|
||||||
|
|
@ -12,6 +13,8 @@ const ARROW_H_PX = 4;
|
||||||
const ARROW_TO_TEXT_PAD_PX = 2;
|
const ARROW_TO_TEXT_PAD_PX = 2;
|
||||||
const TEXT_H_OFFSET = 3;
|
const TEXT_H_OFFSET = 3;
|
||||||
const OUTER_ARROW_TO_TEXT_PAD_PX = 6;
|
const OUTER_ARROW_TO_TEXT_PAD_PX = 6;
|
||||||
|
const EXT_LINEAR_WIDTH_PX = 7;
|
||||||
|
const EXT_ANGULAR_WIDTH_PX = 10;
|
||||||
|
|
||||||
function drawArrow(ctx, x, y, nx, ny, arrowW, arrowH) {
|
function drawArrow(ctx, x, y, nx, ny, arrowW, arrowH) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
|
@ -21,6 +24,14 @@ function drawArrow(ctx, x, y, nx, ny, arrowW, arrowH) {
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawArrowForArc(ctx, px, py, x, y, nx, ny, arrowW, arrowH) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y, 0);
|
||||||
|
ctx.lineTo(px + nx * arrowH, py + ny * arrowH );
|
||||||
|
ctx.lineTo(px - nx * arrowH, py - ny * arrowH);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
function drawExtensionLine(ctx, x, y, nx, ny, width, tip, arrowW) {
|
function drawExtensionLine(ctx, x, y, nx, ny, width, tip, arrowW) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x + ny * arrowW, y + -nx * arrowW);
|
ctx.moveTo(x + ny * arrowW, y + -nx * arrowW);
|
||||||
|
|
@ -119,7 +130,7 @@ class LinearDimension extends SketchObject {
|
||||||
function drawRef(start, x, y) {
|
function drawRef(start, x, y) {
|
||||||
var vec = new Vector(x - start.x, y - start.y);
|
var vec = new Vector(x - start.x, y - start.y);
|
||||||
vec._normalize();
|
vec._normalize();
|
||||||
vec._multiply(7 * unscale);
|
vec._multiply(EXT_LINEAR_WIDTH_PX * unscale);
|
||||||
|
|
||||||
ctx.moveTo(start.x, start.y );
|
ctx.moveTo(start.x, start.y );
|
||||||
ctx.lineTo(x, y);
|
ctx.lineTo(x, y);
|
||||||
|
|
@ -375,9 +386,337 @@ export class DiameterDimension extends SketchObject {
|
||||||
}
|
}
|
||||||
DiameterDimension.prototype._class = 'TCAD.TWO.DiameterDimension';
|
DiameterDimension.prototype._class = 'TCAD.TWO.DiameterDimension';
|
||||||
|
|
||||||
|
export class AngleBetweenDimension extends SketchObject {
|
||||||
|
|
||||||
function getTextOff(scale) {
|
constructor(a, b) {
|
||||||
return 3 * scale;
|
super();
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
this.offset = 20;
|
||||||
|
this.pickA = [];
|
||||||
|
this.pickB = [];
|
||||||
|
this.textHelper = new TextHelper();
|
||||||
|
this.configuration = [this.a.a, this.a.b, this.b.a, this.b.b];
|
||||||
|
this.pickInfo = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
visitParams(callback) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getReferencePoint() {
|
||||||
|
return this.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
translateImpl(dx, dy) {
|
||||||
|
|
||||||
|
const [_ax, _ay] = this.pickA;
|
||||||
|
const [_bx, _by] = this.pickB;
|
||||||
|
|
||||||
|
let _vx = - (_by - _ay);
|
||||||
|
let _vy = _bx - _ax;
|
||||||
|
|
||||||
|
const d = math.distance(_ax, _ay, _bx, _by);
|
||||||
|
|
||||||
|
//normalize
|
||||||
|
let _vxn = _vx / d;
|
||||||
|
let _vyn = _vy / d;
|
||||||
|
|
||||||
|
this.offset += (dx * _vxn + dy * _vyn);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawImpl(ctx, scale, viewer) {
|
||||||
|
const marked = this.markers.length !== 0;
|
||||||
|
|
||||||
|
if (marked) {
|
||||||
|
ctx.save();
|
||||||
|
viewer.setStyle(Styles.HIGHLIGHT, ctx);
|
||||||
|
}
|
||||||
|
this.drawDimension(ctx, scale, viewer)
|
||||||
|
|
||||||
|
if (marked) {
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawDimension(ctx, scale, viewer) {
|
||||||
|
|
||||||
|
const unscale = viewer.unscale;
|
||||||
|
let off = this.offset;
|
||||||
|
const MIN_OFFSET_PX = 20;
|
||||||
|
if (off * scale < MIN_OFFSET_PX) {
|
||||||
|
off = MIN_OFFSET_PX * unscale;
|
||||||
|
}
|
||||||
|
const textOff = unscale * TEXT_H_OFFSET;
|
||||||
|
|
||||||
|
let [aa, ab, ba, bb] = this.configuration;
|
||||||
|
|
||||||
|
let aAng = makeAngle0_360(Math.atan2(ab.y - aa.y, ab.x - aa.x));
|
||||||
|
let bAng = makeAngle0_360(Math.atan2(bb.y - ba.y, bb.x - ba.x));
|
||||||
|
let ang = makeAngle0_360(bAng - aAng);
|
||||||
|
if (ang > Math.PI) {
|
||||||
|
this.configuration.reverse();
|
||||||
|
[aa, ab, ba, bb] = this.configuration;
|
||||||
|
aAng = makeAngle0_360(Math.atan2(ab.y - aa.y, ab.x - aa.x));
|
||||||
|
bAng = makeAngle0_360(Math.atan2(bb.y - ba.y, bb.x - ba.x));
|
||||||
|
ang = makeAngle0_360(bAng - aAng);
|
||||||
|
}
|
||||||
|
// this.a.syncGeometry();
|
||||||
|
// this.b.syncGeometry && this.b.syncGeometry();
|
||||||
|
|
||||||
|
let avx = Math.cos(aAng);
|
||||||
|
let avy = Math.sin(aAng);
|
||||||
|
let bvx = Math.cos(bAng);
|
||||||
|
let bvy = Math.sin(bAng);
|
||||||
|
|
||||||
|
|
||||||
|
this.center = findCenter(aa, ab, ba, bb, avx, avy, bvx, bvy);
|
||||||
|
|
||||||
|
if (!this.center) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [cx, cy] = this.center;
|
||||||
|
|
||||||
|
// if (distanceSquared(aa.x, aa.y, cx, cy) > distanceSquared(ab.x, ab.y, cx, cy)) {
|
||||||
|
// aAng = makeAngle0_360(aAng + Math.PI);
|
||||||
|
// avx *= -1;
|
||||||
|
// avy *= -1;
|
||||||
|
// }
|
||||||
|
// if (distanceSquared(ba.x, ba.y, cx, cy) > distanceSquared(bb.x, bb.y, cx, cy)) {
|
||||||
|
// bAng = makeAngle0_360(bAng + Math.PI);
|
||||||
|
// bvx *= -1;
|
||||||
|
// bvy *= -1;
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
const halfAng = 0.5 * ang;
|
||||||
|
|
||||||
|
let _ax = cx + off * avx;
|
||||||
|
let _ay = cy + off * avy;
|
||||||
|
let _bx = cx + off * bvx;
|
||||||
|
let _by = cy + off * bvy;
|
||||||
|
|
||||||
|
|
||||||
|
const _vxn = Math.cos(aAng + halfAng);
|
||||||
|
const _vyn = Math.sin(aAng + halfAng);
|
||||||
|
|
||||||
|
const mx = cx + off * _vxn;
|
||||||
|
const my = cy + off * _vyn;
|
||||||
|
|
||||||
|
const arrowWpx = ARROW_W_PX;
|
||||||
|
const arrowW = arrowWpx * unscale;
|
||||||
|
const arrowH = ARROW_H_PX * unscale;
|
||||||
|
|
||||||
|
const txt = (1 / DEG_RAD * ang).toFixed(2) + '°';
|
||||||
|
|
||||||
|
this.textHelper.prepare(txt, ctx, viewer);
|
||||||
|
|
||||||
|
let sinPhi = arrowW / off;
|
||||||
|
const cosPhi = Math.sqrt(1 - sinPhi * sinPhi);
|
||||||
|
|
||||||
|
if (cosPhi !== cosPhi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let arrLxV = avx * cosPhi - avy * sinPhi;
|
||||||
|
let arrLyV = avx * sinPhi + avy * cosPhi;
|
||||||
|
|
||||||
|
let arrLx = cx + off*(arrLxV);
|
||||||
|
let arrLy = cy + off*(arrLyV);
|
||||||
|
|
||||||
|
sinPhi *= -1;
|
||||||
|
|
||||||
|
let arrRxV = bvx * cosPhi - bvy * sinPhi;
|
||||||
|
let arrRyV = bvx * sinPhi + bvy * cosPhi;
|
||||||
|
|
||||||
|
let arrRx = cx + off*(arrRxV);
|
||||||
|
let arrRy = cy + off*(arrRyV);
|
||||||
|
|
||||||
|
|
||||||
|
const availableArea = math.distance(arrLx, arrLy, arrRx, arrRy);
|
||||||
|
|
||||||
|
const modelTextWidth = this.textHelper.modelTextWidth;
|
||||||
|
|
||||||
|
const innerMode = modelTextWidth <= availableArea;
|
||||||
|
|
||||||
|
let tx, ty;
|
||||||
|
|
||||||
|
if (innerMode) {
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, off, Math.atan2(arrLyV, arrLxV), Math.atan2(arrRyV, arrRxV));
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
drawArrowForArc(ctx, arrLx, arrLy, _ax, _ay, -arrLxV, -arrLyV, arrowW, arrowH);
|
||||||
|
drawArrowForArc(ctx, arrRx, arrRy, _bx, _by, arrRxV, arrRyV, arrowW, arrowH);
|
||||||
|
|
||||||
|
const h = modelTextWidth/2;
|
||||||
|
tx = (mx + _vxn * textOff) + (- _vyn) * h;
|
||||||
|
ty = (my + _vyn * textOff) + ( _vxn) * h;
|
||||||
|
this.textHelper.draw(tx, ty, _vxn, _vyn, ctx, unscale, viewer, textOff, true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, off, aAng, bAng);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
//sin is inverted by this time
|
||||||
|
|
||||||
|
arrLxV = avx * cosPhi - avy * sinPhi;
|
||||||
|
arrLyV = avx * sinPhi + avy * cosPhi;
|
||||||
|
|
||||||
|
arrLx = cx + off*(arrLxV);
|
||||||
|
arrLy = cy + off*(arrLyV);
|
||||||
|
|
||||||
|
sinPhi *= -1;
|
||||||
|
|
||||||
|
arrRxV = bvx * cosPhi - bvy * sinPhi;
|
||||||
|
arrRyV = bvx * sinPhi + bvy * cosPhi;
|
||||||
|
|
||||||
|
arrRx = cx + off*(arrRxV);
|
||||||
|
arrRy = cy + off*(arrRyV);
|
||||||
|
|
||||||
|
drawArrowForArc(ctx, arrLx, arrLy, _ax, _ay, -arrLxV, -arrLyV, arrowW, arrowH);
|
||||||
|
drawArrowForArc(ctx, arrRx, arrRy, _bx, _by, arrRxV, arrRyV, arrowW, arrowH);
|
||||||
|
|
||||||
|
const longExt = modelTextWidth + 2 * OUTER_ARROW_TO_TEXT_PAD_PX * unscale;
|
||||||
|
const shortExt = OUTER_ARROW_TO_TEXT_PAD_PX * unscale;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(arrLx, arrLy);
|
||||||
|
ctx.lineTo(arrLx + arrLyV * longExt, arrLy - arrLxV * longExt);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(arrRx, arrRy);
|
||||||
|
ctx.lineTo(arrRx - arrRyV * shortExt, arrRy + arrRxV * shortExt);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
|
||||||
|
tx = arrLx - ( -arrLyV) * OUTER_ARROW_TO_TEXT_PAD_PX * unscale + arrLxV * textOff;
|
||||||
|
ty = arrLy - ( arrLxV) * OUTER_ARROW_TO_TEXT_PAD_PX * unscale + arrLyV * textOff;
|
||||||
|
|
||||||
|
this.textHelper.draw(tx, ty, arrLxV, arrLyV, ctx, unscale, viewer, textOff, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPickInfo(cx, cy, _ax, _ay, _bx, _by, off);
|
||||||
|
|
||||||
|
function drawRef(a, b, px, py, vx, vy) {
|
||||||
|
|
||||||
|
const abx = b.x - a.x;
|
||||||
|
const aby = b.y - a.y;
|
||||||
|
|
||||||
|
const apx = px - a.x;
|
||||||
|
const apy = py - a.y;
|
||||||
|
|
||||||
|
const dot = abx * apx + aby * apy;
|
||||||
|
|
||||||
|
|
||||||
|
if (dot < 0) {
|
||||||
|
ctx.save();
|
||||||
|
viewer.setStyle(Styles.CONSTRUCTION, ctx);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(a.x, a.y);
|
||||||
|
ctx.lineTo(px - vx * EXT_ANGULAR_WIDTH_PX * unscale, py - vy * EXT_ANGULAR_WIDTH_PX * unscale);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
} else if (apx * apx + apy * apy > abx * abx + aby * aby) {
|
||||||
|
ctx.save();
|
||||||
|
viewer.setStyle(Styles.CONSTRUCTION, ctx);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(b.x, b.y);
|
||||||
|
ctx.lineTo(px + vx * EXT_ANGULAR_WIDTH_PX * unscale, py + vy * EXT_ANGULAR_WIDTH_PX * unscale);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawRef(aa, ab, _ax, _ay, avx, avy);
|
||||||
|
drawRef(ba, bb, _bx, _by, bvx, bvy);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPickInfo(cx, cy, ax, ay, bx, by, rad) {
|
||||||
|
for (let i = 0; i < arguments.length; ++i) {
|
||||||
|
this.pickInfo[i] = arguments[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
normalDistance(aim, scale) {
|
||||||
|
|
||||||
|
const textDist = this.textHelper.normalDistance(aim);
|
||||||
|
|
||||||
|
if (textDist !== -1) {
|
||||||
|
return textDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.pickInfo.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [cx, cy, ax, ay, bx, by, rad] = this.pickInfo;
|
||||||
|
|
||||||
|
function isPointInsideSector(x, y) {
|
||||||
|
const ca = [ax - cx, ay - cy];
|
||||||
|
const cb = [bx - cx, by - cy];
|
||||||
|
const ct = [x - cx, y - cy];
|
||||||
|
|
||||||
|
vec._normalize(ca);
|
||||||
|
vec._normalize(cb);
|
||||||
|
vec._normalize(ct);
|
||||||
|
const cosAB = vec.dot(ca, cb);
|
||||||
|
const cosAT = vec.dot(ca, ct);
|
||||||
|
|
||||||
|
const isInside = cosAT >= cosAB;
|
||||||
|
const abInverse = vec.cross2d(ca, cb) < 0;
|
||||||
|
const atInverse = vec.cross2d(ca, ct) < 0;
|
||||||
|
|
||||||
|
let result;
|
||||||
|
if (abInverse) {
|
||||||
|
result = !atInverse || !isInside;
|
||||||
|
} else {
|
||||||
|
result = !atInverse && isInside;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInsideSector = isPointInsideSector(aim.x, aim.y);
|
||||||
|
if (isInsideSector) {
|
||||||
|
return Math.abs(math.distance(aim.x, aim.y, cx, cy) - rad);
|
||||||
|
} else {
|
||||||
|
return Math.min(
|
||||||
|
math.distance(aim.x, aim.y, ax, ay),
|
||||||
|
math.distance(aim.x, aim.y, bx, by)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function findCenter(aa, ab, ba, bb, avx, avy, bvx, bvy) {
|
||||||
|
let center = lineLineIntersection2d([aa.x, aa.y], [ba.x, ba.y], [avx, avy], [bvx, bvy]);
|
||||||
|
|
||||||
|
if (!center) {
|
||||||
|
let commonPt = null;
|
||||||
|
aa.visitLinked(p => {
|
||||||
|
if (ba === p || bb === p) {
|
||||||
|
commonPt = aa;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!commonPt) {
|
||||||
|
ab.visitLinked(p => {
|
||||||
|
if (ba === p || bb === p) {
|
||||||
|
commonPt = ab;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!commonPt) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
center = commonPt.toVectorArray();
|
||||||
|
}
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
|
||||||
|
AngleBetweenDimension.prototype._class = 'TCAD.TWO.AngleBetweenDimension';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,6 @@ export const SketchTypes = {
|
||||||
DIM : 'TCAD.TWO.Dimension',
|
DIM : 'TCAD.TWO.Dimension',
|
||||||
HDIM : 'TCAD.TWO.HDimension',
|
HDIM : 'TCAD.TWO.HDimension',
|
||||||
VDIM : 'TCAD.TWO.VDimension',
|
VDIM : 'TCAD.TWO.VDimension',
|
||||||
DDIM : 'TCAD.TWO.DiameterDimension'
|
DDIM : 'TCAD.TWO.DiameterDimension',
|
||||||
|
ANGLE_BW : 'TCAD.TWO.AngleBetweenDimension'
|
||||||
};
|
};
|
||||||
|
|
@ -15,7 +15,7 @@ export class TextHelper {
|
||||||
this.txt = txt;
|
this.txt = txt;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(tx, ty, nx, ny, ctx, unscale, viewer, flipOffset) {
|
draw(tx, ty, nx, ny, ctx, unscale, viewer, flipOffset, staticFlip = false) {
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
let rot = makeAngle0_360(Math.atan2(-nx, ny));
|
let rot = makeAngle0_360(Math.atan2(-nx, ny));
|
||||||
|
|
@ -31,10 +31,12 @@ export class TextHelper {
|
||||||
let dty = [modelTextHeight * nx, ny * modelTextHeight];
|
let dty = [modelTextHeight * nx, ny * modelTextHeight];
|
||||||
|
|
||||||
if (flip) {
|
if (flip) {
|
||||||
const dx = ny * modelTextWidth - nx * 2 *flipOffset;
|
tx += ny * modelTextWidth;
|
||||||
const dy = -nx * modelTextWidth - ny * 2*flipOffset;
|
ty += -nx * modelTextWidth;
|
||||||
tx += dx;
|
const k = staticFlip ? 1.5 : -1;
|
||||||
ty += dy;
|
tx += k * nx * 2 *flipOffset;
|
||||||
|
ty += k * ny * 2 *flipOffset;
|
||||||
|
|
||||||
_negate(dtx);
|
_negate(dtx);
|
||||||
_negate(dty);
|
_negate(dty);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,11 @@ export class EditCircleTool extends Tool {
|
||||||
this.circle = new Circle(
|
this.circle = new Circle(
|
||||||
new EndPoint(p.x, p.y)
|
new EndPoint(p.x, p.y)
|
||||||
);
|
);
|
||||||
if (needSnap) this.viewer.parametricManager.coincidePoints(this.circle.c, p);
|
|
||||||
this.pointPicked(this.circle.c.x, this.circle.c.y);
|
this.pointPicked(this.circle.c.x, this.circle.c.y);
|
||||||
this.sendHint('specify radius');
|
this.sendHint('specify radius');
|
||||||
this.viewer.activeLayer.add(this.circle);
|
this.viewer.activeLayer.add(this.circle);
|
||||||
this.viewer.parametricManager.prepare([this.circle]);
|
this.viewer.parametricManager.prepare([this.circle]);
|
||||||
|
if (needSnap) this.viewer.parametricManager.coincidePoints(this.circle.c, p);
|
||||||
this.viewer.refresh();
|
this.viewer.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import {HDimension, VDimension, Dimension, DiameterDimension} from '../shapes/dim'
|
import {AngleBetweenDimension, DiameterDimension, Dimension, findCenter, HDimension, VDimension,} from '../shapes/dim'
|
||||||
import Vector from 'math/vector';
|
import Vector from 'math/vector';
|
||||||
import {EndPoint} from '../shapes/point'
|
import {EndPoint} from '../shapes/point'
|
||||||
import {Tool} from './tool'
|
import {Tool} from './tool'
|
||||||
|
|
@ -6,6 +6,8 @@ import {DragTool} from "./drag";
|
||||||
import {isInstanceOf} from "../actions/matchUtils";
|
import {isInstanceOf} from "../actions/matchUtils";
|
||||||
import {Segment} from "../shapes/segment";
|
import {Segment} from "../shapes/segment";
|
||||||
import {DEFAULT_SEARCH_BUFFER} from "../viewer2d";
|
import {DEFAULT_SEARCH_BUFFER} from "../viewer2d";
|
||||||
|
import {distance} from "../../math/math";
|
||||||
|
import {_negate, cross2d} from "../../math/vec";
|
||||||
|
|
||||||
export class AddDimTool extends Tool {
|
export class AddDimTool extends Tool {
|
||||||
|
|
||||||
|
|
@ -97,14 +99,14 @@ export class AddCircleDimTool extends Tool {
|
||||||
return o._class === 'TCAD.TWO.Circle' || o._class === 'TCAD.TWO.Arc';
|
return o._class === 'TCAD.TWO.Circle' || o._class === 'TCAD.TWO.Arc';
|
||||||
});
|
});
|
||||||
|
|
||||||
if (objects.length != 0) {
|
if (objects.length !== 0) {
|
||||||
this.dim.obj = objects[0];
|
this.dim.obj = objects[0];
|
||||||
this.viewer.capture('tool', [this.dim.obj], true);
|
this.viewer.capture('tool', [this.dim.obj], true);
|
||||||
} else {
|
} else {
|
||||||
this.dim.obj = null;
|
this.dim.obj = null;
|
||||||
this.viewer.withdrawAll('tool');
|
this.viewer.withdrawAll('tool');
|
||||||
}
|
}
|
||||||
if (this.dim.obj != null) {
|
if (this.dim.obj !== null) {
|
||||||
this.dim.angle = Math.atan2(p.y - this.dim.obj.c.y, p.x - this.dim.obj.c.x);
|
this.dim.angle = Math.atan2(p.y - this.dim.obj.c.y, p.x - this.dim.obj.c.x);
|
||||||
}
|
}
|
||||||
this.viewer.refresh();
|
this.viewer.refresh();
|
||||||
|
|
@ -122,3 +124,127 @@ export class AddCircleDimTool extends Tool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AddAngleTool extends Tool {
|
||||||
|
|
||||||
|
constructor(name, viewer, layer, dimCreation) {
|
||||||
|
super(name, viewer);
|
||||||
|
this.layer = layer;
|
||||||
|
this.a = null;
|
||||||
|
this.dim = null;
|
||||||
|
this.dimCreation = dimCreation;
|
||||||
|
}
|
||||||
|
|
||||||
|
mousemove(e) {
|
||||||
|
const p = this.viewer.screenToModel(e);
|
||||||
|
|
||||||
|
const result = this.viewer.search(p.x, p.y, DEFAULT_SEARCH_BUFFER, true, false, []).filter(o => o._class === 'TCAD.TWO.Segment');
|
||||||
|
const [segment] = result;
|
||||||
|
|
||||||
|
if (this.dim) {
|
||||||
|
let [center, configuration] = this.classify(p.x, p.y);
|
||||||
|
if (configuration) {
|
||||||
|
this.dim.configuration = configuration;
|
||||||
|
}
|
||||||
|
if (!center) {
|
||||||
|
center = segment.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [cx, cy] = center;
|
||||||
|
this.dim.offset = distance(cx, cy, p.x, p.y);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (segment) {
|
||||||
|
this.viewer.capture('tool', [segment], true);
|
||||||
|
} else {
|
||||||
|
this.viewer.withdrawAll('tool');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.viewer.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseup(e) {
|
||||||
|
|
||||||
|
const p = this.viewer.screenToModel(e);
|
||||||
|
|
||||||
|
const [segment] = this.viewer.captured.tool;
|
||||||
|
this.viewer.withdrawAll('tool');
|
||||||
|
|
||||||
|
if (this.a === null) {
|
||||||
|
if (!segment) {
|
||||||
|
this.viewer.toolManager.releaseControl();
|
||||||
|
this.viewer.refresh();
|
||||||
|
}
|
||||||
|
this.a = segment;
|
||||||
|
} else if (this.dim == null) {
|
||||||
|
if (!segment) {
|
||||||
|
this.viewer.toolManager.releaseControl();
|
||||||
|
this.viewer.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dim = this.dimCreation(this.a, segment);
|
||||||
|
let [center, configuration] = this.classify(p.x, p.y);
|
||||||
|
if (configuration) {
|
||||||
|
this.dim.configuration = configuration;
|
||||||
|
}
|
||||||
|
if (!center) {
|
||||||
|
center = segment.a;
|
||||||
|
}
|
||||||
|
const [cx, cy] = center;
|
||||||
|
this.dim.offset = distance(cx, cy, p.x, p.y);
|
||||||
|
this.layer.add(this.dim);
|
||||||
|
this.viewer.refresh();
|
||||||
|
} else {
|
||||||
|
this.viewer.toolManager.releaseControl();
|
||||||
|
this.viewer.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classify(px, py) {
|
||||||
|
|
||||||
|
const line1 = this.dim.a, line2 = this.dim.b;
|
||||||
|
|
||||||
|
const v1 = [line1.ny, - line1.nx];
|
||||||
|
const v2 = [line2.ny, - line2.nx];
|
||||||
|
|
||||||
|
const isec = findCenter(line1.a, line1.b, line2.a, line2.b, v1[0], v1[1], v2[0], v2[1]);
|
||||||
|
if (!isec) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const [cx, cy] = isec;
|
||||||
|
const v = [px - cx, py - cy];
|
||||||
|
|
||||||
|
const insideSector = (v, v1, v2) => cross2d(v1, v) > 0 && cross2d(v2, v) < 0;
|
||||||
|
|
||||||
|
if (insideSector(v, v1, v2)) {
|
||||||
|
return [isec, [line1.a, line1.b, line2.a, line2.b]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insideSector(v, v2, _negate(v1))) {
|
||||||
|
return [isec, [line2.a, line2.b, line1.b, line1.a]];
|
||||||
|
}
|
||||||
|
_negate(v1);
|
||||||
|
|
||||||
|
if (insideSector(v, _negate(v1), _negate(v2))) {
|
||||||
|
return [isec, [line1.b, line1.a, line2.b, line2.a]];
|
||||||
|
}
|
||||||
|
_negate(v1);
|
||||||
|
_negate(v2);
|
||||||
|
|
||||||
|
if (insideSector(v, _negate(v2), v1)) {
|
||||||
|
return [isec, [line2.b, line2.a, line1.a, line1.b]];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return [isec];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddAngleBetweenDimTool extends AddAngleTool {
|
||||||
|
constructor(viewer, layer) {
|
||||||
|
super('angle between dimension', viewer, layer, (a, b) => new AngleBetweenDimension(a, b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export class DragTool extends Tool {
|
||||||
this.obj.translate(dx, dy);
|
this.obj.translate(dx, dy);
|
||||||
}
|
}
|
||||||
// this.viewer.parametricManager.setConstantsFromGeometry(this.obj);
|
// this.viewer.parametricManager.setConstantsFromGeometry(this.obj);
|
||||||
if (!Tool.dumbMode(e) || this.obj.constraints.length !== 0) {
|
if (!Tool.dumbMode(e) && this.obj.constraints.length !== 0) {
|
||||||
// this.viewer.parametricManager.prepare();
|
// this.viewer.parametricManager.prepare();
|
||||||
this.viewer.parametricManager.solve(true);
|
this.viewer.parametricManager.solve(true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import {Ellipse} from '../shapes/ellipse'
|
import {Ellipse} from '../shapes/ellipse'
|
||||||
import {EllipticalArc} from '../shapes/elliptical-arc'
|
|
||||||
import {Circle} from '../shapes/circle'
|
import {Circle} from '../shapes/circle'
|
||||||
import {EditCircleTool} from './circle'
|
import {EditCircleTool} from './circle'
|
||||||
import {DragTool} from './drag'
|
import {DragTool} from './drag'
|
||||||
import {EllipseTool, STATE_RADIUS} from './ellipse'
|
import {EllipseTool, STATE_RADIUS} from './ellipse'
|
||||||
|
import {AngleBetweenDimension} from "../shapes/dim";
|
||||||
|
import {AddAngleBetweenDimTool} from "./dim";
|
||||||
|
|
||||||
export function GetShapeEditTool(viewer, obj, alternative) {
|
export function GetShapeEditTool(viewer, obj, alternative) {
|
||||||
if (obj instanceof Circle && !alternative) {
|
if (obj instanceof Circle && !alternative) {
|
||||||
|
|
@ -18,6 +19,11 @@ export function GetShapeEditTool(viewer, obj, alternative) {
|
||||||
tool.ellipse = obj;
|
tool.ellipse = obj;
|
||||||
tool.state = STATE_RADIUS;
|
tool.state = STATE_RADIUS;
|
||||||
return tool;
|
return tool;
|
||||||
|
} else if (obj instanceof AngleBetweenDimension && !alternative) {
|
||||||
|
const tool = new AddAngleBetweenDimTool(viewer, viewer.dimLayer);
|
||||||
|
tool.a = obj.a;
|
||||||
|
tool.dim = obj;
|
||||||
|
return tool;
|
||||||
} else {
|
} else {
|
||||||
return new DragTool(obj, viewer);
|
return new DragTool(obj, viewer);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ export class Tool {
|
||||||
}
|
}
|
||||||
|
|
||||||
static dumbMode(e) {
|
static dumbMode(e) {
|
||||||
return e.ctrlKey || e.metaKey;
|
return e.ctrlKey || e.metaKey || e.altKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -240,6 +240,9 @@ class Viewer {
|
||||||
|
|
||||||
this.__prevStyle = null;
|
this.__prevStyle = null;
|
||||||
|
|
||||||
|
this.interactiveScale = this.scale / this.retinaPxielRatio;
|
||||||
|
this.unscale = 1 / this.interactiveScale;
|
||||||
|
|
||||||
this.__drawWorkspace(ctx, this._workspace, Viewer.__SKETCH_DRAW_PIPELINE);
|
this.__drawWorkspace(ctx, this._workspace, Viewer.__SKETCH_DRAW_PIPELINE);
|
||||||
this.__drawWorkspace(ctx, this._serviceWorkspace, Viewer.__SIMPLE_DRAW_PIPELINE);
|
this.__drawWorkspace(ctx, this._serviceWorkspace, Viewer.__SIMPLE_DRAW_PIPELINE);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue