Commit 20243182 by Piotr Mitros

cjt fixed schematic bug. Back in the system

parent ef942ac0
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
// properties := {name: value, ...} // properties := {name: value, ...}
// connections := [node, ...] // one per connection point in canoncial order // connections := [node, ...] // one per connection point in canoncial order
// node := string // node := string
// need a netlist? just use the part's type, properites and connections
// TO DO: // TO DO:
...@@ -36,15 +37,13 @@ ...@@ -36,15 +37,13 @@
// - 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
// - simulation: operating points, trans, ac analysis, sweeps?
// set up each schematic entry widget // set up each schematic entry widget
function update_schematics() { function update_schematics() {
// set up each schematic on the page // set up each schematic on the page
var schematics = document.getElementsByClassName('schematic'); var schematics = document.getElementsByClassName('schematic');
for (var i = schematics.length - 1; i >= 0; i--) for (var i = schematics.length - 1; i >= 0; i--)
if (schematics[i].getAttribute("loaded") != "true") { if (schematics[i].getAttribute("loaded") != "true") {
new Schematic(schematics[i]); new schematic.Schematic(schematics[i]);
schematics[i].setAttribute("loaded","true"); schematics[i].setAttribute("loaded","true");
} }
} }
...@@ -60,35 +59,45 @@ function add_schematic_handler(other_onload) { ...@@ -60,35 +59,45 @@ function add_schematic_handler(other_onload) {
} }
window.onload = add_schematic_handler(window.onload); window.onload = add_schematic_handler(window.onload);
background_style = 'rgb(220,220,220)';
element_style = 'rgb(255,255,255)';
thumb_style = 'rgb(128,128,128)';
normal_style = 'rgb(0,0,0)'; // color for unselected components
selected_style = 'rgb(64,255,64)'; // highlight color for selected components
grid_style = "rgb(128,128,128)";
// list of all the defined parts schematic = (function() {
parts_map = { background_style = 'rgb(220,220,220)';
element_style = 'rgb(255,255,255)';
thumb_style = 'rgb(128,128,128)';
normal_style = 'rgb(0,0,0)'; // color for unselected components
selected_style = 'rgb(64,255,64)'; // highlight color for selected components
grid_style = "rgb(128,128,128)";
annotation_style = 'rgb(255,64,64)'; // color for diagram annotations
property_size = 5; // point size for Component property text
annotation_size = 8; // point size for diagram annotations
// list of all the defined parts
parts_map = {
'g': [Ground, 'Ground connection'], 'g': [Ground, 'Ground connection'],
'v': [VSource, 'Voltage source'], 'v': [VSource, 'Voltage source'],
'i': [ISource, 'Current source'], 'i': [ISource, 'Current source'],
'r': [Resistor, 'Resistor'], 'r': [Resistor, 'Resistor'],
'c': [Capacitor, 'Capacitor'], 'c': [Capacitor, 'Capacitor'],
'l': [Inductor, 'Inductor'], 'l': [Inductor, 'Inductor'],
}; 'o': [OpAmp, 'Op Amp'],
'd': [Diode, 'Diode'],
// fix cursor bug in Chrome (default behavior: change to text cursor 'n': [NFet, 'NFet'],
// whenever a drag is initiated). 'p': [PFet, 'PFet'],
document.onselectstart = function() { return false; }; };
/////////////////////////////////////////////////////////////////////////////// // fix cursor bug in Chrome (default behavior: change to text cursor
// // whenever a drag is initiated).
// Schematic = diagram + parts bin + status area //document.onselectstart = function() { return false; };
//
//////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
//
// setup a schematic by populating the <div> with the appropriate children // Schematic = diagram + parts bin + status area
function Schematic(input) { //
////////////////////////////////////////////////////////////////////////////////
// setup a schematic by populating the <div> with the appropriate children
function Schematic(input) {
this.div = document.createElement('div'); this.div = document.createElement('div');
// set up div so we can position elements inside of it // set up div so we can position elements inside of it
this.div.style.position = 'relative'; this.div.style.position = 'relative';
...@@ -98,7 +107,7 @@ function Schematic(input) { ...@@ -98,7 +107,7 @@ function Schematic(input) {
this.scale = 2; this.scale = 2;
this.origin_x = 0; this.origin_x = 0;
this.origin_y = 0; this.origin_y = 0;
this.clipboard = null; this.clipboard = undefined;
// start with a background element with normal positioning // start with a background element with normal positioning
this.background = document.createElement('canvas'); this.background = document.createElement('canvas');
...@@ -135,7 +144,7 @@ function Schematic(input) { ...@@ -135,7 +144,7 @@ function Schematic(input) {
this.canvas.addEventListener('mouseup',schematic_mouse_up,false); this.canvas.addEventListener('mouseup',schematic_mouse_up,false);
this.canvas.addEventListener('dblclick',schematic_double_click,false); this.canvas.addEventListener('dblclick',schematic_double_click,false);
this.canvas.addEventListener('keydown',schematic_key_down,false); this.canvas.addEventListener('keydown',schematic_key_down,false);
this.canvas.addEventListener('keypress',schematic_key_press,false); this.canvas.addEventListener('keyup',schematic_key_up,false);
// toolbar // toolbar
this.tools = new Array(); this.tools = new Array();
...@@ -144,6 +153,18 @@ function Schematic(input) { ...@@ -144,6 +153,18 @@ function Schematic(input) {
this.tools['copy'] = this.add_tool(copy_icon,'Copy: copy selected components into the clipboard',this.copy); this.tools['copy'] = this.add_tool(copy_icon,'Copy: copy selected components into the clipboard',this.copy);
this.tools['paste'] = this.add_tool(paste_icon,'Paste: copy clipboard into the diagram',this.paste); this.tools['paste'] = this.add_tool(paste_icon,'Paste: copy clipboard into the diagram',this.paste);
// simulation interface if cktsim.js is loaded
if (typeof cktsim != 'undefined') {
this.toolbar.push(null); // spacer
this.tools['dc'] = this.add_tool('DC','DC Analysis',this.dc_analysis);
//this.tools['ac'] = this.add_tool('AC','AC Small-Signal Analysis',this.ac_analysis);
//this.tools['tran'] = this.add_tool('TRAN','Transient Analysis',this.transient_analysis);
this.enable_tool('dc',true);
//this.enable_tool('ac',true);
//this.enable_tool('tran',true);
}
// make the canvas "clickable" by registering a dummy click handler // make the canvas "clickable" by registering a dummy click handler
// this should make things work on the iPad // this should make things work on the iPad
this.canvas.addEventListener('click',function(){},false); this.canvas.addEventListener('click',function(){},false);
...@@ -152,9 +173,17 @@ function Schematic(input) { ...@@ -152,9 +173,17 @@ function Schematic(input) {
this.drawCursor = false; this.drawCursor = false;
this.cursor_x = 0; this.cursor_x = 0;
this.cursor_y = 0; this.cursor_y = 0;
this.draw_cursor = null; this.draw_cursor = undefined;
this.select_rect = null; this.select_rect = undefined;
this.wire = null; this.wire = undefined;
this.operating_point = undefined; // result from DC analysis
// state of modifier keys
this.ctrlKey = false;
this.shiftKey = false;
this.altKey = false;
this.cmdKey = false;
// repaint simply draws this buffer and then adds selected elements on top // repaint simply draws this buffer and then adds selected elements on top
this.bg_image = document.createElement('canvas'); this.bg_image = document.createElement('canvas');
...@@ -175,14 +204,16 @@ function Schematic(input) { ...@@ -175,14 +204,16 @@ function Schematic(input) {
for (var i = 0; i < parts.length; i++) { for (var i = 0; i < parts.length; i++) {
var part = new Part(this); var part = new Part(this);
var pm = parts_map[parts[i]]; var pm = parts_map[parts[i]];
part.set_component(new pm[0](part,0,0,0),pm[1]); part.set_component(new pm[0](0,0,0),pm[1]);
this.parts_bin.push(part); this.parts_bin.push(part);
} }
// add all elements to the DOM // add all elements to the DOM
this.div.appendChild(this.background); this.div.appendChild(this.background);
for (var i = 0; i < this.toolbar.length; i++) for (var i = 0; i < this.toolbar.length; i++) {
this.div.appendChild(this.toolbar[i]); var tool = this.toolbar[i];
if (tool != null) this.div.appendChild(tool);
}
this.div.appendChild(this.canvas); this.div.appendChild(this.canvas);
this.div.appendChild(this.status_div); this.div.appendChild(this.status_div);
for (var i = 0; i < this.parts_bin.length; i++) for (var i = 0; i < this.parts_bin.length; i++)
...@@ -200,56 +231,15 @@ function Schematic(input) { ...@@ -200,56 +231,15 @@ function Schematic(input) {
// process initial contents of diagram // process initial contents of diagram
this.load_schematic(this.input.value); this.load_schematic(this.input.value);
}
Schematic.prototype.load_schematic = function(value) {
if (value) {
// convert string value into data structure
var json = JSON.parse(value);
// top level is a list of components
for (var i = json.length - 1; i >= 0; --i) {
var c = json[i];
if (c[0] == 'view') {
// special hack: view component lets us recreate view
this.origin_x = c[1];
this.origin_y = c[2];
this.scale = c[3];
} else if (c[0] == 'w') {
// wire
this.add_wire(c[1][0],c[1][1],c[1][2],c[1][3]);
} else {
// ordinary component
// c := [type, coords, properties, connections]
var type = c[0];
var coords = c[1];
var properties = c[2];
// make the part
var part = new parts_map[type][0](this,coords[0],coords[1],coords[2]);
// give it its properties
for (var name in properties)
part.properties[name] = properties[name];
// add component to the diagram
this.add_component(part);
} }
}
// see what we've got!
this.redraw_background();
}
}
background_margin = 5; 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 // w,h are the dimensions of the canvas, everyone else is positioned accordingly
Schematic.prototype.set_locations = function(w,h) { Schematic.prototype.set_locations = function(w,h) {
// limit the shrinkage factor // limit the shrinkage factor
w = Math.max(w,120); w = Math.max(w,120);
h = Math.max(h,120); h = Math.max(h,120);
...@@ -268,15 +258,21 @@ Schematic.prototype.set_locations = function(w,h) { ...@@ -268,15 +258,21 @@ Schematic.prototype.set_locations = function(w,h) {
// start with tool bar // start with tool bar
var top = background_margin; var top = background_margin;
var max_height = 0;
if (this.toolbar.length > 0) { if (this.toolbar.length > 0) {
tool_left = left; tool_left = left;
for (var i = 0; i < this.toolbar.length; i++) { for (var i = 0; i < this.toolbar.length; i++) {
var img = this.toolbar[i]; var tool = this.toolbar[i];
img.style.left = tool_left + 'px'; if (tool == null) { // spacer
img.style.top = top + 'px'; tool_left += 8;
tool_left += 24; // width + 2*padding + 2*border + gap continue;
} }
top += 27; // height + 2*padding + 2*border + gap; 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 // configure canvas
...@@ -304,7 +300,7 @@ Schematic.prototype.set_locations = function(w,h) { ...@@ -304,7 +300,7 @@ Schematic.prototype.set_locations = function(w,h) {
total_w = part.right(); total_w = part.right();
parts_top = part.bottom() + 2; parts_top = part.bottom() + 2;
if (parts_top + part_h > parts_h_limit) { if (parts_top + part_h > parts_h_limit) {
parts_left = total_w - 1; parts_left = total_w + 2;
parts_top = top; parts_top = top;
} }
} }
...@@ -329,80 +325,21 @@ Schematic.prototype.set_locations = function(w,h) { ...@@ -329,80 +325,21 @@ Schematic.prototype.set_locations = function(w,h) {
c.moveTo(w,h-12); c.lineTo(w-12,h); c.moveTo(w,h-12); c.lineTo(w-12,h);
c.stroke(); c.stroke();
*/ */
} }
// label all the nodes in the circuit
Schematic.prototype.label_connection_points = function() {
// start by clearing all the connection point labels
for (var i = this.components.length - 1; i >=0; --i)
this.components[i].clear_labels();
// components are in charge of labeling their unlabeled connections.
// labels given to connection points will propagate to coincident connection
// points and across Wires.
// let special components like GND label their connection(s)
for (var i = this.components.length - 1; i >=0; --i)
this.components[i].add_default_labels();
// now have components generate labels for unlabeled connections
this.next_label = 0;
for (var i = this.components.length - 1; i >=0; --i)
this.components[i].label_connections();
}
// generate a new label
Schematic.prototype.get_next_label = function() {
// generate next label in sequence
this.next_label += 1;
return this.next_label.toString();
}
// propagate label to coincident connection points
Schematic.prototype.propagate_label = function(label,location) {
var cplist = this.connection_points[location];
for (var i = cplist.length - 1; i >= 0; --i)
cplist[i].propagate_label(label);
}
// update the value field of our corresponding input field with JSON
// representation of schematic
Schematic.prototype.update_value = function() {
// label connection points
this.label_connection_points();
// build JSON data structure, convert to string value for
// input field
this.input.value = JSON.stringify(this.json());
}
// produce a JSON representation of the diagram
Schematic.prototype.json = function() {
var json = [];
// output all the components/wires in the diagram
for (var i = this.components.length - 1; i >=0; --i)
json.push(this.components[i].json());
// capture the current view parameters
json.push(['view',this.origin_x,this.origin_y,this.scale]);
return json;
}
Schematic.prototype.add_component = function(new_c) { Schematic.prototype.add_component = function(new_c) {
this.components.push(new_c); this.components.push(new_c);
// create undoable edit record here // create undoable edit record here
} }
Schematic.prototype.remove_component = function(c) { Schematic.prototype.remove_component = function(c) {
var index = this.components.indexOf(c); var index = this.components.indexOf(c);
if (index != -1) this.components.splice(index,1); if (index != -1) this.components.splice(index,1);
} }
// add connection point to list of connection points at that location // add connection point to list of connection points at that location
Schematic.prototype.add_connection_point = function(cp) { Schematic.prototype.add_connection_point = function(cp) {
var cplist = this.connection_points[cp.location]; var cplist = this.connection_points[cp.location];
if (cplist) cplist.push(cp); if (cplist) cplist.push(cp);
else { else {
...@@ -412,10 +349,10 @@ Schematic.prototype.add_connection_point = function(cp) { ...@@ -412,10 +349,10 @@ Schematic.prototype.add_connection_point = function(cp) {
// return list of conincident connection points // return list of conincident connection points
return cplist; return cplist;
} }
// remove connection point from the list points at the old location // remove connection point from the list points at the old location
Schematic.prototype.remove_connection_point = function(cp,old_location) { Schematic.prototype.remove_connection_point = function(cp,old_location) {
// remove cp from list at old location // remove cp from list at old location
var cplist = this.connection_points[old_location]; var cplist = this.connection_points[old_location];
if (cplist) { if (cplist) {
...@@ -428,24 +365,24 @@ Schematic.prototype.remove_connection_point = function(cp,old_location) { ...@@ -428,24 +365,24 @@ Schematic.prototype.remove_connection_point = function(cp,old_location) {
delete this.connection_points[old_location]; delete this.connection_points[old_location];
} }
} }
} }
// connection point has changed location: remove, then add // connection point has changed location: remove, then add
Schematic.prototype.update_connection_point = function(cp,old_location) { Schematic.prototype.update_connection_point = function(cp,old_location) {
this.remove_connection_point(cp,old_location); this.remove_connection_point(cp,old_location);
return this.add_connection_point(cp); return this.add_connection_point(cp);
} }
// add a wire to the schematic // add a wire to the schematic
Schematic.prototype.add_wire = function(x1,y1,x2,y2) { Schematic.prototype.add_wire = function(x1,y1,x2,y2) {
var new_wire = new Wire(this,x1,y1,x2,y2); var new_wire = new Wire(x1,y1,x2,y2);
this.add_component(new_wire); new_wire.add(this);
new_wire.move_end(); new_wire.move_end();
return new_wire; return new_wire;
} }
// see if connection points of component c split any wires // see if connection points of component c split any wires
Schematic.prototype.check_wires = function(c) { Schematic.prototype.check_wires = function(c) {
for (var i = this.components.length - 1; i >=0; --i) { for (var i = this.components.length - 1; i >=0; --i) {
var cc = this.components[i]; var cc = this.components[i];
if (cc != c) { // don't check a component against itself if (cc != c) { // don't check a component against itself
...@@ -465,14 +402,16 @@ Schematic.prototype.check_wires = function(c) { ...@@ -465,14 +402,16 @@ Schematic.prototype.check_wires = function(c) {
} }
} }
} }
} }
Schematic.prototype.unselect_all = function(which) {
this.operating_point = undefined; // remove annotations
Schematic.prototype.unselect_all = function(which) {
for (var i = this.components.length - 1; i >= 0; --i) for (var i = this.components.length - 1; i >= 0; --i)
if (i != which) this.components[i].set_select(false); if (i != which) this.components[i].set_select(false);
} }
Schematic.prototype.drag_begin = function() { Schematic.prototype.drag_begin = function() {
// let components know they're about to move // let components know they're about to move
for (var i = this.components.length - 1; i >= 0; --i) { for (var i = this.components.length - 1; i >= 0; --i) {
var component = this.components[i]; var component = this.components[i];
...@@ -483,18 +422,18 @@ Schematic.prototype.drag_begin = function() { ...@@ -483,18 +422,18 @@ Schematic.prototype.drag_begin = function() {
this.drag_x = this.cursor_x; this.drag_x = this.cursor_x;
this.drag_y = this.cursor_y; this.drag_y = this.cursor_y;
this.dragging = true; this.dragging = true;
} }
Schematic.prototype.drag_end = function() { Schematic.prototype.drag_end = function() {
// let components know they're done moving // let components know they're done moving
for (var i = this.components.length - 1; i >= 0; --i) { for (var i = this.components.length - 1; i >= 0; --i) {
var component = this.components[i]; var component = this.components[i];
if (component.selected) component.move_end(); if (component.selected) component.move_end();
} }
this.dragging = false; this.dragging = false;
} }
Schematic.prototype.cut = function() { Schematic.prototype.cut = function() {
// clear previous contents // clear previous contents
this.clipboard = []; this.clipboard = [];
...@@ -509,9 +448,9 @@ Schematic.prototype.cut = function() { ...@@ -509,9 +448,9 @@ Schematic.prototype.cut = function() {
// update diagram view // update diagram view
this.redraw(); this.redraw();
} }
Schematic.prototype.copy = function() { Schematic.prototype.copy = function() {
// clear previous contents // clear previous contents
this.clipboard = []; this.clipboard = [];
...@@ -519,21 +458,23 @@ Schematic.prototype.copy = function() { ...@@ -519,21 +458,23 @@ Schematic.prototype.copy = function() {
for (var i = this.components.length - 1; i >=0; --i) { for (var i = this.components.length - 1; i >=0; --i) {
var c = this.components[i]; var c = this.components[i];
if (c.selected) if (c.selected)
this.clipboard.push(c.clone(this,c.x,c.y)); this.clipboard.push(c.clone(c.x,c.y));
}
} }
}
Schematic.prototype.paste = function() { Schematic.prototype.paste = function() {
// compute left,top of bounding box for origins of // compute left,top of bounding box for origins of
// components in the clipboard // components in the clipboard
var left = null; var left = undefined;
var top = null; var top = undefined;
for (var i = this.clipboard.length - 1; i >= 0; --i) { for (var i = this.clipboard.length - 1; i >= 0; --i) {
var c = this.clipboard[i]; var c = this.clipboard[i];
left = left ? Math.min(left,c.x) : left; left = left ? Math.min(left,c.x) : c.x;
top = top ? Math.min(top,c.y) : top; top = top ? Math.min(top,c.y) : c.y;
} }
this.message('cursor '+this.cursor_x+','+this.cursor_y);
// clear current selections // clear current selections
this.unselect_all(-1); this.unselect_all(-1);
this.redraw_background(); // so we see any components that got unselected this.redraw_background(); // so we see any components that got unselected
...@@ -542,24 +483,162 @@ Schematic.prototype.paste = function() { ...@@ -542,24 +483,162 @@ Schematic.prototype.paste = function() {
// them relative to the cursor // them relative to the cursor
for (var i = this.clipboard.length - 1; i >= 0; --i) { for (var i = this.clipboard.length - 1; i >= 0; --i) {
var c = this.clipboard[i]; var c = this.clipboard[i];
var new_c = c.clone(this,this.cursor_x + (c.x - left),this.cursor_y + (c.y - top)); var new_c = c.clone(this.cursor_x + (c.x - left),this.cursor_y + (c.y - top));
this.add_component(new_c);
new_c.set_select(true); new_c.set_select(true);
new_c.add(this);
} }
// see what we've wrought // see what we've wrought
this.redraw(); this.redraw();
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// Drawing support -- deals with scaling and scrolling of diagrama // Netlist and Simulation interface
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// here to redraw background image containing static portions of the schematic. // load diagram from JSON representation
// Also redraws dynamic portion. Schematic.prototype.load_schematic = function(value) {
Schematic.prototype.redraw_background = function() { if (value) {
// convert string value into data structure
var json = JSON.parse(value);
// top level is a list of components
for (var i = json.length - 1; i >= 0; --i) {
var c = json[i];
if (c[0] == 'view') {
// special hack: view component lets us recreate view
this.origin_x = c[1];
this.origin_y = c[2];
this.scale = c[3];
} else if (c[0] == 'w') {
// wire
this.add_wire(c[1][0],c[1][1],c[1][2],c[1][3]);
} else {
// ordinary component
// c := [type, coords, properties, connections]
var type = c[0];
var coords = c[1];
var properties = c[2];
// make the part
var part = new parts_map[type][0](coords[0],coords[1],coords[2]);
// give it its properties
for (var name in properties)
part.properties[name] = properties[name];
// add component to the diagram
part.add(this)
}
}
// see what we've got!
this.redraw_background();
}
}
// label all the nodes in the circuit
Schematic.prototype.label_connection_points = function() {
// start by clearing all the connection point labels
for (var i = this.components.length - 1; i >=0; --i)
this.components[i].clear_labels();
// components are in charge of labeling their unlabeled connections.
// labels given to connection points will propagate to coincident connection
// points and across Wires.
// let special components like GND label their connection(s)
for (var i = this.components.length - 1; i >=0; --i)
this.components[i].add_default_labels();
// now have components generate labels for unlabeled connections
this.next_label = 0;
for (var i = this.components.length - 1; i >=0; --i)
this.components[i].label_connections();
}
// generate a new label
Schematic.prototype.get_next_label = function() {
// generate next label in sequence
this.next_label += 1;
return this.next_label.toString();
}
// propagate label to coincident connection points
Schematic.prototype.propagate_label = function(label,location) {
var cplist = this.connection_points[location];
for (var i = cplist.length - 1; i >= 0; --i)
cplist[i].propagate_label(label);
}
// update the value field of our corresponding input field with JSON
// representation of schematic
Schematic.prototype.update_value = function() {
// label connection points
this.label_connection_points();
// build JSON data structure, convert to string value for
// input field
this.input.value = JSON.stringify(this.json());
}
// produce a JSON representation of the diagram
Schematic.prototype.json = function() {
var json = [];
// output all the components/wires in the diagram
for (var i = this.components.length - 1; i >=0; --i)
json.push(this.components[i].json());
// capture the current view parameters
json.push(['view',this.origin_x,this.origin_y,this.scale]);
return json;
}
///////////////////////////////////////////////////////////////////////////////
//
// Simulation interface
//
////////////////////////////////////////////////////////////////////////////////
Schematic.prototype.dc_analysis = function() {
// remove any previous annotations
this.operating_point = undefined;
this.redraw();
// give all the circuit nodes a name, extract netlist
this.label_connection_points();
var netlist = this.json();
// create a circuit from the netlist
var ckt = new cktsim.Circuit();
ckt.load_netlist(netlist);
// run the analysis
this.operating_point = ckt.dc();
// display results on diagram
this.redraw();
}
Schematic.prototype.ac_analysis = function() {
}
Schematic.prototype.transient_analysis = function() {
}
///////////////////////////////////////////////////////////////////////////////
//
// Drawing support -- deals with scaling and scrolling of diagrama
//
////////////////////////////////////////////////////////////////////////////////
// here to redraw background image containing static portions of the schematic.
// Also redraws dynamic portion.
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 w = this.bg_image.width;
var h = this.bg_imageheight; var h = this.bg_imageheight;
...@@ -590,10 +669,10 @@ Schematic.prototype.redraw_background = function() { ...@@ -590,10 +669,10 @@ Schematic.prototype.redraw_background = function() {
} }
this.redraw(); // background changed, redraw on screen this.redraw(); // background changed, redraw on screen
} }
// redraw what user sees = static image + dynamic parts // redraw what user sees = static image + dynamic parts
Schematic.prototype.redraw = function() { Schematic.prototype.redraw = function() {
var c = this.canvas.getContext('2d'); var c = this.canvas.getContext('2d');
// put static image in the background // put static image in the background
...@@ -639,6 +718,18 @@ Schematic.prototype.redraw = function() { ...@@ -639,6 +718,18 @@ Schematic.prototype.redraw = function() {
c.stroke(); c.stroke();
} }
// display operating point results
if (this.operating_point) {
// make a copy of the operating_point info so we can mess with it
var temp = new Array();
for (var i in this.operating_point) temp[i] = this.operating_point[i];
// run through connection points displaying (once) the voltage
// for each electrical node
for (var location in this.connection_points)
(this.connection_points[location])[0].display_voltage(c,temp);
}
// finally overlay cursor // finally overlay cursor
if (this.drawCursor && this.draw_cursor) { if (this.drawCursor && this.draw_cursor) {
//var x = this.cursor_x; //var x = this.cursor_x;
...@@ -646,38 +737,38 @@ Schematic.prototype.redraw = function() { ...@@ -646,38 +737,38 @@ Schematic.prototype.redraw = function() {
//this.draw_text(c,'('+x+','+y+')',x+this.grid,y-this.grid,10); //this.draw_text(c,'('+x+','+y+')',x+this.grid,y-this.grid,10);
this.draw_cursor(c,this.cursor_x,this.cursor_y); this.draw_cursor(c,this.cursor_x,this.cursor_y);
} }
} }
// draws a cross cursor // draws a cross cursor
Schematic.prototype.cross_cursor = function(c,x,y) { Schematic.prototype.cross_cursor = function(c,x,y) {
this.draw_line(c,x-this.grid,y,x+this.grid,y,1); this.draw_line(c,x-this.grid,y,x+this.grid,y,1);
this.draw_line(c,x,y-this.grid,x,y+this.grid,1); this.draw_line(c,x,y-this.grid,x,y+this.grid,1);
} }
Schematic.prototype.draw_line = function(c,x1,y1,x2,y2,width) { Schematic.prototype.draw_line = function(c,x1,y1,x2,y2,width) {
c.lineWidth = width*this.scale; c.lineWidth = width*this.scale;
c.beginPath(); c.beginPath();
c.moveTo((x1 - this.origin_x) * this.scale,(y1 - this.origin_y) * this.scale); c.moveTo((x1 - this.origin_x) * this.scale,(y1 - this.origin_y) * this.scale);
c.lineTo((x2 - this.origin_x) * this.scale,(y2 - this.origin_y) * this.scale); c.lineTo((x2 - this.origin_x) * this.scale,(y2 - this.origin_y) * this.scale);
c.stroke(); c.stroke();
} }
Schematic.prototype.draw_arc = function(c,x,y,radius,start_radians,end_radians,anticlockwise,width,filled) { Schematic.prototype.draw_arc = function(c,x,y,radius,start_radians,end_radians,anticlockwise,width,filled) {
c.lineWidth = width*this.scale; c.lineWidth = width*this.scale;
c.beginPath(); c.beginPath();
c.arc((x - this.origin_x)*this.scale,(y - this.origin_y)*this.scale,radius*this.scale, c.arc((x - this.origin_x)*this.scale,(y - this.origin_y)*this.scale,radius*this.scale,
start_radians,end_radians,anticlockwise); start_radians,end_radians,anticlockwise);
if (filled) c.fill(); if (filled) c.fill();
else c.stroke(); else c.stroke();
} }
Schematic.prototype.draw_text = function(c,text,x,y,size) { Schematic.prototype.draw_text = function(c,text,x,y,size) {
c.font = size*this.scale+'pt sans-serif' c.font = size*this.scale+'pt sans-serif'
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);
} }
// 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;
...@@ -692,95 +783,108 @@ HTMLCanvasElement.prototype.relMouseCoords = function(event){ ...@@ -692,95 +783,108 @@ HTMLCanvasElement.prototype.relMouseCoords = function(event){
// 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;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// Event handling // Event handling
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// process special keys here since they don't get delivered correctly on keypress // process keystrokes, consuming those that are meaningful to us
function schematic_key_down(event) { function schematic_key_down(event) {
if (!event) event = window.event; if (!event) event = window.event;
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic; var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
var code = event.keyCode; var code = event.keyCode;
if (code == 8 || code == 46) { // keep track of modifier key state
if (code == 16) sch.shiftKey = true;
else if (code == 17) sch.ctrlKey = true;
else if (code == 18) sch.altKey = true;
else if (code == 91) sch.cmdKey = true;
// backspace or delete: delete selected components
else if (code == 8 || code == 46) {
// delete selected components // delete selected components
for (var i = sch.components.length - 1; i >= 0; --i) { for (var i = sch.components.length - 1; i >= 0; --i) {
var component = sch.components[i]; var component = sch.components[i];
if (component.selected) component.delete(1); if (component.selected) component.delete(1);
} }
sch.redraw(); }
// cmd/ctrl x: cut
else if ((sch.ctrlKey || sch.cmdKey) && code == 88) {
sch.cut();
event.preventDefault(); event.preventDefault();
return false; return false;
} }
return true;
}
// process normal characters
function schematic_key_press(event) {
if (!event) event = window.event;
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
var code = window.event ? event.keyCode : event.charCode;
var char = String.fromCharCode(code);
// rotate // cmd/ctrl c: copy
if (!event.control && !event.altKey && (char == 'r' || char == 'R')) { else if ((sch.ctrlKey || sch.cmdKey) && code == 67) {
// rotate sch.copy();
for (var i = sch.components.length - 1; i >= 0; --i) {
var component = sch.components[i];
if (component.selected) component.rotate(1);
}
sch.redraw();
event.preventDefault(); event.preventDefault();
return false; return false;
} }
// cut // cmd/ctrl v: paste
if ((event.ctrlKey || event.altKey) && char == 'x') { else if ((sch.ctrlKey || sch.cmdKey) && code == 86) {
sch.cut(); sch.paste();
event.preventDefault(); event.preventDefault();
return false; return false;
} }
// copy // 'r': rotate component
if ((event.ctrlKey || event.altKey) && char == 'c') { else if (!sch.ctrlKey && !sch.altKey && !sch.cmdKey && code == 82) {
sch.copy(); // rotate
for (var i = sch.components.length - 1; i >= 0; --i) {
var component = sch.components[i];
if (component.selected) component.rotate(1);
}
sch.redraw();
event.preventDefault(); event.preventDefault();
return false; return false;
} }
// paste else return true;
if ((event.ctrlKey || event.altKey) && char == 'v') {
sch.paste(); // consume keystroke
sch.redraw();
event.preventDefault(); event.preventDefault();
return false; return false;
} }
function schematic_key_up(event) {
if (!event) event = window.event;
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
var code = event.keyCode;
return true; if (code == 16) sch.shiftKey = false;
} else if (code == 17) sch.ctrlKey = false;
else if (code == 18) sch.altKey = false;
else if (code == 91) sch.commandKey = false;
}
function schematic_mouse_enter(event) { function schematic_mouse_enter(event) {
if (!event) event = window.event; if (!event) event = window.event;
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic; var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
// see if user has selected a new part // see if user has selected a new part
if (sch.new_part) { if (sch.new_part) {
// revert handler
document.onselectstart = sch.saved_onselectstart;
// grab incoming part, turn off selection of parts bin // grab incoming part, turn off selection of parts bin
var part = sch.new_part; var part = sch.new_part;
sch.new_part = null; sch.new_part = undefined;
part.select(false); part.select(false);
// make a clone of the component in the parts bin
part = part.component.clone(sch,sch.cursor_x,sch.cursor_y);
// unselect everything else in the schematic, add part and select it // unselect everything else in the schematic, add part and select it
sch.unselect_all(-1); sch.unselect_all(-1);
sch.redraw_background(); // so we see any components that got unselected sch.redraw_background(); // so we see any components that got unselected
sch.add_component(part);
// make a clone of the component in the parts bin
part = part.component.clone(sch.cursor_x,sch.cursor_y);
part.add(sch); // add it to schematic
part.set_select(true); part.set_select(true);
// and start dragging it // and start dragging it
...@@ -791,17 +895,17 @@ function schematic_mouse_enter(event) { ...@@ -791,17 +895,17 @@ function schematic_mouse_enter(event) {
sch.redraw(); sch.redraw();
sch.canvas.focus(); // capture key strokes sch.canvas.focus(); // capture key strokes
return false; return false;
} }
function schematic_mouse_leave(event) { function schematic_mouse_leave(event) {
if (!event) event = window.event; if (!event) event = window.event;
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic; var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
sch.drawCursor = false; sch.drawCursor = false;
sch.redraw(); sch.redraw();
return false; return false;
} }
function schematic_mouse_down(event) { function schematic_mouse_down(event) {
if (!event) event = window.event; if (!event) event = window.event;
else event.preventDefault(); else event.preventDefault();
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic; var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
...@@ -813,14 +917,6 @@ function schematic_mouse_down(event) { ...@@ -813,14 +917,6 @@ function schematic_mouse_down(event) {
sch.cursor_x = Math.round(x/sch.grid) * sch.grid; sch.cursor_x = Math.round(x/sch.grid) * sch.grid;
sch.cursor_y = Math.round(y/sch.grid) * sch.grid; sch.cursor_y = Math.round(y/sch.grid) * sch.grid;
/*
// for debugging... triggered by clicks in upper left corner
if (sch.cursor_x < 10 && sch.cursor_y < 10) {
sch.label_connection_points();
sch.append_message(JSON.stringify(sch.json()));
}
*/
// is mouse over a connection point? If so, start dragging a wire // is mouse over a connection point? If so, start dragging a wire
var cplist = sch.connection_points[sch.cursor_x + ',' + sch.cursor_y]; var cplist = sch.connection_points[sch.cursor_x + ',' + sch.cursor_y];
if (cplist && !event.shiftKey) { if (cplist && !event.shiftKey) {
...@@ -854,9 +950,9 @@ function schematic_mouse_down(event) { ...@@ -854,9 +950,9 @@ function schematic_mouse_down(event) {
sch.redraw_background(); sch.redraw_background();
return false; return false;
} }
function schematic_mouse_move(event) { function schematic_mouse_move(event) {
if (!event) event = window.event; if (!event) event = window.event;
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic; var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
...@@ -896,9 +992,9 @@ function schematic_mouse_move(event) { ...@@ -896,9 +992,9 @@ function schematic_mouse_move(event) {
sch.redraw(); sch.redraw();
return false; return false;
} }
function schematic_mouse_up(event) { function schematic_mouse_up(event) {
if (!event) event = window.event; if (!event) event = window.event;
else event.preventDefault(); else event.preventDefault();
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic; var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
...@@ -906,7 +1002,7 @@ function schematic_mouse_up(event) { ...@@ -906,7 +1002,7 @@ function schematic_mouse_up(event) {
// drawing a new wire // drawing a new wire
if (sch.wire) { if (sch.wire) {
var r = sch.wire; var r = sch.wire;
sch.wire = null; sch.wire = undefined;
if (r[0]!=r[2] || r[1]!=r[3]) { if (r[0]!=r[2] || r[1]!=r[3]) {
// insert wire component // insert wire component
...@@ -937,13 +1033,13 @@ function schematic_mouse_up(event) { ...@@ -937,13 +1033,13 @@ function schematic_mouse_up(event) {
sch.components[i].select_rect(s,event.shiftKey); sch.components[i].select_rect(s,event.shiftKey);
} }
sch.select_rect = null; sch.select_rect = undefined;
sch.redraw_background(); sch.redraw_background();
} }
return false; return false;
} }
function schematic_double_click(event) { function schematic_double_click(event) {
if (!event) event = window.event; if (!event) event = window.event;
else event.preventDefault(); else event.preventDefault();
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic; var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
...@@ -957,44 +1053,37 @@ function schematic_double_click(event) { ...@@ -957,44 +1053,37 @@ function schematic_double_click(event) {
// see if we double-clicked a component. If so, edit it's properties // see if we double-clicked a component. If so, edit it's properties
for (var i = sch.components.length - 1; i >= 0; --i) for (var i = sch.components.length - 1; i >= 0; --i)
if (sch.components[i].edit_properties(x,y)) break; if (sch.components[i].edit_properties(x,y))
break;
return false; return false;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// Status message and dialogs // Status message and dialogs
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Schematic.prototype.message = function(message) { Schematic.prototype.message = function(message) {
this.status.nodeValue = message; this.status.nodeValue = message;
} }
Schematic.prototype.append_message = function(message) { Schematic.prototype.append_message = function(message) {
this.status.nodeValue += ' / '+message; this.status.nodeValue += ' / '+message;
} }
// set up a dialog with specified title, content and two buttons at // set up a dialog with specified title, content and two buttons at
// the bottom: OK and Cancel. If Cancel is clicked, dialog goes away // the bottom: OK and Cancel. If Cancel is clicked, dialog goes away
// and we're done. If OK is clicked, dialog goes away and the // and we're done. If OK is clicked, dialog goes away and the
// callback function is called with the content as an argument (so // callback function is called with the content as an argument (so
// that the values of any fields can be captured). // that the values of any fields can be captured).
Schematic.prototype.dialog = function(title,content,callback) { Schematic.prototype.dialog = function(title,content,callback) {
// create the div for the top level of the dialog, add to DOM // create the div for the top level of the dialog, add to DOM
var dialog = document.createElement('div'); var dialog = document.createElement('div');
dialog.sch = this; dialog.sch = this;
dialog.content = content; dialog.content = content;
dialog.callback = callback;
// div to hold the title
var head = document.createElement('div');
head.style.backgroundColor = 'black';
head.style.color = 'white';
head.style.textAlign = 'center';
head.style.padding = '5px';
head.appendChild(document.createTextNode(title));
dialog.appendChild(head);
// div to hold the content // div to hold the content
var body = document.createElement('div'); var body = document.createElement('div');
...@@ -1028,127 +1117,248 @@ Schematic.prototype.dialog = function(title,content,callback) { ...@@ -1028,127 +1117,248 @@ Schematic.prototype.dialog = function(title,content,callback) {
buttons.style.margin = '10px'; buttons.style.margin = '10px';
dialog.appendChild(buttons); dialog.appendChild(buttons);
// add to DOM // put into an overlay window
dialog.style.background = 'white'; this.window(title,dialog);
dialog.style.zindex = '1000'; }
dialog.style.position = 'absolute';
dialog.style.left = this.canvas.mouse_x+'px';
dialog.style.top = this.canvas.mouse_y+'px';
dialog.style.border = '2px solid';
dialog.callback = callback;
this.div.appendChild(dialog);
}
// callback when user click "Cancel" in a dialog // callback when user click "Cancel" in a dialog
function dialog_cancel(event) { function dialog_cancel(event) {
if (!event) event = window.event; if (!event) event = window.event;
var dialog = (window.event) ? event.srcElement.dialog : event.target.dialog; var dialog = (window.event) ? event.srcElement.dialog : event.target.dialog;
// remove the dialog from the top-level div of the schematic window_close(dialog.win);
dialog.parentNode.removeChild(dialog); }
}
// callback when user click "OK" in a dialog // callback when user click "OK" in a dialog
function dialog_okay(event) { function dialog_okay(event) {
if (!event) event = window.event; if (!event) event = window.event;
var dialog = (window.event) ? event.srcElement.dialog : event.target.dialog; var dialog = (window.event) ? event.srcElement.dialog : event.target.dialog;
// remove the dialog from the top-level div of the schematic window_close(dialog.win);
dialog.parentNode.removeChild(dialog);
// invoke the callback with the dialog contents as the argument // invoke the callback with the dialog contents as the argument
if (dialog.callback) dialog.callback(dialog.content); if (dialog.callback) dialog.callback(dialog.content);
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// Toolbar // Draggable, resizeable, closeable window
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Schematic.prototype.add_tool = function(icon,tip,callback) { Schematic.prototype.window = function(title,content) {
var img = document.createElement('img'); // create the div for the top level of the window
img.src = icon; var win = document.createElement('div');
img.style.borderWidth = '1px'; win.sch = this;
img.style.borderStyle = 'solid'; win.content = content;
img.style.borderColor = background_style; win.drag_x = undefined;
img.style.position = 'absolute'; win.draw_y = undefined;
img.style.padding = '2px';
img.addEventListener('mouseover',tool_enter,false); // div to hold the title
img.addEventListener('mouseout',tool_leave,false); var head = document.createElement('div');
img.addEventListener('click',tool_click,false); head.style.backgroundColor = 'black';
head.style.color = 'white';
head.style.textAlign = 'center';
head.style.padding = '5px';
head.appendChild(document.createTextNode(title));
img.sch = this; var close_button = new Image();
img.tip = tip; close_button.src = close_icon;
img.callback = callback; close_button.style.cssFloat = 'right';
this.toolbar.push(img); close_button.addEventListener('click',window_close_button,false);
close_button.win = win;
head.appendChild(close_button);
img.enabled = false; win.appendChild(head);
img.style.opacity = 0.2;
return img; // capture mouse events in title bar
} head.addEventListener('mousedown',window_mouse_down,false);
head.addEventListener('mouseup',window_mouse_up,false);
head.addEventListener('mouseout',window_mouse_up,false);
head.addEventListener('mousemove',window_mouse_move,false);
// div to hold the content
var body = document.createElement('div');
body.appendChild(content);
win.appendChild(body);
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;
// add to DOM
win.style.background = 'white';
win.style.zindex = '1000';
win.style.position = 'absolute';
win.style.left = win.left + 'px';
win.style.top = win.top + 'px';
win.style.border = '2px solid';
this.div.appendChild(win);
}
// close the window
function window_close(win) {
// remove the windw from the top-level div of the schematic
win.parentNode.removeChild(win);
}
function window_close_button(event) {
if (!event) event = window.event;
var src = (window.event) ? event.srcElement : event.target;
window_close(src.win);
}
// capture mouse events in title bar of window
function window_mouse_down(event) {
if (!event) event = window.event;
var src = (window.event) ? event.srcElement : event.target;
var win = src.parentNode;
// remember where mouse is so we can compute dx,dy during drag
win.drag_x = event.pageX;
win.drag_y = event.pageY;
return false;
}
function window_mouse_up(event) {
if (!event) event = window.event;
var src = (window.event) ? event.srcElement : event.target;
var win = src.parentNode;
// show's over folks...
win.drag_x = undefined;
win.drag_y = undefined;
return false;
}
function window_mouse_move(event) {
if (!event) event = window.event;
var win = (window.event) ? event.srcElement.parentNode : event.target.parentNode;
if (win.drag_x) {
var dx = event.pageX - win.drag_x;
var dy = event.pageY - win.drag_y;
Schematic.prototype.enable_tool = function(tname,which) { // move the window
var img = this.tools[tname]; win.left += dx;
img.style.opacity = which ? 1.0 : 0.2; win.top += dy;
img.enabled = which; win.style.left = win.left + 'px';
win.style.top = win.top + 'px';
// update reference point
win.drag_x += dx;
win.drag_y += dy;
return false;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Toolbar
//
////////////////////////////////////////////////////////////////////////////////
Schematic.prototype.add_tool = function(icon,tip,callback) {
var tool;
if (icon.search('data:image') != -1) {
tool = document.createElement('img');
tool.src = icon;
} else {
tool = document.createElement('span');
tool.style.font = 'small-caps small sans-serif';
var label = document.createTextNode(icon);
tool.appendChild(label);
}
// decorate tool
tool.style.borderWidth = '1px';
tool.style.borderStyle = 'solid';
tool.style.borderColor = background_style;
tool.style.position = 'absolute';
tool.style.padding = '2px';
// set up event processing
tool.addEventListener('mouseover',tool_enter,false);
tool.addEventListener('mouseout',tool_leave,false);
tool.addEventListener('click',tool_click,false);
// add to toolbar
tool.sch = this;
tool.tip = tip;
tool.callback = callback;
this.toolbar.push(tool);
tool.enabled = false;
tool.style.opacity = 0.2;
return tool;
}
Schematic.prototype.enable_tool = function(tname,which) {
var tool = this.tools[tname];
tool.style.opacity = which ? 1.0 : 0.2;
tool.enabled = which;
// if disabling tool, remove border and tip // if disabling tool, remove border and tip
if (!which) { if (!which) {
img.style.borderColor = background_style; tool.style.borderColor = background_style;
img.sch.message(''); tool.sch.message('');
}
} }
}
// highlight tool button by turning on border, changing background // highlight tool button by turning on border, changing background
function tool_enter(event) { function tool_enter(event) {
if (!event) event = window.event; if (!event) event = window.event;
var img = (window.event) ? event.srcElement : event.target; var tool = (window.event) ? event.srcElement : event.target;
if (img.enabled) { if (tool.enabled) {
img.style.borderColor = normal_style; tool.style.borderColor = normal_style;
img.sch.message(img.tip); tool.sch.message(tool.tip);
img.opacity = 1.0; tool.opacity = 1.0;
}
} }
}
// unhighlight tool button by turning off border, reverting to normal background // unhighlight tool button by turning off border, reverting to normal background
function tool_leave(event) { function tool_leave(event) {
if (!event) event = window.event; if (!event) event = window.event;
var img = (window.event) ? event.srcElement : event.target; var tool = (window.event) ? event.srcElement : event.target;
if (img.enabled) { if (tool.enabled) {
img.style.borderColor = background_style; tool.style.borderColor = background_style;
img.sch.message(''); tool.sch.message('');
}
} }
}
// handle click on a tool // handle click on a tool
function tool_click(event) { function tool_click(event) {
if (!event) event = window.event; if (!event) event = window.event;
var img = (window.event) ? event.srcElement : event.target; var tool = (window.event) ? event.srcElement : event.target;
if (img.enabled) img.callback.call(img.sch); if (tool.enabled) tool.callback.call(tool.sch);
} }
cut_icon = 'data:image/gif;base64,R0lGODlhEAAQALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAcALAAAAAAQABAAAAQu8MhJqz1g5qs7lxv2gRkQfuWomarXEgDRHjJhf3YtyRav0xcfcFgR0nhB5OwTAQA7'; cut_icon = 'data:image/gif;base64,R0lGODlhEAAQALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAcALAAAAAAQABAAAAQu8MhJqz1g5qs7lxv2gRkQfuWomarXEgDRHjJhf3YtyRav0xcfcFgR0nhB5OwTAQA7';
copy_icon = 'data:image/gif;base64,R0lGODlhEAAQALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAcALAAAAAAQABAAAAQ+8MhJ6wE4Wwqef9gmdV8HiKZJrCz3ecS7TikWfzExvk+M9a0a4MbTkXCgTMeoHPJgG5+yF31SLazsTMTtViIAOw=='; copy_icon = 'data:image/gif;base64,R0lGODlhEAAQALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAcALAAAAAAQABAAAAQ+8MhJ6wE4Wwqef9gmdV8HiKZJrCz3ecS7TikWfzExvk+M9a0a4MbTkXCgTMeoHPJgG5+yF31SLazsTMTtViIAOw==';
paste_icon = 'data:image/gif;base64,R0lGODlhEAAQALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAcALAAAAAAQABAAAARL8MhJqwUYWJnxWp3GDcgAgCdQIqLKXmVLhhnyHiqpr7rME8AgocVDEB5IJHD0SyofBFzxGIQGAbvB0ZkcTq1CKK6z5YorwnR0w44AADs='; paste_icon = 'data:image/gif;base64,R0lGODlhEAAQALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAcALAAAAAAQABAAAARL8MhJqwUYWJnxWp3GDcgAgCdQIqLKXmVLhhnyHiqpr7rME8AgocVDEB5IJHD0SyofBFzxGIQGAbvB0ZkcTq1CKK6z5YorwnR0w44AADs=';
/////////////////////////////////////////////////////////////////////////////// close_icon = 'data:image/gif;base64,R0lGODlhEAAQAMQAAGtra/f3/62tre/v9+bm787O1pycnHNzc6WlpcXFxd7e3tbW1nt7e7W1te/v74SEhMXFzmNjY+bm5v///87OzgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAAQABAAAAVt4DRMZGmSwRQQBUS9MAwRIyQ5Uq7neEFSDtxOF4T8cobIQaE4RAQ5yjHHiCCSD510QtFGvoCFdppDfBu7bYzy+D7WP5ggAgA8Y3FKwi5IAhIweW1vbBGEWy5rilsFi2tGAwSJixAFBCkpJ5ojIQA7';
//
// Parts bin
//
////////////////////////////////////////////////////////////////////////////////
// one instance will be created for each part in the parts bin
function Part(sch) { ///////////////////////////////////////////////////////////////////////////////
//
// Parts bin
//
////////////////////////////////////////////////////////////////////////////////
// one instance will be created for each part in the parts bin
function Part(sch) {
this.sch = sch; this.sch = sch;
this.component = null; this.component = undefined;
this.selected = false; this.selected = false;
// set up canvas // set up canvas
...@@ -1170,22 +1380,23 @@ function Part(sch) { ...@@ -1170,22 +1380,23 @@ function Part(sch) {
// make the part "clickable" by registering a dummy click handler // make the part "clickable" by registering a dummy click handler
// this should make things work on the iPad // this should make things work on the iPad
this.canvas.addEventListener('click',function(){},false); this.canvas.addEventListener('click',function(){},false);
} }
Part.prototype.set_location = function(left,top) { Part.prototype.set_location = function(left,top) {
this.canvas.style.left = left + 'px'; this.canvas.style.left = left + 'px';
this.canvas.style.top = top + 'px'; this.canvas.style.top = top + 'px';
} }
Part.prototype.right = function() { Part.prototype.right = function() {
return this.canvas.offsetLeft + this.canvas.offsetWidth; return this.canvas.offsetLeft + this.canvas.offsetWidth;
} }
Part.prototype.bottom = function() { Part.prototype.bottom = function() {
return this.canvas.offsetTop + this.canvas.offsetHeight; return this.canvas.offsetTop + this.canvas.offsetHeight;
} }
Part.prototype.set_component = function(component,tip) { Part.prototype.set_component = function(component,tip) {
component.sch = this;
this.component = component; this.component = component;
this.tip = tip; this.tip = tip;
...@@ -1198,9 +1409,9 @@ Part.prototype.set_component = function(component,tip) { ...@@ -1198,9 +1409,9 @@ Part.prototype.set_component = function(component,tip) {
this.origin_y = b[1] + dy/2.0 - part_h/(2.0*this.scale); this.origin_y = b[1] + dy/2.0 - part_h/(2.0*this.scale);
this.redraw(); this.redraw();
} }
Part.prototype.redraw = function(part) { Part.prototype.redraw = function(part) {
var c = this.canvas.getContext('2d'); var c = this.canvas.getContext('2d');
// paint background color // paint background color
...@@ -1208,86 +1419,98 @@ Part.prototype.redraw = function(part) { ...@@ -1208,86 +1419,98 @@ Part.prototype.redraw = function(part) {
c.fillRect(0,0,part_w,part_h); c.fillRect(0,0,part_w,part_h);
if (this.component) this.component.draw(c); if (this.component) this.component.draw(c);
} }
Part.prototype.select = function(which) { Part.prototype.select = function(which) {
this.selected = which; this.selected = which;
this.redraw(); this.redraw();
} }
Part.prototype.update_connection_point = function(cp,old_location) { Part.prototype.update_connection_point = function(cp,old_location) {
// no connection points in the parts bin // no connection points in the parts bin
} }
Part.prototype.draw_line = function(c,x1,y1,x2,y2,width) { Part.prototype.draw_line = function(c,x1,y1,x2,y2,width) {
c.lineWidth = width*this.scale; c.lineWidth = width*this.scale;
c.beginPath(); c.beginPath();
c.moveTo((x1 - this.origin_x) * this.scale,(y1 - this.origin_y) * this.scale); c.moveTo((x1 - this.origin_x) * this.scale,(y1 - this.origin_y) * this.scale);
c.lineTo((x2 - this.origin_x) * this.scale,(y2 - this.origin_y) * this.scale); c.lineTo((x2 - this.origin_x) * this.scale,(y2 - this.origin_y) * this.scale);
c.stroke(); c.stroke();
} }
Part.prototype.draw_arc = function(c,x,y,radius,start_radians,end_radians,anticlockwise,width,filled) { Part.prototype.draw_arc = function(c,x,y,radius,start_radians,end_radians,anticlockwise,width,filled) {
c.lineWidth = width*this.scale; c.lineWidth = width*this.scale;
c.beginPath(); c.beginPath();
c.arc((x - this.origin_x)*this.scale,(y - this.origin_y)*this.scale,radius*this.scale, c.arc((x - this.origin_x)*this.scale,(y - this.origin_y)*this.scale,radius*this.scale,
start_radians,end_radians,anticlockwise); start_radians,end_radians,anticlockwise);
if (filled) c.fill(); if (filled) c.fill();
else c.stroke(); else c.stroke();
} }
Part.prototype.draw_text = function(c,text,x,y,size) { Part.prototype.draw_text = function(c,text,x,y,size) {
// no text displayed for the parts icon // no text displayed for the parts icon
} }
function part_enter(event) { function part_enter(event) {
if (!event) event = window.event; if (!event) event = window.event;
var canvas = (window.event) ? event.srcElement : event.target; var canvas = (window.event) ? event.srcElement : event.target;
var part = canvas.part; var part = canvas.part;
// avoid Chrome bug that changes to text cursor whenever
// drag starts. We'll restore the default handler at
// the appropriate point so behavior in other parts of
// the document are unaffected.
part.sch.saved_onselectstart = document.onselectstart;
document.onselectstart = function () { return false; };
canvas.style.borderColor = normal_style; canvas.style.borderColor = normal_style;
part.sch.message(part.tip+': drag onto diagram to insert'); part.sch.message(part.tip+': drag onto diagram to insert');
return false; return false;
} }
function part_leave(event) { function part_leave(event) {
if (!event) event = window.event; if (!event) event = window.event;
var canvas = (window.event) ? event.srcElement : event.target; var canvas = (window.event) ? event.srcElement : event.target;
var part = canvas.part; var part = canvas.part;
if (typeof part.sch.new_part == 'undefined') {
// leaving with no part selected? revert handler
document.onselectstart = part.sch.saved_onselectstart;
}
canvas.style.borderColor = background_style; canvas.style.borderColor = background_style;
part.sch.message(''); part.sch.message('');
return false; return false;
} }
function part_mouse_down(event) { function part_mouse_down(event) {
if (!event) event = window.event; if (!event) event = window.event;
var part = (window.event) ? event.srcElement.part : event.target.part; var part = (window.event) ? event.srcElement.part : event.target.part;
part.select(true); part.select(true);
part.sch.new_part = part; part.sch.new_part = part;
return false; return false;
} }
function part_mouse_up(event) { function part_mouse_up(event) {
if (!event) event = window.event; if (!event) event = window.event;
var part = (window.event) ? event.srcElement.part : event.target.part; var part = (window.event) ? event.srcElement.part : event.target.part;
part.select(false); part.select(false);
part.sch.new_part = null; part.sch.new_part = undefined;
return false; return false;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Rectangle helper functions // Rectangle helper functions
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// rect is an array of the form [left,top,right,bottom] // rect is an array of the form [left,top,right,bottom]
// ensure left < right, top < bottom // ensure left < right, top < bottom
function canonicalize(r) { function canonicalize(r) {
var temp; var temp;
// canonicalize bounding box // canonicalize bounding box
...@@ -1301,18 +1524,18 @@ function canonicalize(r) { ...@@ -1301,18 +1524,18 @@ function canonicalize(r) {
r[1] = r[3]; r[1] = r[3];
r[3] = temp; r[3] = temp;
} }
} }
function between(x,x1,x2) { function between(x,x1,x2) {
return x1 <= x && x <= x2; return x1 <= x && x <= x2;
} }
function inside(rect,x,y) { function inside(rect,x,y) {
return between(x,rect[0],rect[2]) && between(y,rect[1],rect[3]); return between(x,rect[0],rect[2]) && between(y,rect[1],rect[3]);
} }
// only works for manhattan rectangles // only works for manhattan rectangles
function intersect(r1,r2) { function intersect(r1,r2) {
// look for non-intersection, negate result // look for non-intersection, negate result
var result = !(r2[0] > r1[2] || var result = !(r2[0] > r1[2] ||
r2[2] < r1[0] || r2[2] < r1[0] ||
...@@ -1321,18 +1544,16 @@ function intersect(r1,r2) { ...@@ -1321,18 +1544,16 @@ function intersect(r1,r2) {
// if I try to return the above expression, javascript returns undefined!!! // if I try to return the above expression, javascript returns undefined!!!
return result; return result;
} }
////////////////////////////////////////////////////////////////////////////////
//
// Component base class
//
////////////////////////////////////////////////////////////////////////////////
property_size = 5; // point size for Component property text ////////////////////////////////////////////////////////////////////////////////
//
// Component base class
//
////////////////////////////////////////////////////////////////////////////////
function Component(sch,x,y,rotation) { function Component(x,y,rotation) {
this.sch = sch; this.sch = undefined;
this.x = x; this.x = x;
this.y = y; this.y = y;
this.rotation = rotation; this.rotation = rotation;
...@@ -1341,9 +1562,9 @@ function Component(sch,x,y,rotation) { ...@@ -1341,9 +1562,9 @@ function Component(sch,x,y,rotation) {
this.bounding_box = [0,0,0,0]; // in device coords [left,top,right,bottom] this.bounding_box = [0,0,0,0]; // in device coords [left,top,right,bottom]
this.bbox = this.bounding_box; // in absolute coords this.bbox = this.bounding_box; // in absolute coords
this.connections = []; this.connections = [];
} }
Component.prototype.json = function() { Component.prototype.json = function() {
var props = {}; var props = {};
for (var p in this.properties) props[p] = this.properties[p]; for (var p in this.properties) props[p] = this.properties[p];
...@@ -1353,13 +1574,13 @@ Component.prototype.json = function() { ...@@ -1353,13 +1574,13 @@ Component.prototype.json = function() {
var json = [this.type,[this.x, this.y, this.rotation],props,conns]; var json = [this.type,[this.x, this.y, this.rotation],props,conns];
return json; return json;
} }
Component.prototype.add_connection = function(offset_x,offset_y) { Component.prototype.add_connection = function(offset_x,offset_y) {
this.connections.push(new ConnectionPoint(this,offset_x,offset_y)); this.connections.push(new ConnectionPoint(this,offset_x,offset_y));
} }
Component.prototype.update_coords = function() { Component.prototype.update_coords = function() {
var x = this.x; var x = this.x;
var y = this.y; var y = this.y;
...@@ -1374,31 +1595,31 @@ Component.prototype.update_coords = function() { ...@@ -1374,31 +1595,31 @@ Component.prototype.update_coords = function() {
// update connections // update connections
for (var i = this.connections.length - 1; i >= 0; --i) for (var i = this.connections.length - 1; i >= 0; --i)
this.connections[i].update_location(); this.connections[i].update_location();
} }
Component.prototype.rotate = function(amount) { Component.prototype.rotate = function(amount) {
var old_rotation = this.rotation; var old_rotation = this.rotation;
this.rotation = (this.rotation + amount) % 8; this.rotation = (this.rotation + amount) % 8;
this.update_coords(); this.update_coords();
// create an undoable edit record here // create an undoable edit record here
// using old_rotation // using old_rotation
} }
Component.prototype.move_begin = function() { Component.prototype.move_begin = function() {
// remember where we started this move // remember where we started this move
this.move_x = this.x; this.move_x = this.x;
this.move_y = this.y; this.move_y = this.y;
} }
Component.prototype.move = function(dx,dy) { Component.prototype.move = function(dx,dy) {
// update coordinates // update coordinates
this.x += dx; this.x += dx;
this.y += dy; this.y += dy;
this.update_coords(); this.update_coords();
} }
Component.prototype.move_end = function() { Component.prototype.move_end = function() {
var dx = this.x - this.move_x; var dx = this.x - this.move_x;
var dy = this.y - this.move_y; var dy = this.y - this.move_y;
...@@ -1407,9 +1628,15 @@ Component.prototype.move_end = function() { ...@@ -1407,9 +1628,15 @@ Component.prototype.move_end = function() {
this.sch.check_wires(this); this.sch.check_wires(this);
} }
} }
Component.prototype.add = function(sch) {
this.sch = sch; // we now belong to a schematic!
sch.add_component(this);
this.update_coords();
}
Component.prototype.delete = function() { Component.prototype.delete = function() {
// remove connection points from schematic // remove connection points from schematic
for (var i = this.connections.length - 1; i >= 0; --i) { for (var i = this.connections.length - 1; i >= 0; --i) {
var cp = this.connections[i]; var cp = this.connections[i];
...@@ -1418,45 +1645,46 @@ Component.prototype.delete = function() { ...@@ -1418,45 +1645,46 @@ Component.prototype.delete = function() {
// remove component from schematic // remove component from schematic
this.sch.remove_component(this); this.sch.remove_component(this);
this.sch = undefined;
// create an undoable edit record here // create an undoable edit record here
} }
Component.prototype.transform_x = function(x,y) { Component.prototype.transform_x = function(x,y) {
var rot = this.rotation; var rot = this.rotation;
if (rot == 0 || rot == 6) return x; if (rot == 0 || rot == 6) return x;
else if (rot == 1 || rot == 5) return -y; else if (rot == 1 || rot == 5) return -y;
else if (rot == 2 || rot == 4) return -x; else if (rot == 2 || rot == 4) return -x;
else return y; else return y;
} }
Component.prototype.transform_y = function(x,y) { Component.prototype.transform_y = function(x,y) {
var rot = this.rotation; var rot = this.rotation;
if (rot == 1 || rot == 7) return x; if (rot == 1 || rot == 7) return x;
else if (rot == 2 || rot == 6) return -y; else if (rot == 2 || rot == 6) return -y;
else if (rot == 3 || rot == 5) return -x; else if (rot == 3 || rot == 5) return -x;
else return y; else return y;
} }
Component.prototype.draw_line = function(c,x1,y1,x2,y2) { Component.prototype.draw_line = function(c,x1,y1,x2,y2) {
c.strokeStyle = this.selected ? selected_style : normal_style; c.strokeStyle = this.selected ? selected_style : normal_style;
var nx1 = this.transform_x(x1,y1) + this.x; var nx1 = this.transform_x(x1,y1) + this.x;
var ny1 = this.transform_y(x1,y1) + this.y; var ny1 = this.transform_y(x1,y1) + this.y;
var nx2 = this.transform_x(x2,y2) + this.x; var nx2 = this.transform_x(x2,y2) + this.x;
var ny2 = this.transform_y(x2,y2) + this.y; var ny2 = this.transform_y(x2,y2) + this.y;
this.sch.draw_line(c,nx1,ny1,nx2,ny2,1); this.sch.draw_line(c,nx1,ny1,nx2,ny2,1);
} }
Component.prototype.draw_circle = function(c,x,y,radius,filled) { Component.prototype.draw_circle = function(c,x,y,radius,filled) {
if (filled) c.fillStyle = this.selected ? selected_style : normal_style; if (filled) c.fillStyle = this.selected ? selected_style : normal_style;
else c.strokeStyle = this.selected ? selected_style : normal_style; else c.strokeStyle = this.selected ? selected_style : normal_style;
var nx = this.transform_x(x,y) + this.x; var nx = this.transform_x(x,y) + this.x;
var ny = this.transform_y(x,y) + this.y; var ny = this.transform_y(x,y) + this.y;
this.sch.draw_arc(c,nx,ny,radius,0,2*Math.PI,false,1,filled); this.sch.draw_arc(c,nx,ny,radius,0,2*Math.PI,false,1,filled);
} }
rot_angle = [ rot_angle = [
0.0, // NORTH (identity) 0.0, // NORTH (identity)
Math.PI/2, // EAST (rot270) Math.PI/2, // EAST (rot270)
Math.PI, // SOUTH (rot180) Math.PI, // SOUTH (rot180)
...@@ -1465,22 +1693,22 @@ rot_angle = [ ...@@ -1465,22 +1693,22 @@ rot_angle = [
Math.PI/2, // REAST (int-neg) Math.PI/2, // REAST (int-neg)
Math.PI, // RSOUTH (negx) Math.PI, // RSOUTH (negx)
3*Math.PI/2, // RWEST (int-pos) 3*Math.PI/2, // RWEST (int-pos)
]; ];
Component.prototype.draw_arc = function(c,x,y,radius,start_radians,end_radians) { Component.prototype.draw_arc = function(c,x,y,radius,start_radians,end_radians) {
c.strokeStyle = this.selected ? selected_style : normal_style; c.strokeStyle = this.selected ? selected_style : normal_style;
var nx = this.transform_x(x,y) + this.x; var nx = this.transform_x(x,y) + this.x;
var ny = this.transform_y(x,y) + this.y; var ny = this.transform_y(x,y) + this.y;
this.sch.draw_arc(c,nx,ny,radius, this.sch.draw_arc(c,nx,ny,radius,
start_radians+rot_angle[this.rotation],end_radians+rot_angle[this.rotation], start_radians+rot_angle[this.rotation],end_radians+rot_angle[this.rotation],
false,1,false); false,1,false);
} }
Component.prototype.draw = function(c) { Component.prototype.draw = function(c) {
} }
// result of rotating an alignment [rot*9 + align] // result of rotating an alignment [rot*9 + align]
aOrient = [ aOrient = [
0, 1, 2, 3, 4, 5, 6, 7, 8, // NORTH (identity) 0, 1, 2, 3, 4, 5, 6, 7, 8, // NORTH (identity)
2, 5, 8, 1, 4, 7, 0, 3, 6, // EAST (rot270) 2, 5, 8, 1, 4, 7, 0, 3, 6, // EAST (rot270)
8, 7, 6, 5, 4, 3, 2, 1, 0, // SOUTH (rot180) 8, 7, 6, 5, 4, 3, 2, 1, 0, // SOUTH (rot180)
...@@ -1489,60 +1717,63 @@ aOrient = [ ...@@ -1489,60 +1717,63 @@ aOrient = [
8, 5, 2, 7, 4, 1, 6, 3, 0, // REAST (int-neg) 8, 5, 2, 7, 4, 1, 6, 3, 0, // REAST (int-neg)
6, 7, 8, 3, 4, 5, 0, 1, 2, // RSOUTH (negx) 6, 7, 8, 3, 4, 5, 0, 1, 2, // RSOUTH (negx)
0, 3, 6, 1, 4, 7, 2, 5, 8 // RWEST (int-pos) 0, 3, 6, 1, 4, 7, 2, 5, 8 // RWEST (int-pos)
]; ];
textAlign = [ textAlign = [
'left', 'center', 'right', 'left', 'center', 'right',
'left', 'center', 'right', 'left', 'center', 'right',
'left', 'center', 'right' 'left', 'center', 'right'
]; ];
textBaseline = [ textBaseline = [
'top', 'top', 'top', 'top', 'top', 'top',
'middle', 'middle', 'middle', 'middle', 'middle', 'middle',
'bottom', 'bottom', 'bottom' 'bottom', 'bottom', 'bottom'
]; ];
Component.prototype.draw_text = function(c,text,x,y,alignment,size) { Component.prototype.draw_text = function(c,text,x,y,alignment,size,fill) {
var a = aOrient[this.rotation*9 + alignment]; var a = aOrient[this.rotation*9 + alignment];
c.textAlign = textAlign[a]; c.textAlign = textAlign[a];
c.textBaseline = textBaseline[a]; c.textBaseline = textBaseline[a];
if (fill == undefined)
c.fillStyle = this.selected ? selected_style : normal_style; c.fillStyle = this.selected ? selected_style : normal_style;
else
c.fillStyle = fill;
this.sch.draw_text(c,text, this.sch.draw_text(c,text,
this.transform_x(x,y) + this.x, this.transform_x(x,y) + this.x,
this.transform_y(x,y) + this.y, this.transform_y(x,y) + this.y,
size); size);
} }
Component.prototype.set_select = function(which) { Component.prototype.set_select = function(which) {
if (which != this.selected) { if (which != this.selected) {
this.selected = which; this.selected = which;
// create an undoable edit record here // create an undoable edit record here
} }
} }
Component.prototype.select = function(x,y,shiftKey) { Component.prototype.select = function(x,y,shiftKey) {
this.was_previously_selected = this.selected; this.was_previously_selected = this.selected;
if (inside(this.bbox,x,y)) { if (inside(this.bbox,x,y)) {
this.set_select(shiftKey ? !this.selected : true); this.set_select(shiftKey ? !this.selected : true);
return true; return true;
} else return false; } else return false;
} }
Component.prototype.select_rect = function(s) { Component.prototype.select_rect = function(s) {
this.was_previously_selected = this.selected; this.was_previously_selected = this.selected;
if (intersect(this.bbox,s)) if (intersect(this.bbox,s))
this.set_select(true); this.set_select(true);
} }
// if connection point of component c bisects the // if connection point of component c bisects the
// wire represented by this compononent, return that // wire represented by this compononent, return that
// connection point. Otherwise return null. // connection point. Otherwise return null.
Component.prototype.bisect = function(c) { Component.prototype.bisect = function(c) {
return null; return null;
} }
Component.prototype.edit_properties = function(x,y) { Component.prototype.edit_properties = function(x,y) {
if (inside(this.bbox,x,y)) { if (inside(this.bbox,x,y)) {
var content = document.createElement('table'); var content = document.createElement('table');
content.style.marginBotton = '5px'; content.style.marginBotton = '5px';
...@@ -1553,7 +1784,8 @@ Component.prototype.edit_properties = function(x,y) { ...@@ -1553,7 +1784,8 @@ Component.prototype.edit_properties = function(x,y) {
var label = document.createTextNode(i + ': '); var label = document.createTextNode(i + ': ');
var field = document.createElement('input'); var field = document.createElement('input');
field.type = 'text'; field.type = 'text';
field.value = this.properties[i]; var pvalue = this.properties[i];
field.value = pvalue ? pvalue : '';
field.size = 10; field.size = 10;
content.fields.push([i,field]); content.fields.push([i,field]);
...@@ -1570,74 +1802,75 @@ Component.prototype.edit_properties = function(x,y) { ...@@ -1570,74 +1802,75 @@ Component.prototype.edit_properties = function(x,y) {
} }
var component = this; // capture in closure below var component = this; // capture in closure below
this.sch.dialog('Edit Properties',content,function(content) { this.sch.dialog('Edit Properties',content,function(content) {
var fields = content.fields; var fields = content.fields;
for (var i = fields.length - 1; i >= 0; i--) for (var i = fields.length - 1; i >= 0; i--)
component.properties[fields[i][0]] = fields[i][1].value; component.properties[fields[i][0]] = fields[i][1].value;
component.sch.redraw(); // component is selected, so this will redraw it component.sch.redraw_background();
}); });
return true; return true;
} else return false; } else return false;
} }
// clear the labels on all connections // clear the labels on all connections
Component.prototype.clear_labels = function() { Component.prototype.clear_labels = function() {
for (var i = this.connections.length - 1; i >=0; --i) { for (var i = this.connections.length - 1; i >=0; --i) {
this.connections[i].clear_label(); this.connections[i].clear_label();
} }
} }
// default action: don't propagate label // default action: don't propagate label
Component.prototype.propagate_label = function(label) { Component.prototype.propagate_label = function(label) {
} }
// give components a chance to generate default labels for their connection(s) // give components a chance to generate default labels for their connection(s)
// default action: do nothing // default action: do nothing
Component.prototype.add_default_labels = function() { Component.prototype.add_default_labels = function() {
} }
// component should generate labels for all unlabeled connections // component should generate labels for all unlabeled connections
Component.prototype.label_connections = function() { Component.prototype.label_connections = function() {
for (var i = this.connections.length - 1; i >=0; --i) { for (var i = this.connections.length - 1; i >=0; --i) {
var cp = this.connections[i]; var cp = this.connections[i];
if (!cp.label) if (!cp.label)
cp.propagate_label(this.sch.get_next_label()); cp.propagate_label(this.sch.get_next_label());
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Connection point // Connection point
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
connection_point_radius = 2; connection_point_radius = 2;
function ConnectionPoint(parent,x,y) { function ConnectionPoint(parent,x,y) {
this.parent = parent; this.parent = parent;
this.offset_x = x; this.offset_x = x;
this.offset_y = y; this.offset_y = y;
this.location = ''; this.location = '';
this.update_location(); this.update_location();
this.label = null; this.label = undefined;
} }
ConnectionPoint.prototype.toString = function() { ConnectionPoint.prototype.toString = function() {
return '<ConnectionPoint ('+this.offset_x+','+this.offset_y+') '+this.parent.toString()+'>'; return '<ConnectionPoint ('+this.offset_x+','+this.offset_y+') '+this.parent.toString()+'>';
} }
ConnectionPoint.prototype.json = function() { ConnectionPoint.prototype.json = function() {
return this.label; return this.label;
} }
ConnectionPoint.prototype.clear_label = function() { ConnectionPoint.prototype.clear_label = function() {
this.label = null; this.label = undefined;
} }
ConnectionPoint.prototype.propagate_label = function(label) { ConnectionPoint.prototype.propagate_label = function(label) {
// should we check if existing label is the same? it should be... // should we check if existing label is the same? it should be...
if (this.label == null) { if (this.label === undefined) {
// label this connection point // label this connection point
this.label = label; this.label = label;
...@@ -1647,9 +1880,9 @@ ConnectionPoint.prototype.propagate_label = function(label) { ...@@ -1647,9 +1880,9 @@ ConnectionPoint.prototype.propagate_label = function(label) {
// possibly label other cp's for this device? // possibly label other cp's for this device?
this.parent.propagate_label(label); this.parent.propagate_label(label);
} }
} }
ConnectionPoint.prototype.update_location = function() { ConnectionPoint.prototype.update_location = function() {
// update location string which we use as a key to find coincident connection points // update location string which we use as a key to find coincident connection points
var old_location = this.location; var old_location = this.location;
var parent = this.parent; var parent = this.parent;
...@@ -1660,29 +1893,48 @@ ConnectionPoint.prototype.update_location = function() { ...@@ -1660,29 +1893,48 @@ ConnectionPoint.prototype.update_location = function() {
this.location = nx + ',' + ny; this.location = nx + ',' + ny;
// add ourselves to the connection list for the new location // add ourselves to the connection list for the new location
if (parent.sch)
parent.sch.update_connection_point(this,old_location); parent.sch.update_connection_point(this,old_location);
} }
ConnectionPoint.prototype.coincident = function(x,y) { ConnectionPoint.prototype.coincident = function(x,y) {
return this.x==x && this.y==y; return this.x==x && this.y==y;
} }
ConnectionPoint.prototype.draw = function(c,n) { ConnectionPoint.prototype.draw = function(c,n) {
if (n != 2) if (n != 2)
this.parent.draw_circle(c,this.offset_x,this.offset_y,connection_point_radius,n > 2); this.parent.draw_circle(c,this.offset_x,this.offset_y,connection_point_radius,n > 2);
} }
//////////////////////////////////////////////////////////////////////////////// ConnectionPoint.prototype.display_voltage = function(c,vmap) {
// var v = vmap[this.label];
// Wire if (v != undefined) {
// var label = v.toFixed(2) + 'V';
////////////////////////////////////////////////////////////////////////////////
// first draw some solid blocks in the background
this.parent.draw_text(c,'\u2588\u2588\u2588\u2588',this.offset_x,this.offset_y,
4,annotation_size,background_style);
// display the node voltage at this connection point
this.parent.draw_text(c,label,this.offset_x,this.offset_y,
4,annotation_size,annotation_style);
// only display each node voltage once
delete vmap[this.label];
}
}
near_distance = 2; // how close to wire counts as "near by" ////////////////////////////////////////////////////////////////////////////////
//
// Wire
//
////////////////////////////////////////////////////////////////////////////////
function Wire(sch,x1,y1,x2,y2) { near_distance = 2; // how close to wire counts as "near by"
function Wire(x1,y1,x2,y2) {
// arbitrarily call x1,y1 the origin // arbitrarily call x1,y1 the origin
Component.call(this,sch,x1,y1,0); Component.call(this,x1,y1,0);
this.dx = x2 - x1; this.dx = x2 - x1;
this.dy = y2 - y1; this.dy = y2 - y1;
this.add_connection(0,0); this.add_connection(0,0);
...@@ -1700,28 +1952,28 @@ function Wire(sch,x1,y1,x2,y2) { ...@@ -1700,28 +1952,28 @@ function Wire(sch,x1,y1,x2,y2) {
// used in selection calculations // used in selection calculations
this.len = Math.sqrt(this.dx*this.dx + this.dy*this.dy); this.len = Math.sqrt(this.dx*this.dx + this.dy*this.dy);
} }
Wire.prototype = new Component(); Wire.prototype = new Component();
Wire.prototype.constructor = Wire; Wire.prototype.constructor = Wire;
Wire.prototype.toString = function() { Wire.prototype.toString = function() {
return '<Wire ('+this.x+','+this.y+') ('+(this.x+this.dx)+','+(this.y+this.dy)+')>'; return '<Wire ('+this.x+','+this.y+') ('+(this.x+this.dx)+','+(this.y+this.dy)+')>';
} }
Wire.prototype.json = function() { Wire.prototype.json = function() {
var json = ['w',[this.x, this.y, this.x+this.dx, this.y+this.dy]]; var json = ['w',[this.x, this.y, this.x+this.dx, this.y+this.dy]];
return json; return json;
} }
Wire.prototype.draw = function(c) { Wire.prototype.draw = function(c) {
this.draw_line(c,0,0,this.dx,this.dy); this.draw_line(c,0,0,this.dx,this.dy);
} }
Wire.prototype.clone = function(sch,x,y) { Wire.prototype.clone = function(x,y) {
return new Wire(sch,x,y,x+this.dx,y+this.dy); return new Wire(x,y,x+this.dx,y+this.dy);
} }
Wire.prototype.near = function(x,y) { Wire.prototype.near = function(x,y) {
// crude check: (x,y) within expanded bounding box of wire // crude check: (x,y) within expanded bounding box of wire
if (inside(this.bbox,x,y)) { if (inside(this.bbox,x,y)) {
// compute distance between x,y and nearst point on line // compute distance between x,y and nearst point on line
...@@ -1730,28 +1982,28 @@ Wire.prototype.near = function(x,y) { ...@@ -1730,28 +1982,28 @@ Wire.prototype.near = function(x,y) {
if (D <= near_distance) return true; if (D <= near_distance) return true;
} }
return false; return false;
} }
Wire.prototype.select = function(x,y,shiftKey) { Wire.prototype.select = function(x,y,shiftKey) {
this.was_previously_selected = this.selected; this.was_previously_selected = this.selected;
if (this.near(x,y)) { if (this.near(x,y)) {
this.set_select(shiftKey ? !this.selected : true); this.set_select(shiftKey ? !this.selected : true);
return true; return true;
} else return false; } else return false;
} }
// selection rectangle selects wire only if it includes // selection rectangle selects wire only if it includes
// one of the end points // one of the end points
Wire.prototype.select_rect = function(s) { Wire.prototype.select_rect = function(s) {
this.was_previously_selected = this.selected; this.was_previously_selected = this.selected;
if (inside(s,this.x,this.y) || inside(s,this.x+this.dx,this.y+this.dy)) if (inside(s,this.x,this.y) || inside(s,this.x+this.dx,this.y+this.dy))
this.set_select(true); this.set_select(true);
} }
// if connection point of component c bisects the // if connection point of component c bisects the
// wire represented by this compononent, return that // wire represented by this compononent, return that
// connection point. Otherwise return null. // connection point. Otherwise return null.
Wire.prototype.bisect = function(c) { Wire.prototype.bisect = function(c) {
for (var i = c.connections.length - 1; i >= 0; --i) { for (var i = c.connections.length - 1; i >= 0; --i) {
var cp = c.connections[i]; var cp = c.connections[i];
var x = cp.x; var x = cp.x;
...@@ -1768,67 +2020,67 @@ Wire.prototype.bisect = function(c) { ...@@ -1768,67 +2020,67 @@ Wire.prototype.bisect = function(c) {
} }
} }
return null; return null;
} }
Wire.prototype.move_end = function() { Wire.prototype.move_end = function() {
this.sch.check_wires(this); this.sch.check_wires(this);
} }
// wires "conduct" their label to the other end // wires "conduct" their label to the other end
Wire.prototype.propagate_label = function(label) { Wire.prototype.propagate_label = function(label) {
// don't worry about relabeling a cp, it won't recurse! // don't worry about relabeling a cp, it won't recurse!
this.connections[0].propagate_label(label); this.connections[0].propagate_label(label);
this.connections[1].propagate_label(label); this.connections[1].propagate_label(label);
} }
// some actual component will start the labeling of electrical nodes, // some actual component will start the labeling of electrical nodes,
// so do nothing here // so do nothing here
Wire.prototype.label_connections = function() { Wire.prototype.label_connections = function() {
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Ground // Ground
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function Ground(sch,x,y,rotation) { function Ground(x,y,rotation) {
Component.call(this,sch,x,y,rotation); Component.call(this,x,y,rotation);
this.add_connection(0,0); this.add_connection(0,0);
this.bounding_box = [-6,0,6,8]; this.bounding_box = [-6,0,6,8];
this.update_coords(); this.update_coords();
this.type = 'g'; this.type = 'g';
} }
Ground.prototype = new Component(); Ground.prototype = new Component();
Ground.prototype.constructor = Ground; Ground.prototype.constructor = Ground;
Ground.prototype.toString = function() { Ground.prototype.toString = function() {
return '<Ground ('+this.x+','+this.y+')>'; return '<Ground ('+this.x+','+this.y+')>';
} }
Ground.prototype.draw = function(c) { Ground.prototype.draw = function(c) {
this.draw_line(c,0,0,0,8); this.draw_line(c,0,0,0,8);
this.draw_line(c,-6,8,6,8); this.draw_line(c,-6,8,6,8);
} }
Ground.prototype.clone = function(sch,x,y) { Ground.prototype.clone = function(x,y) {
return new Ground(sch,x,y,this.rotation); return new Ground(x,y,this.rotation);
} }
// give components a chance to generate a label for their connection(s) // give components a chance to generate a label for their connection(s)
// default action: do nothing // default action: do nothing
Ground.prototype.add_default_labels = function() { Ground.prototype.add_default_labels = function() {
this.connections[0].propagate_label('0'); // canonical label for GND node this.connections[0].propagate_label('0'); // canonical label for GND node
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Resistor // Resistor
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function Resistor(sch,x,y,rotation,name,r) { function Resistor(x,y,rotation,name,r) {
Component.call(this,sch,x,y,rotation); Component.call(this,x,y,rotation);
this.properties['name'] = name; this.properties['name'] = name;
this.properties['r'] = r ? r : '1'; this.properties['r'] = r ? r : '1';
this.add_connection(0,0); this.add_connection(0,0);
...@@ -1836,15 +2088,15 @@ function Resistor(sch,x,y,rotation,name,r) { ...@@ -1836,15 +2088,15 @@ function Resistor(sch,x,y,rotation,name,r) {
this.bounding_box = [-4,0,4,48]; this.bounding_box = [-4,0,4,48];
this.update_coords(); this.update_coords();
this.type = 'r'; this.type = 'r';
} }
Resistor.prototype = new Component(); Resistor.prototype = new Component();
Resistor.prototype.constructor = Resistor; Resistor.prototype.constructor = Resistor;
Resistor.prototype.toString = function() { Resistor.prototype.toString = function() {
return '<Resistor '+this.properties['r']+' ('+this.x+','+this.y+')>'; return '<Resistor '+this.properties['r']+' ('+this.x+','+this.y+')>';
} }
Resistor.prototype.draw = function(c) { Resistor.prototype.draw = function(c) {
this.draw_line(c,0,0,0,12); this.draw_line(c,0,0,0,12);
this.draw_line(c,0,12,4,14); this.draw_line(c,0,12,4,14);
this.draw_line(c,4,14,-4,18); this.draw_line(c,4,14,-4,18);
...@@ -1858,20 +2110,20 @@ Resistor.prototype.draw = function(c) { ...@@ -1858,20 +2110,20 @@ Resistor.prototype.draw = function(c) {
this.draw_text(c,this.properties['r']+'\u03A9',5,24,3,property_size); this.draw_text(c,this.properties['r']+'\u03A9',5,24,3,property_size);
if (this.properties['name']) if (this.properties['name'])
this.draw_text(c,this.properties['name'],-5,24,5,property_size); this.draw_text(c,this.properties['name'],-5,24,5,property_size);
} }
Resistor.prototype.clone = function(sch,x,y) { Resistor.prototype.clone = function(x,y) {
return new Resistor(sch,x,y,this.rotation,'',this.properties['r']); return new Resistor(x,y,this.rotation,this.properties['name'],this.properties['r']);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Capacitor // Capacitor
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function Capacitor(sch,x,y,rotation,name,c) { function Capacitor(x,y,rotation,name,c) {
Component.call(this,sch,x,y,rotation); Component.call(this,x,y,rotation);
this.properties['name'] = name; this.properties['name'] = name;
this.properties['c'] = c ? c : '1p'; this.properties['c'] = c ? c : '1p';
this.add_connection(0,0); this.add_connection(0,0);
...@@ -1879,15 +2131,15 @@ function Capacitor(sch,x,y,rotation,name,c) { ...@@ -1879,15 +2131,15 @@ function Capacitor(sch,x,y,rotation,name,c) {
this.bounding_box = [-8,0,8,48]; this.bounding_box = [-8,0,8,48];
this.update_coords(); this.update_coords();
this.type = 'c'; this.type = 'c';
} }
Capacitor.prototype = new Component(); Capacitor.prototype = new Component();
Capacitor.prototype.constructor = Capacitor; Capacitor.prototype.constructor = Capacitor;
Capacitor.prototype.toString = function() { Capacitor.prototype.toString = function() {
return '<Capacitor '+this.properties['r']+' ('+this.x+','+this.y+')>'; return '<Capacitor '+this.properties['r']+' ('+this.x+','+this.y+')>';
} }
Capacitor.prototype.draw = function(c) { Capacitor.prototype.draw = function(c) {
this.draw_line(c,0,0,0,22); this.draw_line(c,0,0,0,22);
this.draw_line(c,-8,22,8,22); this.draw_line(c,-8,22,8,22);
this.draw_line(c,-8,26,8,26); this.draw_line(c,-8,26,8,26);
...@@ -1896,20 +2148,20 @@ Capacitor.prototype.draw = function(c) { ...@@ -1896,20 +2148,20 @@ Capacitor.prototype.draw = function(c) {
this.draw_text(c,this.properties['c']+'F',9,24,3,property_size); this.draw_text(c,this.properties['c']+'F',9,24,3,property_size);
if (this.properties['name']) if (this.properties['name'])
this.draw_text(c,this.properties['name'],-9,24,5,property_size); this.draw_text(c,this.properties['name'],-9,24,5,property_size);
} }
Capacitor.prototype.clone = function(sch,x,y) { Capacitor.prototype.clone = function(x,y) {
return new Capacitor(sch,x,y,this.rotation,'',this.properties['c']); return new Capacitor(x,y,this.rotation,this.properties['name'],this.properties['c']);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Inductor // Inductor
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function Inductor(sch,x,y,rotation,name,l) { function Inductor(x,y,rotation,name,l) {
Component.call(this,sch,x,y,rotation); Component.call(this,x,y,rotation);
this.properties['name'] = name; this.properties['name'] = name;
this.properties['l'] = l ? l : '1n'; this.properties['l'] = l ? l : '1n';
this.add_connection(0,0); this.add_connection(0,0);
...@@ -1917,15 +2169,15 @@ function Inductor(sch,x,y,rotation,name,l) { ...@@ -1917,15 +2169,15 @@ function Inductor(sch,x,y,rotation,name,l) {
this.bounding_box = [-4,0,5,48]; this.bounding_box = [-4,0,5,48];
this.update_coords(); this.update_coords();
this.type = 'l'; this.type = 'l';
} }
Inductor.prototype = new Component(); Inductor.prototype = new Component();
Inductor.prototype.constructor = Inductor; Inductor.prototype.constructor = Inductor;
Inductor.prototype.toString = function() { Inductor.prototype.toString = function() {
return '<Inductor '+this.properties['l']+' ('+this.x+','+this.y+')>'; return '<Inductor '+this.properties['l']+' ('+this.x+','+this.y+')>';
} }
Inductor.prototype.draw = function(c) { Inductor.prototype.draw = function(c) {
this.draw_line(c,0,0,0,14); this.draw_line(c,0,0,0,14);
this.draw_arc(c,0,18,4,6*Math.PI/4,3*Math.PI/4); this.draw_arc(c,0,18,4,6*Math.PI/4,3*Math.PI/4);
this.draw_arc(c,0,24,4,5*Math.PI/4,3*Math.PI/4); this.draw_arc(c,0,24,4,5*Math.PI/4,3*Math.PI/4);
...@@ -1936,20 +2188,201 @@ Inductor.prototype.draw = function(c) { ...@@ -1936,20 +2188,201 @@ Inductor.prototype.draw = function(c) {
this.draw_text(c,this.properties['l']+'H',6,24,3,property_size); this.draw_text(c,this.properties['l']+'H',6,24,3,property_size);
if (this.properties['name']) if (this.properties['name'])
this.draw_text(c,this.properties['name'],-3,24,5,property_size); this.draw_text(c,this.properties['name'],-3,24,5,property_size);
} }
Inductor.prototype.clone = function(sch,x,y) { Inductor.prototype.clone = function(x,y) {
return new Inductor(sch,x,y,this.rotation,'',this.properties['l']); return new Inductor(x,y,this.rotation,this.properties['name'],this.properties['l']);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Source // Diode
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function Diode(x,y,rotation,name) {
Component.call(this,x,y,rotation);
this.properties['name'] = name;
this.add_connection(0,0); // anode
this.add_connection(0,48); // cathode
this.bounding_box = [-8,0,8,48];
this.update_coords();
this.type = 'd';
}
Diode.prototype = new Component();
Diode.prototype.constructor = Diode;
Diode.prototype.toString = function() {
return '<Diode ('+this.x+','+this.y+')>';
}
Diode.prototype.draw = function(c) {
this.draw_line(c,0,0,0,16);
this.draw_line(c,-8,16,8,16);
this.draw_line(c,-8,16,0,32);
this.draw_line(c,8,16,0,32);
this.draw_line(c,-8,32,8,32);
this.draw_line(c,0,32,0,48);
if (this.properties['name'])
this.draw_text(c,this.properties['name'],-10,24,5,property_size);
}
Diode.prototype.clone = function(x,y) {
return new Diode(x,y,this.rotation,this.properties['name']);
}
////////////////////////////////////////////////////////////////////////////////
//
// N-channel Mosfet
//
////////////////////////////////////////////////////////////////////////////////
function NFet(x,y,rotation,name,sw,sl) {
Component.call(this,x,y,rotation);
this.properties['name'] = name;
this.properties['scaled width'] = sw ? sw : '2';
this.properties['scaled length'] = sl ? sl : '1';
this.add_connection(0,0); // drain
this.add_connection(0,48); // source
this.add_connection(-24,24); // gate
this.bounding_box = [-24,0,8,48];
this.update_coords();
this.type = 'n';
}
NFet.prototype = new Component();
NFet.prototype.constructor = NFet;
NFet.prototype.toString = function() {
return '<NFet '+this.properties['scaled width']+'/'+this.properties['scaled length']+' ('+this.x+','+this.y+')>';
}
NFet.prototype.draw = function(c) {
this.draw_line(c,0,0,0,16);
this.draw_line(c,-8,16,0,16);
this.draw_line(c,-8,16,-8,32);
this.draw_line(c,-8,32,0,32);
this.draw_line(c,0,32,0,48);
this.draw_line(c,-24,24,-12,24);
this.draw_line(c,-12,16,-12,32);
var dim = this.properties['scaled width']+'/'+this.properties['scaled length'];
if (this.properties['name']) {
this.draw_text(c,this.properties['name'],2,22,6,property_size);
this.draw_text(c,dim,2,26,0,property_size);
} else
this.draw_text(c,dim,2,24,3,property_size);
}
NFet.prototype.clone = function(x,y) {
return new NFet(x,y,this.rotation,this.properties['name'],
this.properties['scaled width'],this.properties['scaled length']);
}
////////////////////////////////////////////////////////////////////////////////
//
// P-channel Mosfet
//
////////////////////////////////////////////////////////////////////////////////
function PFet(x,y,rotation,name,sw,sl) {
Component.call(this,x,y,rotation);
this.properties['name'] = name;
this.properties['scaled width'] = sw ? sw : '2';
this.properties['scaled length'] = sl ? sl : '1';
this.add_connection(0,0); // drain
this.add_connection(0,48); // source
this.add_connection(-24,24); // gate
this.bounding_box = [-24,0,8,48];
this.update_coords();
this.type = 'p';
}
PFet.prototype = new Component();
PFet.prototype.constructor = PFet;
PFet.prototype.toString = function() {
return '<PFet '+this.properties['scaled width']+'/'+this.properties['scaled length']+' ('+this.x+','+this.y+')>';
}
PFet.prototype.draw = function(c) {
this.draw_line(c,0,0,0,16);
this.draw_line(c,-8,16,0,16);
this.draw_line(c,-8,16,-8,32);
this.draw_line(c,-8,32,0,32);
this.draw_line(c,0,32,0,48);
this.draw_line(c,-24,24,-16,24);
function Source(sch,x,y,rotation,name,type,value) { this.draw_circle(c,-14,24,2,false);
Component.call(this,sch,x,y,rotation); this.draw_line(c,-12,16,-12,32);
var dim = this.properties['scaled width']+'/'+this.properties['scaled length'];
if (this.properties['name']) {
this.draw_text(c,this.properties['name'],2,22,6,property_size);
this.draw_text(c,dim,2,26,0,property_size);
} else
this.draw_text(c,dim,2,24,3,property_size);
}
PFet.prototype.clone = function(x,y) {
return new PFet(x,y,this.rotation,this.properties['name'],
this.properties['scaled width'],this.properties['scaled length']);
}
////////////////////////////////////////////////////////////////////////////////
//
// Op Amp
//
////////////////////////////////////////////////////////////////////////////////
function OpAmp(x,y,rotation,name,sw,sl) {
Component.call(this,x,y,rotation);
this.properties['name'] = name;
this.add_connection(0,0); // +
this.add_connection(0,16); // -
this.add_connection(48,8); // output
this.bounding_box = [0,-8,48,24];
this.update_coords();
this.type = 'o';
}
OpAmp.prototype = new Component();
OpAmp.prototype.constructor = OpAmp;
OpAmp.prototype.toString = function() {
return '<OpAmp ('+this.x+','+this.y+')>';
}
OpAmp.prototype.draw = function(c) {
// triangle
this.draw_line(c,8,-8,8,24);
this.draw_line(c,8,-8,40,8);
this.draw_line(c,8,24,40,8);
// inputs and output
this.draw_line(c,0,0,8,0);
this.draw_line(c,0,16,8,16);
this.draw_line(c,40,8,48,8);
// + and -
this.draw_line(c,10,0,16,0);
this.draw_line(c,13,-3,13,3);
this.draw_line(c,10,16,16,16);
if (this.properties['name'])
this.draw_text(c,this.properties['name'],32,16,0,property_size);
}
OpAmp.prototype.clone = function(x,y) {
return new OpAmp(x,y,this.rotation,this.properties['name']);
}
////////////////////////////////////////////////////////////////////////////////
//
// Source
//
////////////////////////////////////////////////////////////////////////////////
function Source(x,y,rotation,name,type,value) {
Component.call(this,x,y,rotation);
this.type = type; this.type = type;
this.properties['name'] = name; this.properties['name'] = name;
this.properties['value'] = value ? value : '1'; this.properties['value'] = value ? value : '1';
...@@ -1957,15 +2390,15 @@ function Source(sch,x,y,rotation,name,type,value) { ...@@ -1957,15 +2390,15 @@ function Source(sch,x,y,rotation,name,type,value) {
this.add_connection(0,48); this.add_connection(0,48);
this.bounding_box = [-12,0,12,48]; this.bounding_box = [-12,0,12,48];
this.update_coords(); this.update_coords();
} }
Source.prototype = new Component(); Source.prototype = new Component();
Source.prototype.constructor = Source; Source.prototype.constructor = Source;
Source.prototype.toString = function() { Source.prototype.toString = function() {
return '<'+this.type+'source '+this.properties['params']+' ('+this.x+','+this.y+')>'; return '<'+this.type+'source '+this.properties['params']+' ('+this.x+','+this.y+')>';
} }
Source.prototype.draw = function(c) { Source.prototype.draw = function(c) {
this.draw_line(c,0,0,0,12); this.draw_line(c,0,0,0,12);
this.draw_circle(c,0,24,12,false); this.draw_circle(c,0,24,12,false);
this.draw_line(c,0,36,0,48); this.draw_line(c,0,36,0,48);
...@@ -1988,29 +2421,40 @@ Source.prototype.draw = function(c) { ...@@ -1988,29 +2421,40 @@ Source.prototype.draw = function(c) {
if (this.properties['name']) if (this.properties['name'])
this.draw_text(c,this.properties['name'],-13,24,5,property_size); this.draw_text(c,this.properties['name'],-13,24,5,property_size);
if (this.properties['value']) if (this.properties['value'])
this.draw_text(c,this.properties['value']+(this.type=='v'?'V':'A'),13,24,3,property_size); this.draw_text(c,this.properties['value'],13,24,3,property_size);
} }
Source.prototype.clone = function(sch,x,y) { Source.prototype.clone = function(x,y) {
return new Source(sch,x,y,this.rotation,'',this.type,this.properties['value']); return new Source(x,y,this.rotation,this.properties['name'],this.type,this.properties['value']);
} }
function VSource(sch,x,y,rotation,name,value) { function VSource(x,y,rotation,name,value) {
Source.call(this,sch,x,y,rotation,name,'v',value); Source.call(this,x,y,rotation,name,'v',value);
this.type = 'v'; this.type = 'v';
} }
VSource.prototype = new Component(); VSource.prototype = new Component();
VSource.prototype.constructor = VSource; VSource.prototype.constructor = VSource;
VSource.prototype.toString = Source.prototype.toString; VSource.prototype.toString = Source.prototype.toString;
VSource.prototype.draw = Source.prototype.draw; VSource.prototype.draw = Source.prototype.draw;
VSource.prototype.clone = Source.prototype.clone; VSource.prototype.clone = Source.prototype.clone;
function ISource(sch,x,y,rotation,name,value) { function ISource(x,y,rotation,name,value) {
Source.call(this,sch,x,y,rotation,name,'i',value); Source.call(this,x,y,rotation,name,'i',value);
this.type = 'i'; this.type = 'i';
} }
ISource.prototype = new Component(); ISource.prototype = new Component();
ISource.prototype.constructor = ISource; ISource.prototype.constructor = ISource;
ISource.prototype.toString = Source.prototype.toString; ISource.prototype.toString = Source.prototype.toString;
ISource.prototype.draw = Source.prototype.draw; ISource.prototype.draw = Source.prototype.draw;
ISource.prototype.clone = Source.prototype.clone; ISource.prototype.clone = Source.prototype.clone;
///////////////////////////////////////////////////////////////////////////////
//
// Module definition
//
///////////////////////////////////////////////////////////////////////////////
var module = {
'Schematic': Schematic,
}
return module;
}());
...@@ -67,20 +67,25 @@ function caption_index(now) { ...@@ -67,20 +67,25 @@ function caption_index(now) {
return i-1; return i-1;
} }
function format_time(t)
{
seconds = Math.round(t);
minutes = Math.round(seconds / 60);
hours = Math.round(minutes / 60);
seconds = seconds % 60;
minutes = minutes % 60;
return hours+":"+((minutes < 10)?"0":"")+minutes+":"+((seconds < 10)?"0":"")+(seconds%60);
}
function update_captions(t) { function update_captions(t) {
var i=caption_index(t); var i=caption_index(t);
$("#std_n5").html(caption_at(i-5)); $("#vidtime").html(format_time(ytplayer.getCurrentTime())+'/'+format_time(ytplayer.getDuration()));
$("#std_n4").html(caption_at(i-4)); var j;
$("#std_n3").html(caption_at(i-3)); for(j=1; j<9; j++) {
$("#std_n2").html(caption_at(i-2)); $("#std_n"+j).html(caption_at(i-j));
$("#std_n1").html(caption_at(i-1)); $("#std_p"+j).html(caption_at(i+j));
}
$("#std_0").html(caption_at(i)); $("#std_0").html(caption_at(i));
$("#std_p1").html(caption_at(i+1));
$("#std_p2").html(caption_at(i+2));
$("#std_p3").html(caption_at(i+3));
$("#std_p4").html(caption_at(i+4));
$("#std_p5").html(caption_at(i+5));
$("#std_p6").html(caption_at(i+6));
} }
function title_seek(i) { function title_seek(i) {
...@@ -106,8 +111,8 @@ var ajax_video=function(){}; ...@@ -106,8 +111,8 @@ var ajax_video=function(){};
function onYouTubePlayerReady(playerId) { function onYouTubePlayerReady(playerId) {
ytplayer = document.getElementById("myytplayer"); ytplayer = document.getElementById("myytplayer");
setInterval(updateytplayerInfo, 1000); setInterval(updateytplayerInfo, 500);
setInterval(ajax_video,1000); setInterval(ajax_video,5000);
ytplayer.addEventListener("onStateChange", "onytplayerStateChange"); ytplayer.addEventListener("onStateChange", "onytplayerStateChange");
ytplayer.addEventListener("onError", "onPlayerError"); ytplayer.addEventListener("onError", "onPlayerError");
if((typeof load_id != "undefined") && (load_id != 0)) { if((typeof load_id != "undefined") && (load_id != 0)) {
...@@ -126,21 +131,23 @@ function videoDestroy() { ...@@ -126,21 +131,23 @@ function videoDestroy() {
} }
function log_event(e, d) { function log_event(e, d) {
// CRITICAL TODO: Change to AJAX
//$("#eventlog").append("<br>"); //$("#eventlog").append("<br>");
//$("#eventlog").append(JSON.stringify(e)); //$("#eventlog").append(JSON.stringify(e));
// TODO: Figure out // TODO: Decide if we want seperate tracking server.
// XMLHttpRequest cannot load http://localhost:7000/userlog. Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin. // If so, we need to resolve:
// * AJAX from different domain (XMLHttpRequest cannot load http://localhost:7000/userlog. Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin.)
/*window['console'].log(JSON.stringify(e)); // * Verifying sessions/authentication
$.get("http://localhost:7000/userlog",
{'user':'pmitros', /*window['console'].log(JSON.stringify(e));*/
'key':'key', $.get("/event",
'event_type':'unknown', {
'data':'e'}, "event_type" : e,
"event" : JSON.stringify(d),
"page" : document.URL
},
function(data) { function(data) {
});*/ });
} }
function seek_slide(type,oe,value) { function seek_slide(type,oe,value) {
......
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