From 4ff3b6c03e565d73e142d4b9690b7990b5746cf1 Mon Sep 17 00:00:00 2001 From: Maya Rajan Date: Wed, 9 Oct 2024 12:12:21 -0400 Subject: [PATCH] testing --- extensions/src/doodlebot/Doodlebot.ts | 18 +- extensions/src/doodlebot/LineDetection.ts | 120 +++--- extensions/src/doodlebot/LineFollowing.ts | 402 +++++++++--------- extensions/src/doodlebot/index.ts | 70 ++-- extensions/src/doodlebot/package.json | 1 + extensions/src/doodlebot/pnpm-lock.yaml | 490 ++++++++++++++++++++++ 6 files changed, 798 insertions(+), 303 deletions(-) diff --git a/extensions/src/doodlebot/Doodlebot.ts b/extensions/src/doodlebot/Doodlebot.ts index e25f4cdc6..83b260e10 100644 --- a/extensions/src/doodlebot/Doodlebot.ts +++ b/extensions/src/doodlebot/Doodlebot.ts @@ -620,14 +620,16 @@ export default class Doodlebot { let line4; let line5; let line6; - line0=[[320, 0], [320, 33.3], [320, 66.6], [320.0, 100.0], [290.55547455897363, 121.01450702944364], [270.9257909316227, 140.13821690036886], [259.9562618457501, 157.8072022603516], [256.49220002915877, 174.45753575696776], [259.3789182096516, 190.525290037793], [267.46172911503135, 206.44653775040322], [279.585945473101, 222.6573515423743], [294.5968800116634, 239.59380406128207], [311.33984545852167, 257.6919679547024], [328.66015454147833, 277.387915870211], [345.40311998833647, 299.11772045538373], [360.41405452689895, 323.3174543577966], [372.5382708849686, 350.42319022502517], [380.62108179034846, 380.87100070464555], [383.50779997084123, 415.09695844423345]] - line1=[[320.0, 0.0], [320.0, 33.400000000000006], [290.55547455897363, 54.41450702944364], [270.9257909316227, 73.53821690036887], [259.9562618457501, 91.20720226035161], [256.49220002915877, 107.85753575696776], [259.3789182096516, 123.92529003779302], [267.46172911503135, 139.84653775040323], [279.585945473101, 156.0573515423743], [294.5968800116634, 172.99380406128208], [311.33984545852167, 191.09196795470243], [328.66015454147833, 210.78791587021098], [345.40311998833647, 232.51772045538374], [360.41405452689895, 256.71745435779656], [372.5382708849686, 283.8231902250252], [380.62108179034846, 314.27100070464553], [383.50779997084123, 348.4969584442334], [380.0437381542499, 386.93713609136466], [369.07420906837734, 430.0276062936151]] - line2=[[320.0, 0.0], [311.8369232127996, 26.161134071432784], [310.5980932335586, 46.92140652387483], [315.47549899916805, 63.213866215870496], [325.6611294465186, 75.97156200596407], [340.346973512501, 86.12754275270002], [358.7250201340062, 94.61485731462274], [379.98725824792496, 102.36655455027662], [403.3256767911483, 110.315683318206], [427.9322647005667, 119.39529247695522], [452.9990109130711, 130.53843088506878], [477.71790436555244, 144.67814740109097], [501.2809339949014, 162.74749088356626], [522.8800887380089, 185.67951019103896], [541.7073575317658, 214.40725418205352], [556.9547293130627, 249.86377171515434], [567.8141930187907, 292.9821116488858], [573.4777375858403, 344.6953228417921]] - line3=[[320.0, 0.0], [327.9833280658778, 15.016650851980016], [340.4769463262831, 25.524522770015245], [356.8719083941727, 32.59722944079429], [376.5592678825036, 37.30838455100564], [398.93007840423263, 40.73160178733782], [423.3753935723168, 43.94049483647929], [449.28626699971255, 48.00867738511856], [476.05375229937704, 54.00976311994425], [503.06890308426705, 63.01736572764473], [529.7227729673393, 76.10509889490865], [555.4064155615508, 94.34657630842443], [579.5108844798585, 118.81541165488063], [601.4272333352191, 150.5852186209658], [620.5465157405895, 190.72961089336843], [636.2597853089262, 240.3222021587769]] - line4=[[320.0, 0.0], [328.3624555126027, 15.776162355216856], [340.77079630514635, 31.77054214536007], [356.078208581285, 48.439516590554405], [373.1378785446733, 66.23946291092469], [390.80299239896493, 85.62675832659549], [407.9267363478144, 107.05778005769183], [423.362296594876, 130.98890532433836], [435.96285934380376, 157.8765113466599], [444.5816107982521, 188.17697534478123], [448.07173716187526, 222.34667453882724], [445.28642463832733, 260.8419861489226], [435.07885943126274, 304.11928739519226], [416.3022277443355, 352.63495549776053]] - line5=[[320.0, 0.0], [323.69086129808085, 22.328213599044407], [328.2294799544362, 46.561896128944376], [332.4062433387309, 72.45548836044426], [335.01153882063034, 99.7634310642892], [334.83575376979974, 128.24016501122384], [330.6692755559043, 157.64013097199293], [321.30249154860934, 187.71776971734135], [305.52578911758, 218.227522018014], [282.1295556324815, 248.92382864475547], [249.9041784629792, 279.5611303683108], [207.64004497873827, 309.89386795942426]] - line6=[[320.0, 0.0], [319.6063583708792, 26.22534112814168], [317.41907209558354, 53.569939827233725], [312.2897285040425, 81.58145729752641], [303.0699149261853, 109.80755473927005], [288.6112186919413, 137.79589335271513], [267.7652271312397, 165.09413433811207], [239.38352757400992, 191.24993889571107], [202.31770735018122, 215.81096822576276], [155.4193537896831, 238.32488352851706]] - + line0=[[0.0, 0.0], [-17.104673497907527, 20.564281569525853], [-42.011478766789594, 39.423381796634736], [-75.92074377141333, 56.314965090186675], [-120.03279647654222, 70.97669585904111], [-164.14484918167207, 85.63842662789493], [-198.05411418629512, 102.53000992144658], [-222.9609194551773, 121.38911014855627], [-240.0655929530852, 141.95339171808214], [-250.56846264478227, 163.96051903888355], [-255.66985649503542, 187.14815651981806], [-256.5701024686096, 211.2539685697469], [-254.4695285302698, 236.015619597528], [-250.56846264478273, 261.17077401202096], [-246.06723277691222, 286.4570962220841], [-242.1661668914246, 311.6122506365771], [-240.06559295308506, 336.3739016643582], [-240.96583892665913, 360.4797137142863], [-246.06723277691242, 383.66735119522167], [-256.5701024686097, 405.6744785160225], [-273.674775966517, 426.2387600855489], [-298.5815812353995, 445.0978603126579], [-332.4908462400224, 461.98944360620965]] + // line0=[[-383.1296156102412, 1230.5622560465351], [-365.3300647514816, 1202.4185890994863], [-347.53051389272196, 1174.2749221524373], [-329.677510929282, 1146.0467396890313], [-285.5654582241526, 1131.3850089201762], [-251.65619321952863, 1114.4934256266242], [-226.74938795064645, 1095.6343253995153], [-209.64471445273944, 1075.0700438299891], [-199.1418447610422, 1053.0629165091882], [-194.040450910789, 1029.8752790282526], [-193.1402049372149, 1005.7694669783241], [-195.24077887555472, 981.007815950543], [-199.1418447610423, 955.8526615360504], [-203.64307462891315, 930.5663393259867], [-207.54414051440074, 905.4111849114944], [-209.64471445274, 880.649533883713], [-208.74446847916602, 856.5437218337844], [-203.64307462891315, 833.3560843528488], [-193.14020493721546, 811.348957032048], [-176.0355314393089, 790.7846754625223], [-151.1287261704265, 771.9255752354127], [-117.21946116580324, 755.0339919418606], [-73.10740846067358, 740.3722611730069], [-28.99535575554421, 725.7105304041522], [4.913909249078415, 708.8189471106002], [29.820714517961108, 689.9598468834912], [46.92538801586829, 669.3955653139649], [57.428257707565535, 647.3884379931638], [62.52965155781868, 624.2008005122286], [63.42989753139284, 600.0949884623001], [61.329323593053175, 575.3333374345189], [57.428257707565706, 550.1781830200262], [52.92702783969469, 524.8918608099631], [49.02596195420699, 499.73670639547066], [46.925388015868066, 474.97505536768887], [47.82563398944188, 450.8692433177603], [52.927027839694745, 427.68160583682493], [63.429897531392214, 405.6744785160235], [80.53457102929917, 385.1101969464983], [105.4413762981817, 366.2510967193885], [139.35064130280483, 349.3595134258366], [183.4626940079338, 334.6977826569824], [227.57474671306295, 320.0360518881285], [261.48401171768626, 303.1444685945765], [286.3908169865691, 284.2853683674667]] + line1=[[320.0, 0.0], [320.0, 23.742172418771816], [324.30030842448235, 47.4783887316922], [331.67226572359556, 71.21035068329016], [340.8872123474861, 94.93976001809521], [350.7164887463037, 118.66831848063619], [359.93143537019427, 142.3977278154408], [367.30339266930685, 166.12968976703922], [371.6037010937892, 189.86590607995961], [371.60370109378977, 213.60807849873117], [366.07473311945506, 237.35790876788278], [353.788137620935, 261.11709863194324], [333.51525504837565, 284.88734983544157], [304.0274258519256, 308.6703641229065], [264.09599048173254, 332.46784323886675], [224.1645551115397, 356.26532235482824], [194.6767259150903, 380.0483366422928], [174.4038433425307, 403.8185878457908], [162.11724784401008, 427.5777777098518], [156.58827986967603, 451.32760797900346]] + line2=[[320.0, 0.0], [320.0, 23.74217241877156], [314.4710320256647, 47.492002687923026], [302.1844365271441, 71.2511925519832], [281.9115539545842, 95.02144375548104], [252.42372475813357, 118.80445804294527], [212.49228938793993, 142.60193715890455], [172.56085401774652, 166.3994162748651], [143.07302482129654, 190.18243056232893], [122.80014224873636, 213.95268176582644], [110.51354675021517, 237.71187162988713], [104.98457877588055, 261.4617018990387], [104.98457877588001, 285.20387431781006], [109.2848872003618, 308.9400906307306], [116.65684449947389, 332.672052582329], [125.87179112336378, 356.4014619171344], [135.70106752218086, 380.1300203796749], [144.91601414607132, 403.8594297144797], [152.28797144518282, 427.59139166607855], [156.588279869665, 451.3276079789992]] + line3=[[320.0, 0.0], [320.0, 31.24123253135492], [326.069280541584, 57.2916330205665], [337.27410615681515, 78.94979101257925], [352.6807413777573, 97.01429605233866], [371.3554507364751, 112.28373768479007], [392.36449876503275, 125.55670545487848], [414.7741499954941, 137.63178890754958], [437.65066895992396, 149.30757758774695], [460.06032019038537, 161.38266104041722], [481.0693682189428, 174.65562881050636], [499.7440775776611, 189.9250704429576], [515.1507127986035, 207.98957548271702], [526.3555384138346, 229.64773347472993], [532.4248189554181, 255.6981339639407], [532.4248189554188, 286.93936649529616], [525.4218029458999, 324.17002061373967], [510.4820354589266, 368.1886858642177], [495.54226797195287, 412.20735111469503]] + line4=[[320.0, 0.0], [320.0, 25.45584625699707], [318.2810797223905, 50.246914922370934], [313.6972923154343, 73.9300209350388], [305.1026909273915, 96.06197923391805], [291.35132870652296, 116.19960475792656], [271.2972588010903, 133.8997124459814], [243.79453435935335, 148.71911723700137], [207.6972085295742, 160.21463406990244], [161.85933446001326, 167.94307788360373], [116.02146039045272, 175.67152169730434], [79.9241345606728, 187.16703853020582], [52.42141011893614, 201.98644332122558], [32.367340213503155, 219.68655100928046], [18.61597799263444, 239.82417653328918], [10.02137660459141, 261.95613483216806], [5.437589197636157, 285.63924084483597], [3.718668920026289, 310.43030951020984], [3.7186689200267438, 335.88615576720713], [4.2916423458953545, 361.56359455474563], [4.291642345894672, 387.01944081174275], [2.5727220682860548, 411.810509477117], [-2.011065338669596, 435.49361548978413], [-10.605666726712684, 457.6255737886634]] + line5=[[320.0, 0.0], [320.0, 46.484831321609256], [319.9999999999994, 92.969662643218], [325.3340819230948, 130.47581426337925], [335.37470671951024, 160.0596014410853], [349.49433533946905, 182.77733943533053], [367.0654287331962, 199.68534350510947], [387.4604478509146, 211.83992890941542], [410.0518536428496, 220.2974109072414], [434.2121070592242, 226.11410475758467], [459.3136690502629, 230.3463257194352], [484.72900056618926, 234.05038905179012], [509.83056255722755, 238.28261001364174], [533.9908159736027, 244.09930386398383], [556.5822217655369, 252.5567858618101], [576.9772408832557, 264.71137126611615], [594.5483342769828, 281.61937533589463], [608.667962896942, 304.3371133301405], [618.708587693357, 333.92090050784526], [624.0426696164516, 371.42705212800644], [624.0426696164528, 417.9118834496162]] + line6=[[320.0, 0.0], [320.0, 24.85058897676393], [321.760786046391, 50.24546525444484], [324.1085007749103, 75.82177063243151], [325.86928682130014, 101.21664691011227], [325.8692868213014, 126.06723588687649], [322.9346434106544, 150.0106793621108], [315.8914992250986, 172.68411913520555], [303.5659969003755, 193.72469700554865], [284.78427907222476, 212.76955477252855], [258.3724883763891, 229.45583423553302], [223.15676744860562, 243.42067719395098], [177.96325892461726, 254.30122544717358], [132.76975040062902, 265.18177370039484], [97.55402947284608, 279.1466166588124], [71.14223877701002, 295.83289612181727], [52.36052094885912, 314.8777538887973], [40.0350186241368, 335.91833175914024], [32.99187443857954, 358.59177153223476], [30.057231027931437, 382.53521500746945], [30.057231027933767, 407.385803984234], [31.81801707432402, 432.7806802619144]] + //line7=[[320.0, 0.0], [320.0, 26.74807529413604], [312.911198184821, 57.174437201259146], [297.6430096598178, 91.84497596952409], [273.1048495303509, 131.3255818470832], [248.5666894008832, 170.80618772464132], [233.2985008758801, 205.47672649290558], [226.2096990607011, 235.90308840002928], [226.2096990607011, 262.6511636941655], [232.20791598123856, 286.2868426234638], [243.11376492766777, 307.3760154360794], [257.8366610053482, 326.4845723801618], [275.2860193196383, 344.17840370386347], [294.3712549758911, 361.02339965533696], [314.0017830794657, 377.585450482735], [333.0870187357199, 394.4304464342091], [350.5363770500079, 412.1242777579095], [365.2592731276887, 431.2328347019926], [376.16512207411927, 452.3220075146087]] + const delay = 0.5; const previousSpeed = 0.1; diff --git a/extensions/src/doodlebot/LineDetection.ts b/extensions/src/doodlebot/LineDetection.ts index 37c041b95..21ce4a3d9 100644 --- a/extensions/src/doodlebot/LineDetection.ts +++ b/extensions/src/doodlebot/LineDetection.ts @@ -1,61 +1,61 @@ -import { createCanvas, loadImage } from 'canvas'; -import { endpoint, port } from "./enums"; - -export class LineDetector { - private lastDetectedLine: number[][] = []; - private isProcessing = false; - private canvas: any; - private ctx: any; - - constructor(private raspberryPiIp: string, private width = 640, private height = 480) { - this.canvas = createCanvas(this.width, this.height); - this.ctx = this.canvas.getContext('2d'); - } - - async detectLine(): Promise { - if (this.isProcessing) return this.lastDetectedLine; - this.isProcessing = true; - - try { - const image = await loadImage(`http://${this.raspberryPiIp}:${port.camera}/${endpoint.video}`); - this.ctx.drawImage(image, 0, 0, this.width, this.height); - const imageData = this.ctx.getImageData(0, 0, this.width, this.height); - const lineCoordinates = this.processImageData(imageData); - - if (lineCoordinates.length > 0) { - this.lastDetectedLine = lineCoordinates; - } - - return this.lastDetectedLine; - } catch (error) { - console.error('Error detecting line:', error); - return this.lastDetectedLine; - } finally { - this.isProcessing = false; - } - } - - private processImageData(imageData: ImageData): number[][] { - const lineCoordinates: number[][] = []; - const threshold = 50; - - for (let y = 0; y < this.height; y++) { - for (let x = 0; x < this.width; x++) { - const index = (y * this.width + x) * 4; - const r = imageData.data[index]; - const g = imageData.data[index + 1]; - const b = imageData.data[index + 2]; +// import { createCanvas, loadImage } from 'canvas'; +// import { endpoint, port } from "./enums"; + +// export class LineDetector { +// private lastDetectedLine: number[][] = []; +// private isProcessing = false; +// private canvas: any; +// private ctx: any; + +// constructor(private raspberryPiIp: string, private width = 640, private height = 480) { +// this.canvas = createCanvas(this.width, this.height); +// this.ctx = this.canvas.getContext('2d'); +// } + +// async detectLine(): Promise { +// if (this.isProcessing) return this.lastDetectedLine; +// this.isProcessing = true; + +// try { +// const image = await loadImage(`http://${this.raspberryPiIp}:${port.camera}/${endpoint.video}`); +// this.ctx.drawImage(image, 0, 0, this.width, this.height); +// const imageData = this.ctx.getImageData(0, 0, this.width, this.height); +// const lineCoordinates = this.processImageData(imageData); + +// if (lineCoordinates.length > 0) { +// this.lastDetectedLine = lineCoordinates; +// } + +// return this.lastDetectedLine; +// } catch (error) { +// console.error('Error detecting line:', error); +// return this.lastDetectedLine; +// } finally { +// this.isProcessing = false; +// } +// } + +// private processImageData(imageData: ImageData): number[][] { +// const lineCoordinates: number[][] = []; +// const threshold = 50; + +// for (let y = 0; y < this.height; y++) { +// for (let x = 0; x < this.width; x++) { +// const index = (y * this.width + x) * 4; +// const r = imageData.data[index]; +// const g = imageData.data[index + 1]; +// const b = imageData.data[index + 2]; - if (r < threshold && g < threshold && b < threshold) { - lineCoordinates.push([x, y]); - } - } - } - return lineCoordinates.sort((a, b) => a[1] - b[1]); - } -} - -export function createLineDetector(raspberryPiIp: string): () => Promise { - const detector = new LineDetector(raspberryPiIp); - return () => detector.detectLine(); -} \ No newline at end of file +// if (r < threshold && g < threshold && b < threshold) { +// lineCoordinates.push([x, y]); +// } +// } +// } +// return lineCoordinates.sort((a, b) => a[1] - b[1]); +// } +// } + +// export function createLineDetector(raspberryPiIp: string): () => Promise { +// const detector = new LineDetector(raspberryPiIp); +// return () => detector.detectLine(); +// } \ No newline at end of file diff --git a/extensions/src/doodlebot/LineFollowing.ts b/extensions/src/doodlebot/LineFollowing.ts index 4263975b3..9a43cd241 100644 --- a/extensions/src/doodlebot/LineFollowing.ts +++ b/extensions/src/doodlebot/LineFollowing.ts @@ -1,12 +1,11 @@ import * as Spline from "cubic-spline"; import * as Bezier from "bezier-js"; -import Doodlebot from "./Doodlebot"; // CONSTANTS const cameraMatrix = [ - [1000, 0, 320], // fx, 0, cx - [0, 1000, 240], // 0, fy, cy - [0, 0, 1] // 0, 0, 1 + [1000, 0, 320], // fx, 0, cx + [0, 1000, 240], // 0, fy, cy + [0, 0, 1] // 0, 0, 1 ]; const wheelBase = 0.0716; @@ -15,159 +14,159 @@ const bezierIncrement = .01; const linearSpeed = .1; const delay = 0.5; -const previousSpeed = 0.1; +const previousSpeed = .1; + + +function distanceBetweenPoints(x1, y1, x2, y2) { + //console.log(x1, x2, y1, y2); + const ground1 = pixelToGroundCoordinates([x1, y1]); + const ground2 = pixelToGroundCoordinates([x2, y2]); + // console.log("calulcating"); + // console.log(ground1); + // console.log(ground2); + const dx = ground2.x - ground1.x; + const dy = ground2.y - ground2.y; + if (isNaN(ground1.x) || isNaN(ground2.x) || isNaN(ground1.y) || isNaN(ground2.y)) { + // console.log(ground1); + // console.log(ground2); + // console.log(x2, y2); + // console.log("hmmm"); + + } + return Math.sqrt(dx * dx + dy * dy); + } + + function findPointAtDistanceWithIncrements(spline: Spline, increment: number, desiredDistance: number): number { + let totalDistance = 0; + const xValues = spline.xs; + console.log(xValues); + + // Iterate through each pair of xValues in the array + for (let i = 0; i < xValues.length - 1; i++) { + let currentX = xValues[i]; + const nextX = xValues[i + 1]; + + // Check the direction between currentX and nextX (allow for backtracking) + let direction = nextX > currentX ? 1 : -1; + + console.log(direction); + // Step through each segment in increments, adjusting for direction + while ((direction === 1 && currentX < nextX) || (direction === -1 && currentX > nextX)) { + + const nextXIncrement = currentX + direction * increment; // Increment or decrement by step size + + const currentY = spline.at(currentX); + const nextY = spline.at(nextXIncrement); + // console.log(spline); + // console.log(currentX, currentY); + + // Calculate distance between current and next increment + if (isNaN(nextY)) { + console.log(nextXIncrement); + console.log( + "MMM" + ); + console.log(spline); + console.log(totalDistance); + console.log(nextXIncrement); + } + const distance = distanceBetweenPoints(currentX, currentY, nextXIncrement, nextY); + + totalDistance += distance; + + // Check if the accumulated distance is equal to or exceeds the desired distance + + if (totalDistance >= desiredDistance) { + console.log("TOTAL DISTANCE"); + console.log(totalDistance); + return nextXIncrement; + } + + // Move to the next increment + currentX = nextXIncrement; + + // Stop if we overshoot the next point + if ((direction === 1 && currentX > nextX) || (direction === -1 && currentX < nextX)) { + currentX = nextX; + } + } + } + + // If the desired distance is beyond all xValues, return the last point + console.log("TOTAL DISTANCE"); + console.log(totalDistance); + return xValues[xValues.length - 1]; + } + + export function followLine(linePixels: number[][], delay1: number, previousSpeed1: number) { - let increasing = true; - // const filteredLinePixels = linePixels.filter((pixel, index, array) => { - // // Skip the first element, as there's no previous element to compare with - // if (index === 0) return true; - - // const prevY = array[index - 1][1]; - // const currentY = pixel[1]; - // if (currentY < prevY) { - // increasing = false; - // } - // // Keep the pixel if the y-value is increasing - // return increasing; - // }); - const xs = linePixels.map((point) => point[0]); - const ys = linePixels.map((point) => point[1]); - const spline = new Spline.default(ys, xs); // Opposite so we get the x values - const x1 = findPointAtDistanceWithIncrements(spline, 0.01, (previousSpeed * delay * 2)*0.75); + let increasing = true; + // const filteredLinePixels = linePixels.filter((pixel, index, array) => { + // // Skip the first element, as there's no previous element to compare with + // if (index === 0) return true; + + // const prevY = array[index - 1][1]; + // const currentY = pixel[1]; + // if (currentY < prevY) { + // increasing = false; + // } + // // Keep the pixel if the y-value is increasing + // return increasing; + // }); + const xs = linePixels.map((point) => point[0]); + const ys = linePixels.map((point) => point[1]); + const spline = new Spline.default(ys, xs); // Opposite so we get the x values + const x1 = findPointAtDistanceWithIncrements(spline, 0.01, (previousSpeed * delay * 2)*0.75); const x2 = findPointAtDistanceWithIncrements(spline, 0.01, (previousSpeed * delay * 2)); const x3 = findPointAtDistanceWithIncrements(spline, 0.01, (previousSpeed * delay)); - + const reference1 = pixelToGroundCoordinates([spline.at(spline.xs[0]), spline.xs[0]]) + const reference2 = pixelToGroundCoordinates([imageDimensions[0]/2, spline.xs[0]]) + const xOffset = reference1.x - reference2.x; console.log("X"); console.log(x1); console.log(x2); console.log((previousSpeed * delay)*0.5); - // console.log(spline); - // const groundCoordinate1 = pixelToGroundCoordinates([spline.at(x1), x1]); - // const groundCoordinate2 = pixelToGroundCoordinates([spline.at(x2), x2]); - // const baseCoordinate = pixelToGroundCoordinates([spline.at(x3), x3]) - const groundCoordinate1 = pixelToGroundCoordinates([spline.at(spline.xs[2]*0.75), spline.xs[2]*0.75]); - const groundCoordinate2 = pixelToGroundCoordinates([spline.at(spline.xs[2]), spline.xs[2]]); + console.log("xs"); + console.log(spline.xs); + const groundCoordinate1 = pixelToGroundCoordinates([spline.at(spline.xs[8]*0.75), spline.xs[8]*0.75]); + const groundCoordinate2 = pixelToGroundCoordinates([spline.at(spline.xs[8]), spline.xs[8]]); const baseCoordinate = pixelToGroundCoordinates([spline.at(spline.xs[0]), spline.xs[0]]) - console.log("ground coordinates"); - console.log(groundCoordinate1.x*100, groundCoordinate1.y*100); - console.log(groundCoordinate2.x*100, groundCoordinate2.y*100); - console.log(baseCoordinate); - - const bezier = new Bezier.Bezier( - { x: baseCoordinate.x, y: baseCoordinate.y }, - { x: baseCoordinate.x, y: baseCoordinate.y + bezierIncrement }, - groundCoordinate1, - groundCoordinate2 - ); - console.log("bezier") - console.log(bezier); - - const motorCommands = []; - // TODO: Improve this function - const bezierPoints = bezierCurvePoints(bezier, bezierSamples); - //console.log(bezierPoints); - for (let i = 0; i < bezierPoints.length - 1; i++) { - //console.log(bezierPoints[i], bezierPoints[i+1]); - const command = purePursuit(bezierPoints[i], bezierPoints[i+1]); - motorCommands.push(command); - } - return motorCommands; - -} - - - -// Function to calculate the Euclidean distance between two points -function distanceBetweenPoints(x1, y1, x2, y2) { - //console.log(x1, x2, y1, y2); - const ground1 = pixelToGroundCoordinates([x1, y1]); - const ground2 = pixelToGroundCoordinates([x2, y2]); - // console.log("calulcating"); - // console.log(ground1); - // console.log(ground2); - const dx = ground2.x - ground1.x; - const dy = ground2.y - ground2.y; - if (isNaN(ground1.x) || isNaN(ground2.x) || isNaN(ground1.y) || isNaN(ground2.y)) { - console.log(ground1); - console.log(ground2); - console.log(x2, y2); - console.log("hmmm"); - - } - return Math.sqrt(dx * dx + dy * dy); -} - -function findPointAtDistanceWithIncrements(spline: Spline, increment: number, desiredDistance: number): number { - let totalDistance = 0; - const xValues = spline.xs; - console.log(xValues); - - // Iterate through each pair of xValues in the array - for (let i = 0; i < xValues.length - 1; i++) { - let currentX = xValues[i]; - const nextX = xValues[i + 1]; - - // Check the direction between currentX and nextX (allow for backtracking) - let direction = nextX > currentX ? 1 : -1; - - console.log(direction); - // Step through each segment in increments, adjusting for direction - while ((direction === 1 && currentX < nextX) || (direction === -1 && currentX > nextX)) { - - const nextXIncrement = currentX + direction * increment; // Increment or decrement by step size - - const currentY = spline.at(currentX); - const nextY = spline.at(nextXIncrement); - // console.log(spline); - // console.log(currentX, currentY); - - // Calculate distance between current and next increment - if (isNaN(nextY)) { - console.log( - "MMM" - ); - console.log(totalDistance); - console.log(nextXIncrement); - } - const distance = distanceBetweenPoints(currentX, currentY, nextXIncrement, nextY); - - totalDistance += distance; - - // Check if the accumulated distance is equal to or exceeds the desired distance - - if (totalDistance >= desiredDistance) { - console.log("TOTAL DISTANCE"); - console.log(totalDistance); - return nextXIncrement; - } - - // Move to the next increment - currentX = nextXIncrement; - - // Stop if we overshoot the next point - if ((direction === 1 && currentX > nextX) || (direction === -1 && currentX < nextX)) { - currentX = nextX; - } + console.log("ground coordinates"); + console.log(groundCoordinate1.x*100, groundCoordinate1.y*100); + console.log(groundCoordinate2.x*100, groundCoordinate2.y*100); + console.log(baseCoordinate); + + const bezier = new Bezier.Bezier( + { x: baseCoordinate.x + xOffset, y: baseCoordinate.y }, + { x: baseCoordinate.x + xOffset, y: baseCoordinate.y + bezierIncrement }, + groundCoordinate1, + groundCoordinate2 + ); + console.log("bezier") + console.log(bezier); + + const motorCommands = []; + // TODO: Improve this function + const bezierPoints = bezierCurvePoints(bezier, bezierSamples); + //console.log(bezierPoints); + for (let i = 0; i < bezierPoints.length - 1; i++) { + //console.log(bezierPoints[i], bezierPoints[i+1]); + const command = purePursuit(bezierPoints[i], bezierPoints[i+1]); + motorCommands.push(command); } - } + return motorCommands; - // If the desired distance is beyond all xValues, return the last point - console.log("TOTAL DISTANCE"); - console.log(totalDistance); - return xValues[xValues.length - 1]; } - - const imageDimensions = [640,480]; const horizontalFOV = 53.5; const verticalFOV = 41.41; @@ -204,78 +203,81 @@ export function pixelToGroundCoordinates( } function bezierCurvePoints(bezier: Bezier, n: number) { - const points = []; - for (let i = 0; i <= n; i++) { - const t = i / n; - const position = bezier.get(t); - const derivative = bezier.derivative(t); - const orientation = Math.atan2(derivative.y, derivative.x); - points.push({ x: position.x, y: position.y, theta: orientation }); - } - - return points; + const points = []; + for (let i = 0; i <= n; i++) { + const t = i / n; + const position = bezier.get(t); + const derivative = bezier.derivative(t); + const orientation = Math.atan2(derivative.y, derivative.x); + points.push({ x: position.x, y: position.y, theta: orientation }); + } + + return points; } function solveForSide(angleA: number, angleB: number, sideB: number) { - // Convert angles from degrees to radians - const angleARad = angleA * (Math.PI / 180); - const angleBRad = angleB * (Math.PI / 180); - - // Calculate the side opposite angleA using the Law of Sines - const sideA = (sideB * Math.sin(angleARad)) / Math.sin(angleBRad); + // Convert angles from degrees to radians + const angleARad = angleA * (Math.PI / 180); + const angleBRad = angleB * (Math.PI / 180); - return sideA; -} - - - -function metersToSteps(distanceMeters: number) { - const diameterInches = 3.25; - const stepsPerRevolution = 3200; - // Convert diameter from inches to meters - const diameterMeters = diameterInches * 0.0254; - - // Calculate circumference in meters - const circumference = Math.PI * diameterMeters; - - // Calculate steps per meter - const stepsPerMeter = stepsPerRevolution / circumference; - - // Convert distance in meters to steps - const steps = distanceMeters * stepsPerMeter; - - return steps; -} - -function purePursuit(currentPoint: {x: number, y: number, theta: number}, lookaheadPoint: {x: number, y: number, theta: number}) { - - if (!lookaheadPoint) { - return { leftWheelSpeed: 0, rightWheelSpeed: 0 }; // No valid lookahead point found + // Calculate the side opposite angleA using the Law of Sines + const sideA = (sideB * Math.sin(angleARad)) / Math.sin(angleBRad); + + return sideA; } + + - const dx = lookaheadPoint.x - currentPoint.x; - const dy = lookaheadPoint.y - currentPoint.y; + function metersToSteps(distanceMeters: number) { + const diameterInches = 3.25; + const stepsPerRevolution = 3200; + // Convert diameter from inches to meters + const diameterMeters = diameterInches * 0.0254; - const lookaheadAngle = lookaheadPoint.theta; - const robotHeading = currentPoint.theta; // Robot's orientation (angle) + // Calculate circumference in meters + const circumference = Math.PI * diameterMeters; - const angleToLookahead = lookaheadAngle - robotHeading; - const lookaheadDistanceToPoint = Math.sqrt(dx * dx + dy * dy); + // Calculate steps per meter + const stepsPerMeter = stepsPerRevolution / circumference; - const curvature = (2 * Math.sin(angleToLookahead)) / lookaheadDistanceToPoint; - const angularVelocity = linearSpeed * curvature; - - const leftWheelDistance = metersToSteps(lookaheadDistanceToPoint * (1 - (wheelBase * curvature) / 2)); - const rightWheelDistance = metersToSteps(lookaheadDistanceToPoint * (1 + (wheelBase * curvature) / 2)); - - // Compute wheel speeds - const leftWheelSpeed = Math.abs(metersToSteps(linearSpeed - (wheelBase / 2) * angularVelocity)); - const rightWheelSpeed = Math.abs(metersToSteps(linearSpeed + (wheelBase / 2) * angularVelocity)); + // Convert distance in meters to steps + const steps = distanceMeters * stepsPerMeter; - return { - leftWheelSpeed, - rightWheelSpeed, - leftWheelDistance, - rightWheelDistance - }; + return steps; + } + +function purePursuit(currentPoint: {x: number, y: number, theta: number}, lookaheadPoint: {x: number, y: number, theta: number}) { + + if (!lookaheadPoint) { + return { leftWheelSpeed: 0, rightWheelSpeed: 0 }; // No valid lookahead point found + } + + const dx = lookaheadPoint.x - currentPoint.x; + const dy = lookaheadPoint.y - currentPoint.y; + + const lookaheadAngle = lookaheadPoint.theta; + const robotHeading = currentPoint.theta; // Robot's orientation (angle) + + + const angleToLookahead = lookaheadAngle - robotHeading; + console.log("ANGLE"); + console.log(angleToLookahead* (180/Math.PI)); + const lookaheadDistanceToPoint = Math.sqrt(dx * dx + dy * dy); + + const curvature = (2 * Math.sin(angleToLookahead)) / lookaheadDistanceToPoint; + const angularVelocity = linearSpeed * curvature; + + const leftWheelDistance = metersToSteps(lookaheadDistanceToPoint * (1 - (wheelBase * curvature) / 2)); + const rightWheelDistance = metersToSteps(lookaheadDistanceToPoint * (1 + (wheelBase * curvature) / 2)); + + // Compute wheel speeds + const leftWheelSpeed = metersToSteps(linearSpeed - (wheelBase / 2) * angularVelocity); + const rightWheelSpeed = metersToSteps(linearSpeed + (wheelBase / 2) * angularVelocity); + + return { + leftWheelSpeed, + rightWheelSpeed, + leftWheelDistance, + rightWheelDistance + }; } diff --git a/extensions/src/doodlebot/index.ts b/extensions/src/doodlebot/index.ts index 122152680..bb7ff54c7 100644 --- a/extensions/src/doodlebot/index.ts +++ b/extensions/src/doodlebot/index.ts @@ -4,7 +4,7 @@ import Doodlebot from "./Doodlebot"; import { splitArgsString } from "./utils"; import EventEmitter from "events"; import { categoryByGesture, classes, emojiByGesture, gestureDetection, gestureMenuItems, gestures, objectDetection } from "./detection"; -import { createLineDetector } from "./LineDetection"; +//import { createLineDetector } from "./LineDetection"; const details: ExtensionMenuDisplayDetails = { name: "Doodlebot", description: "Program a doodlebot robot", @@ -100,47 +100,47 @@ export default class DoodlebotBlocks extends extension(details, "ui", "indicator text: "displayLine" }) async displayLine() { - console.log("displayLine"); - if (!this.lineDetector) { - const ipAddress = await this.doodlebot?.getIPAddress(); - if (!ipAddress) { - console.error("Unable to get IP address for line detection"); - return; - } - this.lineDetector = createLineDetector(ipAddress); - } + // console.log("displayLine"); + // if (!this.lineDetector) { + // const ipAddress = await this.doodlebot?.getIPAddress(); + // if (!ipAddress) { + // console.error("Unable to get IP address for line detection"); + // return; + // } + // this.lineDetector = createLineDetector(ipAddress); + // } - const lineCoordinates = await this.lineDetector(); - if (lineCoordinates.length === 0) { - console.log("No line detected"); - return; - } + // const lineCoordinates = await this.lineDetector(); + // if (lineCoordinates.length === 0) { + // console.log("No line detected"); + // return; + // } - console.log("Line coordinates:", JSON.stringify(lineCoordinates)); + // console.log("Line coordinates:", JSON.stringify(lineCoordinates)); - if (!this.videoDrawable) { - this.videoDrawable = await this.createVideoStreamDrawable(); - } + // if (!this.videoDrawable) { + // this.videoDrawable = await this.createVideoStreamDrawable(); + // } - const canvas = document.createElement('canvas'); - canvas.width = this.imageStream.width; // Assume these properties exist - canvas.height = this.imageStream.height; - const ctx = canvas.getContext('2d'); + // const canvas = document.createElement('canvas'); + // canvas.width = this.imageStream.width; // Assume these properties exist + // canvas.height = this.imageStream.height; + // const ctx = canvas.getContext('2d'); - if (ctx) { - ctx.drawImage(this.imageStream, 0, 0, canvas.width, canvas.height); + // if (ctx) { + // ctx.drawImage(this.imageStream, 0, 0, canvas.width, canvas.height); - ctx.beginPath(); - ctx.moveTo(lineCoordinates[0][0], lineCoordinates[0][1]); - for (let i = 1; i < lineCoordinates.length; i++) { - ctx.lineTo(lineCoordinates[i][0], lineCoordinates[i][1]); - } - ctx.strokeStyle = 'red'; - ctx.lineWidth = 2; - ctx.stroke(); + // ctx.beginPath(); + // ctx.moveTo(lineCoordinates[0][0], lineCoordinates[0][1]); + // for (let i = 1; i < lineCoordinates.length; i++) { + // ctx.lineTo(lineCoordinates[i][0], lineCoordinates[i][1]); + // } + // ctx.strokeStyle = 'red'; + // ctx.lineWidth = 2; + // ctx.stroke(); - this.videoDrawable.update(canvas); - } + // this.videoDrawable.update(canvas); + // } } @buttonBlock("Connect Robot") diff --git a/extensions/src/doodlebot/package.json b/extensions/src/doodlebot/package.json index 091be49c8..55a4eba57 100644 --- a/extensions/src/doodlebot/package.json +++ b/extensions/src/doodlebot/package.json @@ -16,6 +16,7 @@ "@mediapipe/tasks-vision": "^0.10.12", "@types/web-bluetooth": "^0.0.20", "bezier-js": "^6.1.4", + "canvas": "^2.11.2", "cubic-spline": "^3.0.3", "events": "^3.3.0" } diff --git a/extensions/src/doodlebot/pnpm-lock.yaml b/extensions/src/doodlebot/pnpm-lock.yaml index bdae58209..7cd28e3c6 100644 --- a/extensions/src/doodlebot/pnpm-lock.yaml +++ b/extensions/src/doodlebot/pnpm-lock.yaml @@ -14,26 +14,516 @@ importers: '@types/web-bluetooth': specifier: ^0.0.20 version: 0.0.20 + bezier-js: + specifier: ^6.1.4 + version: 6.1.4 + canvas: + specifier: ^2.11.2 + version: 2.11.2 + cubic-spline: + specifier: ^3.0.3 + version: 3.0.3 events: specifier: ^3.3.0 version: 3.3.0 packages: + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + '@mediapipe/tasks-vision@0.10.14': resolution: {integrity: sha512-vOifgZhkndgybdvoRITzRkIueWWSiCKuEUXXK6Q4FaJsFvRJuwgg++vqFUMlL0Uox62U5aEXFhHxlhV7Ja5e3Q==} '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bezier-js@6.1.4: + resolution: {integrity: sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + canvas@2.11.2: + resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} + engines: {node: '>=6'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + cubic-spline@3.0.3: + resolution: {integrity: sha512-yAvcHgrpf/k83pZiO4+R2reWOJlufgjpQhmDD3OXa8YMbjmRgjtUK8pcFOCZvJwqXaMD1isZdL7Z4ghqDPN/yw==} + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decompress-response@4.2.1: + resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} + engines: {node: '>=8'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + mimic-response@2.1.0: + resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} + engines: {node: '>=8'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nan@2.20.0: + resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@3.1.1: + resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + snapshots: + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.6.3 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + '@mediapipe/tasks-vision@0.10.14': {} '@types/web-bluetooth@0.0.20': {} + abbrev@1.1.1: {} + + agent-base@6.0.2: + dependencies: + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + ansi-regex@5.0.1: {} + + aproba@2.0.0: {} + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + + balanced-match@1.0.2: {} + + bezier-js@6.1.4: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + canvas@2.11.2: + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + nan: 2.20.0 + simple-get: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + + chownr@2.0.0: {} + + color-support@1.1.3: {} + + concat-map@0.0.1: {} + + console-control-strings@1.1.0: {} + + cubic-spline@3.0.3: {} + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + decompress-response@4.2.1: + dependencies: + mimic-response: 2.1.0 + + delegates@1.0.0: {} + + detect-libc@2.0.3: {} + + emoji-regex@8.0.0: {} + events@3.3.0: {} + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs.realpath@1.0.0: {} + + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + has-unicode@2.0.1: {} + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + is-fullwidth-code-point@3.0.0: {} + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + mimic-response@2.1.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mkdirp@1.0.4: {} + + ms@2.1.3: {} + + nan@2.20.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + + object-assign@4.1.1: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + path-is-absolute@1.0.1: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + safe-buffer@5.2.1: {} + + semver@6.3.1: {} + + semver@7.6.3: {} + + set-blocking@2.0.0: {} + + signal-exit@3.0.7: {} + + simple-concat@1.0.1: {} + + simple-get@3.1.1: + dependencies: + decompress-response: 4.2.1 + once: 1.4.0 + simple-concat: 1.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + tr46@0.0.3: {} + + util-deprecate@1.0.2: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + + wrappy@1.0.2: {} + + yallist@4.0.0: {}