From 8db4f2f84e288059aca9271a455a79c319a28c4d Mon Sep 17 00:00:00 2001 From: gnat Date: Wed, 19 Feb 2025 13:10:05 -0800 Subject: [PATCH] initial commit --- error-page.sh | 22 +++ run.sh | 1 + serve.sh | 56 ++++++ status_codes.sh | 15 ++ www/DepartureMono-Regular.woff2 | Bin 0 -> 17428 bytes www/index.sh | 131 ++++++++++++++ www/perlin.js | 312 ++++++++++++++++++++++++++++++++ www/pfp.png | Bin 0 -> 36821 bytes www/style.css | 120 ++++++++++++ 9 files changed, 657 insertions(+) create mode 100755 error-page.sh create mode 100755 run.sh create mode 100755 serve.sh create mode 100644 status_codes.sh create mode 100644 www/DepartureMono-Regular.woff2 create mode 100755 www/index.sh create mode 100644 www/perlin.js create mode 100644 www/pfp.png create mode 100644 www/style.css diff --git a/error-page.sh b/error-page.sh new file mode 100755 index 0000000..c93a453 --- /dev/null +++ b/error-page.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +source status_codes.sh + +echo ${1} >/dev/stderr +echo ${status_code[404]} >/dev/stderr + +cat << EOF + + + + NRC website + + + +
+

${1}

+
${status_code[${1}]}
+
+ + +EOF diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..862071a --- /dev/null +++ b/run.sh @@ -0,0 +1 @@ +socat tcp-listen:8000,bind=0.0.0.0,fork,reuseaddr 'exec:bash -c ./serve.sh' diff --git a/serve.sh b/serve.sh new file mode 100755 index 0000000..261c28d --- /dev/null +++ b/serve.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +source status_codes.sh + +declare -A request_headers +declare -A response_headers + +write_response_headers() { + local status=$1 + echo -ne "HTTP/1.1 ${status} ${status_code[${status}]}\r\n" + for header in "${!response_headers[@]}"; do + echo -ne "${header}: ${response_headers["${header}"]}\r\n" + done + echo -ne "\r\n" +} + +IFS=' ' read -r method route version; + +echo $method $route >/dev/stderr +if [ "${method}" != 'GET' ]; then + write_response_headers 405 + ./error-page.sh 405 +fi + +IFS=$'\n' +while read -r line; do + [[ "$line" == $'\r' ]] && break + header=${line%%:*} + value=${line#:*} + request_headers["${header}"]="${value}" +done + +for header in "${!request_headers[@]}"; do + echo "${header} -> ${request_headers["${header}"]}" >/dev/stderr +done + +if [[ "${route}" == "/" ]]; then + response_headers["Content-Type"]="text/html" + write_response_headers 200 + echo "$(www/index.sh)"; +fi + +if [[ -f "www${route}" ]]; then + if [[ "www${route}" == *.sh ]]; then + write_response_headers 200 + echo "$(www"${route}")" + else + write_response_headers 200 + cat "www${route}" + fi +else + if [[ "${route}" != "/" ]]; then + write_response_headers 404 + ./error-page.sh 404 + fi +fi diff --git a/status_codes.sh b/status_codes.sh new file mode 100644 index 0000000..3b5cb67 --- /dev/null +++ b/status_codes.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +declare -A status_code + +status_code[200]="OK" +status_code[204]="No Content" +status_code[206]="Partial Content" +status_code[301]="Moved Permanently" +status_code[400]="Bad Request" +status_code[401]="Unauthorized" +status_code[403]="Forbidden" +status_code[404]="Not Found" +status_code[405]="Method Not Allowed" +status_code[418]="I'm a teapot" +status_code[429]="Too Many Requests" diff --git a/www/DepartureMono-Regular.woff2 b/www/DepartureMono-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..7d8b33b0a53f0c9a2c496e83aaa1ffc3b8bb053f GIT binary patch literal 17428 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

^x;yM%PZJM;QsQ37xS!^ zKnTPE=mQu0ObBRi9DHIDLM%)oTmm#!S01Vp5rxIXW#!~SDM%oV$oyUU?Bgx1zvj+; z(ib|_=`OZ4P_d2{CMG3kWai}N<%cR&u3DwGw+;VEo#=RHyVPYe*p5u0QRxg0n0lKUj3 z&r-$pMOt62`exmCn|=^#A@-BhFLJ*r{h{`kRx7=KjM|v}XVuQGW3lDFGO$Oi9AgzY zR*7d--51zP(a}WDMONNIFlj)JQo5zRwQjTuFcj&QJ{5CAH>bAM+eq0s%G zm}3qAcyVMOEsHuz%FN9Xf&B(n;imwPG`LxmKSpdXm4%z9JSBkCXTqU4K!k0EHiGu) z-U6UKsqLE!&5?IknM=^OZS8e$pSt~>J+4=Um23JahQ5K;pjSjsL}+W@?SKETsb$f& zgRT9(Yrnhq&(;Cg((^NwROEWv#pXyb<`t?q!>v#OBL?8KZ%!wi6U||6 zI8?@bPdfvRfeaBh9Hv9y27`Tg2OO>QZ2%CXVZL?HvWpe2R9XQlQOa3SA)KB8=C6uq zycB>;GY-T{0C+>#C0Ov@Wza_YE(dQVyF46i*cHIn?Oh?dWwI-QNxr*MxR$!B38xx% zwaCUpyV~eI9(QH@W4eeoA;4xDfK6||H-0Kr8A7dCp(3^9loT3H+v&Hr>PjwHzB2hL z8%Qly5Q3nxlWT*!6dLa8@}u9AvX5jsY-sj1=i-<91} zWCO~#-ASHG<(%uwMYOe{E~vHRW$_ARAPKfDio>P_Y7LU34=+Y#z09*wP1~v>H$a7) zNIlD17DQe%L^!!9RdqSi&TzEtSfnsr%5qV8&Opel90Q#!R?p-r%bG!oC13$LjgOe% z?QOTss$y#7^=nimriPP%sLZXXq*i$Md_)Npj*xzmWDT9ngJT + + + NRC website + + + + + + +

+
+
+ the profile photograph natalie uses, a wireframe drawing of a siemens s700 +
+

natalie roentgen connolly

+
.website
+
+
@0x6e6174
|
+
it/she
|
+
Portland, OR
|
+
$(($(($(date +%s) - $(date -d '2007-04-01' +%s))) / $((365*24*3600))))
+
+
+
+ A weird computer witch interested in system administration, computer engineering, open source, and software development. +
+
+
+
+
this website is vaguely intendended to function as a {portfolio,prettier presentation of the information on my resume}.
+
+

natalie elsewhere

+
+
discord: 0x6e6174
+
E-mail: natalie-roentgen-connolly at mail.natalie-roentgen-connolly.website
+
git(hub): 0x6e6174
+
git(.natalieee.net): nat
+
less {serious,"professional"} website: natalieee.net
+
+
+
+

skills

+
    +
  • several years of python experience
  • +
  • vanilla javascript (& gjs)
  • +
  • linux system administration
  • +
  • git
  • +
  • html/(s)css
  • +
  • horrible bash scripts (like this website)
  • +
  • nginx
  • +
+
+
+

experience

+
    +
  • lead programmer of FTC 267 (2021-2023)
  • +
      +
    • responsible for programming the robot and coordinating with the build team
    • +
    +
  • team captain of FTC 187 (2023-2024)
  • +
      +
    • responsible for coordinating team members, delegating tasks, and other administrative duties
    • +
    +
  • wrote python bindings for libastal
  • +
  • created this website and natalieee.net
  • +
+
+
+

education

+
    +
  • 3 years at benson polytechnic highschool (2021-2024)
  • +
  • earned GED through PCC Yes to College program
  • +
+
+
+ + + +EOF diff --git a/www/perlin.js b/www/perlin.js new file mode 100644 index 0000000..9fa8c36 --- /dev/null +++ b/www/perlin.js @@ -0,0 +1,312 @@ +/* + * A speed-improved perlin and simplex noise algorithms for 2D. + * + * Based on example code by Stefan Gustavson (stegu@itn.liu.se). + * Optimisations by Peter Eastman (peastman@drizzle.stanford.edu). + * Better rank ordering method by Stefan Gustavson in 2012. + * Converted to Javascript by Joseph Gentle. + * + * Version 2012-03-09 + * + * This code was placed in the public domain by its original author, + * Stefan Gustavson. You may use it as you see fit, but + * attribution is appreciated. + * + */ + +// https://github.com/josephg/noisejs/blob/master/perlin.js + +(function(global){ + var module = global.noise = {}; + + function Grad(x, y, z) { + this.x = x; this.y = y; this.z = z; + } + + Grad.prototype.dot2 = function(x, y) { + return this.x*x + this.y*y; + }; + + Grad.prototype.dot3 = function(x, y, z) { + return this.x*x + this.y*y + this.z*z; + }; + + var grad3 = [new Grad(1,1,0),new Grad(-1,1,0),new Grad(1,-1,0),new Grad(-1,-1,0), + new Grad(1,0,1),new Grad(-1,0,1),new Grad(1,0,-1),new Grad(-1,0,-1), + new Grad(0,1,1),new Grad(0,-1,1),new Grad(0,1,-1),new Grad(0,-1,-1)]; + + var p = [151,160,137,91,90,15, + 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, + 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, + 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, + 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, + 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, + 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, + 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, + 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, + 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, + 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, + 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, + 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]; + // To remove the need for index wrapping, double the permutation table length + var perm = new Array(512); + var gradP = new Array(512); + + // This isn't a very good seeding function, but it works ok. It supports 2^16 + // different seed values. Write something better if you need more seeds. + module.seed = function(seed) { + if(seed > 0 && seed < 1) { + // Scale the seed out + seed *= 65536; + } + + seed = Math.floor(seed); + if(seed < 256) { + seed |= seed << 8; + } + + for(var i = 0; i < 256; i++) { + var v; + if (i & 1) { + v = p[i] ^ (seed & 255); + } else { + v = p[i] ^ ((seed>>8) & 255); + } + + perm[i] = perm[i + 256] = v; + gradP[i] = gradP[i + 256] = grad3[v % 12]; + } + }; + + module.seed(0); + + /* + for(var i=0; i<256; i++) { + perm[i] = perm[i + 256] = p[i]; + gradP[i] = gradP[i + 256] = grad3[perm[i] % 12]; + }*/ + + // Skewing and unskewing factors for 2, 3, and 4 dimensions + var F2 = 0.5*(Math.sqrt(3)-1); + var G2 = (3-Math.sqrt(3))/6; + + var F3 = 1/3; + var G3 = 1/6; + + // 2D simplex noise + module.simplex2 = function(xin, yin) { + var n0, n1, n2; // Noise contributions from the three corners + // Skew the input space to determine which simplex cell we're in + var s = (xin+yin)*F2; // Hairy factor for 2D + var i = Math.floor(xin+s); + var j = Math.floor(yin+s); + var t = (i+j)*G2; + var x0 = xin-i+t; // The x,y distances from the cell origin, unskewed. + var y0 = yin-j+t; + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if(x0>y0) { // lower triangle, XY order: (0,0)->(1,0)->(1,1) + i1=1; j1=0; + } else { // upper triangle, YX order: (0,0)->(0,1)->(1,1) + i1=0; j1=1; + } + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + var y1 = y0 - j1 + G2; + var x2 = x0 - 1 + 2 * G2; // Offsets for last corner in (x,y) unskewed coords + var y2 = y0 - 1 + 2 * G2; + // Work out the hashed gradient indices of the three simplex corners + i &= 255; + j &= 255; + var gi0 = gradP[i+perm[j]]; + var gi1 = gradP[i+i1+perm[j+j1]]; + var gi2 = gradP[i+1+perm[j+1]]; + // Calculate the contribution from the three corners + var t0 = 0.5 - x0*x0-y0*y0; + if(t0<0) { + n0 = 0; + } else { + t0 *= t0; + n0 = t0 * t0 * gi0.dot2(x0, y0); // (x,y) of grad3 used for 2D gradient + } + var t1 = 0.5 - x1*x1-y1*y1; + if(t1<0) { + n1 = 0; + } else { + t1 *= t1; + n1 = t1 * t1 * gi1.dot2(x1, y1); + } + var t2 = 0.5 - x2*x2-y2*y2; + if(t2<0) { + n2 = 0; + } else { + t2 *= t2; + n2 = t2 * t2 * gi2.dot2(x2, y2); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70 * (n0 + n1 + n2); + }; + + // 3D simplex noise + module.simplex3 = function(xin, yin, zin) { + var n0, n1, n2, n3; // Noise contributions from the four corners + + // Skew the input space to determine which simplex cell we're in + var s = (xin+yin+zin)*F3; // Hairy factor for 2D + var i = Math.floor(xin+s); + var j = Math.floor(yin+s); + var k = Math.floor(zin+s); + + var t = (i+j+k)*G3; + var x0 = xin-i+t; // The x,y distances from the cell origin, unskewed. + var y0 = yin-j+t; + var z0 = zin-k+t; + + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if(x0 >= y0) { + if(y0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } + else if(x0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } + else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } + } else { + if(y0 < z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } + else if(x0 < z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } + else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } + } + // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z), + // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and + // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where + // c = 1/6. + var x1 = x0 - i1 + G3; // Offsets for second corner + var y1 = y0 - j1 + G3; + var z1 = z0 - k1 + G3; + + var x2 = x0 - i2 + 2 * G3; // Offsets for third corner + var y2 = y0 - j2 + 2 * G3; + var z2 = z0 - k2 + 2 * G3; + + var x3 = x0 - 1 + 3 * G3; // Offsets for fourth corner + var y3 = y0 - 1 + 3 * G3; + var z3 = z0 - 1 + 3 * G3; + + // Work out the hashed gradient indices of the four simplex corners + i &= 255; + j &= 255; + k &= 255; + var gi0 = gradP[i+ perm[j+ perm[k ]]]; + var gi1 = gradP[i+i1+perm[j+j1+perm[k+k1]]]; + var gi2 = gradP[i+i2+perm[j+j2+perm[k+k2]]]; + var gi3 = gradP[i+ 1+perm[j+ 1+perm[k+ 1]]]; + + // Calculate the contribution from the four corners + var t0 = 0.6 - x0*x0 - y0*y0 - z0*z0; + if(t0<0) { + n0 = 0; + } else { + t0 *= t0; + n0 = t0 * t0 * gi0.dot3(x0, y0, z0); // (x,y) of grad3 used for 2D gradient + } + var t1 = 0.6 - x1*x1 - y1*y1 - z1*z1; + if(t1<0) { + n1 = 0; + } else { + t1 *= t1; + n1 = t1 * t1 * gi1.dot3(x1, y1, z1); + } + var t2 = 0.6 - x2*x2 - y2*y2 - z2*z2; + if(t2<0) { + n2 = 0; + } else { + t2 *= t2; + n2 = t2 * t2 * gi2.dot3(x2, y2, z2); + } + var t3 = 0.6 - x3*x3 - y3*y3 - z3*z3; + if(t3<0) { + n3 = 0; + } else { + t3 *= t3; + n3 = t3 * t3 * gi3.dot3(x3, y3, z3); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 32 * (n0 + n1 + n2 + n3); + + }; + + // ##### Perlin noise stuff + + function fade(t) { + return t*t*t*(t*(t*6-15)+10); + } + + function lerp(a, b, t) { + return (1-t)*a + t*b; + } + + // 2D Perlin Noise + module.perlin2 = function(x, y) { + // Find unit grid cell containing point + var X = Math.floor(x), Y = Math.floor(y); + // Get relative xy coordinates of point within that cell + x = x - X; y = y - Y; + // Wrap the integer cells at 255 (smaller integer period can be introduced here) + X = X & 255; Y = Y & 255; + + // Calculate noise contributions from each of the four corners + var n00 = gradP[X+perm[Y]].dot2(x, y); + var n01 = gradP[X+perm[Y+1]].dot2(x, y-1); + var n10 = gradP[X+1+perm[Y]].dot2(x-1, y); + var n11 = gradP[X+1+perm[Y+1]].dot2(x-1, y-1); + + // Compute the fade curve value for x + var u = fade(x); + + // Interpolate the four results + return lerp( + lerp(n00, n10, u), + lerp(n01, n11, u), + fade(y)); + }; + + // 3D Perlin Noise + module.perlin3 = function(x, y, z) { + // Find unit grid cell containing point + var X = Math.floor(x), Y = Math.floor(y), Z = Math.floor(z); + // Get relative xyz coordinates of point within that cell + x = x - X; y = y - Y; z = z - Z; + // Wrap the integer cells at 255 (smaller integer period can be introduced here) + X = X & 255; Y = Y & 255; Z = Z & 255; + + // Calculate noise contributions from each of the eight corners + var n000 = gradP[X+ perm[Y+ perm[Z ]]].dot3(x, y, z); + var n001 = gradP[X+ perm[Y+ perm[Z+1]]].dot3(x, y, z-1); + var n010 = gradP[X+ perm[Y+1+perm[Z ]]].dot3(x, y-1, z); + var n011 = gradP[X+ perm[Y+1+perm[Z+1]]].dot3(x, y-1, z-1); + var n100 = gradP[X+1+perm[Y+ perm[Z ]]].dot3(x-1, y, z); + var n101 = gradP[X+1+perm[Y+ perm[Z+1]]].dot3(x-1, y, z-1); + var n110 = gradP[X+1+perm[Y+1+perm[Z ]]].dot3(x-1, y-1, z); + var n111 = gradP[X+1+perm[Y+1+perm[Z+1]]].dot3(x-1, y-1, z-1); + + // Compute the fade curve value for x, y, z + var u = fade(x); + var v = fade(y); + var w = fade(z); + + // Interpolate + return lerp( + lerp( + lerp(n000, n100, u), + lerp(n001, n101, u), w), + lerp( + lerp(n010, n110, u), + lerp(n011, n111, u), w), + v); + }; + +})(this); diff --git a/www/pfp.png b/www/pfp.png new file mode 100644 index 0000000000000000000000000000000000000000..12c9b152fcc3cdecc88bb5f2f7be3479699ad524 GIT binary patch literal 36821 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%0DN$R{MpU*77&zCxS)BrPxJ5`< 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 .header { + display: flex; + flex-direction: row; +} + +#contact { + display:flex; + flex-direction: column; + flex-wrap: nowrap; + + > div { + white-space: nowrap; + overflow-x: scroll; + display: list-item; + scrollbar-width: none; + -ms-overflow-style: none; + + &::-webkit-scrollbar { + display: none; + } + + &::before{ + content: '- ' + } + } +} + +.properties { + display: flex; + flex-direction: row; + flex-wrap: wrap; + word-break: break-word; + overflow-wrap: break-word; + + > div.spacer { + margin: 0 2ch; + } +} + +img { + margin: 0 1em 0 0; +} + +img[src="/pfp.png"] { + width: 200px; + height: 200px; +} + +canvas#bg { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: -1; +}