[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 lorenSchmidt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# octavia noise\na fractal noise algorithm named after a very nice cat\n\n![Screenshot](twelve_octaves_thumb.png)\n\nthis is functionally similar to well known fractal noise algorithms such as value noise, perlin noise, or simplex noise, but does not exhibit the same grid artifacts. it's useful for situations which benefit from organic feature placement or very natural slope distribution (when using perlin or value noise in slope-intensive applications, grid artifacts can be a problem). \n\nthis is most closely related to cellular noise algorithms such as worley noise.\n\n## parameters\nthis has the parameters you'd expect from perlin or value noise: octaves and amplitude ratio (aka roughness or persistence). because the structure of it is so different, it also has several other tuning parameters which can yield interesting textural effects.\n\n### octaves\n![Screenshot](octaves.png)\nlike perlin or value noise, this adds successively finer features via additional layers of noise. this affects speed- it's linearly slower the more octaves are combined. right to left: one octave, six, twelve.\n\n### amplitude ratio\n![Screenshot](amplitude_ratio.png)\nthis is also known as roughness or persistence. this governs how the octaves are combined. finer octaves have this height relative to the next size up. left to right: 3/8, 1/2, 5/8. lower values will allow the lower frequencies to define the texture, while higher favor the finer details. 1/2 is default, which will yield an amplitude series of 1, 1/2, 1/4, 1/8, 1/16...\n\n### softness\n![Screenshot](softness.png)\n0-1. this governs the falloff used for each sample point. the default is 1, which is as continuous as possible. lower softness values can result in steeper, more defined grains, pits, or spires. \n\n### samples\nspace is partitioned into squares. this controls how many samples are placed within each. the radius of each sample is 1, relative to square edge length, so coverage is high, and they overlap significantly with neighboring squares. 2-4 samples will give you decent coverage unless the softness is very low. the algorithm is linearly slower the more samples are placed.\n\n### bias, range\n![Screenshot](bias_range.png)\nthese parameters set the height of each sample. bias is the base value, and range is a random spread above and below it. 0 range will make every sample be exactly the same height. left to right: -1 bias, range 0, 1 bias, range 0, 0 bias, range 1\n"
  },
  {
    "path": "fractal_cell_noise.js",
    "content": "/* \r\n--------------------------------------------------------------------------------\r\n\r\noctavia noise\r\n\r\n--------------------------------------------------------------------------------\r\n\r\nthis is the heart of the cellular noise algorithm. it's a single x, y lookup. the noise loops on itself, and is a rectangle xsize by ysize. x and y are the current point in space we are calculating for (floating point is fine). the space is divided into squares. n points are deterministically placed             \r\n+---------+ in the square (\"samples\" controls this). each is given a random \r\n|   x     | height, by default -1 to 1. a soft 0-1 kernel is centered on each. \r\n|         | it has a diameter equal to the square size, outside of which it \r\n|x     x  | falls off cleanly to 0. \r\n|  x      | \r\n+---------+ d is density- at density 1 you have 1 square for the entire texture space. at density 2 it's 2 squares by 2, etc.. seed is the seed for this octave. softness alters the shape of the falloff (but it always has the same diameter). the heights can be customized customized by setting the bias value (center) and range (it goes from bias - range to bias + range)   */\r\n\r\n// this variant uses a trick to reduce samples\r\n// sample radius is 1/2 square edge instead of 1, which makes overlap from neighboring cells never more than 1/2 square length. this means we can check which quadrant we're in and only check the three nearest neighbors, instead of all 8 neighbors.\r\nfunction curve_stack_2x2_xy(x, y, xsize = 256, ysize = 256, d = 1, seed = 0, softness = 1, samples = 4, bias = 0, range = 1 ) {\r\n\r\n    x /= xsize; y /= xsize \r\n    let ix = Math.floor(x * d); let iy = Math.floor(y * d)\r\n    let ti = 0 // random number table index\r\n    let dm1 = d - 1 // for the bitwise & instead of % range trick\r\n\r\n    c_height = 0\r\n\r\n    // this variant uses a trick to reduce samples\r\n    // sample radius is 1/2 square edge instead of 1, which makes overlap from neighboring cells never more than 1/2 square length. this means we can check which quadrant we're in and only check the three nearest neighbors, instead of all 8 neighbors.\r\n    let left = ix - 1 + (Math.floor(x * 2 * d) & 1)\r\n    let top = iy - 1 + (Math.floor(y * 2 * d) & 1)\r\n    let right = left + 1; let bottom = top + 1\r\n\r\n    // this uses every point within the radius. when doing worley noise, we calculate distances for each point, and compare, getting various other parameters per point. instead, we can drop the distance comparisons, and instead get a height per point and run it through a lightweight kernel, and accumulate\r\n    let px, py, distance_squared, amp\r\n    let cx = left, cy = top\r\n    let sum = 0\r\n    while (cy <= bottom) {\r\n        cx = left\r\n        while (cx <= right) {\r\n            // this is a deterministic noise function with two integer inputs\r\n            ti = pos3int((cx + d) & dm1, (cy + d) & dm1, noise_seed)\r\n            // seed our rng with that value\r\n        \r\n            // this bounded curve runs from -1 to 1. i believe this means that we want to multiply the distance by d. however, this seems to leave seams? maybe i am wrong about the numbers.\r\n            for (let a = 0; a < samples; a ++) {\r\n                px = cx / d + noise_table[(ti ++) & nt_sizem1] / nt_size / d\r\n                py = cy / d + noise_table[(ti ++) & nt_sizem1] / nt_size / d\r\n                distance_squared = d * d * ((x - px) ** 2 + (y - py) ** 2) * 4\r\n                \r\n                let h = bias + -range + 2 * range * noise_table[(ti ++) % nt_size] / nt_size\r\n                // this is a bounded -1 to 1 variant of the witch of agnesi. this will prevent seams when points drop out of the set.\r\n                if (distance_squared < 1.0) {\r\n                    amp = (softness * (1 - distance_squared) / (softness + distance_squared))\r\n                    amp = amp * amp\r\n                    // note that this worked ^ 2, but the derivative was not 0 at -1 and 1\r\n                    sum += h * amp\r\n                }\r\n            }\r\n            cx ++\r\n        }\r\n        cy ++\r\n    }\r\n\r\n    return sum\r\n}\r\n\r\n\r\nfunction curve_stack_3x3_xy(x, y, xsize = 256, ysize = 256, d = 1, seed = 0, softness = 1, samples = 4, bias = 0, range = 1 ) {\r\n\r\n    x /= xsize; y /= xsize \r\n    let ix = Math.floor(x * d); let iy = Math.floor(y * d)\r\n    let ti = 0 // random number table index\r\n\r\n    c_height = 0\r\n\r\n    // this uses every point within the radius. when doing worley noise, we calculate distances for each point, and compare, getting various other parameters per point. instead, we can drop the distance comparisons, and instead get a height per point and run it through a lightweight kernel, and accumulate\r\n    for (let oy = -1; oy <= 1; oy ++) {\r\n        for (let ox = -1; ox <= 1; ox ++) {\r\n            let cx = ix + ox; let cy = iy + oy\r\n            // this is a deterministic noise function with two integer inputs\r\n            ti = pos3int((cx + d) % d, (cy + d) % d, noise_seed)\r\n            // seed our rng with that value\r\n        \r\n            // let count = 1 + prime_cycle() % (samples - 1)\r\n            let count = samples\r\n            // this bounded curve runs from -1 to 1. i believe this means that we want to multiply the distance by d. however, this seems to leave seams? maybe i am wrong about the numbers.\r\n            for (let a = 0; a < count; a ++) {\r\n                let px = cx / d + (noise_table[(ti ++) % nt_size] / nt_size) / d \r\n                let py = cy / d + (noise_table[(ti ++) % nt_size] / nt_size) / d\r\n                let distance = d * Math.sqrt((x - px) ** 2 + (y - py) ** 2) \r\n                let height = bias + -range + 2 * range * noise_table[(ti ++) % nt_size] / nt_size\r\n                // this is a bounded -1 to 1 variant of the witch of agnesi. this will prevent seams when points drop out of the set.\r\n                if (distance < 1.0) {\r\n                    let a = (softness * (1 - distance * distance) \r\n                            / (softness + distance * distance))\r\n                    a = a * a\r\n                    // note that this worked ^ 2, but the derivative was not 0 at -1 and 1\r\n                    c_height += height * a\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    return c_height\r\n}\r\n\r\n\r\nvar stack_octaves = 1\r\nfunction cell_noise_xy(x, y, xsize = 256, ysize = 256, density = 4, seed = 0,octaves = 2, amplitude_ratio = 1/2, softness = 1, samples = 4, bias = 0, range = 1 ) {\r\n    let surface = 0\r\n    for (let a = 0; a < octaves; a ++) {\r\n        let octave_seed = noise_table[seed % nt_size] // inline prime cycle\r\n        seed += pc_increment\r\n        let layer = curve_stack_2x2_xy(x, y, xsize, ysize, density * 2 ** a, octave_seed, softness, samples, bias, range)\r\n\r\n        surface += (amplitude_ratio ** a) * layer\r\n    }\r\n\r\n    c_height = 0.5 * surface\r\n    return c_height\r\n}\r\n/* this evaluates as -1 to 1, very center-weighted\r\n(seed 29477)\r\n-1.0 - -0.9  ▓▓\r\n-0.9 - -0.8  ▓▓\r\n-0.8 - -0.7  ▓▓▓\r\n-0.7 - -0.6  ▓▓▓▓▓\r\n-0.6 - -0.5  ▓▓▓▓▓▓▓\r\n-0.5 - -0.4  ▓▓▓▓▓▓▓▓\r\n-0.4 - -0.3  ▓▓▓▓▓▓▓▓▓▓\r\n-0.3 - -0.2  ▓▓▓▓▓▓▓▓▓▓▓▓▓\r\n-0.2 - -0.1  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\r\n-0.1 -  0.0  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\r\n 0.0 -  0.1  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\r\n 0.1 -  0.2  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓\r\n 0.2 -  0.3  ▓▓▓▓▓▓▓▓▓▓▓▓\r\n 0.3 -  0.4  ▓▓▓▓▓▓▓▓▓▓\r\n 0.4 -  0.5  ▓▓▓▓▓▓▓▓▓\r\n 0.5 -  0.6  ▓▓▓▓▓▓▓\r\n 0.6 -  0.7  ▓▓▓▓▓\r\n 0.7 -  0.8  ▓▓▓▓\r\n 0.8 -  0.9  ▓▓▓\r\n 0.9 -  1.0  ▓▓▓\r\n*/\r\n\r\n\r\n/* \r\n--------------------------------------------------------------------------------\r\n\r\npositional random number generation\r\n\r\n--------------------------------------------------------------------------------\r\nfor 2d value noise, we need to be able to input two coordinates and a seed, and\r\nget a deterministic value for that point in space. you can substitute other\r\napproaches for this one- this is a relatively simple, readable approach i came\r\nup with but there are more cryptographically sound 3 input hashes out there.\r\n\r\nnote that if generalizing this for n dimensions, you'd want your number of dimensions plus one for the seed\r\n*/\r\n\r\n// three input positional rng. the output is an integer in the range 0-nt_size\r\nfunction pos3int(x, y, seed) {\r\n    let linear = (x % ns) + (y % ns) * ns + seed\r\n    linear %= noise_table.length\r\n    return noise_table[linear]\r\n}\r\n\r\n\r\n// used for table setup only\r\nvar seed = 88883\r\nvar noise_table = []\r\nvar ns = 256\r\nvar nt_size = ns * ns\r\nvar nt_sizem1 = nt_size - 1\r\nfunction init_random_table() {\r\n    let list = []\r\n    for (let a = 0; a < nt_size; a ++) {\r\n        list.push(a)\r\n    }\r\n    for (let a = 0; a < nt_size; a ++) {\r\n        noise_table[a] = draw_card(list)\r\n    }\r\n}\r\n\r\n\r\n// if you walk through a table, offsetting your index by a prime number which doesn't divide evenly into your table size, you will cycle through all the entries in the array exactly once, in a nonrepeating order. \r\n// there are three instances of this in the algorithm, all inline, but this self-contained function is here for clarity\r\nvar pc_increment = 101159\r\nvar pc_seed = 0\r\nfunction prime_cycle() {\r\n    let result = noise_table[pc_seed % nt_size]\r\n    pc_seed += pc_increment\r\n    return result\r\n}\r\n\r\n\r\n// used for table setup only\r\n// picks a random element and returns it, removing it from the array\r\nfunction draw_card(array) {\r\n    var index = Math.floor(Math.random() * array.length);\r\n    //console.log(\"index = \" + index);\r\n    var result = array[index];\r\n    if (array.length > 0) {\r\n        return (array.splice(index, 1))[0];\r\n    }\r\n    else\r\n        return \"ERROR: pick running on array of size 0\";\r\n}"
  }
]