Commit 87191432 by Piotr Mitros

Schematic with center tag from cjt

parent d54ab750
...@@ -4,12 +4,30 @@ ...@@ -4,12 +4,30 @@
// //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Chris Terman, Dec. 2011 // Copyright (C) 2011 Massachusetts Institute of Technology
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// create a circuit for simulation using "new cktsim.Circuit()" // create a circuit for simulation using "new cktsim.Circuit()"
// for modified nodal analysis (MNA) stamps see // for modified nodal analysis (MNA) stamps see
// http://books.google.com/books?id=qhHsSlazGrQC&pg=PA44&lpg=PA44&dq=MNA+stamp+inductor&source=bl&ots=ThMq-FmhLo&sig=cTP1ld_fhIJbGPSBXPDbh3Xappk&hl=en&sa=X&ei=6wb-ToecFMHj0QH61-Fs&ved=0CFcQ6AEwAw#v=onepage&q=MNA%20stamp%20inductor&f=false // http://www.analog-electronics.eu/analog-electronics/modified-nodal-analysis/modified-nodal-analysis.xhtml
cktsim = (function() { cktsim = (function() {
...@@ -23,6 +41,15 @@ cktsim = (function() { ...@@ -23,6 +41,15 @@ cktsim = (function() {
T_VOLTAGE = 0; T_VOLTAGE = 0;
T_CURRENT = 1; T_CURRENT = 1;
v_abstol = 1e-6; // criterion for absolute convergence (voltage)
i_abstol = 1e-12; // criterion for absolute convergence (current)
min_time_step = 1e-18; // smallest possible time step
max_iterations = 50; // max iterations before giving up
increase_limit = 4; // if we converge in this many iterations, increase time step
time_step_increase_factor = 2.0;
time_step_decrease_factor = 0.3;
reltol = 0.001; // convergence criterion relative to max observed value
function Circuit() { function Circuit() {
this.node_map = new Array(); this.node_map = new Array();
this.ntypes = []; this.ntypes = [];
...@@ -34,11 +61,6 @@ cktsim = (function() { ...@@ -34,11 +61,6 @@ cktsim = (function() {
this.finalized = false; this.finalized = false;
this.node_index = -1; this.node_index = -1;
// for backward Euler: coeff0 = 1/timestep, coeff1 = 0
// for trapezoidal: coeff0 = 2/timestep, coeff1 = 1
this.coeff0 = undefined;
this.coeff1 = undefined;
} }
// index of ground node // index of ground node
...@@ -67,10 +89,22 @@ cktsim = (function() { ...@@ -67,10 +89,22 @@ cktsim = (function() {
// set up augmented matrix and various temp vectors // set up augmented matrix and various temp vectors
this.matrix = new Array(this.N); this.matrix = new Array(this.N);
for (var i = this.N - 1; i >= 0; --i) this.soln_max = new Array(this.N); // max abs value seen for each unknown
this.rtol = new Array(this.N); // soln_max * reltol
this.abstol = new Array(this.N);
this.solution = new Array(this.N);
for (var i = this.N - 1; i >= 0; --i) {
this.matrix[i] = new Array(this.N + 1); this.matrix[i] = new Array(this.N + 1);
this.swap = new Array(this.N); // keep track of row swaps during pivoting this.soln_max[i] = 0.0;
this.soln = new Array(this.N); // hold swapped solution this.rtol[i] = 0.0;
this.abstol[i] = this.ntypes[i] == T_VOLTAGE ? v_abstol : i_abstol;
this.solution[i] = 0.0;
}
// for backward Euler: coeff0 = 1/timestep, coeff1 = 0
// for trapezoidal: coeff0 = 2/timestep, coeff1 = 1
this.coeff0 = undefined;
this.coeff1 = undefined;
} }
} }
...@@ -84,8 +118,8 @@ cktsim = (function() { ...@@ -84,8 +118,8 @@ cktsim = (function() {
var component = netlist[i]; var component = netlist[i];
var type = component[0]; var type = component[0];
// ignore wires, ground connections and view info // ignore wires, ground connections, scope probes and view info
if (type == 'view' || type == 'w' || type == 'g') continue; if (type == 'view' || type == 'w' || type == 'g' || type == 's') continue;
var properties = component[2]; var properties = component[2];
var name = properties['name']; var name = properties['name'];
...@@ -121,27 +155,127 @@ cktsim = (function() { ...@@ -121,27 +155,127 @@ cktsim = (function() {
} }
} }
// if converges: updates this.solution, this.soln_max, returns iter count
// otherwise: return undefined and set this.problem_node
// The argument should be a function that sets up the linear system.
Circuit.prototype.find_solution = function(load) {
var soln = this.solution;
var old_soln,temp,converged;
// iteratively solve until values convere or iteration limit exceeded
for (var iter = 0; iter < max_iterations; i++) {
// set up equations
this.initialize_linear_system();
load(this);
// solve for node voltages and branch currents
old_soln = soln;
soln = solve_linear_system(this.matrix);
// check convergence: abs(new-old) <= abstol + reltol*max;
converged = true;
for (var i = this.N - 1; i >= 0; --i) {
temp = Math.abs(soln[i] - old_soln);
if (temp > this.abstol[i] + this.rtol[i]) {
converged = false;
this.problem_node = i;
break;
}
}
if (!converged) continue;
// other convergence checks here?
// update solution and maximum
this.solution = soln;
for (var i = this.N - 1; i >= 0; --i) {
temp = Math.abs(soln[i]);
if (temp > this.soln_max[i]) {
this.soln_max[i] = temp;
this.rtol[i] = temp * reltol;
}
}
return iter+1;
}
// too many iterations
return undefined;
}
// DC analysis // DC analysis
Circuit.prototype.dc = function() { Circuit.prototype.dc = function() {
this.finalize(); this.finalize();
// set up equations // this function calls load_dc for all devices
this.initialize_linear_system(); function load_dc(ckt) {
for (var i = this.devices.length - 1; i >= 0; --i) for (var i = ckt.devices.length - 1; i >= 0; --i)
this.devices[i].load_dc(this); ckt.devices[i].load_dc(ckt);
}
// find the operating point
var iterations = this.find_solution(load_dc);
// solve for operating point if (typeof iterations == 'undefined')
var x = solve_linear_system(this.matrix); return 'Node '+this.node_map[this.problem_node]+' did not converge';
// create solution dictionary // create solution dictionary
var result = new Array(); var result = new Array();
for (var name in this.node_map) { for (var name in this.node_map) {
var index = this.node_map[name]; var index = this.node_map[name];
result[name] = (index == -1) ? 0 : x[index]; result[name] = (index == -1) ? 0 : this.solution[index];
} }
return result; return result;
} }
// AC analysis: npts/decade for freqs in range [fstart,fstop]
// result['frequencies'] = vector of log10(sample freqs)
// result['xxx'] = vector of dB(response for node xxx)
Circuit.prototype.ac = function(npts,fstart,fstop) {
this.finalize();
// this function calls load_ac for all devices
function load_ac(ckt) {
for (var i = ckt.devices.length - 1; i >= 0; --i)
ckt.devices[i].load_ac(ckt);
}
// build array to hold list of results for each node
// last entry is for frequency values
var response = new Array(this.N + 1);
for (var i = this.N; i >= 0; --i) response[i] = new Array();
// multiplicative frequency increase between freq points
var delta_f = Math.exp(Math.LN10/npts);
var f = fstart;
fstop *= 1.0001; // capture that last time point!
while (f <= fstop) {
this.omega = 2 * Math.PI * f;
// find the operating point
var iterations = this.find_solution(load_ac);
if (typeof iterations == 'undefined')
return 'Node '+this.node_map[this.problem_node]+' did not converge';
else {
response[this.N].push(f);
for (var i = this.N - 1; i >= 0; --i)
response[i].push(this.solution[i]);
}
f *= delta_f; // increment frequency
}
// create solution dictionary
var result = new Array();
for (var name in this.node_map) {
var index = this.node_map[name];
result[name] = (index == -1) ? 0 : response[index];
}
result['frequencies'] = response[this.N];
return result;
}
Circuit.prototype.r = function(n1,n2,v,name) { Circuit.prototype.r = function(n1,n2,v,name) {
// try to convert string value into numeric value, barf if we can't // try to convert string value into numeric value, barf if we can't
if ((typeof v) == 'string') { if ((typeof v) == 'string') {
...@@ -488,6 +622,8 @@ cktsim = (function() { ...@@ -488,6 +622,8 @@ cktsim = (function() {
return result; return result;
} }
Circuit.prototype.parse_number = parse_number; // make it easy to call from outside
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// Sources // Sources
...@@ -650,19 +786,23 @@ cktsim = (function() { ...@@ -650,19 +786,23 @@ cktsim = (function() {
VSource.prototype.construction = VSource; VSource.prototype.construction = VSource;
// load linear system equations for dc analysis // load linear system equations for dc analysis
VSource.prototype.load_dc = function(ckt,soln) { VSource.prototype.load_dc = function(ckt) {
// MNA stamp for independent voltage source // MNA stamp for independent voltage source
ckt.add_to_A(this.branch,this.npos,1.0); ckt.add_to_A(this.branch,this.npos,1.0);
ckt.add_to_A(this.branch,this.nneg,-1.0); ckt.add_to_A(this.branch,this.nneg,-1.0);
ckt.add_to_A(this.npos,this.branch,1.0); ckt.add_to_A(this.npos,this.branch,1.0);
ckt.add_to_A(this.nneg,this.branch,-1.0); ckt.add_to_A(this.nneg,this.branch,-1.0);
ckt.add_to_b(this.branch,this.src.value(ckt.time)); ckt.add_to_b(this.branch,this.src.dc);
} }
// load linear system equations for tran analysis (just like DC) // load linear system equations for tran analysis (just like DC)
VSource.prototype.load_tran = function(ckt,soln) { VSource.prototype.load_tran = function(ckt,soln) {
this.load_dc(ckt); // MNA stamp for independent voltage source
ckt.add_to_A(this.branch,this.npos,1.0);
ckt.add_to_A(this.branch,this.nneg,-1.0);
ckt.add_to_A(this.npos,this.branch,1.0);
ckt.add_to_A(this.nneg,this.branch,-1.0);
ckt.add_to_b(this.branch,this.src.value(ckt.time));
} }
// return time of next breakpoint for the device // return time of next breakpoint for the device
...@@ -671,12 +811,8 @@ cktsim = (function() { ...@@ -671,12 +811,8 @@ cktsim = (function() {
} }
// small signal model: short circuit // small signal model: short circuit
VSource.prototype.load_ac = function() { VSource.prototype.load_ac = function(ckt) {
// use branch row in matrix to set following constraint on system: this.load_dc(ckt);
// v_pos - v_neg = 0V
ckt.add_to_A(this.branch,this.npos,1.0);
ckt.add_to_A(this.branch,this.nneg,-1.0);
// ckt.add_to_b(this.branch,0); // adding 0 isn't necessary!
} }
function ISource(npos,nneg,v) { function ISource(npos,nneg,v) {
...@@ -691,7 +827,7 @@ cktsim = (function() { ...@@ -691,7 +827,7 @@ cktsim = (function() {
// load linear system equations for dc analysis // load linear system equations for dc analysis
ISource.prototype.load_dc = function(ckt) { ISource.prototype.load_dc = function(ckt) {
var i = this.src.value(ckt.time); var i = this.src.dc;
// MNA stamp for independent current source // MNA stamp for independent current source
ckt.add_to_b(this.npos,-i); // current flow into npos ckt.add_to_b(this.npos,-i); // current flow into npos
...@@ -700,7 +836,11 @@ cktsim = (function() { ...@@ -700,7 +836,11 @@ cktsim = (function() {
// load linear system equations for tran analysis (just like DC) // load linear system equations for tran analysis (just like DC)
ISource.prototype.load_tran = function(ckt,soln) { ISource.prototype.load_tran = function(ckt,soln) {
this.load_dc(ckt); var i = this.src.value(ckt.time);
// MNA stamp for independent current source
ckt.add_to_b(this.npos,-i); // current flow into npos
ckt.add_to_b(this.nneg,i); // and out of nneg
} }
// return time of next breakpoint for the device // return time of next breakpoint for the device
...@@ -709,7 +849,8 @@ cktsim = (function() { ...@@ -709,7 +849,8 @@ cktsim = (function() {
} }
// small signal model: open circuit // small signal model: open circuit
ISource.prototype.load_ac = function() { ISource.prototype.load_ac = function(ckt) {
this.load_dc(ckt);
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
......
...@@ -4,11 +4,29 @@ ...@@ -4,11 +4,29 @@
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Chris Terman, Nov. 2011 // Copyright (C) 2011 Massachusetts Institute of Technology
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// add schematics to a document with // add schematics to a document with
// //
// <input type="hidden" class="schematic" name="unique_form_id" value="...schematic/netlist info..." .../> // <input type="hidden" class="schematic" name="unique_form_id" value="JSON netlist..." .../>
// //
// other attributes you can add to the input tag: // other attributes you can add to the input tag:
// width -- width in pixels of diagram // width -- width in pixels of diagram
...@@ -27,14 +45,8 @@ ...@@ -27,14 +45,8 @@
// need a netlist? just use the part's type, properites and connections // need a netlist? just use the part's type, properites and connections
// TO DO: // TO DO:
// - draggable overlay window base class (dialogs, scope, ...)
// - wire labels? // - wire labels?
// - devices: diode, nfet, pfet, opamp, scope probe
// - icons for test equipment? (scope, sig gen, counter, ...)
// - zoom/scroll canvas // - zoom/scroll canvas
// - freeze_diagram, freeze_properties attributes (freeze certain components/properties?)
// - rotate multiple objects around their center of mass // - rotate multiple objects around their center of mass
// - rubber band wires when moving components // - rubber band wires when moving components
...@@ -100,11 +112,7 @@ schematic = (function() { ...@@ -100,11 +112,7 @@ schematic = (function() {
// setup a schematic by populating the <div> with the appropriate children // setup a schematic by populating the <div> with the appropriate children
function Schematic(input) { function Schematic(input) {
this.div = document.createElement('div'); // set up diagram viewing parameters
// set up div so we can position elements inside of it
this.div.style.position = 'relative';
this.div.style.cursor = 'default';
this.grid = 8; this.grid = 8;
this.scale = 2; this.scale = 2;
this.origin_x = input.getAttribute("origin_x"); this.origin_x = input.getAttribute("origin_x");
...@@ -125,6 +133,15 @@ schematic = (function() { ...@@ -125,6 +133,15 @@ schematic = (function() {
parts = []; parts = [];
} else parts = parts.split(','); } else parts = parts.split(',');
// now add the parts to the parts bin
this.parts_bin = [];
for (var i = 0; i < parts.length; i++) {
var part = new Part(this);
var pm = parts_map[parts[i]];
part.set_component(new pm[0](0,0,0),pm[1]);
this.parts_bin.push(part);
}
// use user-supplied list of analyses, otherwise provide them all // use user-supplied list of analyses, otherwise provide them all
// analyses="" means no analyses // analyses="" means no analyses
var analyses = input.getAttribute('analyses'); var analyses = input.getAttribute('analyses');
...@@ -135,46 +152,6 @@ schematic = (function() { ...@@ -135,46 +152,6 @@ schematic = (function() {
if (parts.length == 0 && analyses.length == 0) this.diagram_only = true; if (parts.length == 0 && analyses.length == 0) this.diagram_only = true;
else this.diagram_only = false; else this.diagram_only = false;
if (!this.diagram_only) {
// start with a background element with normal positioning
this.background = document.createElement('canvas');
this.background.style.backgroundColor = background_style;
this.background.style.borderStyle = 'solid';
this.background.style.borderWidth = '2px';
this.status_div = document.createElement('div');
this.status_div.style.position = 'absolute';
this.status_div.style.padding = '2px';
this.status = document.createTextNode('');
this.status_div.appendChild(this.status);
}
this.connection_points = new Array(); // location string => list of cp's
this.components = [];
// this is where schematic is rendered
this.canvas = document.createElement('canvas');
if (!this.diagram_only) {
this.canvas.tabIndex = 1; // so we get keystrokes
this.canvas.style.borderStyle = 'solid';
this.canvas.style.borderWidth = '1px';
this.canvas.style.borderColor = grid_style;
this.canvas.style.position = 'absolute';
this.canvas.style.outline = 'none';
}
this.canvas.schematic = this;
if (this.edits_allowed) {
this.canvas.addEventListener('mousemove',schematic_mouse_move,false);
this.canvas.addEventListener('mouseover',schematic_mouse_enter,false);
this.canvas.addEventListener('mouseout',schematic_mouse_leave,false);
this.canvas.addEventListener('mousedown',schematic_mouse_down,false);
this.canvas.addEventListener('mouseup',schematic_mouse_up,false);
this.canvas.addEventListener('dblclick',schematic_double_click,false);
this.canvas.addEventListener('keydown',schematic_key_down,false);
this.canvas.addEventListener('keyup',schematic_key_up,false);
}
// toolbar // toolbar
this.tools = new Array(); this.tools = new Array();
this.toolbar = []; this.toolbar = [];
...@@ -206,6 +183,52 @@ schematic = (function() { ...@@ -206,6 +183,52 @@ schematic = (function() {
} }
} }
// set up diagram canvas
this.canvas = document.createElement('canvas');
this.width = input.getAttribute('width');
this.width = parseInt(this.width == undefined ? '400' : this.width);
this.canvas.width = this.width;
this.height = input.getAttribute('height');
this.height = parseInt(this.height == undefined ? '300' : this.height);
this.canvas.height = this.height;
// repaint simply draws this buffer and then adds selected elements on top
this.bg_image = document.createElement('canvas');
this.bg_image.width = this.width;
this.bg_image.height = this.height;
if (!this.diagram_only) {
this.canvas.tabIndex = 1; // so we get keystrokes
this.canvas.style.borderStyle = 'solid';
this.canvas.style.borderWidth = '1px';
this.canvas.style.borderColor = grid_style;
//this.canvas.style.position = 'absolute';
this.canvas.style.outline = 'none';
}
this.canvas.schematic = this;
if (this.edits_allowed) {
this.canvas.addEventListener('mousemove',schematic_mouse_move,false);
this.canvas.addEventListener('mouseover',schematic_mouse_enter,false);
this.canvas.addEventListener('mouseout',schematic_mouse_leave,false);
this.canvas.addEventListener('mousedown',schematic_mouse_down,false);
this.canvas.addEventListener('mouseup',schematic_mouse_up,false);
this.canvas.addEventListener('dblclick',schematic_double_click,false);
this.canvas.addEventListener('keydown',schematic_key_down,false);
this.canvas.addEventListener('keyup',schematic_key_up,false);
}
// set up message area
if (!this.diagram_only) {
this.status_div = document.createElement('div');
this.status = document.createTextNode('');
this.status_div.appendChild(this.status);
this.status_div.style.height = status_height + 'px';
} else this.status_div = undefined;
this.connection_points = new Array(); // location string => list of cp's
this.components = [];
this.dragging = false; this.dragging = false;
this.drawCursor = false; this.drawCursor = false;
this.cursor_x = 0; this.cursor_x = 0;
...@@ -222,148 +245,78 @@ schematic = (function() { ...@@ -222,148 +245,78 @@ schematic = (function() {
this.altKey = false; this.altKey = false;
this.cmdKey = false; this.cmdKey = false;
// repaint simply draws this buffer and then adds selected elements on top // make sure other code can find us!
this.bg_image = document.createElement('canvas'); input.schematic = this;
this.input = input;
// now add the parts to the parts bin // set up DOM -- use nested tables to do the layout
var parts_left = this.width + 3 + background_margin; var table,tr,td;
var parts_top = background_margin; table = document.createElement('table');
this.parts_bin = []; if (!this.diagram_only) {
for (var i = 0; i < parts.length; i++) { table.style.borderStyle = 'solid';
var part = new Part(this); table.style.borderWidth = '2px';
var pm = parts_map[parts[i]]; table.style.padding = '5px';
part.set_component(new pm[0](0,0,0),pm[1]); table.style.backgroundColor = background_style;
this.parts_bin.push(part);
} }
// add all elements to the DOM // add tools to DOM
if (!this.diagram_only) { if (this.toolbar.length > 0) {
this.div.appendChild(this.background); tr = document.createElement('tr');
for (var i = 0; i < this.toolbar.length; i++) { table.appendChild(tr);
td = document.createElement('td');
td.colspan = 2;
td.vAlign = 'baseline';
tr.appendChild(td);
for (var i = 0; i < this.toolbar.length; ++i) {
var tool = this.toolbar[i]; var tool = this.toolbar[i];
if (tool != null) this.div.appendChild(tool); if (tool != null) td.appendChild(tool);
}
}
// add canvas and parts bin to DOM
tr = document.createElement('tr');
tr.vAlign = 'top';
table.appendChild(tr);
td = document.createElement('td');
tr.appendChild(td);
td.appendChild(this.canvas);
td = document.createElement('td');
tr.appendChild(td);
var parts_table = document.createElement('table');
td.appendChild(parts_table);
// fill in parts_table here!!!
var parts_per_column = Math.floor(this.height / part_h);
for (var i = 0; i < parts_per_column; ++i) {
tr = document.createElement('tr');
parts_table.appendChild(tr);
for (var j = i; j < this.parts_bin.length; j += parts_per_column) {
td = document.createElement('td');
tr.appendChild(td);
td.appendChild(this.parts_bin[j].canvas);
} }
this.div.appendChild(this.status_div);
for (var i = 0; i < this.parts_bin.length; i++)
this.div.appendChild(this.parts_bin[i].canvas);
} }
this.div.appendChild(this.canvas);
input.parentNode.insertBefore(this.div,input.nextSibling);
// make sure other code can find us! if (this.status_div != undefined) {
input.schematic = this; tr = document.createElement('tr');
this.input = input; table.appendChild(tr);
td = document.createElement('td');
tr.appendChild(td);
td.colspan = 2;
td.appendChild(this.status_div);
}
// set locations of all the elements in the editor // add to dom
var w = parseInt(input.getAttribute('width')); this.input.parentNode.insertBefore(table,this.input.nextSibling);
var h = parseInt(input.getAttribute('height'));
this.set_locations(w,h);
// process initial contents of diagram // process initial contents of diagram
this.load_schematic(this.input.value); this.load_schematic(this.input.value);
} }
background_margin = 5;
part_w = 42; // size of a parts bin compartment part_w = 42; // size of a parts bin compartment
part_h = 42; part_h = 42;
status_height = 18; status_height = 18;
// w,h are the dimensions of the canvas, everyone else is positioned accordingly
Schematic.prototype.set_locations = function(w,h) {
// limit the shrinkage factor
w = Math.max(w,120);
h = Math.max(h,120);
this.width = w;
this.height = h;
this.bg_image.width = w;
this.bg_image.height = h;
if (this.diagram_only) {
this.canvas.width = w;
this.canvas.height = h;
this.redraw_background(); // redraw diagram
return;
}
this.min_x = 0;
this.min_y = 0;
this.max_x = w/this.scale;
this.max_y = h/this.scale;
var left,top;
// start with tool bar
left = 2*background_margin; // space to the left
top = background_margin;
var max_height = 0;
if (this.toolbar.length > 0) {
tool_left = left;
for (var i = 0; i < this.toolbar.length; i++) {
var tool = this.toolbar[i];
if (tool == null) { // spacer
tool_left += 8;
continue;
}
tool.style.left = tool_left + 'px';
tool.style.top = top + 'px';
tool_left += tool.offsetWidth + 2; // width + padding + border + gap
max_height = Math.max(max_height,tool.offsetHeight);
}
top += max_height + 5; // height + padding + border + gap;
}
// configure canvas
this.canvas.style.left = left + 'px';
this.canvas.style.top = top + 'px';
this.canvas.width = w;
this.canvas.height = h;
this.redraw_background(); // redraw diagram
// configure status bar
this.status_div.style.left = left + 'px';
this.status_div.style.top = this.canvas.offsetTop + this.canvas.offsetHeight + 3 + 'px';
this.status_div.style.width = (w - 4) + 'px'; // subtract interior padding
this.status_div.style.height = status_height + 'px';
// configure parts bin
var total_w = this.canvas.offsetLeft + this.canvas.offsetWidth;
var parts_left = total_w + 5;
var parts_top = top;
var parts_h_limit = this.canvas.offsetTop + this.canvas.offsetHeight;
for (var i = 0; i < this.parts_bin.length; i++) {
var part = this.parts_bin[i];
part.set_location(parts_left,parts_top);
total_w = part.right();
parts_top = part.bottom() + 2;
if (parts_top + part_h > parts_h_limit) {
parts_left = total_w + 2;
parts_top = top;
}
}
// configure background
var total_h = this.status_div.offsetTop + this.status_div.offsetHeight + background_margin;
total_w += background_margin;
this.background.height = total_h;
this.background.width = total_w;
/* enable when there's support for resizing schematic
// redraw thumb
var c = this.background.getContext('2d');
c.clearRect(0,0,w,h);
c.strokeStyle = thumb_style;
c.lineWidth = 1;
c.beginPath();
w = total_w - 1;
h = total_h - 1;
c.moveTo(w,h-4); c.lineTo(w-4,h);
c.moveTo(w,h-8); c.lineTo(w-8,h);
c.moveTo(w,h-12); c.lineTo(w-12,h);
c.stroke();
*/
}
Schematic.prototype.add_component = function(new_c) { Schematic.prototype.add_component = function(new_c) {
this.components.push(new_c); this.components.push(new_c);
...@@ -570,10 +523,10 @@ schematic = (function() { ...@@ -570,10 +523,10 @@ schematic = (function() {
part.add(this) part.add(this)
} }
} }
// see what we've got!
this.redraw_background();
} }
// see what we've got!
this.redraw_background();
} }
// label all the nodes in the circuit // label all the nodes in the circuit
...@@ -764,8 +717,6 @@ schematic = (function() { ...@@ -764,8 +717,6 @@ schematic = (function() {
// Also redraws dynamic portion. // Also redraws dynamic portion.
Schematic.prototype.redraw_background = function() { Schematic.prototype.redraw_background = function() {
var c = this.bg_image.getContext('2d'); var c = this.bg_image.getContext('2d');
var w = this.bg_image.width;
var h = this.bg_imageheight;
c.lineCap = 'round'; c.lineCap = 'round';
...@@ -776,10 +727,10 @@ schematic = (function() { ...@@ -776,10 +727,10 @@ schematic = (function() {
// grid // grid
c.strokeStyle = grid_style; c.strokeStyle = grid_style;
var first_x = this.min_x; var first_x = 0;
var last_x = this.max_x; var last_x = this.width/this.scale;
var first_y = this.min_y; var first_y = 0;
var last_y = this.max_y; var last_y = this.height/this.scale;
for (var i = first_x; i < last_x; i += this.grid) for (var i = first_x; i < last_x; i += this.grid)
this.draw_line(c,i,first_y,i,last_y,0.1); this.draw_line(c,i,first_y,i,last_y,0.1);
for (var i = first_y; i < last_y; i += this.grid) for (var i = first_y; i < last_y; i += this.grid)
...@@ -903,22 +854,27 @@ schematic = (function() { ...@@ -903,22 +854,27 @@ schematic = (function() {
c.fillText(text,(x - this.origin_x) * this.scale,(y - this.origin_y) * this.scale); c.fillText(text,(x - this.origin_x) * this.scale,(y - this.origin_y) * this.scale);
} }
HTMLCanvasElement.prototype.totalOffset = function(){
}
// add method to canvas to compute relative coords for event // add method to canvas to compute relative coords for event
HTMLCanvasElement.prototype.relMouseCoords = function(event){ HTMLCanvasElement.prototype.relMouseCoords = function(event){
// run up the DOM tree to figure out coords for top,left of canvas // run up the DOM tree to figure out coords for top,left of canvas
var totalOffsetX = 0; var totalOffsetX = 0;
var totalOffsetY = 0; var totalOffsetY = 0;
var canvasY = 0;
var currentElement = this; var currentElement = this;
do { do {
totalOffsetX += currentElement.offsetLeft; totalOffsetX += currentElement.offsetLeft;
totalOffsetY += currentElement.offsetTop; totalOffsetY += currentElement.offsetTop;
} }
while(currentElement = currentElement.offsetParent); while (currentElement = currentElement.offsetParent);
// now compute relative position of click within the canvas // now compute relative position of click within the canvas
this.mouse_x = event.pageX - totalOffsetX; this.mouse_x = event.pageX - totalOffsetX;
this.mouse_y = event.pageY - totalOffsetY; this.mouse_y = event.pageY - totalOffsetY;
this.page_x = event.pageX;
this.page_y = event.pageY;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
...@@ -1365,8 +1321,8 @@ schematic = (function() { ...@@ -1365,8 +1321,8 @@ schematic = (function() {
content.win = win; // so content can contact us content.win = win; // so content can contact us
// compute location in top-level div // compute location in top-level div
win.left = this.canvas.mouse_x + this.canvas.offsetLeft; win.left = this.canvas.page_x;
win.top = this.canvas.mouse_y + this.canvas.offsetTop; win.top = this.canvas.page_y;
// add to DOM // add to DOM
win.style.background = 'white'; win.style.background = 'white';
...@@ -1375,7 +1331,8 @@ schematic = (function() { ...@@ -1375,7 +1331,8 @@ schematic = (function() {
win.style.left = win.left + 'px'; win.style.left = win.left + 'px';
win.style.top = win.top + 'px'; win.style.top = win.top + 'px';
win.style.border = '2px solid'; win.style.border = '2px solid';
this.div.appendChild(win);
this.input.parentNode.insertBefore(win,this.input.nextSibling);
} }
// close the window // close the window
...@@ -1458,7 +1415,7 @@ schematic = (function() { ...@@ -1458,7 +1415,7 @@ schematic = (function() {
tool.style.borderWidth = '1px'; tool.style.borderWidth = '1px';
tool.style.borderStyle = 'solid'; tool.style.borderStyle = 'solid';
tool.style.borderColor = background_style; tool.style.borderColor = background_style;
tool.style.position = 'absolute'; //tool.style.position = 'absolute';
tool.style.padding = '2px'; tool.style.padding = '2px';
// set up event processing // set up event processing
...@@ -1783,7 +1740,7 @@ schematic = (function() { ...@@ -1783,7 +1740,7 @@ schematic = (function() {
this.canvas.style.borderStyle = 'solid'; this.canvas.style.borderStyle = 'solid';
this.canvas.style.borderWidth = '1px'; this.canvas.style.borderWidth = '1px';
this.canvas.style.borderColor = background_style; this.canvas.style.borderColor = background_style;
this.canvas.style.position = 'absolute'; //this.canvas.style.position = 'absolute';
this.canvas.style.cursor = 'default'; this.canvas.style.cursor = 'default';
this.canvas.height = part_w; this.canvas.height = part_w;
this.canvas.width = part_h; this.canvas.width = part_h;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment