{"id":17437336,"url":"https://github.com/blixt/js-procedural","last_synced_at":"2025-03-01T11:33:34.947Z","repository":{"id":18997740,"uuid":"22219832","full_name":"blixt/js-procedural","owner":"blixt","description":"Library for defining procedural functions.","archived":false,"fork":false,"pushed_at":"2014-08-04T11:24:10.000Z","size":214,"stargazers_count":26,"open_issues_count":1,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-02-15T11:52:20.795Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/blixt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-07-24T16:06:24.000Z","updated_at":"2022-08-27T07:08:53.000Z","dependencies_parsed_at":"2022-07-26T23:46:12.841Z","dependency_job_id":null,"html_url":"https://github.com/blixt/js-procedural","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blixt%2Fjs-procedural","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blixt%2Fjs-procedural/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blixt%2Fjs-procedural/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blixt%2Fjs-procedural/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/blixt","download_url":"https://codeload.github.com/blixt/js-procedural/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240152377,"owners_count":19756073,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-17T11:42:59.576Z","updated_at":"2025-03-01T11:33:34.572Z","avatar_url":"https://github.com/blixt.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"procedural\n==========\n\nLibrary for defining procedural functions in a hierarchy to allow for complex\nprocedurally generated content.\n\n\nIntroduction\n------------\n\nThis library aims to make it easy to create procedurally generated content,\nwhich usually means semi-random content that follows a complex set of rules.\nOne great example of procedural generated content is the world in Minecraft.\nIt's random for everyone who starts the game, but follows certain rules that\ntogether make a recognizable environment (hills, trees, caves, villages, ...)\n\nYou use this library by defining procedural functions. A procedural function\nmay *take* any number of parameters, and can *provide* any number of values.\nAdditionally, it may *generate* other procedural functions that depend on it,\nas well as its values.\n\nEach instance of a procedural function (which you get by calling the function)\nhas a unique hash which is used when calling any of the built-in pseudo-random\nnumber generators. This means you will always get the same sequence of random\nnumbers for the same set of parameters, which is the most important aspect of\nprocedurally generated content.\n\nHere's how you define a procedural function that takes a single parameter:\n\n```javascript\nvar procedural = require('procedural');\nvar world = procedural('world').takes('name');\n```\n\nYou may want a world to define if it's habitable:\n\n```javascript\nworld.provides('habitable', function (world) {\n  // For now, all worlds will be habitable.\n  return true;\n});\n```\n\nYou can now create your world instance like this:\n\n```javascript\nvar earth = world('Earth');\nconsole.log('Does', earth.name, 'support life?');\nconsole.log(earth.habitable ? 'Yes' : 'No');\n```\n\nNow it makes sense for a region in a world to be accessible...\n\n```javascript\nvar region = world.generates('region')\n  .takes('x', 'y')\n  .provides('temperature', function (region) {\n    // Get a random number generator for this region.\n    var rnd = region.getRandGen();\n\n    // Make temperature depend on what kind of world we're on.\n    if (region.world.habitable) {\n      return rnd.nextInt(10, 35);\n    } else {\n      return rnd.nextInt(1000, 3500);\n    }\n  });\n```\n\nLet's find out what temperature we've got at (0, 0).\n\n```javascript\nconsole.log('Temperature is:', earth.region(0, 0).temperature);\n```\n\nNote that generated random numbers will be different for different parameters:\n\n```javascript\nvar kryptonTemp = world('Krypton').region(0, 0).temperature;\nconsole.log('But on Krypton it is:', kryptonTemp);\n```\n\nBut wait, Krypton isn't habitable! Let's fix that by revisiting the habitable\nvalue of worlds.\n\n```javascript\nworld.provides('habitable', function (world) {\n  // Only Earth is known to be habitable (for now...).\n  return world.name == 'Earth';\n});\n```\n\nIf you rerun all the code together now, you'll see that Krypton will be a bit\nhotter than before, while Earth is still comfortable.\n\nI hope this example shows how useful it is to have a set of procedurally\ngenerated values that depend on each other when you want to create random\ncontent that still follows a set of rules.\n\nFor another example, see the bottom of this README, or go check out one of\nthese demos:\n\n* TODO: The space demo.\n\n\nAPI\n---\n\nTODO: Define the API here.\n\n\nFull example\n------------\n\nHow to define a procedurally generated avatar.\n\n```javascript\nvar procedural = require('procedural');\n\nvar avatar = procedural('avatar')\n  // The block size is just visual, so it shouldn't affect randomization.\n  .doNotHash('blockSize')\n  // The username is needed to create a unique avatar for every user.\n  .takes('username')\n  // Size, in blocks. Different sizes will create different avatars.\n  .takes('size', function validate(avatar, blocks) {\n    // Ensure that size is a positive integer divisible by 2.\n    return typeof blocks == 'number' \u0026\u0026 blocks \u003e 0 \u0026\u0026 !(blocks % 2);\n  })\n  // The pixel size of a single (square) block.\n  .takes('blockSize', function validate(avatar, px) {\n    return typeof px == 'number' \u0026\u0026 px \u003e 0;\n  })\n  // Calculate the colors that make up the avatar.\n  .provides('hueAngle', function (avatar) {\n    // Use a named number generator to get an independent sequence.\n    return avatar.getRandGen('color').nextInt(360);\n  })\n  .provides('background', function (avatar) {\n    return 'hsl(' + avatar.hueAngle + ', 100%, 50%)';\n  })\n  .provides('foreground', function (avatar) {\n    var hueAngle = (avatar.hueAngle + 180) % 360;\n    return 'hsl(' + hueAngle + ', 100%, 50%)';\n  })\n  // 75% of avatars have a mirrored effect, others don't.\n  .provides('isMirrored', function (avatar) {\n    return avatar.getRandGen('mirror').nextFloat() \u003e .25;\n  })\n  // A particular avatar has a unique set of blocks.\n  .generates('block')\n    // The validator will run independently for both parameters.\n    .takes('x', 'y', function validate(block, xy) {\n      // We can refer to the parent instance (the avatar).\n      return typeof xy == 'number' \u0026\u0026 xy \u003e= 0 \u0026\u0026 xy \u003c block.avatar.size;\n    })\n    // The color of this block.\n    .provides('color', function (block) {\n      // You don't have to use named random generators.\n      if (block.getRandGen().nextFloat() \u003e .5) {\n        return block.avatar.foreground;\n      } else {\n        return block.avatar.background;\n      }\n    })\n    // Go back to defining the parent (avatar).\n    .done()\n  // Renders to a canvas and returns a URL for \u003cimg\u003e.\n  .provides('url', function (avatar) {\n    var canvas = document.createElement('canvas'),\n        context = canvas.getContext('2d');\n\n    canvas.width = avatar.size * avatar.blockSize;\n    canvas.height = avatar.size * avatar.blockSize;\n\n    context.fillStyle = avatar.background;\n    context.fillRect(0, 0, avatar.size, avatar.size);\n\n    var finalX = avatar.isMirrored ? avatar.size / 2 : avatar.size,\n        blockSize = avatar.blockSize;\n\n    for (var y = 0; y \u003c avatar.size; y++) {\n      for (var x = 0; x \u003c finalX; x++) {\n        var realX = x * blockSize, realY = y * blockSize;\n\n        var block = avatar.block(x, y);\n        context.fillStyle = block.color;\n        context.fillRect(realX, realY, blockSize, blockSize);\n\n        if (avatar.isMirrored) {\n          var mirroredX = avatar.size * blockSize - realX - blockSize;\n          context.fillRect(mirroredX, realY, blockSize, blockSize);\n        }\n      }\n    }\n\n    return canvas.toDataURL();\n  });\n\nvar img = document.createElement('img');\nimg.src = avatar('bob', 16, 4).url;\ndocument.body.appendChild(img);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblixt%2Fjs-procedural","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblixt%2Fjs-procedural","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblixt%2Fjs-procedural/lists"}