-
Notifications
You must be signed in to change notification settings - Fork 1
/
Node.py
368 lines (296 loc) · 10.7 KB
/
Node.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
import Port as port
class Node(object):
def __init__(self):
"""
type: The type of node that this node is.
id: is the nodes instance id, this should be unique for each node in the graph, and is not serialized.
name: The name of this node, allowing users to rename there node
portsIn: List of input ports
portsOut: List of output ports
dirty: if the node has had some values updated on it, then it gets flagged as dirty
"""
self.type = ""
self.id = -1
self.name = ""
self.portsIn = []
self.portsOut = []
self._dirty = True
self.initInputPorts()
self.initOutputPorts()
"""
dirty property
This property is used to check if the node contains any ports
that are dirty, or connection to nodes that are dirty.
How to clean the node:
When a node is evaluated, all input ports should be checked to
see if they are clean, if they are all clean then set the node
to be clean by setting the self._dirty = true
"""
@property
def dirty(self):
return self._dirty
@dirty.setter
def dirty(self, val):
# if setting the node to be dirty, all conncted nodes up stream must be have there
# connected inputs set to dirty as well.
if self._dirty == False and val:
for port in self.portsOut:
for edgePort in port.edges:
edgePort.setDirty()
self._dirty = val
def initInputPorts(self):
"""
MUST OVERRIDE
This method is used to place all input port initialisation for your node
"""
pass
def initOutputPorts(self):
"""
MUST OVERRIDE
This method is used to place all output port initialisation for your node
"""
pass
def evaluate(self):
"""
MUST BE OVERLOADED
This method executes the node, reading the inputs and performing what ever computations on the inputs,
then sends it to the output ports
"""
pass
def getInputPort(self, name):
"""
Finds the port from the port list that has this name, else returns None
Args:
name (str): Name of the port you are wanting to return
Returns:
Port: The port with the matching name
"""
for port in self.portsIn:
if port.name == name:
return port
return None
def getOutputPort(self, name):
for port in self.portsOut:
if port.name == name:
return port
return None
def addInputPort(self, name, value=0.0):
"""
Creates a new input port on the node
Args:
name(str): The name of the port
value(float): The value of the port
Returns:
Port: The newly created port
"""
newPort = port.Port(name, self, value)
self.portsIn.append(newPort)
return newPort
def addOutputPort(self, name):
newPort = port.Port(name, self)
self.portsOut.append(newPort)
return newPort
def isConnected(self):
"""
Checks all ports to see if any are connected, if a connection is found, then returns true
"""
for port in self.portsIn + self.portsOut:
if port.isConnected():
return True
return False
def evaluateConnection(self):
for port in self.portsIn:
if port.dirty:
if port.isConnected():
port.edges[0].node.evaluate()
port.value = port.edges[0].value
port.dirty = False
def __repr__(self):
return "{} > Input Ports: {} OutputPorts:{}".format(self.type, len(self.portsIn), len(self.portsOut))
# CONSTANT NODES
class SumNode(Node):
def __init__(self):
super(SumNode,self).__init__()
self.type = self.__class__.__name__
def initInputPorts(self):
# initialise Input Ports
self.addInputPort(name="value1")
self.addInputPort(name="value2")
def initOutputPorts(self):
# initialise Output Ports
self.addOutputPort(name="result")
def evaluate(self):
"""
Performs the computation of the node and updates the output ports
"""
if self.dirty:
self.evaluateConnection()
sum = 0
for port in self.portsIn:
sum += port.value
self.portsOut[0].value = sum
self.dirty=False
class NegateNode(Node):
def __init__(self):
super(NegateNode, self).__init__()
self.type = self.__class__.__name__
def initInputPorts(self):
self.addInputPort("value")
def initOutputPorts(self):
self.addOutputPort("result")
def evaluate(self):
# only evaluates the node if it is dirty
if self.dirty:
self.evaluateConnection()
self.portsOut[0].value = -self.portsIn[0].value
self.dirty=False
class SubtractNode(Node):
def __init__(self):
super(SubtractNode, self).__init__()
self.type = self.__class__.__name__
def initInputPorts(self):
# initialise Input Ports
self.addInputPort(name="value1")
self.addInputPort(name="value2")
def initOutputPorts(self):
# initialise Output Ports
self.addOutputPort(name="result")
def evaluate(self):
# only evaluates the node if it is dirty
if self.dirty:
self.evaluateConnection()
# TODO: move just the evaluation code into a function and have
# all the dirty and updating of the ports to outside of this function
value = self.portsIn[0].value
for port in self.portsIn[1:]:
value -= port.value
self.portsOut[0].value = value
self.dirty = False
class MultiplyNode(Node):
def __init__(self):
super(MultiplyNode, self).__init__()
self.type = self.__class__.__name__
def initInputPorts(self):
# initialise Input Ports
self.addInputPort(name="value1")
self.addInputPort(name="value2")
def initOutputPorts(self):
# initialise Output Ports
self.addOutputPort(name="result")
def evaluate(self):
# only evaluates the node if it is dirty
if self.dirty:
self.evaluateConnection()
val = self.portsIn[0].value
for port in self.portsIn[1:]:
val *= port.value
self.portsOut[0].value = val
self.dirty = False
#======== CONSTANTS ==========
class ConstantNode(Node):
def __init__(self):
super(ConstantNode, self).__init__()
self.type = self.__class__.__name__
def initInputPorts(self):
# initialise Input Ports
self.addInputPort(name="value")
def initOutputPorts(self):
# initialise Output Ports
self.addOutputPort(name="result")
def evaluate(self):
self.portsOut[0].value = self.portsIn[0].value
class ArrayNode(ConstantNode):
def __init__(self):
super(ConstantNode, self).__init__()
self.type = self.__class__.__name__
class IntNode(ConstantNode):
def __init__(self):
super(ConstantNode, self).__init__()
self.type = self.__class__.__name__
class FloatNode(ConstantNode):
def __init__(self):
super(ConstantNode, self).__init__()
self.type = self.__class__.__name__
class MatrixNode(ConstantNode):
def __init__(self):
super(ConstantNode, self).__init__()
self.type = self.__class__.__name__
class ScalarToVector(ConstantNode):
def __init__(self):
super(ConstantNode, self).__init__()
self.type = self.__class__.__name__
def initInputPorts(self):
# initialise Input Ports
self.addInputPort(name="x")
self.addInputPort(name="y")
self.addInputPort(name="z")
def evaluate(self):
self.portsOut[0].value = [self.portsIn[0].value, self.portsIn[1].value, self.portsIn[2].value]
class VectorToScalar(ConstantNode):
def __init__(self):
super(ConstantNode, self).__init__()
self.type = self.__class__.__name__
def initInputPorts(self):
self.addInputPort(name="vector")
def initOutputPorts(self):
self.addOutputPort(name="x")
self.addOutputPort(name="y")
self.addOutputPort(name="z")
def evaluate(self):
self.portsOut[0].value = self.portsIn[0].value[0]
self.portsOut[1].value = self.portsIn[0].value[1]
self.portsOut[2].value = self.portsIn[0].value[2]
"""
Note Node:
This node is not actually an evaluation node, but will be used to keep notes when the UI is implemented
"""
class NoteNode():
pass
"""
Container Node:
This node, is used to create a node which is actually multiple
interconnected nodes, performing a specific task. Instead of coding
a node, you can create a node from interconnected Nodes.
Users create there own input and output ports.
Port Structure:
Internal input and output Ports, that the contained nodes will connect to,
to access data from outside the container, and send data outside of the container
When a port is added to this node, an opposite port with the same name will be added
to the internal ports.
"""
#TODO: Add some checks to make sure when you remove a node, it is no longer connected to any node inside of the container
class ContainerNode(Node):
internalNodes = []
def __init__(self):
super(ContainerNode, self).__init__()
self.type = self.__class__.__name__
def evaluate(self):
if self.dirty:
self.evaluateConnection()
# evaluate internal connections
for port in self.portsIn:
for edge in port.internalEdges:
edge.value = port.value
edge.dirty = False
port.dirty = False
for port in self.portsOut:
port.internalEdges[0].node.evaluate()
port.value = port.internalEdges[0].value
port.dirty = False
self.dirty = False
def addNode(self, node):
self.internalNodes.append(node)
def createNode(self, nodeType):
newNode = nodeType()
self.internalNodes.append(newNode)
return newNode
def removeNode(self, node):
self.internalNodes.remove(node)
def addInputPort(self, name, value=0.0):
newPort = port.ContainerPort(name, self, value)
self.portsIn.append(newPort)
return newPort
def addOutputPort(self, name):
newPort = port.ContainerPort(name, self)
self.portsOut.append(newPort)
return newPort