diff --git a/js/cktsim.js b/js/cktsim.js
index e61beee0f0..ca289a6b8e 100644
--- a/js/cktsim.js
+++ b/js/cktsim.js
@@ -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()"
// 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() {
@@ -23,6 +41,15 @@ cktsim = (function() {
T_VOLTAGE = 0;
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() {
this.node_map = new Array();
this.ntypes = [];
@@ -34,11 +61,6 @@ cktsim = (function() {
this.finalized = false;
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
@@ -67,10 +89,22 @@ cktsim = (function() {
// set up augmented matrix and various temp vectors
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.swap = new Array(this.N); // keep track of row swaps during pivoting
- this.soln = new Array(this.N); // hold swapped solution
+ this.soln_max[i] = 0.0;
+ 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() {
var component = netlist[i];
var type = component[0];
- // ignore wires, ground connections and view info
- if (type == 'view' || type == 'w' || type == 'g') continue;
+ // ignore wires, ground connections, scope probes and view info
+ if (type == 'view' || type == 'w' || type == 'g' || type == 's') continue;
var properties = component[2];
var name = properties['name'];
@@ -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
Circuit.prototype.dc = function() {
this.finalize();
- // set up equations
- this.initialize_linear_system();
- for (var i = this.devices.length - 1; i >= 0; --i)
- this.devices[i].load_dc(this);
+ // this function calls load_dc for all devices
+ function load_dc(ckt) {
+ for (var i = ckt.devices.length - 1; i >= 0; --i)
+ ckt.devices[i].load_dc(ckt);
+ }
- // solve for operating point
- var x = solve_linear_system(this.matrix);
+ // find the operating point
+ var iterations = this.find_solution(load_dc);
+
+ if (typeof iterations == 'undefined')
+ return 'Node '+this.node_map[this.problem_node]+' did not converge';
// 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 : x[index];
+ result[name] = (index == -1) ? 0 : this.solution[index];
}
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) {
// try to convert string value into numeric value, barf if we can't
if ((typeof v) == 'string') {
@@ -488,6 +622,8 @@ cktsim = (function() {
return result;
}
+ Circuit.prototype.parse_number = parse_number; // make it easy to call from outside
+
///////////////////////////////////////////////////////////////////////////////
//
// Sources
@@ -650,19 +786,23 @@ cktsim = (function() {
VSource.prototype.construction = VSource;
// 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
+ 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.dc);
+ }
+
+ // load linear system equations for tran analysis (just like DC)
+ VSource.prototype.load_tran = function(ckt,soln) {
// 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));
-
- }
-
- // load linear system equations for tran analysis (just like DC)
- VSource.prototype.load_tran = function(ckt,soln) {
- this.load_dc(ckt);
}
// return time of next breakpoint for the device
@@ -671,12 +811,8 @@ cktsim = (function() {
}
// small signal model: short circuit
- VSource.prototype.load_ac = function() {
- // use branch row in matrix to set following constraint on system:
- // 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!
+ VSource.prototype.load_ac = function(ckt) {
+ this.load_dc(ckt);
}
function ISource(npos,nneg,v) {
@@ -691,7 +827,7 @@ cktsim = (function() {
// load linear system equations for dc analysis
ISource.prototype.load_dc = function(ckt) {
- var i = this.src.value(ckt.time);
+ var i = this.src.dc;
// MNA stamp for independent current source
ckt.add_to_b(this.npos,-i); // current flow into npos
@@ -700,7 +836,11 @@ cktsim = (function() {
// load linear system equations for tran analysis (just like DC)
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
@@ -709,7 +849,8 @@ cktsim = (function() {
}
// small signal model: open circuit
- ISource.prototype.load_ac = function() {
+ ISource.prototype.load_ac = function(ckt) {
+ this.load_dc(ckt);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/js/schematic.js b/js/schematic.js
index 3d9b64135c..2febce050d 100644
--- a/js/schematic.js
+++ b/js/schematic.js
@@ -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
//
-//
+//
//
// other attributes you can add to the input tag:
// width -- width in pixels of diagram
@@ -27,14 +45,8 @@
// need a netlist? just use the part's type, properites and connections
// TO DO:
-
-// - draggable overlay window base class (dialogs, scope, ...)
// - wire labels?
-// - devices: diode, nfet, pfet, opamp, scope probe
-// - icons for test equipment? (scope, sig gen, counter, ...)
-
// - zoom/scroll canvas
-// - freeze_diagram, freeze_properties attributes (freeze certain components/properties?)
// - rotate multiple objects around their center of mass
// - rubber band wires when moving components
@@ -100,11 +112,7 @@ schematic = (function() {
// setup a schematic by populating the
with the appropriate children
function Schematic(input) {
- this.div = document.createElement('div');
- // set up div so we can position elements inside of it
- this.div.style.position = 'relative';
- this.div.style.cursor = 'default';
-
+ // set up diagram viewing parameters
this.grid = 8;
this.scale = 2;
this.origin_x = input.getAttribute("origin_x");
@@ -125,6 +133,15 @@ schematic = (function() {
parts = [];
} 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
// analyses="" means no analyses
var analyses = input.getAttribute('analyses');
@@ -135,46 +152,6 @@ schematic = (function() {
if (parts.length == 0 && analyses.length == 0) this.diagram_only = true;
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
this.tools = new Array();
this.toolbar = [];
@@ -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.drawCursor = false;
this.cursor_x = 0;
@@ -222,148 +245,78 @@ schematic = (function() {
this.altKey = false;
this.cmdKey = false;
- // repaint simply draws this buffer and then adds selected elements on top
- this.bg_image = document.createElement('canvas');
-
- // now add the parts to the parts bin
- var parts_left = this.width + 3 + background_margin;
- var parts_top = background_margin;
- 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);
- }
-
- // add all elements to the DOM
- if (!this.diagram_only) {
- this.div.appendChild(this.background);
- for (var i = 0; i < this.toolbar.length; i++) {
- var tool = this.toolbar[i];
- if (tool != null) this.div.appendChild(tool);
- }
- 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!
input.schematic = this;
this.input = input;
- // set locations of all the elements in the editor
- var w = parseInt(input.getAttribute('width'));
- var h = parseInt(input.getAttribute('height'));
- this.set_locations(w,h);
+ // set up DOM -- use nested tables to do the layout
+ var table,tr,td;
+ table = document.createElement('table');
+ if (!this.diagram_only) {
+ table.style.borderStyle = 'solid';
+ table.style.borderWidth = '2px';
+ table.style.padding = '5px';
+ table.style.backgroundColor = background_style;
+ }
+
+ // add tools to DOM
+ if (this.toolbar.length > 0) {
+ tr = document.createElement('tr');
+ 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];
+ 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);
+ }
+ }
+
+ if (this.status_div != undefined) {
+ tr = document.createElement('tr');
+ table.appendChild(tr);
+ td = document.createElement('td');
+ tr.appendChild(td);
+ td.colspan = 2;
+ td.appendChild(this.status_div);
+ }
+
+ // add to dom
+ this.input.parentNode.insertBefore(table,this.input.nextSibling);
// process initial contents of diagram
this.load_schematic(this.input.value);
}
- background_margin = 5;
part_w = 42; // size of a parts bin compartment
part_h = 42;
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) {
this.components.push(new_c);
@@ -570,10 +523,10 @@ schematic = (function() {
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
@@ -764,8 +717,6 @@ schematic = (function() {
// Also redraws dynamic portion.
Schematic.prototype.redraw_background = function() {
var c = this.bg_image.getContext('2d');
- var w = this.bg_image.width;
- var h = this.bg_imageheight;
c.lineCap = 'round';
@@ -776,10 +727,10 @@ schematic = (function() {
// grid
c.strokeStyle = grid_style;
- var first_x = this.min_x;
- var last_x = this.max_x;
- var first_y = this.min_y;
- var last_y = this.max_y;
+ var first_x = 0;
+ var last_x = this.width/this.scale;
+ var first_y = 0;
+ var last_y = this.height/this.scale;
for (var i = first_x; i < last_x; i += this.grid)
this.draw_line(c,i,first_y,i,last_y,0.1);
for (var i = first_y; i < last_y; i += this.grid)
@@ -903,22 +854,27 @@ schematic = (function() {
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
HTMLCanvasElement.prototype.relMouseCoords = function(event){
// run up the DOM tree to figure out coords for top,left of canvas
var totalOffsetX = 0;
var totalOffsetY = 0;
- var canvasY = 0;
var currentElement = this;
do {
totalOffsetX += currentElement.offsetLeft;
totalOffsetY += currentElement.offsetTop;
}
- while(currentElement = currentElement.offsetParent);
+ while (currentElement = currentElement.offsetParent);
// now compute relative position of click within the canvas
this.mouse_x = event.pageX - totalOffsetX;
this.mouse_y = event.pageY - totalOffsetY;
+
+ this.page_x = event.pageX;
+ this.page_y = event.pageY;
}
///////////////////////////////////////////////////////////////////////////////
@@ -1365,8 +1321,8 @@ schematic = (function() {
content.win = win; // so content can contact us
// compute location in top-level div
- win.left = this.canvas.mouse_x + this.canvas.offsetLeft;
- win.top = this.canvas.mouse_y + this.canvas.offsetTop;
+ win.left = this.canvas.page_x;
+ win.top = this.canvas.page_y;
// add to DOM
win.style.background = 'white';
@@ -1375,7 +1331,8 @@ schematic = (function() {
win.style.left = win.left + 'px';
win.style.top = win.top + 'px';
win.style.border = '2px solid';
- this.div.appendChild(win);
+
+ this.input.parentNode.insertBefore(win,this.input.nextSibling);
}
// close the window
@@ -1458,7 +1415,7 @@ schematic = (function() {
tool.style.borderWidth = '1px';
tool.style.borderStyle = 'solid';
tool.style.borderColor = background_style;
- tool.style.position = 'absolute';
+ //tool.style.position = 'absolute';
tool.style.padding = '2px';
// set up event processing
@@ -1783,7 +1740,7 @@ schematic = (function() {
this.canvas.style.borderStyle = 'solid';
this.canvas.style.borderWidth = '1px';
this.canvas.style.borderColor = background_style;
- this.canvas.style.position = 'absolute';
+ //this.canvas.style.position = 'absolute';
this.canvas.style.cursor = 'default';
this.canvas.height = part_w;
this.canvas.width = part_h;