|
| 1 | + |
| 2 | +var utils = require("./utils.js").Utils; |
| 3 | +var paper = require("./paperjs/src/load.js"); |
| 4 | +var colors = require('colors'); |
| 5 | +var fs = require("fs"); |
| 6 | + |
| 7 | +var precision = { |
| 8 | + point: 0.01, |
| 9 | + area: 20, |
| 10 | + bitmap: 20 |
| 11 | +}; |
| 12 | + |
| 13 | +var testModes = { |
| 14 | + paths: false, |
| 15 | + curves: false, |
| 16 | + area: true, |
| 17 | + bitmap: true |
| 18 | +}; |
| 19 | + |
| 20 | +var width = 600, height = 600; |
| 21 | +var canvas = new paper.Canvas(width, height); |
| 22 | +var ctx = canvas.getContext("2d"); |
| 23 | + |
| 24 | +paper.setup(canvas); |
| 25 | + |
| 26 | +var fname = "", fSplit = /(\\|\/)?(.*(\\|\/))*(.*).json/i, |
| 27 | + testName, suit, testCount, timeDiff, fn, |
| 28 | + casesDone = 0, casesPassed = 0, casesFailed = 0, |
| 29 | + i, j, li, lj, cases, tCase, op1, op2, res, resOriginal, |
| 30 | + testResult = {}, testRunCount = 0, boolTime = 0, txt, |
| 31 | + // Define styles for previewing |
| 32 | + style1 = { fillColor: null, strokeColor: new paper.Color(0,0,0) }, |
| 33 | + style2 = { strokeColor: new paper.Color(1,0,0), fillColor: new paper.Color(1,0,0, 0.4) }, |
| 34 | + styleBlack = { strokeColor: null, fillColor: new paper.Color(0,0,0) }, |
| 35 | + styleNull = { strokeColor: null, fillColor: null }, |
| 36 | + // Export failed cases |
| 37 | + failCases = { cases: [] }; |
| 38 | + |
| 39 | +// Process args |
| 40 | +if(process.argv.length < 3){ |
| 41 | + console.log("Usage : node boolean-test.js <filename> [options]") |
| 42 | + return; |
| 43 | +} |
| 44 | + |
| 45 | +var fname = "/" + process.argv[2]; |
| 46 | +testName = fSplit.exec(fname)[4]; |
| 47 | + |
| 48 | +// Load the test suit |
| 49 | +utils.log("Loading test suit \"" + testName + "\"..."); |
| 50 | +utils.timer.start("load"); |
| 51 | +suit = utils.loadJSONfile(__dirname + fname); |
| 52 | +fn = suit.fn; |
| 53 | +suit = suit.tests; |
| 54 | +timeDiff = utils.timer.end("load", "ms").toFixed(1); |
| 55 | +// Find the cumulative test count |
| 56 | +testCount = suit.reduce(function(sum, a){ return sum + a.cases.length }, 0); |
| 57 | +utils.log(testCount + " tests loaded from test suit \"" + testName + "\"; in " + timeDiff + " ms."); |
| 58 | + |
| 59 | +console.log(); |
| 60 | +utils.log("Testing paperjs " + fn + " operator.", testName); |
| 61 | + |
| 62 | + |
| 63 | +// begin the actual test |
| 64 | +// TODO: modularize! |
| 65 | +utils.progress.setup(testName, testCount); |
| 66 | +utils.timer.start("test"); |
| 67 | +var vectorTest, rasterTest; |
| 68 | +for (i = 0, li = suit.length; i < li; i++) { |
| 69 | + op1 = utils.getPath(paper, suit[i].op1); |
| 70 | + cases = suit[i].cases; |
| 71 | + // console.log(suit[i].name); |
| 72 | + for (j = 0, lj = cases.length; j < lj; j++) { |
| 73 | + // DEBUG:============================================================ |
| 74 | + // console.log(cases[j].name); |
| 75 | + // DEBUG:============================================================ |
| 76 | + tCase = cases[j]; |
| 77 | + op2 = utils.getPath(paper, tCase.op2); |
| 78 | + resOriginal = utils.getPath(paper, tCase.res); |
| 79 | + |
| 80 | + // Perform the paperjs boolean op |
| 81 | + utils.timer.start("bool"); |
| 82 | + if(fn === "subtract") |
| 83 | + res = op2[fn](op1); |
| 84 | + else |
| 85 | + res = op1[fn](op2); |
| 86 | + boolTime += utils.timer.end("bool", "ms"); |
| 87 | + |
| 88 | + ++casesDone; |
| 89 | + vectorTest = compare(res, resOriginal, precision, testResult); |
| 90 | + rasterTest = testRasterArea(); |
| 91 | + if(vectorTest && rasterTest) |
| 92 | + ++casesPassed; |
| 93 | + else{ |
| 94 | + ++casesFailed; |
| 95 | + // Save this failed cases |
| 96 | + failCases.cases.push({ |
| 97 | + "name" : suit[i].name + "_" + tCase.name, |
| 98 | + "fn" : fn, |
| 99 | + "op1" : suit[i].op1, |
| 100 | + "op2" : tCase.op2, |
| 101 | + "res" : tCase.res, |
| 102 | + }); |
| 103 | + // Save a preview file of the failed case |
| 104 | + resOriginal.style = style1; |
| 105 | + res.style = style2; |
| 106 | + txt = new paper.PointText([10, 20]); |
| 107 | + txt.content = (testModes.paths ? "children: " + testResult.child + "(" + testResult.ch1 + ", " + testResult.ch2 + ")" : "") + |
| 108 | + (testModes.curves ? ", curves: " + testResult.curves : "") + |
| 109 | + (testModes.area ? ", area: " + testResult.area : ""); |
| 110 | + txt.fillColor = "#000"; |
| 111 | + paper.view.draw(); |
| 112 | + fs.writeFileSync(__dirname + "/out/test_" + testName + "_" + suit[i].name + "_" + tCase.name + ".png", canvas.toBuffer()); |
| 113 | + if(testModes.bitmap) |
| 114 | + testRasterArea(__dirname + "/out/test_" + testName + "_" + suit[i].name + "_" + tCase.name + "_v.png", canvas.toBuffer()); |
| 115 | + txt.remove(); |
| 116 | + } |
| 117 | + // console.log(testResult); |
| 118 | + |
| 119 | + ++testRunCount; |
| 120 | + op1.remove(); |
| 121 | + op2.remove(); |
| 122 | + res.remove(); |
| 123 | + resOriginal.remove(); |
| 124 | + utils.progress.update(casesDone, casesPassed, casesFailed); |
| 125 | + } |
| 126 | +} |
| 127 | +timeDiff = formatTimeIvl(utils.timer.end("test", "ms")); |
| 128 | +utils.progress.close(); |
| 129 | + |
| 130 | +// Save the failed cases to disk, if any. |
| 131 | +if(failCases.cases.length){ |
| 132 | + if(testName) |
| 133 | + fs.writeFileSync(__dirname + "/out/" + testName + "-fail.json", JSON.stringify(failCases)); |
| 134 | +} |
| 135 | + |
| 136 | +// Print the test report |
| 137 | +console.log(); |
| 138 | +console.log(" Test Report [" + testName + "]"); |
| 139 | +console.log("----------------------------------------------------"); |
| 140 | +console.log(" Tests run - " + testRunCount.toString()); |
| 141 | +console.log(" Tests passed - " + casesPassed.toString().green + " " + (casesPassed * 100.0/testRunCount).toFixed(1) + "%"); |
| 142 | +console.log(" Tests failed - " + casesFailed.toString().red + " " + (casesFailed * 100.0/testRunCount).toFixed(1) + "%"); |
| 143 | +console.log(); |
| 144 | +console.log(" Elapsed - " + timeDiff); |
| 145 | +console.log(" Boolean op. time - " + formatTimeIvl(boolTime)); |
| 146 | +console.log(" Boolean avg. time - " + formatTimeIvl(boolTime / testRunCount)); |
| 147 | +console.log(); |
| 148 | +// --END-- |
| 149 | + |
| 150 | +// Compare raster |
| 151 | +function testRasterArea(saveFileName) { |
| 152 | + if (!testModes.bitmap) |
| 153 | + return true; |
| 154 | + |
| 155 | + res.style = resOriginal.style = styleNull; |
| 156 | + paper.view.draw(); |
| 157 | + ctx.antialias = 'none'; |
| 158 | + resOriginal.style = res.style = styleBlack; |
| 159 | + res.blendMode = 'xor'; |
| 160 | + paper.view.draw(); |
| 161 | + // Accumulated pixels |
| 162 | + var cumul = 0; |
| 163 | + // Run the test |
| 164 | + if(!saveFileName){ |
| 165 | + var image = ctx.getImageData(0, 0, width, height), |
| 166 | + pixels = image.data, cw = image.width * 4, ch = image.height, |
| 167 | + imgi, imgj; |
| 168 | + for (imgj = 0; imgj < ch; imgj++) { |
| 169 | + var pixrow = imgj * cw; |
| 170 | + for (imgi = 0; imgi < cw; imgi += 4) { |
| 171 | + if(pixrow[pixrow + imgi]) |
| 172 | + ++cumul; |
| 173 | + } |
| 174 | + } |
| 175 | + } else |
| 176 | + // Save the bitmap |
| 177 | + fs.writeFileSync(saveFileName, canvas.toBuffer()); |
| 178 | + |
| 179 | + // Reset the default values |
| 180 | + ctx.antialias = 'default'; |
| 181 | + res.blendMode = 'normal'; |
| 182 | + |
| 183 | + return cumul < precision.bitmap; |
| 184 | +} |
| 185 | + |
| 186 | +// Compare two paths |
| 187 | +function compare(p1, p2, precision, results) { |
| 188 | + var eqChild = false, eqCurves = false, eqArea = false, |
| 189 | + ch1 = p1 instanceof paper.CompoundPath ? p1.children.length : 1, |
| 190 | + ch2 = p2 instanceof paper.CompoundPath ? p2.children.length : 1, |
| 191 | + crv1 = p1.getCurves(), crv2 = p2.getCurves(), |
| 192 | + a1 = Math.abs(p1.getArea()) | 0, a2 = Math.abs(p2.getArea()) | 0; |
| 193 | + |
| 194 | + eqChild = ch1 === ch2; |
| 195 | + eqCurves = crv1.length === crv2.length; |
| 196 | + eqArea = Math.abs(a1 - a2) < precision.area; |
| 197 | + |
| 198 | + if (results){ |
| 199 | + results.child = eqChild; |
| 200 | + results.ch1 = ch1; |
| 201 | + results.ch2 = ch2; |
| 202 | + results.curves = eqCurves; |
| 203 | + results.area = eqArea; |
| 204 | + } |
| 205 | + |
| 206 | + return (!testModes.paths || eqChild) && (!testModes.curves || eqCurves) && (!testModes.area || eqArea); |
| 207 | +} |
| 208 | + |
| 209 | +function formatTimeIvl(tms) { |
| 210 | + var ts = 0, tm = 0; |
| 211 | + if (tms > 1000){ |
| 212 | + ts = (tms / 1000.0) | 0; |
| 213 | + tms -= ts * 1000; |
| 214 | + } |
| 215 | + if (ts > 60){ |
| 216 | + tm = (ts / 60.0) | 0; |
| 217 | + ts -= tm * 60; |
| 218 | + } |
| 219 | + return tm + " : " + ts + " : " + tms.toFixed(1); |
| 220 | +} |
0 commit comments