From 4fddbd44c5cf9c42810a7ee38e152e02c28ded4a Mon Sep 17 00:00:00 2001 From: Kenneth Jao Date: Thu, 17 Aug 2017 12:33:30 -0400 Subject: [PATCH 1/9] initial commit - basic projection --- saku/index.css | 41 +++++++++++++++++++++++++++ saku/index.html | 23 +++++++++++++++ saku/index.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 saku/index.css create mode 100644 saku/index.html create mode 100644 saku/index.js diff --git a/saku/index.css b/saku/index.css new file mode 100644 index 0000000..fb986bb --- /dev/null +++ b/saku/index.css @@ -0,0 +1,41 @@ +@import url('https://fonts.googleapis.com/css?family=Raleway'); +@import url('https://fonts.googleapis.com/css?family=Josefin+Sans'); + +html { + font-family: 'Raleway'; + /*background-color: #15171B;*/ +} + +body { + margin: 0; +} + +#hexagon { + height: 300; +} + +#hexagon polygon { + cursor: pointer; + fill: rgba(0,0,0,0); + stroke-width: 2px; + stroke: #D0790E; + + transition: fill 0.2s ease; +} + +#hexagon text { + fill: #D0790E; + pointer-events: none; +} + +.hex { + background-color: red; +} + +#hexagon polygon:hover { + fill: rgba(255,255,255,0.05); +} + +#glCanvas { + border: 2px solid black; +} \ No newline at end of file diff --git a/saku/index.html b/saku/index.html new file mode 100644 index 0000000..ef071e2 --- /dev/null +++ b/saku/index.html @@ -0,0 +1,23 @@ + + + + + Foxnet + + + + + + + + + + + + \ No newline at end of file diff --git a/saku/index.js b/saku/index.js new file mode 100644 index 0000000..3c4c3d1 --- /dev/null +++ b/saku/index.js @@ -0,0 +1,74 @@ +var canvas = document.getElementById("glCanvas"); +ctx = canvas.getContext("2d"); + +objects = {}; + +camera = [[(canvas.width/2)-50,canvas.height/2,0],[0,90]]; // [[Position],[Rotation]]; + +triangle = [ + [0,25,1], + [25,50,1], + [25,0,1] +] + +function drawShape(shape) { + ctx.beginPath(); + var newShape = []; + var cP = camera[0]; + var cR = camera[1]; + // Camera direction vector + var cV = [ + Rnd(Math.cos(toRad(cR[1])),3), + Rnd(Math.sin(toRad(cR[0])),3), + Rnd(Math.sin(toRad(cR[1])),3) + ]; + console.log(cV); + // Perspective mapping + for(var i = 0; i < shape.length; i++) { + var x = shape[i][0]; + var y = shape[i][1]; + var z = shape[i][2]; + var pV = [x-cV[0],y-cV[1],z-cV[1]]; + console.log(pV); + var theta = Rnd(Math.acos((dot(cV,pV)/(mag(cV)*mag(pV)))),5); + console.log(theta); + + var dZ = Math.abs(shape[i][2] - c[2]); + newShape.push([c[0]+(x-c[0])/dZ,c[1]+(y-c[1])/dZ]); + } + ctx.moveTo(newShape[0][0],newShape[0][1]); + for(var i = 1; i < shape.length; i++) { + ctx.lineTo(newShape[i][0],newShape[i][1]); + } + ctx.fill(); +} + +function toRad(deg) { + return deg/180*Math.PI; +} + +function Rnd(num,fig) { + return Math.round(num*Math.pow(10,fig))/Math.pow(10,fig); +} + +function dot(vecOne, vecTwo) { + if(vecOne.length !== vecTwo.length) { + //throw error + return; + } + var final = 0; + for(var i = 0; i < vecOne.length; i++) { + final += vecOne[i]*vecTwo[i]; + } + return final; +} + +function mag(vec) { + var rad = 0; + for(var i = 0; i < vec.length; i++) { + rad += Math.pow(vec[i],2); + } + return Math.sqrt(rad); +} + +drawShape(triangle); \ No newline at end of file From aedb683f592b7626d374b0678096da602a4b7eec Mon Sep 17 00:00:00 2001 From: Kenneth Jao Date: Sat, 26 Aug 2017 18:25:11 -0400 Subject: [PATCH 2/9] rotational camera perspective mapping --- saku/index.js | 111 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 20 deletions(-) diff --git a/saku/index.js b/saku/index.js index 3c4c3d1..ca7f57d 100644 --- a/saku/index.js +++ b/saku/index.js @@ -3,39 +3,84 @@ ctx = canvas.getContext("2d"); objects = {}; -camera = [[(canvas.width/2)-50,canvas.height/2,0],[0,90]]; // [[Position],[Rotation]]; - +camera = [[canvas.width/2,0,canvas.height/2],[1,1],1]; // [[Position],[Rotation(x,z)],Focal]; triangle = [ - [0,25,1], - [25,50,1], - [25,0,1] + [0,1,25], + [25,1,50], + [25,1,0] ] function drawShape(shape) { ctx.beginPath(); var newShape = []; - var cP = camera[0]; - var cR = camera[1]; + var cP = camera[0]; // Camera Position + console.log(cP); + var cR = camera[1]; // Camera Rotation + var cF = camera[2]; // Camera direction vector var cV = [ - Rnd(Math.cos(toRad(cR[1])),3), - Rnd(Math.sin(toRad(cR[0])),3), - Rnd(Math.sin(toRad(cR[1])),3) + Rnd(cF*Math.sin(toRad(cR[1])),3), // 0 Degrees Z points straight to Y. + Rnd(cF*Math.cos(toRad(cR[1])),3), + Rnd(cF*Math.sin(toRad(cR[0])),3) // 0 Degrees X points straight to Y. ]; - console.log(cV); + console.log("Camera Vector: ",cV); // Perspective mapping - for(var i = 0; i < shape.length; i++) { + for(var i = 0; i < shape.length; i++) { // Each point in 3D var x = shape[i][0]; var y = shape[i][1]; var z = shape[i][2]; - var pV = [x-cV[0],y-cV[1],z-cV[1]]; - console.log(pV); - var theta = Rnd(Math.acos((dot(cV,pV)/(mag(cV)*mag(pV)))),5); - console.log(theta); + var pV = [x-cP[0],y-cP[1],z-cP[2]]; // Point direction vector + // Restricting to X and Z dimensions and comparing to Y. + var distPX = mag(dim("XY",pV)); + var distPZ = mag(dim("ZY",pV)); + var distCX = mag(dim("XY",cV)); + var distCZ = mag(dim("ZY",cV)); + + /* + sin(acos(x)) = sqrt(1-x^2) + cos(acos(x)) = x + */ + /*var thetaX = Rnd(Math.acos( + dot(dim("XZ",cV),dim("XZ",pV)) / + (distCX*distPX) + ),5); + var thetaY = Rnd(Math.acos( + dot(dim("YZ",cV),dim("YZ",pV)) / + (distCY*distPY) + ),5); + var oppX = distPX * Math.sin(thetaX); + var oppY = distPY * Math.sin(thetaY); + */ + + var adjX = dot(dim("XY",cV),dim("XY",pV)) / distCX; + var adjZ = dot(dim("ZY",cV),dim("ZY",pV)) / distCZ; - var dZ = Math.abs(shape[i][2] - c[2]); - newShape.push([c[0]+(x-c[0])/dZ,c[1]+(y-c[1])/dZ]); + var oppX = distPX * Math.sqrt(1-Math.pow(adjX/distPX,2)); + var oppZ = distPZ * Math.sqrt(1-Math.pow(adjZ/distPZ,2)); + + var projOppX = distCX*oppX/adjX; // Represents X + var projOppZ = distCZ*oppZ/adjZ; // Represents Y + // If the dot product is greater than 0, b is on the right of a. + if(adjX > 0) projOppX *= -1; + if(adjZ < 0) projOppZ *= -1; + + console.log("--------\nPoint Position: ", shape[i], + "\nPosition Vector " + i + ": ", pV, + "\nDistancePXY: ", distPX, + "\nDistancePZY: ", distPZ, + "\nDistanceCXY: ", distCX, + "\nDistanceCZY: ", distCZ, + "\nAdjacentXY: ", adjX, + "\nAdjacentZY: ", adjZ, + "\nOppositeXY: ", oppX, + "\nOppositeZY: ", oppZ, + "\nProjectedOppXY: ", projOppX, + "\nProjectedOppZY: ", projOppZ + ); + + newShape.push([Rnd(cP[0]+projOppX,3),Rnd(cP[2]+projOppZ,3)]); } + console.log(newShape); ctx.moveTo(newShape[0][0],newShape[0][1]); for(var i = 1; i < shape.length; i++) { ctx.lineTo(newShape[i][0],newShape[i][1]); @@ -53,7 +98,7 @@ function Rnd(num,fig) { function dot(vecOne, vecTwo) { if(vecOne.length !== vecTwo.length) { - //throw error + throw new SizeMismatch('VectorDimMismatch', [vecOne,vecTwo]); return; } var final = 0; @@ -71,4 +116,30 @@ function mag(vec) { return Math.sqrt(rad); } -drawShape(triangle); \ No newline at end of file +function dim(dimensions, vector) { + var newVec = []; + var ref = { + "x": 0, + "y": 1, + "z": 2 + }; + + if(dimensions.constructor === Array) { + for(var i = 0; i < dimensions.length; i++) { + newVec.push(vector[dimensions[i]]); + } + } else if(dimensions.constructor === String) { + for(var i = 0; i < dimensions.length; i++) { + newVec.push(vector[ref[dimensions[i].toLowerCase()]]); + } + } + return newVec; +} + +function SizeMismatch(message, obj) { + this.mesage = message; + this.name = "SizeMismatch"; + this.matrix = obj; +} + +drawShape(triangle); From ffcc4cf09528e064e4fa07efdfa8b11b28469a2d Mon Sep 17 00:00:00 2001 From: Kenneth Jao Date: Sat, 26 Aug 2017 18:53:40 -0400 Subject: [PATCH 3/9] added descriptive comments and logging --- saku/index.js | 114 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 45 deletions(-) diff --git a/saku/index.js b/saku/index.js index ca7f57d..9f3543d 100644 --- a/saku/index.js +++ b/saku/index.js @@ -2,19 +2,23 @@ var canvas = document.getElementById("glCanvas"); ctx = canvas.getContext("2d"); objects = {}; - +verbose = true; camera = [[canvas.width/2,0,canvas.height/2],[1,1],1]; // [[Position],[Rotation(x,z)],Focal]; -triangle = [ - [0,1,25], - [25,1,50], - [25,1,0] -] + +triangle = { + "name": "Triangle", + "vertices": [ + [0,1,25], + [25,1,50], + [25,1,0] + ] +} function drawShape(shape) { ctx.beginPath(); + var log = []; var newShape = []; var cP = camera[0]; // Camera Position - console.log(cP); var cR = camera[1]; // Camera Rotation var cF = camera[2]; // Camera direction vector @@ -23,12 +27,27 @@ function drawShape(shape) { Rnd(cF*Math.cos(toRad(cR[1])),3), Rnd(cF*Math.sin(toRad(cR[0])),3) // 0 Degrees X points straight to Y. ]; - console.log("Camera Vector: ",cV); + + if(verbose) { + log = { + "Shape Name": shape.name, + "Camera Position": vecToObj(cP), + "Camera Rotation": { + "X": cR[0], + "Z": cR[1] + }, + "Camera Focal": cF, + "Camera Vector": vecToObj(cV), + "Shape Vertice Values": {}, + "Canvas Points": {} + } + } + // Perspective mapping - for(var i = 0; i < shape.length; i++) { // Each point in 3D - var x = shape[i][0]; - var y = shape[i][1]; - var z = shape[i][2]; + for(var i = 0; i < shape.vertices.length; i++) { // Each point in 3D + var x = shape.vertices[i][0]; + var y = shape.vertices[i][1]; + var z = shape.vertices[i][2]; var pV = [x-cP[0],y-cP[1],z-cP[2]]; // Point direction vector // Restricting to X and Z dimensions and comparing to Y. var distPX = mag(dim("XY",pV)); @@ -36,53 +55,49 @@ function drawShape(shape) { var distCX = mag(dim("XY",cV)); var distCZ = mag(dim("ZY",cV)); - /* - sin(acos(x)) = sqrt(1-x^2) - cos(acos(x)) = x - */ - /*var thetaX = Rnd(Math.acos( - dot(dim("XZ",cV),dim("XZ",pV)) / - (distCX*distPX) - ),5); - var thetaY = Rnd(Math.acos( - dot(dim("YZ",cV),dim("YZ",pV)) / - (distCY*distPY) - ),5); - var oppX = distPX * Math.sin(thetaX); - var oppY = distPY * Math.sin(thetaY); + /* Adjacent and Opposite calculated with math simplifications + cos(arccosx) = x + sin(arccosx) = sqrt(1-x^2) */ - var adjX = dot(dim("XY",cV),dim("XY",pV)) / distCX; + var adjX = dot(dim("XY",cV),dim("XY",pV)) / distCX; var adjZ = dot(dim("ZY",cV),dim("ZY",pV)) / distCZ; var oppX = distPX * Math.sqrt(1-Math.pow(adjX/distPX,2)); var oppZ = distPZ * Math.sqrt(1-Math.pow(adjZ/distPZ,2)); - var projOppX = distCX*oppX/adjX; // Represents X - var projOppZ = distCZ*oppZ/adjZ; // Represents Y + var projOppX = distCX*oppX/adjX; // Represents X in projective plane. + var projOppZ = distCZ*oppZ/adjZ; // Represents Y in projective plane. // If the dot product is greater than 0, b is on the right of a. if(adjX > 0) projOppX *= -1; if(adjZ < 0) projOppZ *= -1; - console.log("--------\nPoint Position: ", shape[i], - "\nPosition Vector " + i + ": ", pV, - "\nDistancePXY: ", distPX, - "\nDistancePZY: ", distPZ, - "\nDistanceCXY: ", distCX, - "\nDistanceCZY: ", distCZ, - "\nAdjacentXY: ", adjX, - "\nAdjacentZY: ", adjZ, - "\nOppositeXY: ", oppX, - "\nOppositeZY: ", oppZ, - "\nProjectedOppXY: ", projOppX, - "\nProjectedOppZY: ", projOppZ - ); + var canvasPointX = Rnd(cP[0]+projOppX,3); + var canvasPointY = Rnd(cP[2]+projOppZ,3) - newShape.push([Rnd(cP[0]+projOppX,3),Rnd(cP[2]+projOppZ,3)]); + if(verbose) { + var num = "Point " + (i+1).toString(); + log["Shape Vertice Values"][num] = { + "Point Position": vecToObj(shape.vertices[i]), + "Position Vector": vecToObj(pV), + "DistancePXY": distPX, + "DistancePZY": distPZ, + "DistanceCXY": distCX, + "DistanceCZY": distCZ, + "AdjacentXY": adjX, + "AdjacentZY": adjZ, + "OppositeXY": oppX, + "OppositeZY": oppZ, + "ProjectedOppXY": projOppX, + "ProjectedOppZY": projOppZ + }; + log["Canvas Points"][num] = vecToObj([canvasPointX, canvasPointY]); + } + newShape.push([canvasPointX, canvasPointY]); } - console.log(newShape); + if(verbose) console.log(log); ctx.moveTo(newShape[0][0],newShape[0][1]); - for(var i = 1; i < shape.length; i++) { + for(var i = 1; i < shape.vertices.length; i++) { ctx.lineTo(newShape[i][0],newShape[i][1]); } ctx.fill(); @@ -142,4 +157,13 @@ function SizeMismatch(message, obj) { this.matrix = obj; } +function vecToObj(vec) { + var obj = {}; + var ref = ["X","Y","Z"]; + for(var i = 0; i < vec.length; i++) { + obj[ref[i]] = vec[i]; + } + return obj; +} + drawShape(triangle); From 6150a33694954f996442e36112c63213c6bfee55 Mon Sep 17 00:00:00 2001 From: Kenneth Jao Date: Mon, 28 Aug 2017 00:48:29 -0400 Subject: [PATCH 4/9] Added constructors for scene and respective functions --- saku/index.css | 2 +- saku/index.html | 12 +--- saku/index.js | 150 ++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 122 insertions(+), 42 deletions(-) diff --git a/saku/index.css b/saku/index.css index fb986bb..cd5a378 100644 --- a/saku/index.css +++ b/saku/index.css @@ -37,5 +37,5 @@ body { } #glCanvas { - border: 2px solid black; + } \ No newline at end of file diff --git a/saku/index.html b/saku/index.html index ef071e2..d6939eb 100644 --- a/saku/index.html +++ b/saku/index.html @@ -7,17 +7,9 @@ - - + - - + \ No newline at end of file diff --git a/saku/index.js b/saku/index.js index 9f3543d..2623210 100644 --- a/saku/index.js +++ b/saku/index.js @@ -1,21 +1,97 @@ -var canvas = document.getElementById("glCanvas"); -ctx = canvas.getContext("2d"); +Saku = { + Scene: function() { + this.objects = []; + this.addObject = function(obj) { + var invalid = [varType(obj) !== "Object", varType(obj)]; + if(varType(obj) !== "Object") throw TypeError("Expected Object as argument; Got " + invalid[1] + "."); -objects = {}; -verbose = true; -camera = [[canvas.width/2,0,canvas.height/2],[1,1],1]; // [[Position],[Rotation(x,z)],Focal]; + this.objects.push(obj); + } + }, + Object: function(vertices, name) { + if(vertices === undefined) throw TypeError("Not enough arguments; Expected Array as argument."); + var invalid = [[varType(vertices) !== "Array", varType(vertices)]]; + invalid[1] = vertices.every(function(element) { return varType(element) !== "Array"; }); + invalid[2] = invalid[1] || vertices.every(function(element) { return element.every(function(number) { return isNaN(number); }); }); + if(invalid[0][0]) throw TypeError("Expected Array as argument; Got " + invalid[0][1]); + if(invalid[1]) throw TypeError("Expected Arrays as 1st dimensional element."); + if(invalid[2]) throw TypeError("Expected Number as 2nd dimensional element."); -triangle = { - "name": "Triangle", - "vertices": [ - [0,1,25], - [25,1,50], - [25,1,0] - ] + this.name = name || "Unnamed Object"; + this.vertices = vertices; + + this.translateX = function(value) { + this.vertices.forEach(function(ele, index, arr) { + arr[index][0] = ele[0] + value; + }); + } + + this.translateY = function(value) { + this.vertices.forEach(function(ele, index, arr) { + arr[index][1] = ele[1] + value; + }); + } + + this.translateZ = function(value) { + this.vertices.forEach(function(ele, index, arr) { + arr[index][2] = ele[2] + value; + }); + } + + this.translate = function(value) { + this.vertices.forEach(function(ele, index, arr) { + arr[index][0] = ele[0] + value; + arr[index][1] = ele[1] + value; + arr[index][2] = ele[2] + value; + }); + } + } } -function drawShape(shape) { - ctx.beginPath(); +Saku["Triangle"] = function(name) { + Saku.Object.call(this, [ + [500,4,500], + [250,4,800], + [250,4,600] + ], (name || "Triangle")); + this.prototype = Object.create(Saku.Object.prototype); +} + +Saku["Square"] = function(name) { + Saku.Object.call(this, [ + [1000,4,600], + [1500,4,600], + [1500,4,1100], + [1000,4,1100] + ], (name || "Square")); + this.prototype = Object.create(Saku.Object.prototype); +} + +function varType(variable) { + var type = typeof variable; + if(type === "object") { + return (variable.constructor === Array) ? "Array" : "Object"; + } else { + return type[0].toUpperCase() + type.slice(1); + } +} + +function updateFrame() { + ctx.clearRect(0,0,canvas.width,canvas.height) + for(var i = 0; i < scene.objects.length; i++) { + var object = scene.objects[i]; + object = projectObj(object); + ctx.beginPath(); + ctx.moveTo(object[0][0],object[0][1]); + for(var j = 1; j < object.length; j++) { + ctx.lineTo(object[j][0],object[j][1]); + } + ctx.fill(); + + } +} + +function projectObj(object) { var log = []; var newShape = []; var cP = camera[0]; // Camera Position @@ -30,7 +106,7 @@ function drawShape(shape) { if(verbose) { log = { - "Shape Name": shape.name, + "Object Name": object.name, "Camera Position": vecToObj(cP), "Camera Rotation": { "X": cR[0], @@ -38,16 +114,16 @@ function drawShape(shape) { }, "Camera Focal": cF, "Camera Vector": vecToObj(cV), - "Shape Vertice Values": {}, + "Object Vertice Values": {}, "Canvas Points": {} } } // Perspective mapping - for(var i = 0; i < shape.vertices.length; i++) { // Each point in 3D - var x = shape.vertices[i][0]; - var y = shape.vertices[i][1]; - var z = shape.vertices[i][2]; + for(var i = 0; i < object.vertices.length; i++) { // Each point in 3D + var x = object.vertices[i][0]; + var y = object.vertices[i][1]; + var z = object.vertices[i][2]; var pV = [x-cP[0],y-cP[1],z-cP[2]]; // Point direction vector // Restricting to X and Z dimensions and comparing to Y. var distPX = mag(dim("XY",pV)); @@ -77,8 +153,8 @@ function drawShape(shape) { if(verbose) { var num = "Point " + (i+1).toString(); - log["Shape Vertice Values"][num] = { - "Point Position": vecToObj(shape.vertices[i]), + log["Object Vertice Values"][num] = { + "Point Position": vecToObj(object.vertices[i]), "Position Vector": vecToObj(pV), "DistancePXY": distPX, "DistancePZY": distPZ, @@ -96,11 +172,7 @@ function drawShape(shape) { newShape.push([canvasPointX, canvasPointY]); } if(verbose) console.log(log); - ctx.moveTo(newShape[0][0],newShape[0][1]); - for(var i = 1; i < shape.vertices.length; i++) { - ctx.lineTo(newShape[i][0],newShape[i][1]); - } - ctx.fill(); + return newShape; } function toRad(deg) { @@ -160,10 +232,26 @@ function SizeMismatch(message, obj) { function vecToObj(vec) { var obj = {}; var ref = ["X","Y","Z"]; - for(var i = 0; i < vec.length; i++) { - obj[ref[i]] = vec[i]; - } + vec.forEach(function(part) { + obj[ref[part]] = vec[part]; + }); return obj; } -drawShape(triangle); +var canvas = document.getElementById("glCanvas"); +canvas.width = window.innerWidth; +canvas.height = window.innerHeight; +ctx = canvas.getContext("2d"); + +drawObjects = []; +verbose = false; +camera = [[canvas.width/2,0,canvas.height/2],[0,0],1]; // [[Position],[Rotation(x,z)],Focal]; + +scene = new Saku.Scene(); +var triangle = new Saku.Triangle(); +var square = new Saku.Square(); + +scene.addObject(triangle); +scene.addObject(square); + +setInterval(updateFrame, 500); \ No newline at end of file From ccd6926b0c9eb972ad2986d0520b4e7e6e7ecb39 Mon Sep 17 00:00:00 2001 From: Kenneth Jao Date: Mon, 28 Aug 2017 20:08:02 -0400 Subject: [PATCH 5/9] Added more constructors, camera size --- saku/index.js | 70 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/saku/index.js b/saku/index.js index 2623210..0bd6e9b 100644 --- a/saku/index.js +++ b/saku/index.js @@ -1,5 +1,13 @@ +//temp scaling 1 = 1 centimeter +// 1 centimeter = 100px + Saku = { - Scene: function() { + Scene: function(canvas, pixelScale, globalScale) { + //invalid + this.canvas = canvas; + this.globalScale = "centimeter"; + this.pixelScale = pixelScale || 100; + this.camera = undefined; this.objects = []; this.addObject = function(obj) { var invalid = [varType(obj) !== "Object", varType(obj)]; @@ -7,6 +15,16 @@ Saku = { this.objects.push(obj); } + this.setCamera = function(cam) { + //invalid + this.camera = cam; + } + }, + Camera: function(pos, rot, foc) { + //invalid + this.position = pos || [0,0,0]; + this.rotation = rot || [0,0]; + this.focal = foc || 0.35; }, Object: function(vertices, name) { if(vertices === undefined) throw TypeError("Not enough arguments; Expected Array as argument."); @@ -50,19 +68,19 @@ Saku = { Saku["Triangle"] = function(name) { Saku.Object.call(this, [ - [500,4,500], - [250,4,800], - [250,4,600] + [0,4,0], + [0,4,5], + [3,4,3] ], (name || "Triangle")); this.prototype = Object.create(Saku.Object.prototype); } Saku["Square"] = function(name) { Saku.Object.call(this, [ - [1000,4,600], - [1500,4,600], - [1500,4,1100], - [1000,4,1100] + [0,4,60], + [20,4,60], + [0,4,80], + [20,4,80] ], (name || "Square")); this.prototype = Object.create(Saku.Object.prototype); } @@ -76,27 +94,28 @@ function varType(variable) { } } -function updateFrame() { +function updateFrame(scene) { + var ctx = document.getElementById(scene.canvas).getContext("2d"); ctx.clearRect(0,0,canvas.width,canvas.height) for(var i = 0; i < scene.objects.length; i++) { var object = scene.objects[i]; - object = projectObj(object); + object = projectObj(object, scene); + console.log(object); ctx.beginPath(); ctx.moveTo(object[0][0],object[0][1]); for(var j = 1; j < object.length; j++) { ctx.lineTo(object[j][0],object[j][1]); } - ctx.fill(); - + ctx.fill(); } } -function projectObj(object) { +function projectObj(object, scene) { var log = []; var newShape = []; - var cP = camera[0]; // Camera Position - var cR = camera[1]; // Camera Rotation - var cF = camera[2]; + var cP = scene.camera.position; + var cR = scene.camera.rotation; + var cF = scene.camera.focal; // Camera direction vector var cV = [ Rnd(cF*Math.sin(toRad(cR[1])),3), // 0 Degrees Z points straight to Y. @@ -148,8 +167,9 @@ function projectObj(object) { if(adjX > 0) projOppX *= -1; if(adjZ < 0) projOppZ *= -1; - var canvasPointX = Rnd(cP[0]+projOppX,3); - var canvasPointY = Rnd(cP[2]+projOppZ,3) + var canvas = document.getElementById(scene.canvas); + var canvasPointX = Rnd(canvas.width/2+projOppX*scene.pixelScale,3); + var canvasPointY = Rnd(canvas.height/2+projOppZ*scene.pixelScale,3); if(verbose) { var num = "Point " + (i+1).toString(); @@ -232,8 +252,8 @@ function SizeMismatch(message, obj) { function vecToObj(vec) { var obj = {}; var ref = ["X","Y","Z"]; - vec.forEach(function(part) { - obj[ref[part]] = vec[part]; + vec.forEach(function(part, index) { + obj[ref[index]] = part; }); return obj; } @@ -241,17 +261,17 @@ function vecToObj(vec) { var canvas = document.getElementById("glCanvas"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; -ctx = canvas.getContext("2d"); drawObjects = []; -verbose = false; -camera = [[canvas.width/2,0,canvas.height/2],[0,0],1]; // [[Position],[Rotation(x,z)],Focal]; +verbose = true; // [[Position],[Rotation(x,z)],Focal]; -scene = new Saku.Scene(); +scene = new Saku.Scene("glCanvas"); var triangle = new Saku.Triangle(); var square = new Saku.Square(); +var camera = new Saku.Camera(); +scene.setCamera(camera); scene.addObject(triangle); scene.addObject(square); -setInterval(updateFrame, 500); \ No newline at end of file +setInterval(updateFrame(scene), 500); \ No newline at end of file From a371bcceeb4bf7f1b9bcb8396dadb9d33229053a Mon Sep 17 00:00:00 2001 From: Kenneth Jao Date: Tue, 29 Aug 2017 02:30:25 -0400 Subject: [PATCH 6/9] Fixed projection axis flipping, --- saku/index.js | 52 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/saku/index.js b/saku/index.js index 0bd6e9b..d69484b 100644 --- a/saku/index.js +++ b/saku/index.js @@ -26,17 +26,18 @@ Saku = { this.rotation = rot || [0,0]; this.focal = foc || 0.35; }, - Object: function(vertices, name) { + Object: function(vertices, options) { if(vertices === undefined) throw TypeError("Not enough arguments; Expected Array as argument."); var invalid = [[varType(vertices) !== "Array", varType(vertices)]]; invalid[1] = vertices.every(function(element) { return varType(element) !== "Array"; }); invalid[2] = invalid[1] || vertices.every(function(element) { return element.every(function(number) { return isNaN(number); }); }); if(invalid[0][0]) throw TypeError("Expected Array as argument; Got " + invalid[0][1]); if(invalid[1]) throw TypeError("Expected Arrays as 1st dimensional element."); - if(invalid[2]) throw TypeError("Expected Number as 2nd dimensional element."); + if(invalid[2]) throw TypeError("Expected Numbers as 2nd dimensional element."); - this.name = name || "Unnamed Object"; + this.name = options.name || "Unnamed Object"; this.vertices = vertices; + this.origin = options.origin || getOrigin(vertices); this.translateX = function(value) { this.vertices.forEach(function(ele, index, arr) { @@ -68,20 +69,20 @@ Saku = { Saku["Triangle"] = function(name) { Saku.Object.call(this, [ - [0,4,0], - [0,4,5], - [3,4,3] - ], (name || "Triangle")); + [0,2,0], + [0,2,5], + [3,2,3] + ], {name: (name || "Triangle")}); this.prototype = Object.create(Saku.Object.prototype); } Saku["Square"] = function(name) { Saku.Object.call(this, [ - [0,4,60], - [20,4,60], - [0,4,80], - [20,4,80] - ], (name || "Square")); + [0,1,5], + [5,1,5], + [5,1,10], + [0,1,10] + ], {name: (name || "Square")}); this.prototype = Object.create(Saku.Object.prototype); } @@ -94,13 +95,22 @@ function varType(variable) { } } +function getOrigin(vert) { + var origin = [0,0,0]; + for(var i = 0; i < vert[0].length; i++) { + for(var j = 0; j < vert.length; j++) origin[i] += vert[j][i]; + origin[i] /= Rnd(vert.length,3); + } + return origin; +} + function updateFrame(scene) { var ctx = document.getElementById(scene.canvas).getContext("2d"); ctx.clearRect(0,0,canvas.width,canvas.height) for(var i = 0; i < scene.objects.length; i++) { var object = scene.objects[i]; object = projectObj(object, scene); - console.log(object); + ctx.beginPath(); ctx.moveTo(object[0][0],object[0][1]); for(var j = 1; j < object.length; j++) { @@ -133,6 +143,7 @@ function projectObj(object, scene) { }, "Camera Focal": cF, "Camera Vector": vecToObj(cV), + "Object Origin": vecToObj(object.origin), "Object Vertice Values": {}, "Canvas Points": {} } @@ -163,13 +174,16 @@ function projectObj(object, scene) { var projOppX = distCX*oppX/adjX; // Represents X in projective plane. var projOppZ = distCZ*oppZ/adjZ; // Represents Y in projective plane. - // If the dot product is greater than 0, b is on the right of a. - if(adjX > 0) projOppX *= -1; - if(adjZ < 0) projOppZ *= -1; + // If the dot product of a and b rotated -pi/2 is greater than 0, b is on the right of a. + var aheadX = dot([cV[0],-pV[1]],[cV[1],pV[0]]) > 0; + var aheadZ = dot([cV[2],-pV[1]],[cV[1],pV[2]]) > 0; + + if(aheadX) projOppX *= -1; + if(aheadZ) projOppZ *= -1; var canvas = document.getElementById(scene.canvas); var canvasPointX = Rnd(canvas.width/2+projOppX*scene.pixelScale,3); - var canvasPointY = Rnd(canvas.height/2+projOppZ*scene.pixelScale,3); + var canvasPointY = Rnd(canvas.height/2-projOppZ*scene.pixelScale,3); if(verbose) { var num = "Point " + (i+1).toString(); @@ -263,7 +277,7 @@ canvas.width = window.innerWidth; canvas.height = window.innerHeight; drawObjects = []; -verbose = true; // [[Position],[Rotation(x,z)],Focal]; +verbose = false; // [[Position],[Rotation(x,z)],Focal]; scene = new Saku.Scene("glCanvas"); var triangle = new Saku.Triangle(); @@ -274,4 +288,4 @@ scene.setCamera(camera); scene.addObject(triangle); scene.addObject(square); -setInterval(updateFrame(scene), 500); \ No newline at end of file +setInterval(function() { updateFrame(scene); }, 500); From e694f6e8cddde2dede96e43b0269a975cd146c18 Mon Sep 17 00:00:00 2001 From: Kenneth Jao Date: Wed, 30 Aug 2017 01:01:44 -0400 Subject: [PATCH 7/9] fixed camera vector calculations --- saku/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/saku/index.js b/saku/index.js index d69484b..aa1580e 100644 --- a/saku/index.js +++ b/saku/index.js @@ -128,8 +128,8 @@ function projectObj(object, scene) { var cF = scene.camera.focal; // Camera direction vector var cV = [ - Rnd(cF*Math.sin(toRad(cR[1])),3), // 0 Degrees Z points straight to Y. - Rnd(cF*Math.cos(toRad(cR[1])),3), + Rnd(cF*Math.cos(toRad(cR[0]))*Math.sin(toRad(cR[1])),3), // 0 Degrees Z points straight to Y. + Rnd(cF*Math.cos(toRad(cR[0]))*Math.cos(toRad(cR[1])),3), Rnd(cF*Math.sin(toRad(cR[0])),3) // 0 Degrees X points straight to Y. ]; From 2b3ce55a1d67b8a740b25a89f107c00f427c5973 Mon Sep 17 00:00:00 2001 From: Kenneth Jao Date: Sun, 10 Sep 2017 02:15:51 -0400 Subject: [PATCH 8/9] fixed perspective mapping --- saku/index.js | 61 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/saku/index.js b/saku/index.js index aa1580e..487bb00 100644 --- a/saku/index.js +++ b/saku/index.js @@ -6,7 +6,7 @@ Saku = { //invalid this.canvas = canvas; this.globalScale = "centimeter"; - this.pixelScale = pixelScale || 100; + this.pixelScale = pixelScale || 1000; this.camera = undefined; this.objects = []; this.addObject = function(obj) { @@ -20,11 +20,12 @@ Saku = { this.camera = cam; } }, - Camera: function(pos, rot, foc) { + Camera: function(pos, rot, foc, size) { //invalid this.position = pos || [0,0,0]; this.rotation = rot || [0,0]; this.focal = foc || 0.35; + this.size = size || 0.32; }, Object: function(vertices, options) { if(vertices === undefined) throw TypeError("Not enough arguments; Expected Array as argument."); @@ -69,19 +70,19 @@ Saku = { Saku["Triangle"] = function(name) { Saku.Object.call(this, [ - [0,2,0], - [0,2,5], - [3,2,3] + [0,50,0], + [0,50,5], + [3,50,3] ], {name: (name || "Triangle")}); this.prototype = Object.create(Saku.Object.prototype); } Saku["Square"] = function(name) { Saku.Object.call(this, [ - [0,1,5], - [5,1,5], - [5,1,10], - [0,1,10] + [0,50,5], + [5,50,5], + [5,50,10], + [0,50,10] ], {name: (name || "Square")}); this.prototype = Object.create(Saku.Object.prototype); } @@ -123,6 +124,7 @@ function updateFrame(scene) { function projectObj(object, scene) { var log = []; var newShape = []; + var canvas = document.getElementById(scene.canvas); var cP = scene.camera.position; var cR = scene.camera.rotation; var cF = scene.camera.focal; @@ -163,27 +165,29 @@ function projectObj(object, scene) { /* Adjacent and Opposite calculated with math simplifications cos(arccosx) = x - sin(arccosx) = sqrt(1-x^2) + sin(arccosx) = sqrt(1-x^2) or Pythagorean theorem */ - var adjX = dot(dim("XY",cV),dim("XY",pV)) / distCX; - var adjZ = dot(dim("ZY",cV),dim("ZY",pV)) / distCZ; + var adjX = Rnd(dot(dim("XY",cV),dim("XY",pV)) / distCX,5); + var adjZ = Rnd(dot(dim("ZY",cV),dim("ZY",pV)) / distCZ,5); - var oppX = distPX * Math.sqrt(1-Math.pow(adjX/distPX,2)); - var oppZ = distPZ * Math.sqrt(1-Math.pow(adjZ/distPZ,2)); + var oppX = Math.sqrt(Math.pow(distPX,2) - Math.pow(adjX,2)); + var oppZ = Math.sqrt(Math.pow(distPZ,2) - Math.pow(adjZ,2)); - var projOppX = distCX*oppX/adjX; // Represents X in projective plane. - var projOppZ = distCZ*oppZ/adjZ; // Represents Y in projective plane. - // If the dot product of a and b rotated -pi/2 is greater than 0, b is on the right of a. - var aheadX = dot([cV[0],-pV[1]],[cV[1],pV[0]]) > 0; - var aheadZ = dot([cV[2],-pV[1]],[cV[1],pV[2]]) > 0; + var projOppX = Rnd(distCX*oppX/adjX,5); // Represents X in projective plane. + var projOppZ = Rnd(distCZ*oppZ/adjZ,5); // Represents Y in projective plane. + // If the dot product of a and (b rotated -pi/2) is greater than 0, b is on the right of a. + var aheadX = dot([cV[0],cV[1]],[-pV[1],pV[0]]); + var aheadZ = dot([cV[2],cV[1]],[-pV[1],pV[2]]); - if(aheadX) projOppX *= -1; - if(aheadZ) projOppZ *= -1; + if(aheadX < 0) projOppX *= -1; + if(aheadZ > 0) projOppZ *= -1; - var canvas = document.getElementById(scene.canvas); - var canvasPointX = Rnd(canvas.width/2+projOppX*scene.pixelScale,3); - var canvasPointY = Rnd(canvas.height/2-projOppZ*scene.pixelScale,3); + projOppX += scene.camera.size/2; + projOppZ += (canvas.height/canvas.width)*scene.camera.size/2; + + var canvasPointX = Rnd(projOppX * canvas.width/scene.camera.size,3); + var canvasPointY = Rnd(projOppZ * canvas.width/scene.camera.size,3); if(verbose) { var num = "Point " + (i+1).toString(); @@ -199,7 +203,9 @@ function projectObj(object, scene) { "OppositeXY": oppX, "OppositeZY": oppZ, "ProjectedOppXY": projOppX, - "ProjectedOppZY": projOppZ + "ProjectedOppZY": projOppZ, + "AheadXY": aheadX, + "AheadZY": aheadZ }; log["Canvas Points"][num] = vecToObj([canvasPointX, canvasPointY]); } @@ -289,3 +295,8 @@ scene.addObject(triangle); scene.addObject(square); setInterval(function() { updateFrame(scene); }, 500); + +function verb() { + verbose = true; + setTimeout(function() { verbose = false; }, 600); +} From bc578c80b9b34d57c8d636ddc01433b9c0540c9e Mon Sep 17 00:00:00 2001 From: Kenneth Jao Date: Wed, 6 Dec 2017 13:51:51 -0500 Subject: [PATCH 9/9] 3D organization, better Round function, arrayOperation function --- saku/index.js | 97 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 23 deletions(-) diff --git a/saku/index.js b/saku/index.js index 487bb00..aa6f263 100644 --- a/saku/index.js +++ b/saku/index.js @@ -27,7 +27,7 @@ Saku = { this.focal = foc || 0.35; this.size = size || 0.32; }, - Object: function(vertices, options) { + Face: function(vertices, options) { if(vertices === undefined) throw TypeError("Not enough arguments; Expected Array as argument."); var invalid = [[varType(vertices) !== "Array", varType(vertices)]]; invalid[1] = vertices.every(function(element) { return varType(element) !== "Array"; }); @@ -36,7 +36,7 @@ Saku = { if(invalid[1]) throw TypeError("Expected Arrays as 1st dimensional element."); if(invalid[2]) throw TypeError("Expected Numbers as 2nd dimensional element."); - this.name = options.name || "Unnamed Object"; + this.name = options.name || "Unnamed Object Face"; this.vertices = vertices; this.origin = options.origin || getOrigin(vertices); @@ -65,26 +65,68 @@ Saku = { arr[index][2] = ele[2] + value; }); } + }, + Model: function(faces, connections, name) { + this.name = options.name || "Unnamed Object Model"; + this.faces = faces; + this.connections = connections; } } -Saku["Triangle"] = function(name) { - Saku.Object.call(this, [ - [0,50,0], - [0,50,5], - [3,50,3] - ], {name: (name || "Triangle")}); - this.prototype = Object.create(Saku.Object.prototype); +var ref = ["Triangle", "Square", "Pentagon", "Hexagon", "Heptagon", "Octagon", "Nonagon", "Decagon"]; + +Saku["Polygon"] = function(sides, name, size) { + var defArray = []; + var offset = !(sides%2)*Math.PI/sides; + for(var i = 0; i < sides; i++) { + defArray.push([ + Math.sin(i*-2*Math.PI/sides+offset), + 0, + Math.cos(i*-2*Math.PI/sides+offset) + ]); + } + + defArray = Rnd(defArray,5); + Saku.Face.call(this, defArray, {name: (name || (sides > 10) ? sides+"-gon" : ref[sides-3])}); + this.prototype = Object.create(Saku.Face.prototype); } -Saku["Square"] = function(name) { - Saku.Object.call(this, [ - [0,50,5], - [5,50,5], - [5,50,10], - [0,50,10] - ], {name: (name || "Square")}); - this.prototype = Object.create(Saku.Object.prototype); +ref.forEach(function(ele, index) { + Saku[ele] = function(name) { + Saku.Polygon.call(this, index+3, name); + this.prototype = Object.create(Saku.Face.prototype); + } +}); + +/*Saku["Cube"] = function(name) { + var defArray = [ + [-0.5, -0.5, 0.5], + [0.5, -0.5, 0.5], + [] + ] +}*/ + +function arrayOperation(item, operator, amount) { + var operators = { + "+": function(x,y) {return x+y}, + "-": function(x,y) {return x-y}, + "*": function(x,y) {return x*y}, + "/": function(x,y) {return x/y}, + "^": function(x,y) {return Math.pow(x,y)}, + "log": function(x,y) {return Math.log(x) / Math.log(y || 10)} + } + var type = varType(item); + if(type === "Array") { + var arr = []; + for(var i = 0; i < item.length; i++) { + arr[i] = arrayOperation(item[i], operator, amount); + } + return arr; + } else if(type === "Number") { + return operators[operator](item, amount); + } else { + throw new TypeError("Expected Numbers, got " + type + "."); + } } function varType(variable) { @@ -219,8 +261,18 @@ function toRad(deg) { return deg/180*Math.PI; } -function Rnd(num,fig) { - return Math.round(num*Math.pow(10,fig))/Math.pow(10,fig); +function Rnd(item,fig) { + if(varType(item) === "Array") { + var arr = []; + for(var i = 0; i < item.length; i++) { + arr[i] = Rnd(item[i],fig); + } + return arr; + } else if(varType(item) === "Number") { + return Math.round(item*Math.pow(10,fig))/Math.pow(10,fig); + } else { + throw new TypeError("Expected Integers, got " + varType(item) + "."); + } } function dot(vecOne, vecTwo) { @@ -286,13 +338,12 @@ drawObjects = []; verbose = false; // [[Position],[Rotation(x,z)],Focal]; scene = new Saku.Scene("glCanvas"); -var triangle = new Saku.Triangle(); -var square = new Saku.Square(); +var polygon = new Saku.Polygon(3); var camera = new Saku.Camera(); scene.setCamera(camera); -scene.addObject(triangle); -scene.addObject(square); +scene.addObject(polygon); +polygon.translateY(10); setInterval(function() { updateFrame(scene); }, 500);