Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 63 additions & 27 deletions Demo/Shared/ContentView.swift
Original file line number Diff line number Diff line change
@@ -1,57 +1,93 @@
import Flow
import SwiftUI
import Combine

func simplePatch() -> Patch {
let generator = Node(name: "generator", titleBarColor: Color.cyan, outputs: ["out"])
let processor = Node(name: "processor", titleBarColor: Color.red, inputs: ["in"], outputs: ["out"])
let mixer = Node(name: "mixer", titleBarColor: Color.gray, inputs: ["in1", "in2"], outputs: ["out"])
let output = Node(name: "output", titleBarColor: Color.purple, inputs: ["in"])

let nodes = [generator, processor, generator, processor, mixer, output]

let wires = Set([Wire(from: OutputID(0, 0), to: InputID(1, 0)),
Wire(from: OutputID(1, 0), to: InputID(4, 0)),
Wire(from: OutputID(2, 0), to: InputID(3, 0)),
Wire(from: OutputID(3, 0), to: InputID(4, 1)),
Wire(from: OutputID(4, 0), to: InputID(5, 0))])

var patch = Patch(nodes: nodes, wires: wires)
patch.recursiveLayout(nodeIndex: 5, at: CGPoint(x: 800, y: 50))
func simplePatch() -> Patch {
let int1 = IntNode(name: "Integer 1")
let int2 = IntNode(name: "Integer 2")

let nodes = [int1, int2]

let wires = Set([
Wire(from: OutputID(int1, \.[0]), to: InputID(int2, \.[0]))
])

let patch = Patch(nodes: nodes, wires: wires)
patch.recursiveLayout(nodeId: int2.id, at: CGPoint(x: 600, y: 50))
return patch
}

/// Bit of a stress test to show how Flow performs with more nodes.
func randomPatch() -> Patch {
var randomNodes: [Node] = []
var randomNodes: [BaseNode] = []

for n in 0 ..< 50 {
let randomPoint = CGPoint(x: 1000 * Double.random(in: 0 ... 1),
y: 1000 * Double.random(in: 0 ... 1))
randomNodes.append(Node(name: "node\(n)",
position: randomPoint,
inputs: ["In"],
outputs: ["Out"]))
randomNodes.append(IntNode(name: "Integer \(n)", position: randomPoint))
}

var randomWires: Set<Wire> = []
for n in 0 ..< 50 {
randomWires.insert(Wire(from: OutputID(n, 0), to: InputID(Int.random(in: 0 ... 49), 0)))
randomWires.insert(
Wire(
from: OutputID(randomNodes[n], \.[0]),
to: InputID(randomNodes[Int.random(in: 0 ... 49)], \.[0])
)
)
}
return Patch(nodes: randomNodes, wires: randomWires)
}

struct ContentView: View {
@State var patch = simplePatch()
@State var selection = Set<NodeIndex>()
@StateObject var patch = simplePatch()
@State var selection = Set<NodeId>()

func addNode() {
let newNode = Node(name: "processor", titleBarColor: Color.red, inputs: ["in"], outputs: ["out"])
func addNode(type: DemoNodeType) {
let newNode: BaseNode
switch type {
case .integer:
newNode = IntNode(name: "Integer")
case .string:
let stringNode = StringNode(name: "")
stringNode.setValue("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent at tortor egestas ante ultricies lobortis. Cras fringilla, turpis id volutpat mollis, ligula metus egestas ante, sed fringilla ex sapien in elit.")
newNode = stringNode
case .trigger:
newNode = TriggerButtonNode(name: "Trigger")
}
patch.nodes.append(newNode)
}

var body: some View {
ZStack(alignment: .topTrailing) {
NodeEditor(patch: $patch, selection: $selection)
Button("Add Node", action: addNode).padding()
NodeEditor(patch: patch, selection: $selection)
.onWireAdded { wire in
print("Added wire: \(wire)")
}
.onWireRemoved { wire in
print("Removed wire: \(wire)")
}

Menu("Add node") {
Button(action: { addNode(type: .integer) }) {
Label("Add Integer Node", systemImage: "number")
}

Button(action: { addNode(type: .string) }) {
Label("Add Text Node", systemImage: "textformat")
}

Button(action: { addNode(type: .trigger) }) {
Label("Add Trigger Node", systemImage: "button.programmable")
}
}
.padding()
}
}

}

enum DemoNodeType {
case integer, string, trigger
}
42 changes: 12 additions & 30 deletions Flow.playground/Contents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,26 @@ import PlaygroundSupport
import SwiftUI

func simplePatch() -> Patch {
let midiSource = Node(name: "MIDI source",
outputs: [
Port(name: "out ch. 1", type: .midi),
Port(name: "out ch. 2", type: .midi),
])
let generator = Node(name: "generator",
inputs: [
Port(name: "midi in", type: .midi),
Port(name: "CV in", type: .control),
],
outputs: [Port(name: "out")])
let processor = Node(name: "processor", inputs: ["in"], outputs: ["out"])
let mixer = Node(name: "mixer", inputs: ["in1", "in2"], outputs: ["out"])
let output = Node(name: "output", inputs: ["in"])

let nodes = [midiSource, generator, processor, generator, processor, mixer, output]

let int1 = IntNode(name: "Integer 1")
let int2 = IntNode(name: "Integer 2")

let nodes = [int1, int2]

let wires = Set([
Wire(from: OutputID(0, 0), to: InputID(1, 0)),
Wire(from: OutputID(0, 1), to: InputID(3, 0)),
Wire(from: OutputID(1, 0), to: InputID(2, 0)),
Wire(from: OutputID(2, 0), to: InputID(5, 0)),
Wire(from: OutputID(3, 0), to: InputID(4, 0)),
Wire(from: OutputID(4, 0), to: InputID(5, 1)),
Wire(from: OutputID(5, 0), to: InputID(6, 0)),
Wire(from: OutputID(int1, \.[0]), to: InputID(int2, \.[0]))
])

var patch = Patch(nodes: nodes, wires: wires)
patch.recursiveLayout(nodeIndex: 6, at: CGPoint(x: 1000, y: 50))
let patch = Patch(nodes: nodes, wires: wires)
patch.recursiveLayout(nodeId: int2.id, at: CGPoint(x: 600, y: 50))
return patch
}

struct FlowDemoView: View {
@State var patch = simplePatch()
@State var selection = Set<NodeIndex>()
@StateObject var patch = simplePatch()
@State var selection = Set<NodeId>()

public var body: some View {
NodeEditor(patch: $patch, selection: $selection)
NodeEditor(patch: patch, selection: $selection)
.nodeColor(.secondary)
.portColor(for: .control, .gray)
.portColor(for: .signal, Gradient(colors: [.yellow, .blue]))
Expand Down
2 changes: 2 additions & 0 deletions Sources/Flow/Model/LayoutConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public struct LayoutConstants {
public var nodeTitleFont = Font.title
public var portNameFont = Font.caption
public var nodeCornerRadius: CGFloat = 5
public var backgroundlinesSpacing: CGFloat = 50
public var backgroundlinesPattern: [CGFloat] = [4,4]

public init() {}
}
32 changes: 0 additions & 32 deletions Sources/Flow/Model/Node+Gestures.swift

This file was deleted.

15 changes: 1 addition & 14 deletions Sources/Flow/Model/Node+Layout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,11 @@ import Foundation
public extension Node {
/// Calculates the bounding rectangle for a node.
func rect(layout: LayoutConstants) -> CGRect {
let position = position ?? .zero
let maxio = CGFloat(max(inputs.count, outputs.count))
let size = CGSize(width: layout.nodeWidth,
height: CGFloat((maxio * (layout.portSize.height + layout.portSpacing)) + layout.nodeTitleHeight + layout.portSpacing))

return CGRect(origin: position, size: size)
}

/// Calculates the bounding rectangle for an input port (not including the name).
func inputRect(input: PortIndex, layout: LayoutConstants) -> CGRect {
let y = layout.nodeTitleHeight + CGFloat(input) * (layout.portSize.height + layout.portSpacing) + layout.portSpacing
return CGRect(origin: position + CGSize(width: layout.portSpacing, height: y),
size: layout.portSize)
}

/// Calculates the bounding rectangle for an output port (not including the name).
func outputRect(output: PortIndex, layout: LayoutConstants) -> CGRect {
let y = layout.nodeTitleHeight + CGFloat(output) * (layout.portSize.height + layout.portSpacing) + layout.portSpacing
return CGRect(origin: position + CGSize(width: layout.nodeWidth - layout.portSpacing - layout.portSize.width, height: y),
size: layout.portSize)
}
}
Loading