Check-in [1514d83c29]
Not logged in
Overview
SHA1:1514d83c2900c9083ef9ecce78ed71651cd0119f
Date: 2013-01-02 17:22:01
User: Derek
Comment:started moving around code in files to factor stuff.
Timelines: family | ancestors | descendants | both | trunk
Other Links: files | file ages | folders | manifest
Tags And Properties
Context
2013-01-03
20:26
[e9599cdacc] starting basic work on wireframe_model.js wrote some test cases in seperate page (user: Derek, tags: trunk)
2013-01-02
17:22
[1514d83c29] started moving around code in files to factor stuff. (user: Derek, tags: trunk)
2012-12-30
22:51
[e34dd1120f] got rotation of selected points to work (user: Jacob, tags: trunk)
Changes

Modified draw.html from [2d990104f4] to [00f152a683].

     1      1   <html>
     2      2   <script src='matrix_vector_lib.js' ></script>
            3  +<script src='wireframe_model.js'></script>
     3      4   
     4      5   <script>
     5         -
            6  +/*
     6      7   //  TODO rotate selected points
     7      8   //  TODO control with zoom rotate or pan means that action is only applied to the camera not the selected points.
     8      9   //  TODO clicking and dragging on a line allows you to place a point somewhere on that line.  Perhaps with the modifier key it would break up the existing line into line segments.
     9     10   //  TODO when lines are deleted, readd the solo points to the solo_points list
    10     11   //  TODO z for undo shift + z for redo.
    11     12   //  TODO 
    12     13   //  TODO drop down minimalist menu in top right of canvas.
................................................................................
    26     27   //  even in future code changes, ie statically configured with an initial.
    27     28   //
    28     29   //
    29     30   
    30     31   
    31     32   
    32     33   var canvas;
    33         -var origin = [0, 0, 0];
           34  +
    34     35   var frame_rate = 20;
    35     36   var saveTimeout = 10*1000;
    36     37   
    37     38   
    38     39   
    39         -// to prevent camera from rotating beyond straight vertical
    40         -var vertical_camera_rotation = 0;
    41         -
    42         -var zoom_scale = 2.667;  // scalar zoom property.
    43         -var zoom_dist = 1;
    44     40   var last_zoom_dist = 1;
    45     41   var zoom_factor = 0.83;
    46     42   
    47     43   
    48     44   
    49     45   // camera animation
    50     46   var max_move_delta = 0.01;
................................................................................
    51     47   var max_angle_delta = Math.PI;
    52     48   
    53     49   var delta_horizontal_angle = 0;
    54     50   var delta_vertical_angle = 0;
    55     51   var delta_position = [0,0,0];
    56     52   
    57     53   
    58         -//  Use to rotate selected points about a vector line
    59         -var selection_rotation_axis = null;
    60         -var rotation_center = [0,0,0];
    61         -var zoom_center = origin.slice(0);
    62         -
    63         -var selected_points_start_loc = [];
    64         -
    65         -
    66         -
    67         -
    68         -
    69         -var points = []; //global
    70         -var solo_points = {};
    71         -var lines = [];
    72         -
    73         -var point_projections = [];
    74         -var line_midpoint_projections = [];
    75         -
    76         -var highlight_object = null;
    77         -var mouse_dragging = false;
    78         -var last_mouse_down = null;
    79         -var mouse_loc = null;
    80         -
    81     54   
    82     55   
    83     56   var selected_points = [];
    84     57   var selected_lines = [];
    85     58   
    86     59   var helpmsg = //"3D drawing tool\n\n" +
    87     60                 "Select     Left Click\n" +
................................................................................
   500    473       if(vertical_camera_rotation + theta < -Math.PI/2)
   501    474           theta = -Math.PI/2 - vertical_camera_rotation;
   502    475           
   503    476       vertical_camera_rotation += theta;
   504    477       
   505    478       rotateView([1, 0, 0], theta, true); }
   506    479       
   507         -    
   508         -function moveCamera(offset, cameracentric){
   509         -    if(cameracentric) offset = matrix_mult(view_transform, [offset])[0];
   510         -    //alert("offset: " + offset);
   511         -    vector_add(origin, offset, origin); }
   512         -    
   513         -
   514         -// converts a point from three space to the canvas plane.
   515         -// Note: because of depth perspective, this conversion is not
   516         -// easy to define if the point lies behind the camera.
   517         -// There are two options:
   518         -// When drawing a line, another colinear point in front of the camera may be provided
   519         -// to help find an alertnate point.
   520         -// if both points lie behind the camera or the colinear_point is not provided,
   521         -// this function will return null.
   522         - 
   523         -function project(canvas, xyz, view_transform, origin, colinear_point){
   524         -    if(!xyz) return null;  // point has been deleted or does not exist
   525         -    
   526         -    var w = canvas.width;
   527         -    var h = canvas.height;
   528         -    
   529         -    var scale = Math.min(w, h);
   530         -    
   531         -    var v = xyz.slice(0);
   532         -    if(origin) v = vector_minus(v, origin, v);
   533         -    
   534         -    var z = vector_dot(view_transform[2], v);
   535         -    
   536         -    if(z <= -zoom_dist){
   537         -        if(!colinear_point) return null;
   538         -        
   539         -        var v2 = colinear_point.slice(0);
   540         -        if(origin) vector_minus(v2, origin, v2);
   541         -        
   542         -        var z2 = vector_dot(view_transform[2], v2);
   543         -        
   544         -        if(z2 < 0) return null;
   545         -        
   546         -        // get the coefficients for a complex combination.
   547         -        // t*z + (1-t)*z2 = 0.0002  -- z of new point is just barely infront of the camera.
   548         -        
   549         -        var t = (0.0002 - z2)/(z - z2);  // no division by zero, z is negative, z2 is positive
   550         -        
   551         -        vector_add(vector_scale(v, t, v),
   552         -                   vector_scale(v2, 1-t, v2),
   553         -                   v);
   554         -                   
   555         -        z = vector_dot(view_transform[2], v); }
   556         -
   557         -        
   558         -    var scale2 = zoom_scale * scale / (zoom_dist + z);
   559         -    
   560         -    return [ scale2 * vector_dot(view_transform[0], v) + 0.5 * w,
   561         -             scale2 * vector_dot(view_transform[1], v) + 0.5 * h ]; }
   562         -   
   563         -
   564         -   
   565         -//  removes deleted points and lines
   566         -//  works with global objects.
   567         -
   568         -function cleanupDeletedPoints(){
   569         -    var newpoints = [];
   570         -    var newlines = [];
   571         -    var pointmap = {};
   572         -    
   573         -    var j = 0;
   574         -    
   575         -    for(var i = 0; i < points.length; ++i){
   576         -       
   577         -       if(points[i]){
   578         -           newpoints.push(points[i]);
   579         -           pointmap[i] = j;
   580         -           ++j; }
   581         -           
   582         -       else{
   583         -           pointmap[i] = -1; }}
   584         -    
   585         -    
   586         -    for(var i = 0; i < lines.length; ++i){
   587         -        if(!lines[i]) continue;
   588         -        var a = lines[i][0];
   589         -        var b = lines[i][1];
   590         -        
   591         -        if(pointmap[a] != -1 && pointmap[b] != -1){
   592         -            newlines.push([ pointmap[a], pointmap[b] ]); }}
   593         -    
   594         -    points = newpoints;
   595         -    lines = newlines; }
   596         -        
   597         -
   598         -function addPoint(pt){
   599         -    points.push(pt.slice(0));
   600         -    var index = points.length - 1;
   601         -    solo_points[index] = true;
   602         -    return index; }
   603         -    
   604         -    
   605         -function addLine(pt0, pt1){
   606         -    delete solo_points[pt0];
   607         -    delete solo_points[pt1];
   608         -    lines.push([pt0, pt1]);
   609         -    return lines.length - 1; }    
   610         -    
   611         -    
   612         -    
   613         -function draw(canvas){
   614         -    var ctx = canvas.getContext('2d');
   615         -    var w = canvas.width;
   616         -    var h = canvas.height;
   617         -    
   618         -    // ctx.fillStyle = "rgba(100, 100, 100, .05)";
   619         -    
   620         -    
   621         -    ctx.beginPath();
   622         -    
   623         -    for(var i = 0; i < lines.length; ++i){
   624         -        var line = lines[i];
   625         -        if(!line) continue;
   626         -        //draw line
   627         -        
   628         -        var pt0 = points[line[0]];
   629         -        var pt1 = points[line[1]];
   630         -            
   631         -        var _pt0 = point_projections[line[0]];
   632         -        var _pt1 = point_projections[line[1]];
   633         -        
   634         -        //  finish the line even if one point is on wrong side of viewer.
   635         -        if(!_pt0 && _pt1){
   636         -            _pt0 = project(canvas, pt0, view_transform, origin, pt1); }
   637         -        else if(_pt0 && !_pt1){
   638         -            _pt1 = project(canvas, pt1, view_transform, origin, pt0); }
   639         -            
   640         -        if(!_pt0 || !_pt1) continue;
   641         -        
   642         -        var x0 = _pt0[0];
   643         -        var y0 = _pt0[1];
   644         -
   645         -        var x1 = _pt1[0];
   646         -        var y1 = _pt1[1];
   647         -        
   648         -        ctx.moveTo(x0, y0);
   649         -        ctx.lineTo(x1, y1); }
   650         -    
   651         -    ctx.clearRect(0, 0, w, h);
   652         -	
   653         -    
   654         -	ctx.strokeStyle = "rgba(0, 0, 0, 0.9)";
   655         -    ctx.stroke();
   656         -    
   657         -	ctx.fillStyle = "rgba(0, 0, 0, 0.9)";
   658         -	
   659         -    for(var k in solo_points){
   660         -       var pt = point_projections[k];
   661         -       if(pt) ctx.fillRect(pt[0] - 2, pt[1] - 2, 4, 4); }
   662         -        
   663         -	//
   664         -	//  The following drawing of axes is to help provide an perspective
   665         -	//  The nuances of this draw behavior are created to provide visual cues for interpreting perspective and depth.
   666         -	//  
   667         -	//  Because we use a 2d canvas library for the drawing the rest of the points/shapes after projection, these visual information nuances are important.
   668         -	//
   669         -
   670         -	// axis lines and dots
   671         -	
   672         -    var faxis_pt = project(canvas, vector_add(origin, [0, 0, FORWARD_AXIS_LEN]), view_transform, origin);
   673         -	var vaxis_pt = project(canvas, vector_add(origin, [0, -VERTICAL_AXIS_LEN, 0]), view_transform, origin);
   674         -	
   675         -	ctx.fillStyle = AXIS_POINT_COLOR;
   676         -	
   677         -	var fdot = vector_dot(view_transform[2], [0, 0, 1]);
   678         -	
   679         -	var a = MIN_AXIS_POINT_RADIUS;  //random letters for intermediate computations
   680         -	var b = MAX_AXIS_POINT_RADIUS;
   681         -	var c = (a+b)/2;
   682         -	var d = (b-a)/2;
   683         -	
   684         -	var fr = c - d * fdot;
   685         -	var vdot = vector_dot(view_transform[2], [0, -1, 0]);
   686         -	var vr = c - d * vdot;
   687         -	
   688         -	ctx.lineWidth = AXIS_LINE_WIDTH;
   689         -	ctx.beginPath();
   690         -	
   691         -	if(fdot >=0 && faxis_pt){ // occlude this axis, draw first
   692         -		ctx.moveTo(w/2, h/2);
   693         -		ctx.lineTo(faxis_pt[0], faxis_pt[1]);	
   694         -		ctx.fillRect(faxis_pt[0] - fr, faxis_pt[1] - fr, 2*fr, 2*fr); }
   695         -		
   696         -	if(vdot >=0 && vaxis_pt){ // occlude this axis, draw first
   697         -		ctx.moveTo(w/2, h/2);
   698         -		ctx.lineTo(vaxis_pt[0], vaxis_pt[1]);	
   699         -		ctx.fillRect(vaxis_pt[0] - vr, vaxis_pt[1] - vr, 2*vr, 2*vr); }
   700         -	
   701         -	ctx.stroke();
   702         -	
   703         -		
   704         -	// draw center red square
   705         -	ctx.fillStyle = CENTER_POINT_COLOR;
   706         -	var cr = CENTER_POINT_RADIUS;
   707         -	ctx.fillRect(w/2 - cr, h/2 - cr, 2*cr, 2*cr);
   708         -		
   709         -	ctx.fillStyle = AXIS_POINT_COLOR;
   710         -	
   711         -	
   712         -	if(fdot < 0 && faxis_pt){
   713         -		ctx.beginPath();
   714         -		ctx.moveTo(w/2, h/2);
   715         -		ctx.lineTo(faxis_pt[0], faxis_pt[1]);
   716         -		ctx.stroke();
   717         -		ctx.fillRect(faxis_pt[0] - fr, faxis_pt[1] - fr, 2*fr, 2*fr); }
   718         -		
   719         -	if(vdot < 0 && vaxis_pt){
   720         -		ctx.beginPath();
   721         -		ctx.moveTo(w/2, h/2);
   722         -		ctx.lineTo(vaxis_pt[0], vaxis_pt[1]);
   723         -		ctx.stroke();
   724         -		ctx.fillRect(vaxis_pt[0] - vr, vaxis_pt[1] - vr, 2*vr, 2*vr); }
   725         -	
   726         -
   727         -	// reset line width
   728         -	ctx.lineWidth = LINE_WIDTH;
   729         -	
   730         -	
   731         -
   732         -    
   733         -		
   734         -	
   735         -	for(var i = 0; i < selected_points.length; ++i){
   736         -		var pt;
   737         -		
   738         -		if(selected_points[i] == -1)
   739         -			pt = [w/2, h/2];
   740         -		else pt = point_projections[selected_points[i]];
   741         -		
   742         -		ctx.fillStyle = POINT_COLOR;
   743         -		if(pt) ctx.fillRect(pt[0] - 4, pt[1] - 4, 8, 8); }
   744         -	
   745         -	
   746         -	if(highlight_object !== null){
   747         -		
   748         -		
   749         -		if(highlight_object < point_projections.length){
   750         -			var pt;
   751         -			if(highlight_object == -1)
   752         -				pt = [w/2, h/2];
   753         -			else pt = point_projections[highlight_object];
   754         -			
   755         -			ctx.fillStyle = "rgba(50, 50, 50, .6)";    
   756         -			ctx.fillRect(pt[0] - 4, pt[1] - 4, 8, 8);  // TODO getting a error here that pt is null 
   757         -			document.body.style.cursor = "hand"; }
   758         -			
   759         -		else if(highlight_object < point_projections.length + line_midpoint_projections.length){
   760         -			// alert("drawing highlighted line");
   761         -			
   762         -			var line = lines[highlight_object - point_projections.length];
   763         -			if(line){
   764         -				var pt1 = point_projections[line[0]];
   765         -				var pt2 = point_projections[line[1]];
   766         -				
   767         -				if(pt1 && pt2){
   768         -				
   769         -					ctx.beginPath();
   770         -					
   771         -					ctx.moveTo(pt1[0], pt1[1]);
   772         -					ctx.lineTo(pt2[0], pt2[1]);
   773         -					ctx.lineWidth = 3;
   774         -					ctx.stroke();
   775         -					ctx.lineWidth = 1; }}}
   776         -			
   777         -		else{
   778         -			throw "highlight object index to large"; }}
   779         -		
   780         -	else{
   781         -		document.body.style.cursor = "crosshair"; }
   782         -	
   783         -	if(mouse_dragging){
   784         -	
   785         -		if(selected_points.length && !getKeyState(16)){  // if shift is held, we are selecting more points.
   786         -			if(mouse_loc && getKeyState(1000)){
   787         -				ctx.beginPath();
   788         -				var pt = point_projections[selected_points[0]];
   789         -				if(pt){
   790         -					ctx.moveTo(mouse_loc[0], mouse_loc[1]);
   791         -					ctx.lineTo(pt[0], pt[1]); }
   792         -				ctx.stroke(); }}
   793         -				
   794         -		else if(mouse_loc && last_mouse_down){  
   795         -		
   796         -		
   797         -		    if(getKeyState(1000)){     // select points inside square
   798         -				var minx = Math.min(mouse_loc[0], last_mouse_down[0]);
   799         -				var maxx = Math.max(mouse_loc[0], last_mouse_down[0]);
   800         -				var miny = Math.min(mouse_loc[1], last_mouse_down[1]);
   801         -				var maxy = Math.max(mouse_loc[1], last_mouse_down[1]);
   802         -				ctx.beginPath();
   803         -				ctx.rect(minx, miny, maxx - minx, maxy - miny);
   804         -				ctx.stroke(); }}
   805         -	}
   806         -    
   807         -    ctx.fillStyle = "rgb(0,0,0)";
   808         -	writeMsg(canvas, msg); }
   809    480   
   810    481       
   811    482       
   812    483   window.onload = function(){
   813    484   
   814    485       // load localStorage saved state.
   815    486       if(localStorage.points && localStorage.lines){
................................................................................
   823    494       canvas.height = window.innerHeight - 20;
   824    495       
   825    496       
   826    497       window.onresize = function(){
   827    498           canvas.width = window.innerWidth - 20;
   828    499           canvas.height = window.innerHeight - 20; }
   829    500   
   830         -    
   831         -    
          501  +
   832    502       function getRotationAngle(x, size, max, center_size){
   833    503   
   834    504           if(!center_size) center_size = 0.05;  // the proportional size of the area in which no rotation is effected by the mouse movement
   835    505           if(!max) max = Math.PI/128;
   836    506           
   837    507           //  center does nothing
   838    508           if(Math.abs(x) < size * center_size/2) x = 0;
................................................................................
   984    654       var saveAction = function(){
   985    655           localStorage.points = JSON.stringify(points);
   986    656           localStorage.lines = JSON.stringify(lines);
   987    657       }
   988    658           
   989    659       setInterval(saveAction, saveTimeout);
   990    660   }
   991         -
          661  +*/
   992    662   
   993    663   </script>
   994    664   
   995    665   
   996    666   <body></body>
   997    667   </html>

Added wireframe_model.js version [0b9640f7fc].

            1  +
            2  +
            3  +// global namespace object
            4  +var WireframeModel;
            5  +
            6  +
            7  +(function(){
            8  +
            9  +//  A wireframe model and all the controls needed to move the camera and draw.
           10  +//  despite the name, it may also include polygon surfaces.
           11  +
           12  +var origin = [0, 0, 0];
           13  +
           14  +
           15  +
           16  +var points = []; //global
           17  +var solo_points = {};
           18  +var lines = [];
           19  +
           20  +var point_projections = [];
           21  +var line_midpoint_projections = [];
           22  +
           23  +
           24  +var highlight_object = null;
           25  +var mouse_dragging = false;
           26  +var last_mouse_down = null;
           27  +var mouse_loc = null;
           28  +
           29  +
           30  +
           31  +var zoom_scale = 2.667;  // scalar zoom property.
           32  +var zoom_dist = 1;
           33  +
           34  +
           35  +    
           36  +function moveCamera(offset, cameracentric){
           37  +    if(cameracentric) offset = matrix_mult(view_transform, [offset])[0];
           38  +    //alert("offset: " + offset);
           39  +    vector_add(origin, offset, origin); }
           40  +	
           41  +	
           42  +	
           43  +	
           44  +    
           45  +
           46  +// converts a point from three space to the canvas plane.
           47  +// Note: because of depth perspective, this conversion is not
           48  +// easy to define if the point lies behind the camera.
           49  +// There are two options:
           50  +// When drawing a line, another colinear point in front of the camera may be provided
           51  +// to help find an alertnate point.
           52  +// if both points lie behind the camera or the colinear_point is not provided,
           53  +// this function will return null.
           54  + 
           55  +function project(canvas, xyz, view_transform, origin, colinear_point){
           56  +    if(!xyz) return null;  // point has been deleted or does not exist
           57  +    
           58  +    var w = canvas.width;
           59  +    var h = canvas.height;
           60  +    
           61  +    var scale = Math.min(w, h);
           62  +    
           63  +    var v = xyz.slice(0);
           64  +    if(origin) v = vector_minus(v, origin, v);
           65  +    
           66  +    var z = vector_dot(view_transform[2], v);
           67  +    
           68  +    if(z <= -zoom_dist){
           69  +        if(!colinear_point) return null;
           70  +        
           71  +        var v2 = colinear_point.slice(0);
           72  +        if(origin) vector_minus(v2, origin, v2);
           73  +        
           74  +        var z2 = vector_dot(view_transform[2], v2);
           75  +        
           76  +        if(z2 < 0) return null;
           77  +        
           78  +        // get the coefficients for a complex combination.
           79  +        // t*z + (1-t)*z2 = 0.0002  -- z of new point is just barely infront of the camera.
           80  +        
           81  +        var t = (0.0002 - z2)/(z - z2);  // no division by zero, z is negative, z2 is positive
           82  +        
           83  +        vector_add(vector_scale(v, t, v),
           84  +                   vector_scale(v2, 1-t, v2),
           85  +                   v);
           86  +                   
           87  +        z = vector_dot(view_transform[2], v); }
           88  +
           89  +        
           90  +    var scale2 = zoom_scale * scale / (zoom_dist + z);
           91  +    
           92  +    return [ scale2 * vector_dot(view_transform[0], v) + 0.5 * w,
           93  +             scale2 * vector_dot(view_transform[1], v) + 0.5 * h ]; }
           94  +   
           95  +
           96  +   
           97  +//  removes deleted points and lines
           98  +//  works with global objects.
           99  +
          100  +function cleanupDeletedPoints(){
          101  +    var newpoints = [];
          102  +    var newlines = [];
          103  +    var pointmap = {};
          104  +    
          105  +    var j = 0;
          106  +    
          107  +    for(var i = 0; i < points.length; ++i){
          108  +       
          109  +       if(points[i]){
          110  +           newpoints.push(points[i]);
          111  +           pointmap[i] = j;
          112  +           ++j; }
          113  +           
          114  +       else{
          115  +           pointmap[i] = -1; }}
          116  +    
          117  +    
          118  +    for(var i = 0; i < lines.length; ++i){
          119  +        if(!lines[i]) continue;
          120  +        var a = lines[i][0];
          121  +        var b = lines[i][1];
          122  +        
          123  +        if(pointmap[a] != -1 && pointmap[b] != -1){
          124  +            newlines.push([ pointmap[a], pointmap[b] ]); }}
          125  +    
          126  +    points = newpoints;
          127  +    lines = newlines; }
          128  +        
          129  +
          130  +function addPoint(pt){
          131  +    points.push(pt.slice(0));
          132  +    var index = points.length - 1;
          133  +    solo_points[index] = true;
          134  +    return index; }
          135  +    
          136  +    
          137  +function addLine(pt0, pt1){
          138  +    delete solo_points[pt0];
          139  +    delete solo_points[pt1];
          140  +    lines.push([pt0, pt1]);
          141  +    return lines.length - 1; }    
          142  +
          143  +
          144  +
          145  +function draw(canvas){
          146  +    var ctx = canvas.getContext('2d');
          147  +    var w = canvas.width;
          148  +    var h = canvas.height;
          149  +    
          150  +    // ctx.fillStyle = "rgba(100, 100, 100, .05)";
          151  +    
          152  +    
          153  +    ctx.beginPath();
          154  +    
          155  +    for(var i = 0; i < lines.length; ++i){
          156  +        var line = lines[i];
          157  +        if(!line) continue;
          158  +        //draw line
          159  +        
          160  +        var pt0 = points[line[0]];
          161  +        var pt1 = points[line[1]];
          162  +            
          163  +        var _pt0 = point_projections[line[0]];
          164  +        var _pt1 = point_projections[line[1]];
          165  +        
          166  +        //  finish the line even if one point is on wrong side of viewer.
          167  +        if(!_pt0 && _pt1){
          168  +            _pt0 = project(canvas, pt0, view_transform, origin, pt1); }
          169  +        else if(_pt0 && !_pt1){
          170  +            _pt1 = project(canvas, pt1, view_transform, origin, pt0); }
          171  +            
          172  +        if(!_pt0 || !_pt1) continue;
          173  +        
          174  +        var x0 = _pt0[0];
          175  +        var y0 = _pt0[1];
          176  +
          177  +        var x1 = _pt1[0];
          178  +        var y1 = _pt1[1];
          179  +        
          180  +        ctx.moveTo(x0, y0);
          181  +        ctx.lineTo(x1, y1); }
          182  +    
          183  +    ctx.clearRect(0, 0, w, h);
          184  +	
          185  +    
          186  +	ctx.strokeStyle = "rgba(0, 0, 0, 0.9)";
          187  +    ctx.stroke();
          188  +    
          189  +	ctx.fillStyle = "rgba(0, 0, 0, 0.9)";
          190  +	
          191  +    for(var k in solo_points){
          192  +       var pt = point_projections[k];
          193  +       if(pt) ctx.fillRect(pt[0] - 2, pt[1] - 2, 4, 4); }
          194  +        
          195  +	//
          196  +	//  The following drawing of axes is to help provide an perspective
          197  +	//  The nuances of this draw behavior are created to provide visual cues for interpreting perspective and depth.
          198  +	//  
          199  +	//  Because we use a 2d canvas library for the drawing the rest of the points/shapes after projection, these visual information nuances are important.
          200  +	//
          201  +
          202  +	// axis lines and dots
          203  +	
          204  +    var faxis_pt = project(canvas, vector_add(origin, [0, 0, FORWARD_AXIS_LEN]), view_transform, origin);
          205  +	var vaxis_pt = project(canvas, vector_add(origin, [0, -VERTICAL_AXIS_LEN, 0]), view_transform, origin);
          206  +	
          207  +	ctx.fillStyle = AXIS_POINT_COLOR;
          208  +	
          209  +	var fdot = vector_dot(view_transform[2], [0, 0, 1]);
          210  +	
          211  +	var a = MIN_AXIS_POINT_RADIUS;  //random letters for intermediate computations
          212  +	var b = MAX_AXIS_POINT_RADIUS;
          213  +	var c = (a+b)/2;
          214  +	var d = (b-a)/2;
          215  +	
          216  +	var fr = c - d * fdot;
          217  +	var vdot = vector_dot(view_transform[2], [0, -1, 0]);
          218  +	var vr = c - d * vdot;
          219  +	
          220  +	ctx.lineWidth = AXIS_LINE_WIDTH;
          221  +	ctx.beginPath();
          222  +	
          223  +	if(fdot >=0 && faxis_pt){ // occlude this axis, draw first
          224  +		ctx.moveTo(w/2, h/2);
          225  +		ctx.lineTo(faxis_pt[0], faxis_pt[1]);	
          226  +		ctx.fillRect(faxis_pt[0] - fr, faxis_pt[1] - fr, 2*fr, 2*fr); }
          227  +		
          228  +	if(vdot >=0 && vaxis_pt){ // occlude this axis, draw first
          229  +		ctx.moveTo(w/2, h/2);
          230  +		ctx.lineTo(vaxis_pt[0], vaxis_pt[1]);	
          231  +		ctx.fillRect(vaxis_pt[0] - vr, vaxis_pt[1] - vr, 2*vr, 2*vr); }
          232  +	
          233  +	ctx.stroke();
          234  +	
          235  +		
          236  +	// draw center red square
          237  +	ctx.fillStyle = CENTER_POINT_COLOR;
          238  +	var cr = CENTER_POINT_RADIUS;
          239  +	ctx.fillRect(w/2 - cr, h/2 - cr, 2*cr, 2*cr);
          240  +		
          241  +	ctx.fillStyle = AXIS_POINT_COLOR;
          242  +	
          243  +	
          244  +	if(fdot < 0 && faxis_pt){
          245  +		ctx.beginPath();
          246  +		ctx.moveTo(w/2, h/2);
          247  +		ctx.lineTo(faxis_pt[0], faxis_pt[1]);
          248  +		ctx.stroke();
          249  +		ctx.fillRect(faxis_pt[0] - fr, faxis_pt[1] - fr, 2*fr, 2*fr); }
          250  +		
          251  +	if(vdot < 0 && vaxis_pt){
          252  +		ctx.beginPath();
          253  +		ctx.moveTo(w/2, h/2);
          254  +		ctx.lineTo(vaxis_pt[0], vaxis_pt[1]);
          255  +		ctx.stroke();
          256  +		ctx.fillRect(vaxis_pt[0] - vr, vaxis_pt[1] - vr, 2*vr, 2*vr); }
          257  +	
          258  +
          259  +	// reset line width
          260  +	ctx.lineWidth = LINE_WIDTH;
          261  +	
          262  +	
          263  +
          264  +    
          265  +		
          266  +	
          267  +	for(var i = 0; i < selected_points.length; ++i){
          268  +		var pt;
          269  +		
          270  +		if(selected_points[i] == -1)
          271  +			pt = [w/2, h/2];
          272  +		else pt = point_projections[selected_points[i]];
          273  +		
          274  +		ctx.fillStyle = POINT_COLOR;
          275  +		if(pt) ctx.fillRect(pt[0] - 4, pt[1] - 4, 8, 8); }
          276  +	
          277  +	
          278  +	if(highlight_object !== null){
          279  +		
          280  +		
          281  +		if(highlight_object < point_projections.length){
          282  +			var pt;
          283  +			if(highlight_object == -1)
          284  +				pt = [w/2, h/2];
          285  +			else pt = point_projections[highlight_object];
          286  +			
          287  +			ctx.fillStyle = "rgba(50, 50, 50, .6)";    
          288  +			ctx.fillRect(pt[0] - 4, pt[1] - 4, 8, 8);  // TODO getting a error here that pt is null 
          289  +			document.body.style.cursor = "hand"; }
          290  +			
          291  +		else if(highlight_object < point_projections.length + line_midpoint_projections.length){
          292  +			// alert("drawing highlighted line");
          293  +			
          294  +			var line = lines[highlight_object - point_projections.length];
          295  +			if(line){
          296  +				var pt1 = point_projections[line[0]];
          297  +				var pt2 = point_projections[line[1]];
          298  +				
          299  +				if(pt1 && pt2){
          300  +				
          301  +					ctx.beginPath();
          302  +					
          303  +					ctx.moveTo(pt1[0], pt1[1]);
          304  +					ctx.lineTo(pt2[0], pt2[1]);
          305  +					ctx.lineWidth = 3;
          306  +					ctx.stroke();
          307  +					ctx.lineWidth = 1; }}}
          308  +			
          309  +		else{
          310  +			throw "highlight object index to large"; }}
          311  +		
          312  +	else{
          313  +		document.body.style.cursor = "crosshair"; }
          314  +	
          315  +	if(mouse_dragging){
          316  +	
          317  +		if(selected_points.length && !getKeyState(16)){  // if shift is held, we are selecting more points.
          318  +			if(mouse_loc && getKeyState(1000)){
          319  +				ctx.beginPath();
          320  +				var pt = point_projections[selected_points[0]];
          321  +				if(pt){
          322  +					ctx.moveTo(mouse_loc[0], mouse_loc[1]);
          323  +					ctx.lineTo(pt[0], pt[1]); }
          324  +				ctx.stroke(); }}
          325  +				
          326  +		else if(mouse_loc && last_mouse_down){  
          327  +		
          328  +		
          329  +		    if(getKeyState(1000)){     // select points inside square
          330  +				var minx = Math.min(mouse_loc[0], last_mouse_down[0]);
          331  +				var maxx = Math.max(mouse_loc[0], last_mouse_down[0]);
          332  +				var miny = Math.min(mouse_loc[1], last_mouse_down[1]);
          333  +				var maxy = Math.max(mouse_loc[1], last_mouse_down[1]);
          334  +				ctx.beginPath();
          335  +				ctx.rect(minx, miny, maxx - minx, maxy - miny);
          336  +				ctx.stroke(); }}
          337  +	}
          338  +    
          339  +    ctx.fillStyle = "rgb(0,0,0)";
          340  +	writeMsg(canvas, msg); }
          341  +	
          342  +	
          343  +
          344  +
          345  +})();