Index: test_wireframe_model.html ================================================================== --- test_wireframe_model.html +++ test_wireframe_model.html @@ -79,10 +79,36 @@ + + +window.onload = function(){ + + var canvas = document.createElement("canvas"); + + canvas.width = 500; + canvas.height = 500; + + document.body.appendChild(canvas); + + var ctx = canvas.getContext("2d"); + + + var model = new WireframeModel(); + model.addPoint([0, 0, 0]); + model.addPoint([0, .1, 0]); + model.addPoint([0, 0, .1]); + model.addPoint([-.1, .1, 0]); + model.addPoint([-.1, 0, .1]); + + model.draw(canvas); + + +} + Index: wireframe_model.js ================================================================== --- wireframe_model.js +++ wireframe_model.js @@ -9,10 +9,135 @@ (function(){ + + + +// converts a point from three space to the canvas plane. +// Note: because of depth perspective, this conversion is not +// easy to define if the point lies behind the camera. +// There are two options: +// When drawing a line, another colinear point in front of the camera may be provided +// to help find an alternate point. +// if both points lie behind the camera or the colinear_point is not provided, +// this function will return null. + +function project(canvas, xyz, view, colinear_point){ + if(!xyz) return null; // point has been deleted or does not exist + + var view_transform = view.transform; + var origin = view.origin; + var zoom_scale = view.zoom_scale; + var zoom_dist = view.zoom_dist; + + if(!zoom_scale) zoom_scale = 1; + if(!zoom_dist) zoom_dist = 0; + if(!origin) origin = [0,0,0]; + + + var w = canvas.width; + var h = canvas.height; + + var scale = Math.min(w, h); + + var v = xyz.slice(0); + if(origin) v = vector_minus(v, origin, v); + + var z = vector_dot(view_transform[2], v); + + if(z <= -zoom_dist){ // this point is on the wrong side of the viewer, find the closest point on the correct side if we can. + if(!colinear_point) return null; + + var v2 = colinear_point.slice(0); + if(origin) vector_minus(v2, origin, v2); + + var z2 = vector_dot(view_transform[2], v2); + + if(z2 < 0) return null; + + // get the coefficients for a complex combination. + // t*z + (1-t)*z2 = 0.0002 -- z of new point is just barely infront of the camera. + + var t = (0.0002 - z2)/(z - z2); // no division by zero, z is negative, z2 is positive + + vector_add(vector_scale(v, t, v), + vector_scale(v2, 1-t, v2), + v); + + z = vector_dot(view_transform[2], v); } + + + var scale2 = zoom_scale * scale / (zoom_dist + z); + + return [ scale2 * vector_dot(view_transform[0], v) + 0.5 * w, + scale2 * vector_dot(view_transform[1], v) + 0.5 * h ]; } + + + +// Just like the project function above but for a set of points. +// It's put into a loop a little better. +function project_all(canvas, points, point_projections, view){ + + var view_transform = view.transform; + var origin = view.origin; + var zoom_scale = view.zoom_scale; + var zoom_dist = view.zoom_dist; + + if(!zoom_scale) zoom_scale = 1; + if(!zoom_dist) zoom_dist = 0; + if(!origin) origin = [0,0,0]; + + + if(!point_projections) point_projections = []; + point_projections.length = points.length; + + var w = canvas.width; + var h = canvas.height; + var w_2 = w/2; + var h_2 = h/2; + + var scale = zoom_scale * Math.min(w, h); + + var v00 = view_transform[0][0]; + var v01 = view_transform[0][1]; + var v02 = view_transform[0][2]; + var v10 = view_transform[1][0]; + var v11 = view_transform[1][1]; + var v12 = view_transform[1][2]; + var v20 = view_transform[2][0]; + var v21 = view_transform[2][1]; + var v22 = view_transform[2][2]; + + + for(var i = 0; i < points.length; ++i){ + var pt = points[i]; + + var x = pt[0]; + var y = pt[1]; + var z = pt[2]; + + if(origin){ + x -= origin[0]; + y -= origin[1]; + z -= origin[2]; } + + var depth = x*v20 + y*v21 + z*v22; + var scale2 = scale / (zoom_dist + depth); + + if(!point_projections[i]) point_projections[i] = [0, 0]; + + var point_projection = point_projections[i]; + + point_projection[0] = scale2 * (x*v00 + y*v01 + z*v02) + w_2; + point_projection[1] = scale2 * (x*v10 + y*v11 + z*v22) + h_2; } + + return point_projections; } + + + // A wireframe model and all the controls needed to move the camera and draw. // despite the name, it may also include polygon surfaces. function __WireframeModel(){ @@ -24,22 +149,30 @@ [0, 0, 1]]; var points = []; //global - var solo_points = {}; + var point_projections = []; + //var solo_points = {}; var lines = []; - + var zoom_scale = 1; + var zoom_dist = 1; // just because these actual objects is returned // doesn't mean you should modify it externally if you can help it. WFMObj.getPoints = function getPoints(){ return points; } + + WFMObj.getPoint = function getPoint(index){ + return points[index].slice(0); } WFMObj.getLines = function getLines(){ return lines; } + + WFMObj.getLine = function getLine(index){ + return lines[index].slice(0); } WFMObj.getViewTransform = function getViewTransform(){ return view_transform; } @@ -76,12 +209,28 @@ if(ptindex < 0 || ptindex >= points.length){ throw "pt ref at index " + i + " of line was not in bounds: " + ptindex; }} lines.push(line); return lines.length - 1; } - - + + + WFMObj.draw = function draw(canvas){ + var ctx = canvas.getContext("2d"); + + var view = {}; + view.transform = view_transform; + view.origin = view_origin; + view.zoom_scale = zoom_scale; + view.zoom_dist = zoom_dist; + + project_all(canvas, points, point_projections, view); + // project_all() + + for(var i = 0; i < point_projections.length; ++i){ + var point_projection = point_projections[i]; + if(point_projection) ctx.fillRect(point_projection[0] - 1, point_projection[1] - 1, 2, 2); } + } } // use constructor as global namespace object. WireframeModel = __WireframeModel; @@ -114,60 +263,12 @@ vector_add(origin, offset, origin); } - - -// converts a point from three space to the canvas plane. -// Note: because of depth perspective, this conversion is not -// easy to define if the point lies behind the camera. -// There are two options: -// When drawing a line, another colinear point in front of the camera may be provided -// to help find an alertnate point. -// if both points lie behind the camera or the colinear_point is not provided, -// this function will return null. - -function project(canvas, xyz, view_transform, origin, colinear_point){ - if(!xyz) return null; // point has been deleted or does not exist - - var w = canvas.width; - var h = canvas.height; - - var scale = Math.min(w, h); - - var v = xyz.slice(0); - if(origin) v = vector_minus(v, origin, v); - - var z = vector_dot(view_transform[2], v); - - if(z <= -zoom_dist){ - if(!colinear_point) return null; - - var v2 = colinear_point.slice(0); - if(origin) vector_minus(v2, origin, v2); - - var z2 = vector_dot(view_transform[2], v2); - - if(z2 < 0) return null; - - // get the coefficients for a complex combination. - // t*z + (1-t)*z2 = 0.0002 -- z of new point is just barely infront of the camera. - - var t = (0.0002 - z2)/(z - z2); // no division by zero, z is negative, z2 is positive - - vector_add(vector_scale(v, t, v), - vector_scale(v2, 1-t, v2), - v); - - z = vector_dot(view_transform[2], v); } - - - var scale2 = zoom_scale * scale / (zoom_dist + z); - - return [ scale2 * vector_dot(view_transform[0], v) + 0.5 * w, - scale2 * vector_dot(view_transform[1], v) + 0.5 * h ]; } + + // removes deleted points and lines // works with global objects.