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 {HDimension, VDimension, Dimension, DiameterDimension} from './shapes/dim' import {Constraints} from './parametric' import {HashTable} from '../utils/hashmap' import Vector from 'math/vector'; import exportTextData from 'gems/exportTextData'; import NurbsCurve from '../brep/geom/curves/nurbsCurve'; import {NurbsObject} from './shapes/nurbsObject'; import {System} from './system'; import {AlgNumConstraint} from "./constr/ANConstraints"; const Types = { POINT : 'TCAD.TWO.EndPoint', SEGMENT : 'TCAD.TWO.Segment', ARC : 'TCAD.TWO.Arc', CIRCLE : 'TCAD.TWO.Circle', ELLIPSE : 'TCAD.TWO.Ellipse', ELL_ARC : 'TCAD.TWO.EllipticalArc', BEZIER : 'TCAD.TWO.BezierCurve', NURBS : 'TCAD.TWO.NurbsObject', DIM : 'TCAD.TWO.Dimension', HDIM : 'TCAD.TWO.HDimension', VDIM : 'TCAD.TWO.VDimension', DDIM : 'TCAD.TWO.DiameterDimension' }; 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(); const index = {}; function endPoint(p) { const [id, [xref, x], [yref, y]] = p; let ep = index[id]; if (ep !== undefined) { return; } ep = new EndPoint(x, y); index[xref] = ep.params.x; index[yref] = ep.params.y; index[id] = ep; return ep; } 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]; } } } var layer = viewer.createLayer(name, Styles.DEFAULT); viewer.layers.push(layer); return layer; } const version = sketch.version || 1; var T = Types; var maxEdge = 0; var sketchLayers = sketch['layers']; var boundary = sketch['boundary']; var boundaryNeedsUpdate = !(boundary === undefined || boundary == null); if (sketchLayers !== undefined) { for (var l = 0; l < sketchLayers.length; ++l) { var ioLayer = sketchLayers[l]; var layerName = ioLayer['name']; var boundaryProcessing = layerName === IO.BOUNDARY_LAYER_NAME && boundaryNeedsUpdate; var layer = getLayer(this.viewer, layerName); if (!!ioLayer.style) layer.style = ioLayer.style; layer.readOnly = !!ioLayer.readOnly; var layerData = ioLayer['data']; for (i = 0; i < layerData.length; ++i) { var obj = layerData[i]; var skobj = null; var _class = obj['_class']; var aux = !!obj['aux']; var role = obj['role']; //support legacy format if (!role) { role = layerName === '_construction_' ? 'construction' : null; } if (boundaryProcessing) { if (_class === T.SEGMENT && boundary.lines.length === 0) continue; else if (_class === T.ARC && boundary.arcs.length === 0) continue; else if (_class === T.CIRCLE && boundary.circles.length === 0) continue; } 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 if (_class === T.HDIM) { skobj = new HDimension(obj['a'], obj['b']); skobj.flip = obj['flip']; } else if (_class === T.VDIM) { skobj = new VDimension(obj['a'], obj['b']); skobj.flip = obj['flip']; } else if (_class === T.DIM) { skobj = new Dimension(obj['a'], obj['b']); skobj.flip = obj['flip']; } else if (_class === T.DDIM) { skobj = new DiameterDimension(obj['obj']); } if (skobj != null) { skobj.role = role; if (!aux) skobj.stabilize(this.viewer); if (aux) skobj.accept(function(o){o.aux = true; return true;}); if (obj['edge'] !== undefined) { skobj.edge = obj['edge']; maxEdge = Math.max(maxEdge, skobj.edge); } 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]; } } } if (boundaryProcessing) { if (_class === T.SEGMENT) this.synchLine(skobj, boundary.lines.shift()); else if (_class === T.ARC) this.synchArc(skobj, boundary.arcs.shift()); else if (_class === T.CIRCLE) this.synchCircle(skobj, boundary.circles.shift()); } } } } for (i = 0; i < this.viewer.dimLayer.objects.length; ++i) { obj = this.viewer.dimLayer.objects[i]; if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) { obj.a = index[obj.a]; obj.b = index[obj.b]; } else if (obj._class === T.DDIM) { obj.obj = index[obj.obj]; } } if (boundaryNeedsUpdate) { this.addNewBoundaryObjects(boundary, maxEdge); } const boundaryLayer = this.viewer.findLayerByName(IO.BOUNDARY_LAYER_NAME); if (boundaryLayer != null) { this.linkEndPoints(boundaryLayer.objects); } var sketchConstraints = sketch['constraints']; if (sketchConstraints !== undefined) { for (var i = 0; i < sketchConstraints.length; ++i) { try { if (version > 1) { this.viewer.parametricManager.algnNumSystem.addConstraint(AlgNumConstraint.read(sketchConstraints[i], index)); } else { const c = this.parseConstr(sketchConstraints[i], index); this.viewer.parametricManager._add(c); } } catch (msg) { console.info("Skipping. " + msg); } } this.viewer.parametricManager.notify(); } var constants = sketch['constants']; if (constants !== undefined) { this.viewer.params.constantDefinition = constants; } }; IO.prototype.linkEndPoints = function(objects) { const index = HashTable.forVector2d(); for (let obj of objects) { obj.accept((o) => { if (o._class == Types.POINT) { const equalPoint = index.get(o); if (equalPoint == null) { index.put(o, o); } else { o.linked.push(equalPoint); equalPoint.linked.push(o); } } return true; }) } }; 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.addNewBoundaryObjects = function(boundary, maxEdge) { var 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; var i, obj, id = maxEdge + 1; function __processAux(obj) { obj.edge = id ++; obj.accept(function(o){ o.aux = true; return true; }); } for (i = 0; i < boundary.lines.length; ++i) { var edge = boundary.lines[i]; var seg = this.viewer.addSegment(edge.a.x, edge.a.y, edge.b.x, edge.b.y, boundaryLayer); __processAux(seg); } for (i = 0; i < boundary.arcs.length; ++i) { var a = boundary.arcs[i]; var 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); __processAux(arc); } for (i = 0; i < boundary.circles.length; ++i) { obj = boundary.circles[i]; var circle = new Circle(new EndPoint(obj.c.x, obj.c.y)); circle.r.set(obj.r); boundaryLayer.objects.push(circle); __processAux(circle); } 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); __processAux(nurbs); } }; 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); if (this.viewer.parametricManager.system.subSystems.length !== 0) { this.viewer.parametricManager.system = new System(); 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]; var toLayer = {name : layer.name, style : layer.style, readOnly: layer.readOnly, data : []}; sketch.layers.push(toLayer); for (var i = 0; i < layer.objects.length; ++i) { var obj = layer.objects[i]; var 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; toLayer.data.push(to); 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.flip = obj.flip; } else if (obj._class === T.DDIM) { to.obj = obj.obj.id; } const children = nonPointChildren(obj).map(c => c.id); if (children.length !== 0) { to.children = children; } } } } sketch.constraints = []; const systemConstraints = this.viewer.parametricManager.algnNumSystem.constraints; for (let sc of systemConstraints) { if (!sc.internal) { sketch.constraints.push(sc.write()); } } var constantDefinition = this.viewer.params.constantDefinition; if (constantDefinition !== undefined && constantDefinition != null && !/^\s*$/.test(constantDefinition)) { sketch.constants = constantDefinition; } 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('', [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('', [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('', [obj.a.x, obj.a.y, r, r, dir, 1, obj.b.x, obj.b.y]); } else if (obj._class === T.CIRCLE) { out.fline('', [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(''); } bbox.inc(20); return _format("\n", bbox.bbox) + out.data + "" }; 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, Types};