mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-20 23:43:27 +01:00
stable ids, new saving format
This commit is contained in:
parent
0dc66251d9
commit
9730e2c8ec
49 changed files with 1449 additions and 1186 deletions
57
modules/lstream/index.d.ts
vendored
Normal file
57
modules/lstream/index.d.ts
vendored
Normal 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>;
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES5",
|
||||
"baseUrl": ".",
|
||||
"downlevelIteration": true,
|
||||
"paths": {
|
||||
"*": [
|
||||
"modules/*",
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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' />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, {Fragment} from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import 'ui/styles/init/index.less';
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@
|
|||
border-radius: @itemRadius 0 0 @itemRadius;
|
||||
padding: 3px 3px;
|
||||
background-color: @alt-color;
|
||||
& svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.menuButton {
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
84
web/app/sketcher/generators/boundaryGenerator.js
Normal file
84
web/app/sketcher/generators/boundaryGenerator.js
Normal 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) => {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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
613
web/app/sketcher/io.ts
Normal 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};
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
5
web/app/sketcher/shapes/dim.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import {SketchObject} from "./sketch-object";
|
||||
|
||||
export interface Dimension extends SketchObject {
|
||||
|
||||
}
|
||||
|
|
@ -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';
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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';
|
||||
|
||||
|
||||
|
|
@ -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';
|
||||
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
15
web/app/sketcher/shapes/shape.ts
Normal file
15
web/app/sketcher/shapes/shape.ts
Normal 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) {
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
37
web/app/sketcher/sketcherStreams.ts
Normal file
37
web/app/sketcher/sketcherStreams.ts
Normal 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;
|
||||
};
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
Loading…
Reference in a new issue