{"id":13765338,"url":"https://github.com/pixelpapercraft/pixel-papercraft-generator-builder","last_synced_at":"2025-05-10T21:30:57.846Z","repository":{"id":37016702,"uuid":"376392902","full_name":"pixelpapercraft/pixel-papercraft-generator-builder","owner":"pixelpapercraft","description":"Generator builder for Pixel Papercraft.","archived":false,"fork":false,"pushed_at":"2024-06-11T03:09:29.000Z","size":18014,"stargazers_count":32,"open_issues_count":26,"forks_count":13,"subscribers_count":5,"default_branch":"main","last_synced_at":"2024-11-17T01:32:46.188Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.pixelpapercraft.com/print","language":"ReScript","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/pixelpapercraft.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-12T22:16:28.000Z","updated_at":"2024-09-01T16:09:59.000Z","dependencies_parsed_at":"2024-08-03T16:13:59.136Z","dependency_job_id":null,"html_url":"https://github.com/pixelpapercraft/pixel-papercraft-generator-builder","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pixelpapercraft%2Fpixel-papercraft-generator-builder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pixelpapercraft%2Fpixel-papercraft-generator-builder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pixelpapercraft%2Fpixel-papercraft-generator-builder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pixelpapercraft%2Fpixel-papercraft-generator-builder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pixelpapercraft","download_url":"https://codeload.github.com/pixelpapercraft/pixel-papercraft-generator-builder/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253484884,"owners_count":21915917,"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-08-03T16:00:37.199Z","updated_at":"2025-05-10T21:30:56.629Z","avatar_url":"https://github.com/pixelpapercraft.png","language":"ReScript","funding_links":[],"categories":["ReScript"],"sub_categories":["Example Apps"],"readme":"# Pixel Papercraft Generator Builder\n\n## Getting started\n\n### First time setup\n\nInstall Node and Git.\n\nNode is best installed using [Node Version Manager](https://github.com/nvm-sh/nvm).\n\nThe project currently requires the following Node and NPM versions:\n\n```\n\"engines\": {\n  \"node\": \"\u003e=18\",\n  \"npm\": \"\u003e=9\"\n}\n```\n\nInstall Node.\n\n```sh\nnvm install v18\n```\n\nTo ensure you have the latest NPM:\n\n```\nnpm i -g npm@latest\n```\n\nInstall Git [from the official site](https://git-scm.com/downloads).\n\nClone this repository and install the dependencies:\n\n```sh\ncd \u003cparent_folder_for_the_generator_builder\u003e\ngit clone https://github.com/pixelpapercraft/pixel-papercraft-generator-builder generator-builder\ncd generator-builder\nnpm install\n```\n\n### Set up ReScript and Visual Studio Code\n\nGenerators are developed using the [ReScript](https://rescript-lang.org/) programming language.\n\nReScript development is best done using [Visual Studio Code](https://code.visualstudio.com/).\n\nAfter installing VSCode, then install the official ReScript extension `rescript-vscode` from the VSCode Extensions panel.\n\n### Development\n\nEnsure you are running the correct version of Node:\n\n```\nnvm use\n```\n\nIn one terminal, start the ReScript compiler:\n\n```\nnpm run res:watch\n```\n\nIn another terminal, start the web server:\n\n```\nnpm run dev\n```\n\nThen open your browser:\n\n```\nhttp://localhost:3000\n```\n\n## Example Generator\n\nThis is a quick example of what a simple generator script might look like.\n\nThis will be explained further in the sections below.\n\n```res\n// Unique id for the generator\nlet id = \"face-generator\"\n\n// Name for the generator\nlet name = \"Face Generator\"\n\n// Array of image URLs the generator may use\nlet images: array\u003cGenerator.imageDef\u003e = [\n  {\n    id: \"Background\",\n    url: Generator.require(\"./images/Background.png\"),\n  }\n]\n\n// Array of texture URLs the generator may use\nlet textures: array\u003cGenerator.textureDef\u003e = [\n  {\n    id: \"Skin\",\n    url: Generator.require(\"./textures/Skin.png\"),\n    standardWidth: 64,\n    standardHeight: 64,\n  },\n]\n\n// The generator script\nlet script = () =\u003e {\n  Generator.drawImage(\"Background\", (0, 0))\n\n  let ox = 74\n  let oy = 25\n  Generator.drawTexture(\n    \"Skin\",\n    (16, 8, 8, 8),\n    (ox + 128, oy + 64, 64, 64),\n    ()\n  )\n}\n\n// Export the generator details\nlet generator: Generator.generatorDef = {\n  id: id,\n  name: name,\n  images: images,\n  textures: textures,\n  script: script,\n}\n```\n\n## Developing generators\n\nCode for generators is in the `src/generators` directory.\n\nYou can use any directory structure you like for each generator, but a recommended structure is:\n\n```\n/example\n  /images\n  /textures\n  /Example.res\n```\n\nEach generator should export a property named `generator` that has the type `Generator.generatorDef`. For example:\n\n```res\nlet generator: Generator.generatorDef = {\n  id: id,\n  name: name,\n  history: history,\n  thumbnail: Some(thumbnail),\n  video: None,\n  instructions: Some(\u003cGenerator.Markdown\u003e {instructions} \u003c/Generator.Markdown\u003e),\n  images: images,\n  textures: textures,\n  script: script,\n}\n```\n\nThese properties are:\n\n| Property     | Description                                      |\n| ------------ | ------------------------------------------------ |\n| id           | Unique Id for the generator, used in the URL     |\n| name         | Name for the generator                           |\n| images       | Array of image URLs                              |\n| textures     | Array of texture image URLs                      |\n| script       | Generator script                                 |\n| history      | Array of strings, used as the history of updates |\n| thumbnail    | Thumbnail image                                  |\n| video        | Video (optional)                                 |\n| instructions | Instructions for the generator                   |\n\nLastly, add the generator to the `src/generators/Generators.res` file.\n\n```res\nlet generators = [\n  Example.generator,\n]\n```\n\n## Contributing\n\nTo contribute to the project, you will need to do a few steps:\n\n1. Create a \"fork\" of the generator project into your account.\n2. Create a \"branch\" for a particular change you want to make.\n3. Create a \"pull request\" to request for your branch to be added to the main generator project.\n\n[Read more about proposing changes on Github](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests).\n\nIf you need help, ask in the #generator-help channel on Discord.\n\n**Tip:** It's best to keep your branches and pull requests small. For example, create a separate branch and pull request for each generator. This helps to get the changes merged into the main project more quickly.\n\n## Images vs Textures\n\n**Images** are just simple images that can be drawn onto the pages. You cannot draw parts of images, you can only draw the whole image. This makes them very fast to draw. You will typically use images for things like backgrounds and folds.\n\n**Textures** are used when you want to draw parts of an image onto the page and those parts may be scaled, flipped or rotated, etc. Textures are slow to draw because of the image processing needed.\n\n## Generator Programming Guide\n\n### Loading images\n\nImages needed by the generator are specified as an array of `Generator.imageDef`.\n\nNote that the `url` property of the images must be created with `Generator.require()`.\n\n```res\nlet images: array\u003cGenerator.imageDef\u003e = [\n  {\n    id: \"Background\",\n    url: Generator.require(\"./images/Background.png\"),\n  },\n  {\n    id: \"Folds\",\n    url: Generator.require(\"./images/Folds.png\"),\n  },\n]\n```\n\n### Loading textures\n\nTextures needed by the generator are specified as an array of `Generator.textureDef`.\n\nNote that the `url` property of the textures must be created with `Generator.require()`.\n\nAlso note that textures must specify a `standardWidth` and `standardHeight`. This allows higher resolution versions of those textures to also work.\n\n```res\nlet textures: array\u003cGenerator.textureDef\u003e = [\n  {\n    id: \"Skin\",\n    url: Generator.require(\"./textures/Skin.png\"),\n    standardWidth: 64,\n    standardHeight: 64,\n  },\n]\n```\n\n### Writing the script\n\nThe script should be specified as the `script` property of the generator.\n\n```res\nlet script = () =\u003e {\n  Generator.drawImage(\"Background\", (0, 0))\n}\n```\n\n### Defining Inputs for Textures\n\nUse the `defineTextureInput` function.\n\nYou must also specify some options:\n\n- `standardWidth` and `standardHeight` - the default width and height of the texture. These are required so that higher resolution textures will work.\n- `choices` - an array of texture names that may be selected instead of selecting a texture file. Specify an empty array `[]` if none required.\n\n```res\nGenerator.defineTextureInput(\n  \"Skin\",\n  {\n    standardWidth: 64,\n    standardHeight: 64,\n    choices: [\"Steve\", \"Alex\"]\n  }\n)\n```\n\n### Drawing images\n\nUse the `Generator.drawImage()` function.\n\nTo draw an image at position `x` = `0` and `y` = `0`:\n\n```res\nGenerator.drawImage(\"Background\", (0, 0))\n```\n\nTo draw an image at position `x` = `50` and `y` = `100`:\n\n```res\nGenerator.drawImage(\"Background\", (50, 100))\n```\n\n### Drawing textures\n\nDrawing a texture means that you want to copy a rectangle from the texture onto the page.\n\nTo do this, you need to:\n\n1. Identify the x, y coordinates plus width and height of a rectangle on the texture to copy\n2. Identify the x, y coordinates plus width and height of a rectangle on the page to draw it into\n\nFor example, suppose we want to copy the face from the following texture onto the page.\n\n![Texture with part of the image highlighted](./docs/images/draw-texture-skin.png)\n\nThis is a rectangle (a square in this case) with the following coordinates and size:\n\n```\nx = 8\ny = 8\nwidth = 8\nheight = 8\n```\n\nIn ReScript we will write these coordinates as:\n\n```res\n(8, 8, 8, 8)\n```\n\nNext, we need to identify the rectange on the page.\n\n![Page with part of the image highlighted](./docs/images/draw-texture-page.png)\n\nIt needs to draw onto the page as a bigger rectangle with the following coordinates and size:\n\n```\nx = 138\ny = 89\nwidth = 64\nheight = 64\n```\n\nIn ReScript we write this as:\n\n```res\n(138, 89, 64, 64)\n```\n\nAlso suppose the name of the texture is `Skin`, then to draw this texture onto the page we would write:\n\n```res\nGenerator.drawTexture(\n  \"Skin\",\n  (8, 8, 8, 8),\n  (138, 89, 64, 64),\n  (),\n);\n```\n\n### Rotating textures\n\nWhen using `Generator.drawTexture()` you can rotate textures using the `~rotate` argument.\n\nThe `~rotate` argument can be any `float` value.\n\n```res\nGenerator.drawTexture(\n  \"Skin\",\n  (8, 8, 8, 8),\n  (138, 89, 64, 64),\n  ~rotate=90.0,\n  (),\n);\n```\n\n### Flipping textures\n\nWhen using `Generator.drawTexture()` you can flip textures using the `~flip` argument.\n\nThe `~flip` argument can be either `~flip=#Horizontal` or `~flip=#Vertical`.\n\n```res\nGenerator.drawTexture(\n  \"Skin\",\n  (8, 8, 8, 8),\n  (138, 89, 64, 64),\n  ~flip=#Vertical,\n  (),\n);\n```\n\n### Pixelating textures\n\nWhen drawing textures and the destination shape is different to the source shape it can cause the texture to sometimes appear squashed, which makes the texture look messy. In these cases you can apply a `pixelate` option to keep the result looking pixelated.\n\n```res\nGenerator.drawTexture(\n  \"Skin\",\n  (8, 8, 8, 8), // Square shape\n  (150, 150, 64, 256), // Rectangle shape\n  ~pixelate=true,\n  (),\n);\n```\n\n### Blending colors\n\nWhen using `Generator.drawTexture()` you can blend textures with a color using the `~blend` argument.\n\nThis is useful when adding a tint to certain blocks in Minecraft corresponding to a biome.\n\nThe `~blend` argument can be either:\n\n- `#None` meaning no blend.\n- `#MultiplyHex(string)` for a hex string, such as `#MultiplyHex(\"#90814D\")`.\n- `#MultiplyRGB(int, int, int)` for red, green and blue values from 0 to 255, such as `#MultiplyRGB(144, 129, 77)`.\n\n```res\nGenerator.drawTexture(\n  \"Skin\",\n  (8, 8, 8, 8),\n  (138, 89, 64, 64),\n  ~blend=#MultiplyHex(\"#90814D\"),\n  (),\n);\n```\n\n### Using multiple pages\n\nBy default the generator gives you one blank page to work with, however some designs may require more than one page.\n\nTo specify additional pages you can use the `Generator.usePage()` function. You just have to choose a name for each page, which can be any name you like.\n\n```res\n// Choose the first page\nGenerator.usePage(\"Head and Body\");\n\n// Draw images and textures here\n\n// Choose the next page\nGenerator.usePage(\"Legs\");\n\n// Draw images and textures here\n```\n\nIf each page doesn't have a specific purpose, just call them \"Page 1\", \"Page 2\", etc.\n\n```res\n// Choose the first page\nGenerator.usePage(\"Page 1\");\n\n// Draw images and textures here\n\n// Then choose the next page\nGenerator.usePage(\"Page 2\");\n\n// Draw images and textures here\n```\n\n### Getting user input\n\nIt's sometimes useful to give your user some choices in your generator.\n\nFor example, some people want the fold lines, and others don't, or your generator might have list of weapons they can choose from.\n\n#### Boolean inputs\n\nBoolean inputs provide a `true` or `false` choice. They may be used to show or hide certain parts of your generator.\n\nUse `Generator.defineBooleanInput()` to create boolean inputs.\n\n```res\nGenerator.defineBooleanInput(\"Show Folds\", true) // Initially true\nGenerator.defineBooleanInput(\"Show Labels\", true) // Initially true\n```\n\nTo use these values use `Generator.getBooleanInputValue()`\n\n```res\nlet showFolds = Generator.getBooleanInputValue(\"Show Folds\");\nlet showLabels = Generator.getBooleanInputValue(\"Show Labels\");\n\nif (showFolds) {\n  Generator.drawImage(\"Folds\", (0, 0));\n}\n\nif (showLabels) {\n  Generator.drawImage(\"Labels\", (0, 0));\n}\n```\n\n#### Select inputs\n\nSelect variables let your user choose from a list of values that you provide.\n\nUse `Generator.defineSelectInput()` to create select inputs.\n\n```res\nGenerator.defineSelectInput(\"Weapon\", [\"None\", \"Sword\", \"Crossbow\"])\n```\n\nTo use these values use `Generator.getSelectInputValue()`\n\n```res\nlet weapon = Generator.getSelectInputValue(\"Weapon\");\n\nif weapon === \"Sword\" {\n  Generator.drawImage(\"Sword\", (100, 100))\n} else if weapon === \"Crossbow\" {\n  Generator.drawImage(\"Crossbow\", (100, 100))\n}\n```\n\nOr an alternative syntax:\n\n```res\nlet weapon = Generator.getSelectInputValue(\"Weapon\");\n\nswitch weapon {\n  | \"Sword\" =\u003e Generator.drawImage(\"Sword\", (100, 100))\n  | \"Crossbow\" =\u003e Generator.drawImage(\"Crossbow\", (100, 100))\n  | _ =\u003e () // All other values, do nothing\n}\n```\n\n### Defining clickable regions\n\nYou can define clickable regions on your pages, and what actions should occur when a region is clicked.\n\nClickable regions are defined using the `Generator.defineRegionInput(region, onClick)` function.\n\nFor example, you could toggle a variable using a clickable region.\n\n```res\n// Define the input\nGenerator.defineBooleanInput(\"Show overlay\", false)\n\n// Get the input value\nlet showOverlay = Generator.getBooleanInputValue(\"Show overlay\")\n\nlet region = (0, 0, 100, 100)\nlet onClick = () =\u003e {\n  // Toggle the input value\n  Generator.setBooleanInputValue(\"Show overlay\", !showOverlay)\n}\n\n// Define the clickable region\nGenerator.defineRegionInput(region, onClick)\n```\n\nBut this could be written more concisely as:\n\n```res\nGenerator.defineBooleanInput(\"Show overlay\", false)\nlet showOverlay = Generator.getBooleanInputValue(\"Show overlay\")\n\nGenerator.defineRegionInput((0, 0, 100, 100), () =\u003e {\n  Generator.setBooleanInputValue(\"Show overlay\", !showOverlay)\n})\n```\n\n### Providing instructions or comments\n\nYou can provide some text instructions and comments with your inputs using `Generator.defineText()`.\n\n```res\nGenerator.defineText(\"Click body parts to change the texture.\")\n```\n\n### Filling the background color\n\nBy default, the generator uses a transparent background. You can fill the background with `Generator.fillBackgroundColor()`.\n\n```res\n// Fill the background color with red\nGenerator.fillBackgroundColor(\"#ff0000\")\n\n// Fill the background color with white\nGenerator.fillBackgroundColorWithWhite()\n```\n\nNote: This does not overwrite any images of textures you've drawn. It just changes the background from transparent to the color you specify.\n\n### Drawing lines\n\nYou can draw straight lines onto the generator canvas. The line color, width, dash pattern, and dash offset can all be customized, but the defaults will make a line that works well for standard tab lines.\n\n```res\n// Draw a simple black line starting at (0, 100) and ends at (50, 150)\nGenerator.drawLine((0, 100), (50, 150), ())\n\n// Draw the same line, but red and with a 4 pixel width\nGenerator.drawLine((0, 100), (50, 150), ~color=\"#ff0000\", ~width=4, ())\n```\n\n### Drawing fold lines\n\nYou can also draw standard fold lines with a simpler function:\n\n```res\nGenerator.drawFoldLine((0, 100), (50, 150))\n```\n\n### Getting pixel colors\n\nThere are a few functions to get specific pixel colors:\n\n- `Generator.getTexturePixelColor(textureName, x, y)`\n- `Generator.getImagePixelColor(imageName, x, y)`\n- `Generator.getCurrentPagePixelColor(x, y)`\n- `Generator.getPagePixelColor(pageName, x, y)`\n\nFor example, using `Generator.getTexturePixelColor()`:\n\nIf you have a texture named `Colors` and you want to get the pixel color at position `(0, 0)` then you would call:\n\n```res\nlet color = Generator.getTexturePixelColor(\"Colors\", 0, 0)\n```\n\nThis returns a [ReScript Option](https://rescript-lang.org/docs/manual/latest/api/belt/option) which may contain a tuple `(red, green, blue, alpha)`.\n\nEach value of `red`, `green`, `blue` and `alpha` are integers from 0 to 255.\n\nMore complete example:\n\n```res\nlet colorOpt = Generator.getTexturePixelColor(\"Colors\", 0, 0)\nlet color = switch colorOpt {\n| None =\u003e \"Unknown\"\n| Some(color) =\u003e {\n    let (r, g, b, a) = color\n    let r = Belt.Int.toString(r)\n    let g = Belt.Int.toString(g)\n    let b = Belt.Int.toString(b)\n    let a = Belt.Int.toString(a)\n    `(${r}, ${g}, ${b}, ${a})`\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpixelpapercraft%2Fpixel-papercraft-generator-builder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpixelpapercraft%2Fpixel-papercraft-generator-builder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpixelpapercraft%2Fpixel-papercraft-generator-builder/lists"}