stable ids, new saving format

This commit is contained in:
Val Erastov (xibyte) 2020-05-14 03:49:53 -07:00
parent 0dc66251d9
commit 9730e2c8ec
49 changed files with 1449 additions and 1186 deletions

57
modules/lstream/index.d.ts vendored Normal file
View file

@ -0,0 +1,57 @@
interface StreamBase<T> {
attach(callback: (value: T) => any): () => void
map<T, V>(fn: (value: T) => V);
filter<T>(stream: Stream<T>, predicate: (T) => boolean): Stream<T>;
pairwise(first: T): Stream<[T, T]>;
scan(initAccumulator: any): Stream<any>;
remember(initialValue: T, usingStream: any): Stream<T>
distinct(): Stream<T>;
throttle(delay?: number, accumulator?: any): Stream<T>;
pipe(otherStream): () => void;
}
interface Stream<T> extends StreamBase<T> {
}
interface StateStream<T> extends Stream<T> {
value: T;
next(value?: T) : void;
}
export function stream<T>(): Stream<T>;
export function eventStream<T>(): Stream<T>;
export function combine(...streams: Stream<any>[]): Stream<any[]>;
export function merge(...streams: Stream<any>[]): Stream<any>;
export function state<T>(initialValue: T): StateStream<T>;
export function distinctState<T>(initialValue: T): StateStream<T>;
export function externalState<T>(get: any, set: any): StateStream<T>;
export function never<T>(): Stream<T>
export function constant<T>(value: T): Stream<T>
export function map<T, V>(stream: Stream<T>, fn: (T) => V): Stream<V>;
export function filter<T>(stream: Stream<T>, predicate: (T) => boolean): Stream<T>;

View file

@ -10,7 +10,7 @@ export function testSegmentWizard(env, tpi) {
assertEquals(1, tpi.viewer.activeLayer.objects.length);
const segment = tpi.viewer.activeLayer.objects[0];
assertEquals('TCAD.TWO.Segment', segment._class);
assertEquals('Segment', segment.TYPE);
const [asx, asy] = tpi.toScreen(10, 10);
const [bsx, bsy] = tpi.toScreen(100, 100);
assertPoint2DEquals(tpi.toModel(asx, asy), segment.a);

View file

@ -7,6 +7,7 @@
"allowSyntheticDefaultImports": true,
"target": "ES5",
"baseUrl": ".",
"downlevelIteration": true,
"paths": {
"*": [
"modules/*",

View file

@ -380,25 +380,12 @@ export function createShared() {
return shared;
}
export const CURVE_CLASSES = new Set();
CURVE_CLASSES.add('TCAD.TWO.Arc');
CURVE_CLASSES.add('TCAD.TWO.Circle');
CURVE_CLASSES.add('TCAD.TWO.Ellipse');
CURVE_CLASSES.add('TCAD.TWO.EllipticalArc');
CURVE_CLASSES.add('TCAD.TWO.BezierCurve');
export function isCurveClass(className) {
return CURVE_CLASSES.has(className);
return false;
}
export function isSmoothPiece(shared) {
return shared.__tcad &&
!!shared.__tcad.csgInfo && !!shared.__tcad.csgInfo.derivedFrom
&& isCurveClass(shared.__tcad.csgInfo.derivedFrom._class);
}
var POLYGON_COUNTER = 0;
/** @constructor */
export function Polygon(shell, holes, normal) {
this.id = POLYGON_COUNTER ++;
if (!holes) {

View file

@ -17,8 +17,8 @@ import {InplaceSketcher} from "../../sketch/components/InplaceSketcher";
import {ContextualControls} from "../../../sketcher/components/ContextualControls";
import {ConstraintEditor} from "../../../sketcher/components/ConstraintEditor";
import SketcherOperationWizard from "../../../sketcher/components/SketcherOperationWizard";
import {StageControl} from "../../../sketcher/components/StageControl";
import {ToastContainer} from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
export default class View3d extends React.Component {
@ -28,6 +28,7 @@ export default class View3d extends React.Component {
render() {
return <UISystem className={ls.root}>
<ToastContainer />
<FloatView />
<div className={ls.mainArea} >
<div id='viewer-container' key='viewer-container' />

View file

@ -1,4 +1,4 @@
import React, {Fragment} from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import 'ui/styles/init/index.less';

View file

@ -32,9 +32,9 @@ export default function revolve(polygons, axisSegment, angle, resolution) {
let shared = createShared();
let sketchConnectionObject = pOrig[p].sketchConnectionObject;
if (sketchConnectionObject) {
if (sketchConnectionObject._class == 'TCAD.TWO.Segment') {
if (sketchConnectionObject.TYPE === 'Segment') {
sketchConnectionObject = Object.assign({}, sketchConnectionObject, {
_class: 'TCAD.TWO.Arc',
TYPE: 'Arc',
id: sketchConnectionObject.id + ":REVOLVED" // just avoid having object with the same ID but different classes
});
}

View file

@ -150,7 +150,7 @@ RevolveWizard.prototype.dispose = function() {
};
function isSketchSegment(line) {
return line.__TCAD_SketchObject && line.__TCAD_SketchObject._class === 'TCAD.TWO.Segment';
return line.__TCAD_SketchObject && line.__TCAD_SketchObject.TYPE === 'Segment';
}
function firstSegment(objects) {
for (let line of objects) {

File diff suppressed because one or more lines are too long

View file

@ -62,58 +62,47 @@ export function ReadSketch(sketch, sketchId, readConstructionSegments) {
}
let vectorFactory = new VectorFactory();
let pointsById = new Map();
function ReadSketchPoint(arr) {
let pointId = arr[0];
pointId = coiJoints.master(pointId);
let point = pointsById.get(pointId);
if (!point) {
point = vectorFactory.create(readSketchFloat(arr[1][1]), readSketchFloat(arr[2][1]), 0);
pointsById.set(pointId, point);
}
return point;
function ReadSketchPoint(pt) {
return vectorFactory.create(pt.x, pt.y, 0);
}
if (sketch.layers !== undefined) {
for (let layer of sketch.layers) {
const isConstructionLayer = layer.name === "_construction_";
for (let obj of layer.data) {
let isConstructionObject = isConstructionLayer || obj.role === 'construction';
if (isConstructionObject && !readConstructionSegments) continue;
// if (isConstructionObject && obj._class !== 'TCAD.TWO.Segment') continue;
if (obj.edge !== undefined) continue;
if (!!obj.aux && obj.role !== 'virtual') continue;
if (obj._class === 'TCAD.TWO.Segment') {
const segA = ReadSketchPoint(obj.points[0]);
const segB = ReadSketchPoint(obj.points[1]);
const pushOn = isConstructionObject ? out.constructionSegments : out.connections;
pushOn.push(new sm.Segment(getID(obj), segA, segB));
} else if (obj._class === 'TCAD.TWO.Arc') {
const arcA = ReadSketchPoint(obj.points[0]);
const arcB = ReadSketchPoint(obj.points[1]);
const arcCenter = ReadSketchPoint(obj.points[2]);
out.connections.push(new sm.Arc(getID(obj), arcA, arcB, arcCenter));
} else if (obj._class === 'TCAD.TWO.EllipticalArc') {
const ep1 = ReadSketchPoint(obj.ep1);
const ep2 = ReadSketchPoint(obj.ep2);
const a = ReadSketchPoint(obj.a);
const b = ReadSketchPoint(obj.b);
out.connections.push(new sm.EllipticalArc(getID(obj), ep1, ep2, a, b, readSketchFloat(obj.r)));
} else if (obj._class === 'TCAD.TWO.BezierCurve') {
const a = ReadSketchPoint(obj.a);
const b = ReadSketchPoint(obj.b);
const cp1 = ReadSketchPoint(obj.cp1);
const cp2 = ReadSketchPoint(obj.cp2);
out.connections.push(new sm.BezierCurve(getID(obj), a, b, cp1, cp2));
} else if (obj._class === 'TCAD.TWO.Circle') {
const circleCenter = ReadSketchPoint(obj.c);
out.loops.push(new sm.Circle(getID(obj), circleCenter, readSketchFloat(obj.r)));
} else if (obj._class === 'TCAD.TWO.Ellipse') {
const ep1 = ReadSketchPoint(obj.ep1);
const ep2 = ReadSketchPoint(obj.ep2);
out.loops.push(new sm.Ellipse(getID(obj), ep1, ep2, readSketchFloat(obj.r)));
}
}
if (sketch.version !== 3) {
return out;
}
for (let obj of sketch.objects) {
let isConstructionObject = obj.role === 'construction';
if (isConstructionObject && !readConstructionSegments) continue;
// if (isConstructionObject && obj._class !== 'TCAD.TWO.Segment') continue;
const data = obj.data;
if (obj.type === 'Segment') {
const segA = ReadSketchPoint(data.a);
const segB = ReadSketchPoint(data.b);
const pushOn = isConstructionObject ? out.constructionSegments : out.connections;
pushOn.push(new sm.Segment(getID(obj), segA, segB));
} else if (obj.type === 'Arc') {
const arcA = ReadSketchPoint(data.a);
const arcB = ReadSketchPoint(data.b);
const arcCenter = ReadSketchPoint(data.c);
out.connections.push(new sm.Arc(getID(obj), arcA, arcB, arcCenter));
} else if (obj.type === 'EllipticalArc') {
const ep1 = ReadSketchPoint(data.ep1);
const ep2 = ReadSketchPoint(data.ep2);
const a = ReadSketchPoint(data.a);
const b = ReadSketchPoint(data.b);
out.connections.push(new sm.EllipticalArc(getID(obj), ep1, ep2, a, b, readSketchFloat(data.r)));
} else if (obj.type === 'BezierCurve') {
const a = ReadSketchPoint(data.cp1);
const b = ReadSketchPoint(data.cp4);
const cp1 = ReadSketchPoint(data.cp2);
const cp2 = ReadSketchPoint(data.cp3);
out.connections.push(new sm.BezierCurve(getID(obj), a, b, cp1, cp2));
} else if (obj.type === 'Circle') {
const circleCenter = ReadSketchPoint(data.c);
out.loops.push(new sm.Circle(getID(obj), circleCenter, readSketchFloat(data.r)));
} else if (obj.type === 'Ellipse') {
const ep1 = ReadSketchPoint(data.ep1);
const ep2 = ReadSketchPoint(data.ep2);
out.loops.push(new sm.Ellipse(getID(obj), ep1, ep2, readSketchFloat(data.r)));
}
}
return out;

View file

@ -68,7 +68,7 @@ export function SketchObjectExplorer() {
<div className={ls.titleBar}>Objects</div>
<div className={ls.scrollableArea}>
{objects.map(o => <div key={o.id} className={cx(ls.objectItem, getClassName(o))}>
<span className={ls.objectIcon}><img width="15px" src='img/vec/pointOnArc.svg'/></span>
<span className={ls.objectIcon}><ObjectIcon object={o}/></span>
{getObjectRole(o)}
<span onClick={e => tweakSelection(o, e.shiftKey)}
className={cx(ls.objectTag, o.marked && ls.selected)}>{o.simpleClassName} <span>{o.id}</span> </span>
@ -79,8 +79,8 @@ export function SketchObjectExplorer() {
}
function ObjectIcon({object}) {
return null;
const Icon = object.icon;
return <Icon />;
}
function getClassName() {

View file

@ -15,6 +15,10 @@
border-radius: @itemRadius 0 0 @itemRadius;
padding: 3px 3px;
background-color: @alt-color;
& svg {
width: 16px;
height: 16px;
}
}
.menuButton {

View file

@ -319,7 +319,7 @@ export class AlgNumSubSystem {
});
console.log('with respect to:');
this.substitutionOrder.forEach(x => console.log('X' + x.id + ' = ' + this.substitutedParams.get(x).toString()));
this.substitutionOrder.forEach(x => console.log(x.toString() + ' = ' + this.substitutedParams.get(x).toString()));
}
}

View file

@ -0,0 +1,84 @@
import {NoIcon} from "../icons/NoIcon";
import {NOOP} from "../../../../modules/gems/func";
import {Arc} from "../shapes/arc";
import {EndPoint} from "../shapes/point";
import {Circle} from "../shapes/circle";
import {NurbsObject} from "../shapes/nurbsObject";
import NurbsCurve from "../../brep/geom/curves/nurbsCurve";
import {Segment} from "../shapes/segment";
export const BoundaryGeneratorSchema = {
id: 'Boundary',
title: 'Boundary',
description: 'Generates object comming as a boundary data from the part editor',
internal: true,
icon: NoIcon,
persistGeneratedObjects: false,
params: [
{
name: 'boundaryData',
label: 'Boundary Data',
type: 'object'
},
],
sourceObjects: () => {
},
removeObject(params, generatedObjects, object, destroy, fullDestroy) {
},
initiateState: state => {
},
generate: (params, state) => {
const {boundaryData: boundary} = params;
const out = [];
let i, obj;
function process(obj) {
obj.visitParams(param => {
param.set = NOOP;
});
out.push(obj);
}
for (i = 0; i < boundary.lines.length; ++i) {
let edge = boundary.lines[i];
let seg = new Segment(edge.a.x, edge.a.y, edge.b.x, edge.b.y,'boundary/' + edge.id);
process(seg);
}
for (i = 0; i < boundary.arcs.length; ++i) {
const a = boundary.arcs[i];
const arc = new Arc(
a.a.x, a.a.y,
a.b.x, a.b.y,
a.c.x, a.c.y,
'boundary/' + a.id
);
process(arc);
}
for (i = 0; i < boundary.circles.length; ++i) {
obj = boundary.circles[i];
const circle = new Circle(obj.c.x, obj.c.y, 'boundary/' + obj.id);
circle.r.set(obj.r);
process(circle);
}
for (i = 0; i < boundary.nurbses.length; ++i) {
let nurbsData = boundary.nurbses[i];
let nurbs = new NurbsObject(NurbsCurve.deserialize(nurbsData), 'boundary/' + nurbsData.id);
process(nurbs);
}
return out;
},
regenerate: (params, generatedObjects, viewer, state) => {
}
};

View file

@ -8,6 +8,7 @@ export const MirrorGeneratorSchema = {
title: 'Mirror',
description: 'Reflects objects off of a given line',
icon: MirrorGeneratorIcon,
persistGeneratedObjects: true,
params: [
{
@ -56,7 +57,7 @@ export const MirrorGeneratorSchema = {
state.dir = new Vector();
},
generate: (params, viewer, state, onCreate) => {
generate: (params, state) => {
const {reflectionLine: [reflectionLine], objects} = params;
@ -64,15 +65,12 @@ export const MirrorGeneratorSchema = {
return objects.map(o => {
const copy = o.copy();
onCreate(copy);
o.layer.add(copy);
reflect(state.dir, reflectionLine, o, copy);
return copy;
});
},
regenerate: (params, generatedObjects, viewer, state) => {
regenerate: (params, generatedObjects, state) => {
const {reflectionLine: [reflectionLine], objects} = params;

View file

@ -2,6 +2,7 @@ import {NOOP} from "gems/func";
import {MirrorGeneratorSchema} from "./mirrorGenerator";
import {memoize} from "lodash/function";
import {indexArray} from "gems/iterables";
import {PREDEFINED_LAYERS} from "../viewer2d";
const SCHEMAS = [
MirrorGeneratorSchema,
@ -34,12 +35,18 @@ export class SketchGenerator {
generate(viewer) {
this.init();
this.generatedObjects = this.schema.generate(this.params, viewer, this.internalState, obj => obj.generator = this);
this.generatedObjects.forEach(obj => obj.syncGeometry());
let layer = viewer.findLayerByName(PREDEFINED_LAYERS.SKETCH);
this.generatedObjects = this.schema.generate(this.params, this.internalState);
this.generatedObjects.forEach(obj => {
obj.generator = this;
this.stage.assignObject(obj);
layer.objects.push(obj);
obj.syncGeometry()
});
}
regenerate(viewer) {
this.schema.regenerate(this.params, this.generatedObjects, viewer, this.internalState);
this.schema.regenerate(this.params, this.generatedObjects, this.internalState);
this.generatedObjects.forEach(obj => obj.syncGeometry());
}
@ -102,7 +109,7 @@ export class SketchGenerator {
typeId: schema.id,
params,
stage: this.stage&&this.stage.index,
generatedObjects: this.generatedObjects.map(obj => obj.id)
generatedObjects: this.schema.persistGeneratedObjects ? this.generatedObjects.map(obj => obj.id) : undefined
};
}
}

View file

@ -1,803 +0,0 @@
import {Generator} from './id-generator'
import {Layer} from './viewer2d'
import {Styles} from './styles'
import {Arc} from './shapes/arc'
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 {BezierCurve} from './shapes/bezier-curve'
import {AngleBetweenDimension, DiameterDimension, Dimension, HDimension, VDimension} from './shapes/dim'
import {Constraints} from './parametric'
import Vector from 'math/vector';
import exportTextData from 'gems/exportTextData';
import NurbsCurve from '../brep/geom/curves/nurbsCurve';
import {NurbsObject} from './shapes/nurbsObject';
import {AlgNumConstraint} from "./constr/ANConstraints";
import {SketchGenerator} from "./generators/sketchGenerator";
import {SketchTypes} from "./shapes/sketch-types";
import {NOOP} from "../../../modules/gems/func";
const Types = SketchTypes;
IO.BOUNDARY_LAYER_NAME = "__bounds__";
/** @constructor */
function IO(viewer) {
this.viewer = viewer;
}
IO.prototype.loadSketch = function(sketchData) {
return this._loadSketch(JSON.parse(sketchData));
};
IO.prototype.serializeSketch = function(metadata) {
return JSON.stringify(this._serializeSketch(metadata));
};
IO.prototype._loadSketch = function(sketch) {
this.cleanUpData();
this.viewer.parametricManager.startTransaction();
const index = {};
function endPoint(p) {
const [id, [xref, x], [yref, y]] = p;
let ep = index[id];
if (ep !== undefined) {
return ep;
}
ep = new EndPoint(x, y);
index[xref] = ep.params.x;
index[yref] = ep.params.y;
index[id] = ep;
return ep;
}
const getStage = pointer => {
if (pointer === undefined) {
return this.viewer.parametricManager.stage;
}
this.viewer.parametricManager.accommodateStages(pointer);
return this.viewer.parametricManager.getStage(pointer);
};
let layerIdGen = 0;
function getLayer(viewer, name) {
if (name === undefined) {
name = "layer_" + layerIdGen++;
} else {
if (name === viewer.dimLayer.name) {
return viewer.dimLayer;
}
for (let i = 0; i < viewer.layers.length; ++i) {
if (name === viewer.layers[i].name) {
return viewer.layers[i];
}
}
}
let layer = viewer.createLayer(name, Styles.DEFAULT);
viewer.layers.push(layer);
return layer;
}
const version = sketch.version || 1;
let T = Types;
let sketchLayers = sketch.layers;
let boundary = sketch.boundary;
const dimensions = [];
if (sketchLayers !== undefined) {
for (let l = 0; l < sketchLayers.length; ++l) {
let ioLayer = sketchLayers[l];
let layerName = ioLayer.name;
if (layerName === IO.BOUNDARY_LAYER_NAME && boundary) {
continue;
}
let layer = getLayer(this.viewer, layerName);
// if (!!ioLayer.style) layer.style = ioLayer.style;
layer.readOnly = !!ioLayer.readOnly;
let layerData = ioLayer.data;
for (let i = 0; i < layerData.length; ++i) {
let obj = layerData[i];
let skobj = null;
let _class = obj._class;
let aux = !!obj.aux;
let role = obj.role;
//support legacy format
if (!role) {
role = layerName === '_construction_' ? 'construction' : null;
}
if (_class === T.SEGMENT) {
const [aRef, bRef] = obj.points;
const a = endPoint(aRef);
const b = endPoint(bRef);
skobj = new Segment(a, b);
} else if (_class === T.POINT) {
skobj = endPoint(obj.location);
} else if (_class === T.ARC) {
const points = obj.points;
const a = endPoint(points[0]);
const b = endPoint(points[1]);
const c = endPoint(points[2]);
skobj = new Arc(a, b, c);
} else if (_class === T.CIRCLE) {
const c = endPoint(obj.c);
skobj = new Circle(c);
skobj.r.set(obj.r);
} else if (_class === T.ELLIPSE) {
const ep1 = endPoint(obj.ep1);
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.BEZIER) {
const a = endPoint(obj.a);
const b = endPoint(obj.b);
const cp1 = endPoint(obj.cp1);
const cp2 = endPoint(obj.cp2);
skobj = new BezierCurve(a, b, cp1, cp2);
} else {
dimensions.push(obj);
}
if (skobj != null) {
skobj.role = role;
getStage(obj.stage).assignObject(skobj);
if (!aux) skobj.stabilize(this.viewer);
if (aux) skobj.accept(function(o){o.aux = true; return true;});
layer.add(skobj);
index[obj.id] = skobj;
//reindex non point children to recover constraints
const childrenIds = obj.children;
if (childrenIds) {
const children = nonPointChildren(skobj);
for (let childIdx = 0; childIdx < childrenIds.length; childIdx++) {
index[childrenIds[childIdx]] = children[childIdx];
}
}
}
}
}
}
for (let obj of dimensions) {
let _class = obj._class;
let skobj = null;
if (_class === T.HDIM) {
skobj = new HDimension(index[obj.a], index[obj.b]);
obj.offset !== undefined && (skobj.offset = obj.offset);
} 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;
if (obj.configuration) {
skobj.configuration = obj.configuration.map(o => index[o]);
}
}
if (skobj !== null) {
this.viewer.dimLayer.add(skobj);
index[obj.id] = skobj;
}
}
if (boundary) {
this.createBoundaryObjects(boundary);
}
if (sketch.constraints && !sketch.stages) {
sketch.stages = [
{
constraints: sketch.constraints,
generators: []
}
]
}
if (sketch.stages) {
for (let stage of sketch.stages) {
for (let constr of stage.constraints) {
try {
const constraint = AlgNumConstraint.read(constr, index);
const stage = getStage(constr.stage||0);
stage.addConstraint(constraint);
} catch (e) {
console.error(e);
console.error("skipping errant constraint: " + constr&&constr.typeId);
}
}
for (let gen of stage.generators) {
try {
const generator = SketchGenerator.read(gen, index);
const stage = getStage(gen.stage||0);
stage.addGenerator(generator);
} catch (e) {
console.error(e);
console.error("skipping errant generator: " + gen&&gen.typeId);
}
}
}
}
let constants = sketch.constants;
if (constants !== undefined) {
this.viewer.parametricManager.$constantDefinition.next(constants);
}
this.viewer.parametricManager.finishTransaction();
this.viewer.parametricManager.notify();
};
IO.prototype.synchLine = function(skobj, edgeObj) {
skobj.a.x = edgeObj.a.x;
skobj.a.y = edgeObj.a.y;
skobj.b.x = edgeObj.b.x;
skobj.b.y = edgeObj.b.y;
};
IO.prototype.synchArc = function(skobj, edgeObj) {
skobj.a.x = edgeObj.a.x;
skobj.a.y = edgeObj.a.y;
skobj.b.x = edgeObj.b.x;
skobj.b.y = edgeObj.b.y;
skobj.c.x = edgeObj.c.x;
skobj.c.y = edgeObj.c.y;
};
IO.prototype.synchCircle = function(skobj, edgeObj) {
skobj.c.x = edgeObj.c.x;
skobj.c.y = edgeObj.c.y;
skobj.r.set(edgeObj.r);
};
IO.prototype.createBoundaryObjects = function(boundary) {
const groundStage = this.viewer.parametricManager.groundStage;
let boundaryLayer = this.viewer.findLayerByName(IO.BOUNDARY_LAYER_NAME);
if (boundaryLayer === null) {
boundaryLayer = this.viewer.createLayer(IO.BOUNDARY_LAYER_NAME, Styles.BOUNDS);
this.viewer.layers.splice(0, 0, boundaryLayer);
}
boundaryLayer.readOnly = true;
boundaryLayer.style = Styles.BOUNDS;
let i, obj;
function setId(obj, id) {
obj.id = id;
let counter = 0;
obj.accept(child => {
if (child !== obj) {
child.id = id + '/' + (++counter);
}
});
obj.visitParams(param => {
param.set = NOOP;
});
obj.stage = groundStage;
}
for (i = 0; i < boundary.lines.length; ++i) {
let edge = boundary.lines[i];
let seg = this.viewer.addSegment(edge.a.x, edge.a.y, edge.b.x, edge.b.y, boundaryLayer);
setId(seg, edge.id);
}
for (i = 0; i < boundary.arcs.length; ++i) {
const a = boundary.arcs[i];
const arc = new Arc(
new EndPoint(a.a.x, a.a.y),
new EndPoint(a.b.x, a.b.y),
new EndPoint(a.c.x, a.c.y)
);
boundaryLayer.objects.push(arc);
setId(arc, a.id);
}
for (i = 0; i < boundary.circles.length; ++i) {
obj = boundary.circles[i];
const circle = new Circle(new EndPoint(obj.c.x, obj.c.y));
circle.r.set(obj.r);
boundaryLayer.objects.push(circle);
setId(circle, obj.id);
}
for (i = 0; i < boundary.nurbses.length; ++i) {
let nurbsData = boundary.nurbses[i];
let nurbs = new NurbsObject(NurbsCurve.deserialize(nurbsData), new EndPoint(), new EndPoint());
boundaryLayer.objects.push(nurbs);
setId(nurbs, nurbsData.id);
}
};
IO.prototype.cleanUpData = function() {
for (var l = 0; l < this.viewer.layers.length; ++l) {
var layer = this.viewer.layers[l];
if (layer.objects.length !== 0) {
layer.objects = [];
}
}
this.viewer.deselectAll();
Generator.resetIDGenerator(0);
this.viewer.parametricManager.reset();
this.viewer.parametricManager.notify();
};
IO.prototype._serializeSketch = function(metadata) {
var sketch = {};
//sketch.boundary = boundary;
sketch.layers = [];
function point(p) {
return [ p.id, [p.params.x.id, p.x], [p.params.y.id, p.y] ];
}
var T = Types;
var toSave = [this.viewer.dimLayers, this.viewer.layers];
for (var t = 0; t < toSave.length; ++t) {
var layers = toSave[t];
for (var l = 0; l < layers.length; ++l) {
var layer = layers[l];
if (layer.name === IO.BOUNDARY_LAYER_NAME) {
continue;
}
var toLayer = {name : layer.name, readOnly: layer.readOnly, data : []};
sketch.layers.push(toLayer);
for (var i = 0; i < layer.objects.length; ++i) {
const obj = layer.objects[i];
if (obj.isAnnotation) {
continue;
}
try {
const to = {id: obj.id, _class: obj._class, role: obj.role};
if (obj.aux) to.aux = obj.aux;
if (obj.edge !== undefined) to.edge = obj.edge;
to.stage = this.viewer.parametricManager.getStageIndex(obj.stage);
if (obj._class === T.SEGMENT) {
to.points = [point(obj.a), point(obj.b)];
} else if (obj._class === T.POINT) {
to.location = point(obj);
} else if (obj._class === T.ARC) {
to.points = [point(obj.a), point(obj.b), point(obj.c)];
} else if (obj._class === T.CIRCLE) {
to.c = point(obj.c);
to.r = obj.r.get();
} else if (obj._class === T.ELLIPSE) {
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.BEZIER) {
to.a = point(obj.a);
to.b = point(obj.b);
to.cp1 = point(obj.cp1);
to.cp2 = point(obj.cp2);
} else if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) {
to.a = obj.a.id;
to.b = obj.b.id;
to.offset = obj.offset;
} else if (obj._class === T.DDIM) {
to.obj = obj.obj.id;
to.angle = obj.angle;
} else if (obj._class === T.ANGLE_BW) {
to.a = obj.a.id;
to.b = obj.b.id;
to.offset = obj.offset;
to.configuration = obj.configuration.map(o => o.id);
}
const children = nonPointChildren(obj).map(c => c.id);
if (children.length !== 0) {
to.children = children;
}
toLayer.data.push(to);
} catch (e) {
console.error(e);
}
}
}
}
sketch.stages = [];
for (let stage of this.viewer.parametricManager.stages) {
const stageOut = {
constraints: [],
generators: [],
};
const systemConstraints = stage.algNumSystem.allConstraints;
for (let sc of systemConstraints) {
if (!sc.internal) {
stageOut.constraints.push(sc.write());
}
}
for (let gen of stage.generators) {
stageOut.generators.push(gen.write());
}
sketch.stages.push(stageOut);
}
const constantDefinition = this.viewer.parametricManager.constantDefinition;
if (constantDefinition !== undefined && constantDefinition != null && !/^\s*$/.test(constantDefinition)) {
sketch.constants = constantDefinition;
}
sketch.scene = {
dx: this.viewer.translate.x,
dy: this.viewer.translate.y,
scale: this.viewer.scale,
};
sketch.metadata = metadata;
sketch.version = 2;
return sketch;
};
function nonPointChildren(obj){
const children = [];
obj.accept((o) => {
if (o._class !== Types.POINT) {
children.push(o);
}
return true;
});
return children;
}
IO.prototype.parseConstr = function (c, index) {
var name = c[0];
var ps = c[1];
function find(id) {
var p = index[id];
if (!p) {
throw "Constraint " + name + " refers to nonexistent object.";
}
return p;
}
var constrCreate = Constraints.Factory[name];
if (constrCreate === undefined) {
throw "Constraint " + name + " doesn't exist.";
}
return constrCreate(find, ps);
};
function _format(str, args) {
if (args.length == 0) return str;
var i = 0;
return str.replace(/\$/g, function() {
if (args === undefined || args[i] === undefined) throw "format arguments mismatch";
var val = args[i];
if (typeof val === 'number') val = val.toPrecision();
i ++;
return val;
});
}
/** @constructor */
function PrettyColors() {
var colors = ["#000000", "#00008B", "#006400", "#8B0000", "#FF8C00", "#E9967A"];
var colIdx = 0;
this.next = function () {
return colors[colIdx++ % colors.length];
}
}
/** @constructor */
function TextBuilder() {
this.data = "";
this.fline = function (chunk, args) {
this.data += _format(chunk, args) + "\n"
};
this.line = function (chunk) {
this.data += chunk + "\n"
};
this.number = function (n) {
this.data += n.toPrecision()
};
this.numberln = function (n) {
this.number(n)
this.data += "\n"
}
}
/** @constructor */
function BBox() {
var bbox = [Number.MAX_VALUE, Number.MAX_VALUE, - Number.MAX_VALUE, - Number.MAX_VALUE];
var T = Types;
this.checkLayers = function(layers) {
for (var l = 0; l < layers.length; ++l)
for (var i = 0; i < layers[l].objects.length; ++i)
this.check(layers[l].objects[i]);
};
this.check = function(obj) {
if (obj._class === T.SEGMENT) {
this.checkBounds(obj.a.x, obj.a.y);
this.checkBounds(obj.b.x, obj.b.y);
} else if (obj._class === T.POINT) {
this.checkBounds(obj.x, obj.y);
} else if (obj._class === T.ARC) {
this.checkCircBounds(obj.c.x, obj.c.y, obj.r.get());
} else if (obj._class === T.CIRCLE) {
this.checkCircBounds(obj.c.x, obj.c.y, obj.r.get());
} else if (obj._class === T.ELLIPSE || obj._class === T.ELL_ARC) {
this.checkCircBounds(obj.centerX, obj.centerY, Math.max(obj.radiusX, obj.radiusY));
} else if (obj) {
obj.accept((o) => {
if (o._class == T.POINT) {
this.checkBounds(o.x, o.y);
}
return true;
});
// } else if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) {
}
};
this.isValid = function() {
return bbox[0] != Number.MAX_VALUE;
};
this.checkBounds = function(x, y) {
bbox[0] = Math.min(bbox[0], x);
bbox[1] = Math.min(bbox[1], y);
bbox[2] = Math.max(bbox[2], x);
bbox[3] = Math.max(bbox[3], y);
};
this.checkCircBounds = function(x, y, r) {
this.checkBounds(x + r, y + r);
this.checkBounds(x - r, y + r);
this.checkBounds(x - r, y - r);
this.checkBounds(x - r, y + r);
};
this.inc = function(by) {
bbox[0] -= by;
bbox[1] -= by;
bbox[2] += by;
bbox[3] += by;
};
this.width = function() {
return bbox[2] - bbox[0];
};
this.height = function() {
return bbox[3] - bbox[1];
};
this.bbox = bbox;
}
IO.prototype.getWorkspaceToExport = function() {
return [this.viewer.layers];
};
IO.prototype.getLayersToExport = function() {
var ws = this.getWorkspaceToExport();
var toExport = [];
for (var t = 0; t < ws.length; ++t) {
var layers = ws[t];
for (var l = 0; l < layers.length; ++l) {
var layer = layers[l];
toExport.push(layer)
}
}
return toExport;
};
IO.prototype.svgExport = function () {
var T = Types;
var out = new TextBuilder();
var bbox = new BBox();
var a = new Vector();
var b = new Vector();
var prettyColors = new PrettyColors();
var toExport = this.getLayersToExport();
for (var l = 0; l < toExport.length; ++l) {
var layer = toExport[l];
var color = prettyColors.next();
out.fline('<g id="$" fill="$" stroke="$" stroke-width="$">', [layer.name, "none", color, '2']);
for (var i = 0; i < layer.objects.length; ++i) {
var obj = layer.objects[i];
if (obj._class !== T.POINT) bbox.check(obj);
if (obj._class === T.SEGMENT) {
out.fline('<line x1="$" y1="$" x2="$" y2="$" />', [obj.a.x, obj.a.y, obj.b.x, obj.b.y]);
} else if (obj._class === T.ARC) {
a.set(obj.a.x - obj.c.x, obj.a.y - obj.c.y, 0);
b.set(obj.b.x - obj.c.x, obj.b.y - obj.c.y, 0);
var dir = a.cross(b).z > 0 ? 0 : 1;
var r = obj.r.get();
out.fline('<path d="M $ $ A $ $ 0 $ $ $ $" />', [obj.a.x, obj.a.y, r, r, dir, 1, obj.b.x, obj.b.y]);
} else if (obj._class === T.CIRCLE) {
out.fline('<circle cx="$" cy="$" r="$" />', [obj.c.x, obj.c.y, obj.r.get()]);
// } else if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) {
}
}
out.line('</g>');
}
bbox.inc(20);
return _format("<svg viewBox='$ $ $ $'>\n", bbox.bbox) + out.data + "</svg>"
};
IO.prototype.dxfExport = function () {
var T = Types;
var out = new TextBuilder();
var bbox = new BBox();
var toExport = this.getLayersToExport();
var i;
bbox.checkLayers(toExport);
out.line("999");
out.line("js.parametric.sketcher");
out.line("0");
out.line("SECTION");
out.line("2");
out.line("HEADER");
out.line("9");
out.line("$ACADVER");
out.line("1");
out.line("AC1006");
out.line("9");
out.line("$INSBASE");
out.line("10");
out.line("0");
out.line("20");
out.line("0");
out.line("30");
out.line("0");
out.line("9");
out.line("$EXTMIN");
out.line("10");
out.numberln(bbox.bbox[0]);
out.line("20");
out.numberln(bbox.bbox[1]);
out.line("9");
out.line("$EXTMAX");
out.line("10");
out.numberln(bbox.bbox[2]);
out.line("20");
out.numberln(bbox.bbox[3]);
out.line("0");
out.line("ENDSEC");
out.line("0");
out.line("SECTION");
out.line("2");
out.line("TABLES");
for (i = 0; i < toExport.length; i++) {
out.line("0");
out.line("LAYER");
out.line("2");
out.line("" + (i + 1));
out.line("70");
out.line("64");
out.line("62");
out.line("7");
out.line("6");
out.line("CONTINUOUS");
}
out.line("0");
out.line("ENDTAB");
out.line("0");
out.line("ENDSEC");
out.line("0");
out.line("SECTION");
out.line("2");
out.line("BLOCKS");
out.line("0");
out.line("ENDSEC");
out.line("0");
out.line("SECTION");
out.line("2");
out.line("ENTITIES");
for (var l = 0; l < toExport.length; l++) {
var lid = l + 1;
var layer = toExport[l];
for (i = 0; i < layer.objects.length; ++i) {
var obj = layer.objects[i];
if (obj._class === T.POINT) {
out.line("0");
out.line("POINT");
out.line("8");
out.line(lid);
out.line("10");
out.numberln(obj.x);
out.line("20");
out.numberln(obj.y);
out.line("30");
out.line("0");
} else if (obj._class === T.SEGMENT) {
out.line("0");
out.line("LINE");
out.line("8");
out.line(lid);
//out.line("62"); color
//out.line("4");
out.line("10");
out.numberln(obj.a.x);
out.line("20");
out.numberln(obj.a.y);
out.line("30");
out.line("0");
out.line("11");
out.numberln(obj.b.x);
out.line("21");
out.numberln(obj.b.y);
out.line("31");
out.line("0");
} else if (obj._class === T.ARC) {
out.line("0");
out.line("ARC");
out.line("8");
out.line(lid);
out.line("10");
out.numberln(obj.c.x);
out.line("20");
out.numberln(obj.c.y);
out.line("30");
out.line("0");
out.line("40");
out.numberln(obj.r.get());
out.line("50");
out.numberln(obj.getStartAngle() * (180 / Math.PI));
out.line("51");
out.numberln(obj.getEndAngle() * (180 / Math.PI));
} else if (obj._class === T.CIRCLE) {
out.line("0");
out.line("CIRCLE");
out.line("8");
out.line(lid);
out.line("10");
out.numberln(obj.c.x);
out.line("20");
out.numberln(obj.c.y);
out.line("30");
out.line("0");
out.line("40");
out.numberln(obj.r.get());
// } else if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) {
}
}
}
out.line("0");
out.line("ENDSEC");
out.line("0");
out.line("EOF");
return out.data;
};
IO.exportTextData = exportTextData;
export {IO, BBox};

613
web/app/sketcher/io.ts Normal file
View file

@ -0,0 +1,613 @@
import {Generator} from './id-generator'
import {Viewer} from './viewer2d'
import {Styles} from './styles'
import {Arc} from './shapes/arc'
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 {BezierCurve} from './shapes/bezier-curve'
import {LinearDimension, AngleBetweenDimension, DiameterDimension, Dimension, HDimension, VDimension} from './shapes/dim'
import Vector from 'math/vector';
import exportTextData from 'gems/exportTextData';
import {AlgNumConstraint} from "./constr/ANConstraints";
import {SketchGenerator} from "./generators/sketchGenerator";
import {BoundaryGeneratorSchema} from "./generators/boundaryGenerator";
import {SketchTypes} from "./shapes/sketch-types";
import {SketchObject} from "./shapes/sketch-object";
export interface SketchFormat_V3 {
version: number;
objects: {
id: string,
type: string,
role: string,
stage: number,
data: any
}[];
dimensions: {
id: string,
type: string,
data: any
}[];
stages: {
generators: {
typeId: string
}[];
constraints: {
typeId: string
}[];
}[];
constants: {
[key: string]: string;
};
metadata: any;
boundary?: ExternalBoundary
}
class ExternalBoundary {
}
export class IO {
static exportTextData = exportTextData
viewer: Viewer;
constructor(viewer) {
this.viewer = viewer;
}
loadSketch(sketchData) {
return this._loadSketch(JSON.parse(sketchData));
}
serializeSketch(metadata) {
return JSON.stringify(this._serializeSketch(metadata));
}
_loadSketch(sketch: SketchFormat_V3) {
this.cleanUpData();
this.viewer.parametricManager.startTransaction();
try {
const getStage = pointer => {
if (pointer === undefined) {
return this.viewer.parametricManager.stage;
}
this.viewer.parametricManager.accommodateStages(pointer);
return this.viewer.parametricManager.getStage(pointer);
};
if (sketch.boundary) {
this.createBoundaryObjects(sketch.boundary);
}
if (sketch.version !== 3) {
return;
}
const sketchLayer = this.viewer.findLayerByName('sketch');
for (let obj of sketch.objects) {
try {
let skobj: SketchObject = null;
let type = obj.type;
if (type === Segment.prototype.TYPE) {
skobj = Segment.read(obj.id, obj.data);
} else if (type === EndPoint.prototype.TYPE) {
skobj = EndPoint.read(obj.id, obj.data);
} else if (type === Arc.prototype.TYPE) {
skobj = Arc.read(obj.id, obj.data);
} else if (type === Circle.prototype.TYPE) {
skobj = Circle.read(obj.id, obj.data);
} else if (type === Ellipse.prototype.TYPE) {
skobj = Ellipse.read(obj.id, obj.data);
} else if (type === EllipticalArc.prototype.TYPE) {
skobj = EllipticalArc.read(obj.id, obj.data);
} else if (type === EllipticalArc.prototype.TYPE) {
skobj = BezierCurve.read(obj.id, obj.data);
}
if (skobj != null) {
skobj.role = obj.role;
getStage(obj.stage).assignObject(skobj);
sketchLayer.add(skobj);
skobj.stabilize(this.viewer);
}
} catch (e) {
console.error(e);
console.error("Failed loading " + obj.type + " " + obj.id);
}
}
for (let obj of sketch.dimensions) {
try {
let type = obj.type;
let skobj = null;
if (type === HDimension.prototype.TYPE) {
skobj = LinearDimension.load(HDimension, obj.id, obj.data);
} else if (type === VDimension.prototype.TYPE) {
skobj = LinearDimension.load(VDimension, obj.id, obj.data);
} else if (type === LinearDimension.prototype.TYPE) {
skobj = LinearDimension.load(LinearDimension, obj.id, obj.data);
} else if (type === DiameterDimension.prototype.TYPE) {
skobj = DiameterDimension.load(obj.id, obj.data);
} else if (type === AngleBetweenDimension.prototype.TYPE) {
skobj = AngleBetweenDimension.load(obj.id, obj.data);
}
if (skobj !== null) {
this.viewer.dimLayer.add(skobj);
}
} catch (e) {
console.error(e);
console.error("Failed loading " + obj.type + " " + obj.id);
}
}
const index = this.viewer.createIndex();
for (let i = 0; i < sketch.stages.length; i++) {
let dataStage = sketch.stages[i];
let stage = getStage(i);
for (let constr of dataStage.constraints) {
try {
const constraint = AlgNumConstraint.read(constr, index);
stage.addConstraint(constraint)
} catch (e) {
console.error(e);
console.error("skipping errant constraint: " + constr&&constr.typeId);
}
}
for (let gen of dataStage.generators) {
try {
const generator = SketchGenerator.read(gen, index);
stage.addGenerator(generator)
} catch (e) {
console.error(e);
console.error("skipping errant generator: " + gen&&gen.typeId);
}
}
}
let constants = sketch.constants;
if (constants !== undefined) {
this.viewer.parametricManager.$constantDefinition.next(constants);
}
} finally {
this.viewer.parametricManager.finishTransaction();
this.viewer.parametricManager.notify();
}
};
createBoundaryObjects(boundary) {
const boundaryGenerator = new SketchGenerator({
boundaryData: boundary
}, BoundaryGeneratorSchema);
this.viewer.parametricManager.addGeneratorToStage(boundaryGenerator, this.viewer.parametricManager.groundStage);
}
cleanUpData() {
for (var l = 0; l < this.viewer.layers.length; ++l) {
var layer = this.viewer.layers[l];
if (layer.objects.length !== 0) {
layer.objects = [];
}
}
this.viewer.deselectAll();
Generator.resetIDGenerator(0);
this.viewer.parametricManager.reset();
this.viewer.parametricManager.notify();
};
_serializeSketch(metadata) {
const sketch: SketchFormat_V3 = {
version: 3,
objects: [],
dimensions: [],
stages: [],
constants: this.viewer.parametricManager.constantDefinition,
metadata
};
for (let layer of this.viewer.layers) {
for (let obj of layer.objects) {
if (obj instanceof Dimension) {
continue;
}
if (obj.isGenerated && !obj.generator.schema.persistGeneratedObjects) {
continue;
}
try {
sketch.objects.push({
id: obj.id,
type: obj.TYPE,
role: obj.role,
stage: this.viewer.parametricManager.getStageIndex(obj.stage),
data: obj.write()
});
} catch (e) {
console.error(e);
}
}
}
for (let obj of this.viewer.dimLayer.objects) {
try {
sketch.dimensions.push({
id: obj.id,
type: obj.TYPE,
data: obj.write()
});
} catch (e) {
console.error(e);
}
}
for (let stage of this.viewer.parametricManager.stages) {
const stageOut = {
constraints: [],
generators: [],
};
const systemConstraints = stage.algNumSystem.allConstraints;
for (let sc of systemConstraints) {
if (!sc.internal) {
stageOut.constraints.push(sc.write());
}
}
for (let gen of stage.generators) {
if (gen.internal) {
continue;
}
stageOut.generators.push(gen.write());
}
sketch.stages.push(stageOut);
}
return sketch;
};
getWorkspaceToExport() {
return [this.viewer.layers];
};
getLayersToExport() {
var ws = this.getWorkspaceToExport();
var toExport = [];
for (var t = 0; t < ws.length; ++t) {
var layers = ws[t];
for (var l = 0; l < layers.length; ++l) {
var layer = layers[l];
toExport.push(layer)
}
}
return toExport;
}
svgExport() {
var T = SketchTypes;
var out = new TextBuilder();
var bbox = new BBox();
var a = new Vector();
var b = new Vector();
var prettyColors = new PrettyColors();
var toExport = this.getLayersToExport();
for (var l = 0; l < toExport.length; ++l) {
var layer = toExport[l];
var color = prettyColors.next();
out.fline('<g id="$" fill="$" stroke="$" stroke-width="$">', [layer.name, "none", color, '2']);
for (var i = 0; i < layer.objects.length; ++i) {
var obj = layer.objects[i];
if (obj._class !== T.POINT) bbox.check(obj);
if (obj._class === T.SEGMENT) {
out.fline('<line x1="$" y1="$" x2="$" y2="$" />', [obj.a.x, obj.a.y, obj.b.x, obj.b.y]);
} else if (obj._class === T.ARC) {
a.set(obj.a.x - obj.c.x, obj.a.y - obj.c.y, 0);
b.set(obj.b.x - obj.c.x, obj.b.y - obj.c.y, 0);
var dir = a.cross(b).z > 0 ? 0 : 1;
var r = obj.r.get();
out.fline('<path d="M $ $ A $ $ 0 $ $ $ $" />', [obj.a.x, obj.a.y, r, r, dir, 1, obj.b.x, obj.b.y]);
} else if (obj._class === T.CIRCLE) {
out.fline('<circle cx="$" cy="$" r="$" />', [obj.c.x, obj.c.y, obj.r.get()]);
// } else if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) {
}
}
out.line('</g>');
}
bbox.inc(20);
return _format("<svg viewBox='$ $ $ $'>\n", bbox.bbox) + out.data + "</svg>"
};
dxfExport() {
var T = SketchTypes;
var out = new TextBuilder();
var bbox = new BBox();
var toExport = this.getLayersToExport();
var i;
bbox.checkLayers(toExport);
out.line("999");
out.line("js.parametric.sketcher");
out.line("0");
out.line("SECTION");
out.line("2");
out.line("HEADER");
out.line("9");
out.line("$ACADVER");
out.line("1");
out.line("AC1006");
out.line("9");
out.line("$INSBASE");
out.line("10");
out.line("0");
out.line("20");
out.line("0");
out.line("30");
out.line("0");
out.line("9");
out.line("$EXTMIN");
out.line("10");
out.numberln(bbox.bbox[0]);
out.line("20");
out.numberln(bbox.bbox[1]);
out.line("9");
out.line("$EXTMAX");
out.line("10");
out.numberln(bbox.bbox[2]);
out.line("20");
out.numberln(bbox.bbox[3]);
out.line("0");
out.line("ENDSEC");
out.line("0");
out.line("SECTION");
out.line("2");
out.line("TABLES");
for (i = 0; i < toExport.length; i++) {
out.line("0");
out.line("LAYER");
out.line("2");
out.line("" + (i + 1));
out.line("70");
out.line("64");
out.line("62");
out.line("7");
out.line("6");
out.line("CONTINUOUS");
}
out.line("0");
out.line("ENDTAB");
out.line("0");
out.line("ENDSEC");
out.line("0");
out.line("SECTION");
out.line("2");
out.line("BLOCKS");
out.line("0");
out.line("ENDSEC");
out.line("0");
out.line("SECTION");
out.line("2");
out.line("ENTITIES");
for (var l = 0; l < toExport.length; l++) {
var lid = l + 1;
var layer = toExport[l];
for (i = 0; i < layer.objects.length; ++i) {
var obj = layer.objects[i];
if (obj._class === T.POINT) {
out.line("0");
out.line("POINT");
out.line("8");
out.line(lid);
out.line("10");
out.numberln(obj.x);
out.line("20");
out.numberln(obj.y);
out.line("30");
out.line("0");
} else if (obj._class === T.SEGMENT) {
out.line("0");
out.line("LINE");
out.line("8");
out.line(lid);
//out.line("62"); color
//out.line("4");
out.line("10");
out.numberln(obj.a.x);
out.line("20");
out.numberln(obj.a.y);
out.line("30");
out.line("0");
out.line("11");
out.numberln(obj.b.x);
out.line("21");
out.numberln(obj.b.y);
out.line("31");
out.line("0");
} else if (obj._class === T.ARC) {
out.line("0");
out.line("ARC");
out.line("8");
out.line(lid);
out.line("10");
out.numberln(obj.c.x);
out.line("20");
out.numberln(obj.c.y);
out.line("30");
out.line("0");
out.line("40");
out.numberln(obj.r.get());
out.line("50");
out.numberln(obj.getStartAngle() * (180 / Math.PI));
out.line("51");
out.numberln(obj.getEndAngle() * (180 / Math.PI));
} else if (obj._class === T.CIRCLE) {
out.line("0");
out.line("CIRCLE");
out.line("8");
out.line(lid);
out.line("10");
out.numberln(obj.c.x);
out.line("20");
out.numberln(obj.c.y);
out.line("30");
out.line("0");
out.line("40");
out.numberln(obj.r.get());
// } else if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) {
}
}
}
out.line("0");
out.line("ENDSEC");
out.line("0");
out.line("EOF");
return out.data;
};
}
function _format(str, args) {
if (args.length == 0) return str;
var i = 0;
return str.replace(/\$/g, function() {
if (args === undefined || args[i] === undefined) throw "format arguments mismatch";
var val = args[i];
if (typeof val === 'number') val = val.toPrecision();
i ++;
return val;
});
}
/** @constructor */
function PrettyColors() {
var colors = ["#000000", "#00008B", "#006400", "#8B0000", "#FF8C00", "#E9967A"];
var colIdx = 0;
this.next = function () {
return colors[colIdx++ % colors.length];
}
}
/** @constructor */
function TextBuilder() {
this.data = "";
this.fline = function (chunk, args) {
this.data += _format(chunk, args) + "\n"
};
this.line = function (chunk) {
this.data += chunk + "\n"
};
this.number = function (n) {
this.data += n.toPrecision()
};
this.numberln = function (n) {
this.number(n)
this.data += "\n"
}
}
/** @constructor */
function BBox() {
var bbox = [Number.MAX_VALUE, Number.MAX_VALUE, - Number.MAX_VALUE, - Number.MAX_VALUE];
var T = SketchTypes;
this.checkLayers = function(layers) {
for (var l = 0; l < layers.length; ++l)
for (var i = 0; i < layers[l].objects.length; ++i)
this.check(layers[l].objects[i]);
};
this.check = function(obj) {
if (obj._class === T.SEGMENT) {
this.checkBounds(obj.a.x, obj.a.y);
this.checkBounds(obj.b.x, obj.b.y);
} else if (obj._class === T.POINT) {
this.checkBounds(obj.x, obj.y);
} else if (obj._class === T.ARC) {
this.checkCircBounds(obj.c.x, obj.c.y, obj.r.get());
} else if (obj._class === T.CIRCLE) {
this.checkCircBounds(obj.c.x, obj.c.y, obj.r.get());
} else if (obj._class === T.ELLIPSE || obj._class === T.ELL_ARC) {
this.checkCircBounds(obj.centerX, obj.centerY, Math.max(obj.radiusX, obj.radiusY));
} else if (obj) {
obj.accept((o) => {
if (o._class == T.POINT) {
this.checkBounds(o.x, o.y);
}
return true;
});
// } else if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) {
}
};
this.isValid = function() {
return bbox[0] != Number.MAX_VALUE;
};
this.checkBounds = function(x, y) {
bbox[0] = Math.min(bbox[0], x);
bbox[1] = Math.min(bbox[1], y);
bbox[2] = Math.max(bbox[2], x);
bbox[3] = Math.max(bbox[3], y);
};
this.checkCircBounds = function(x, y, r) {
this.checkBounds(x + r, y + r);
this.checkBounds(x - r, y + r);
this.checkBounds(x - r, y - r);
this.checkBounds(x - r, y + r);
};
this.inc = function(by) {
bbox[0] -= by;
bbox[1] -= by;
bbox[2] += by;
bbox[3] += by;
};
this.width = function() {
return bbox[2] - bbox[0];
};
this.height = function() {
return bbox[3] - bbox[1];
};
this.bbox = bbox;
}
export {BBox};

View file

@ -261,7 +261,7 @@ class ParametricManager {
obj.constraints.clear();
obj.generators.clear();
this.viewer.dimLayer.traverse(dim => {
this.viewer.dimLayer.traverseSketchObjects(dim => {
obj.accept(o => {
if (dim.dependsOn && dim.dependsOn(o)) {
this._removeObject(dim);
@ -284,22 +284,20 @@ class ParametricManager {
}
prepare(interactiveObjects) {
this.groundStage.prepare(interactiveObjects);
for (let stage of this.stages) {
stage.algNumSystem.prepare(interactiveObjects);
stage.prepare(interactiveObjects);
}
}
solve(rough) {
this.groundStage.solve(rough);
for (let stage of this.stages) {
stage.algNumSystem.solve(rough);
stage.generators.forEach(gen => {
gen.regenerate(this.viewer);
})
stage.solve(rough);
}
}
addGenerator(generator) {
generator.generate(this.viewer);
let highestStage = this.stages[0];
@ -309,9 +307,20 @@ class ParametricManager {
}
});
this.addGeneratorToStage(generator, highestStage);
if (highestStage !== this.stage && !this.inTransaction) {
toast("Generator's been added to stage " + highestStage.index + "!")
}
this.refresh();
}
addGeneratorToStage(generator, stage) {
let fail = false;
generator.sourceObjects(obj => {
if (obj.isGenerated && obj.stage === highestStage) {
if (obj.isGenerated && obj.stage === stage) {
toast("Cannot refer to a generated object from the same stage is being added to.");
}
});
@ -320,13 +329,8 @@ class ParametricManager {
return;
}
highestStage.addGenerator(generator);
if (highestStage !== this.stage && !this.inTransaction) {
toast("Generator's been added to stage " + highestStage.index + "!")
}
this.refresh();
stage.addGenerator(generator);
generator.generate(this.viewer);
}
coincidePoints(pt1, pt2) {
@ -449,6 +453,17 @@ class SolveStage {
return new AlgNumSubSystem(calcVisualLimit, this.parametricManager.constantResolver, this);
}
prepare(interactiveObjects) {
this.algNumSystem.prepare(interactiveObjects);
}
solve(rough) {
this.algNumSystem.solve(rough);
this.generators.forEach(gen => {
gen.regenerate(this.viewer);
})
}
get index() {
return this.viewer.parametricManager.getStageIndex(this);
}

View file

@ -70,7 +70,7 @@ export function getDescription(definition) {
}
function stringifyTypes(types, minQuantity) {
return types.map(t => t.prototype._class.replace('TCAD.TWO.', '') + (minQuantity > 1 ? 's' : '')).join(' or ');
return types.map(t => t.prototype.TYPE + (minQuantity > 1 ? 's' : '')).join(' or ');
}
export class MatchIndex {

View file

@ -1,4 +1,4 @@
import {AngleBetweenDimension, DiameterDimension, Dimension} from "../dim";
import {AngleBetweenDimension, DiameterDimension, LinearDimension} from "../dim";
import {Styles} from "../../styles";
export class AngleBetweenAnnotation extends AngleBetweenDimension {
@ -23,6 +23,8 @@ export class AngleBetweenAnnotation extends AngleBetweenDimension {
}
}
AngleBetweenAnnotation.prototype.TYPE = 'AngleBetweenAnnotation';
AngleBetweenAnnotation.prototype._class = 'TCAD.TWO.AngleBetweenAnnotation';
export class AngleAbsoluteAnnotation extends AngleBetweenDimension {
@ -85,10 +87,10 @@ export class AngleAbsoluteAnnotation extends AngleBetweenDimension {
}
}
AngleAbsoluteAnnotation.prototype._class = 'TCAD.TWO.AngleAbsoluteAnnotation';
AngleAbsoluteAnnotation.prototype._class = 'AngleAbsoluteAnnotation';
export class LengthAnnotation extends Dimension {
export class LengthAnnotation extends LinearDimension {
constructor(segment, constraint) {
super(segment.a, segment.b);
@ -110,6 +112,8 @@ export class LengthAnnotation extends Dimension {
}
}
LengthAnnotation.prototype.TYPE = 'LengthAnnotation';
LengthAnnotation.prototype._class = 'TCAD.TWO.LengthAnnotation';
export class RadiusLengthAnnotation extends DiameterDimension {
@ -134,4 +138,6 @@ export class RadiusLengthAnnotation extends DiameterDimension {
}
}
RadiusLengthAnnotation.prototype.TYPE = 'RadiusLengthAnnotation';
RadiusLengthAnnotation.prototype._class = 'TCAD.TWO.RadiusLengthAnnotation';

View file

@ -1,21 +1,30 @@
import * as math from '../../math/math';
import Vector from 'math/vector';
import {SketchObject} from './sketch-object';
import {SketchObject, SketchObjectSerializationData} from './sketch-object';
import {Param} from "./param";
import {AlgNumConstraint, ConstraintDefinitions} from "../constr/ANConstraints";
import {makeAngle0_360} from "../../math/math";
import {EndPoint, SketchPointSerializationData} from "./point";
export class Arc extends SketchObject {
a: EndPoint;
b: EndPoint;
c: EndPoint;
r: Param;
ang1: Param;
ang2: Param;
constructor(a, b, c) {
super();
this.a = a;
this.b = b;
this.c = c;
a.parent = this;
b.parent = this;
c.parent = this;
this.children.push(a, b, c);
constructor(ax, ay, bx, by, cx, cy, id?: string) {
super(id);
this.a = new EndPoint(ax, ay, this.id + ':A');
this.b = new EndPoint(bx, by, this.id + ':B');
this.c = new EndPoint(cx, cy, this.id + ':C');
this.a.parent = this;
this.b.parent = this;
this.c.parent = this;
this.children.push(this.a, this.b, this.c);
this.r = new Param(0, 'R');
this.r.enforceVisualLimit = true;
@ -153,7 +162,7 @@ export class Arc extends SketchObject {
}
copy() {
return new Arc(this.a.copy(), this.b.copy(), this.c.copy());
return new Arc(this.a.x, this.a.y, this.b.x, this.b.y, this.c.x, this.c.y);
}
mirror(dest, mirroringFunc) {
@ -161,6 +170,34 @@ export class Arc extends SketchObject {
this.b.mirror(dest.a, mirroringFunc);
this.c.mirror(dest.c, mirroringFunc);
}
write(): SketchArcSerializationData {
return {
a: this.a.write(),
b: this.b.write(),
c: this.c.write()
};
}
static read(id: string, data: SketchArcSerializationData): Arc {
return new Arc(
data.a.x,
data.a.y,
data.b.x,
data.b.y,
data.c.x,
data.c.y,
id
)
}
}
export interface SketchArcSerializationData extends SketchObjectSerializationData {
a: SketchPointSerializationData;
b: SketchPointSerializationData;
c: SketchPointSerializationData;
}
Arc.prototype.TYPE = 'Arc';
Arc.prototype._class = 'TCAD.TWO.Arc';

View file

@ -5,19 +5,23 @@ import {ConvexHull2D} from '../../math/convex-hull'
import * as draw_utils from '../shapes/draw-utils'
import * as math from '../../math/math';
import {EndPoint} from "./point";
export class BezierCurve extends SketchObject {
constructor(a, b, cp1, cp2) {
super();
this.a = a;
this.b = b;
this.cp1 = cp1;
this.cp2 = cp2;
constructor(ax, ay, bx, by, cp1x, cp1y, cp2x, cp2y, id) {
super(id);
const s1 = new Segment(ax, ay, cp1x, cp1y, this.id + ':1');
const s2 = new Segment(bx, by, cp2x, cp2y, this.id + ':2');
this.addChild(s1);
this.addChild(s2);
this.a = this.s1.a;
this.b = this.s2.b;
this.cp1 = this.s1.b;
this.cp2 = this.s2.a;
this.addChild(new Segment(a, cp1));
this.addChild(new Segment(b, cp2));
for (let c of this.children) {
c.role = 'objectConstruction';
}
@ -61,8 +65,8 @@ export class BezierCurve extends SketchObject {
let hero = -1;
for (let p = segments.length - 1, q = 0; q < segments.length; p = q ++) {
const dist = Math.min(Segment.calcNormalDistance(aim, segments[p], segments[q]));
if (dist != -1) {
hero = hero == -1 ? dist : Math.min(dist, hero);
if (dist !== -1) {
hero = hero === -1 ? dist : Math.min(dist, hero);
}
}
return hero;
@ -91,7 +95,33 @@ export class BezierCurve extends SketchObject {
ctx.stroke();
}
}
write() {
return {
cp1: this.a.write(),
cp2: this.cp1.write(),
cp3: this.cp2.write(),
cp4: this.b.write()
};
}
static read(id, data) {
return new BezierCurve(
data.cp1.x,
data.cp1.y,
data.cp2.x,
data.cp2.y,
data.cp3.x,
data.cp3.y,
data.cp4.x,
data.cp4.y,
id
)
}
}
BezierCurve.prototype.TYPE = 'BezierCurve';
BezierCurve.prototype._class = 'TCAD.TWO.BezierCurve';
const RECOVER_LENGTH = 100;

View file

@ -1,17 +1,18 @@
import * as math from '../../math/math';
import {SketchObject} from './sketch-object'
import {Param} from "./param";
import {EndPoint} from "./point";
export const MIN_RADIUS = 100;
export class Circle extends SketchObject {
constructor(c) {
super();
this.c = c;
c.parent = this;
this.children.push(c);
this.r = new Param(0, 'R');
constructor(cx, cy, r = 0, id) {
super(id);
this.c = new EndPoint(cx, cy, this.id + ':C');
this.c.parent = this;
this.children.push(this.c);
this.r = new Param(r, 'R');
this.r.enforceVisualLimit = true;
}
@ -46,6 +47,24 @@ export class Circle extends SketchObject {
circle.r.set(this.r.get());
return circle;
}
write() {
return {
c: this.c.write(),
r: this.r.get()
}
}
static read(id, data) {
return new Circle(
data.c.x,
data.c.y,
data.r,
id
)
}
}
Circle.prototype._class = 'TCAD.TWO.Circle';
Circle.prototype.TYPE = 'Circle';

5
web/app/sketcher/shapes/dim.d.ts vendored Normal file
View file

@ -0,0 +1,5 @@
import {SketchObject} from "./sketch-object";
export interface Dimension extends SketchObject {
}

View file

@ -2,11 +2,11 @@ import * as math 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 {SketchObject} from './sketch-object'
import {Styles} from "../styles";
import {TextHelper} from "./textHelper";
import {isInstanceOf} from "../actions/matchUtils";
import {Arc} from "./arc";
import {SketchObject} from "./sketch-object";
const ARROW_W_PX = 15;
const ARROW_H_PX = 4;
@ -44,10 +44,17 @@ function drawExtensionLine(ctx, x, y, nx, ny, width, tip, arrowW) {
ctx.stroke();
}
class LinearDimension extends SketchObject {
export class Dimension extends SketchObject {
constructor(id) {
super(id);
}
}
export class LinearDimension extends Dimension {
constructor(a, b) {
super();
constructor(a, b, id) {
super(id);
this.a = a;
this.b = b;
this.offset = 20;
@ -210,23 +217,32 @@ class LinearDimension extends SketchObject {
return Math.abs(sdist);
}
}
write() {
return {
a: this.a.id,
b: this.b.id,
offset: this.offset
}
}
export class Dimension extends LinearDimension {
constructor(a, b) {
super(a, b);
static load(constr, id, data, index) {
const dim = new constr(
index[data.a],
index[data.b],
id
);
dim.offset = data.offset;
return dim;
}
}
Dimension.prototype._class = 'TCAD.TWO.Dimension';
LinearDimension.prototype._class = 'TCAD.TWO.LinearDimension';
LinearDimension.prototype.TYPE = 'LinearDimension';
export class HDimension extends LinearDimension {
constructor(a, b) {
super(a, b);
constructor(a, b, id) {
super(a, b, id);
}
getA() {
@ -238,11 +254,12 @@ export class HDimension extends LinearDimension {
}
}
HDimension.prototype._class = 'TCAD.TWO.HDimension';
HDimension.prototype.TYPE = 'HDimension';
export class VDimension extends LinearDimension {
constructor(a, b) {
super(a, b);
constructor(a, b, id) {
super(a, b, id);
}
getA() {
@ -254,12 +271,13 @@ export class VDimension extends LinearDimension {
}
}
VDimension.prototype._class = 'TCAD.TWO.VDimension';
VDimension.prototype.TYPE = 'VDimension';
export class DiameterDimension extends SketchObject {
export class DiameterDimension extends Dimension {
constructor(obj) {
super();
constructor(obj, id) {
super(id);
this.obj = obj;
this.angle = Math.PI / 4;
this.textHelper = new TextHelper();
@ -387,13 +405,29 @@ export class DiameterDimension extends SketchObject {
}
write() {
return {
obj: this.obj.id,
angle: this.angle
}
}
static load(constr, id, data, index) {
const dim = new DiameterDimension(
index[data.obj],
id
);
dim.angle = data.angle;
return dim;
}
}
DiameterDimension.prototype._class = 'TCAD.TWO.DiameterDimension';
DiameterDimension.prototype.TYPE = 'DiameterDimension';
export class AngleBetweenDimension extends SketchObject {
export class AngleBetweenDimension extends Dimension {
constructor(a, b) {
super();
constructor(a, b, id) {
super(id);
this.a = a;
this.b = b;
this.offset = 20;
@ -669,6 +703,26 @@ export class AngleBetweenDimension extends SketchObject {
);
}
}
write() {
return {
a: this.a.id,
b: this.b.id,
offset: this.offset,
configuration: this.configuration.map(o => o.id)
}
}
static load(constr, id, data, index) {
const dim = new AngleBetweenDimension(
index[data.a],
index[data.b],
id
);
dim.offset = data.offset;
dim.configuration = data.configuration.map(id => index[id]);
return dim;
}
}
export function findCenter(aa, ab, ba, bb, avx, avy, bvx, bvy) {
@ -698,5 +752,6 @@ export function findCenter(aa, ab, ba, bb, avx, avy, bvx, bvy) {
}
AngleBetweenDimension.prototype._class = 'TCAD.TWO.AngleBetweenDimension';
AngleBetweenDimension.prototype.TYPE = 'AngleBetweenDimension';

View file

@ -2,18 +2,19 @@ import {SketchObject} from './sketch-object'
import * as math from '../../math/math';
import {Param} from "./param";
import {SketchSegmentSerializationData} from "./segment";
import {EndPoint} from "./point";
export class Ellipse extends SketchObject {
constructor(ep1, ep2) {
super();
this.ep1 = ep1;
this.ep2 = ep2;
constructor(x1, y1, x2, y2, r, id) {
super(id);
this.ep1 = new EndPoint(x1, y1, this.id + ':1');
this.ep2 = new EndPoint(x2, y2, this.id + ':2');
this.addChild(this.ep1);
this.addChild(this.ep2);
this.r = new Param(0, 'R');
this.r = new Param(r === undefined ? 0 : this.radiusX * 0.5, 'R');
this.r.enforceVisualLimit = true;
this.r.set(this.radiusX * 0.5);
}
recoverIfNecessary() {
@ -88,8 +89,29 @@ export class Ellipse extends SketchObject {
static findMinorRadius(majorRadius, pntRadius, pntAngle) {
return Math.abs( Math.sin(pntAngle) / Math.sqrt(1 / sq(pntRadius) - sq(Math.cos(pntAngle) / majorRadius)) );
}
write() {
return {
ep1: this.ep1.write(),
ep2: this.ep2.write(),
r: this.r.get()
};
}
static read(id, data) {
return new Ellipse(
data.ep1.x,
data.ep1.y,
data.ep2.x,
data.ep2.y,
data.r,
id
)
}
}
Ellipse.prototype._class = 'TCAD.TWO.Ellipse';
Ellipse.prototype.TYPE = 'Ellipse';
const sq = (a) => a * a;
const RECOVER_LENGTH = 100;

View file

@ -3,15 +3,16 @@ import {Constraints} from '../parametric'
import * as math from '../../math/math';
import {swap} from '../../utils/utils'
import {EndPoint} from "./point";
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);
constructor(x1, y1, x2, y2, ax, ay, bx, by, r, id) {
super(x1, y1, x2, y2, r, id);
this.a = new EndPoint(ax, ay, this.id + ':A');
this.b = new EndPoint(bx, by, this.id + ':B');
this.addChild(this.a);
this.addChild(this.b);
//we'd like to have angles points have higher selection order
swap(this.children, 0, this.children.length - 2);
@ -49,7 +50,32 @@ export class EllipticalArc extends Ellipse {
xx *= deformScale;
return Math.atan2(yy, xx);
}
write() {
return {
ep1: this.ep1.write(),
ep2: this.ep2.write(),
a: this.a.write(),
b: this.b.write(),
r: this.r.get()
}
}
static read(id, data) {
return new EllipticalArc(
data.ep1.x,
data.ep1.y,
data.ep2.x,
data.ep2.y,
data.a.x,
data.a.y,
data.b.x,
data.b.y,
data.r,
id
);
}
}
EllipticalArc.prototype._class = 'TCAD.TWO.EllipticalArc';
EllipticalArc.prototype.TYPE = 'ELLIPTICAL_ARC';
EllipticalArc.prototype.TYPE = 'EllipticalArc';

View file

@ -2,21 +2,18 @@ import {SketchObject} from './sketch-object'
import * as vec from '../../math/vec';
import {curveTessellate} from '../../brep/geom/impl/nurbs-ext';
import {Ellipse} from "./ellipse";
import {EndPoint} from "./point";
const __v = [0, 0, 0];
export class NurbsObject extends SketchObject {
constructor(curve, a, b) {
super();
constructor(curve, id) {
super(id);
this.curve = curve;
this.a = a;
this.b = b;
let cp = curve.data.controlPoints;
this.a.x = cp[0].x;
this.a.y = cp[0].y;
this.b.x = cp[cp.length - 1].x;
this.b.y = cp[cp.length - 1].y;
this.a = new EndPoint(cp[0].x, cp[0].y, this.id + ":A");
this.b = new EndPoint(cp[cp.length - 1].x, cp[cp.length - 1].y, this.id + ":B");
this.bezierPieces = this.calcBezierPiecewise();
}
@ -90,3 +87,4 @@ export class NurbsObject extends SketchObject {
}
NurbsObject.prototype._class = 'TCAD.TWO.NurbsObject';
NurbsObject.prototype.TYPE = 'NurbsObject';

View file

@ -3,6 +3,13 @@ import {Param as SolverParam} from '../constr/solver';
export class Param {
id: number;
value: number;
solverParam: any;
private readonly debugSymbol: string;
normalizer: (number) => any;
enforceVisualLimit: boolean = false;
constructor(value, debugSymbol) {
this.id = Generator.genID();
this.value = value;

View file

@ -1,16 +1,20 @@
import {SketchObject} from './sketch-object'
import {SketchObject, SketchObjectSerializationData} from './sketch-object'
import {DrawPoint} from './draw-utils'
import Vector from 'math/vector';
import {Param} from "./param";
import {ConstraintDefinitions} from "../constr/ANConstraints";
import {dfs} from "../../../../modules/gems/traverse";
import {SketchSegmentSerializationData} from "./segment";
export class EndPoint extends SketchObject {
constructor(x, y) {
super();
this.parent = null;
params : {
x: Param,
y: Param
};
constructor(x, y, id?) {
super(id);
this.params = {
x: new Param(x, 'X'),
y: new Param(y, 'Y')
@ -22,7 +26,7 @@ export class EndPoint extends SketchObject {
}
set x(val) {
return this.params.x.set(val);
this.params.x.set(val);
}
get y() {
@ -30,7 +34,7 @@ export class EndPoint extends SketchObject {
}
set y(val) {
return this.params.y.set(val);
this.params.y.set(val);
}
visitParams(callback) {
@ -93,7 +97,29 @@ export class EndPoint extends SketchObject {
dest.x = x;
dest.y = y;
}
write(): SketchPointSerializationData {
return {
x: this.x,
y: this.y
}
}
static read(id: string, data: SketchPointSerializationData): EndPoint {
return new EndPoint(
data.x,
data.y,
id
)
}
}
export interface SketchPointSerializationData extends SketchObjectSerializationData {
x: number;
y: number;
}
EndPoint.prototype._class = 'TCAD.TWO.EndPoint';
EndPoint.prototype.TYPE = 'Point';

View file

@ -1,23 +1,28 @@
import {SketchObject} from './sketch-object'
import {SketchObject, SketchObjectSerializationData} from './sketch-object'
import Vector from 'math/vector';
import * as math from '../../math/math'
import {DEG_RAD, makeAngle0_360} from '../../math/math'
import {Param} from "./param";
import {AlgNumConstraint, ConstraintDefinitions} from "../constr/ANConstraints";
import {EndPoint, SketchPointSerializationData} from "./point";
export class Segment extends SketchObject {
constructor(a, b) {
super();
this.a = a;
this.b = b;
a.parent = this;
b.parent = this;
this.children.push(a, b);
this.params = {
ang: new Param(undefined, 'A'),
t: new Param(undefined, 'T')
};
a: EndPoint;
b: EndPoint;
params = {
ang: new Param(undefined, 'A'),
t: new Param(undefined, 'T')
};
constructor(x1:number, y1:number, x2:number, y2:number, id?: string) {
super(id);
this.a = new EndPoint(x1, y1, this.id + ':A');
this.b = new EndPoint(x2, y2, this.id + ':B');
this.a.parent = this;
this.b.parent = this;
this.children.push(this.a, this.b);
this.params.ang.normalizer = makeAngle0_360;
this.params.t.enforceVisualLimit = true;
this.syncGeometry();
@ -159,8 +164,31 @@ export class Segment extends SketchObject {
}
copy() {
return new Segment(this.a.copy(), this.b.copy());
return new Segment(this.a.x, this.a.y, this.b.x, this.b.y);
}
write(): SketchSegmentSerializationData {
return {
a: this.a.write(),
b: this.b.write()
}
}
static read(id: string, data: SketchSegmentSerializationData): Segment {
return new Segment(
data.a.x,
data.a.y,
data.b.x,
data.b.y,
id
)
}
}
export interface SketchSegmentSerializationData extends SketchObjectSerializationData {
a: SketchPointSerializationData;
b: SketchPointSerializationData;
}
Segment.prototype._class = 'TCAD.TWO.Segment';
Segment.prototype.TYPE = 'Segment';

View file

@ -1,16 +0,0 @@
export class Shape {
constructor() {
this.visible = true;
this.style = null;
this.role = null;
}
accept(visitor) {
return visitor(this);
}
draw(ctx, scale) {
}
}

View file

@ -0,0 +1,15 @@
import {Viewer} from "../viewer2d";
export class Shape {
visible: boolean = true;
style: any = null;
role: string = null;
accept(visitor) {
return visitor(this);
}
draw(ctx: any, scale: number, viewer: Viewer) {
}
}

View file

@ -1,27 +1,32 @@
import {Generator} from '../id-generator'
import {Shape} from './shape'
import {Styles} from "../styles";
import {SketchTypes} from "./sketch-types";
import {NoIcon} from "../icons/NoIcon";
import {Layer, Viewer} from "../viewer2d";
export class SketchObject extends Shape {
export abstract class SketchObject extends Shape {
constructor() {
ref: string;
id: string;
parent: SketchObject = null;
markers: any[] = [];
children: SketchObject[] =[];
layer: Layer = null;
constraints: Set<any> = new Set();
readOnly: boolean = false;
fullyConstrained: boolean = false;
generator: any = null;
generators: Set<any> = new Set();
_stage: any = null;
constructor(id: string) {
super();
this.id = Generator.genID();
this.parent = null;
this.markers = [];
this.children = [];
this.layer = null;
this.constraints = new Set();
this.readOnly = false;
this.fullyConstrained = false;
this.generator = null;
this.generators = new Set();
this._stage = null;
this.ref= Generator.genID();
this.id = id || this.ref;
}
get isGenerated() {
let obj = this;
let obj: SketchObject = this;
while (obj) {
if (obj.generator) {
return true;
@ -97,7 +102,7 @@ export class SketchObject extends Shape {
translateImpl(dx, dy) {
this.accept(function (obj) {
if (obj._class === 'TCAD.TWO.EndPoint') {
if (obj.TYPE === 'Point') {
obj.translate(dx, dy);
}
return true;
@ -120,7 +125,9 @@ export class SketchObject extends Shape {
return this.markers.length !== 0;
}
draw(ctx, scale, viewer) {
abstract drawImpl(ctx: CanvasRenderingContext2D, scale: number, viewer: Viewer);
draw(ctx: CanvasRenderingContext2D, scale: number, viewer: Viewer) {
if (!this.visible) return;
const customStyle = this.getCustomStyle();
if (customStyle !== null) {
@ -191,11 +198,11 @@ export class SketchObject extends Shape {
}
get simpleClassName() {
return this._class.replace('TCAD.TWO.', '');
return this.TYPE;
}
get effectiveLayer() {
let shape = this;
let shape: SketchObject = this;
while (shape) {
if (shape.layer) {
return shape.layer;
@ -215,7 +222,7 @@ export class SketchObject extends Shape {
}
ancestry(cb) {
let obj = this;
let obj: SketchObject = this;
while (obj) {
cb(obj);
obj = obj.parent;
@ -223,7 +230,7 @@ export class SketchObject extends Shape {
}
root() {
let obj = this;
let obj: SketchObject = this;
while (obj.parent) {
obj = obj.parent;
}
@ -233,11 +240,21 @@ export class SketchObject extends Shape {
get isRoot() {
return this.parent === null;
}
get icon() {
return NoIcon;
}
abstract write(): SketchObjectSerializationData;
}
export interface SketchObjectSerializationData {
}
export function pointIterator(shape, func) {
shape.accept(o => {
if (o._class === SketchTypes.POINT) {
if (o.TYPE === 'Point') {
func(o);
}
return true;

View file

@ -1,4 +1,4 @@
import {Viewer} from './viewer2d.js'
import {Viewer} from './viewer2d'
import {IO} from './io'
import React from "react";
import {state, stream} from "lstream";

View file

@ -1,25 +0,0 @@
import {state, stream} from 'lstream';
export default function(viewer) {
const streams = {};
streams.objectsUpdate = stream();
streams.objects = streams.objectsUpdate.throttle().map(() => {
let objects = [];
viewer.layers.forEach(l => l.objects.forEach(o => objects.push(o)));
return objects;
}).remember([]);
streams.addingRoleMode = state(null);
streams.selection = state([]);
streams.objectUpdate = stream();
streams.dimScale = state(1);
streams.tool = {
$change: stream(),
$message: stream(),
$hint: stream()
};
return streams;
};

View file

@ -0,0 +1,37 @@
import {state, StateStream, Stream, stream} from 'lstream';
export interface SketcherStreams {
selection: StateStream<any[]>;
addingRoleMode: StateStream<any>;
objectsUpdate: StateStream<any>;
objects: StateStream<any>;
objectUpdate: Stream<any>;
dimScale: StateStream<number>;
tool: { $change: Stream<any>; $message: Stream<any>; $hint: Stream<any> };
}
export default function(viewer): SketcherStreams {
const streams: any = {
};
streams.objectsUpdate = stream();
streams.objects = streams.objectsUpdate.throttle().map(() => {
let objects = [];
viewer.layers.forEach(l => l.objects.forEach(o => objects.push(o)));
return objects;
}).remember([]);
streams.addingRoleMode = state(null);
streams.selection = state([]);
streams.objectUpdate = stream();
streams.dimScale = state(1);
streams.tool = {
$change: stream(),
$message: stream(),
$hint: stream()
};
return streams as SketcherStreams;
};

View file

@ -60,9 +60,9 @@ export class AddArcTool extends Tool {
createArcStep(p) {
this.viewer.historyManager.checkpoint();
this.arc = new Arc(
new EndPoint(p.x, p.y),
new EndPoint(p.x, p.y),
new EndPoint(p.x, p.y)
p.x, p.y,
p.x, p.y,
p.x, p.y
);
this.point = this.arc.a;
this.viewer.activeLayer.add(this.arc);

View file

@ -31,8 +31,14 @@ export class BezierCurveTool extends Tool {
mouseup(e) {
if (this.curve == null) {
this.checkIfConnectedToOtherCurve();
const p = this.endpoint(e);
this.curve = new BezierCurve(p, p.copy(), p.copy(), p.copy());
const p = this.viewer.screenToModel(e);
this.curve = new BezierCurve(p.x, p.y, p.x, p.y, p.x, p.y, p.x, p.y);
if (this.viewer.snapped != null) {
this.snapIfNeed(this.curve.a);
}
this.viewer.activeLayer.add(this.curve);
this.viewer.refresh();
} else {

View file

@ -47,9 +47,7 @@ export class EditCircleTool extends Tool {
this.viewer.historyManager.checkpoint();
const needSnap = tryToSnap && this.viewer.snapped != null;
const p = needSnap ? this.viewer.snapped : center;
this.circle = new Circle(
new EndPoint(p.x, p.y)
);
this.circle = new Circle(p.x, p.y);
this.pointPicked(this.circle.c.x, this.circle.c.y);
this.sendHint('specify radius');
this.viewer.activeLayer.add(this.circle);

View file

@ -1,4 +1,10 @@
import {AngleBetweenDimension, DiameterDimension, Dimension, findCenter, HDimension, VDimension,} from '../shapes/dim'
import {
AngleBetweenDimension,
DiameterDimension,
findCenter,
HDimension, LinearDimension,
VDimension,
} from '../shapes/dim'
import Vector from 'math/vector';
import {EndPoint} from '../shapes/point'
import {Tool} from './tool'
@ -69,7 +75,7 @@ export class AddDimTool extends Tool {
export class AddFreeDimTool extends AddDimTool {
constructor(viewer, layer) {
super('free dimension', viewer, layer, (a, b) => new Dimension(a, b));
super('free dimension', viewer, layer, (a, b) => new LinearDimension(a, b));
}
}
@ -96,7 +102,7 @@ export class AddCircleDimTool extends Tool {
mousemove(e) {
var p = this.viewer.screenToModel(e);
var objects = this.viewer.search(p.x, p.y, DEFAULT_SEARCH_BUFFER, true, false, []).filter(function (o) {
return o._class === 'TCAD.TWO.Circle' || o._class === 'TCAD.TWO.Arc';
return o.TYPE === 'Circle' || o.TYPE === 'Arc';
});
if (objects.length !== 0) {
@ -137,7 +143,7 @@ export class AddAngleTool extends Tool {
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 result = this.viewer.search(p.x, p.y, DEFAULT_SEARCH_BUFFER, true, false, []).filter(o => o.TYPE === 'Segment');
const [segment] = result;
if (this.dim) {

View file

@ -32,8 +32,8 @@ export class EllipseTool extends Tool {
}
newEllipse(p) {
const ep = () => new EndPoint(p.x, p.y);
return this.arc ? new EllipticalArc(ep(), ep(), ep(), ep()) : new Ellipse(ep(), ep());
return this.arc ? new EllipticalArc(p.x, p.y, p.x, p.y, p.x, p.y, p.x, p.y) : new Ellipse(p.x, p.y, p.x, p.y);
}
demoBPoint() {

View file

@ -60,9 +60,9 @@ export class FilletTool extends Tool {
vec._plus(point1);
const arc = new Arc(
new EndPoint(point1.x, point1.y),
new EndPoint(point2.x, point2.y),
new EndPoint(vec.x, vec.y));
point1.x, point1.y,
point2.x, point2.y,
vec.x, vec.y);
point1.parent.layer.add(arc);
const pm = this.viewer.parametricManager;
@ -127,7 +127,7 @@ export class FilletTool extends Tool {
}
static isLine(line) {
return line != null && line._class === 'TCAD.TWO.Segment';
return line != null && line.TYPE === 'Segment';
}
getCandidate(e) {

View file

@ -34,7 +34,7 @@ export class LoopPickTool extends Tool {
}
otherEnd(point) {
if (point.parent.a.id == point.id) {
if (point.parent.a.id === point.id) {
return point.parent.b;
} else {
return point.parent.a;
@ -45,8 +45,8 @@ export class LoopPickTool extends Tool {
this.loops.clear();
const points = [];
this.viewer.accept((obj) => {
if (obj._class == 'TCAD.TWO.EndPoint' && obj.parent &&
obj.parent.a && obj.parent.b && (obj.parent.a == obj || obj.parent.b == obj)) {
if (obj.TYPE === 'Point' && obj.parent &&
obj.parent.a && obj.parent.b && (obj.parent.a === obj || obj.parent.b === obj)) {
points.push(obj);
}
return true;
@ -68,7 +68,7 @@ export class LoopPickTool extends Tool {
return points.length;
}
};
const loopPoints = Graph.findAllLoops(graph, (p) => p.id, (a, b) => a.id == b.id);
const loopPoints = Graph.findAllLoops(graph, (p) => p.id, (a, b) => a.id === b.id);
const loops = loopPoints.map(l => this.cleanLoop(l));
for (let loop of loops) {
for (let point of loop.points) {

View file

@ -18,7 +18,7 @@ export class OffsetTool extends LoopPickTool {
const length = loopEdges.length;
for (let obj of loopEdges) {
if (!SUPPORTED_OBJECTS.has(obj._class)) {
if (!SUPPORTED_OBJECTS.has(obj.TYPE)) {
alert(obj._class + " isn't supported for offsets");
return;
}
@ -43,14 +43,14 @@ export class OffsetTool extends LoopPickTool {
for (let i = 0; i < length; ++i) {
const edge = edges[i];
const origEdge = loopEdges[i];
const edgeInverse = loopPoints[i] != origEdge.a;
const inverse = mainInverse != edgeInverse;
const edgeInverse = loopPoints[i] !== origEdge.a;
const inverse = mainInverse !== edgeInverse;
this.viewer.activeLayer.add(edge);
if (edge._class == 'TCAD.TWO.Segment') {
if (edge.TYPE === 'Segment') {
pm._add(new Constraints.Parallel(origEdge, edge));
pm._add(new Constraints.P2LDistanceSigned(origEdge.a, inverse?edge.b:edge.a, inverse?edge.a:edge.b, offsetConstant));
} else if (edge._class == 'TCAD.TWO.Arc') {
} else if (edge.TYPE === 'Arc') {
edge.stabilize(this.viewer);
pm._linkObjects([edge.c, origEdge.c]);
pm._add(new Constraints.RadiusOffset(inverse?origEdge:edge, inverse?edge:origEdge, offsetConstant));
@ -59,13 +59,13 @@ export class OffsetTool extends LoopPickTool {
for (let i = 0; i < edges.length; i++) {
const next = ((i + 1) % edges.length);
if (loopEdges[i].a.linked.indexOf(loopEdges[next].a) != -1) {
if (loopEdges[i].a.linked.indexOf(loopEdges[next].a) !== -1) {
pm._linkObjects([edges[i].a, edges[next].a]);
} else if (loopEdges[i].a.linked.indexOf(loopEdges[next].b) != -1) {
} else if (loopEdges[i].a.linked.indexOf(loopEdges[next].b) !== -1) {
pm._linkObjects([edges[i].a, edges[next].b]);
} else if (loopEdges[i].b.linked.indexOf(loopEdges[next].a) != -1) {
} else if (loopEdges[i].b.linked.indexOf(loopEdges[next].a) !== -1) {
pm._linkObjects([edges[i].b, edges[next].a]);
} else if (loopEdges[i].b.linked.indexOf(loopEdges[next].b) != -1) {
} else if (loopEdges[i].b.linked.indexOf(loopEdges[next].b) !== -1) {
pm._linkObjects([edges[i].b, edges[next].b]);
}
}
@ -76,10 +76,10 @@ export class OffsetTool extends LoopPickTool {
twoConnectedArcs() {
function isArc(edge) {
return edge._class == 'TCAD.TWO.Arc';
return edge._class === 'Arc';
}
const edges = this.pickedLoop.edges;
return edges.length == 2 && isArc(edges[0]) && isArc(edges[1]);
return edges.length === 2 && isArc(edges[0]) && isArc(edges[1]);
}
}
@ -89,8 +89,8 @@ function segmentToVector(segment) {
}
const SUPPORTED_OBJECTS = new Set();
SUPPORTED_OBJECTS.add('TCAD.TWO.Segment');
SUPPORTED_OBJECTS.add('TCAD.TWO.Arc');
SUPPORTED_OBJECTS.add('Segment');
SUPPORTED_OBJECTS.add('Arc');
function SimpleEdge(a, b) {
this.a = a;

View file

@ -64,19 +64,18 @@ export class RectangleTool extends Tool {
}
createRectangle(v) {
const p = new EndPoint(v.x, v.y);
//from top, clockwise
this.rectangle = [
new Segment(p, p.copy()),
new Segment(p.copy(), p.copy()),
new Segment(p.copy(), p.copy()),
new Segment(p.copy(), p.copy())
new Segment(v.x, v.y, v.x, v.y),
new Segment(v.x, v.y, v.x, v.y),
new Segment(v.x, v.y, v.x, v.y),
new Segment(v.x, v.y, v.x, v.y)
];
for (let s of this.rectangle) {
this.viewer.activeLayer.add(s);
this.snapExclude.push(s.a, s.b);
}
this.pointPicked(p.x, p.y);
this.pointPicked(v.x, v.y);
this.viewer.refresh();
}

View file

@ -53,16 +53,6 @@ export class Tool {
}
}
endpoint(e) {
const ep = new EndPoint(0, 0);
if (this.viewer.snapped != null) {
this.snapIfNeed(ep);
} else {
ep.setFromPoint(this.viewer.screenToModel(e))
}
return ep;
}
static dumbMode(e) {
return e.ctrlKey || e.metaKey || e.altKey;
}

View file

@ -1,21 +1,57 @@
import {Styles} from './styles';
import {ParametricManager} from './parametric';
import {HistoryManager} from './history';
import {ToolManager} from './tools/manager';
import {PanTool} from './tools/pan';
import {Segment} from './shapes/segment';
import {EndPoint} from './shapes/point';
import {Point} from './shapes/primitives';
import {ReferencePoint} from './shapes/reference-point';
import {BasisOrigin} from './shapes/basis-origin';
import Vector from 'math/vector';
import * as draw_utils from './shapes/draw-utils';
import {Matrix3} from '../math/l3space';
import sketcherStreams from './sketcherStreams';
import {BBox} from "./io";
import sketcherStreams, {SketcherStreams} from './sketcherStreams';
import {BBox, IO} from "./io";
import {NOOP} from "../../../modules/gems/func";
import {Shape} from "./shapes/shape";
import {SketchObject} from "./shapes/sketch-object";
import {Styles} from './styles';
import {Dimension} from "./shapes/dim";
class Viewer {
export class Viewer {
presicion: number;
canvas: any;
io: IO;
streams: SketcherStreams;
retinaPxielRatio: number;
ctx: CanvasRenderingContext2D;
onWindowResize: () => void;
private _activeLayer: Layer;
layers: Layer<SketchObject>[];
dimLayer: Layer<Dimension>;
annotationLayer: Layer<Dimension>;
dimLayers: Layer<Dimension>[];
private readonly _workspace: Layer[][];
referencePoint: Shape;
toolManager: any;
parametricManager: any;
translate: { x: number; y: number };
scale: number;
captured: {
highlight2: any[],
tool: any[],
highlight: any[],
selection: any[]
};
historyManager: any;
transformation: any;
screenToModelMatrix: any;
private readonly _serviceWorkspace: Layer<Shape>[][];
private __prevStyle: null;
interactiveScale: number;
unscale: number;
customSelectionHandler: any;
constructor(canvas, IO) {
@ -44,19 +80,15 @@ class Viewer {
updateCanvasSize();
window.addEventListener('resize', this.onWindowResize, false);
Object.defineProperty(this, "activeLayer", {
get: viewer.getActiveLayer,
set: viewer.setActiveLayer
});
this.ctx = this.canvas.getContext("2d");
this._activeLayer = null;
this.layers = [
this.createLayer("sketch", Styles.DEFAULT)
this.createLayer(PREDEFINED_LAYERS.GROUND, Styles.DEFAULT),
this.createLayer(PREDEFINED_LAYERS.SKETCH, Styles.DEFAULT)
// this.createLayer("_construction_", Styles.CONSTRUCTION)
];
this.dimLayer = this.createLayer("_dim", Styles.DIM);
this.annotationLayer = this.createLayer("_annotations", Styles.ANNOTATIONS);
this.annotationLayer = this.createLayer<Dimension>("_annotations", Styles.ANNOTATIONS);
this.dimLayers = [this.dimLayer, this.annotationLayer];
this.streams.dimScale.attach(() => this.refresh());
@ -64,7 +96,6 @@ class Viewer {
this.referencePoint = new ReferencePoint();
this.referencePoint.visible = false;
this._serviceWorkspace = [this._createServiceLayers()];
this.toolManager = new ToolManager(this, new PanTool(this));
this.parametricManager = new ParametricManager(this);
@ -72,14 +103,17 @@ class Viewer {
this.translate = {x: 0.0, y: 0.0};
this.scale = 1.0;
// @ts-ignore
this.captured = {
};
Object.keys(CAPTURES).forEach(key => this.captured[key] = []);
this.historyManager = new HistoryManager(this);
this.transformation = null;
this.screenToModelMatrix = null;
this._serviceWorkspace = [this._createServiceLayers()];
this.refresh();
}
@ -123,9 +157,7 @@ class Viewer {
};
addSegment(x1, y1, x2, y2, layer) {
var a = new EndPoint(x1, y1);
var b = new EndPoint(x2, y2);
var line = new Segment(a, b);
var line = new Segment(x1, y1, x2, y2);
layer.add(line);
return line;
};
@ -199,14 +231,21 @@ class Viewer {
return pickResult;
};
_createServiceLayers() {
let layer = this.createLayer("_service", Styles.SERVICE);
_createServiceLayers(): Layer<Shape>[] {
let layer = this.createLayer<Shape>("_service", Styles.SERVICE);
// layer.objects.push(new CrossHair(0, 0, 20));
layer.objects.push(new Point(0, 0, 2));
// layer.objects.push(new Point(0, 0, 2));
layer.objects.push(this.referencePoint);
layer.objects.push(new BasisOrigin(null, this));
return [layer];
const origin = new EndPoint(0, 0);
origin.id = 'ORIGIN';
layer.objects.push(origin);
origin.stage = this.parametricManager.groundStage;
origin.visitParams(param => {
param.set = NOOP;
});
return [layer];
};
refresh() {
@ -304,7 +343,7 @@ class Viewer {
this.withdrawAll('tool')
};
showBounds(x1, y1, x2, y2, offset) {
showBounds(x1, y1, x2, y2) {
const dx = Math.max(x2 - x1, 1);
const dy = Math.max(y2 - y1, 1);
const cRatio = this.canvas.width / this.canvas.height;
@ -383,9 +422,7 @@ class Viewer {
//same as accept but without controlling when to break the flow
traverse(visitor) {
for (let layer of this.layers) {
for (let object of layer.objects) {
object.traverse(visitor);
}
layer.traverseSketchObjects(visitor)
}
}
@ -410,6 +447,12 @@ class Viewer {
return result;
};
createIndex() {
const index = {};
this.traverse(o => index[o.id] = o);
return index;
}
select(objs, exclusive) {
if (this.customSelectionHandler) {
this.customSelectionHandler(objs, exclusive);
@ -474,7 +517,7 @@ class Viewer {
}
unHighlight(objs) {
this.withdrawAll('highlight', objs);
this.withdraw('highlight', objs);
}
pick(e) {
@ -482,29 +525,25 @@ class Viewer {
return this.search(m.x, m.y, DEFAULT_SEARCH_BUFFER, true, false, []);
};
getActiveLayer() {
var layer = this._activeLayer;
get activeLayer() {
let layer = this._activeLayer;
if (layer == null || layer.readOnly) {
layer = null;
for (var i = 0; i < this.layers.length; i++) {
var l = this.layers[i];
for (let i = 0; i < this.layers.length; i++) {
let l = this.layers[i];
if (!l.readOnly) {
layer = l;
break;
}
}
}
if (layer == null) {
layer = this.createLayer("sketch", Styles.DEFAULT);
this.layers.push(layer);
}
return layer;
return this.findLayerByName(PREDEFINED_LAYERS.SKETCH);
};
setActiveLayerName(layerName) {
set activeLayerName(layerName) {
let layer = this.findLayerByName(layerName);
if (layer) {
this.activeLayer = layer;
this._activeLayer = layer;
} else {
console.warn("layer doesn't exist: " + layerName);
}
@ -516,36 +555,13 @@ class Viewer {
}
};
equalizeLinkedEndpoints() {
const visited = new Set();
function equalize(obj) {
if (visited.has(obj.id)) return;
visited.add(obj.id);
for (let link of obj.linked) {
if (isEndPoint(link)) {
equalize(obj, link);
link.setFromPoint(obj);
equalize(link);
}
}
}
this.accept((obj) => {
if (isEndPoint(obj)) {
equalize(obj);
}
return true;
});
};
fullHeavyUIRefresh() {
this.refresh();
this.parametricManager.notify();
};
createLayer(name, style, onUpdate) {
return new Layer(name, style, this)
createLayer<T>(name, style) {
return new Layer<T>(name, style, this)
};
objectsUpdate = () => this.streams.objectsUpdate.next();
@ -574,7 +590,14 @@ class Viewer {
const isEndPoint = o => o._class === 'TCAD.TWO.EndPoint';
const isConstruction = o => o.role === 'construction';
class Layer {
export class Layer<T = Shape> {
name: any;
style: any;
stylesByRoles: { virtual: any; objectConstruction: any; construction: any };
objects: T[];
readOnly: boolean;
viewer: Viewer;
constructor(name, style, viewer) {
this.name = name;
@ -614,8 +637,12 @@ class Layer {
}
};
traverse(callback) {
this.objects.forEach(o => o.traverse(callback));
traverseSketchObjects(callback) {
this.objects.forEach(o => {
if (o instanceof SketchObject) {
o.traverse(callback)
}
});
}
_addAndNotify(object) {
@ -653,4 +680,9 @@ const measurer = {x: 0, y: 0, z: 0};
export const DEFAULT_SEARCH_BUFFER = 20;
export {Viewer, Styles}
export const PREDEFINED_LAYERS = {
SKETCH: "sketch",
GROUND: "ground",
};
export {Styles};