Commit 20243182 by Piotr Mitros

cjt fixed schematic bug. Back in the system

parent ef942ac0
......@@ -23,6 +23,7 @@
// properties := {name: value, ...}
// connections := [node, ...] // one per connection point in canoncial order
// node := string
// need a netlist? just use the part's type, properites and connections
// TO DO:
......@@ -36,15 +37,13 @@
// - rotate multiple objects around their center of mass
// - rubber band wires when moving components
// - simulation: operating points, trans, ac analysis, sweeps?
// set up each schematic entry widget
function update_schematics() {
// set up each schematic on the page
var schematics = document.getElementsByClassName('schematic');
for (var i = schematics.length - 1; i >= 0; i--)
if (schematics[i].getAttribute("loaded") != "true") {
new Schematic(schematics[i]);
new schematic.Schematic(schematics[i]);
schematics[i].setAttribute("loaded","true");
}
}
......@@ -60,35 +59,45 @@ function add_schematic_handler(other_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
parts_map = {
schematic = (function() {
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'],
'v': [VSource, 'Voltage source'],
'i': [ISource, 'Current source'],
'r': [Resistor, 'Resistor'],
'c': [Capacitor, 'Capacitor'],
'l': [Inductor, 'Inductor'],
};
// fix cursor bug in Chrome (default behavior: change to text cursor
// whenever a drag is initiated).
document.onselectstart = function() { return false; };
///////////////////////////////////////////////////////////////////////////////
//
// Schematic = diagram + parts bin + status area
//
////////////////////////////////////////////////////////////////////////////////
// setup a schematic by populating the <div> with the appropriate children
function Schematic(input) {
'o': [OpAmp, 'Op Amp'],
'd': [Diode, 'Diode'],
'n': [NFet, 'NFet'],
'p': [PFet, 'PFet'],
};
// fix cursor bug in Chrome (default behavior: change to text cursor
// whenever a drag is initiated).
//document.onselectstart = function() { return false; };
///////////////////////////////////////////////////////////////////////////////
//
// Schematic = diagram + parts bin + status area
//
////////////////////////////////////////////////////////////////////////////////
// setup a schematic by populating the <div> with the appropriate children
function Schematic(input) {
this.div = document.createElement('div');
// set up div so we can position elements inside of it
this.div.style.position = 'relative';
......@@ -98,7 +107,7 @@ function Schematic(input) {
this.scale = 2;
this.origin_x = 0;
this.origin_y = 0;
this.clipboard = null;
this.clipboard = undefined;
// start with a background element with normal positioning
this.background = document.createElement('canvas');
......@@ -135,7 +144,7 @@ function Schematic(input) {
this.canvas.addEventListener('mouseup',schematic_mouse_up,false);
this.canvas.addEventListener('dblclick',schematic_double_click,false);
this.canvas.addEventListener('keydown',schematic_key_down,false);
this.canvas.addEventListener('keypress',schematic_key_press,false);
this.canvas.addEventListener('keyup',schematic_key_up,false);
// toolbar
this.tools = new Array();
......@@ -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['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
// this should make things work on the iPad
this.canvas.addEventListener('click',function(){},false);
......@@ -152,9 +173,17 @@ function Schematic(input) {
this.drawCursor = false;
this.cursor_x = 0;
this.cursor_y = 0;
this.draw_cursor = null;
this.select_rect = null;
this.wire = null;
this.draw_cursor = undefined;
this.select_rect = undefined;
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
this.bg_image = document.createElement('canvas');
......@@ -175,14 +204,16 @@ function Schematic(input) {
for (var i = 0; i < parts.length; i++) {
var part = new Part(this);
var pm = parts_map[parts[i]];
part.set_component(new pm[0](part,0,0,0),pm[1]);
part.set_component(new pm[0](0,0,0),pm[1]);
this.parts_bin.push(part);
}
// add all elements to the DOM
this.div.appendChild(this.background);
for (var i = 0; i < this.toolbar.length; i++)
this.div.appendChild(this.toolbar[i]);
for (var i = 0; i < this.toolbar.length; i++) {
var tool = this.toolbar[i];
if (tool != null) this.div.appendChild(tool);
}
this.div.appendChild(this.canvas);
this.div.appendChild(this.status_div);
for (var i = 0; i < this.parts_bin.length; i++)
......@@ -200,56 +231,15 @@ function Schematic(input) {
// process initial contents of diagram
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;
part_w = 42; // size of a parts bin compartment
part_h = 42;
status_height = 18;
background_margin = 5;
part_w = 42; // size of a parts bin compartment
part_h = 42;
status_height = 18;
// w,h are the dimensions of the canvas, everyone else is positioned accordingly
Schematic.prototype.set_locations = function(w,h) {
// w,h are the dimensions of the canvas, everyone else is positioned accordingly
Schematic.prototype.set_locations = function(w,h) {
// limit the shrinkage factor
w = Math.max(w,120);
h = Math.max(h,120);
......@@ -268,15 +258,21 @@ Schematic.prototype.set_locations = function(w,h) {
// start with tool bar
var top = background_margin;
var max_height = 0;
if (this.toolbar.length > 0) {
tool_left = left;
for (var i = 0; i < this.toolbar.length; i++) {
var img = this.toolbar[i];
img.style.left = tool_left + 'px';
img.style.top = top + 'px';
tool_left += 24; // width + 2*padding + 2*border + gap
var tool = this.toolbar[i];
if (tool == null) { // spacer
tool_left += 8;
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
......@@ -304,7 +300,7 @@ Schematic.prototype.set_locations = function(w,h) {
total_w = part.right();
parts_top = part.bottom() + 2;
if (parts_top + part_h > parts_h_limit) {
parts_left = total_w - 1;
parts_left = total_w + 2;
parts_top = top;
}
}
......@@ -329,80 +325,21 @@ Schematic.prototype.set_locations = function(w,h) {
c.moveTo(w,h-12); c.lineTo(w-12,h);
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);
// create undoable edit record here
}
}
Schematic.prototype.remove_component = function(c) {
Schematic.prototype.remove_component = function(c) {
var index = this.components.indexOf(c);
if (index != -1) this.components.splice(index,1);
}
}
// add connection point to list of connection points at that location
Schematic.prototype.add_connection_point = function(cp) {
// add connection point to list of connection points at that location
Schematic.prototype.add_connection_point = function(cp) {
var cplist = this.connection_points[cp.location];
if (cplist) cplist.push(cp);
else {
......@@ -412,10 +349,10 @@ Schematic.prototype.add_connection_point = function(cp) {
// return list of conincident connection points
return cplist;
}
}
// remove connection point from the list points at the old location
Schematic.prototype.remove_connection_point = function(cp,old_location) {
// remove connection point from the list points at the old location
Schematic.prototype.remove_connection_point = function(cp,old_location) {
// remove cp from list at old location
var cplist = this.connection_points[old_location];
if (cplist) {
......@@ -428,24 +365,24 @@ Schematic.prototype.remove_connection_point = function(cp,old_location) {
delete this.connection_points[old_location];
}
}
}
}
// connection point has changed location: remove, then add
Schematic.prototype.update_connection_point = function(cp,old_location) {
// connection point has changed location: remove, then add
Schematic.prototype.update_connection_point = function(cp,old_location) {
this.remove_connection_point(cp,old_location);
return this.add_connection_point(cp);
}
}
// add a wire to the schematic
Schematic.prototype.add_wire = function(x1,y1,x2,y2) {
var new_wire = new Wire(this,x1,y1,x2,y2);
this.add_component(new_wire);
// add a wire to the schematic
Schematic.prototype.add_wire = function(x1,y1,x2,y2) {
var new_wire = new Wire(x1,y1,x2,y2);
new_wire.add(this);
new_wire.move_end();
return new_wire;
}
}
// see if connection points of component c split any wires
Schematic.prototype.check_wires = function(c) {
// see if connection points of component c split any wires
Schematic.prototype.check_wires = function(c) {
for (var i = this.components.length - 1; i >=0; --i) {
var cc = this.components[i];
if (cc != c) { // don't check a component against itself
......@@ -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)
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
for (var i = this.components.length - 1; i >= 0; --i) {
var component = this.components[i];
......@@ -483,18 +422,18 @@ Schematic.prototype.drag_begin = function() {
this.drag_x = this.cursor_x;
this.drag_y = this.cursor_y;
this.dragging = true;
}
}
Schematic.prototype.drag_end = function() {
Schematic.prototype.drag_end = function() {
// let components know they're done moving
for (var i = this.components.length - 1; i >= 0; --i) {
var component = this.components[i];
if (component.selected) component.move_end();
}
this.dragging = false;
}
}
Schematic.prototype.cut = function() {
Schematic.prototype.cut = function() {
// clear previous contents
this.clipboard = [];
......@@ -509,9 +448,9 @@ Schematic.prototype.cut = function() {
// update diagram view
this.redraw();
}
}
Schematic.prototype.copy = function() {
Schematic.prototype.copy = function() {
// clear previous contents
this.clipboard = [];
......@@ -519,21 +458,23 @@ Schematic.prototype.copy = function() {
for (var i = this.components.length - 1; i >=0; --i) {
var c = this.components[i];
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
// components in the clipboard
var left = null;
var top = null;
var left = undefined;
var top = undefined;
for (var i = this.clipboard.length - 1; i >= 0; --i) {
var c = this.clipboard[i];
left = left ? Math.min(left,c.x) : left;
top = top ? Math.min(top,c.y) : top;
left = left ? Math.min(left,c.x) : c.x;
top = top ? Math.min(top,c.y) : c.y;
}
this.message('cursor '+this.cursor_x+','+this.cursor_y);
// clear current selections
this.unselect_all(-1);
this.redraw_background(); // so we see any components that got unselected
......@@ -542,24 +483,162 @@ Schematic.prototype.paste = function() {
// them relative to the cursor
for (var i = this.clipboard.length - 1; i >= 0; --i) {
var c = this.clipboard[i];
var new_c = c.clone(this,this.cursor_x + (c.x - left),this.cursor_y + (c.y - top));
this.add_component(new_c);
var new_c = c.clone(this.cursor_x + (c.x - left),this.cursor_y + (c.y - top));
new_c.set_select(true);
new_c.add(this);
}
// see what we've wrought
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.
// Also redraws dynamic portion.
Schematic.prototype.redraw_background = function() {
// load diagram from JSON representation
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](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 w = this.bg_image.width;
var h = this.bg_imageheight;
......@@ -590,10 +669,10 @@ Schematic.prototype.redraw_background = function() {
}
this.redraw(); // background changed, redraw on screen
}
}
// redraw what user sees = static image + dynamic parts
Schematic.prototype.redraw = function() {
// redraw what user sees = static image + dynamic parts
Schematic.prototype.redraw = function() {
var c = this.canvas.getContext('2d');
// put static image in the background
......@@ -639,6 +718,18 @@ Schematic.prototype.redraw = function() {
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
if (this.drawCursor && this.draw_cursor) {
//var x = this.cursor_x;
......@@ -646,38 +737,38 @@ Schematic.prototype.redraw = function() {
//this.draw_text(c,'('+x+','+y+')',x+this.grid,y-this.grid,10);
this.draw_cursor(c,this.cursor_x,this.cursor_y);
}
}
}
// draws a cross cursor
Schematic.prototype.cross_cursor = function(c,x,y) {
// draws a cross cursor
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,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.beginPath();
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.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.beginPath();
c.arc((x - this.origin_x)*this.scale,(y - this.origin_y)*this.scale,radius*this.scale,
start_radians,end_radians,anticlockwise);
if (filled) c.fill();
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.fillText(text,(x - this.origin_x) * this.scale,(y - this.origin_y) * this.scale);
}
}
// add method to canvas to compute relative coords for event
HTMLCanvasElement.prototype.relMouseCoords = function(event){
// add method to canvas to compute relative coords for event
HTMLCanvasElement.prototype.relMouseCoords = function(event){
// run up the DOM tree to figure out coords for top,left of canvas
var totalOffsetX = 0;
var totalOffsetY = 0;
......@@ -692,95 +783,108 @@ HTMLCanvasElement.prototype.relMouseCoords = function(event){
// now compute relative position of click within the canvas
this.mouse_x = event.pageX - totalOffsetX;
this.mouse_y = event.pageY - totalOffsetY;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Event handling
//
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Event handling
//
////////////////////////////////////////////////////////////////////////////////
// process special keys here since they don't get delivered correctly on keypress
function schematic_key_down(event) {
// process keystrokes, consuming those that are meaningful to us
function schematic_key_down(event) {
if (!event) event = window.event;
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
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
for (var i = sch.components.length - 1; i >= 0; --i) {
var component = sch.components[i];
if (component.selected) component.delete(1);
}
sch.redraw();
}
// cmd/ctrl x: cut
else if ((sch.ctrlKey || sch.cmdKey) && code == 88) {
sch.cut();
event.preventDefault();
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
if (!event.control && !event.altKey && (char == 'r' || char == 'R')) {
// rotate
for (var i = sch.components.length - 1; i >= 0; --i) {
var component = sch.components[i];
if (component.selected) component.rotate(1);
}
sch.redraw();
// cmd/ctrl c: copy
else if ((sch.ctrlKey || sch.cmdKey) && code == 67) {
sch.copy();
event.preventDefault();
return false;
}
// cut
if ((event.ctrlKey || event.altKey) && char == 'x') {
sch.cut();
// cmd/ctrl v: paste
else if ((sch.ctrlKey || sch.cmdKey) && code == 86) {
sch.paste();
event.preventDefault();
return false;
}
// copy
if ((event.ctrlKey || event.altKey) && char == 'c') {
sch.copy();
// 'r': rotate component
else if (!sch.ctrlKey && !sch.altKey && !sch.cmdKey && code == 82) {
// 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();
return false;
}
// paste
if ((event.ctrlKey || event.altKey) && char == 'v') {
sch.paste();
else return true;
// consume keystroke
sch.redraw();
event.preventDefault();
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;
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
// see if user has selected a new part
if (sch.new_part) {
// revert handler
document.onselectstart = sch.saved_onselectstart;
// grab incoming part, turn off selection of parts bin
var part = sch.new_part;
sch.new_part = null;
sch.new_part = undefined;
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
sch.unselect_all(-1);
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);
// and start dragging it
......@@ -791,17 +895,17 @@ function schematic_mouse_enter(event) {
sch.redraw();
sch.canvas.focus(); // capture key strokes
return false;
}
}
function schematic_mouse_leave(event) {
function schematic_mouse_leave(event) {
if (!event) event = window.event;
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
sch.drawCursor = false;
sch.redraw();
return false;
}
}
function schematic_mouse_down(event) {
function schematic_mouse_down(event) {
if (!event) event = window.event;
else event.preventDefault();
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
......@@ -813,14 +917,6 @@ function schematic_mouse_down(event) {
sch.cursor_x = Math.round(x/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
var cplist = sch.connection_points[sch.cursor_x + ',' + sch.cursor_y];
if (cplist && !event.shiftKey) {
......@@ -854,9 +950,9 @@ function schematic_mouse_down(event) {
sch.redraw_background();
return false;
}
}
function schematic_mouse_move(event) {
function schematic_mouse_move(event) {
if (!event) event = window.event;
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
......@@ -896,9 +992,9 @@ function schematic_mouse_move(event) {
sch.redraw();
return false;
}
}
function schematic_mouse_up(event) {
function schematic_mouse_up(event) {
if (!event) event = window.event;
else event.preventDefault();
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
......@@ -906,7 +1002,7 @@ function schematic_mouse_up(event) {
// drawing a new wire
if (sch.wire) {
var r = sch.wire;
sch.wire = null;
sch.wire = undefined;
if (r[0]!=r[2] || r[1]!=r[3]) {
// insert wire component
......@@ -937,13 +1033,13 @@ function schematic_mouse_up(event) {
sch.components[i].select_rect(s,event.shiftKey);
}
sch.select_rect = null;
sch.select_rect = undefined;
sch.redraw_background();
}
return false;
}
}
function schematic_double_click(event) {
function schematic_double_click(event) {
if (!event) event = window.event;
else event.preventDefault();
var sch = (window.event) ? event.srcElement.schematic : event.target.schematic;
......@@ -957,44 +1053,37 @@ function schematic_double_click(event) {
// see if we double-clicked a component. If so, edit it's properties
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;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Status message and dialogs
//
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Status message and dialogs
//
////////////////////////////////////////////////////////////////////////////////
Schematic.prototype.message = function(message) {
Schematic.prototype.message = function(message) {
this.status.nodeValue = message;
}
}
Schematic.prototype.append_message = function(message) {
Schematic.prototype.append_message = function(message) {
this.status.nodeValue += ' / '+message;
}
}
// set up a dialog with specified title, content and two buttons at
// 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
// callback function is called with the content as an argument (so
// that the values of any fields can be captured).
Schematic.prototype.dialog = function(title,content,callback) {
// set up a dialog with specified title, content and two buttons at
// 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
// callback function is called with the content as an argument (so
// that the values of any fields can be captured).
Schematic.prototype.dialog = function(title,content,callback) {
// create the div for the top level of the dialog, add to DOM
var dialog = document.createElement('div');
dialog.sch = this;
dialog.content = content;
// 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);
dialog.callback = callback;
// div to hold the content
var body = document.createElement('div');
......@@ -1028,127 +1117,248 @@ Schematic.prototype.dialog = function(title,content,callback) {
buttons.style.margin = '10px';
dialog.appendChild(buttons);
// add to DOM
dialog.style.background = 'white';
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);
}
// put into an overlay window
this.window(title,dialog);
}
// callback when user click "Cancel" in a dialog
function dialog_cancel(event) {
// callback when user click "Cancel" in a dialog
function dialog_cancel(event) {
if (!event) event = window.event;
var dialog = (window.event) ? event.srcElement.dialog : event.target.dialog;
// remove the dialog from the top-level div of the schematic
dialog.parentNode.removeChild(dialog);
}
window_close(dialog.win);
}
// callback when user click "OK" in a dialog
function dialog_okay(event) {
// callback when user click "OK" in a dialog
function dialog_okay(event) {
if (!event) event = window.event;
var dialog = (window.event) ? event.srcElement.dialog : event.target.dialog;
// remove the dialog from the top-level div of the schematic
dialog.parentNode.removeChild(dialog);
window_close(dialog.win);
// invoke the callback with the dialog contents as the argument
if (dialog.callback) dialog.callback(dialog.content);
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Toolbar
//
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Draggable, resizeable, closeable window
//
////////////////////////////////////////////////////////////////////////////////
Schematic.prototype.add_tool = function(icon,tip,callback) {
var img = document.createElement('img');
img.src = icon;
img.style.borderWidth = '1px';
img.style.borderStyle = 'solid';
img.style.borderColor = background_style;
img.style.position = 'absolute';
img.style.padding = '2px';
Schematic.prototype.window = function(title,content) {
// create the div for the top level of the window
var win = document.createElement('div');
win.sch = this;
win.content = content;
win.drag_x = undefined;
win.draw_y = undefined;
img.addEventListener('mouseover',tool_enter,false);
img.addEventListener('mouseout',tool_leave,false);
img.addEventListener('click',tool_click,false);
// 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));
img.sch = this;
img.tip = tip;
img.callback = callback;
this.toolbar.push(img);
var close_button = new Image();
close_button.src = close_icon;
close_button.style.cssFloat = 'right';
close_button.addEventListener('click',window_close_button,false);
close_button.win = win;
head.appendChild(close_button);
img.enabled = false;
img.style.opacity = 0.2;
win.appendChild(head);
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) {
var img = this.tools[tname];
img.style.opacity = which ? 1.0 : 0.2;
img.enabled = which;
// move the window
win.left += dx;
win.top += dy;
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 (!which) {
img.style.borderColor = background_style;
img.sch.message('');
tool.style.borderColor = background_style;
tool.sch.message('');
}
}
}
// highlight tool button by turning on border, changing background
function tool_enter(event) {
// highlight tool button by turning on border, changing background
function tool_enter(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.style.borderColor = normal_style;
img.sch.message(img.tip);
img.opacity = 1.0;
if (tool.enabled) {
tool.style.borderColor = normal_style;
tool.sch.message(tool.tip);
tool.opacity = 1.0;
}
}
}
// unhighlight tool button by turning off border, reverting to normal background
function tool_leave(event) {
// unhighlight tool button by turning off border, reverting to normal background
function tool_leave(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.style.borderColor = background_style;
img.sch.message('');
if (tool.enabled) {
tool.style.borderColor = background_style;
tool.sch.message('');
}
}
}
// handle click on a tool
function tool_click(event) {
// handle click on a tool
function tool_click(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=';
///////////////////////////////////////////////////////////////////////////////
//
// Parts bin
//
////////////////////////////////////////////////////////////////////////////////
close_icon = 'data:image/gif;base64,R0lGODlhEAAQAMQAAGtra/f3/62tre/v9+bm787O1pycnHNzc6WlpcXFxd7e3tbW1nt7e7W1te/v74SEhMXFzmNjY+bm5v///87OzgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAAQABAAAAVt4DRMZGmSwRQQBUS9MAwRIyQ5Uq7neEFSDtxOF4T8cobIQaE4RAQ5yjHHiCCSD510QtFGvoCFdppDfBu7bYzy+D7WP5ggAgA8Y3FKwi5IAhIweW1vbBGEWy5rilsFi2tGAwSJixAFBCkpJ5ojIQA7';
// 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.component = null;
this.component = undefined;
this.selected = false;
// set up canvas
......@@ -1170,22 +1380,23 @@ function Part(sch) {
// make the part "clickable" by registering a dummy click handler
// this should make things work on the iPad
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.top = top + 'px';
}
}
Part.prototype.right = function() {
Part.prototype.right = function() {
return this.canvas.offsetLeft + this.canvas.offsetWidth;
}
}
Part.prototype.bottom = function() {
Part.prototype.bottom = function() {
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.tip = 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.redraw();
}
}
Part.prototype.redraw = function(part) {
Part.prototype.redraw = function(part) {
var c = this.canvas.getContext('2d');
// paint background color
......@@ -1208,86 +1419,98 @@ Part.prototype.redraw = function(part) {
c.fillRect(0,0,part_w,part_h);
if (this.component) this.component.draw(c);
}
}
Part.prototype.select = function(which) {
Part.prototype.select = function(which) {
this.selected = which;
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
}
}
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.beginPath();
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.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.beginPath();
c.arc((x - this.origin_x)*this.scale,(y - this.origin_y)*this.scale,radius*this.scale,
start_radians,end_radians,anticlockwise);
if (filled) c.fill();
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
}
}
function part_enter(event) {
function part_enter(event) {
if (!event) event = window.event;
var canvas = (window.event) ? event.srcElement : event.target;
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;
part.sch.message(part.tip+': drag onto diagram to insert');
return false;
}
}
function part_leave(event) {
function part_leave(event) {
if (!event) event = window.event;
var canvas = (window.event) ? event.srcElement : event.target;
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;
part.sch.message('');
return false;
}
}
function part_mouse_down(event) {
function part_mouse_down(event) {
if (!event) event = window.event;
var part = (window.event) ? event.srcElement.part : event.target.part;
part.select(true);
part.sch.new_part = part;
return false;
}
}
function part_mouse_up(event) {
function part_mouse_up(event) {
if (!event) event = window.event;
var part = (window.event) ? event.srcElement.part : event.target.part;
part.select(false);
part.sch.new_part = null;
part.sch.new_part = undefined;
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
function canonicalize(r) {
// ensure left < right, top < bottom
function canonicalize(r) {
var temp;
// canonicalize bounding box
......@@ -1301,18 +1524,18 @@ function canonicalize(r) {
r[1] = r[3];
r[3] = temp;
}
}
}
function between(x,x1,x2) {
function between(x,x1,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]);
}
}
// only works for manhattan rectangles
function intersect(r1,r2) {
// only works for manhattan rectangles
function intersect(r1,r2) {
// look for non-intersection, negate result
var result = !(r2[0] > r1[2] ||
r2[2] < r1[0] ||
......@@ -1321,18 +1544,16 @@ function intersect(r1,r2) {
// if I try to return the above expression, javascript returns undefined!!!
return result;
}
////////////////////////////////////////////////////////////////////////////////
//
// Component base class
//
////////////////////////////////////////////////////////////////////////////////
}
property_size = 5; // point size for Component property text
////////////////////////////////////////////////////////////////////////////////
//
// Component base class
//
////////////////////////////////////////////////////////////////////////////////
function Component(sch,x,y,rotation) {
this.sch = sch;
function Component(x,y,rotation) {
this.sch = undefined;
this.x = x;
this.y = y;
this.rotation = 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.bbox = this.bounding_box; // in absolute coords
this.connections = [];
}
}
Component.prototype.json = function() {
Component.prototype.json = function() {
var props = {};
for (var p in this.properties) props[p] = this.properties[p];
......@@ -1353,13 +1574,13 @@ Component.prototype.json = function() {
var json = [this.type,[this.x, this.y, this.rotation],props,conns];
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));
}
}
Component.prototype.update_coords = function() {
Component.prototype.update_coords = function() {
var x = this.x;
var y = this.y;
......@@ -1374,31 +1595,31 @@ Component.prototype.update_coords = function() {
// update connections
for (var i = this.connections.length - 1; i >= 0; --i)
this.connections[i].update_location();
}
}
Component.prototype.rotate = function(amount) {
Component.prototype.rotate = function(amount) {
var old_rotation = this.rotation;
this.rotation = (this.rotation + amount) % 8;
this.update_coords();
// create an undoable edit record here
// using old_rotation
}
}
Component.prototype.move_begin = function() {
Component.prototype.move_begin = function() {
// remember where we started this move
this.move_x = this.x;
this.move_y = this.y;
}
}
Component.prototype.move = function(dx,dy) {
Component.prototype.move = function(dx,dy) {
// update coordinates
this.x += dx;
this.y += dy;
this.update_coords();
}
}
Component.prototype.move_end = function() {
Component.prototype.move_end = function() {
var dx = this.x - this.move_x;
var dy = this.y - this.move_y;
......@@ -1407,9 +1628,15 @@ Component.prototype.move_end = function() {
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
for (var i = this.connections.length - 1; i >= 0; --i) {
var cp = this.connections[i];
......@@ -1418,45 +1645,46 @@ Component.prototype.delete = function() {
// remove component from schematic
this.sch.remove_component(this);
this.sch = undefined;
// create an undoable edit record here
}
}
Component.prototype.transform_x = function(x,y) {
Component.prototype.transform_x = function(x,y) {
var rot = this.rotation;
if (rot == 0 || rot == 6) return x;
else if (rot == 1 || rot == 5) return -y;
else if (rot == 2 || rot == 4) return -x;
else return y;
}
}
Component.prototype.transform_y = function(x,y) {
Component.prototype.transform_y = function(x,y) {
var rot = this.rotation;
if (rot == 1 || rot == 7) return x;
else if (rot == 2 || rot == 6) return -y;
else if (rot == 3 || rot == 5) return -x;
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;
var nx1 = this.transform_x(x1,y1) + this.x;
var ny1 = this.transform_y(x1,y1) + this.y;
var nx2 = this.transform_x(x2,y2) + this.x;
var ny2 = this.transform_y(x2,y2) + this.y;
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;
else c.strokeStyle = this.selected ? selected_style : normal_style;
var nx = this.transform_x(x,y) + this.x;
var ny = this.transform_y(x,y) + this.y;
this.sch.draw_arc(c,nx,ny,radius,0,2*Math.PI,false,1,filled);
}
}
rot_angle = [
rot_angle = [
0.0, // NORTH (identity)
Math.PI/2, // EAST (rot270)
Math.PI, // SOUTH (rot180)
......@@ -1465,22 +1693,22 @@ rot_angle = [
Math.PI/2, // REAST (int-neg)
Math.PI, // RSOUTH (negx)
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;
var nx = this.transform_x(x,y) + this.x;
var ny = this.transform_y(x,y) + this.y;
this.sch.draw_arc(c,nx,ny,radius,
start_radians+rot_angle[this.rotation],end_radians+rot_angle[this.rotation],
false,1,false);
}
}
Component.prototype.draw = function(c) {
}
Component.prototype.draw = function(c) {
}
// result of rotating an alignment [rot*9 + align]
aOrient = [
// result of rotating an alignment [rot*9 + align]
aOrient = [
0, 1, 2, 3, 4, 5, 6, 7, 8, // NORTH (identity)
2, 5, 8, 1, 4, 7, 0, 3, 6, // EAST (rot270)
8, 7, 6, 5, 4, 3, 2, 1, 0, // SOUTH (rot180)
......@@ -1489,60 +1717,63 @@ aOrient = [
8, 5, 2, 7, 4, 1, 6, 3, 0, // REAST (int-neg)
6, 7, 8, 3, 4, 5, 0, 1, 2, // RSOUTH (negx)
0, 3, 6, 1, 4, 7, 2, 5, 8 // RWEST (int-pos)
];
];
textAlign = [
textAlign = [
'left', 'center', 'right',
'left', 'center', 'right',
'left', 'center', 'right'
];
];
textBaseline = [
textBaseline = [
'top', 'top', 'top',
'middle', 'middle', 'middle',
'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];
c.textAlign = textAlign[a];
c.textBaseline = textBaseline[a];
if (fill == undefined)
c.fillStyle = this.selected ? selected_style : normal_style;
else
c.fillStyle = fill;
this.sch.draw_text(c,text,
this.transform_x(x,y) + this.x,
this.transform_y(x,y) + this.y,
size);
}
}
Component.prototype.set_select = function(which) {
Component.prototype.set_select = function(which) {
if (which != this.selected) {
this.selected = which;
// 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;
if (inside(this.bbox,x,y)) {
this.set_select(shiftKey ? !this.selected : true);
return true;
} else return false;
}
}
Component.prototype.select_rect = function(s) {
Component.prototype.select_rect = function(s) {
this.was_previously_selected = this.selected;
if (intersect(this.bbox,s))
this.set_select(true);
}
}
// if connection point of component c bisects the
// wire represented by this compononent, return that
// connection point. Otherwise return null.
Component.prototype.bisect = function(c) {
// if connection point of component c bisects the
// wire represented by this compononent, return that
// connection point. Otherwise return null.
Component.prototype.bisect = function(c) {
return null;
}
}
Component.prototype.edit_properties = function(x,y) {
Component.prototype.edit_properties = function(x,y) {
if (inside(this.bbox,x,y)) {
var content = document.createElement('table');
content.style.marginBotton = '5px';
......@@ -1553,7 +1784,8 @@ Component.prototype.edit_properties = function(x,y) {
var label = document.createTextNode(i + ': ');
var field = document.createElement('input');
field.type = 'text';
field.value = this.properties[i];
var pvalue = this.properties[i];
field.value = pvalue ? pvalue : '';
field.size = 10;
content.fields.push([i,field]);
......@@ -1570,74 +1802,75 @@ Component.prototype.edit_properties = function(x,y) {
}
var component = this; // capture in closure below
this.sch.dialog('Edit Properties',content,function(content) {
var fields = content.fields;
for (var i = fields.length - 1; i >= 0; i--)
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;
} else return false;
}
}
// clear the labels on all connections
Component.prototype.clear_labels = function() {
// clear the labels on all connections
Component.prototype.clear_labels = function() {
for (var i = this.connections.length - 1; i >=0; --i) {
this.connections[i].clear_label();
}
}
}
// default action: don't propagate label
Component.prototype.propagate_label = function(label) {
}
// default action: don't propagate label
Component.prototype.propagate_label = function(label) {
}
// give components a chance to generate default labels for their connection(s)
// default action: do nothing
Component.prototype.add_default_labels = function() {
}
// give components a chance to generate default labels for their connection(s)
// default action: do nothing
Component.prototype.add_default_labels = function() {
}
// component should generate labels for all unlabeled connections
Component.prototype.label_connections = function() {
// component should generate labels for all unlabeled connections
Component.prototype.label_connections = function() {
for (var i = this.connections.length - 1; i >=0; --i) {
var cp = this.connections[i];
if (!cp.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.offset_x = x;
this.offset_y = y;
this.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()+'>';
}
}
ConnectionPoint.prototype.json = function() {
ConnectionPoint.prototype.json = function() {
return this.label;
}
}
ConnectionPoint.prototype.clear_label = function() {
this.label = null;
}
ConnectionPoint.prototype.clear_label = function() {
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...
if (this.label == null) {
if (this.label === undefined) {
// label this connection point
this.label = label;
......@@ -1647,9 +1880,9 @@ ConnectionPoint.prototype.propagate_label = function(label) {
// possibly label other cp's for this device?
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
var old_location = this.location;
var parent = this.parent;
......@@ -1660,29 +1893,48 @@ ConnectionPoint.prototype.update_location = function() {
this.location = nx + ',' + ny;
// add ourselves to the connection list for the new location
if (parent.sch)
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;
}
}
ConnectionPoint.prototype.draw = function(c,n) {
ConnectionPoint.prototype.draw = function(c,n) {
if (n != 2)
this.parent.draw_circle(c,this.offset_x,this.offset_y,connection_point_radius,n > 2);
}
}
////////////////////////////////////////////////////////////////////////////////
//
// Wire
//
////////////////////////////////////////////////////////////////////////////////
ConnectionPoint.prototype.display_voltage = function(c,vmap) {
var v = vmap[this.label];
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
Component.call(this,sch,x1,y1,0);
Component.call(this,x1,y1,0);
this.dx = x2 - x1;
this.dy = y2 - y1;
this.add_connection(0,0);
......@@ -1700,28 +1952,28 @@ function Wire(sch,x1,y1,x2,y2) {
// used in selection calculations
this.len = Math.sqrt(this.dx*this.dx + this.dy*this.dy);
}
Wire.prototype = new Component();
Wire.prototype.constructor = Wire;
}
Wire.prototype = new Component();
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)+')>';
}
}
Wire.prototype.json = function() {
Wire.prototype.json = function() {
var json = ['w',[this.x, this.y, this.x+this.dx, this.y+this.dy]];
return json;
}
}
Wire.prototype.draw = function(c) {
Wire.prototype.draw = function(c) {
this.draw_line(c,0,0,this.dx,this.dy);
}
}
Wire.prototype.clone = function(sch,x,y) {
return new Wire(sch,x,y,x+this.dx,y+this.dy);
}
Wire.prototype.clone = function(x,y) {
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
if (inside(this.bbox,x,y)) {
// compute distance between x,y and nearst point on line
......@@ -1730,28 +1982,28 @@ Wire.prototype.near = function(x,y) {
if (D <= near_distance) return true;
}
return false;
}
}
Wire.prototype.select = function(x,y,shiftKey) {
Wire.prototype.select = function(x,y,shiftKey) {
this.was_previously_selected = this.selected;
if (this.near(x,y)) {
this.set_select(shiftKey ? !this.selected : true);
return true;
} else return false;
}
}
// selection rectangle selects wire only if it includes
// one of the end points
Wire.prototype.select_rect = function(s) {
// selection rectangle selects wire only if it includes
// one of the end points
Wire.prototype.select_rect = function(s) {
this.was_previously_selected = this.selected;
if (inside(s,this.x,this.y) || inside(s,this.x+this.dx,this.y+this.dy))
this.set_select(true);
}
}
// if connection point of component c bisects the
// wire represented by this compononent, return that
// connection point. Otherwise return null.
Wire.prototype.bisect = function(c) {
// if connection point of component c bisects the
// wire represented by this compononent, return that
// connection point. Otherwise return null.
Wire.prototype.bisect = function(c) {
for (var i = c.connections.length - 1; i >= 0; --i) {
var cp = c.connections[i];
var x = cp.x;
......@@ -1768,67 +2020,67 @@ Wire.prototype.bisect = function(c) {
}
}
return null;
}
}
Wire.prototype.move_end = function() {
Wire.prototype.move_end = function() {
this.sch.check_wires(this);
}
}
// wires "conduct" their label to the other end
Wire.prototype.propagate_label = function(label) {
// wires "conduct" their label to the other end
Wire.prototype.propagate_label = function(label) {
// don't worry about relabeling a cp, it won't recurse!
this.connections[0].propagate_label(label);
this.connections[1].propagate_label(label);
}
}
// some actual component will start the labeling of electrical nodes,
// so do nothing here
Wire.prototype.label_connections = function() {
}
// some actual component will start the labeling of electrical nodes,
// so do nothing here
Wire.prototype.label_connections = function() {
}
////////////////////////////////////////////////////////////////////////////////
//
// Ground
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Ground
//
////////////////////////////////////////////////////////////////////////////////
function Ground(sch,x,y,rotation) {
Component.call(this,sch,x,y,rotation);
function Ground(x,y,rotation) {
Component.call(this,x,y,rotation);
this.add_connection(0,0);
this.bounding_box = [-6,0,6,8];
this.update_coords();
this.type = 'g';
}
Ground.prototype = new Component();
Ground.prototype.constructor = Ground;
}
Ground.prototype = new Component();
Ground.prototype.constructor = Ground;
Ground.prototype.toString = function() {
Ground.prototype.toString = function() {
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,-6,8,6,8);
}
}
Ground.prototype.clone = function(sch,x,y) {
return new Ground(sch,x,y,this.rotation);
}
Ground.prototype.clone = function(x,y) {
return new Ground(x,y,this.rotation);
}
// give components a chance to generate a label for their connection(s)
// default action: do nothing
Ground.prototype.add_default_labels = function() {
// give components a chance to generate a label for their connection(s)
// default action: do nothing
Ground.prototype.add_default_labels = function() {
this.connections[0].propagate_label('0'); // canonical label for GND node
}
}
////////////////////////////////////////////////////////////////////////////////
//
// Resistor
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Resistor
//
////////////////////////////////////////////////////////////////////////////////
function Resistor(sch,x,y,rotation,name,r) {
Component.call(this,sch,x,y,rotation);
function Resistor(x,y,rotation,name,r) {
Component.call(this,x,y,rotation);
this.properties['name'] = name;
this.properties['r'] = r ? r : '1';
this.add_connection(0,0);
......@@ -1836,15 +2088,15 @@ function Resistor(sch,x,y,rotation,name,r) {
this.bounding_box = [-4,0,4,48];
this.update_coords();
this.type = 'r';
}
Resistor.prototype = new Component();
Resistor.prototype.constructor = Resistor;
}
Resistor.prototype = new Component();
Resistor.prototype.constructor = Resistor;
Resistor.prototype.toString = function() {
Resistor.prototype.toString = function() {
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,12,4,14);
this.draw_line(c,4,14,-4,18);
......@@ -1858,20 +2110,20 @@ Resistor.prototype.draw = function(c) {
this.draw_text(c,this.properties['r']+'\u03A9',5,24,3,property_size);
if (this.properties['name'])
this.draw_text(c,this.properties['name'],-5,24,5,property_size);
}
}
Resistor.prototype.clone = function(sch,x,y) {
return new Resistor(sch,x,y,this.rotation,'',this.properties['r']);
}
Resistor.prototype.clone = function(x,y) {
return new Resistor(x,y,this.rotation,this.properties['name'],this.properties['r']);
}
////////////////////////////////////////////////////////////////////////////////
//
// Capacitor
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Capacitor
//
////////////////////////////////////////////////////////////////////////////////
function Capacitor(sch,x,y,rotation,name,c) {
Component.call(this,sch,x,y,rotation);
function Capacitor(x,y,rotation,name,c) {
Component.call(this,x,y,rotation);
this.properties['name'] = name;
this.properties['c'] = c ? c : '1p';
this.add_connection(0,0);
......@@ -1879,15 +2131,15 @@ function Capacitor(sch,x,y,rotation,name,c) {
this.bounding_box = [-8,0,8,48];
this.update_coords();
this.type = 'c';
}
Capacitor.prototype = new Component();
Capacitor.prototype.constructor = Capacitor;
}
Capacitor.prototype = new Component();
Capacitor.prototype.constructor = Capacitor;
Capacitor.prototype.toString = function() {
Capacitor.prototype.toString = function() {
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,-8,22,8,22);
this.draw_line(c,-8,26,8,26);
......@@ -1896,20 +2148,20 @@ Capacitor.prototype.draw = function(c) {
this.draw_text(c,this.properties['c']+'F',9,24,3,property_size);
if (this.properties['name'])
this.draw_text(c,this.properties['name'],-9,24,5,property_size);
}
}
Capacitor.prototype.clone = function(sch,x,y) {
return new Capacitor(sch,x,y,this.rotation,'',this.properties['c']);
}
Capacitor.prototype.clone = function(x,y) {
return new Capacitor(x,y,this.rotation,this.properties['name'],this.properties['c']);
}
////////////////////////////////////////////////////////////////////////////////
//
// Inductor
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Inductor
//
////////////////////////////////////////////////////////////////////////////////
function Inductor(sch,x,y,rotation,name,l) {
Component.call(this,sch,x,y,rotation);
function Inductor(x,y,rotation,name,l) {
Component.call(this,x,y,rotation);
this.properties['name'] = name;
this.properties['l'] = l ? l : '1n';
this.add_connection(0,0);
......@@ -1917,15 +2169,15 @@ function Inductor(sch,x,y,rotation,name,l) {
this.bounding_box = [-4,0,5,48];
this.update_coords();
this.type = 'l';
}
Inductor.prototype = new Component();
Inductor.prototype.constructor = Inductor;
}
Inductor.prototype = new Component();
Inductor.prototype.constructor = Inductor;
Inductor.prototype.toString = function() {
Inductor.prototype.toString = function() {
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_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);
......@@ -1936,20 +2188,201 @@ Inductor.prototype.draw = function(c) {
this.draw_text(c,this.properties['l']+'H',6,24,3,property_size);
if (this.properties['name'])
this.draw_text(c,this.properties['name'],-3,24,5,property_size);
}
}
Inductor.prototype.clone = function(sch,x,y) {
return new Inductor(sch,x,y,this.rotation,'',this.properties['l']);
}
Inductor.prototype.clone = function(x,y) {
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) {
Component.call(this,sch,x,y,rotation);
this.draw_circle(c,-14,24,2,false);
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.properties['name'] = name;
this.properties['value'] = value ? value : '1';
......@@ -1957,15 +2390,15 @@ function Source(sch,x,y,rotation,name,type,value) {
this.add_connection(0,48);
this.bounding_box = [-12,0,12,48];
this.update_coords();
}
Source.prototype = new Component();
Source.prototype.constructor = Source;
}
Source.prototype = new Component();
Source.prototype.constructor = Source;
Source.prototype.toString = function() {
Source.prototype.toString = function() {
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_circle(c,0,24,12,false);
this.draw_line(c,0,36,0,48);
......@@ -1988,29 +2421,40 @@ Source.prototype.draw = function(c) {
if (this.properties['name'])
this.draw_text(c,this.properties['name'],-13,24,5,property_size);
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) {
return new Source(sch,x,y,this.rotation,'',this.type,this.properties['value']);
}
Source.prototype.clone = function(x,y) {
return new Source(x,y,this.rotation,this.properties['name'],this.type,this.properties['value']);
}
function VSource(sch,x,y,rotation,name,value) {
Source.call(this,sch,x,y,rotation,name,'v',value);
function VSource(x,y,rotation,name,value) {
Source.call(this,x,y,rotation,name,'v',value);
this.type = 'v';
}
VSource.prototype = new Component();
VSource.prototype.constructor = VSource;
VSource.prototype.toString = Source.prototype.toString;
VSource.prototype.draw = Source.prototype.draw;
VSource.prototype.clone = Source.prototype.clone;
function ISource(sch,x,y,rotation,name,value) {
Source.call(this,sch,x,y,rotation,name,'i',value);
}
VSource.prototype = new Component();
VSource.prototype.constructor = VSource;
VSource.prototype.toString = Source.prototype.toString;
VSource.prototype.draw = Source.prototype.draw;
VSource.prototype.clone = Source.prototype.clone;
function ISource(x,y,rotation,name,value) {
Source.call(this,x,y,rotation,name,'i',value);
this.type = 'i';
}
ISource.prototype = new Component();
ISource.prototype.constructor = ISource;
ISource.prototype.toString = Source.prototype.toString;
ISource.prototype.draw = Source.prototype.draw;
ISource.prototype.clone = Source.prototype.clone;
}
ISource.prototype = new Component();
ISource.prototype.constructor = ISource;
ISource.prototype.toString = Source.prototype.toString;
ISource.prototype.draw = Source.prototype.draw;
ISource.prototype.clone = Source.prototype.clone;
///////////////////////////////////////////////////////////////////////////////
//
// Module definition
//
///////////////////////////////////////////////////////////////////////////////
var module = {
'Schematic': Schematic,
}
return module;
}());
......@@ -67,20 +67,25 @@ function caption_index(now) {
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) {
var i=caption_index(t);
$("#std_n5").html(caption_at(i-5));
$("#std_n4").html(caption_at(i-4));
$("#std_n3").html(caption_at(i-3));
$("#std_n2").html(caption_at(i-2));
$("#std_n1").html(caption_at(i-1));
$("#vidtime").html(format_time(ytplayer.getCurrentTime())+'/'+format_time(ytplayer.getDuration()));
var j;
for(j=1; j<9; j++) {
$("#std_n"+j).html(caption_at(i-j));
$("#std_p"+j).html(caption_at(i+j));
}
$("#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) {
......@@ -106,8 +111,8 @@ var ajax_video=function(){};
function onYouTubePlayerReady(playerId) {
ytplayer = document.getElementById("myytplayer");
setInterval(updateytplayerInfo, 1000);
setInterval(ajax_video,1000);
setInterval(updateytplayerInfo, 500);
setInterval(ajax_video,5000);
ytplayer.addEventListener("onStateChange", "onytplayerStateChange");
ytplayer.addEventListener("onError", "onPlayerError");
if((typeof load_id != "undefined") && (load_id != 0)) {
......@@ -126,21 +131,23 @@ function videoDestroy() {
}
function log_event(e, d) {
// CRITICAL TODO: Change to AJAX
//$("#eventlog").append("<br>");
//$("#eventlog").append(JSON.stringify(e));
// TODO: Figure out
// 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));
$.get("http://localhost:7000/userlog",
{'user':'pmitros',
'key':'key',
'event_type':'unknown',
'data':'e'},
// TODO: Decide if we want seperate tracking server.
// 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.)
// * Verifying sessions/authentication
/*window['console'].log(JSON.stringify(e));*/
$.get("/event",
{
"event_type" : e,
"event" : JSON.stringify(d),
"page" : document.URL
},
function(data) {
});*/
});
}
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