diff --git a/README.md b/README.md index df6fb90..7d8b72d 100644 --- a/README.md +++ b/README.md @@ -6,31 +6,154 @@ [![Platform Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fyukiny0811%2Feasy-node-editor%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/yukiny0811/easy-node-editor) [![License](https://img.shields.io/github/license/yukiny0811/easy-node-editor)](https://github.com/yukiny0811/easy-node-editor/blob/main/LICENSE) -## Sample Code +## Usage1 - Standard output node +### Step 1. Create your node +Create a class, and inherit it from NodeModelBase class. ```.swift -import SwiftUI -import EasyNodeEditor +class YourOutputNode: NodeModelBase { +} +``` +### Step 2. Create Outputs +Create output variables. +Put ```@Output``` for output variables. +Make sure you put ```@objc``` wrapper to all your output variables. +There are no restrictions on the naming of the variables. Name the variables whatever you want, and the library will automatically use that name to display in the node. +```.swift +class YourOutputNode: NodeModelBase { + @objc @Output var output: Int = 3 +} +``` + +### Step 3. Register your node +Register your node when you instanciate EasyNodeEditor View. +```.swift struct ContentView: View { var body: some View { - EasyNodeEditor(nodeTypes: [Output3Node.self, MultiplyNode.self, ShowNode.self, AddAllNode.self]) + EasyNodeEditor(nodeTypes: [YourOutputNode.self]) } } +``` -class Output3Node: NodeModelBase { - @objc @Output var output: Int = 3 - override func displayTitle() -> String { - return "Outputs 3" +**This is it!** +The EasyNodeEditor Library will create a node like this. + +image + +## Usage2 - Standard input and output node + +### Step 1. Create your node +Create a class, and inherit it from NodeModelBase class. +```.swift +class YourIONode: NodeModelBase { +} +``` + +### Step 2. Create Inputs and Outputs +Create inputs and/or outputs. +Put ```@Input``` for input variables, and ```@Output``` for output variables. +Make sure you put ```@objc``` wrapper to all your input and output variables. +There are no restrictions on the naming of the variables. Name the variables whatever you want, and the library will automatically use that name to display in the node. +```.swift +class YourIONode: NodeModelBase { + @objc @Input var input: Int = 0 + @objc @Output var output: Int = 0 +} +``` + +### Step 3. Define what happens when input value changes +Override ```processOnChange()``` function, and define your process. +Don't change input value inside ```processOnChange()```. It will start infinite loop. +```.swift +class YourIONode: NodeModelBase { + @objc @Input var input: Int = 0 + @objc @Output var output: Int = 0 + override func processOnChange() { + output = input * 5 } } +``` -class MultiplyNodeSubModel: ObservableObject { +### Step 4. Register your node +Register your node when you instanciate EasyNodeEditor View. +```.swift +struct ContentView: View { + var body: some View { + EasyNodeEditor(nodeTypes: [YourOutputNode.self, YoutIONode.self]) + } +} +``` + +**Very easy!!** +The EasyNodeEditor Library will create a node like this. + +image + +## Usage 3 - Standard display node + +### Step 1. Create your node +Create a class, and inherit it from NodeModelBase class. +```.swift +class YourDisplayNode: NodeModelBase { +} +``` + +### Step 2. Create Inputs +Create inputs. +Put ```@Input``` for input variables. +Make sure you put ```@objc``` wrapper to all your input variables. +There are no restrictions on the naming of the variables. Name the variables whatever you want, and the library will automatically use that name to display in the node. +```.swift +class YourDisplayNode: NodeModelBase { + @objc @Input var input: Int = 0 +} +``` + +### Step3. Create View +Override ```middleContent()``` function, and define your View. +```.swift +class YourDisplayNode: NodeModelBase { + @objc @Input var input: Int = 0 + override func middleContent() -> AnyView { + return AnyView( + Group { + Text("number is now -> \(input)") + } + ) + } +} +``` + +### Step 4. Register your node +Register your node when you instanciate EasyNodeEditor View. +```.swift +struct ContentView: View { + var body: some View { + EasyNodeEditor(nodeTypes: [YourOutputNode.self, YoutIONode.self, YourDisplayNode.self]) + } +} +``` + +**Amazing!!** +The EasyNodeEditor Library will create a node like this. + +image + +## Usage 4 - Standard Interactive Node + +I assume you have read Usage 1 ~ 3 here. +For interactive nodes, EasyNodeEditor provides ```@Middle``` property wrapper. +Whenever the value of the variables with ```@Input``` or ```@Middle``` changes, ```processOnChange()``` function will fire. +If you need a binding object for interaction, create a class which inherits ```ObservableObject``` and define a ```@Published``` variable inside. Variables defined directly inside your node class will not be bindable. +After finished making, register your node as usual. +```.swift +class YourInteractiveNodeSubModel: ObservableObject { @Published var sliderValue: Double = 0.0 } -class MultiplyNode: NodeModelBase { +class YourInteractiveNode: NodeModelBase { @objc @Input var input: Int = 0 - @ObservedObject var subModel = MultiplyNodeSubModel() + @ObservedObject var subModel = YourInteractiveNodeSubModel() @objc @Middle var count: Int = 0 @objc @Output var output: Int = 0 override func processOnChange() { @@ -48,28 +171,69 @@ class MultiplyNode: NodeModelBase { ) } } +``` -class AddAllNode: NodeModelBase { - @objc @Input var input1: Int = 0 - @objc @Input var input2: Int = 0 - @objc @Output var output: Int = 0 - override func processOnChange() { - output = input1 + input2 +**Simple!!** +The EasyNodeEditor Library will create a node like this. + +![GIF 2022-09-17 at 8 21 30 PM](https://user-images.githubusercontent.com/28947703/190854074-28cd9699-70a1-4dca-b480-6f69f9cb43f6.gif) + +## Full Sample Code + +```.swift +import SwiftUI +import EasyNodeEditor + +struct ContentView: View { + var body: some View { + EasyNodeEditor(nodeTypes: [YourOutputNode.self, YourIONode.self, YourDisplayNode.self, YourInteractiveNode.self]) } } -class ShowNode: NodeModelBase { - @objc @Input var myinput: Int = 0 - var showCount = 0 +class YourOutputNode: NodeModelBase { + @objc @Output var output: Int = 3 +} + +class YourIONode: NodeModelBase { + @objc @Input var input: Int = 0 + @objc @Output var output: Int = 0 override func processOnChange() { - showCount = myinput + output = input * 5 } +} + +class YourDisplayNode: NodeModelBase { + @objc @Input var input: Int = 0 override func middleContent() -> AnyView { return AnyView( Group { - Text("number is now -> \(showCount)") + Text("number is now -> \(input)") } ) } } + +class YourInteractiveNodeSubModel: ObservableObject { + @Published var sliderValue: Double = 0.0 +} +class YourInteractiveNode: NodeModelBase { + @objc @Input var input: Int = 0 + @ObservedObject var subModel = YourInteractiveNodeSubModel() + @objc @Middle var count: Int = 0 + @objc @Output var output: Int = 0 + override func processOnChange() { + output = input * count + } + override func middleContent() -> AnyView { + return AnyView( + Group { + Slider(value: self.$subModel.sliderValue, in: 0...100, onEditingChanged: { changed in + self.count = Int(self.subModel.sliderValue) + }) + } + .frame(minWidth: 200, maxWidth: 200) + .fixedSize() + ) + } +} ```