├── .dockerignore ├── .github └── FUNDING.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── classes ├── Graph.js └── nodes │ ├── Connection.js │ ├── Input.js │ ├── InputColor.js │ ├── InputColorState.js │ ├── InputImage.js │ ├── InputImageState.js │ ├── InputNumber.js │ ├── InputString.js │ ├── Node.js │ ├── NodeColor.js │ ├── NodeImage.js │ ├── NodeNumber.js │ ├── NodeString.js │ ├── Output.js │ ├── OutputColor.js │ ├── OutputImage.js │ ├── OutputNumber.js │ ├── OutputString.js │ ├── Properties.js │ ├── adjustment │ ├── Blur.js │ ├── BlurProperties.jsx │ ├── BrightnessContrast.js │ ├── BrightnessContrastProperties.jsx │ ├── Dither.js │ ├── DitherProperties.jsx │ ├── GaussianBlur.js │ ├── GaussianBlurProperties.jsx │ ├── Greyscale.js │ ├── GreyscaleProperties.jsx │ ├── HSL.js │ ├── HSLProperties.jsx │ ├── Invert.js │ ├── InvertProperties.jsx │ ├── Levels.js │ ├── LevelsProperties.jsx │ ├── Normalize.js │ ├── NormalizeProperties.jsx │ ├── OutputLevels.js │ └── OutputLevelsProperties.jsx │ ├── alpha │ ├── Fade.js │ ├── FadeProperties.jsx │ ├── Mask.js │ ├── MaskProperties.jsx │ ├── Opacity.js │ ├── OpacityProperties.jsx │ ├── Opaque.js │ └── OpaqueProperties.jsx │ ├── channel │ ├── ChannelMerge.js │ ├── ChannelMergeProperties.jsx │ ├── ChannelSplit.js │ └── ChannelSplitProperties.jsx │ ├── color │ ├── AnalogousColors.js │ ├── AnalogousColorsProperties.jsx │ ├── ColorAdjust.js │ ├── ColorAdjustProperties.jsx │ ├── ColorInput.js │ ├── ColorInputProperties.jsx │ ├── ComplimentColor.js │ ├── ComplimentColorProperties.jsx │ ├── GetAlpha.js │ ├── GetAlphaProperties.jsx │ ├── GetBrightness.js │ ├── GetBrightnessProperties.jsx │ ├── GetLuminance.js │ ├── GetLuminanceProperties.jsx │ ├── HSLInput.js │ ├── HSLInputProperties.jsx │ ├── HSVInput.js │ ├── HSVInputProperties.jsx │ ├── MixColors.js │ ├── MixColorsProperties.jsx │ ├── MonochromaticColors.js │ ├── MonochromaticColorsProperties.jsx │ ├── RGBInput.js │ ├── RGBProperties.jsx │ ├── Sample.js │ ├── SampleProperties.jsx │ ├── SplitComplimentColors.js │ ├── SplitComplimentColorsProperties.jsx │ ├── TetradColors.js │ ├── TetradColorsProperties.jsx │ ├── TriadColors.js │ └── TriadColorsProperties.jsx │ ├── composing │ ├── Blend.js │ ├── BlendProperties.jsx │ ├── Blit.js │ └── BlitProperties.jsx │ ├── control │ ├── Counter.js │ ├── CounterProperties.jsx │ ├── Else.js │ ├── ElseColor.js │ ├── ElseColorProperties.jsx │ ├── ElseNumber.js │ ├── ElseNumberProperties.jsx │ ├── ElseProperties.jsx │ ├── ElseString.js │ ├── ElseStringProperties.jsx │ ├── IfColor.js │ ├── IfColorProperties.jsx │ ├── IfImage.js │ ├── IfImageProperties.jsx │ ├── IfNumber.js │ ├── IfNumberProperties.jsx │ ├── IfString.js │ ├── IfStringProperties.jsx │ ├── Loop.js │ ├── LoopInputImageLoopOutput.js │ ├── LoopInputNumberNumLoops.js │ ├── LoopOutputImageLoopInput.js │ └── LoopProperties.jsx │ ├── filter │ ├── AmbientOcclusion.js │ ├── AmbientOcclusionProperties.jsx │ ├── NormalMap.js │ ├── NormalMapProperties.jsx │ ├── Pixelate.js │ ├── PixelateProperties.jsx │ ├── Posterize.js │ ├── PosterizeProperties.jsx │ ├── Sepia.js │ ├── SepiaProperties.jsx │ ├── Sobel.js │ ├── SobelProperties.jsx │ ├── StainedGlass.js │ └── StainedGlassProperties.jsx │ ├── image │ ├── Circle.js │ ├── CircleProperties.jsx │ ├── Faultline.js │ ├── FaultlineProperties.jsx │ ├── Gradient.js │ ├── GradientProperties.jsx │ ├── ImageUpload.js │ ├── ImageUploadProperties.jsx │ ├── Line.js │ ├── LineProperties.jsx │ ├── PasteImage.js │ ├── PasteImageProperties.jsx │ ├── Simplex.js │ ├── SimplexProperties.jsx │ ├── Text.js │ ├── TextProperties.jsx │ ├── Triangle.js │ ├── TriangleProperties.jsx │ ├── UniformColor.js │ ├── UniformColorProperties.jsx │ ├── UrlImage.js │ ├── UrlImageProperties.jsx │ ├── Worley.js │ └── WorleyProperties.jsx │ ├── number │ ├── AbsoluteValue.js │ ├── AbsoluteValueProperties.jsx │ ├── AddNumbers.js │ ├── AddNumbersProperties.jsx │ ├── Atan2.js │ ├── Atan2Properties.jsx │ ├── Ceil.js │ ├── CeilProperties.jsx │ ├── Cos.js │ ├── CosProperties.jsx │ ├── DegreesToRadians.js │ ├── DegreesToRadiansProperties.jsx │ ├── DivideNumbers.js │ ├── DivideNumbersProperties.jsx │ ├── ExponentNumbers.js │ ├── ExponentNumbersProperties.jsx │ ├── Floor.js │ ├── FloorProperties.jsx │ ├── GetImageSize.js │ ├── GetImageSizeProperties.jsx │ ├── Max.js │ ├── MaxProperties.jsx │ ├── Min.js │ ├── MinProperties.jsx │ ├── ModuloNumbers.js │ ├── ModuloNumbersProperties.jsx │ ├── MultiplyNumbers.js │ ├── MultiplyNumbersProperties.jsx │ ├── Number.js │ ├── NumberProperties.jsx │ ├── Pi.js │ ├── PiProperties.jsx │ ├── RadiansToDegrees.js │ ├── RadiansToDegreesProperties.jsx │ ├── RandomNumber.js │ ├── RandomNumberProperties.jsx │ ├── Round.js │ ├── RoundProperties.jsx │ ├── Sin.js │ ├── SinProperties.jsx │ ├── Slider.js │ ├── SliderProperties.jsx │ ├── SquareRoot.js │ ├── SquareRootProperties.jsx │ ├── SubtractNumbers.js │ └── SubtractNumbersProperties.jsx │ ├── outputs │ ├── Output.js │ └── OutputProperties.jsx │ ├── strings │ ├── Length.js │ ├── LengthProperties.jsx │ ├── NumberToString.js │ ├── NumberToStringProperties.jsx │ ├── StringInput.js │ └── StringInputProperties.jsx │ └── transform │ ├── Crop.js │ ├── CropProperties.jsx │ ├── Displace.js │ ├── DisplaceProperties.jsx │ ├── FlipHorizontal.js │ ├── FlipHorizontalProperties.jsx │ ├── FlipVertical.js │ ├── FlipVerticalProperties.jsx │ ├── Resize.js │ ├── ResizeProperties.jsx │ ├── Rotate.js │ └── RotateProperties.jsx ├── components ├── GraphProperties.jsx ├── GraphThumbs.jsx ├── GraphView.jsx ├── JsonView.jsx ├── PropertiesInputNumber.jsx ├── PropertiesInputSlider.jsx └── TopBar.jsx ├── deploy.bat ├── deploy.sh ├── layouts └── MainLayout.js ├── lib ├── functions.js └── settings.js ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── createaccount.jsx ├── g │ ├── [graphId].js │ └── [graphId] │ │ └── [slug].js ├── graphs.jsx ├── index.js ├── json │ ├── [graphId].js │ └── [graphId] │ │ └── [slug].js ├── login.jsx ├── logout.jsx └── u │ └── [userId].js ├── public └── static │ ├── discordIcon.png │ ├── discordIcon_highres.png │ ├── emailIcon.png │ ├── emailIcon_highres.png │ ├── favicon.png │ ├── fonts │ └── open-sans │ │ ├── open-sans-10-black │ │ ├── open-sans-10-black.fnt │ │ └── open-sans-10-black.png │ │ ├── open-sans-12-black │ │ ├── open-sans-12-black.fnt │ │ └── open-sans-12-black.png │ │ ├── open-sans-128-black │ │ ├── open-sans-128-black.fnt │ │ └── open-sans-128-black.png │ │ ├── open-sans-128-white │ │ ├── open-sans-128-white.fnt │ │ └── open-sans-128-white.png │ │ ├── open-sans-14-black │ │ ├── open-sans-14-black.fnt │ │ └── open-sans-14-black.png │ │ ├── open-sans-16-black │ │ ├── open-sans-16-black.fnt │ │ └── open-sans-16-black.png │ │ ├── open-sans-16-white │ │ ├── open-sans-16-white.fnt │ │ └── open-sans-16-white.png │ │ ├── open-sans-32-black │ │ ├── open-sans-32-black.fnt │ │ └── open-sans-32-black.png │ │ ├── open-sans-32-white │ │ ├── open-sans-32-white.fnt │ │ └── open-sans-32-white.png │ │ ├── open-sans-64-black │ │ ├── open-sans-64-black.fnt │ │ └── open-sans-64-black.png │ │ ├── open-sans-64-white │ │ ├── open-sans-64-white.fnt │ │ └── open-sans-64-white.png │ │ ├── open-sans-8-black │ │ ├── open-sans-8-black.fnt │ │ └── open-sans-8-black.png │ │ └── open-sans-8-white │ │ ├── open-sans-8-white.fnt │ │ └── open-sans-8-white.png │ ├── githubLogo.png │ ├── githubLogo_highres.png │ ├── og.jpg │ ├── patreonIcon.png │ ├── patreonIcon_highres.png │ ├── screenshots │ ├── screenshot01.JPG │ ├── screenshot02.JPG │ └── screenshot03.JPG │ ├── trelloIcon.png │ ├── trelloIcon_highres.png │ ├── twitterIcon.png │ ├── twitterIcon_highres.png │ └── viewportBg.jpg └── server ├── api ├── createaccount.js ├── deletegraph.js ├── graph.js ├── graphs.js ├── login.js ├── logout.js └── savegraph.js ├── functions.js └── index.js /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .next 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: dan335 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .next/ 2 | node_modules/ 3 | /npm-debug.log 4 | .env 5 | .DS_Store 6 | /.env.production 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | 3 | RUN apk add --no-cache make gcc g++ python3 build-base 4 | 5 | ENV PORT=80 TERM=xterm 6 | 7 | RUN mkdir -p /usr/src/app 8 | WORKDIR /usr/src/app 9 | 10 | COPY package.json /usr/src/app/ 11 | RUN npm install --production 12 | 13 | COPY . /usr/src/app 14 | 15 | RUN npm run build 16 | EXPOSE 80 17 | 18 | CMD [ "npm", "start" ] 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Daniel Phillips 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NIMP 2 | 3 | https://nimp.app 4 | 5 | Node-Based Image Manipulation Program 6 | 7 | ![](https://nimp.app/static/screenshots/screenshot03.JPG) 8 | 9 | ### Run 10 | ``` 11 | start Mongodb 12 | git clone https://github.com/dan335/nimp 13 | cd nimp 14 | npm install 15 | npm run dev 16 | View in browser at http://localhost:3000 17 | ``` 18 | 19 | * See the License file for license rights and limitations (MIT). 20 | * Pull requests are welcome. 21 | -------------------------------------------------------------------------------- /classes/nodes/InputColor.js: -------------------------------------------------------------------------------- 1 | import Input from './Input.js'; 2 | 3 | 4 | export default class InputColor extends Input { 5 | constructor(node, index, name) { 6 | super(node, index, name, 'Color'); 7 | this.color = null; 8 | } 9 | 10 | connectionMade() { 11 | if (this.node.bg.classList.contains('selected')) { 12 | this.node.propertiesComponentInstance.setState({hasColorInput:true}); 13 | } 14 | } 15 | 16 | 17 | connectionRemoved() { 18 | if (this.node.bg.classList.contains('selected')) { 19 | this.node.propertiesComponentInstance.setState({hasColorInput:false}); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /classes/nodes/InputColorState.js: -------------------------------------------------------------------------------- 1 | // need to eventually merge InputNumber with this and update all nodes to use this 2 | 3 | 4 | import InputColor from './InputColor.js'; 5 | 6 | 7 | export default class InputColorState extends InputColor { 8 | constructor(node, index, name, stateVariableName) { 9 | super(node, index, name); 10 | this.stateVariableName = stateVariableName; 11 | } 12 | 13 | 14 | connectionMade() { 15 | if (this.node.bg.classList.contains('selected')) { 16 | const t = {}; 17 | t[this.stateVariableName] = true; 18 | this.node.propertiesComponentInstance.setState(t); 19 | } 20 | } 21 | 22 | 23 | connectionRemoved() { 24 | if (this.node.bg.classList.contains('selected')) { 25 | const t = {}; 26 | t[this.stateVariableName] = false; 27 | this.node.propertiesComponentInstance.setState(t); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /classes/nodes/InputImage.js: -------------------------------------------------------------------------------- 1 | import Input from './Input.js'; 2 | 3 | 4 | export default class InputImage extends Input { 5 | constructor(node, index, name) { 6 | super(node, index, name, 'Image'); 7 | this.image = null; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /classes/nodes/InputImageState.js: -------------------------------------------------------------------------------- 1 | // need to eventually merge InputNumber with this and update all nodes to use this 2 | 3 | 4 | import InputImage from './InputImage.js'; 5 | 6 | 7 | export default class InputImageState extends InputImage { 8 | constructor(node, index, name, stateVariableName) { 9 | super(node, index, name); 10 | this.stateVariableName = stateVariableName; 11 | } 12 | 13 | 14 | connectionMade() { 15 | if (this.node.bg.classList.contains('selected')) { 16 | const t = {}; 17 | t[this.stateVariableName] = true; 18 | this.node.propertiesComponentInstance.setState(t); 19 | } 20 | } 21 | 22 | 23 | connectionRemoved() { 24 | if (this.node.bg.classList.contains('selected')) { 25 | const t = {}; 26 | t[this.stateVariableName] = false; 27 | this.node.propertiesComponentInstance.setState(t); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /classes/nodes/InputNumber.js: -------------------------------------------------------------------------------- 1 | import Input from './Input.js'; 2 | 3 | 4 | export default class InputNumber extends Input { 5 | constructor(node, index, name, stateVariableName) { 6 | super(node, index, name, 'Number'); 7 | this.number = null; 8 | this.stateVariableName = stateVariableName; 9 | } 10 | 11 | connectionMade() { 12 | if (this.node.bg.classList.contains('selected')) { 13 | const t = {}; 14 | t[this.stateVariableName] = true; 15 | this.node.propertiesComponentInstance.setState(t); 16 | } 17 | } 18 | 19 | 20 | connectionRemoved() { 21 | if (this.node.bg.classList.contains('selected')) { 22 | const t = {}; 23 | t[this.stateVariableName] = false; 24 | this.node.propertiesComponentInstance.setState(t); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /classes/nodes/InputString.js: -------------------------------------------------------------------------------- 1 | import Input from './Input.js'; 2 | 3 | 4 | export default class InputString extends Input { 5 | constructor(node, index, name, stateVariableName) { 6 | super(node, index, name, 'String'); 7 | this.string = null; 8 | this.stateVariableName = stateVariableName; 9 | } 10 | 11 | 12 | connectionMade() { 13 | if (this.node.bg.classList.contains('selected')) { 14 | const t = {}; 15 | t[this.stateVariableName] = true; 16 | this.node.propertiesComponentInstance.setState(t); 17 | } 18 | } 19 | 20 | 21 | connectionRemoved() { 22 | if (this.node.bg.classList.contains('selected')) { 23 | const t = {}; 24 | t[this.stateVariableName] = false; 25 | this.node.propertiesComponentInstance.setState(t); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /classes/nodes/NodeColor.js: -------------------------------------------------------------------------------- 1 | import Node from './Node.js'; 2 | const tinycolor = require("tinycolor2"); 3 | var debounce = require('lodash.debounce'); 4 | 5 | 6 | export default class NodeColor extends Node { 7 | constructor(className, graph, x, y, name, propertiesComponent, settings) { 8 | super(className, graph, x, y, name, propertiesComponent, settings); 9 | 10 | this.color = tinycolor('#fff'); 11 | 12 | this.debouncedRenderPreview = debounce(this.renderPreview, 300); 13 | this.renderName(); 14 | } 15 | 16 | 17 | view() { 18 | // do nothing 19 | } 20 | 21 | deView() { 22 | // do nothing 23 | } 24 | 25 | 26 | passToChildren() { 27 | this.outputs.forEach(output => { 28 | output.connections.forEach(conn => { 29 | if (this.color == null) { 30 | conn.color = null; 31 | } else { 32 | conn.color = this.color; 33 | } 34 | conn.runNode(); 35 | }) 36 | }) 37 | } 38 | 39 | 40 | run(inputThatTriggered) { 41 | this.timer.textContent = (Date.now() - this.runTimer) + 'ms'; 42 | this.bg.classList.remove('running'); 43 | this.passToChildren(); 44 | 45 | if (this.color) { 46 | this.debouncedRenderPreview(); 47 | } else { 48 | this.preview.setAttributeNS(null, 'href', ''); 49 | } 50 | } 51 | 52 | 53 | renderPreview() { 54 | new Jimp(1, 1, this.color.toHex8(), (error, image) => { 55 | if (error) { 56 | console.log(error); 57 | } else { 58 | image.getBufferAsync(Jimp.MIME_PNG).then(i => { 59 | this.preview.setAttributeNS(null, 'href', 'data:'+Jimp.MIME_PNG+';base64,'+i.toString('base64')); 60 | }); 61 | } 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /classes/nodes/NodeNumber.js: -------------------------------------------------------------------------------- 1 | import Node from './Node.js'; 2 | 3 | 4 | export default class NodeNumber extends Node { 5 | constructor(className, graph, x, y, name, propertiesComponent, settings) { 6 | super(className, graph, x, y, name, propertiesComponent, settings); 7 | 8 | this.number = 1; 9 | 10 | this.textPreview.style.display = 'block'; 11 | this.renderName(); 12 | } 13 | 14 | 15 | view() { 16 | // do nothing 17 | } 18 | 19 | deView() { 20 | // do nothing 21 | } 22 | 23 | 24 | passToChildren() { 25 | this.outputs.forEach(output => { 26 | output.connections.forEach(conn => { 27 | if (this.number == null || isNaN(this.number)) { 28 | conn.number = null; 29 | } else { 30 | conn.number = this.number; 31 | } 32 | conn.runNode(); 33 | }) 34 | }) 35 | } 36 | 37 | 38 | run(inputThatTriggered) { 39 | this.timer.textContent = (Date.now() - this.runTimer) + 'ms'; 40 | this.bg.classList.remove('running'); 41 | this.passToChildren(); 42 | this.renderPreview(); 43 | } 44 | 45 | 46 | renderPreview() { 47 | if (this.number != null) { 48 | this.textPreview.textContent = this.number; 49 | } else { 50 | this.textPreview.textContent = ''; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /classes/nodes/NodeString.js: -------------------------------------------------------------------------------- 1 | import Node from './Node.js'; 2 | 3 | 4 | export default class NodeString extends Node { 5 | constructor(className, graph, x, y, name, propertiesComponent, settings) { 6 | super(className, graph, x, y, name, propertiesComponent, settings); 7 | 8 | this.string = 'Nimp'; 9 | 10 | this.textPreview.style.display = 'block'; 11 | this.renderName(); 12 | } 13 | 14 | 15 | view() { 16 | // do nothing 17 | } 18 | 19 | deView() { 20 | // do nothing 21 | } 22 | 23 | 24 | passToChildren() { 25 | this.outputs.forEach(output => { 26 | output.connections.forEach(conn => { 27 | if (this.string == null) { 28 | conn.string = null; 29 | } else { 30 | conn.string = this.string; 31 | } 32 | conn.runNode(); 33 | }) 34 | }) 35 | } 36 | 37 | 38 | run(inputThatTriggered) { 39 | this.timer.textContent = (Date.now() - this.runTimer) + 'ms'; 40 | this.bg.classList.remove('running'); 41 | this.passToChildren(); 42 | this.renderPreview(); 43 | } 44 | 45 | 46 | renderPreview() { 47 | if (this.string != null) { 48 | this.textPreview.textContent = this.string; 49 | } else { 50 | this.textPreview.textContent = ''; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /classes/nodes/OutputColor.js: -------------------------------------------------------------------------------- 1 | import Output from './Output.js'; 2 | 3 | 4 | export default class OutputColor extends Output { 5 | constructor(node, index, name) { 6 | super(node, index, name, 'Color'); 7 | } 8 | 9 | 10 | removeConnection(inputConnection, run) { 11 | inputConnection.color = null; 12 | super.removeConnection(inputConnection, run); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /classes/nodes/OutputImage.js: -------------------------------------------------------------------------------- 1 | import Output from './Output.js'; 2 | 3 | 4 | export default class OutputImage extends Output { 5 | constructor(node, index, name) { 6 | super(node, index, name, 'Image'); 7 | } 8 | 9 | 10 | removeConnection(inputConnection, run) { 11 | inputConnection.image = null; 12 | super.removeConnection(inputConnection, run); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /classes/nodes/OutputNumber.js: -------------------------------------------------------------------------------- 1 | import Output from './Output.js'; 2 | 3 | 4 | export default class OutputNumber extends Output { 5 | constructor(node, index, name) { 6 | super(node, index, name, 'Number'); 7 | } 8 | 9 | 10 | removeConnection(inputConnection, run) { 11 | inputConnection.number = null; 12 | super.removeConnection(inputConnection, run); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /classes/nodes/OutputString.js: -------------------------------------------------------------------------------- 1 | import Output from './Output.js'; 2 | 3 | 4 | export default class OutputString extends Output { 5 | constructor(node, index, name) { 6 | super(node, index, name, 'String'); 7 | } 8 | 9 | 10 | removeConnection(inputConnection, run) { 11 | inputConnection.string = null; 12 | super.removeConnection(inputConnection, run); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /classes/nodes/Properties.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Properties extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | 7 | props.node.propertiesComponentInstance = this; 8 | } 9 | 10 | 11 | colorChange() { 12 | const elm = document.getElementById('colorInput'); 13 | this.props.node.hexColor = elm.value; 14 | this.props.node.run(null); 15 | } 16 | 17 | 18 | nameChange() { 19 | const elm = document.getElementById('nameInput'); 20 | this.props.node.name = elm.value; 21 | this.props.node.renderName(); 22 | } 23 | 24 | 25 | runNode() { 26 | this.props.node.run(null); 27 | } 28 | 29 | 30 | renderRun() { 31 | return ( 32 | 33 | ) 34 | } 35 | 36 | 37 | renderName() { 38 | return ( 39 |
40 |
41 | Name
42 | {this.nameChange(event);}}/> 43 |
44 | ) 45 | } 46 | 47 | 48 | renderColor() { 49 | if (!this.state.hasColorInput) { 50 | return ( 51 |
52 | Color
53 | {this.colorChange(event);}} /> 54 |
55 | ) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/Blur.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import BlurProperties from './BlurProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | import InputNumber from '../InputNumber.js'; 6 | 7 | 8 | export default class Blur extends NodeImage { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'Blur', BlurProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputImage(this, 0, 'Input'), 14 | new InputNumber(this, 1, 'Radius', 'hasRadiusInput') 15 | ]; 16 | this.outputs = [ 17 | new OutputImage(this, 0, 'Output') 18 | ]; 19 | 20 | this.radius = typeof settings.radius !== 'undefined' ? settings.radius : 10; 21 | } 22 | 23 | 24 | run(inputThatTriggered) { 25 | if (this.inputs[0].image) { 26 | this.bg.classList.add('running'); 27 | this.runTimer = Date.now(); 28 | 29 | let radius = this.radius; 30 | 31 | if (this.inputs[1].number != null) { 32 | radius = this.inputs[1].number; 33 | } 34 | 35 | radius = Math.max(1, radius); 36 | 37 | // blur requires radius to be rounded? 38 | radius = Math.round(radius); 39 | 40 | if (this.isInsideALoop) { 41 | let image = this.inputs[0].image.clone(); 42 | image.blur(radius); 43 | this.image = image; 44 | super.run(inputThatTriggered); 45 | 46 | } else { 47 | Jimp.read(this.inputs[0].image).then(image => { 48 | image.blur(radius, (error, image) => { 49 | if (error) { 50 | console.log(error); 51 | } else { 52 | this.image = image; 53 | super.run(inputThatTriggered); 54 | } 55 | }); 56 | }) 57 | } 58 | } else { 59 | this.runTimer = Date.now(); 60 | this.image = null; 61 | super.run(inputThatTriggered); 62 | } 63 | } 64 | 65 | 66 | toJson() { 67 | let json = super.toJson(); 68 | 69 | json.settings.radius = this.radius; 70 | 71 | return json; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/BlurProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class BlurProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Blur
11 |
12 | Fast blur.
13 |
14 | 15 | 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/BrightnessContrastProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | export default class BrightnessContrastProperties extends Properties { 5 | 6 | render() { 7 | return ( 8 |
9 |
Brightness/Contrast
10 |
11 | 12 | 13 | 14 | {this.renderRun()} 15 |
16 |
17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/Dither.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import DitherProperties from './DitherProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | 6 | export default class Dither extends NodeImage { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Dither', DitherProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputImage(this, 0, 'Input') 12 | ]; 13 | this.outputs = [ 14 | new OutputImage(this, 0, 'Output') 15 | ]; 16 | } 17 | 18 | 19 | run(inputThatTriggered) { 20 | if (this.inputs[0].image) { 21 | this.bg.classList.add('running'); 22 | this.runTimer = Date.now(); 23 | 24 | if (this.isInsideALoop) { 25 | const image = this.inputs[0].image.clone(); 26 | image.dither565(); 27 | this.image = image; 28 | super.run(inputThatTriggered); 29 | } else { 30 | Jimp.read(this.inputs[0].image).then(image => { 31 | image.dither565((error, image) => { 32 | this.image = image; 33 | super.run(inputThatTriggered); 34 | }); 35 | }) 36 | } 37 | 38 | } else { 39 | this.runTimer = Date.now(); 40 | this.image = null; 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/DitherProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class DitherProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Dither Image
14 |
15 | Apply a dither effect to an image. 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/GaussianBlurProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class GaussianBlurProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Gaussian Blur
11 |
12 | Slow but high quality blur.
13 |
14 | 15 | 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/Greyscale.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import GreyscaleProperties from './GreyscaleProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | 6 | export default class Greyscale extends NodeImage { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Greyscale', GreyscaleProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputImage(this, 0, 'Input') 12 | ]; 13 | this.outputs = [ 14 | new OutputImage(this, 0, 'Output') 15 | ]; 16 | } 17 | 18 | 19 | run(inputThatTriggered) { 20 | if (this.inputs[0].image) { 21 | this.bg.classList.add('running'); 22 | this.runTimer = Date.now(); 23 | 24 | if (this.isInsideALoop) { 25 | const image = this.inputs[0].image.clone(); 26 | image.greyscale(); 27 | this.image = image; 28 | super.run(inputThatTriggered); 29 | } else { 30 | Jimp.read(this.inputs[0].image).then(image => { 31 | image.greyscale((error, image) => { 32 | this.image = image; 33 | super.run(inputThatTriggered); 34 | }); 35 | }) 36 | } 37 | 38 | } else { 39 | this.runTimer = Date.now(); 40 | this.image = null; 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/GreyscaleProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GreyscaleProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Convert to Greyscale
14 |
15 | {this.renderRun()} 16 |
17 |
18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/HSLProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | 5 | export default class HSLProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Hue Saturation Lightness
11 |
12 | 13 | 14 | 15 | 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/Invert.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import InvertProperties from './InvertProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | 6 | export default class Invert extends NodeImage { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Invert', InvertProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputImage(this, 0, 'Input') 12 | ]; 13 | this.outputs = [ 14 | new OutputImage(this, 0, 'Output') 15 | ]; 16 | } 17 | 18 | 19 | run(inputThatTriggered) { 20 | if (this.inputs[0].image) { 21 | this.bg.classList.add('running'); 22 | this.runTimer = Date.now(); 23 | 24 | if (this.isInsideALoop) { 25 | const image = this.inputs[0].image.clone(); 26 | image.invert(); 27 | this.image = image; 28 | super.run(inputThatTriggered); 29 | } else { 30 | Jimp.read(this.inputs[0].image).then(image => { 31 | image.invert((error, image) => { 32 | this.image = image; 33 | super.run(inputThatTriggered); 34 | }); 35 | }) 36 | } 37 | 38 | } else { 39 | this.runTimer = Date.now(); 40 | this.image = null; 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/InvertProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class InvertProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Invert Colors
14 |
15 | {this.renderRun()} 16 |
17 |
18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/LevelsProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | export default class LevelsProperties extends Properties { 5 | 6 | render() { 7 | return ( 8 |
9 |
Input Levels
10 |
11 | 12 | 13 | 14 | {this.renderRun()} 15 |
16 |
17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/Normalize.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import NormalizeProperties from './NormalizeProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | 6 | export default class Normalize extends NodeImage { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Normalize', NormalizeProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputImage(this, 0, 'Input') 12 | ]; 13 | this.outputs = [ 14 | new OutputImage(this, 0, 'Output') 15 | ]; 16 | } 17 | 18 | 19 | run(inputThatTriggered) { 20 | if (this.inputs[0].image) { 21 | this.bg.classList.add('running'); 22 | this.runTimer = Date.now(); 23 | 24 | if (this.isInsideALoop) { 25 | const image = this.inputs[0].image.clone(); 26 | image.normalize(); 27 | this.image = image; 28 | super.run(inputThatTriggered); 29 | } else { 30 | Jimp.read(this.inputs[0].image).then(image => { 31 | image.normalize((error, image) => { 32 | this.image = image; 33 | super.run(inputThatTriggered); 34 | }); 35 | }) 36 | } 37 | 38 | } else { 39 | this.runTimer = Date.now(); 40 | this.image = null; 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/NormalizeProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class NormalizeProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Normalize Image
14 |
15 | Normalizes an images color by computing a histogram. 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/adjustment/OutputLevelsProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | export default class OutputLevelsProperties extends Properties { 5 | 6 | render() { 7 | return ( 8 |
9 |
Output Levels
10 |
11 | 12 | 13 | {this.renderRun()} 14 |
15 |
16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /classes/nodes/alpha/Fade.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import FadeProperties from './FadeProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | import InputNumber from '../InputNumber.js'; 6 | 7 | 8 | export default class Fade extends NodeImage { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'Fade', FadeProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputImage(this, 0, 'Input'), 14 | new InputNumber(this, 1, 'Amount', 'hasAmountInput') 15 | ]; 16 | this.outputs = [ 17 | new OutputImage(this, 0, 'Output') 18 | ]; 19 | 20 | this.amount = typeof settings.amount !== 'undefined' ? settings.amount : 0.5; 21 | } 22 | 23 | 24 | run(inputThatTriggered) { 25 | if (this.inputs[0].image) { 26 | this.bg.classList.add('running'); 27 | this.runTimer = Date.now(); 28 | 29 | let amount = this.amount; 30 | 31 | if (this.inputs[1].number != null) { 32 | amount = this.inputs[1].number; 33 | } 34 | 35 | amount = Math.min(1, Math.max(0, amount)); 36 | 37 | if (this.isInsideALoop) { 38 | let image = this.inputs[0].image.clone(); 39 | image.fade(amount); 40 | this.image = image; 41 | super.run(inputThatTriggered); 42 | 43 | } else { 44 | Jimp.read(this.inputs[0].image).then(image => { 45 | image.fade(amount, (error, image) => { 46 | if (error) { 47 | console.log(error); 48 | } else { 49 | this.image = image; 50 | super.run(inputThatTriggered); 51 | } 52 | }); 53 | }) 54 | } 55 | } else { 56 | this.runTimer = Date.now(); 57 | this.image = null; 58 | super.run(inputThatTriggered); 59 | } 60 | } 61 | 62 | 63 | toJson() { 64 | let json = super.toJson(); 65 | 66 | json.settings.amount = this.amount; 67 | 68 | return json; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /classes/nodes/alpha/FadeProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | export default class FadeProperties extends Properties { 5 | 6 | render() { 7 | return ( 8 |
9 |
Fade
10 |
11 | 12 | {this.renderRun()} 13 |
14 |
15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /classes/nodes/alpha/MaskProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | export default class MaskProperties extends Properties { 5 | 6 | render() { 7 | return ( 8 |
9 |
Mask
10 |
11 | Position of image.

12 | 13 | 14 | {this.renderRun()} 15 |
16 |
17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /classes/nodes/alpha/Opacity.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import OpacityProperties from './OpacityProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | import InputNumber from '../InputNumber.js'; 6 | 7 | 8 | export default class Opacity extends NodeImage { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'Opacity', OpacityProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputImage(this, 0, 'Input'), 14 | new InputNumber(this, 1, 'Amount', 'hasAmountInput') 15 | ]; 16 | this.outputs = [ 17 | new OutputImage(this, 0, 'Output') 18 | ]; 19 | 20 | this.amount = typeof settings.amount !== 'undefined' ? settings.amount : 0.5; 21 | } 22 | 23 | 24 | run(inputThatTriggered) { 25 | if (this.inputs[0].image) { 26 | this.bg.classList.add('running'); 27 | this.runTimer = Date.now(); 28 | 29 | let amount = this.amount; 30 | 31 | if (this.inputs[1].number != null) { 32 | amount = this.inputs[1].number; 33 | } 34 | 35 | amount = Math.min(1, Math.max(0, amount)); 36 | 37 | if (this.isInsideALoop) { 38 | let image = this.inputs[0].image.clone(); 39 | image.opacity(amount); 40 | this.image = image; 41 | super.run(inputThatTriggered); 42 | 43 | } else { 44 | Jimp.read(this.inputs[0].image).then(image => { 45 | image.opacity(amount, (error, image) => { 46 | if (error) { 47 | console.log(error); 48 | } else { 49 | this.image = image; 50 | super.run(inputThatTriggered); 51 | } 52 | }); 53 | }) 54 | } 55 | } else { 56 | this.runTimer = Date.now(); 57 | this.image = null; 58 | super.run(inputThatTriggered); 59 | } 60 | } 61 | 62 | 63 | toJson() { 64 | let json = super.toJson(); 65 | 66 | json.settings.amount = this.amount; 67 | 68 | return json; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /classes/nodes/alpha/OpacityProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | export default class OpacityProperties extends Properties { 5 | 6 | render() { 7 | return ( 8 |
9 |
Opacity
10 |
11 | Multiply the alpha channel by an amount.
12 |
13 | 14 | {this.renderRun()} 15 |
16 |
17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /classes/nodes/alpha/Opaque.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import OpaqueProperties from './OpaqueProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | 6 | export default class Opaque extends NodeImage { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Opaque', OpaqueProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputImage(this, 0, 'Input') 12 | ]; 13 | this.outputs = [ 14 | new OutputImage(this, 0, 'Output') 15 | ]; 16 | } 17 | 18 | 19 | run(inputThatTriggered) { 20 | if (this.inputs[0].image) { 21 | this.bg.classList.add('running'); 22 | this.runTimer = Date.now(); 23 | 24 | if (this.isInsideALoop) { 25 | const image = this.inputs[0].image.clone(); 26 | image.opaque(); 27 | this.image = image; 28 | super.run(inputThatTriggered); 29 | } else { 30 | Jimp.read(this.inputs[0].image).then(image => { 31 | image.opaque((error, image) => { 32 | if (error) { 33 | console.log(error); 34 | } else { 35 | this.image = image; 36 | super.run(inputThatTriggered); 37 | } 38 | }); 39 | }) 40 | } 41 | 42 | } else { 43 | this.runTimer = Date.now(); 44 | this.image = null; 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/alpha/OpaqueProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class OpaqueProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Opaque
14 |
15 | Set the alpha channel of every pixel to be opaque. 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/channel/ChannelMergeProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class ChannelMergeProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | 11 | render() { 12 | return ( 13 |
14 |
Channel Merge
15 |
16 | 17 | {this.renderRun()} 18 |
19 |
20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /classes/nodes/channel/ChannelSplitProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class ChannelSplitProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | 11 | render() { 12 | return ( 13 |
14 |
Channel Split
15 |
16 | 17 | {this.renderRun()} 18 |
19 |
20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /classes/nodes/color/AnalogousColorsProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GetAlphaProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
Analogous Colors
21 | 22 |
23 | {this.renderName()} 24 | {this.renderRun()} 25 |
26 |
27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /classes/nodes/color/ColorAdjustProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | 5 | export default class ColorAdjustProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Color Adjust
11 | 12 |
13 | 14 | 15 | 16 | 17 | {this.renderName()} 18 | {this.renderRun()} 19 |
20 |
21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /classes/nodes/color/ColorInput.js: -------------------------------------------------------------------------------- 1 | import NodeColor from '../NodeColor.js'; 2 | import ColorInputProperties from './ColorInputProperties.jsx'; 3 | import OutputColor from '../OutputColor.js'; 4 | const tinycolor = require("tinycolor2"); 5 | 6 | 7 | export default class ColorInput extends NodeColor { 8 | constructor(className, graph, x, y, settings) { 9 | super(className, graph, x, y, 'Color Input', ColorInputProperties, settings); 10 | 11 | this.inputs = []; 12 | this.outputs = [ 13 | new OutputColor(this, 0, 'Output') 14 | ]; 15 | 16 | this.string = typeof settings.hue !== 'undefined' ? settings.hue : '#fff'; 17 | } 18 | 19 | 20 | toJson() { 21 | let json = super.toJson(); 22 | 23 | json.settings.string = this.string; 24 | 25 | return json; 26 | } 27 | 28 | 29 | run(inputThatTriggered) { 30 | this.bg.classList.add('running'); 31 | this.runTimer = Date.now(); 32 | 33 | // hue = Math.min(255, Math.max(0, hue)); 34 | // saturation = Math.min(255, Math.max(0, saturation)); 35 | // value = Math.min(255, Math.max(0, value)); 36 | // alpha = Math.min(255, Math.max(0, alpha)); 37 | 38 | this.color = tinycolor(this.string); 39 | 40 | super.run(inputThatTriggered); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /classes/nodes/color/ColorInputProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class ColorInputProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
HSV Input
21 | 22 |
23 | Color
24 | {this.stringChange(event)}} /> 25 |

26 | Examples of Possible Values
27 | 34 | 35 | See Tinycolor for all options. 36 | {this.renderName()} 37 | {this.renderRun()} 38 |
39 |
40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /classes/nodes/color/ComplimentColor.js: -------------------------------------------------------------------------------- 1 | import NodeColor from '../NodeColor.js'; 2 | import ComplimentColorProperties from './ComplimentColorProperties.jsx'; 3 | import OutputColor from '../OutputColor.js'; 4 | const tinycolor = require("tinycolor2"); 5 | import InputColor from '../InputColor.js'; 6 | 7 | 8 | export default class ComplimentColor extends NodeColor { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'Compliment Color', ComplimentColorProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputColor(this, 0, 'Input'), 14 | ]; 15 | this.outputs = [ 16 | new OutputColor(this, 0, 'Compliment 1'), 17 | ]; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | return json; 25 | } 26 | 27 | 28 | run(inputThatTriggered) { 29 | this.bg.classList.add('running'); 30 | this.runTimer = Date.now(); 31 | 32 | if (this.inputs[0].color) { 33 | this.color = this.inputs[0].color.clone().complement(); 34 | 35 | } else { 36 | this.color = tinycolor('#000'); 37 | } 38 | 39 | super.run(inputThatTriggered); 40 | } 41 | 42 | 43 | renderPreview() { 44 | new Jimp(1, 1, this.color.toHex8String(), (error, image) => { 45 | if (error) { 46 | console.log(error); 47 | } else { 48 | image.getBufferAsync(Jimp.MIME_JPEG).then(i => { 49 | this.preview.setAttributeNS(null, 'href', 'data:'+Jimp.MIME_JPEG+';base64,'+i.toString('base64')); 50 | }); 51 | } 52 | }) 53 | } 54 | 55 | 56 | passToChildren() { 57 | this.outputs[0].connections.forEach(conn => { 58 | conn.color = this.color; 59 | conn.runNode(); 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /classes/nodes/color/ComplimentColorProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class ComplimentColorProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
Compliment Color
21 | 22 |
23 | {this.renderName()} 24 | {this.renderRun()} 25 |
26 |
27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /classes/nodes/color/GetAlpha.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import GetAlphaProperties from './GetAlphaProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | const tinycolor = require("tinycolor2"); 5 | import InputColor from '../InputColor.js'; 6 | 7 | 8 | export default class GetAlpha extends NodeNumber { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'Get Alpha', GetAlphaProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputColor(this, 0, 'Input') 14 | ]; 15 | this.outputs = [ 16 | new OutputNumber(this, 0, 'Alpha') 17 | ]; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | return json; 25 | } 26 | 27 | 28 | run(inputThatTriggered) { 29 | this.bg.classList.add('running'); 30 | this.runTimer = Date.now(); 31 | 32 | if (this.inputs[0].color) { 33 | 34 | this.number = this.inputs[0].color.clone().getAlpha(); 35 | 36 | } else { 37 | 38 | this.color = null 39 | } 40 | 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /classes/nodes/color/GetAlphaProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GetAlphaProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
Alpha Value From Color
21 | 22 |
23 | 0 - 1 24 | {this.renderName()} 25 | {this.renderRun()} 26 |
27 |
28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /classes/nodes/color/GetBrightness.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import GetBrightnessProperties from './GetBrightnessProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | const tinycolor = require("tinycolor2"); 5 | import InputColor from '../InputColor.js'; 6 | 7 | 8 | export default class GetBrightness extends NodeNumber { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'Get Brightness', GetBrightnessProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputColor(this, 0, 'Input') 14 | ]; 15 | this.outputs = [ 16 | new OutputNumber(this, 0, 'Brightness') 17 | ]; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | return json; 25 | } 26 | 27 | 28 | run(inputThatTriggered) { 29 | this.bg.classList.add('running'); 30 | this.runTimer = Date.now(); 31 | 32 | if (this.inputs[0].color) { 33 | 34 | this.number = this.inputs[0].color.clone().getBrightness(); 35 | 36 | } else { 37 | 38 | this.color = null 39 | } 40 | 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /classes/nodes/color/GetBrightnessProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GetBrightnessProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
Brightness From Color
21 | 22 |
23 | 0 - 255 24 | {this.renderName()} 25 | {this.renderRun()} 26 |
27 |
28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /classes/nodes/color/GetLuminance.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import GetLuminanceProperties from './GetLuminanceProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | const tinycolor = require("tinycolor2"); 5 | import InputColor from '../InputColor.js'; 6 | 7 | 8 | export default class GetLuminance extends NodeNumber { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'Get Luminance', GetLuminanceProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputColor(this, 0, 'Input') 14 | ]; 15 | this.outputs = [ 16 | new OutputNumber(this, 0, 'Luminance') 17 | ]; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | return json; 25 | } 26 | 27 | 28 | run(inputThatTriggered) { 29 | this.bg.classList.add('running'); 30 | this.runTimer = Date.now(); 31 | 32 | if (this.inputs[0].color) { 33 | 34 | this.number = this.inputs[0].color.clone().getLuminance(); 35 | 36 | } else { 37 | 38 | this.color = null 39 | } 40 | 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /classes/nodes/color/GetLuminanceProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GetLuminanceProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
Luminance From Color
21 | 22 |
23 | 0 - 1 24 | {this.renderName()} 25 | {this.renderRun()} 26 |
27 |
28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /classes/nodes/color/HSLInputProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | 5 | export default class HSLInputProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
HSL Input
11 | 12 |
13 | 14 | 15 | 16 | 17 | {this.renderName()} 18 | {this.renderRun()} 19 |
20 |
21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /classes/nodes/color/HSVInputProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | 5 | export default class HSVInputProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
HSV Input
11 | 12 |
13 | 14 | 15 | 16 | 17 | {this.renderName()} 18 | {this.renderRun()} 19 |
20 |
21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /classes/nodes/color/MixColorsProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | export default class MixColorsProperties extends Properties { 5 | 6 | constructor(props) { 7 | super(props); 8 | 9 | this.state = { 10 | hasColorAInput: props.node.inputs[0].parent ? true : false, 11 | hasColorBInput: props.node.inputs[1].parent ? true : false 12 | } 13 | } 14 | 15 | 16 | colorAChange() { 17 | const elm = document.getElementById('colorAInput'); 18 | this.props.node.colorA = elm.value; 19 | this.props.node.run(null); 20 | } 21 | 22 | colorBChange() { 23 | const elm = document.getElementById('colorBInput'); 24 | this.props.node.colorB = elm.value; 25 | this.props.node.run(null); 26 | } 27 | 28 | 29 | renderColorA() { 30 | if (!this.state.hasColorAInput) { 31 | return ( 32 |
33 | Color A   34 | {this.colorAChange(event);}} /> 35 |
36 | ) 37 | } 38 | } 39 | 40 | renderColorB() { 41 | if (!this.state.hasColorBInput) { 42 | return ( 43 |
44 | Color B   45 | {this.colorBChange(event);}} /> 46 |
47 | ) 48 | } 49 | } 50 | 51 | 52 | render() { 53 | return ( 54 |
55 |
Mix Colors
56 | 57 |
58 | {this.renderColorA()} 59 | {this.renderColorB()} 60 |
61 | 62 | {this.renderName()} 63 | {this.renderRun()} 64 |
65 |
66 | ) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /classes/nodes/color/MonochromaticColorsProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GetAlphaProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
Monochromatic Colors
21 | 22 |
23 | {this.renderName()} 24 | {this.renderRun()} 25 |
26 |
27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /classes/nodes/color/RGBProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputSlider from '../../../components/PropertiesInputSlider.jsx'; 3 | 4 | export default class RGBProperties extends Properties { 5 | 6 | render() { 7 | return ( 8 |
9 |
RGB Input
10 | 11 |
12 | 13 | 14 | 15 | 16 | {this.renderName()} 17 | {this.renderRun()} 18 |
19 |
20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /classes/nodes/color/SampleProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | export default class SampleProperties extends Properties { 5 | 6 | render() { 7 | return ( 8 |
9 |
Sample
10 |
11 | Get color of image at pixel x,y.
12 |
13 | 14 | 15 | {this.renderName()} 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/color/SplitComplimentColorsProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GetAlphaProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
Split Compliment Colors
21 | 22 |
23 | {this.renderName()} 24 | {this.renderRun()} 25 |
26 |
27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /classes/nodes/color/TetradColorsProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GetAlphaProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
Tetrad Colors
21 | 22 |
23 | {this.renderName()} 24 | {this.renderRun()} 25 |
26 |
27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /classes/nodes/color/TriadColorsProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GetAlphaProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
Triad Colors
21 | 22 |
23 | {this.renderName()} 24 | {this.renderRun()} 25 |
26 |
27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /classes/nodes/composing/BlitProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class BlitProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Blit
11 |
12 | Copy an image or part of an image on top of another image. 13 |

14 | 15 | 16 | 17 | 18 | 19 | 20 | {this.renderRun()} 21 |
22 |
23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /classes/nodes/control/Counter.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import CounterProperties from './CounterProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import InputColor from '../InputColor.js'; 5 | import InputImage from '../InputImage.js'; 6 | import InputString from '../InputString.js'; 7 | import OutputNumber from '../OutputNumber.js'; 8 | 9 | export default class Counter extends NodeNumber { 10 | constructor(className, graph, x, y, settings) { 11 | super(className, graph, x, y, 'Counter', CounterProperties, settings); 12 | 13 | this.inputs = [ 14 | new InputNumber(this, 0, 'Increment', 'hasNumber'), 15 | new InputColor(this, 1, 'Increment', 'hasColor'), 16 | new InputImage(this, 2, 'Increment', 'hasImage'), 17 | new InputString(this, 3, 'Increment', 'hasString'), 18 | new InputNumber(this, 4, 'Reset', 'hasNumberReset'), 19 | new InputColor(this, 5, 'Reset', 'hasColorReset'), 20 | new InputImage(this, 6, 'Reset', 'hasImageReset'), 21 | new InputString(this, 7, 'Reset', 'hasStringReset'), 22 | ]; 23 | this.outputs = [ 24 | new OutputNumber(this, 0, 'Counter') 25 | ]; 26 | 27 | this.number = -1; 28 | } 29 | 30 | 31 | toJson() { 32 | let json = super.toJson(); 33 | 34 | return json; 35 | } 36 | 37 | 38 | run(inputThatTriggered) { 39 | if (inputThatTriggered && inputThatTriggered.index >= 4) { 40 | this.number = 0 41 | } else { 42 | this.number++; 43 | } 44 | this.runTimer = Date.now(); 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /classes/nodes/control/CounterProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class CounterProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.resetCounter = this.resetCounter.bind(this); 9 | } 10 | 11 | 12 | resetCounter() { 13 | this.props.node.number = -1; 14 | this.props.node.run(null); 15 | } 16 | 17 | 18 | render() { 19 | return ( 20 |
21 |
Counter a Number
22 |
23 | Counts how many times this node has been run. Is set to 0 when page is loaded. 24 |

25 | 26 | {this.renderName()} 27 | {this.renderRun()} 28 |
29 |
30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /classes/nodes/control/ElseColor.js: -------------------------------------------------------------------------------- 1 | import NodeColor from '../NodeColor.js'; 2 | import ElseColorProperties from './ElseColorProperties.jsx'; 3 | import OutputColor from '../OutputColor.js'; 4 | import InputColor from '../InputColor.js'; 5 | const tinycolor = require("tinycolor2"); 6 | import InputNumber from '../InputNumber.js'; 7 | 8 | 9 | export default class ElseColor extends NodeColor { 10 | constructor(className, graph, x, y, settings) { 11 | super(className, graph, x, y, 'Color If Else', ElseColorProperties, settings); 12 | 13 | this.inputs = [ 14 | new InputColor(this, 0, 'Input A'), 15 | new InputColor(this, 1, 'Input B'), 16 | new InputNumber(this, 2, 'Test', 'hasAInput') 17 | ]; 18 | this.outputs = [ 19 | new OutputColor(this, 0, 'Output') 20 | ]; 21 | 22 | this.a = typeof settings.a !== 'undefined' ? settings.a : 1; 23 | } 24 | 25 | 26 | toJson() { 27 | let json = super.toJson(); 28 | 29 | json.settings.a = this.a; 30 | 31 | return json; 32 | } 33 | 34 | 35 | run(inputThatTriggered) { 36 | let a = this.a; 37 | 38 | if (this.inputs[2].number != null) { 39 | a = this.inputs[2].number; 40 | } 41 | 42 | if (this.inputs[0].color && this.inputs[1].color) { 43 | this.bg.classList.add('running'); 44 | this.runTimer = Date.now(); 45 | 46 | this.color = tinycolor('#000'); 47 | if (a) { 48 | if (this.inputs[0].color) { 49 | this.color = this.inputs[0].color; 50 | } 51 | } else { 52 | if (this.inputs[1].color) { 53 | this.color = this.inputs[1].color; 54 | } 55 | } 56 | 57 | super.run(inputThatTriggered); 58 | 59 | } else { 60 | this.runTimer = Date.now(); 61 | this.color = tinycolor('#000'); 62 | super.run(inputThatTriggered); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /classes/nodes/control/ElseColorProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class ElseProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasAInput: props.node.inputs[2].parent ? true : false 10 | } 11 | 12 | this.aChange = this.aChange.bind(this); 13 | } 14 | 15 | 16 | aChange(event) { 17 | const elm = document.getElementById('aInput'); 18 | this.props.node.a = Number(elm.value); 19 | this.props.node.run(null); 20 | } 21 | 22 | 23 | renderA() { 24 | if (!this.state.hasAInput) { 25 | return ( 26 |
27 | Test Number
28 | {this.aChange(event);}} /> 29 |
30 | ) 31 | } 32 | } 33 | 34 | 35 | render() { 36 | return ( 37 |
38 |
Color If Else
39 |
40 | {this.renderA()} 41 | 42 |

43 | Outputs color A if test number is true. Outputs color B if test number is false. Number is considered false if it is 0 or -0. 44 | {this.renderRun()} 45 |
46 |
47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /classes/nodes/control/ElseNumber.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import ElseNumberProperties from './ElseNumberProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | import InputNumber from '../InputNumber.js'; 5 | 6 | 7 | export default class ElseNumber extends NodeNumber { 8 | constructor(className, graph, x, y, settings) { 9 | super(className, graph, x, y, 'Number If Else', ElseNumberProperties, settings); 10 | 11 | this.inputs = [ 12 | new InputNumber(this, 0, 'Input A'), 13 | new InputNumber(this, 1, 'Input B'), 14 | new InputNumber(this, 2, 'Test', 'hasAInput') 15 | ]; 16 | this.outputs = [ 17 | new OutputNumber(this, 0, 'Output') 18 | ]; 19 | 20 | this.a = typeof settings.a !== 'undefined' ? settings.a : 1; 21 | } 22 | 23 | 24 | toJson() { 25 | let json = super.toJson(); 26 | 27 | json.settings.a = this.a; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | let a = this.a; 35 | 36 | if (this.inputs[2].number != null) { 37 | a = this.inputs[2].number; 38 | } 39 | 40 | if (this.inputs[0].number != null && this.inputs[1].number != null) { 41 | this.bg.classList.add('running'); 42 | this.runTimer = Date.now(); 43 | 44 | this.number = null; 45 | if (a) { 46 | if (this.inputs[0].number != null) { 47 | this.number = this.inputs[0].number; 48 | } 49 | } else { 50 | if (this.inputs[1].number != null) { 51 | this.number = this.inputs[1].number; 52 | } 53 | } 54 | 55 | super.run(inputThatTriggered); 56 | 57 | } else { 58 | this.runTimer = Date.now(); 59 | this.number = null; 60 | super.run(inputThatTriggered); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /classes/nodes/control/ElseNumberProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class ElseNumberProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasAInput: props.node.inputs[2].parent ? true : false 10 | } 11 | 12 | this.aChange = this.aChange.bind(this); 13 | } 14 | 15 | 16 | aChange(event) { 17 | const elm = document.getElementById('aInput'); 18 | this.props.node.a = Number(elm.value); 19 | this.props.node.run(null); 20 | } 21 | 22 | 23 | renderA() { 24 | if (!this.state.hasAInput) { 25 | return ( 26 |
27 | Test Number
28 | {this.aChange(event);}} /> 29 |
30 | ) 31 | } 32 | } 33 | 34 | 35 | render() { 36 | return ( 37 |
38 |
Number If Else
39 |
40 | {this.renderA()} 41 | 42 |

43 | Outputs number A if test number is true. Outputs number B if test number is false. Number is considered false if it is 0 or -0. 44 | {this.renderRun()} 45 |
46 |
47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /classes/nodes/control/ElseProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class ElseProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasAInput: props.node.inputs[2].parent ? true : false 10 | } 11 | 12 | this.aChange = this.aChange.bind(this); 13 | } 14 | 15 | 16 | aChange(event) { 17 | const elm = document.getElementById('aInput'); 18 | this.props.node.a = Number(elm.value); 19 | this.props.node.run(null); 20 | } 21 | 22 | 23 | renderA() { 24 | if (!this.state.hasAInput) { 25 | return ( 26 |
27 | Test Number
28 | {this.aChange(event);}} /> 29 |
30 | ) 31 | } 32 | } 33 | 34 | 35 | render() { 36 | return ( 37 |
38 |
Image If Else
39 |
40 | {this.renderA()} 41 | 42 |

43 | Outputs image A if test number is true. Outputs image B if test number is false. Number is considered false if it is 0 or -0. 44 | {this.renderRun()} 45 |
46 |
47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /classes/nodes/control/ElseString.js: -------------------------------------------------------------------------------- 1 | import NodeString from '../NodeString.js'; 2 | import ElseStringProperties from './ElseStringProperties.jsx'; 3 | import OutputString from '../OutputString.js'; 4 | import InputNumber from '../InputNumber.js'; 5 | import InputString from '../InputString.js'; 6 | 7 | 8 | export default class ElseString extends NodeString { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'String If Else', ElseStringProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputString(this, 0, 'Input A'), 14 | new InputString(this, 1, 'Input B'), 15 | new InputNumber(this, 2, 'Test', 'hasAInput') 16 | ]; 17 | this.outputs = [ 18 | new OutputString(this, 0, 'Output') 19 | ]; 20 | 21 | this.a = typeof settings.a !== 'undefined' ? settings.a : 1; 22 | } 23 | 24 | 25 | toJson() { 26 | let json = super.toJson(); 27 | 28 | json.settings.a = this.a; 29 | 30 | return json; 31 | } 32 | 33 | 34 | run(inputThatTriggered) { 35 | let a = this.a; 36 | 37 | if (this.inputs[2].number != null) { 38 | a = this.inputs[2].number; 39 | } 40 | 41 | if (this.inputs[0].string != null && this.inputs[1].string != null) { 42 | this.bg.classList.add('running'); 43 | this.runTimer = Date.now(); 44 | 45 | this.string = null; 46 | if (a) { 47 | if (this.inputs[0].string != null) { 48 | this.string = this.inputs[0].string; 49 | } 50 | } else { 51 | if (this.inputs[1].string != null) { 52 | this.string = this.inputs[1].string; 53 | } 54 | } 55 | 56 | super.run(inputThatTriggered); 57 | 58 | } else { 59 | this.runTimer = Date.now(); 60 | this.string = null; 61 | super.run(inputThatTriggered); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /classes/nodes/control/ElseStringProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class ElseStringProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasAInput: props.node.inputs[2].parent ? true : false 10 | } 11 | 12 | this.aChange = this.aChange.bind(this); 13 | } 14 | 15 | 16 | aChange(event) { 17 | const elm = document.getElementById('aInput'); 18 | this.props.node.a = Number(elm.value); 19 | this.props.node.run(null); 20 | } 21 | 22 | 23 | renderA() { 24 | if (!this.state.hasAInput) { 25 | return ( 26 |
27 | Test Number
28 | {this.aChange(event);}} /> 29 |
30 | ) 31 | } 32 | } 33 | 34 | 35 | render() { 36 | return ( 37 |
38 |
String If Else
39 |
40 | {this.renderA()} 41 | 42 |

43 | Outputs string A if test number is true. Outputs string B if test number is false. Number is considered false if it is 0 or -0. 44 | {this.renderRun()} 45 |
46 |
47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /classes/nodes/control/IfColor.js: -------------------------------------------------------------------------------- 1 | import NodeColor from '../NodeColor.js'; 2 | import IfColorProperties from './IfColorProperties.jsx'; 3 | import OutputColor from '../OutputColor.js'; 4 | import InputColor from '../InputColor.js'; 5 | import InputNumber from '../InputNumber.js'; 6 | 7 | 8 | export default class IfColor extends NodeColor { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'Color If', IfColorProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputColor(this, 0, 'Input'), 14 | new InputNumber(this, 1, 'Test', 'hasAInput') 15 | ]; 16 | this.outputs = [ 17 | new OutputColor(this, 0, 'Output') 18 | ]; 19 | 20 | this.a = typeof settings.a !== 'undefined' ? settings.a : 1; 21 | } 22 | 23 | 24 | toJson() { 25 | let json = super.toJson(); 26 | 27 | json.settings.a = this.a; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | let a = this.a; 35 | 36 | if (this.inputs[1].number != null) { 37 | a = this.inputs[1].number; 38 | } 39 | 40 | if (a) { 41 | if (this.inputs[0].color) { 42 | this.bg.classList.add('running'); 43 | this.runTimer = Date.now(); 44 | this.color = this.inputs[0].color; 45 | super.run(inputThatTriggered); 46 | } else { 47 | this.runTimer = Date.now(); 48 | this.color = tinycolor('#000'); 49 | super.run(inputThatTriggered); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /classes/nodes/control/IfColorProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | export default class IfProperties extends Properties { 5 | 6 | 7 | render() { 8 | return ( 9 |
10 |
Color If
11 |
12 | 13 | 14 |

15 | Outputs color if test number is true. Does nothing if number is false. Number is considered false if it is 0 or -0. 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/control/IfImage.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import IfImageProperties from './IfImageProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | import InputNumber from '../InputNumber.js'; 6 | 7 | 8 | export default class IfImage extends NodeImage { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'Image If', IfImageProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputImage(this, 0, 'Input'), 14 | new InputNumber(this, 1, 'Test', 'hasAInput') 15 | ]; 16 | this.outputs = [ 17 | new OutputImage(this, 0, 'Output') 18 | ]; 19 | 20 | this.a = typeof settings.a !== 'undefined' ? settings.a : 1; 21 | } 22 | 23 | 24 | toJson() { 25 | let json = super.toJson(); 26 | 27 | json.settings.a = this.a; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | let a = this.a; 35 | 36 | if (this.inputs[1].number != null) { 37 | a = this.inputs[1].number; 38 | } 39 | 40 | if (a) { 41 | if (this.inputs[0].image) { 42 | this.bg.classList.add('running'); 43 | this.runTimer = Date.now(); 44 | 45 | if (this.isInsideALoop) { 46 | let image = this.inputs[0].image.clone(); 47 | this.image = image; 48 | super.run(inputThatTriggered); 49 | 50 | } else { 51 | Jimp.read(this.inputs[0].image).then(image => { 52 | this.image = image; 53 | super.run(inputThatTriggered); 54 | }) 55 | } 56 | } else { 57 | this.runTimer = Date.now(); 58 | this.image = null; 59 | super.run(inputThatTriggered); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /classes/nodes/control/IfImageProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | export default class IfProperties extends Properties { 5 | 6 | 7 | render() { 8 | return ( 9 |
10 |
Image If
11 |
12 | 13 | 14 |

15 | Outputs image if test number is true. Does nothing if number is false. Number is considered false if it is 0 or -0. 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/control/IfNumber.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import IfNumberProperties from './IfNumberProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | import InputNumber from '../InputNumber.js'; 5 | 6 | 7 | export default class IfNumber extends NodeNumber { 8 | constructor(className, graph, x, y, settings) { 9 | super(className, graph, x, y, 'Number If', IfNumberProperties, settings); 10 | 11 | this.inputs = [ 12 | new InputNumber(this, 0, 'Input'), 13 | new InputNumber(this, 1, 'Test', 'hasAInput') 14 | ]; 15 | this.outputs = [ 16 | new OutputNumber(this, 0, 'Output') 17 | ]; 18 | 19 | this.a = typeof settings.a !== 'undefined' ? settings.a : 1; 20 | } 21 | 22 | 23 | toJson() { 24 | let json = super.toJson(); 25 | 26 | json.settings.a = this.a; 27 | 28 | return json; 29 | } 30 | 31 | 32 | run(inputThatTriggered) { 33 | let a = this.a; 34 | 35 | if (this.inputs[1].number != null) { 36 | a = this.inputs[1].number; 37 | } 38 | 39 | if (a) { 40 | if (this.inputs[0].number != null) { 41 | this.bg.classList.add('running'); 42 | this.runTimer = Date.now(); 43 | this.number = this.inputs[0].number; 44 | super.run(inputThatTriggered); 45 | } else { 46 | this.runTimer = Date.now(); 47 | this.number = null; 48 | super.run(inputThatTriggered); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /classes/nodes/control/IfNumberProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | export default class IfProperties extends Properties { 5 | 6 | 7 | render() { 8 | return ( 9 |
10 |
Number If
11 |
12 | 13 | 14 |

15 | Outputs number if test number is true. Does nothing if number is false. Number is considered false if it is 0 or -0. 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/control/IfString.js: -------------------------------------------------------------------------------- 1 | import NodeString from '../NodeString.js'; 2 | import IfStringProperties from './IfStringProperties.jsx'; 3 | import OutputString from '../OutputString.js'; 4 | import InputString from '../InputString.js'; 5 | import InputNumber from '../InputNumber.js'; 6 | 7 | 8 | export default class IfString extends NodeString { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'String If', IfStringProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputString(this, 0, 'Input'), 14 | new InputNumber(this, 1, 'Test', 'hasAInput') 15 | ]; 16 | this.outputs = [ 17 | new OutputString(this, 0, 'Output') 18 | ]; 19 | 20 | this.a = typeof settings.a !== 'undefined' ? settings.a : 1; 21 | } 22 | 23 | 24 | toJson() { 25 | let json = super.toJson(); 26 | 27 | json.settings.a = this.a; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | let a = this.a; 35 | 36 | if (this.inputs[1].number != null) { 37 | a = this.inputs[1].number; 38 | } 39 | 40 | if (a) { 41 | if (this.inputs[0].string != null) { 42 | this.bg.classList.add('running'); 43 | this.runTimer = Date.now(); 44 | this.string = this.inputs[0].string; 45 | super.run(inputThatTriggered); 46 | } else { 47 | this.runTimer = Date.now(); 48 | this.string = null; 49 | super.run(inputThatTriggered); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /classes/nodes/control/IfStringProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | export default class IfProperties extends Properties { 5 | 6 | 7 | render() { 8 | return ( 9 |
10 |
String If
11 |
12 | 13 | 14 |

15 | Outputs string if test number is true. Does nothing if number is false. Number is considered false if it is 0 or -0. 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/control/LoopInputImageLoopOutput.js: -------------------------------------------------------------------------------- 1 | import InputImage from '../InputImage.js'; 2 | 3 | 4 | export default class LoopInputImageLoopOutput extends InputImage { 5 | constructor(node, index, name) { 6 | super(node, index, name); 7 | } 8 | 9 | 10 | connectionMade() { 11 | if (this.node.bg.classList.contains('selected')) { 12 | this.node.propertiesComponentInstance.setState({hasLoopOutput:true}); 13 | } 14 | } 15 | 16 | 17 | connectionRemoved() { 18 | if (this.node.bg.classList.contains('selected')) { 19 | this.node.propertiesComponentInstance.setState({hasLoopOutput:false}); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /classes/nodes/control/LoopInputNumberNumLoops.js: -------------------------------------------------------------------------------- 1 | import InputNumber from '../InputNumber.js'; 2 | 3 | 4 | export default class LoopInputNumberNumLoops extends InputNumber { 5 | constructor(node, index, name) { 6 | super(node, index, name); 7 | } 8 | 9 | 10 | connectionMade() { 11 | if (this.node.bg.classList.contains('selected')) { 12 | this.node.propertiesComponentInstance.setState({hasNumLoopsInput:true}); 13 | } 14 | } 15 | 16 | 17 | connectionRemoved() { 18 | if (this.node.bg.classList.contains('selected')) { 19 | this.node.propertiesComponentInstance.setState({hasNumLoopsInput:false}); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /classes/nodes/control/LoopProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class LoopProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasLoopOutput: props.node.inputs[2].parent ? true : false, 10 | hasLoopInput: props.node.outputs[1].connections.length ? true : false, 11 | hasNumLoopsInput: props.node.inputs[1].parent ? true : false, 12 | } 13 | 14 | this.numLoopsChange = this.numLoopsChange.bind(this); 15 | } 16 | 17 | 18 | numLoopsChange(event) { 19 | const elm = document.getElementById('numLoopsInput'); 20 | this.props.node.numLoops = Number(elm.value); 21 | this.props.node.run(null); 22 | } 23 | 24 | 25 | renderNumLoops() { 26 | if (!this.state.hasNumLoopsInput) { 27 | return ( 28 |
29 | Number of Loops
30 | {this.numLoopsChange(event);}} /> 31 |
32 | ) 33 | } 34 | } 35 | 36 | 37 | render() { 38 | return ( 39 |
40 |
Loop
41 |
42 | This node creates a loop that runs n times. 43 |

44 | The loop starts with the "loop start" output and ends with the "loop end" input. Loop start and loop end should connect to each other with nodes in between. 45 |

46 | After the loop is finished the output image will be set. 47 |

48 | {this.renderNumLoops()} 49 |

50 | Increment output starts with 0 and adds 1 each loop. 51 | {this.renderRun()} 52 |
53 |
54 | ) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /classes/nodes/filter/AmbientOcclusionProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | export default class AmbientOcclusionProperties extends Properties { 5 | 6 | constructor(props) { 7 | super(props); 8 | } 9 | 10 | 11 | render() { 12 | return ( 13 |
14 |
Ambient Occlusion
15 |
16 | 17 | {this.renderRun()} 18 |

19 |
20 | Uses input as a height map. Black is lowest and white is highest. Calculates how much each pixel is exposed to ambient lighting.
21 |
22 | Increasing radius greatly decreases performance. 23 |
24 |
25 |
26 | ) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /classes/nodes/filter/NormalMapProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class NormalMapProperties extends Properties { 6 | 7 | constructor(props) { 8 | super(props); 9 | } 10 | 11 | 12 | render() { 13 | return ( 14 |
15 |
Height to Normal Map
16 |
17 | 18 | {this.renderRun()} 19 |
20 |
21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /classes/nodes/filter/PixelateProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class BlitProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Pixelate
11 |
12 | Apply a pixelation effect to the image or a region. 13 |

14 | 15 | 16 | 17 | 18 | 19 | {this.renderRun()} 20 |
21 |
22 | ) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /classes/nodes/filter/Posterize.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import PosterizeProperties from './PosterizeProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | import InputNumber from '../InputNumber.js'; 6 | 7 | 8 | export default class Posterize extends NodeImage { 9 | constructor(className, graph, x, y, settings) { 10 | super(className, graph, x, y, 'Posterize', PosterizeProperties, settings); 11 | 12 | this.inputs = [ 13 | new InputImage(this, 0, 'Input'), 14 | new InputNumber(this, 1, 'Amount', 'hasAmountInput') 15 | ]; 16 | this.outputs = [ 17 | new OutputImage(this, 0, 'Output') 18 | ]; 19 | 20 | this.amount = typeof settings.amount !== 'undefined' ? settings.amount : 5; 21 | } 22 | 23 | 24 | run(inputThatTriggered) { 25 | if (this.inputs[0].image) { 26 | this.bg.classList.add('running'); 27 | this.runTimer = Date.now(); 28 | 29 | let amount = this.amount; 30 | 31 | if (this.inputs[1].number != null) { 32 | amount = this.inputs[1].number; 33 | } 34 | 35 | if (this.isInsideALoop) { 36 | let image = this.inputs[0].image.clone(); 37 | image.posterize(amount); 38 | this.image = image; 39 | super.run(inputThatTriggered); 40 | 41 | } else { 42 | Jimp.read(this.inputs[0].image).then(image => { 43 | image.posterize(amount, (error, image) => { 44 | if (error) { 45 | console.log(error); 46 | } else { 47 | this.image = image; 48 | super.run(inputThatTriggered); 49 | } 50 | }); 51 | }) 52 | } 53 | } else { 54 | this.runTimer = Date.now(); 55 | this.image = null; 56 | super.run(inputThatTriggered); 57 | } 58 | } 59 | 60 | 61 | toJson() { 62 | let json = super.toJson(); 63 | 64 | json.settings.amount = this.amount; 65 | 66 | return json; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /classes/nodes/filter/PosterizeProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class PosterizeProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Posterize
11 |
12 | 13 | {this.renderRun()} 14 |
15 |
16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /classes/nodes/filter/Sepia.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import SepiaProperties from './SepiaProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | 6 | export default class Sepia extends NodeImage { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Sepia', SepiaProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputImage(this, 0, 'Input') 12 | ]; 13 | this.outputs = [ 14 | new OutputImage(this, 0, 'Output') 15 | ]; 16 | } 17 | 18 | 19 | run(inputThatTriggered) { 20 | if (this.inputs[0].image) { 21 | this.bg.classList.add('running'); 22 | this.runTimer = Date.now(); 23 | 24 | if (this.isInsideALoop) { 25 | const image = this.inputs[0].image.clone(); 26 | image.sepia(); 27 | this.image = image; 28 | super.run(inputThatTriggered); 29 | } else { 30 | Jimp.read(this.inputs[0].image).then(image => { 31 | image.sepia((error, image) => { 32 | this.image = image; 33 | super.run(inputThatTriggered); 34 | }); 35 | }) 36 | } 37 | 38 | } else { 39 | this.runTimer = Date.now(); 40 | this.image = null; 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /classes/nodes/filter/SepiaProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class SepiaProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Sepia Filter
14 |
15 | {this.renderRun()} 16 |
17 |
18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /classes/nodes/filter/SobelProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class SepiaProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Sobel Filter
14 |
15 | {this.renderRun()} 16 |
17 |
18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /classes/nodes/filter/StainedGlassProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class StainedGlassProperties extends Properties { 6 | 7 | constructor(props) { 8 | super(props); 9 | } 10 | 11 | 12 | render() { 13 | return ( 14 |
15 |
Height to Normal Map
16 |
17 | 18 | {this.renderRun()} 19 |
20 |
21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /classes/nodes/image/CircleProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class CircleProperties extends Properties { 6 | 7 | constructor(props) { 8 | super(props); 9 | 10 | this.state = { 11 | hasColorInput: props.node.inputs[3].parent ? true : false, 12 | } 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |
Circle
19 |
20 | 21 | 22 | 23 |
24 | {this.renderColor()} 25 | 26 | {this.renderRun()} 27 |
28 |
29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /classes/nodes/image/FaultlineProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class FaultlineProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Faultline Noise
11 |
12 | Generate noise using fault line formation algorithm.

13 | 14 | 15 | 16 | 17 | 18 | {this.renderRun()} 19 | 20 |

21 | For each iteration pick two random points to create a line. Raise or lower pixel depending on which side of the line it is on. 22 |

23 | https://github.com/barisusakli/faultline 24 |
25 |
26 | ) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /classes/nodes/image/ImageUpload.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import ImageUploadProperties from './ImageUploadProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class ImageUpload extends NodeImage { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'ImageUpload', ImageUploadProperties, settings); 9 | 10 | this.inputs = []; 11 | this.outputs = [ 12 | new OutputImage(this, 0, 'Output'), 13 | new OutputNumber(this, 1, 'Width'), 14 | new OutputNumber(this, 2, 'Height') 15 | ]; 16 | 17 | this.base64 = typeof settings.base64 !== 'undefined' ? settings.base64 : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.base64 = null; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | if (this.base64) { 32 | this.bg.classList.add('running'); 33 | this.runTimer = Date.now(); 34 | Jimp.read(this.base64).then(image => { 35 | this.image = image; 36 | super.run(inputThatTriggered); 37 | }) 38 | } else { 39 | this.runTimer = Date.now(); 40 | this.image = null; 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | 45 | 46 | passToChildren() { 47 | if (this.image) { 48 | this.outputs[1].connections.forEach(conn => { 49 | conn.number = this.image.bitmap.width; 50 | conn.runNode(); 51 | }) 52 | this.outputs[2].connections.forEach(conn => { 53 | conn.number = this.image.bitmap.height; 54 | conn.runNode(); 55 | }) 56 | } 57 | 58 | super.passToChildren(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /classes/nodes/image/ImageUploadProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GreyscaleProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | progress: null 10 | } 11 | 12 | this.onUploadImage = this.onUploadImage.bind(this); 13 | } 14 | 15 | 16 | onUploadImage(event) { 17 | const elm = document.getElementById('uploadInput'); 18 | const reader = new FileReader(); 19 | 20 | // reader.addEventListener('load', (e) => { 21 | // this.props.node.base64 = e.target.result; 22 | // this.props.node.run(null); 23 | // }) 24 | 25 | reader.onloadstart = (event) => { 26 | this.setState({progress: 0}); 27 | } 28 | 29 | reader.onprogress = (event) => { 30 | if (event.lengthComputable) { 31 | this.setState({progress: event.loaded / event.total * 100}); 32 | } 33 | } 34 | 35 | reader.onloadend = (event) => { 36 | this.setState({progress: null}); 37 | this.props.node.base64 = event.target.result; 38 | this.props.node.run(null); 39 | } 40 | 41 | reader.readAsDataURL(elm.files[0]); 42 | } 43 | 44 | 45 | render() { 46 | return ( 47 |
48 |
Image Upload
49 |
50 | {this.onUploadImage(event);}} /> 51 |

52 | {this.state.progress != null ? 53 | Math.round(this.state.progress)+'% Uploaded' 54 | : null} 55 |

56 | Uploaded images are not saved with the graph. 57 | {this.renderRun()} 58 |
59 |
60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /classes/nodes/image/LineProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class LineProperties extends Properties { 6 | 7 | constructor(props) { 8 | super(props); 9 | 10 | this.state = { 11 | hasColorInput: props.node.inputs[6].parent ? true : false, 12 | } 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |
Line
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {this.renderColor()} 28 | 29 | {this.renderRun()} 30 |
31 |
32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /classes/nodes/image/PasteImageProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class PasteImageProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Image from Clipboard
14 |
15 | When this node is selected it captures images that are pasted. 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/image/SimplexProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class SimplexProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Simplex Noise
11 |
12 | 13 | 14 | 15 | 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/image/TriangleProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class TriangleProperties extends Properties { 6 | 7 | constructor(props) { 8 | super(props); 9 | 10 | this.state = { 11 | hasColorInput: props.node.inputs[8].parent ? true : false, 12 | } 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |
Triangle
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {this.renderColor()} 29 | 30 | {this.renderRun()} 31 |
32 |
33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /classes/nodes/image/UniformColorProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class UniformColorProperties extends Properties { 6 | 7 | constructor(props) { 8 | super(props); 9 | 10 | this.state = { 11 | hasColorInput: props.node.inputs[2].parent ? true : false, 12 | } 13 | } 14 | 15 | 16 | render() { 17 | return ( 18 |
19 |
Uniform Color
20 |
21 | 22 | 23 | 24 |

25 | 26 | {this.renderColor()} 27 | 28 | {this.renderRun()} 29 |
30 |
31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /classes/nodes/image/UrlImage.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import UrlImageProperties from './UrlImageProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | import Jimp from "jimp"; 6 | import fetch from 'isomorphic-unfetch'; 7 | 8 | 9 | export default class UrlImage extends NodeImage { 10 | constructor(className, graph, x, y, settings) { 11 | super(className, graph, x, y, 'Image from Url', UrlImageProperties, settings); 12 | 13 | this.inputs = []; 14 | this.outputs = [ 15 | new OutputImage(this, 0, 'Output'), 16 | new OutputNumber(this, 1, 'Width'), 17 | new OutputNumber(this, 2, 'Height') 18 | ]; 19 | 20 | //this.url = 'https://i.imgur.com/mdlwVuL.jpg'; 21 | this.url = typeof settings.url !== 'undefined' ? settings.url : 'https://i.imgur.com/3aDSTiBl.jpg'; 22 | } 23 | 24 | 25 | toJson() { 26 | let json = super.toJson(); 27 | 28 | json.settings.url = this.url; 29 | 30 | return json; 31 | } 32 | 33 | 34 | run(inputThatTriggered) { 35 | this.bg.classList.add('running'); 36 | this.runTimer = Date.now(); 37 | Jimp.read(this.url).then(image => { 38 | this.image = image; 39 | super.run(inputThatTriggered); 40 | }).catch(error => { 41 | console.log(error); 42 | }) 43 | } 44 | 45 | 46 | passToChildren() { 47 | if (this.image) { 48 | this.outputs[1].connections.forEach(conn => { 49 | conn.number = this.image.bitmap.width; 50 | conn.runNode(); 51 | }) 52 | this.outputs[2].connections.forEach(conn => { 53 | conn.number = this.image.bitmap.height; 54 | conn.runNode(); 55 | }) 56 | } 57 | 58 | super.passToChildren(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /classes/nodes/image/UrlImageProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class UrlImageProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.urlChange = this.urlChange.bind(this); 9 | } 10 | 11 | 12 | urlChange(event) { 13 | const elm = document.getElementById('urlInput'); 14 | this.props.node.url = elm.value; 15 | this.props.node.run(null); 16 | } 17 | 18 | 19 | render() { 20 | return ( 21 |
22 |
Image from URL
23 |
24 | URL
25 | {this.urlChange(event);}} /> 26 | 27 |

28 | If this fails it's probably because the remote site doesn't allow cross-origin resource sharing. Some sites like Imgur allow it. 29 | {this.renderRun()} 30 |
31 |
32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /classes/nodes/image/WorleyProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class WorleyProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Worley Noise
11 |
12 | 13 | 14 | 15 | 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/number/AbsoluteValue.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import AbsoluteValueProperties from './AbsoluteValueProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | import InputNumber from '../InputNumber.js'; 5 | 6 | export default class AbsoluteValue extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Absolute Value', AbsoluteValueProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Result') 15 | ]; 16 | 17 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.a = this.a; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | if (this.inputs[0].number == null) { 32 | this.a = null; 33 | } else { 34 | this.a = this.inputs[0].number; 35 | } 36 | 37 | if (this.a != null && !isNaN(this.a)) { 38 | this.bg.classList.add('running'); 39 | this.runTimer = Date.now(); 40 | this.number = Math.abs(this.a); 41 | super.run(inputThatTriggered); 42 | } else { 43 | this.runTimer = Date.now(); 44 | this.number = null; 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/number/AbsoluteValueProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class AbsoluteValueProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | } 11 | } 12 | 13 | 14 | renderResult() { 15 | if (this.state.hasA) { 16 | return ( 17 |
18 | |{this.props.node.a}|= {Math.abs(this.props.node.a)} 19 |
20 | ) 21 | } 22 | } 23 | 24 | 25 | render() { 26 | return ( 27 |
28 |
Add Numbers
29 |
30 | {this.renderResult()} 31 | {this.renderName()} 32 | {this.renderRun()} 33 |
34 |
35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/nodes/number/AddNumbers.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import AddNumbersProperties from './AddNumbersProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | import InputNumber from '../InputNumber.js'; 5 | 6 | export default class AddNumbers extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Add', AddNumbersProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | new InputNumber(this, 1, 'b', 'hasB'), 13 | ]; 14 | this.outputs = [ 15 | new OutputNumber(this, 0, 'Result') 16 | ]; 17 | 18 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 19 | this.b = typeof settings.b !== 'undefined' ? settings.b : null; 20 | } 21 | 22 | 23 | toJson() { 24 | let json = super.toJson(); 25 | 26 | json.settings.a = this.a; 27 | json.settings.b = this.b; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | if (this.inputs[0].number == null) { 35 | this.a = null; 36 | } else { 37 | this.a = this.inputs[0].number; 38 | } 39 | 40 | if (this.inputs[1].number == null) { 41 | this.b = null; 42 | } else { 43 | this.b = this.inputs[1].number; 44 | } 45 | 46 | if (this.a != null && this.b != null && !isNaN(this.a) && !isNaN(this.b)) { 47 | this.bg.classList.add('running'); 48 | this.runTimer = Date.now(); 49 | this.number = this.a + this.b; 50 | super.run(inputThatTriggered); 51 | } else { 52 | this.runTimer = Date.now(); 53 | this.number = null; 54 | super.run(inputThatTriggered); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /classes/nodes/number/AddNumbersProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class AddNumbersProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | hasB: props.node.inputs[1].parent ? true : false 11 | } 12 | } 13 | 14 | 15 | renderResult() { 16 | if (this.state.hasA && this.state.hasB) { 17 | return ( 18 |
19 | {this.props.node.a} + {this.props.node.b} = {this.props.node.a + this.props.node.b} 20 |
21 | ) 22 | } 23 | } 24 | 25 | 26 | render() { 27 | return ( 28 |
29 |
Add Numbers
30 |
31 | {this.renderResult()} 32 | {this.renderName()} 33 | {this.renderRun()} 34 |
35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /classes/nodes/number/Atan2.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import Atan2Properties from './Atan2Properties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class Atan2 extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Atan2', Atan2Properties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'x', 'hasX'), 12 | new InputNumber(this, 1, 'y', 'hasY'), 13 | ]; 14 | this.outputs = [ 15 | new OutputNumber(this, 0, 'Result') 16 | ]; 17 | 18 | this.numX = typeof settings.numX !== 'undefined' ? settings.numX : null; 19 | this.numY = typeof settings.numY !== 'undefined' ? settings.numY : null; 20 | } 21 | 22 | 23 | toJson() { 24 | let json = super.toJson(); 25 | 26 | json.settings.numX = this.numX; 27 | json.settings.numY = this.numY; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | if (this.inputs[0].number == null) { 35 | this.numX = null; 36 | } else { 37 | this.numX = this.inputs[0].number; 38 | } 39 | 40 | if (this.inputs[1].number == null) { 41 | this.numY = null; 42 | } else { 43 | this.numY = this.inputs[1].number; 44 | } 45 | 46 | if (this.numX != null && !isNaN(this.numX) && this.numY != null && !isNaN(this.numY)) { 47 | this.bg.classList.add('running'); 48 | this.runTimer = Date.now(); 49 | this.number = Math.atan2(this.numY, this.numX); 50 | super.run(inputThatTriggered); 51 | } else { 52 | this.runTimer = Date.now(); 53 | this.number = null; 54 | super.run(inputThatTriggered); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /classes/nodes/number/Atan2Properties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class AddNumbersProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasX: props.node.inputs[0].parent ? true : false, 10 | hasY: props.node.inputs[1].parent ? true : false 11 | } 12 | } 13 | 14 | 15 | renderResult() { 16 | if (this.state.hasX && this.state.hasY) { 17 | return ( 18 |
19 | atan2({this.props.node.numY}, {this.props.node.numX}) = {Math.atan2(this.props.node.numY, this.props.node.numX)} 20 |
21 | ) 22 | } 23 | } 24 | 25 | 26 | render() { 27 | return ( 28 |
29 |
Atan2
30 |
31 | {this.renderResult()} 32 | {this.renderName()} 33 | {this.renderRun()} 34 |
35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /classes/nodes/number/Ceil.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import CeilProperties from './CeilProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class Ceil extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Ceil', CeilProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Result') 15 | ]; 16 | 17 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.a = this.a; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | if (this.inputs[0].number == null) { 32 | this.a = null; 33 | } else { 34 | this.a = this.inputs[0].number; 35 | } 36 | 37 | if (this.a != null && !isNaN(this.a)) { 38 | this.bg.classList.add('running'); 39 | this.runTimer = Date.now(); 40 | this.number = Math.ceil(this.a); 41 | super.run(inputThatTriggered); 42 | } else { 43 | this.runTimer = Date.now(); 44 | this.number = null; 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/number/CeilProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class CeilProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | } 11 | } 12 | 13 | 14 | renderResult() { 15 | if (this.state.hasA) { 16 | return ( 17 |
18 | ceil({this.props.node.a}) = {Math.ceil(this.props.node.a)} 19 |
20 | ) 21 | } 22 | } 23 | 24 | 25 | render() { 26 | return ( 27 |
28 |
Ceil a Number
29 |
30 | {this.renderResult()} 31 | {this.renderName()} 32 | {this.renderRun()} 33 |
34 |
35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/nodes/number/Cos.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import CosProperties from './CosProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class Cos extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Cosine', CosProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Result') 15 | ]; 16 | 17 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.a = this.a; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | if (this.inputs[0].number == null) { 32 | this.a = null; 33 | } else { 34 | this.a = this.inputs[0].number; 35 | } 36 | 37 | if (this.a != null && !isNaN(this.a)) { 38 | this.bg.classList.add('running'); 39 | this.runTimer = Date.now(); 40 | this.number = Math.cos(this.a); 41 | super.run(inputThatTriggered); 42 | } else { 43 | this.runTimer = Date.now(); 44 | this.number = null; 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/number/CosProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class CosProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | } 11 | } 12 | 13 | 14 | renderResult() { 15 | if (this.state.hasA) { 16 | return ( 17 |
18 | cos({this.props.node.a}) = {Math.cos(this.props.node.a)} 19 |
20 | ) 21 | } 22 | } 23 | 24 | 25 | render() { 26 | return ( 27 |
28 |
Cosine of a Number
29 |
30 | {this.renderResult()} 31 | {this.renderName()} 32 | {this.renderRun()} 33 |
34 |
35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/nodes/number/DegreesToRadians.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import DegreesToRadiansProperties from './DegreesToRadiansProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class DegreesToRadians extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Degrees to Radians', DegreesToRadiansProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Result') 15 | ]; 16 | 17 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.a = this.a; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | if (this.inputs[0].number == null) { 32 | this.a = null; 33 | } else { 34 | this.a = this.inputs[0].number; 35 | } 36 | 37 | if (this.a != null && !isNaN(this.a)) { 38 | this.bg.classList.add('running'); 39 | this.runTimer = Date.now(); 40 | this.number = this.a * (Math.PI / 180); 41 | super.run(inputThatTriggered); 42 | } else { 43 | this.runTimer = Date.now(); 44 | this.number = null; 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/number/DegreesToRadiansProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class DegreesToRadiansProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | } 11 | } 12 | 13 | 14 | renderResult() { 15 | if (this.state.hasA) { 16 | return ( 17 |
18 | sin({this.props.node.a}) = {this.props.node.a*(Math.PI/180)} 19 |
20 | ) 21 | } 22 | } 23 | 24 | 25 | render() { 26 | return ( 27 |
28 |
Degrees to Radians
29 |
30 | {this.renderResult()} 31 | {this.renderName()} 32 | {this.renderRun()} 33 |
34 |
35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/nodes/number/DivideNumbers.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import DivideNumbersProperties from './DivideNumbersProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class DivideNumbers extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Divide', DivideNumbersProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | new InputNumber(this, 1, 'b', 'hasB'), 13 | ]; 14 | this.outputs = [ 15 | new OutputNumber(this, 0, 'Result') 16 | ]; 17 | 18 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 19 | this.b = typeof settings.b !== 'undefined' ? settings.b : null; 20 | } 21 | 22 | 23 | toJson() { 24 | let json = super.toJson(); 25 | 26 | json.settings.a = this.a; 27 | json.settings.b = this.b; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | if (this.inputs[0].number == null) { 35 | this.a = null; 36 | } else { 37 | this.a = this.inputs[0].number; 38 | } 39 | 40 | if (this.inputs[1].number == null) { 41 | this.b = null; 42 | } else { 43 | this.b = this.inputs[1].number; 44 | } 45 | 46 | if (this.a != null && this.b != null && !isNaN(this.a) && !isNaN(this.b)) { 47 | this.bg.classList.add('running'); 48 | this.runTimer = Date.now(); 49 | this.number = this.a / this.b; 50 | super.run(inputThatTriggered); 51 | } else { 52 | this.runTimer = Date.now(); 53 | this.number = null; 54 | super.run(inputThatTriggered); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /classes/nodes/number/DivideNumbersProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class DivideNumbersProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | hasB: props.node.inputs[1].parent ? true : false 11 | } 12 | } 13 | 14 | 15 | renderResult() { 16 | if (this.state.hasA && this.state.hasB) { 17 | return ( 18 |
19 | {this.props.node.a} / {this.props.node.b} = {this.props.node.a / this.props.node.b} 20 |
21 | ) 22 | } 23 | } 24 | 25 | 26 | render() { 27 | return ( 28 |
29 |
Divide Numbers
30 |
31 | {this.renderResult()} 32 | {this.renderName()} 33 | {this.renderRun()} 34 |
35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /classes/nodes/number/ExponentNumbers.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import ExponentNumbersProperties from './ExponentNumbersProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class ExponentNumbers extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Exponent', ExponentNumbersProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | new InputNumber(this, 1, 'b', 'hasB'), 13 | ]; 14 | this.outputs = [ 15 | new OutputNumber(this, 0, 'Result') 16 | ]; 17 | 18 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 19 | this.b = typeof settings.b !== 'undefined' ? settings.b : null; 20 | } 21 | 22 | 23 | toJson() { 24 | let json = super.toJson(); 25 | 26 | json.settings.a = this.a; 27 | json.settings.b = this.b; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | if (this.inputs[0].number == null) { 35 | this.a = null; 36 | } else { 37 | this.a = this.inputs[0].number; 38 | } 39 | 40 | if (this.inputs[1].number == null) { 41 | this.b = null; 42 | } else { 43 | this.b = this.inputs[1].number; 44 | } 45 | 46 | if (this.a != null && this.b != null && !isNaN(this.a) && !isNaN(this.b)) { 47 | this.bg.classList.add('running'); 48 | this.runTimer = Date.now(); 49 | this.number = Math.pow(this.a, this.b); 50 | super.run(inputThatTriggered); 51 | } else { 52 | this.runTimer = Date.now(); 53 | this.number = null; 54 | super.run(inputThatTriggered); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /classes/nodes/number/ExponentNumbersProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class ExponentNumbersProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | hasB: props.node.inputs[1].parent ? true : false 11 | } 12 | } 13 | 14 | 15 | renderResult() { 16 | if (this.state.hasA && this.state.hasB) { 17 | return ( 18 |
19 | {this.props.node.a} ^ {this.props.node.b} = {Math.pow(this.props.node.a, this.props.node.b)} 20 |
21 | ) 22 | } 23 | } 24 | 25 | 26 | render() { 27 | return ( 28 |
29 |
Exponent of Numbers
30 |
31 | {this.renderResult()} 32 | {this.renderName()} 33 | {this.renderRun()} 34 |
35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /classes/nodes/number/Floor.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import FloorProperties from './FloorProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class Floor extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Floor', FloorProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Result') 15 | ]; 16 | 17 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.a = this.a; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | if (this.inputs[0].number == null) { 32 | this.a = null; 33 | } else { 34 | this.a = this.inputs[0].number; 35 | } 36 | 37 | if (this.a != null && !isNaN(this.a)) { 38 | this.bg.classList.add('running'); 39 | this.runTimer = Date.now(); 40 | this.number = Math.floor(this.a); 41 | super.run(inputThatTriggered); 42 | } else { 43 | this.runTimer = Date.now(); 44 | this.number = null; 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/number/FloorProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class FloorProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | } 11 | } 12 | 13 | 14 | renderResult() { 15 | if (this.state.hasA) { 16 | return ( 17 |
18 | floor({this.props.node.a}) = {Math.floor(this.props.node.a)} 19 |
20 | ) 21 | } 22 | } 23 | 24 | 25 | render() { 26 | return ( 27 |
28 |
Floor a Number
29 |
30 | {this.renderResult()} 31 | {this.renderName()} 32 | {this.renderRun()} 33 |
34 |
35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/nodes/number/GetImageSize.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import GetImageSizeProperties from './GetImageSizeProperties.jsx'; 3 | import InputImage from '../InputImage.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class GetImageSize extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Get Image Size', GetImageSizeProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputImage(this, 0, 'Input'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Width'), 15 | new OutputNumber(this, 1, 'Height') 16 | ]; 17 | } 18 | 19 | 20 | toJson() { 21 | let json = super.toJson(); 22 | 23 | return json; 24 | } 25 | 26 | 27 | run(inputThatTriggered) { 28 | if (this.inputs[0].image != null) { 29 | this.bg.classList.add('running'); 30 | this.runTimer = Date.now(); 31 | this.number = this.inputs[0].image.bitmap.width+' x '+this.inputs[0].image.bitmap.height; 32 | super.run(inputThatTriggered); 33 | } else { 34 | this.runTimer = Date.now(); 35 | this.number = null; 36 | super.run(inputThatTriggered); 37 | } 38 | } 39 | 40 | 41 | passToChildren() { 42 | this.outputs[0].connections.forEach(conn => { 43 | if (this.inputs[0].image == null) { 44 | conn.number = null; 45 | } else { 46 | conn.number = this.inputs[0].image.bitmap.width; 47 | } 48 | conn.runNode(); 49 | }) 50 | 51 | this.outputs[1].connections.forEach(conn => { 52 | if (this.inputs[0].image == null) { 53 | conn.number = null; 54 | } else { 55 | conn.number = this.inputs[0].image.bitmap.height; 56 | } 57 | conn.runNode(); 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /classes/nodes/number/GetImageSizeProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class GetImageSizeProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Get Image Size
14 |
15 | {this.renderName()} 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/number/Max.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import MaxProperties from './MaxProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class Max extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Max', MaxProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | new InputNumber(this, 1, 'b', 'hasB'), 13 | ]; 14 | this.outputs = [ 15 | new OutputNumber(this, 0, 'Result') 16 | ]; 17 | 18 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 19 | this.b = typeof settings.b !== 'undefined' ? settings.b : null; 20 | } 21 | 22 | 23 | toJson() { 24 | let json = super.toJson(); 25 | 26 | json.settings.a = this.a; 27 | json.settings.b = this.b; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | if (this.inputs[0].number == null) { 35 | this.a = null; 36 | } else { 37 | this.a = this.inputs[0].number; 38 | } 39 | 40 | if (this.inputs[1].number == null) { 41 | this.b = null; 42 | } else { 43 | this.b = this.inputs[1].number; 44 | } 45 | 46 | if (this.a != null && this.b != null && !isNaN(this.a) && !isNaN(this.b)) { 47 | this.bg.classList.add('running'); 48 | this.runTimer = Date.now(); 49 | this.number = Math.max(this.a, this.b); 50 | super.run(inputThatTriggered); 51 | } else { 52 | this.runTimer = Date.now(); 53 | this.number = null; 54 | super.run(inputThatTriggered); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /classes/nodes/number/MaxProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class MaxNumbersProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | hasB: props.node.inputs[1].parent ? true : false 11 | } 12 | } 13 | 14 | 15 | renderResult() { 16 | if (this.state.hasA && this.state.hasB) { 17 | return ( 18 |
19 | max({this.props.node.a}, {this.props.node.b}) = {Math.max(this.props.node.a, this.props.node.b)} 20 |
21 | ) 22 | } 23 | } 24 | 25 | 26 | render() { 27 | return ( 28 |
29 |
Maximum of Two Numbers
30 |
31 | {this.renderResult()} 32 | {this.renderName()} 33 | {this.renderRun()} 34 |
35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /classes/nodes/number/Min.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import MinProperties from './MinProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class Min extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Min', MinProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | new InputNumber(this, 1, 'b', 'hasB'), 13 | ]; 14 | this.outputs = [ 15 | new OutputNumber(this, 0, 'Result') 16 | ]; 17 | 18 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 19 | this.b = typeof settings.b !== 'undefined' ? settings.b : null; 20 | } 21 | 22 | 23 | toJson() { 24 | let json = super.toJson(); 25 | 26 | json.settings.a = this.a; 27 | json.settings.b = this.b; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | if (this.inputs[0].number == null) { 35 | this.a = null; 36 | } else { 37 | this.a = this.inputs[0].number; 38 | } 39 | 40 | if (this.inputs[1].number == null) { 41 | this.b = null; 42 | } else { 43 | this.b = this.inputs[1].number; 44 | } 45 | 46 | if (this.a != null && this.b != null && !isNaN(this.a) && !isNaN(this.b)) { 47 | this.bg.classList.add('running'); 48 | this.runTimer = Date.now(); 49 | this.number = Math.min(this.a, this.b); 50 | super.run(inputThatTriggered); 51 | } else { 52 | this.runTimer = Date.now(); 53 | this.number = null; 54 | super.run(inputThatTriggered); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /classes/nodes/number/MinProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class MinNumbersProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | hasB: props.node.inputs[1].parent ? true : false 11 | } 12 | } 13 | 14 | 15 | renderResult() { 16 | if (this.state.hasA && this.state.hasB) { 17 | return ( 18 |
19 | min({this.props.node.a}, {this.props.node.b}) = {Math.min(this.props.node.a, this.props.node.b)} 20 |
21 | ) 22 | } 23 | } 24 | 25 | 26 | render() { 27 | return ( 28 |
29 |
Minimum of Two Numbers
30 |
31 | {this.renderResult()} 32 | {this.renderName()} 33 | {this.renderRun()} 34 |
35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /classes/nodes/number/ModuloNumbers.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import ModuloNumbersProperties from './ModuloNumbersProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class ModuloNumbers extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Mod', ModuloNumbersProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | new InputNumber(this, 1, 'b', 'hasB'), 13 | ]; 14 | this.outputs = [ 15 | new OutputNumber(this, 0, 'Result') 16 | ]; 17 | 18 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 19 | this.b = typeof settings.b !== 'undefined' ? settings.b : null; 20 | } 21 | 22 | 23 | toJson() { 24 | let json = super.toJson(); 25 | 26 | json.settings.a = this.a; 27 | json.settings.b = this.b; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | if (this.inputs[0].number == null) { 35 | this.a = null; 36 | } else { 37 | this.a = this.inputs[0].number; 38 | } 39 | 40 | if (this.inputs[1].number == null) { 41 | this.b = null; 42 | } else { 43 | this.b = this.inputs[1].number; 44 | } 45 | 46 | if (this.a != null && this.b != null && !isNaN(this.a) && !isNaN(this.b)) { 47 | this.bg.classList.add('running'); 48 | this.runTimer = Date.now(); 49 | this.number = this.a % this.b; 50 | super.run(inputThatTriggered); 51 | } else { 52 | this.runTimer = Date.now(); 53 | this.number = null; 54 | super.run(inputThatTriggered); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /classes/nodes/number/ModuloNumbersProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class ModuloNumbersProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | hasB: props.node.inputs[1].parent ? true : false 11 | } 12 | } 13 | 14 | 15 | renderResult() { 16 | if (this.state.hasA && this.state.hasB) { 17 | return ( 18 |
19 | {this.props.node.a} % {this.props.node.b} = {this.props.node.a % this.props.node.b} 20 |
21 | ) 22 | } 23 | } 24 | 25 | 26 | render() { 27 | return ( 28 |
29 |
Modulo Numbers
30 |
31 | {this.renderResult()} 32 | {this.renderName()} 33 | {this.renderRun()} 34 |
35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /classes/nodes/number/MultiplyNumbers.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import MultiplyNumbersProperties from './MultiplyNumbersProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class MultiplyNumbers extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Multiply', MultiplyNumbersProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | new InputNumber(this, 1, 'b', 'hasB'), 13 | ]; 14 | this.outputs = [ 15 | new OutputNumber(this, 0, 'Result') 16 | ]; 17 | 18 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 19 | this.b = typeof settings.b !== 'undefined' ? settings.b : null; 20 | } 21 | 22 | 23 | toJson() { 24 | let json = super.toJson(); 25 | 26 | json.settings.a = this.a; 27 | json.settings.b = this.b; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | if (this.inputs[0].number == null) { 35 | this.a = null; 36 | } else { 37 | this.a = this.inputs[0].number; 38 | } 39 | 40 | if (this.inputs[1].number == null) { 41 | this.b = null; 42 | } else { 43 | this.b = this.inputs[1].number; 44 | } 45 | 46 | if (this.a != null && this.b != null && !isNaN(this.a) && !isNaN(this.b)) { 47 | this.bg.classList.add('running'); 48 | this.runTimer = Date.now(); 49 | this.number = this.a * this.b; 50 | super.run(inputThatTriggered); 51 | } else { 52 | this.runTimer = Date.now(); 53 | this.number = null; 54 | super.run(inputThatTriggered); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /classes/nodes/number/MultiplyNumbersProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class MultiplyNumbersProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | hasB: props.node.inputs[1].parent ? true : false 11 | } 12 | } 13 | 14 | 15 | renderResult() { 16 | if (this.state.hasA && this.state.hasB) { 17 | return ( 18 |
19 | {this.props.node.a} * {this.props.node.b} = {this.props.node.a * this.props.node.b} 20 |
21 | ) 22 | } 23 | } 24 | 25 | 26 | render() { 27 | return ( 28 |
29 |
Multiply Numbers
30 |
31 | {this.renderResult()} 32 | {this.renderName()} 33 | {this.renderRun()} 34 |
35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /classes/nodes/number/Number.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import NumberProperties from './NumberProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | 5 | export default class Number extends NodeNumber { 6 | constructor(className, graph, x, y, settings) { 7 | super(className, graph, x, y, 'Number', NumberProperties, settings); 8 | 9 | this.inputs = []; 10 | this.outputs = [ 11 | new OutputNumber(this, 0, 'Output') 12 | ]; 13 | 14 | this.number = typeof settings.number !== 'undefined' ? settings.number : 1; 15 | } 16 | 17 | 18 | toJson() { 19 | let json = super.toJson(); 20 | 21 | json.settings.number = this.number; 22 | 23 | return json; 24 | } 25 | 26 | 27 | run(inputThatTriggered) { 28 | this.bg.classList.add('running'); 29 | this.runTimer = Date.now(); 30 | super.run(inputThatTriggered); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /classes/nodes/number/NumberProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class NumberProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Number Input
11 | 12 |
13 | 14 | {this.renderName()} 15 | {this.renderRun()} 16 |
17 |
18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /classes/nodes/number/Pi.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import PiProperties from './PiProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | 5 | export default class Pi extends NodeNumber { 6 | constructor(className, graph, x, y, settings) { 7 | super(className, graph, x, y, 'Pi', PiProperties, settings); 8 | 9 | this.inputs = []; 10 | this.outputs = [ 11 | new OutputNumber(this, 0, 'Output') 12 | ]; 13 | 14 | this.number = Math.PI; 15 | } 16 | 17 | 18 | toJson() { 19 | let json = super.toJson(); 20 | 21 | json.settings.number = this.number; 22 | 23 | return json; 24 | } 25 | 26 | 27 | run(inputThatTriggered) { 28 | this.bg.classList.add('running'); 29 | this.runTimer = Date.now(); 30 | super.run(inputThatTriggered); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /classes/nodes/number/PiProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class PiProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Pi
14 | 15 |
16 | {this.props.node.number} 17 | {this.renderName()} 18 | {this.renderRun()} 19 |
20 |
21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /classes/nodes/number/RadiansToDegrees.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import RadiansToDegreesProperties from './RadiansToDegreesProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class RadiansToDegrees extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Radians to Degrees', RadiansToDegreesProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Result') 15 | ]; 16 | 17 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.a = this.a; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | if (this.inputs[0].number == null) { 32 | this.a = null; 33 | } else { 34 | this.a = this.inputs[0].number; 35 | } 36 | 37 | if (this.a != null && !isNaN(this.a)) { 38 | this.bg.classList.add('running'); 39 | this.runTimer = Date.now(); 40 | this.number = this.a * (180 / Math.PI); 41 | super.run(inputThatTriggered); 42 | } else { 43 | this.runTimer = Date.now(); 44 | this.number = null; 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/number/RadiansToDegreesProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class DegreesToRadiansProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | } 11 | } 12 | 13 | 14 | renderResult() { 15 | if (this.state.hasA) { 16 | return ( 17 |
18 | sin({this.props.node.a}) = {this.props.node.a * (180 / Math.PI)} 19 |
20 | ) 21 | } 22 | } 23 | 24 | 25 | render() { 26 | return ( 27 |
28 |
Radians to Degrees
29 |
30 | {this.renderResult()} 31 | {this.renderName()} 32 | {this.renderRun()} 33 |
34 |
35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/nodes/number/RandomNumber.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import RandomNumberProperties from './RandomNumberProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | import InputNumber from '../InputNumber.js'; 5 | 6 | export default class RandomNumber extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Random Number', RandomNumberProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'Regenerate') 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Output') 15 | ]; 16 | 17 | this.number = Math.random(); 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.number = this.number; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | this.bg.classList.add('running'); 32 | this.runTimer = Date.now(); 33 | this.number = Math.random(); 34 | super.run(inputThatTriggered); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /classes/nodes/number/RandomNumberProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class RandomNumberProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.run = this.run.bind(this); 9 | } 10 | 11 | 12 | run(event) { 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
Random Number
21 | 22 |
23 | Random number between 0 and 1.
24 |
25 | 26 |

27 | When the regenerate input changes this node will generate a new random number. 28 |
29 |
30 | Regenerate input is optional. 31 | {this.renderName()} 32 | {this.renderRun()} 33 |
34 |
35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/nodes/number/Round.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import RoundProperties from './RoundProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class Round extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Round', RoundProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Result') 15 | ]; 16 | 17 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.a = this.a; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | if (this.inputs[0].number == null) { 32 | this.a = null; 33 | } else { 34 | this.a = this.inputs[0].number; 35 | } 36 | 37 | if (this.a != null && !isNaN(this.a)) { 38 | this.bg.classList.add('running'); 39 | this.runTimer = Date.now(); 40 | this.number = Math.round(this.a); 41 | super.run(inputThatTriggered); 42 | } else { 43 | this.runTimer = Date.now(); 44 | this.number = null; 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/number/RoundProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class RoundProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | } 11 | } 12 | 13 | 14 | renderResult() { 15 | if (this.state.hasA) { 16 | return ( 17 |
18 | round({this.props.node.a}) = {Math.round(this.props.node.a)} 19 |
20 | ) 21 | } 22 | } 23 | 24 | 25 | render() { 26 | return ( 27 |
28 |
Round a Number
29 |
30 | {this.renderResult()} 31 | {this.renderName()} 32 | {this.renderRun()} 33 |
34 |
35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/nodes/number/Sin.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import SinProperties from './SinProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class Sin extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Sine', SinProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Result') 15 | ]; 16 | 17 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.a = this.a; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | if (this.inputs[0].number == null) { 32 | this.a = null; 33 | } else { 34 | this.a = this.inputs[0].number; 35 | } 36 | 37 | if (this.a != null && !isNaN(this.a)) { 38 | this.bg.classList.add('running'); 39 | this.runTimer = Date.now(); 40 | this.number = Math.sin(this.a); 41 | super.run(inputThatTriggered); 42 | } else { 43 | this.runTimer = Date.now(); 44 | this.number = null; 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/number/SinProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class SinProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | } 11 | } 12 | 13 | 14 | renderResult() { 15 | if (this.state.hasA) { 16 | return ( 17 |
18 | sin({this.props.node.a}) = {Math.sin(this.props.node.a)} 19 |
20 | ) 21 | } 22 | } 23 | 24 | 25 | render() { 26 | return ( 27 |
28 |
Sine of a Number
29 |
30 | {this.renderResult()} 31 | {this.renderName()} 32 | {this.renderRun()} 33 |
34 |
35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/nodes/number/Slider.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import SliderProperties from './SliderProperties.jsx'; 3 | import OutputNumber from '../OutputNumber.js'; 4 | import InputNumber from '../InputNumber.js'; 5 | 6 | export default class Slider extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Slider', SliderProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'Min', 'hasMinInput'), 12 | new InputNumber(this, 1, 'Max', 'hasMaxInput'), 13 | new InputNumber(this, 2, 'Step', 'hasStepInput'), 14 | ]; 15 | this.outputs = [ 16 | new OutputNumber(this, 0, 'Output') 17 | ]; 18 | 19 | this.number = typeof settings.number !== 'undefined' ? settings.number : 1; 20 | this.min = typeof settings.min !== 'undefined' ? settings.min : 0; 21 | this.max = typeof settings.max !== 'undefined' ? settings.max : 10; 22 | this.step = typeof settings.max !== 'undefined' ? settings.step : 0.01; 23 | } 24 | 25 | 26 | toJson() { 27 | let json = super.toJson(); 28 | 29 | json.settings.number = this.number; 30 | json.settings.min = this.min; 31 | json.settings.max = this.max; 32 | json.settings.step = this.step; 33 | 34 | return json; 35 | } 36 | 37 | 38 | run(inputThatTriggered) { 39 | this.bg.classList.add('running'); 40 | this.runTimer = Date.now(); 41 | 42 | if (this.inputs[0].number != null) { 43 | this.min = this.inputs[0].number; 44 | } 45 | 46 | if (this.inputs[1].number != null) { 47 | this.max = this.inputs[1].number; 48 | } 49 | 50 | if (this.inputs[2].number != null) { 51 | this.step = this.inputs[2].number; 52 | } 53 | 54 | super.run(inputThatTriggered); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /classes/nodes/number/SquareRoot.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import SquareRootProperties from './SquareRootProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class SquareRoot extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'SquareRoot', SquareRootProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Result') 15 | ]; 16 | 17 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.a = this.a; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | if (this.inputs[0].number == null) { 32 | this.a = null; 33 | } else { 34 | this.a = this.inputs[0].number; 35 | } 36 | 37 | if (this.a != null && !isNaN(this.a)) { 38 | this.bg.classList.add('running'); 39 | this.runTimer = Date.now(); 40 | this.number = Math.sqrt(this.a); 41 | super.run(inputThatTriggered); 42 | } else { 43 | this.runTimer = Date.now(); 44 | this.number = null; 45 | super.run(inputThatTriggered); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/number/SquareRootProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class SquareRootProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | } 11 | } 12 | 13 | 14 | renderResult() { 15 | if (this.state.hasA) { 16 | return ( 17 |
18 | sqrt({this.props.node.a}) = {Math.sqrt(this.props.node.a)} 19 |
20 | ) 21 | } 22 | } 23 | 24 | 25 | render() { 26 | return ( 27 |
28 |
SquareRoot of a Number
29 |
30 | {this.renderResult()} 31 | {this.renderName()} 32 | {this.renderRun()} 33 |
34 |
35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/nodes/number/SubtractNumbers.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import SubtractNumbersProperties from './SubtractNumbersProperties.jsx'; 3 | import InputNumber from '../InputNumber.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class SubtractNumbers extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Subtract', SubtractNumbersProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'a', 'hasA'), 12 | new InputNumber(this, 1, 'b', 'hasB'), 13 | ]; 14 | this.outputs = [ 15 | new OutputNumber(this, 0, 'Result') 16 | ]; 17 | 18 | this.a = typeof settings.a !== 'undefined' ? settings.a : null; 19 | this.b = typeof settings.b !== 'undefined' ? settings.b : null; 20 | } 21 | 22 | 23 | toJson() { 24 | let json = super.toJson(); 25 | 26 | json.settings.a = this.a; 27 | json.settings.b = this.b; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | if (this.inputs[0].number == null) { 35 | this.a = null; 36 | } else { 37 | this.a = this.inputs[0].number; 38 | } 39 | 40 | if (this.inputs[1].number == null) { 41 | this.b = null; 42 | } else { 43 | this.b = this.inputs[1].number; 44 | } 45 | 46 | if (this.a != null && this.b != null && !isNaN(this.a) && !isNaN(this.b)) { 47 | this.bg.classList.add('running'); 48 | this.runTimer = Date.now(); 49 | this.number = this.a - this.b; 50 | super.run(inputThatTriggered); 51 | } else { 52 | this.runTimer = Date.now(); 53 | this.number = null; 54 | super.run(inputThatTriggered); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /classes/nodes/number/SubtractNumbersProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class SubtractNumbersProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | hasA: props.node.inputs[0].parent ? true : false, 10 | hasB: props.node.inputs[1].parent ? true : false 11 | } 12 | } 13 | 14 | 15 | renderResult() { 16 | if (this.state.hasA && this.state.hasB) { 17 | return ( 18 |
19 | {this.props.node.a} - {this.props.node.b} = {this.props.node.a - this.props.node.b} 20 |
21 | ) 22 | } 23 | } 24 | 25 | 26 | render() { 27 | return ( 28 |
29 |
Subtract Numbers
30 |
31 | {this.renderResult()} 32 | {this.renderName()} 33 | {this.renderRun()} 34 |
35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /classes/nodes/strings/Length.js: -------------------------------------------------------------------------------- 1 | import NodeNumber from '../NodeNumber.js'; 2 | import LengthProperties from './LengthProperties.jsx'; 3 | import InputString from '../InputString.js'; 4 | import OutputNumber from '../OutputNumber.js'; 5 | 6 | export default class Length extends NodeNumber { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'String Length', LengthProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputString(this, 0, 'string', 'String'), 12 | ]; 13 | this.outputs = [ 14 | new OutputNumber(this, 0, 'Result') 15 | ]; 16 | 17 | this.string = typeof settings.string !== 'undefined' ? settings.string : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.string = this.string; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | let str = this.string; 32 | if (this.inputs[0].string != null) { 33 | str = this.inputs[0].string; 34 | } 35 | 36 | if (str != null) { 37 | this.bg.classList.add('running'); 38 | this.runTimer = Date.now(); 39 | this.number = str.length; 40 | super.run(inputThatTriggered); 41 | } else { 42 | this.runTimer = Date.now(); 43 | this.number = null; 44 | super.run(inputThatTriggered); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /classes/nodes/strings/LengthProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class LengthProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Length a String
14 |
15 | {this.renderName()} 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/strings/NumberToString.js: -------------------------------------------------------------------------------- 1 | import NodeString from '../NodeString.js'; 2 | import NumberToStringProperties from './NumberToStringProperties.jsx'; 3 | import OutputString from '../OutputString.js'; 4 | import InputNumber from '../InputNumber.js'; 5 | 6 | export default class NumberToString extends NodeString { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Number to String', NumberToStringProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputNumber(this, 0, 'Number', 'hasNumberInput') 12 | ]; 13 | this.outputs = [ 14 | new OutputString(this, 0, 'Output') 15 | ]; 16 | 17 | this.string = typeof settings.string !== 'undefined' ? settings.string : null; 18 | } 19 | 20 | 21 | toJson() { 22 | let json = super.toJson(); 23 | 24 | json.settings.string = this.string; 25 | 26 | return json; 27 | } 28 | 29 | 30 | run(inputThatTriggered) { 31 | let num = null; 32 | 33 | if (this.inputs[0].number != null) { 34 | num = this.inputs[0].number; 35 | } 36 | 37 | this.bg.classList.add('running'); 38 | this.runTimer = Date.now(); 39 | 40 | if (num != null) { 41 | this.string = num.toString(); 42 | } else { 43 | this.string = null; 44 | } 45 | 46 | super.run(inputThatTriggered); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /classes/nodes/strings/NumberToStringProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class NumberToStringProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Number to String
14 |
15 | {this.renderName()} 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/strings/StringInput.js: -------------------------------------------------------------------------------- 1 | import NodeString from '../NodeString.js'; 2 | import StringInputProperties from './StringInputProperties.jsx'; 3 | import OutputString from '../OutputString.js'; 4 | 5 | export default class StringInput extends NodeString { 6 | constructor(className, graph, x, y, settings) { 7 | super(className, graph, x, y, 'String Input', StringInputProperties, settings); 8 | 9 | this.inputs = []; 10 | this.outputs = [ 11 | new OutputString(this, 0, 'Output') 12 | ]; 13 | 14 | this.string = typeof settings.string !== 'undefined' ? settings.string : 'Nimp'; 15 | } 16 | 17 | 18 | toJson() { 19 | let json = super.toJson(); 20 | 21 | json.settings.string = this.string; 22 | 23 | return json; 24 | } 25 | 26 | 27 | run(inputThatTriggered) { 28 | this.bg.classList.add('running'); 29 | this.runTimer = Date.now(); 30 | super.run(inputThatTriggered); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /classes/nodes/strings/StringInputProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class StringInputProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | stringChange(event) { 11 | const elm = document.getElementById('stringInput'); 12 | this.props.node.string = elm.value; 13 | this.props.node.run(null); 14 | } 15 | 16 | 17 | render() { 18 | return ( 19 |
20 |
String Input
21 | 22 |
23 | String Input
24 | {this.stringChange(event);}} /> 25 | {this.renderName()} 26 | {this.renderRun()} 27 |
28 |
29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /classes/nodes/transform/CropProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class CropProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Crop
11 |
12 | 13 | 14 | 15 | 16 | {this.renderRun()} 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /classes/nodes/transform/Displace.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import DisplaceProperties from './DisplaceProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | import InputNumber from '../InputNumber.js'; 6 | 7 | export default class Displace extends NodeImage { 8 | constructor(className, graph, x, y, settings) { 9 | super(className, graph, x, y, 'Displace', DisplaceProperties, settings); 10 | 11 | this.inputs = [ 12 | new InputImage(this, 0, 'Input'), 13 | new InputImage(this, 1, 'Map'), 14 | new InputNumber(this, 2, 'Offset', 'hasOffsetInput') 15 | ]; 16 | this.outputs = [ 17 | new OutputImage(this, 0, 'Output') 18 | ]; 19 | 20 | this.offset = typeof settings.offset !== 'undefined' ? settings.offset : 20; 21 | } 22 | 23 | 24 | toJson() { 25 | let json = super.toJson(); 26 | 27 | json.settings.offset = this.offset; 28 | 29 | return json; 30 | } 31 | 32 | 33 | run(inputThatTriggered) { 34 | if (this.inputs[0].image && this.inputs[1].image) { 35 | this.bg.classList.add('running'); 36 | this.runTimer = Date.now(); 37 | 38 | let offset = this.offset; 39 | 40 | if (this.inputs[2].number != null) { 41 | offset = this.inputs[2].number; 42 | } 43 | 44 | if (this.isInsideALoop) { 45 | let image = this.inputs[0].image.clone(); 46 | image.displace(this.inputs[1].image, offset); 47 | this.image = image; 48 | super.run(inputThatTriggered); 49 | } else { 50 | Jimp.read(this.inputs[0].image).then(image => { 51 | image.displace(this.inputs[1].image, offset, (error, image) => { 52 | if (error) { 53 | console.log(error); 54 | } else { 55 | this.image = image; 56 | super.run(inputThatTriggered); 57 | } 58 | }) 59 | }) 60 | } 61 | 62 | } else { 63 | this.runTimer = Date.now(); 64 | this.image = null; 65 | super.run(inputThatTriggered); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /classes/nodes/transform/DisplaceProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | import PropertiesInputNumber from '../../../components/PropertiesInputNumber.jsx'; 3 | 4 | 5 | export default class DisplaceProperties extends Properties { 6 | 7 | render() { 8 | return ( 9 |
10 |
Displace
11 |
12 | Displace the image pixels based on the provided displacement map.
13 |
14 | 15 | {this.renderRun()} 16 |
17 |
18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /classes/nodes/transform/FlipHorizontal.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import FlipHorizontalProperties from './FlipHorizontalProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | 6 | export default class FlipHorizontal extends NodeImage { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Flip Horizontally', FlipHorizontalProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputImage(this, 0, 'Input') 12 | ]; 13 | this.outputs = [ 14 | new OutputImage(this, 0, 'Output') 15 | ]; 16 | } 17 | 18 | 19 | run(inputThatTriggered) { 20 | if (this.inputs[0].image) { 21 | this.bg.classList.add('running'); 22 | this.runTimer = Date.now(); 23 | 24 | if (this.isInsideALoop) { 25 | const image = this.inputs[0].image.clone(); 26 | image.flip(true, false); 27 | this.image = image; 28 | super.run(inputThatTriggered); 29 | } else { 30 | Jimp.read(this.inputs[0].image).then(image => { 31 | image.flip(true, false, (error, image) => { 32 | this.image = image; 33 | super.run(inputThatTriggered); 34 | }); 35 | }) 36 | } 37 | 38 | } else { 39 | this.runTimer = Date.now(); 40 | this.image = null; 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /classes/nodes/transform/FlipHorizontalProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class FlipHorizontalProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Flip Horizontally
14 |
15 | {this.renderRun()} 16 |
17 |
18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /classes/nodes/transform/FlipVertical.js: -------------------------------------------------------------------------------- 1 | import NodeImage from '../NodeImage.js'; 2 | import FlipVerticalProperties from './FlipVerticalProperties.jsx'; 3 | import OutputImage from '../OutputImage.js'; 4 | import InputImage from '../InputImage.js'; 5 | 6 | export default class FlipVertical extends NodeImage { 7 | constructor(className, graph, x, y, settings) { 8 | super(className, graph, x, y, 'Flip Vertically', FlipVerticalProperties, settings); 9 | 10 | this.inputs = [ 11 | new InputImage(this, 0, 'Input') 12 | ]; 13 | this.outputs = [ 14 | new OutputImage(this, 0, 'Output') 15 | ]; 16 | } 17 | 18 | 19 | run(inputThatTriggered) { 20 | if (this.inputs[0].image) { 21 | this.bg.classList.add('running'); 22 | this.runTimer = Date.now(); 23 | 24 | if (this.isInsideALoop) { 25 | const image = this.inputs[0].image.clone(); 26 | image.flip(false, true); 27 | this.image = image; 28 | super.run(inputThatTriggered); 29 | } else { 30 | Jimp.read(this.inputs[0].image).then(image => { 31 | image.flip(false, true, (error, image) => { 32 | this.image = image; 33 | super.run(inputThatTriggered); 34 | }); 35 | }) 36 | } 37 | 38 | } else { 39 | this.runTimer = Date.now(); 40 | this.image = null; 41 | super.run(inputThatTriggered); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /classes/nodes/transform/FlipVerticalProperties.jsx: -------------------------------------------------------------------------------- 1 | import Properties from '../Properties.js'; 2 | 3 | export default class FlipVerticalProperties extends Properties { 4 | 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | 10 | render() { 11 | return ( 12 |
13 |
Flip Vertically
14 |
15 | {this.renderRun()} 16 |
17 |
18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /components/JsonView.jsx: -------------------------------------------------------------------------------- 1 | import TopBar from '../components/TopBar.jsx'; 2 | import Head from 'next/head'; 3 | import React from 'react'; 4 | 5 | 6 | export default class JsonView extends React.Component { 7 | render() { 8 | 9 | return ( 10 |
11 |
12 | 13 |
14 | 15 |

16 |
{JSON.stringify(this.props.graphToLoad.graph, null, 2) }
17 |
18 |
19 | 20 | 25 |
26 | ) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /deploy.bat: -------------------------------------------------------------------------------- 1 | docker login registry.gitlab.com -u danphi 2 | docker build -t registry.gitlab.com/danphi/nimp --platform linux/amd64 . 3 | docker push registry.gitlab.com/danphi/nimp 4 | ssh dan@host.winston.net "cd ~/server; docker compose pull; docker compose up -d" -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | docker login registry.gitlab.com -u danphi 2 | docker build -t registry.gitlab.com/danphi/nimp --platform linux/amd64 . 3 | docker push registry.gitlab.com/danphi/nimp 4 | ssh dan@host.winston.net "cd ~/server; docker compose pull; docker compose up -d" 5 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const withCSS = require('@zeit/next-css') 2 | 3 | module.exports = withCSS({ 4 | publicRuntimeConfig: {} 5 | }) 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Nimp", 3 | "version": "0.1.0", 4 | "description": "Node-based image manipulation program.", 5 | "main": "index.js", 6 | "scripts": { 7 | "next": "next dev", 8 | "dev": "cross-env API_URL=http://localhost:3000 MONGO_URL=mongodb://localhost NODE_ENV=development nodemon server/index.js --watch server", 9 | "build": "next build", 10 | "start": "NODE_ENV=production node server/index.js" 11 | }, 12 | "author": "Dan", 13 | "license": "MIT", 14 | "dependencies": { 15 | "@zeit/next-css": "^1.0.1", 16 | "bcryptjs": "^2.4.3", 17 | "body-parser": "^1.19.0", 18 | "bson-objectid": "^1.3.0", 19 | "connect-mongodb-session": "^3.1.1", 20 | "copy-to-clipboard": "^3.2.0", 21 | "express": "^4.17.1", 22 | "express-session": "^1.17.0", 23 | "isomorphic-unfetch": "^3.0.0", 24 | "jimp": "0.8.5", 25 | "lodash.debounce": "^4.0.8", 26 | "mongodb": "^3.3.3", 27 | "next": "^13.3.0", 28 | "normalize.css": "^8.0.1", 29 | "react": "^18.2.0", 30 | "react-dom": "^18.2.0", 31 | "simplex-noise": "^2.4.0", 32 | "tinycolor2": "^1.4.1", 33 | "worley-noise": "^2.0.1" 34 | }, 35 | "devDependencies": { 36 | "cross-env": "^6.0.3", 37 | "nodemon": "^2.0.21" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pages/g/[graphId].js: -------------------------------------------------------------------------------- 1 | import GraphView from '../../components/GraphView.jsx'; 2 | import Head from 'next/head'; 3 | import MainLayout from '../../layouts/MainLayout.js'; 4 | import React from 'react'; 5 | 6 | export default class G extends React.Component { 7 | static async getInitialProps({req, res, query}) { 8 | const user = req && req.session && req.session.user ? req.session.user : null; 9 | 10 | let graph = null; 11 | if (query.graphId) { 12 | let body = {graphId: query.graphId, userId: null}; 13 | if (user) { 14 | body.userId = user._id; 15 | } 16 | 17 | const result = await fetch(process.env.API_URL + '/api/graph', { 18 | method: 'post', 19 | headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json' }, 20 | body: JSON.stringify(body) 21 | }); 22 | 23 | if (result.status == 200) { 24 | graph = await result.json(); 25 | } else { 26 | if (result.status == 404) { 27 | res.statusCode = 404; 28 | res.end('Graph not found with this id.'); 29 | return; 30 | } else if (result.status == 401) { 31 | res.statusCode = 401; 32 | res.end('Graph is private.'); 33 | return; 34 | } else { 35 | res.statusCode = 500; 36 | res.end('Invalid graph id.'); 37 | return; 38 | } 39 | } 40 | } 41 | 42 | return {user:user, graph:graph}; 43 | } 44 | 45 | render() { 46 | return ( 47 | 48 | {this.props.graph && ( 49 | 50 | {this.props.graph.title} - Nimp 51 | 52 | 53 | )} 54 | 55 | 56 | ) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pages/g/[graphId]/[slug].js: -------------------------------------------------------------------------------- 1 | import GraphView from '../../../components/GraphView.jsx'; 2 | import Head from 'next/head'; 3 | import MainLayout from '../../../layouts/MainLayout.js'; 4 | import React from 'react'; 5 | 6 | export default class Slug extends React.Component { 7 | static async getInitialProps({req, res, query}) { 8 | const user = req && req.session && req.session.user ? req.session.user : null; 9 | 10 | let graph = null; 11 | if (query.graphId) { 12 | let body = {graphId: query.graphId, userId: null}; 13 | if (user) { 14 | body.userId = user._id; 15 | } 16 | 17 | const result = await fetch(process.env.API_URL + '/api/graph', { 18 | method: 'post', 19 | headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json' }, 20 | body: JSON.stringify(body) 21 | }); 22 | 23 | if (result.status == 200) { 24 | graph = await result.json(); 25 | } else { 26 | if (result.status == 404) { 27 | res.statusCode = 404; 28 | res.end('Graph not found with this id.'); 29 | return; 30 | } else if (result.status == 401) { 31 | res.statusCode = 401; 32 | res.end('Graph is private.'); 33 | return; 34 | } else { 35 | res.statusCode = 500; 36 | res.end('Invalid graph id.'); 37 | return; 38 | } 39 | } 40 | } 41 | 42 | return {user:user, graph:graph}; 43 | } 44 | 45 | render() { 46 | return ( 47 | 48 | {this.props.graph && ( 49 | 50 | {this.props.graph.title} - Nimp 51 | 52 | 53 | )} 54 | 55 | 56 | ) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pages/graphs.jsx: -------------------------------------------------------------------------------- 1 | import MainLayout from '../layouts/MainLayout.js'; 2 | import TopBar from '../components/TopBar.jsx'; 3 | import fetch from 'isomorphic-unfetch' 4 | import GraphThumbs from '../components/GraphThumbs.jsx'; 5 | import React from 'react'; 6 | 7 | 8 | export default class Graphs extends React.Component { 9 | 10 | static async getInitialProps({req, query}) { 11 | const user = req && req.session && req.session.user ? req.session.user : null; 12 | 13 | let graphs = []; 14 | 15 | const result = await fetch(process.env.API_URL + '/api/graphs', { 16 | method: 'get', 17 | headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json' } 18 | }); 19 | 20 | if (result.status == 200) { 21 | graphs = await result.json(); 22 | } 23 | 24 | return {user:user, graphs:graphs}; 25 | } 26 | 27 | 28 | constructor(props) { 29 | super(props); 30 | } 31 | 32 | 33 | render() { 34 | return ( 35 |
36 | 37 | 38 |
39 | 40 |
41 |
42 | 43 | 48 |
49 | ) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import GraphView from '../components/GraphView.jsx'; 2 | import MainLayout from '../layouts/MainLayout.js'; 3 | import React from 'react'; 4 | 5 | export default class Index extends React.Component { 6 | static async getInitialProps({req, query}) { 7 | const user = req && req.session && req.session.user ? req.session.user : null; 8 | return {user:user, graph:null}; 9 | } 10 | 11 | render() { 12 | return ( 13 | 14 | 15 | 16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pages/json/[graphId].js: -------------------------------------------------------------------------------- 1 | import JsonView from '../../components/JsonView.jsx'; 2 | import Head from 'next/head'; 3 | import MainLayout from '../../layouts/MainLayout.js'; 4 | import React from 'react'; 5 | 6 | 7 | export default class Json extends React.Component { 8 | static async getInitialProps({req, res, query}) { 9 | const user = req && req.session && req.session.user ? req.session.user : null; 10 | 11 | let graph = null; 12 | if (query.graphId) { 13 | let body = {graphId: query.graphId, userId: null}; 14 | if (user) { 15 | body.userId = user._id; 16 | } 17 | 18 | const result = await fetch(process.env.API_URL + '/api/graph', { 19 | method: 'post', 20 | headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json' }, 21 | body: JSON.stringify(body) 22 | }); 23 | 24 | if (result.status == 200) { 25 | graph = await result.json(); 26 | } else { 27 | if (result.status == 404) { 28 | res.statusCode = 404; 29 | res.end('Graph not found with this id.'); 30 | return; 31 | } else if (result.status == 401) { 32 | res.statusCode = 401; 33 | res.end('Graph is private.'); 34 | return; 35 | } else { 36 | res.statusCode = 500; 37 | res.end('Invalid graph id.'); 38 | return; 39 | } 40 | } 41 | } 42 | 43 | return {user:user, graph:graph}; 44 | } 45 | 46 | render() { 47 | return ( 48 | 49 | {this.props.graph && ( 50 | 51 | {this.props.graph.title} - Nimp 52 | 53 | 54 | )} 55 | 56 | 57 | ) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pages/json/[graphId]/[slug].js: -------------------------------------------------------------------------------- 1 | import JsonView from '../../../components/JsonView.jsx'; 2 | import Head from 'next/head'; 3 | import MainLayout from '../../../layouts/MainLayout.js'; 4 | import React from 'react'; 5 | 6 | 7 | export default class JsonSlug extends React.Component { 8 | static async getInitialProps({req, res, query}) { 9 | const user = req && req.session && req.session.user ? req.session.user : null; 10 | 11 | let graph = null; 12 | if (query.graphId) { 13 | let body = {graphId: query.graphId, userId: null}; 14 | if (user) { 15 | body.userId = user._id; 16 | } 17 | 18 | const result = await fetch(process.env.API_URL + '/api/graph', { 19 | method: 'post', 20 | headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json' }, 21 | body: JSON.stringify(body) 22 | }); 23 | 24 | if (result.status == 200) { 25 | graph = await result.json(); 26 | } else { 27 | if (result.status == 404) { 28 | res.statusCode = 404; 29 | res.end('Graph not found with this id.'); 30 | return; 31 | } else if (result.status == 401) { 32 | res.statusCode = 401; 33 | res.end('Graph is private.'); 34 | return; 35 | } else { 36 | res.statusCode = 500; 37 | res.end('Invalid graph id.'); 38 | return; 39 | } 40 | } 41 | } 42 | 43 | return {user:user, graph:graph}; 44 | } 45 | 46 | render() { 47 | return ( 48 | 49 | {this.props.graph && ( 50 | 51 | {this.props.graph.title} - Nimp 52 | 53 | 54 | )} 55 | 56 | 57 | ) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pages/logout.jsx: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-unfetch'; 2 | import MainLayout from '../layouts/MainLayout.js'; 3 | import React from 'react'; 4 | 5 | export default class Logout extends React.Component { 6 | 7 | static async getInitialProps({req, query}) { 8 | const user = req && req.session && req.session.user ? req.session.user : null; 9 | return {user:user}; 10 | } 11 | 12 | 13 | componentDidMount() { 14 | fetch('/api/logout', { 15 | method: 'get', 16 | headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json'} 17 | }).then(result => { 18 | window.location.href = '/'; 19 | }) 20 | } 21 | 22 | 23 | render() { 24 | return ( 25 |
26 | 27 | Logging out... 28 | 29 |
30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pages/u/[userId].js: -------------------------------------------------------------------------------- 1 | import MainLayout from '../../layouts/MainLayout.js'; 2 | import TopBar from '../../components/TopBar.jsx'; 3 | import fetch from 'isomorphic-unfetch' 4 | import GraphThumbs from '../../components/GraphThumbs.jsx'; 5 | import React from 'react'; 6 | 7 | 8 | export default class UserGraphs extends React.Component { 9 | 10 | static async getInitialProps({req, query}) { 11 | const user = req && req.session && req.session.user ? req.session.user : null; 12 | 13 | let graphs = []; 14 | 15 | let isUser = false; 16 | if (user && user._id == query.userId) { 17 | isUser = true; 18 | } 19 | 20 | const result = await fetch(process.env.API_URL + '/api/usergraphs', { 21 | method: 'post', 22 | headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json' }, 23 | body: JSON.stringify({userId:query.userId, isUser:isUser}) 24 | }); 25 | 26 | if (result.status == 200) { 27 | graphs = await result.json(); 28 | } 29 | 30 | return {user:user, graphs:graphs}; 31 | } 32 | 33 | 34 | constructor(props) { 35 | super(props); 36 | } 37 | 38 | 39 | render() { 40 | return ( 41 |
42 | 43 | 44 |
45 | 46 |
47 |
48 | 49 | 54 |
55 | ) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /public/static/discordIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/discordIcon.png -------------------------------------------------------------------------------- /public/static/discordIcon_highres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/discordIcon_highres.png -------------------------------------------------------------------------------- /public/static/emailIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/emailIcon.png -------------------------------------------------------------------------------- /public/static/emailIcon_highres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/emailIcon_highres.png -------------------------------------------------------------------------------- /public/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/favicon.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-10-black/open-sans-10-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-10-black/open-sans-10-black.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-12-black/open-sans-12-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-12-black/open-sans-12-black.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-128-black/open-sans-128-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-128-black/open-sans-128-black.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-128-white/open-sans-128-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-128-white/open-sans-128-white.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-14-black/open-sans-14-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-14-black/open-sans-14-black.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-16-black/open-sans-16-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-16-black/open-sans-16-black.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-16-white/open-sans-16-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-16-white/open-sans-16-white.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-32-black/open-sans-32-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-32-black/open-sans-32-black.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-32-white/open-sans-32-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-32-white/open-sans-32-white.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-64-black/open-sans-64-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-64-black/open-sans-64-black.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-64-white/open-sans-64-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-64-white/open-sans-64-white.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-8-black/open-sans-8-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-8-black/open-sans-8-black.png -------------------------------------------------------------------------------- /public/static/fonts/open-sans/open-sans-8-white/open-sans-8-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/fonts/open-sans/open-sans-8-white/open-sans-8-white.png -------------------------------------------------------------------------------- /public/static/githubLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/githubLogo.png -------------------------------------------------------------------------------- /public/static/githubLogo_highres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/githubLogo_highres.png -------------------------------------------------------------------------------- /public/static/og.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/og.jpg -------------------------------------------------------------------------------- /public/static/patreonIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/patreonIcon.png -------------------------------------------------------------------------------- /public/static/patreonIcon_highres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/patreonIcon_highres.png -------------------------------------------------------------------------------- /public/static/screenshots/screenshot01.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/screenshots/screenshot01.JPG -------------------------------------------------------------------------------- /public/static/screenshots/screenshot02.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/screenshots/screenshot02.JPG -------------------------------------------------------------------------------- /public/static/screenshots/screenshot03.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/screenshots/screenshot03.JPG -------------------------------------------------------------------------------- /public/static/trelloIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/trelloIcon.png -------------------------------------------------------------------------------- /public/static/trelloIcon_highres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/trelloIcon_highres.png -------------------------------------------------------------------------------- /public/static/twitterIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/twitterIcon.png -------------------------------------------------------------------------------- /public/static/twitterIcon_highres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/twitterIcon_highres.png -------------------------------------------------------------------------------- /public/static/viewportBg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan335/nimp/5101262701580b60fb73d97ae8aaab8d8eade839/public/static/viewportBg.jpg -------------------------------------------------------------------------------- /server/api/deletegraph.js: -------------------------------------------------------------------------------- 1 | var ObjectId = require('bson-objectid'); 2 | 3 | 4 | module.exports = function(app) { 5 | app.post('/api/deletegraph', (req, res, next) => { 6 | if (!req.session.user) { 7 | return res.status(500).end(); 8 | } 9 | 10 | if (req.body.graphId.length != 24) { 11 | return res.status(500).end(); 12 | } 13 | 14 | const graphId = new ObjectId(req.body.graphId); 15 | 16 | const collection = req.app.locals.db.collection('graphs'); 17 | 18 | collection.deleteOne({userId:req.session.user._id, _id:graphId}, {}, (error, result) => { 19 | if (error) { 20 | res.status(500).end(); 21 | } else { 22 | res.status(200).end(); 23 | } 24 | }) 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /server/api/graph.js: -------------------------------------------------------------------------------- 1 | var ObjectId = require('mongodb').ObjectID; 2 | 3 | 4 | module.exports = function(app) { 5 | app.post('/api/graph', (req, res, next) => { 6 | if (req.body.graphId.length != 24) { 7 | return res.status(500).end('Invalid graph id.'); 8 | } 9 | 10 | const collection = req.app.locals.db.collection('graphs'); 11 | const graphId = new ObjectId(req.body.graphId); 12 | 13 | collection.findOne({_id:graphId}, {projection:{thumbnail:0}}, (error, graph) => { 14 | if (error) { 15 | return res.status(500).end('Error finding graph.'); 16 | } else { 17 | if (!graph) { 18 | res.status(404).end('No graph found.'); 19 | } else { 20 | if (graph.isPublic) { 21 | res.json(graph).end(); 22 | } else { 23 | if (graph.userId != req.body.userId) { 24 | return res.status(401).end('Not authorized.'); 25 | } else { 26 | return res.json(graph).end(); 27 | } 28 | } 29 | } 30 | } 31 | }) 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /server/api/graphs.js: -------------------------------------------------------------------------------- 1 | var ObjectId = require('bson-objectid'); 2 | 3 | 4 | module.exports = function(app) { 5 | app.get('/api/graphs', (req, res, next) => { 6 | const collection = req.app.locals.db.collection('graphs'); 7 | const cursor = collection.find({isPublic:true}, {sort:{updatedAt:-1}, projection:{userId:1, username:1, views:1, thumbnail:1, title:1, url:1}}); 8 | cursor.toArray((error, graphs) => { 9 | if (error) { 10 | console.log(error); 11 | res.status(500).end(); 12 | } else { 13 | res.json(graphs).end(); 14 | } 15 | }) 16 | }) 17 | 18 | 19 | app.post('/api/usergraphs', (req, res, next) => { 20 | const collection = req.app.locals.db.collection('graphs'); 21 | 22 | if (req.body.userId.length != 24) { 23 | return res.status(500).end(); 24 | } 25 | 26 | const userId = new ObjectId(req.body.userId); 27 | 28 | let find = {userId:userId}; 29 | 30 | if (!req.body.isUser) { 31 | find.isPublic = true; 32 | } 33 | 34 | const cursor = collection.find(find, {sort:{updatedAt:-1}, projection:{userId:1, username:1, views:1, thumbnail:1, title:1, url:1}}); 35 | cursor.toArray((error, graphs) => { 36 | if (error) { 37 | console.log(error); 38 | res.status(500).end(); 39 | } else { 40 | res.json(graphs).end(); 41 | } 42 | }) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /server/api/login.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require('bcryptjs'); 2 | 3 | 4 | module.exports = function(app) { 5 | app.post('/api/login', (req, res, next) => { 6 | if (!req.body.email || !req.body.email.length) { 7 | return res.status(500).send('Email address required.'); 8 | } 9 | 10 | if (!req.body.password || !req.body.password.length) { 11 | return res.status(500).send('Password required.'); 12 | } 13 | 14 | const collection = req.app.locals.db.collection('users'); 15 | 16 | collection.findOne({email:req.body.email.toLowerCase().trim()}, {}, (error, user) => { 17 | if (error) { 18 | return res.status(500).send('Error finding user.'); 19 | } else { 20 | if (user) { 21 | 22 | bcrypt.compare(req.body.password, user.password, (error, result) => { 23 | if (error) { 24 | return res.status(500).send('Error comparing passwords.'); 25 | } else { 26 | if (result == true) { 27 | req.session.user = user; 28 | return res.status(200).end(); 29 | } else { 30 | return res.status(500).send('Wrong password.'); 31 | } 32 | } 33 | }) 34 | } else { 35 | return res.status(500).send('Email not found.'); 36 | } 37 | } 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /server/api/logout.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | app.get('/api/logout', (req, res, next) => { 3 | delete req.session.user; 4 | res.status(200).end(); 5 | }) 6 | } 7 | -------------------------------------------------------------------------------- /server/functions.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stringToSlug(text) { 3 | const a = 'àáäâèéëêìíïîòóöôùúüûñçßÿœæŕśńṕẃǵǹḿǘẍźḧ·/_,:;' 4 | const b = 'aaaaeeeeiiiioooouuuuncsyoarsnpwgnmuxzh------' 5 | const p = new RegExp(a.split('').join('|'), 'g') 6 | 7 | return text.toString().toLowerCase() 8 | .replace(/\s+/g, '-') // Replace spaces with - 9 | .replace(p, c => 10 | b.charAt(a.indexOf(c))) // Replace special chars 11 | .replace(/&/g, '-and-') // Replace & with 'and' 12 | .replace(/[^\w\-]+/g, '') // Remove all non-word chars 13 | .replace(/\-\-+/g, '-') // Replace multiple - with single - 14 | .replace(/^-+/, '') // Trim - from start of text 15 | .replace(/-+$/, '') // Trim - from end of text 16 | }, 17 | } 18 | --------------------------------------------------------------------------------