-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathdata.py
174 lines (152 loc) · 6.48 KB
/
data.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
# Copyright Hugo Aboud, Kaspars Jaudzems, Mark Steward, vanous
#
# This file is part of BlenderDMX.
#
# BlenderDMX is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# BlenderDMX is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.
import bpy
from bpy.props import IntProperty
from bpy.types import PropertyGroup
from .logging import DMX_Log
def update_callback(self, context):
# https://blender.stackexchange.com/questions/238441/force-redraw-add-on-custom-propery-in-n-panel-from-a-separate-thread
# The above (tag_redraw()) doesn't work, but it seems that even if this method is empty
# just the fact that there is an update callback on the DMX_Value.channel
# causes the UI to be refreshed periodically even when not in focus
return None
class DMX_Value(PropertyGroup):
channel: IntProperty(name="DMX Value", update=update_callback, default=0)
class DMX_Data:
_universes = []
_virtuals = {} # Virtual channels. These are per fixture and have an attribute and a value
_dmx = None # Cache access to the context.scene
_live_view_data = [0] * 512
@staticmethod
def prepare_empty_buffer():
# Prepare structure in the dmx_values collection, this is then used for the LiveDMX table
if DMX_Data._dmx is not None:
dmx = DMX_Data._dmx
dmx.dmx_values.clear()
buffer = [0] * 512
for i in buffer:
dmx.dmx_values.add()
dmx.dmx_values[-1].channel = i
@staticmethod
def setup(universes):
try:
DMX_Data._dmx = bpy.context.scene.dmx
except:
pass
DMX_Data.prepare_empty_buffer()
old_n = len(DMX_Data._universes)
# shrinking (less universes then before)
if universes < old_n:
DMX_Log.log.info(f"DMX Universes Deallocated: {universes}, to {old_n}")
DMX_Data._universes = DMX_Data._universes[:universes]
# growing (more universes then before)
else:
for u in range(old_n, universes):
DMX_Data._universes.append(bytearray([0] * 512))
DMX_Log.log.debug(f"DMX Universe Allocated: {u}")
@staticmethod
def get_value(universe, *channels):
"""Used for the namespace bdmx function
Returns value of the given channels"""
sum = 0
for idx, channel in enumerate(reversed(channels)):
val = DMX_Data.get(universe, channel, 1)[0]
sum |= val << (idx << 3)
return sum
@staticmethod
def get(universe, addr, n):
if universe >= len(DMX_Data._universes):
return bytearray([0] * n)
if addr + n > 512:
return bytearray([0] * n)
return DMX_Data._universes[universe][addr - 1 : addr + n - 1]
@staticmethod
def set(universe, addr, val):
DMX_Log.log.debug((universe, addr, val))
if universe >= len(DMX_Data._universes):
return
if not bpy.context.scene.dmx.universes[universe]:
return
if bpy.context.scene.dmx.universes[universe].input != "BLENDERDMX":
return
if val > 255:
return
# This happened when importing one MVR file
if addr > 511:
return
if DMX_Data._dmx is not None:
dmx = (
bpy.context.scene.dmx
) # hmm, here we use non cached dmx, probably this was safer...
if dmx.get_selected_live_dmx_universe().input == "BLENDERDMX":
dmx = bpy.context.scene.dmx
dmx.dmx_values[addr - 1].channel = val
DMX_Data._universes[universe][addr - 1] = val
# LiveDMX view
if DMX_Data._dmx is not None:
dmx = (
bpy.context.scene.dmx
) # ...or maybe it prevents using this call before the class is ready?
selected_live_dmx_universe = dmx.get_selected_live_dmx_universe()
if selected_live_dmx_universe is None: # this should not happen
raise ValueError(
"Missing selected universe, as if DMX base class is empty..."
)
if (
selected_live_dmx_universe.input == "BLENDERDMX"
and selected_live_dmx_universe.id == universe
):
DMX_Data._live_view_data = DMX_Data._universes[universe]
@staticmethod
def set_virtual(fixture, attribute, geometry, value):
"""Set value of virtual channel for given fixture"""
DMX_Log.log.debug((fixture, attribute, value))
if value > 255:
return
if fixture not in DMX_Data._virtuals:
DMX_Data._virtuals[fixture] = {}
if attribute not in DMX_Data._virtuals[fixture]:
DMX_Data._virtuals[fixture][attribute] = {}
DMX_Data._virtuals[fixture][attribute]["value"] = value
DMX_Data._virtuals[fixture][attribute]["geometry"] = geometry
@staticmethod
def get_virtual(fixture):
"""Get virtual channels for a given fixture"""
if fixture in DMX_Data._virtuals:
return DMX_Data._virtuals[fixture]
return {}
@staticmethod
def set_universe(universe, data, source):
DMX_Log.log.debug((universe, data))
if universe >= len(DMX_Data._universes):
return
dmx_changed = DMX_Data._universes[universe] != data
if dmx_changed:
DMX_Data._universes[universe] = data
if DMX_Data._dmx is not None:
dmx = bpy.context.scene.dmx
selected_live_dmx_universe = dmx.get_selected_live_dmx_universe()
if selected_live_dmx_universe is None: # this should not happen
raise ValueError(
"Missing selected universe, as if DMX base class is empty..."
)
if (
selected_live_dmx_universe.input == source
and selected_live_dmx_universe.id == universe
):
if dmx_changed:
DMX_Data._live_view_data = data