Schematic with center tag from cjt
This commit is contained in:
211
js/cktsim.js
211
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);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
353
js/schematic.js
353
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
|
||||
//
|
||||
// <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:
|
||||
// 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 <div> 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;
|
||||
|
||||
Reference in New Issue
Block a user