From 494f9f6c406fab6a312649c6b73067ced172be81 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Sat, 5 Feb 2022 21:30:22 -0500 Subject: [PATCH] Save different instances of subcircuits separately while keeping the format backward compatible Although different instances of subcircuits starts identical, they corresponds to distinct runtime object and their internal may deviate from each other due to edits done on each subcircuits. The current format disgard all the changes except for the first subcircuit and apply the edit on the first subcircuit to all instances during loading. This change modifies the saving format while keeping the file backward compatible so that the new file can still be openned by old code, with only minor display differences. This is done by automatically generate new celltype for each instances of the subcircuit on saving and saving the old value that is only useful for display in a separate attribute `disp_celltype`. This automatic name generation is also only done when there are multiple instances of the same cell type so that we can keep generating names shorter and the file as closed to the old format as possible. --- src/cells/subcircuit.mjs | 6 +++-- src/circuit.mjs | 53 +++++++++++++++++++++++++++++++++++++--- src/index.mjs | 2 +- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/cells/subcircuit.mjs b/src/cells/subcircuit.mjs index 8fc9c31..6512d04 100644 --- a/src/cells/subcircuit.mjs +++ b/src/cells/subcircuit.mjs @@ -28,7 +28,9 @@ export const Subcircuit = Box.define('Subcircuit', { } }, { initialize() { - this.bindAttrToProp('text.type/text', 'celltype'); + if (!this.get('disp_celltype')) + this.set('disp_celltype', this.get('celltype')) + this.bindAttrToProp('text.type/text', 'disp_celltype'); const graph = this.get('graph'); console.assert(graph instanceof joint.dia.Graph); @@ -102,7 +104,7 @@ export const Subcircuit = Box.define('Subcircuit', { selector: 'type' } ], Box.prototype.markupZoom), - _gateParams: Box.prototype._gateParams.concat(['celltype']), + _gateParams: Box.prototype._gateParams.concat(['celltype', 'disp_celltype']), _unsupportedPropChanges: Box.prototype._unsupportedPropChanges.concat(['celltype']) }); diff --git a/src/circuit.mjs b/src/circuit.mjs index 89f64ad..3b3da10 100644 --- a/src/circuit.mjs +++ b/src/circuit.mjs @@ -292,8 +292,19 @@ export class HeadlessCircuit { for (const elem of graph.getElements()) { const args = ret.devices[elem.get('id')] = elem.getGateParams(layout); if (!laid_out) delete args.position; - if (elem instanceof this._cells.Subcircuit && !subcircuits[elem.get('celltype')]) { - subcircuits[elem.get('celltype')] = fromGraph(elem.get('graph')); + if (elem instanceof this._cells.Subcircuit) { + const celltype = elem.get('celltype'); + const subcircuit = { + dev: args, + graph: fromGraph(elem.get('graph')) + }; + const prev_sub = subcircuits[celltype]; + if (!prev_sub) { + subcircuits[celltype] = [subcircuit]; + } + else { + prev_sub.push(subcircuit); + } } } for (const elem of graph.getLinks()) { @@ -302,7 +313,43 @@ export class HeadlessCircuit { return ret; } const ret = fromGraph(this._graph); - ret.subcircuits = subcircuits; + ret.subcircuits = {}; + for (const celltype in subcircuits) { + const subs = subcircuits[celltype]; + const nsubs = subs.length; + if (nsubs == 1) { + // We check for conflict of generated names with + // both the original names and the new names + // so it's guaranteed that none of the original names conflict + // with the generated names + console.assert(!(celltype in ret.subcircuits)); + ret.subcircuits[celltype] = subs[0].graph; + continue; + } + let cnt = -1; + const gen_name = () => { + while (true) { + const id = cnt++; + // Use the original name for the first one to keep the file closer + // to the old one. + if (id == -1) + return celltype; + const name = `${celltype}$${id}`; + if (name in subcircuits || name in ret.subcircuits) + continue; + return name; + } + }; + for (let i = 0; i < nsubs; i++) { + const sub = subs[i]; + // Rename, assign to return value, and fix the reference (celltype) + // in the device tree. + sub.dev.celltype = gen_name(); + if (!sub.dev.disp_celltype && sub.dev.celltype !== celltype) + sub.dev.disp_celltype = celltype; + ret.subcircuits[sub.dev.celltype] = sub.graph; + } + } return ret; } waitForWire(wire, trigger) { diff --git a/src/index.mjs b/src/index.mjs index 3d0f92f..0970517 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -162,7 +162,7 @@ export class Circuit extends HeadlessCircuit { // subcircuit display this.listenTo(paper, 'open:subcircuit', (model) => { const div = $('
', { - title: model.get('celltype') + ' ' + model.get('label') + title: model.get('disp_celltype') + ' ' + model.get('label') }).appendTo('html > body'); const pdiv = $('
').appendTo(div); const graph = model.get('graph');