@rocketshow/designer 1.35.0 → 1.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/assets/i18n/de.json +3 -1
  2. package/assets/i18n/en.json +3 -1
  3. package/esm2022/lib/fixture/fixture-capability/fixture-capability-channel/fixture-capability-channel.component.mjs +181 -33
  4. package/esm2022/lib/fixture/fixture-capability/fixture-capability.component.mjs +14 -5
  5. package/esm2022/lib/fixture-pool/fixture-pool.component.mjs +8 -10
  6. package/esm2022/lib/models/fixture-capability.mjs +47 -4
  7. package/esm2022/lib/preview/models/color-changer-3d.mjs +15 -27
  8. package/esm2022/lib/preview/models/fixture-3d.mjs +91 -105
  9. package/esm2022/lib/preview/models/moving-head-3d.mjs +32 -41
  10. package/esm2022/lib/preview/preview.component.mjs +4 -6
  11. package/esm2022/lib/services/fixture.service.mjs +84 -30
  12. package/esm2022/lib/services/preset.service.mjs +16 -15
  13. package/esm2022/lib/services/preview.service.mjs +37 -57
  14. package/esm2022/lib/services/project-load.service.mjs +16 -19
  15. package/fesm2022/rocketshow-designer.mjs +531 -339
  16. package/fesm2022/rocketshow-designer.mjs.map +1 -1
  17. package/lib/fixture/fixture-capability/fixture-capability-channel/fixture-capability-channel.component.d.ts +9 -2
  18. package/lib/fixture/fixture-capability/fixture-capability.component.d.ts +5 -1
  19. package/lib/models/fixture-capability.d.ts +47 -4
  20. package/lib/preview/models/color-changer-3d.d.ts +4 -5
  21. package/lib/preview/models/fixture-3d.d.ts +17 -16
  22. package/lib/preview/models/moving-head-3d.d.ts +4 -5
  23. package/lib/services/fixture.service.d.ts +5 -0
  24. package/lib/services/preset.service.d.ts +1 -0
  25. package/lib/services/preview.service.d.ts +5 -2
  26. package/package.json +1 -1
  27. package/rocketshow-designer-1.37.0.tgz +0 -0
  28. package/rocketshow-designer-1.35.0.tgz +0 -0
@@ -119,18 +119,61 @@ class FixtureCapability {
119
119
  return;
120
120
  }
121
121
  this.type = FixtureCapabilityType[data.type];
122
- this.angleStart = data.angleStart;
123
- this.angleEnd = data.angleEnd;
124
122
  this.color = data.color;
125
123
  if (data.dmxRange) {
126
124
  this.dmxRange = data.dmxRange;
127
125
  }
128
126
  this.wheel = data.wheel;
129
127
  this.slotNumber = data.slotNumber;
128
+ this.comment = data.comment;
129
+ this.soundControlled = data.soundControlled;
130
+ this.shutterEffect = data.shutterEffect;
131
+ this.randomTiming = data.randomTiming;
132
+ this.angle = data.angle;
133
+ this.angleStart = data.angleStart;
134
+ this.angleEnd = data.angleEnd;
135
+ this.speed = data.speed;
136
+ this.speedStart = data.speedStart;
137
+ this.speedEnd = data.speedEnd;
130
138
  this.brightness = data.brightness;
131
139
  this.brightnessStart = data.brightnessStart;
132
140
  this.brightnessEnd = data.brightnessEnd;
133
- this.comment = data.comment;
141
+ this.duration = data.duration;
142
+ this.durationStart = data.durationStart;
143
+ this.durationEnd = data.durationEnd;
144
+ this.colorTemperature = data.colorTemperature;
145
+ this.colorTemperatureStart = data.colorTemperatureStart;
146
+ this.colorTemperatureEnd = data.colorTemperatureEnd;
147
+ this.soundSensitivity = data.soundSensitivity;
148
+ this.soundSensitivityStart = data.soundSensitivityStart;
149
+ this.soundSensitivityEnd = data.soundSensitivityEnd;
150
+ this.horizontalAngle = data.horizontalAngle;
151
+ this.horizontalAngleStart = data.horizontalAngleStart;
152
+ this.horizontalAngleEnd = data.horizontalAngleEnd;
153
+ this.verticalAngle = data.verticalAngle;
154
+ this.verticalAngleStart = data.verticalAngleStart;
155
+ this.verticalAngleEnd = data.verticalAngleEnd;
156
+ this.distance = data.distance;
157
+ this.distanceStart = data.distanceStart;
158
+ this.distanceEnd = data.distanceEnd;
159
+ this.openPercent = data.openPercent;
160
+ this.openPercentStart = data.openPercentStart;
161
+ this.openPercentEnd = data.openPercentEnd;
162
+ this.frostIntensity = data.frostIntensity;
163
+ this.frostIntensityStart = data.frostIntensityStart;
164
+ this.frostIntensityEnd = data.frostIntensityEnd;
165
+ this.fogOutput = data.fogOutput;
166
+ this.fogOutputStart = data.fogOutputStart;
167
+ this.fogOutputEnd = data.fogOutputEnd;
168
+ this.time = data.time;
169
+ this.timeStart = data.timeStart;
170
+ this.timeEnd = data.timeEnd;
171
+ this.insertion = data.insertion;
172
+ this.insertionStart = data.insertionStart;
173
+ this.insertionEnd = data.insertionEnd;
174
+ this.colors = data.colors;
175
+ this.colorsStart = data.colorsStart;
176
+ this.colorsEnd = data.colorsEnd;
134
177
  }
135
178
  }
136
179
 
@@ -1029,9 +1072,21 @@ class FixtureService {
1029
1072
  }
1030
1073
  }
1031
1074
  }
1075
+ fixtureUuidAndPixelKeyEquals(fixtureUuid1, fixtureUuid2, pixelKey1, pixelKey2) {
1076
+ return fixtureUuid1 === fixtureUuid2 && ((pixelKey1 == null && pixelKey2 == null) || pixelKey1 === pixelKey2);
1077
+ }
1032
1078
  getCachedFixtureByUuid(uuid, pixelKey) {
1033
1079
  for (const fixture of this.cachedFixtures) {
1034
- if (fixture.fixture.uuid === uuid && ((!fixture.pixelKey && !pixelKey) || fixture.pixelKey === pixelKey)) {
1080
+ if (this.fixtureUuidAndPixelKeyEquals(fixture.fixture.uuid, uuid, fixture.pixelKey, pixelKey)) {
1081
+ return fixture;
1082
+ }
1083
+ }
1084
+ // not yet found -> try searching the fixture without pixelKey for the general channels
1085
+ if (pixelKey) {
1086
+ return undefined;
1087
+ }
1088
+ for (const fixture of this.cachedFixtures) {
1089
+ if (!fixture.pixelKey && fixture.fixture.uuid === uuid) {
1035
1090
  return fixture;
1036
1091
  }
1037
1092
  }
@@ -1250,9 +1305,12 @@ class FixtureService {
1250
1305
  }
1251
1306
  return undefined;
1252
1307
  }
1308
+ getChannelNameWithPixelKey(templateChannelName, pixelKey) {
1309
+ return templateChannelName.replace('$pixelKey', pixelKey);
1310
+ }
1253
1311
  addCachedChannel(channels, channel, templateChannelName, pixelKey, profile) {
1254
1312
  const cachedFixtureChannel = new CachedFixtureChannel();
1255
- const channelName = templateChannelName.replace('$pixelKey', pixelKey);
1313
+ const channelName = this.getChannelNameWithPixelKey(templateChannelName, pixelKey);
1256
1314
  cachedFixtureChannel.channel = channel;
1257
1315
  cachedFixtureChannel.name = channelName;
1258
1316
  cachedFixtureChannel.capabilities = this.getCapabilitiesByChannel(cachedFixtureChannel.channel, channelName, profile);
@@ -1267,6 +1325,26 @@ class FixtureService {
1267
1325
  alphanumericSort(a, b) {
1268
1326
  return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });
1269
1327
  }
1328
+ getAllPixelKeys(profile) {
1329
+ let result = [];
1330
+ for (let pixelKeyX of profile.matrix.pixelKeys) {
1331
+ for (let pixelKeyY of pixelKeyX) {
1332
+ for (let pixelKeyZ of pixelKeyY) {
1333
+ if (pixelKeyZ) {
1334
+ result.push(pixelKeyZ);
1335
+ }
1336
+ }
1337
+ }
1338
+ }
1339
+ return result;
1340
+ }
1341
+ getAllPixelGroups(profile) {
1342
+ let result = [];
1343
+ for (const property of Object.keys(profile.matrix.pixelGroups)) {
1344
+ result.push(property);
1345
+ }
1346
+ return result;
1347
+ }
1270
1348
  getPixelKeysInOrder(profile, repeatFor) {
1271
1349
  let result = [];
1272
1350
  // could contain just a single string (e.g. 'eachPixelABC') or
@@ -1274,6 +1352,9 @@ class FixtureService {
1274
1352
  // an array of pixel (groups)
1275
1353
  result = repeatFor;
1276
1354
  }
1355
+ else if (repeatFor.length === 0) {
1356
+ return result;
1357
+ }
1277
1358
  else if (repeatFor[0] === 'eachPixelABC') {
1278
1359
  // Gets computed into an alphanumerically sorted list of all pixelKeys
1279
1360
  if (profile.matrix.pixelCount.length === 3) {
@@ -1286,23 +1367,13 @@ class FixtureService {
1286
1367
  }
1287
1368
  }
1288
1369
  else {
1289
- for (let pixelKeyX of profile.matrix.pixelKeys) {
1290
- for (let pixelKeyY of pixelKeyX) {
1291
- for (let pixelKeyZ of pixelKeyY) {
1292
- if (pixelKeyZ) {
1293
- result.push(pixelKeyZ);
1294
- }
1295
- }
1296
- }
1297
- }
1370
+ result = result.concat(this.getAllPixelKeys(profile));
1298
1371
  }
1299
1372
  result.sort(this.alphanumericSort);
1300
1373
  }
1301
1374
  else if (repeatFor[0] === 'eachPixelGroup') {
1302
1375
  // Gets computed into an array of all pixel group keys, ordered by appearance in the JSON file
1303
- for (const property of Object.keys(profile.matrix.pixelGroups)) {
1304
- result.push(property);
1305
- }
1376
+ result = result.concat(this.getAllPixelGroups(profile));
1306
1377
  }
1307
1378
  else if (repeatFor[0] === 'eachPixelXYZ') {
1308
1379
  for (let x = 0; x < profile.matrix.pixelKeys.length; x++) {
@@ -1426,13 +1497,27 @@ class FixtureService {
1426
1497
  for (const channel of mode.channels) {
1427
1498
  if (channel.name && !pixelKey) {
1428
1499
  // direct reference to a channel
1500
+ // don't check the fine channels. only add the coarse channel.
1501
+ let channelFound = false;
1429
1502
  for (const availableChannelName of Object.keys(profile.availableChannels)) {
1430
- // don't check the fine channels. only add the coarse channel.
1431
1503
  if (channel.name === availableChannelName) {
1432
1504
  const availableChannel = profile.availableChannels[availableChannelName];
1433
1505
  this.addCachedChannel(channels, availableChannel, availableChannelName, null, profile);
1434
1506
  }
1435
1507
  }
1508
+ // check the template channels, if not found in the available channels
1509
+ if (!channelFound) {
1510
+ for (const templateChannelName of Object.keys(profile.templateChannels)) {
1511
+ let existingPixelKeys = this.getAllPixelKeys(profile).concat(this.getAllPixelGroups(profile));
1512
+ for (const existingPixelKey of existingPixelKeys) {
1513
+ const channelName = this.getChannelNameWithPixelKey(templateChannelName, existingPixelKey);
1514
+ if (channel.name === channelName) {
1515
+ const templateChannel = profile.templateChannels[templateChannelName];
1516
+ this.addCachedChannel(channels, templateChannel, channelName, existingPixelKey, profile);
1517
+ }
1518
+ }
1519
+ }
1520
+ }
1436
1521
  }
1437
1522
  else {
1438
1523
  // reference a channel through a pixel matrix
@@ -1479,28 +1564,30 @@ class FixtureService {
1479
1564
  let mode = this.getModeByFixture(profile, fixture);
1480
1565
  // add the unique pixel keys
1481
1566
  for (let channel of mode.channels) {
1482
- this.getPixelKeysInOrder(profile, channel.repeatFor).forEach((str) => pixelKeys.add(str));
1567
+ this.getPixelKeysInOrder(profile, channel.repeatFor).forEach((str) => {
1568
+ pixelKeys.add(str);
1569
+ });
1483
1570
  }
1484
1571
  return Array.from(pixelKeys);
1485
1572
  }
1573
+ // does the fixture have a general channel without reference to specific pixel keys in the current mode?
1574
+ fixtureHasGeneralChannel(fixture) {
1575
+ let profile = this.getProfileByUuid(fixture.profileUuid);
1576
+ let mode = this.getModeByFixture(profile, fixture);
1577
+ // add the unique pixel keys
1578
+ for (let channel of mode.channels) {
1579
+ if (this.getPixelKeysInOrder(profile, channel.repeatFor).length === 0) {
1580
+ return true;
1581
+ }
1582
+ }
1583
+ return false;
1584
+ }
1486
1585
  updateCachedFixtures() {
1487
1586
  // calculate some frequently used values as a cache to save cpu time
1488
1587
  // afterwards
1489
1588
  this.cachedFixtures = [];
1490
1589
  for (const fixture of this.projectService.project.fixtures) {
1491
- const pixelKeys = this.fixtureGetUniquePixelKeys(fixture);
1492
- if (pixelKeys.length > 0) {
1493
- for (let pixelKey of pixelKeys) {
1494
- const cachedFixture = new CachedFixture();
1495
- cachedFixture.fixture = fixture;
1496
- cachedFixture.pixelKey = pixelKey;
1497
- cachedFixture.profile = this.getProfileByUuid(fixture.profileUuid);
1498
- cachedFixture.mode = this.getModeByFixture(cachedFixture.profile, fixture);
1499
- cachedFixture.channels = this.getCachedChannels(cachedFixture.profile, cachedFixture.mode, pixelKey);
1500
- this.cachedFixtures.push(cachedFixture);
1501
- }
1502
- }
1503
- else {
1590
+ if (this.fixtureHasGeneralChannel(fixture)) {
1504
1591
  const cachedFixture = new CachedFixture();
1505
1592
  cachedFixture.fixture = fixture;
1506
1593
  cachedFixture.profile = this.getProfileByUuid(fixture.profileUuid);
@@ -1508,6 +1595,16 @@ class FixtureService {
1508
1595
  cachedFixture.channels = this.getCachedChannels(cachedFixture.profile, cachedFixture.mode, null);
1509
1596
  this.cachedFixtures.push(cachedFixture);
1510
1597
  }
1598
+ const pixelKeys = this.fixtureGetUniquePixelKeys(fixture);
1599
+ for (let pixelKey of pixelKeys) {
1600
+ const cachedFixture = new CachedFixture();
1601
+ cachedFixture.fixture = fixture;
1602
+ cachedFixture.pixelKey = pixelKey;
1603
+ cachedFixture.profile = this.getProfileByUuid(fixture.profileUuid);
1604
+ cachedFixture.mode = this.getModeByFixture(cachedFixture.profile, fixture);
1605
+ cachedFixture.channels = this.getCachedChannels(cachedFixture.profile, cachedFixture.mode, pixelKey);
1606
+ this.cachedFixtures.push(cachedFixture);
1607
+ }
1511
1608
  }
1512
1609
  }
1513
1610
  capabilitiesMatch(type1, type2, color1, color2, wheel1, wheel2, profileUuid1, profileUuid2) {
@@ -1766,9 +1863,12 @@ class PresetService {
1766
1863
  }
1767
1864
  }
1768
1865
  }
1866
+ fixtureUuidAndPixelKeyEquals(fixtureUuid1, fixtureUuid2, pixelKey1, pixelKey2) {
1867
+ return fixtureUuid1 === fixtureUuid2 && ((!pixelKey1 && !pixelKey2) || pixelKey1 === pixelKey2);
1868
+ }
1769
1869
  getPresetFixture(preset, fixtureUuid, pixelKey) {
1770
1870
  for (const fixture of preset.fixtures) {
1771
- if (fixture.fixtureUuid === fixtureUuid && ((!fixture.pixelKey && !pixelKey) || fixture.pixelKey === pixelKey)) {
1871
+ if (this.fixtureUuidAndPixelKeyEquals(fixture.fixtureUuid, fixtureUuid, fixture.pixelKey, pixelKey)) {
1772
1872
  return fixture;
1773
1873
  }
1774
1874
  }
@@ -1824,21 +1924,19 @@ class PresetService {
1824
1924
  return;
1825
1925
  }
1826
1926
  for (const fixture of this.projectService.project.fixtures) {
1827
- const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
1828
- if (pixelKeys.length > 0) {
1829
- for (const pixelKey of pixelKeys) {
1830
- if (!this.fixtureIsSelected(fixture, pixelKey)) {
1831
- const presetFixture = new PresetFixture();
1832
- presetFixture.fixtureUuid = fixture.uuid;
1833
- presetFixture.pixelKey = pixelKey;
1834
- this.selectedPreset.fixtures.push(presetFixture);
1835
- }
1927
+ if (this.fixtureService.fixtureHasGeneralChannel(fixture)) {
1928
+ if (!this.fixtureIsSelected(fixture, null)) {
1929
+ const presetFixture = new PresetFixture();
1930
+ presetFixture.fixtureUuid = fixture.uuid;
1931
+ this.selectedPreset.fixtures.push(presetFixture);
1836
1932
  }
1837
1933
  }
1838
- else {
1839
- if (!this.fixtureIsSelected(fixture, null)) {
1934
+ const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
1935
+ for (const pixelKey of pixelKeys) {
1936
+ if (!this.fixtureIsSelected(fixture, pixelKey)) {
1840
1937
  const presetFixture = new PresetFixture();
1841
1938
  presetFixture.fixtureUuid = fixture.uuid;
1939
+ presetFixture.pixelKey = pixelKey;
1842
1940
  this.selectedPreset.fixtures.push(presetFixture);
1843
1941
  }
1844
1942
  }
@@ -1976,8 +2074,8 @@ class PresetService {
1976
2074
  }
1977
2075
  hasCapabilityType(type) {
1978
2076
  // there is at least one channel with at least one intensity capability
1979
- for (const projectFixture of this.selectedPreset.fixtures) {
1980
- const fixture = this.fixtureService.getCachedFixtureByUuid(projectFixture.fixtureUuid, projectFixture.pixelKey);
2077
+ for (const presetFixture of this.selectedPreset.fixtures) {
2078
+ const fixture = this.fixtureService.getCachedFixtureByUuid(presetFixture.fixtureUuid, presetFixture.pixelKey);
1981
2079
  for (const channel of fixture.channels) {
1982
2080
  if (channel.channel) {
1983
2081
  for (const capability of channel.capabilities) {
@@ -3037,6 +3135,14 @@ class PreviewService {
3037
3135
  // roughness: 0.5,
3038
3136
  // metalness: 0.5,
3039
3137
  });
3138
+ this.fixtureMaterial = new THREE.MeshLambertMaterial({
3139
+ color: 0x0d0d0d,
3140
+ emissive: 0x0d0d0d,
3141
+ });
3142
+ this.fixtureSelectedMaterial = new THREE.MeshLambertMaterial({
3143
+ color: 0xff00ff,
3144
+ emissive: 0xff00ff,
3145
+ });
3040
3146
  }
3041
3147
  getAlreadyCalculatedFixture(fixtures, fixtureIndex) {
3042
3148
  // Has this fixture already been calculated (same universe and dmx start address as a fixture before)
@@ -3120,29 +3226,34 @@ class PreviewService {
3120
3226
  getFixtureIndex(preset, fixtureUuid, pixelKey) {
3121
3227
  let index = 0;
3122
3228
  const countedFirstDmxChannelPixelKey = [];
3229
+ if (!this.presetService.getPresetFixture(preset, fixtureUuid, pixelKey)) {
3230
+ // fixture is not in preset
3231
+ return undefined;
3232
+ }
3123
3233
  // Loop over the global fixtures to retain the order
3124
3234
  for (const projectFixture of this.projectService.project.presetFixtures) {
3125
- const presetFixture = this.presetService.getPresetFixture(preset, fixtureUuid, pixelKey);
3126
- if (!presetFixture) {
3127
- // Not found in the preset
3128
- return undefined;
3129
- }
3130
- if (projectFixture.fixtureUuid === fixtureUuid && ((!projectFixture.pixelKey && !pixelKey) || projectFixture.pixelKey === pixelKey)) {
3131
- return index;
3132
- }
3133
- const fixture = this.fixtureService.getFixtureByUuid(projectFixture.fixtureUuid);
3134
- const firstDmxChannelAndFixtureUuid = {
3135
- firstDmxChannel: fixture.dmxFirstChannel,
3136
- pixelKey: projectFixture.pixelKey,
3137
- };
3138
- const exists = countedFirstDmxChannelPixelKey.some((item) => item.firstDmxChannel === firstDmxChannelAndFixtureUuid.firstDmxChannel &&
3139
- ((!item.pixelKey && !firstDmxChannelAndFixtureUuid.pixelKey) || item.pixelKey === firstDmxChannelAndFixtureUuid.pixelKey));
3140
- // don't count fixtures on the same channel as already counted ones
3141
- if (!exists) {
3142
- index++;
3143
- countedFirstDmxChannelPixelKey.push(firstDmxChannelAndFixtureUuid);
3235
+ for (const presetFixture of preset.fixtures) {
3236
+ if (this.presetService.fixtureUuidAndPixelKeyEquals(projectFixture.fixtureUuid, presetFixture.fixtureUuid, projectFixture.pixelKey, presetFixture.pixelKey)) {
3237
+ if (this.presetService.fixtureUuidAndPixelKeyEquals(projectFixture.fixtureUuid, fixtureUuid, projectFixture.pixelKey, pixelKey)) {
3238
+ return index;
3239
+ }
3240
+ const fixture = this.fixtureService.getFixtureByUuid(projectFixture.fixtureUuid);
3241
+ const firstDmxChannelAndFixtureUuid = {
3242
+ firstDmxChannel: fixture.dmxFirstChannel,
3243
+ pixelKey: projectFixture.pixelKey,
3244
+ };
3245
+ const exists = countedFirstDmxChannelPixelKey.some((item) => item.firstDmxChannel === firstDmxChannelAndFixtureUuid.firstDmxChannel &&
3246
+ ((!item.pixelKey && !firstDmxChannelAndFixtureUuid.pixelKey) || item.pixelKey === firstDmxChannelAndFixtureUuid.pixelKey));
3247
+ // don't count fixtures on the same channel as already counted ones
3248
+ if (!exists) {
3249
+ index++;
3250
+ countedFirstDmxChannelPixelKey.push(firstDmxChannelAndFixtureUuid);
3251
+ }
3252
+ break;
3253
+ }
3144
3254
  }
3145
3255
  }
3256
+ return undefined;
3146
3257
  }
3147
3258
  getPresetIntensity(preset, timeMillis) {
3148
3259
  // When fading is in progress (on preset or scene-level), the current preset does not
@@ -3375,43 +3486,6 @@ class PreviewService {
3375
3486
  }
3376
3487
  return calculatedFixtures;
3377
3488
  }
3378
- setUniverseValues(fixtures, masterDimmerValue) {
3379
- // TODO only, if monitoring is enabled
3380
- return;
3381
- // // Reset all DMX universes
3382
- // for (const universe of this.universeService.universes) {
3383
- // universe.channelValues = [];
3384
- // for (let i = 0; i < 512; i++) {
3385
- // universe.channelValues.push(0);
3386
- // }
3387
- // }
3388
- // // loop over each fixture
3389
- // fixtures.forEach((channelValues: FixtureChannelValue[], cachedFixture: CachedFixture) => {
3390
- // // TODO Get the correct universe for this fixture
3391
- // const universe: Universe = this.universeService.getUniverseByUuid(cachedFixture.fixture.dmxUniverseUuid);
3392
- // // loop over each channel for this fixture
3393
- // for (let channelIndex = 0; channelIndex < cachedFixture.mode.channels.length; channelIndex++) {
3394
- // const channelObj = cachedFixture.mode.channels[channelIndex];
3395
- // if (typeof channelObj === 'string') {
3396
- // const channelName = channelObj;
3397
- // // match this mode channel with a channel value
3398
- // for (const channelValue of channelValues) {
3399
- // const channel = this.fixtureService.getChannelByName(cachedFixture, channelName);
3400
- // if (channel && channel.channel) {
3401
- // const fineIndex = channel.channel.fineChannelAliases.indexOf(channelName);
3402
- // if (channel.name === channelValue.channelName || fineIndex > -1) {
3403
- // const universeChannel = cachedFixture.fixture.dmxFirstChannel + channelIndex;
3404
- // const dmxValue =
3405
- // Math.floor(channelValue.value / Math.pow(256, channel.channel.fineChannelAliases.length - (fineIndex + 1))) % 256;
3406
- // // TODO
3407
- // break;
3408
- // }
3409
- // }
3410
- // }
3411
- // }
3412
- // }
3413
- // });
3414
- }
3415
3489
  fixtureIsSelected(uuid, pixelKey, presets) {
3416
3490
  for (const preset of presets) {
3417
3491
  if (this.presetService.getPresetFixture(preset.preset, uuid, pixelKey)) {
@@ -3492,6 +3566,10 @@ class PreviewService {
3492
3566
  this.scene.add(mesh);
3493
3567
  this.stageMeshes.push(mesh);
3494
3568
  }
3569
+ ngOnDestroy() {
3570
+ this.fixtureMaterial.dispose();
3571
+ this.fixtureSelectedMaterial.dispose();
3572
+ }
3495
3573
  static { this.ɵfac = function PreviewService_Factory(t) { return new (t || PreviewService)(i0.ɵɵinject(PresetService), i0.ɵɵinject(FixtureService), i0.ɵɵinject(SceneService), i0.ɵɵinject(TimelineService), i0.ɵɵinject(ProjectService)); }; }
3496
3574
  static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: PreviewService, factory: PreviewService.ɵfac, providedIn: 'root' }); }
3497
3575
  }
@@ -3503,8 +3581,9 @@ class PreviewService {
3503
3581
  }], () => [{ type: PresetService }, { type: FixtureService }, { type: SceneService }, { type: TimelineService }, { type: ProjectService }], null); })();
3504
3582
 
3505
3583
  class Fixture3d {
3506
- constructor(fixtureService, fixture, scene) {
3584
+ constructor(fixtureService, previewService, fixture, scene, hasSpotLight = false) {
3507
3585
  this.fixtureService = fixtureService;
3586
+ this.previewService = previewService;
3508
3587
  this.pointLightMaxIntensity = 2;
3509
3588
  this.spotLightLightMaxIntensity = 10;
3510
3589
  this.fixtureHasDimmer = false;
@@ -3513,13 +3592,10 @@ class Fixture3d {
3513
3592
  this.dimmer = 0;
3514
3593
  this.isSelected = false;
3515
3594
  this.isLoaded = false;
3595
+ this.hasSpotLight = false;
3516
3596
  this.fixture = fixture;
3517
3597
  this.scene = scene;
3518
- // TODO Don't create it for each fixture but pass it from the preview service
3519
- this.selectedMaterial = new THREE.MeshLambertMaterial({
3520
- color: 0xff00ff,
3521
- emissive: 0xff00ff,
3522
- });
3598
+ this.hasSpotLight = hasSpotLight;
3523
3599
  // evaluate, various capabilities of this fixture
3524
3600
  for (const cachedChannel of this.fixture.channels) {
3525
3601
  if (cachedChannel.channel) {
@@ -3539,28 +3615,88 @@ class Fixture3d {
3539
3615
  }
3540
3616
  }
3541
3617
  }
3618
+ createSpotlightMaterial() {
3619
+ const vertexShader = `
3620
+ varying vec3 vNormal;
3621
+ varying vec3 vWorldPosition;
3622
+
3623
+ void main(){
3624
+ vNormal = normalize(normalMatrix * normal);
3625
+
3626
+ vec4 worldPosition = modelMatrix * vec4(position, 1.0);
3627
+ vWorldPosition = worldPosition.xyz;
3628
+
3629
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
3630
+ }`;
3631
+ const fragmentShader = `
3632
+ varying vec3 vNormal;
3633
+ varying vec3 vWorldPosition;
3634
+
3635
+ uniform vec3 lightColor;
3636
+ uniform float opacity;
3637
+ uniform vec3 spotPosition;
3638
+ uniform float attenuation;
3639
+ uniform float anglePower;
3640
+
3641
+ void main(){
3642
+ float intensity;
3643
+
3644
+ intensity = distance(vWorldPosition, spotPosition) / attenuation;
3645
+ intensity = 1.0 - clamp(intensity, 0.0, 1.0);
3646
+
3647
+ vec3 normal = vec3(vNormal.x, vNormal.y, abs(vNormal.z));
3648
+ float angleIntensity = pow(dot(normal, vec3(0.0, 0.0, 1.0)), anglePower);
3649
+ intensity = intensity * angleIntensity * opacity;
3650
+
3651
+ gl_FragColor = vec4(lightColor, intensity);
3652
+ }`;
3653
+ this.spotlightMaterial = new THREE.ShaderMaterial({
3654
+ uniforms: {
3655
+ attenuation: {
3656
+ type: 'f',
3657
+ value: 800.0,
3658
+ },
3659
+ anglePower: {
3660
+ type: 'f',
3661
+ value: 2,
3662
+ },
3663
+ spotPosition: {
3664
+ type: 'v3',
3665
+ value: new THREE.Vector3(0, 0, 0),
3666
+ },
3667
+ lightColor: {
3668
+ type: 'c',
3669
+ value: new THREE.Color('white'),
3670
+ },
3671
+ opacity: {
3672
+ type: 'f',
3673
+ value: 1.0,
3674
+ },
3675
+ },
3676
+ vertexShader,
3677
+ fragmentShader,
3678
+ transparent: true,
3679
+ depthWrite: false,
3680
+ });
3681
+ }
3542
3682
  createObjects() {
3543
- // Add the spotlight
3544
- this.spotLight = new THREE.SpotLight(0xffffff, 50000);
3545
- this.spotLight.angle = 2;
3546
- this.spotLight.penumbra = 1;
3547
- this.spotLight.decay = 0.01;
3548
- this.spotLight.distance = 0;
3549
- this.spotLight.intensity = 1;
3550
- this.spotLightHelper = new THREE.SpotLightHelper(this.spotLight);
3551
- this.scene.add(this.spotLightHelper);
3552
- const spotLightTarget = new THREE.Object3D();
3553
- this.spotLight.target = spotLightTarget;
3554
- this.spotlightGroup = new THREE.Object3D();
3555
- this.spotlightGroup.add(this.spotLight);
3556
- this.spotlightGroup.add(spotLightTarget);
3557
- this.spotLight.position.set(0, -1.4, 0);
3558
- spotLightTarget.position.set(0, -10, 0);
3559
- this.createSpotLightBeam();
3560
- // Add the point light for the surrounding light
3561
- this.pointLight = new THREE.PointLight(0xffffff, 500, 0, 0.1);
3562
- this.spotlightGroup.add(this.pointLight);
3563
- this.pointLight.position.set(0, -1.4, 0);
3683
+ if (this.hasSpotLight) {
3684
+ this.createSpotlightMaterial();
3685
+ this.spotLight = new THREE.SpotLight(0xffffff, 50000);
3686
+ this.spotLight.angle = 2;
3687
+ this.spotLight.penumbra = 1;
3688
+ this.spotLight.decay = 0.01;
3689
+ this.spotLight.distance = 0;
3690
+ this.spotLight.intensity = 1;
3691
+ const spotLightTarget = new THREE.Object3D();
3692
+ this.spotLight.target = spotLightTarget;
3693
+ this.spotlightGroup = new THREE.Object3D();
3694
+ this.spotlightGroup.add(this.spotLight);
3695
+ this.spotlightGroup.add(spotLightTarget);
3696
+ this.spotLight.position.set(0, -1.4, 0);
3697
+ spotLightTarget.position.set(0, -10, 0);
3698
+ this.createSpotLightBeam();
3699
+ }
3564
3700
  }
3565
3701
  getCapabilityInValue(channel, value) {
3566
3702
  for (const capability of channel.capabilities) {
@@ -3641,11 +3777,7 @@ class Fixture3d {
3641
3777
  // Take the color into account for the beam (don't show a black beam)
3642
3778
  const color = new THREE.Color('rgb(' + this.colorRed + ', ' + this.colorGreen + ', ' + this.colorBlue + ')');
3643
3779
  const intensityColor = Math.max(this.colorRed, this.colorGreen, this.colorBlue);
3644
- if (this.pointLight) {
3645
- this.pointLight.color = color;
3646
- this.pointLight.intensity = this.pointLightMaxIntensity * this.dimmer;
3647
- }
3648
- if (this.spotLight) {
3780
+ if (this.hasSpotLight) {
3649
3781
  this.spotLight.color = color;
3650
3782
  this.spotLightBeam.material.uniforms.lightColor.value = color;
3651
3783
  this.spotLightBeam.material.uniforms.opacity.value = Math.min(intensityColor, this.dimmer);
@@ -3685,82 +3817,13 @@ class Fixture3d {
3685
3817
  }
3686
3818
  }
3687
3819
  }
3688
- atmosphereMat() {
3689
- const vertexShader = `
3690
- varying vec3 vNormal;
3691
- varying vec3 vWorldPosition;
3692
-
3693
- void main(){
3694
- vNormal = normalize(normalMatrix * normal);
3695
-
3696
- vec4 worldPosition = modelMatrix * vec4(position, 1.0);
3697
- vWorldPosition = worldPosition.xyz;
3698
-
3699
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
3700
- }`;
3701
- const fragmentShader = `
3702
- varying vec3 vNormal;
3703
- varying vec3 vWorldPosition;
3704
-
3705
- uniform vec3 lightColor;
3706
- uniform float opacity;
3707
- uniform vec3 spotPosition;
3708
- uniform float attenuation;
3709
- uniform float anglePower;
3710
-
3711
- void main(){
3712
- float intensity;
3713
-
3714
- intensity = distance(vWorldPosition, spotPosition) / attenuation;
3715
- intensity = 1.0 - clamp(intensity, 0.0, 1.0);
3716
-
3717
- vec3 normal = vec3(vNormal.x, vNormal.y, abs(vNormal.z));
3718
- float angleIntensity = pow(dot(normal, vec3(0.0, 0.0, 1.0)), anglePower);
3719
- intensity = intensity * angleIntensity * opacity;
3720
-
3721
- gl_FragColor = vec4(lightColor, intensity);
3722
- }`;
3723
- const material = new THREE.ShaderMaterial({
3724
- uniforms: {
3725
- attenuation: {
3726
- type: 'f',
3727
- value: 800.0,
3728
- },
3729
- anglePower: {
3730
- type: 'f',
3731
- value: 2,
3732
- },
3733
- spotPosition: {
3734
- type: 'v3',
3735
- value: new THREE.Vector3(0, 0, 0),
3736
- },
3737
- lightColor: {
3738
- type: 'c',
3739
- value: new THREE.Color('white'),
3740
- },
3741
- opacity: {
3742
- type: 'f',
3743
- value: 1.0,
3744
- },
3745
- },
3746
- vertexShader,
3747
- fragmentShader,
3748
- transparent: true,
3749
- depthWrite: false,
3750
- });
3751
- return material;
3752
- }
3753
3820
  createSpotLightBeam() {
3754
- if (this.spotLightBeam) {
3755
- this.spotlightGroup.remove(this.spotLightBeam);
3756
- }
3757
3821
  // TODO set the correct angle from the profile
3758
3822
  const beamAngleDegrees = 14;
3759
3823
  const geometry = new THREE.CylinderGeometry(0.1, beamAngleDegrees * 1.2, 100, 64, 20, false);
3760
3824
  geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, -geometry.parameters.height / 2, 0));
3761
3825
  geometry.applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
3762
- this.atmosphereMaterial = this.atmosphereMat();
3763
- this.spotLightBeam = new THREE.Mesh(geometry, this.atmosphereMaterial);
3826
+ this.spotLightBeam = new THREE.Mesh(geometry, this.spotlightMaterial);
3764
3827
  this.spotLightBeam.position.set(0, -0.02, 0);
3765
3828
  this.spotLightBeam.receiveShadow = false;
3766
3829
  this.spotLightBeam.castShadow = false;
@@ -3768,30 +3831,17 @@ class Fixture3d {
3768
3831
  this.spotLightBeam.rotation.x = Math.PI / 2;
3769
3832
  }
3770
3833
  destroy() {
3771
- this.selectedMaterial.dispose();
3772
- this.atmosphereMaterial.dispose();
3834
+ if (this.hasSpotLight) {
3835
+ this.spotlightMaterial.dispose();
3836
+ }
3773
3837
  }
3774
3838
  }
3775
3839
 
3776
3840
  class ColorChanger3d extends Fixture3d {
3777
- createObjects() {
3778
- super.createObjects();
3779
- this.material = new THREE.MeshLambertMaterial({
3780
- color: 0x0d0d0d,
3781
- emissive: 0x0d0d0d,
3782
- });
3783
- this.mesh.material = this.material;
3784
- this.spotLightHelper = new THREE.SpotLightHelper(this.spotLight);
3785
- // scene.add(this.spotLightHelper);
3786
- this.objectGroup.add(this.spotlightGroup);
3787
- this.objectGroup.add(this.mesh);
3788
- // A moving head has about 32 cm in width
3789
- this.objectGroup.scale.multiplyScalar(9);
3790
- this.scene.add(this.objectGroup);
3791
- this.isLoaded = true;
3792
- }
3793
- constructor(fixtureService, previewMeshService, fixture, scene) {
3794
- super(fixtureService, fixture, scene);
3841
+ constructor(fixtureService, previewService, previewMeshService, fixture, scene) {
3842
+ super(fixtureService, previewService, fixture, scene, true);
3843
+ this.fixtureService = fixtureService;
3844
+ this.previewService = previewService;
3795
3845
  this.objectGroup = new THREE.Object3D();
3796
3846
  forkJoin([previewMeshService.getMesh('color-changer')])
3797
3847
  .pipe(map(([colorChanger]) => {
@@ -3800,8 +3850,14 @@ class ColorChanger3d extends Fixture3d {
3800
3850
  }))
3801
3851
  .subscribe();
3802
3852
  }
3803
- getObject() {
3804
- return this.objectGroup;
3853
+ createObjects() {
3854
+ super.createObjects();
3855
+ this.mesh.material = this.previewService.fixtureMaterial;
3856
+ this.objectGroup.add(this.spotlightGroup);
3857
+ this.objectGroup.add(this.mesh);
3858
+ this.objectGroup.scale.multiplyScalar(9);
3859
+ this.scene.add(this.objectGroup);
3860
+ this.isLoaded = true;
3805
3861
  }
3806
3862
  updatePreview(channelValues, masterDimmerValue) {
3807
3863
  if (!this.isLoaded) {
@@ -3813,36 +3869,48 @@ class ColorChanger3d extends Fixture3d {
3813
3869
  if (this.lastSelected !== this.isSelected) {
3814
3870
  if (this.isSelected) {
3815
3871
  for (const child of this.mesh.children) {
3816
- child.material = this.selectedMaterial;
3872
+ child.material = this.previewService.fixtureSelectedMaterial;
3817
3873
  }
3818
3874
  }
3819
3875
  else {
3820
3876
  for (const child of this.mesh.children) {
3821
- child.material = this.material;
3877
+ child.material = this.previewService.fixtureMaterial;
3822
3878
  }
3823
3879
  }
3824
3880
  this.lastSelected = this.isSelected;
3825
3881
  }
3826
- // Update the light helpers
3827
- // this.spotLightHelper.update();
3828
3882
  }
3829
3883
  destroy() {
3830
3884
  this.scene.remove(this.objectGroup);
3831
- this.material.dispose();
3832
- this.spotLightHelper.dispose();
3833
3885
  }
3834
3886
  }
3835
3887
 
3836
3888
  class MovingHead3d extends Fixture3d {
3889
+ constructor(fixtureService, previewService, previewMeshService, fixture, scene) {
3890
+ super(fixtureService, previewService, fixture, scene, true);
3891
+ this.fixtureService = fixtureService;
3892
+ this.previewService = previewService;
3893
+ this.headGroup = new THREE.Object3D();
3894
+ this.armGroup = new THREE.Object3D();
3895
+ this.objectGroup = new THREE.Object3D();
3896
+ forkJoin([
3897
+ previewMeshService.getMesh('moving_head_socket'),
3898
+ previewMeshService.getMesh('moving_head_arm'),
3899
+ previewMeshService.getMesh('moving_head_head'),
3900
+ ])
3901
+ .pipe(map(([socket, arm, head]) => {
3902
+ this.socket = socket;
3903
+ this.arm = arm;
3904
+ this.head = head;
3905
+ this.createObjects();
3906
+ }))
3907
+ .subscribe();
3908
+ }
3837
3909
  createObjects() {
3838
3910
  super.createObjects();
3839
- this.material = new THREE.MeshLambertMaterial({
3840
- color: 0x0d0d0d,
3841
- emissive: 0x0d0d0d,
3842
- });
3843
- this.socket.material = this.material;
3844
- this.arm.material = this.material;
3845
- this.head.material = this.material;
3911
+ this.socket.material = this.previewService.fixtureMaterial;
3912
+ this.arm.material = this.previewService.fixtureMaterial;
3913
+ this.head.material = this.previewService.fixtureMaterial;
3846
3914
  // Add the head
3847
3915
  this.head.scale.multiplyScalar(0.9);
3848
3916
  this.head.position.set(-0.1, 0, 0);
@@ -3863,32 +3931,12 @@ class MovingHead3d extends Fixture3d {
3863
3931
  this.scene.add(this.objectGroup);
3864
3932
  this.isLoaded = true;
3865
3933
  }
3866
- constructor(fixtureService, previewMeshService, fixture, scene) {
3867
- super(fixtureService, fixture, scene);
3868
- this.headGroup = new THREE.Object3D();
3869
- this.armGroup = new THREE.Object3D();
3870
- this.objectGroup = new THREE.Object3D();
3871
- forkJoin([
3872
- previewMeshService.getMesh('moving_head_socket'),
3873
- previewMeshService.getMesh('moving_head_arm'),
3874
- previewMeshService.getMesh('moving_head_head'),
3875
- ])
3876
- .pipe(map(([socket, arm, head]) => {
3877
- this.socket = socket;
3878
- this.arm = arm;
3879
- this.head = head;
3880
- this.createObjects();
3881
- }))
3882
- .subscribe();
3883
- }
3884
- getObject() {
3885
- return this.objectGroup;
3886
- }
3887
3934
  updatePreview(channelValues, masterDimmerValue) {
3888
3935
  if (!this.isLoaded) {
3889
3936
  return;
3890
3937
  }
3891
3938
  super.updatePreview(channelValues, masterDimmerValue);
3939
+ this.updatePosition(this.objectGroup);
3892
3940
  // Apply default settings
3893
3941
  let panStart = 0;
3894
3942
  let panEnd = 255;
@@ -3917,38 +3965,33 @@ class MovingHead3d extends Fixture3d {
3917
3965
  }
3918
3966
  }
3919
3967
  }
3920
- this.updatePosition(this.objectGroup);
3921
3968
  // calculate the y/x rotation in radiants based on pan/tilt (0-1) respecting the max pan/tilt
3922
3969
  this.armGroup.rotation.y = THREE.MathUtils.degToRad(panEnd * this.pan + panStart) - THREE.MathUtils.degToRad(panEnd / 2);
3923
3970
  this.headGroup.rotation.x = THREE.MathUtils.degToRad(tiltEnd * this.tilt + tiltStart) - THREE.MathUtils.degToRad(tiltEnd / 2);
3924
- // Update the angle (only on change, because it's expensive)
3971
+ // Update the angle only on change, because it's expensive
3925
3972
  // TODO update the correct angle from the profile
3926
3973
  const beamAngleDregrees = 14;
3927
3974
  if (this.lastBeamAngleDegrees !== beamAngleDregrees) {
3928
3975
  this.spotLight.angle = THREE.MathUtils.degToRad(beamAngleDregrees);
3929
- this.createSpotLightBeam();
3930
3976
  this.lastBeamAngleDegrees = beamAngleDregrees;
3931
3977
  }
3932
3978
  // Update the material
3933
3979
  if (this.lastSelected !== this.isSelected) {
3934
3980
  if (this.isSelected) {
3935
- this.socket.material = this.selectedMaterial;
3936
- this.arm.material = this.selectedMaterial;
3937
- this.head.material = this.selectedMaterial;
3981
+ this.socket.material = this.previewService.fixtureSelectedMaterial;
3982
+ this.arm.material = this.previewService.fixtureSelectedMaterial;
3983
+ this.head.material = this.previewService.fixtureSelectedMaterial;
3938
3984
  }
3939
3985
  else {
3940
- this.socket.material = this.material;
3941
- this.arm.material = this.material;
3942
- this.head.material = this.material;
3986
+ this.socket.material = this.previewService.fixtureMaterial;
3987
+ this.arm.material = this.previewService.fixtureMaterial;
3988
+ this.head.material = this.previewService.fixtureMaterial;
3943
3989
  }
3944
3990
  this.lastSelected = this.isSelected;
3945
3991
  }
3946
- // this.spotLightHelper.update();
3947
3992
  }
3948
3993
  destroy() {
3949
3994
  this.scene.remove(this.objectGroup);
3950
- this.material.dispose();
3951
- this.spotLightHelper.dispose();
3952
3995
  }
3953
3996
  }
3954
3997
 
@@ -3981,13 +4024,13 @@ class PreviewComponent {
3981
4024
  for (const fixture of this.fixtureService.cachedFixtures) {
3982
4025
  switch (fixture.profile.categories[0]) {
3983
4026
  case FixtureCategory['Moving Head']:
3984
- this.fixtures3d.push(new MovingHead3d(this.fixtureService, this.previewMeshService, fixture, this.scene));
4027
+ this.fixtures3d.push(new MovingHead3d(this.fixtureService, this.previewService, this.previewMeshService, fixture, this.scene));
3985
4028
  break;
3986
4029
  case FixtureCategory['Color Changer']:
3987
- this.fixtures3d.push(new ColorChanger3d(this.fixtureService, this.previewMeshService, fixture, this.scene));
4030
+ this.fixtures3d.push(new ColorChanger3d(this.fixtureService, this.previewService, this.previewMeshService, fixture, this.scene));
3988
4031
  break;
3989
4032
  case FixtureCategory['Blinder']:
3990
- this.fixtures3d.push(new ColorChanger3d(this.fixtureService, this.previewMeshService, fixture, this.scene));
4033
+ this.fixtures3d.push(new ColorChanger3d(this.fixtureService, this.previewService, this.previewMeshService, fixture, this.scene));
3991
4034
  break;
3992
4035
  }
3993
4036
  }
@@ -4047,8 +4090,6 @@ class PreviewComponent {
4047
4090
  fixture3d.isSelected = this.previewService.fixtureIsSelected(fixture3d.fixture.fixture.uuid, fixture3d.fixture.pixelKey, presets);
4048
4091
  }
4049
4092
  }
4050
- // TODO enable for monitoring the DMX universes
4051
- this.previewService.setUniverseValues(calculatedFixtures, this.projectService.project.masterDimmerValue);
4052
4093
  // Render the scene
4053
4094
  this.render();
4054
4095
  requestAnimationFrame(this.animate.bind(this));
@@ -4282,39 +4323,36 @@ class ProjectLoadService {
4282
4323
  }
4283
4324
  migrateToVersion2() {
4284
4325
  for (let fixture of this.projectService.project.fixtures) {
4285
- const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
4286
- if (pixelKeys.length > 0) {
4287
- for (let pixelKey of pixelKeys) {
4288
- const projectFixture = new PresetFixture();
4289
- projectFixture.fixtureUuid = fixture.uuid;
4290
- projectFixture.pixelKey = pixelKey;
4291
- this.projectService.project.presetFixtures.push(projectFixture);
4292
- }
4326
+ if (this.fixtureService.fixtureHasGeneralChannel(fixture)) {
4327
+ const projectFixture = new PresetFixture();
4328
+ projectFixture.fixtureUuid = fixture.uuid;
4329
+ this.projectService.project.presetFixtures.push(projectFixture);
4293
4330
  }
4294
- else {
4331
+ const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
4332
+ for (let pixelKey of pixelKeys) {
4295
4333
  const projectFixture = new PresetFixture();
4296
4334
  projectFixture.fixtureUuid = fixture.uuid;
4335
+ projectFixture.pixelKey = pixelKey;
4297
4336
  this.projectService.project.presetFixtures.push(projectFixture);
4298
4337
  }
4299
4338
  }
4300
4339
  for (let preset of this.projectService.project.presets) {
4301
4340
  for (let fixtureUuid of preset.fixtureUuids) {
4302
4341
  const fixture = this.fixtureService.getFixtureByUuid(fixtureUuid);
4303
- const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
4304
- if (pixelKeys.length > 0) {
4305
- for (let pixelKey of pixelKeys) {
4306
- const presetFixture = new PresetFixture();
4307
- presetFixture.fixtureUuid = fixtureUuid;
4308
- presetFixture.pixelKey = pixelKey;
4309
- preset.fixtures.push(presetFixture);
4310
- }
4342
+ if (this.fixtureService.fixtureHasGeneralChannel(fixture)) {
4343
+ const presetFixture = new PresetFixture();
4344
+ presetFixture.fixtureUuid = fixtureUuid;
4345
+ preset.fixtures.push(presetFixture);
4311
4346
  }
4312
- else {
4347
+ const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
4348
+ for (let pixelKey of pixelKeys) {
4313
4349
  const presetFixture = new PresetFixture();
4314
4350
  presetFixture.fixtureUuid = fixtureUuid;
4351
+ presetFixture.pixelKey = pixelKey;
4315
4352
  preset.fixtures.push(presetFixture);
4316
4353
  }
4317
4354
  }
4355
+ preset.fixtureUuids = undefined;
4318
4356
  }
4319
4357
  this.projectService.project.version = 2;
4320
4358
  }
@@ -5578,18 +5616,16 @@ class FixturePoolComponent {
5578
5616
  }
5579
5617
  }
5580
5618
  if (!found) {
5581
- const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
5582
- if (pixelKeys.length > 0) {
5583
- for (let pixelKey of pixelKeys) {
5584
- const presetFixture = new PresetFixture();
5585
- presetFixture.fixtureUuid = fixture.uuid;
5586
- presetFixture.pixelKey = pixelKey;
5587
- this.projectService.project.presetFixtures.push(presetFixture);
5588
- }
5619
+ if (this.fixtureService.fixtureHasGeneralChannel(fixture)) {
5620
+ const presetFixture = new PresetFixture();
5621
+ presetFixture.fixtureUuid = fixture.uuid;
5622
+ this.projectService.project.presetFixtures.push(presetFixture);
5589
5623
  }
5590
- else {
5624
+ const pixelKeys = this.fixtureService.fixtureGetUniquePixelKeys(fixture);
5625
+ for (let pixelKey of pixelKeys) {
5591
5626
  const presetFixture = new PresetFixture();
5592
5627
  presetFixture.fixtureUuid = fixture.uuid;
5628
+ presetFixture.pixelKey = pixelKey;
5593
5629
  this.projectService.project.presetFixtures.push(presetFixture);
5594
5630
  }
5595
5631
  }
@@ -8201,13 +8237,13 @@ function FixtureCapabilityComponent_div_2_Template(rf, ctx) { if (rf & 1) {
8201
8237
  const ctx_r2 = i0.ɵɵnextContext();
8202
8238
  i0.ɵɵclassProp("h-100", ctx_r2.isChrome());
8203
8239
  i0.ɵɵadvance();
8204
- i0.ɵɵproperty("ngIf", ctx_r2.presetService.hasCapabilityDimmer());
8240
+ i0.ɵɵproperty("ngIf", ctx_r2.hasCapabilityDimmer);
8205
8241
  i0.ɵɵadvance();
8206
- i0.ɵɵproperty("ngIf", ctx_r2.presetService.hasCapabilityColorOrColorWheel());
8242
+ i0.ɵɵproperty("ngIf", ctx_r2.hasCapabilityColorOrColorWheel);
8207
8243
  i0.ɵɵadvance();
8208
8244
  i0.ɵɵproperty("ngForOf", i0.ɵɵpipeBind1(4, 6, ctx_r2.colorWheelChannels));
8209
8245
  i0.ɵɵadvance(2);
8210
- i0.ɵɵproperty("ngIf", ctx_r2.presetService.hasCapabilityPanTilt());
8246
+ i0.ɵɵproperty("ngIf", ctx_r2.hasCapabilityPanTilt);
8211
8247
  } }
8212
8248
  function FixtureCapabilityComponent_div_3_Template(rf, ctx) { if (rf & 1) {
8213
8249
  i0.ɵɵelementStart(0, "div", 10)(1, "p", 11);
@@ -8236,6 +8272,9 @@ class FixtureCapabilityComponent {
8236
8272
  this.introService = introService;
8237
8273
  // map containing the profile and the channel name containing the wheel
8238
8274
  this.colorWheelChannels = new Map();
8275
+ this.hasCapabilityDimmer = false;
8276
+ this.hasCapabilityColorOrColorWheel = false;
8277
+ this.hasCapabilityPanTilt = false;
8239
8278
  this.fixtureSelectionChangedSubscription = this.presetService.fixtureSelectionChanged.subscribe(() => {
8240
8279
  this.update();
8241
8280
  });
@@ -8284,8 +8323,14 @@ class FixtureCapabilityComponent {
8284
8323
  }
8285
8324
  }
8286
8325
  }
8326
+ updateCapabilities() {
8327
+ this.hasCapabilityDimmer = this.presetService.hasCapabilityDimmer();
8328
+ this.hasCapabilityColorOrColorWheel = this.presetService.hasCapabilityColorOrColorWheel();
8329
+ this.hasCapabilityPanTilt = this.presetService.hasCapabilityPanTilt();
8330
+ }
8287
8331
  update() {
8288
8332
  this.updateColorWheels();
8333
+ this.updateCapabilities();
8289
8334
  this.changeDetectorRef.detectChanges();
8290
8335
  }
8291
8336
  isChrome() {
@@ -8311,7 +8356,7 @@ class FixtureCapabilityComponent {
8311
8356
  }
8312
8357
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureCapabilityComponent, [{
8313
8358
  type: Component,
8314
- args: [{ selector: 'lib-app-fixture-capability', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"card border-secondary panel h-100\" [class.card-intro-active]=\"introService.showStep('capabilities')\">\n <div class=\"card-body capability-container d-flex h-100\" style=\"overflow-y: hidden\">\n <!-- h-100 needs to be applied in chrome to avoid performance issues with many layouts (reflows)\n during sliding. But the class must not be applied e.g. in Safari, because the bottom margin is not correct\n anymore afterwards. -->\n <div *ngIf=\"presetService.selectedPreset\" class=\"row m-0 pl-3\" [class.h-100]=\"isChrome()\">\n <!-- Dimmer -->\n <!-- the height attribute is required to avoid performance issues with many layouts (reflows)\n in Chrome during sliding -->\n <div *ngIf=\"presetService.hasCapabilityDimmer()\" class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-dimmer></lib-app-fixture-capability-dimmer>\n </div>\n\n <!-- Color -->\n <!-- Also show for color wheel fixtures to approx. the color -->\n <div *ngIf=\"presetService.hasCapabilityColorOrColorWheel()\" class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-color></lib-app-fixture-capability-color>\n </div>\n\n <!-- Color wheels -->\n <div\n *ngFor=\"let entry of colorWheelChannels | keyvalue; let i = index\"\n class=\"col col-auto p-0 my-3 mr-3\"\n style=\"height: calc(100% - 2rem)\"\n >\n <lib-app-fixture-capability-color-wheel\n [profile]=\"entry.key\"\n [channel]=\"entry.value\"\n [wheelIndex]=\"i\"\n [showContainer]=\"colorWheelChannels.size > 1\"\n ></lib-app-fixture-capability-color-wheel>\n </div>\n\n <!-- Pan/Tilt -->\n <div *ngIf=\"presetService.hasCapabilityPanTilt()\" class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-pan-tilt></lib-app-fixture-capability-pan-tilt>\n </div>\n\n <!-- Fix spacing at the end -->\n <div class=\"col col-auto p-0 my-3 mr-3\">&nbsp;</div>\n </div>\n\n <div *ngIf=\"projectService.project.fixtures.length == 0\" class=\"m-auto\">\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-fixtures' | translate }}</p>\n </div>\n\n <div\n *ngIf=\"\n projectService.project.fixtures.length > 0 && presetService.selectedPreset && presetService.selectedPreset.fixtures.length == 0\n \"\n class=\"m-auto\"\n >\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-fixtures-selected' | translate }}</p>\n </div>\n </div>\n</div>\n" }]
8359
+ args: [{ selector: 'lib-app-fixture-capability', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"card border-secondary panel h-100\" [class.card-intro-active]=\"introService.showStep('capabilities')\">\n <div class=\"card-body capability-container d-flex h-100\" style=\"overflow-y: hidden\">\n <!-- h-100 needs to be applied in chrome to avoid performance issues with many layouts (reflows)\n during sliding. But the class must not be applied e.g. in Safari, because the bottom margin is not correct\n anymore afterwards. -->\n <div *ngIf=\"presetService.selectedPreset\" class=\"row m-0 pl-3\" [class.h-100]=\"isChrome()\">\n <!-- Dimmer -->\n <!-- the height attribute is required to avoid performance issues with many layouts (reflows)\n in Chrome during sliding -->\n <div *ngIf=\"hasCapabilityDimmer\" class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-dimmer></lib-app-fixture-capability-dimmer>\n </div>\n\n <!-- Color -->\n <!-- Also show for color wheel fixtures to approx. the color -->\n <div *ngIf=\"hasCapabilityColorOrColorWheel\" class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-color></lib-app-fixture-capability-color>\n </div>\n\n <!-- Color wheels -->\n <div\n *ngFor=\"let entry of colorWheelChannels | keyvalue; let i = index\"\n class=\"col col-auto p-0 my-3 mr-3\"\n style=\"height: calc(100% - 2rem)\"\n >\n <lib-app-fixture-capability-color-wheel\n [profile]=\"entry.key\"\n [channel]=\"entry.value\"\n [wheelIndex]=\"i\"\n [showContainer]=\"colorWheelChannels.size > 1\"\n ></lib-app-fixture-capability-color-wheel>\n </div>\n\n <!-- Pan/Tilt -->\n <div *ngIf=\"hasCapabilityPanTilt\" class=\"col col-auto p-0 my-3 mr-3\" style=\"height: calc(100% - 2rem)\">\n <lib-app-fixture-capability-pan-tilt></lib-app-fixture-capability-pan-tilt>\n </div>\n\n <!-- Fix spacing at the end -->\n <div class=\"col col-auto p-0 my-3 mr-3\">&nbsp;</div>\n </div>\n\n <div *ngIf=\"projectService.project.fixtures.length == 0\" class=\"m-auto\">\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-fixtures' | translate }}</p>\n </div>\n\n <div\n *ngIf=\"\n projectService.project.fixtures.length > 0 && presetService.selectedPreset && presetService.selectedPreset.fixtures.length == 0\n \"\n class=\"m-auto\"\n >\n <p class=\"m-auto\" style=\"color: #717171\">{{ 'designer.fixture.no-fixtures-selected' | translate }}</p>\n </div>\n </div>\n</div>\n" }]
8315
8360
  }], () => [{ type: PresetService }, { type: FixtureService }, { type: i0.ChangeDetectorRef }, { type: ProjectService }, { type: IntroService }], null); })();
8316
8361
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FixtureCapabilityComponent, { className: "FixtureCapabilityComponent" }); })();
8317
8362
 
@@ -9958,11 +10003,49 @@ class MasterDimmerComponent {
9958
10003
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MasterDimmerComponent, { className: "MasterDimmerComponent" }); })();
9959
10004
 
9960
10005
  const _c0$2 = ["sliderValue"];
9961
- function FixtureCapabilityChannelComponent_div_9_div_1_span_6_Template(rf, ctx) { if (rf & 1) {
10006
+ function FixtureCapabilityChannelComponent_div_9_div_1_span_5_Template(rf, ctx) { if (rf & 1) {
10007
+ i0.ɵɵelementStart(0, "span");
10008
+ i0.ɵɵtext(1);
10009
+ i0.ɵɵelementEnd();
10010
+ } if (rf & 2) {
10011
+ const capability_r3 = i0.ɵɵnextContext().$implicit;
10012
+ const ctx_r1 = i0.ɵɵnextContext(2);
10013
+ i0.ɵɵadvance();
10014
+ i0.ɵɵtextInterpolate1(" (", ctx_r1.getDescription(capability_r3), ")");
10015
+ } }
10016
+ function FixtureCapabilityChannelComponent_div_9_div_1_div_7_Template(rf, ctx) { if (rf & 1) {
10017
+ i0.ɵɵelement(0, "div", 20);
10018
+ } if (rf & 2) {
10019
+ const color_r4 = ctx.$implicit;
10020
+ i0.ɵɵstyleProp("background-color", color_r4);
10021
+ } }
10022
+ function FixtureCapabilityChannelComponent_div_9_div_1_small_8_Template(rf, ctx) { if (rf & 1) {
10023
+ i0.ɵɵelementStart(0, "small");
10024
+ i0.ɵɵtext(1);
10025
+ i0.ɵɵelementEnd();
10026
+ } if (rf & 2) {
10027
+ const capability_r3 = i0.ɵɵnextContext().$implicit;
10028
+ const ctx_r1 = i0.ɵɵnextContext(2);
10029
+ i0.ɵɵadvance();
10030
+ i0.ɵɵtextInterpolate2(" (", ctx_r1.getDescriptionStart(capability_r3), " to ", ctx_r1.getDescriptionEnd(capability_r3), ")");
10031
+ } }
10032
+ function FixtureCapabilityChannelComponent_div_9_div_1_span_9_Template(rf, ctx) { if (rf & 1) {
9962
10033
  i0.ɵɵelementStart(0, "span");
9963
10034
  i0.ɵɵtext(1, " - ");
9964
10035
  i0.ɵɵelementEnd();
9965
10036
  } }
10037
+ function FixtureCapabilityChannelComponent_div_9_div_1_i_11_Template(rf, ctx) { if (rf & 1) {
10038
+ i0.ɵɵelement(0, "i", 21);
10039
+ i0.ɵɵpipe(1, "translate");
10040
+ } if (rf & 2) {
10041
+ i0.ɵɵpropertyInterpolate("popover", i0.ɵɵpipeBind1(1, 1, "designer.fixture.capability-sound-controlled"));
10042
+ } }
10043
+ function FixtureCapabilityChannelComponent_div_9_div_1_i_12_Template(rf, ctx) { if (rf & 1) {
10044
+ i0.ɵɵelement(0, "i", 22);
10045
+ i0.ɵɵpipe(1, "translate");
10046
+ } if (rf & 2) {
10047
+ i0.ɵɵpropertyInterpolate("popover", i0.ɵɵpipeBind1(1, 1, "designer.fixture.capability-random-timing"));
10048
+ } }
9966
10049
  function FixtureCapabilityChannelComponent_div_9_div_1_Template(rf, ctx) { if (rf & 1) {
9967
10050
  const _r1 = i0.ɵɵgetCurrentView();
9968
10051
  i0.ɵɵelementStart(0, "div", 5)(1, "input", 14);
@@ -9972,31 +10055,44 @@ function FixtureCapabilityChannelComponent_div_9_div_1_Template(rf, ctx) { if (r
9972
10055
  i0.ɵɵelementStart(2, "label", 15);
9973
10056
  i0.ɵɵtext(3);
9974
10057
  i0.ɵɵpipe(4, "translate");
9975
- i0.ɵɵelementStart(5, "small");
9976
- i0.ɵɵtemplate(6, FixtureCapabilityChannelComponent_div_9_div_1_span_6_Template, 2, 0, "span", 16);
9977
- i0.ɵɵtext(7);
9978
- i0.ɵɵelementEnd()()();
10058
+ i0.ɵɵtemplate(5, FixtureCapabilityChannelComponent_div_9_div_1_span_5_Template, 2, 1, "span", 16);
10059
+ i0.ɵɵelementStart(6, "small");
10060
+ i0.ɵɵtemplate(7, FixtureCapabilityChannelComponent_div_9_div_1_div_7_Template, 1, 2, "div", 17)(8, FixtureCapabilityChannelComponent_div_9_div_1_small_8_Template, 2, 2, "small", 16)(9, FixtureCapabilityChannelComponent_div_9_div_1_span_9_Template, 2, 0, "span", 16);
10061
+ i0.ɵɵtext(10);
10062
+ i0.ɵɵelementEnd();
10063
+ i0.ɵɵtemplate(11, FixtureCapabilityChannelComponent_div_9_div_1_i_11_Template, 2, 3, "i", 18)(12, FixtureCapabilityChannelComponent_div_9_div_1_i_12_Template, 2, 3, "i", 19);
10064
+ i0.ɵɵelementEnd()();
9979
10065
  } if (rf & 2) {
9980
10066
  const capability_r3 = ctx.$implicit;
9981
- const i_r4 = ctx.index;
10067
+ const i_r5 = ctx.index;
9982
10068
  const ctx_r1 = i0.ɵɵnextContext(2);
9983
10069
  i0.ɵɵadvance();
9984
10070
  i0.ɵɵpropertyInterpolate1("name", "capabilityGeneric", ctx_r1.capabilityIndex, "Option");
9985
- i0.ɵɵpropertyInterpolate2("id", "capabilityGeneric", ctx_r1.capabilityIndex, "Option", i_r4, "");
10071
+ i0.ɵɵpropertyInterpolate2("id", "capabilityGeneric", ctx_r1.capabilityIndex, "Option", i_r5, "");
9986
10072
  i0.ɵɵtwoWayProperty("ngModel", ctx_r1.selectedCapability);
9987
10073
  i0.ɵɵproperty("value", capability_r3);
9988
10074
  i0.ɵɵadvance();
9989
- i0.ɵɵpropertyInterpolate2("for", "capabilityGeneric", ctx_r1.capabilityIndex, "Option", i_r4, "");
10075
+ i0.ɵɵpropertyInterpolate2("for", "capabilityGeneric", ctx_r1.capabilityIndex, "Option", i_r5, "");
10076
+ i0.ɵɵadvance();
10077
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(4, 18, "designer.fixtureCapabilityType." + capability_r3.capability.type), "");
10078
+ i0.ɵɵadvance(2);
10079
+ i0.ɵɵproperty("ngIf", ctx_r1.getDescription(capability_r3));
10080
+ i0.ɵɵadvance(2);
10081
+ i0.ɵɵproperty("ngForOf", capability_r3.capability.colors);
10082
+ i0.ɵɵadvance();
10083
+ i0.ɵɵproperty("ngIf", ctx_r1.getDescriptionStart(capability_r3));
9990
10084
  i0.ɵɵadvance();
9991
- i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(4, 13, "designer.fixtureCapabilityType." + capability_r3.capability.type), "");
9992
- i0.ɵɵadvance(3);
9993
10085
  i0.ɵɵproperty("ngIf", capability_r3.capability.comment);
9994
10086
  i0.ɵɵadvance();
9995
10087
  i0.ɵɵtextInterpolate1(" ", capability_r3.capability.comment, "");
10088
+ i0.ɵɵadvance();
10089
+ i0.ɵɵproperty("ngIf", capability_r3.capability.soundControlled);
10090
+ i0.ɵɵadvance();
10091
+ i0.ɵɵproperty("ngIf", capability_r3.capability.randomTiming);
9996
10092
  } }
9997
10093
  function FixtureCapabilityChannelComponent_div_9_Template(rf, ctx) { if (rf & 1) {
9998
10094
  i0.ɵɵelementStart(0, "div", 12);
9999
- i0.ɵɵtemplate(1, FixtureCapabilityChannelComponent_div_9_div_1_Template, 8, 15, "div", 13);
10095
+ i0.ɵɵtemplate(1, FixtureCapabilityChannelComponent_div_9_div_1_Template, 13, 20, "div", 13);
10000
10096
  i0.ɵɵelementEnd();
10001
10097
  } if (rf & 2) {
10002
10098
  const ctx_r1 = i0.ɵɵnextContext();
@@ -10004,20 +10100,30 @@ function FixtureCapabilityChannelComponent_div_9_Template(rf, ctx) { if (rf & 1)
10004
10100
  i0.ɵɵproperty("ngForOf", ctx_r1._channel.capabilities);
10005
10101
  } }
10006
10102
  function FixtureCapabilityChannelComponent_div_10_Template(rf, ctx) { if (rf & 1) {
10007
- const _r5 = i0.ɵɵgetCurrentView();
10008
- i0.ɵɵelementStart(0, "div", 17)(1, "div", 18)(2, "input", 19, 0);
10009
- i0.ɵɵlistener("ngModelChange", function FixtureCapabilityChannelComponent_div_10_Template_input_ngModelChange_2_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setValue($event)); });
10103
+ const _r6 = i0.ɵɵgetCurrentView();
10104
+ i0.ɵɵelementStart(0, "div", 23)(1, "div", 24)(2, "input", 25, 0);
10105
+ i0.ɵɵlistener("ngModelChange", function FixtureCapabilityChannelComponent_div_10_Template_input_ngModelChange_2_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setValue($event)); });
10106
+ i0.ɵɵelementEnd()();
10107
+ i0.ɵɵelementStart(4, "div", 26);
10108
+ i0.ɵɵtext(5);
10109
+ i0.ɵɵelementEnd();
10110
+ i0.ɵɵelementStart(6, "div", 27)(7, "mv-slider", 28, 1);
10111
+ i0.ɵɵlistener("change", function FixtureCapabilityChannelComponent_div_10_Template_mv_slider_change_7_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setValue($event.newValue)); });
10112
+ i0.ɵɵelementEnd()();
10113
+ i0.ɵɵelementStart(9, "div", 29);
10114
+ i0.ɵɵtext(10);
10010
10115
  i0.ɵɵelementEnd()();
10011
- i0.ɵɵelementStart(4, "div", 20)(5, "mv-slider", 21, 1);
10012
- i0.ɵɵlistener("change", function FixtureCapabilityChannelComponent_div_10_Template_mv_slider_change_5_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setValue($event.newValue)); });
10013
- i0.ɵɵelementEnd()()();
10014
10116
  } if (rf & 2) {
10015
10117
  const ctx_r1 = i0.ɵɵnextContext();
10016
10118
  i0.ɵɵclassProp("pl-0", (ctx_r1._channel.capabilities == null ? null : ctx_r1._channel.capabilities.length) > 1);
10017
10119
  i0.ɵɵadvance(2);
10018
10120
  i0.ɵɵproperty("ngModel", ctx_r1.templateValue);
10019
10121
  i0.ɵɵadvance(3);
10122
+ i0.ɵɵtextInterpolate(ctx_r1.descriptionEnd);
10123
+ i0.ɵɵadvance(2);
10020
10124
  i0.ɵɵproperty("value", ctx_r1.templateValue)("orientation", "vertical")("reversed", true)("tooltip", "hide")("min", ctx_r1.rangeMin)("max", ctx_r1.rangeMax)("step", 1);
10125
+ i0.ɵɵadvance(3);
10126
+ i0.ɵɵtextInterpolate(ctx_r1.descriptionStart);
10021
10127
  } }
10022
10128
  class FixtureCapabilityChannelComponent {
10023
10129
  set channel(value) {
@@ -10026,11 +10132,12 @@ class FixtureCapabilityChannelComponent {
10026
10132
  }
10027
10133
  constructor(presetService) {
10028
10134
  this.presetService = presetService;
10029
- this.rangeMin = 0;
10030
- this.rangeMax = 0;
10031
10135
  this.defaultValue = 0;
10032
10136
  this.value = 0;
10033
10137
  this.templateValue = 0;
10138
+ this.hasRange = false;
10139
+ this.rangeMin = 0;
10140
+ this.rangeMax = 0;
10034
10141
  }
10035
10142
  updateChannel() {
10036
10143
  this.selectedCapability = this._channel.capabilities[0];
@@ -10038,25 +10145,107 @@ class FixtureCapabilityChannelComponent {
10038
10145
  this.calculateTemplateValue();
10039
10146
  if (this.value >= 0) {
10040
10147
  for (const capability of this._channel.capabilities) {
10041
- if (capability.capability.dmxRange.length > 0 && capability.centerValue === this.value) {
10148
+ if (capability.capability.dmxRange.length > 0 &&
10149
+ capability.capability.dmxRange[0] <= this.value &&
10150
+ this.value <= capability.capability.dmxRange[1]) {
10042
10151
  this.selectedCapability = capability;
10043
10152
  break;
10044
10153
  }
10045
10154
  }
10046
10155
  }
10156
+ this.defaultValue = this.getDefaultValue();
10157
+ this.description = this.getDescription(this.selectedCapability);
10158
+ this.hasRange = this.capabilityHasRange();
10047
10159
  this.rangeMin = this.getRangeMin();
10048
10160
  this.rangeMax = this.getRangeMax();
10049
- this.defaultValue = this.getDefaultValue();
10161
+ this.descriptionStart = this.getDescriptionStart(this.selectedCapability);
10162
+ this.descriptionEnd = this.getDescriptionEnd(this.selectedCapability);
10050
10163
  }
10051
10164
  capabilityHasRange() {
10052
- if (this._channel.capabilities && this._channel.capabilities.length === 1) {
10165
+ if (this._channel?.capabilities?.length === 1) {
10166
+ // TODO does this work in all cases? if we have no option, it's always a range?
10053
10167
  return true;
10054
10168
  }
10055
- if (this.selectedCapability && this.selectedCapability.capability.angleStart) {
10169
+ if (this.selectedCapability?.capability.angleStart ||
10170
+ this.selectedCapability?.capability.speedStart ||
10171
+ this.selectedCapability?.capability.brightnessStart ||
10172
+ this.selectedCapability?.capability.durationStart ||
10173
+ this.selectedCapability?.capability.colorTemperatureStart ||
10174
+ this.selectedCapability?.capability.soundSensitivityStart ||
10175
+ this.selectedCapability?.capability.horizontalAngleStart ||
10176
+ this.selectedCapability?.capability.verticalAngleStart ||
10177
+ this.selectedCapability?.capability.distanceStart ||
10178
+ this.selectedCapability?.capability.openPercentStart ||
10179
+ this.selectedCapability?.capability.frostIntensityStart ||
10180
+ this.selectedCapability?.capability.fogOutputStart ||
10181
+ this.selectedCapability?.capability.timeStart ||
10182
+ this.selectedCapability?.capability.insertionStart ||
10183
+ this.selectedCapability?.capability.colorsStart) {
10056
10184
  return true;
10057
10185
  }
10058
10186
  return false;
10059
10187
  }
10188
+ getDescriptionStart(capability) {
10189
+ if (!capability || !capability.capability) {
10190
+ return '';
10191
+ }
10192
+ return (capability.capability.angleStart ||
10193
+ capability.capability.speedStart ||
10194
+ capability.capability.brightnessStart ||
10195
+ capability.capability.durationStart ||
10196
+ capability.capability.colorTemperatureStart ||
10197
+ capability.capability.soundSensitivityStart ||
10198
+ capability.capability.horizontalAngleStart ||
10199
+ capability.capability.verticalAngleStart ||
10200
+ capability.capability.distanceStart ||
10201
+ capability.capability.openPercentStart ||
10202
+ capability.capability.frostIntensityStart ||
10203
+ capability.capability.fogOutputStart ||
10204
+ capability.capability.timeStart ||
10205
+ capability.capability.insertionStart ||
10206
+ capability.capability.colorsStart)?.toString();
10207
+ }
10208
+ getDescriptionEnd(capability) {
10209
+ if (!capability || !capability.capability) {
10210
+ return '';
10211
+ }
10212
+ return (capability.capability.angleEnd ||
10213
+ capability.capability.speedEnd ||
10214
+ capability.capability.brightnessEnd ||
10215
+ capability.capability.durationEnd ||
10216
+ capability.capability.colorTemperatureEnd ||
10217
+ capability.capability.soundSensitivityEnd ||
10218
+ capability.capability.horizontalAngleEnd ||
10219
+ capability.capability.verticalAngleEnd ||
10220
+ capability.capability.distanceEnd ||
10221
+ capability.capability.openPercentEnd ||
10222
+ capability.capability.frostIntensityEnd ||
10223
+ capability.capability.fogOutputEnd ||
10224
+ capability.capability.timeEnd ||
10225
+ capability.capability.insertionEnd ||
10226
+ capability.capability.colorsEnd)?.toString();
10227
+ }
10228
+ getDescription(capability) {
10229
+ if (!capability || !capability.capability) {
10230
+ return '';
10231
+ }
10232
+ return (capability.capability.slotNumber ||
10233
+ capability.capability.shutterEffect ||
10234
+ capability.capability.angle ||
10235
+ capability.capability.speed ||
10236
+ capability.capability.brightness ||
10237
+ capability.capability.duration ||
10238
+ capability.capability.colorTemperature ||
10239
+ capability.capability.soundSensitivity ||
10240
+ capability.capability.horizontalAngle ||
10241
+ capability.capability.verticalAngle ||
10242
+ capability.capability.distance ||
10243
+ capability.capability.openPercent ||
10244
+ capability.capability.frostIntensity ||
10245
+ capability.capability.fogOutput ||
10246
+ capability.capability.time ||
10247
+ capability.capability.insertion)?.toString();
10248
+ }
10060
10249
  getRangeMin() {
10061
10250
  if (this.selectedCapability.capability.dmxRange.length > 0) {
10062
10251
  return this.selectedCapability.capability.dmxRange[0];
@@ -10074,7 +10263,7 @@ class FixtureCapabilityChannelComponent {
10074
10263
  if (isNaN(value)) {
10075
10264
  return;
10076
10265
  }
10077
- if (!ignoreCapabilityRange && (value < this.getRangeMin() || value > this.getRangeMax())) {
10266
+ if (!ignoreCapabilityRange && (value < this.rangeMin || value > this.rangeMax)) {
10078
10267
  return;
10079
10268
  }
10080
10269
  this.value = value;
@@ -10086,7 +10275,9 @@ class FixtureCapabilityChannelComponent {
10086
10275
  // TODO use the same technique in the dimmer/pan/tilt/color sliders
10087
10276
  if (!this.valueSetTimer) {
10088
10277
  this.valueSetTimer = setTimeout(() => {
10089
- this.sliderValue.nativeElement.value = this.value;
10278
+ if (this.sliderValue) {
10279
+ this.sliderValue.nativeElement.value = this.value;
10280
+ }
10090
10281
  this.valueSetTimer = undefined;
10091
10282
  }, 30);
10092
10283
  }
@@ -10116,12 +10307,13 @@ class FixtureCapabilityChannelComponent {
10116
10307
  }
10117
10308
  capabilitySelected() {
10118
10309
  // select the center value of the selected capability
10119
- if (this.capabilityHasRange) {
10310
+ if (this.capabilityHasRange()) {
10120
10311
  this.setValue(this.selectedCapability.capability.dmxRange[0], true);
10121
10312
  }
10122
10313
  else {
10123
10314
  this.setValue(this.selectedCapability.centerValue, true);
10124
10315
  }
10316
+ this.updateChannel();
10125
10317
  }
10126
10318
  static { this.ɵfac = function FixtureCapabilityChannelComponent_Factory(t) { return new (t || FixtureCapabilityChannelComponent)(i0.ɵɵdirectiveInject(PresetService)); }; }
10127
10319
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FixtureCapabilityChannelComponent, selectors: [["lib-app-fixture-capability-channel"]], viewQuery: function FixtureCapabilityChannelComponent_Query(rf, ctx) { if (rf & 1) {
@@ -10129,7 +10321,7 @@ class FixtureCapabilityChannelComponent {
10129
10321
  } if (rf & 2) {
10130
10322
  let _t;
10131
10323
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.sliderValue = _t.first);
10132
- } }, inputs: { profile: "profile", channel: "channel", capabilityIndex: "capabilityIndex" }, decls: 11, vars: 10, consts: [["sliderValue", ""], ["slider", ""], [1, "card", "border-secondary", "h-100", 2, "min-height", "200px"], [1, "card-header"], [1, "my-auto"], [1, "form-check"], ["type", "checkbox", 1, "form-check-input", 3, "ngModelChange", "id", "ngModel"], [1, "form-check-label", 3, "for"], [1, "card-body", "h-100", "d-flex", 2, "flex-direction", "column", "overflow", "hidden"], [1, "row", "h-100"], ["class", "col mr-3", "style", "overflow-y: auto", 4, "ngIf"], ["class", "col col-auto d-flex mx-auto", "style", "flex-direction: column", 3, "pl-0", 4, "ngIf"], [1, "col", "mr-3", 2, "overflow-y", "auto"], ["class", "form-check", 4, "ngFor", "ngForOf"], ["type", "radio", 1, "form-check-input", 3, "ngModelChange", "change", "ngModel", "name", "id", "value"], [1, "form-check-label", 2, "max-width", "200px", 3, "for"], [4, "ngIf"], [1, "col", "col-auto", "d-flex", "mx-auto", 2, "flex-direction", "column"], [1, "mb-4", "w-100", "d-flex"], ["type", "text", 1, "mx-auto", 2, "width", "45px", "height", "20px", "text-align", "center", "font-size", "12px", "font-family", "monospace", 3, "ngModelChange", "ngModel"], [1, "h-100", "w-100", "d-flex"], [1, "mx-auto", 2, "height", "100%", 3, "change", "value", "orientation", "reversed", "tooltip", "min", "max", "step"]], template: function FixtureCapabilityChannelComponent_Template(rf, ctx) { if (rf & 1) {
10324
+ } }, inputs: { profile: "profile", channel: "channel", capabilityIndex: "capabilityIndex" }, decls: 11, vars: 10, consts: [["sliderValue", ""], ["slider", ""], [1, "card", "border-secondary", "h-100", 2, "min-height", "200px"], [1, "card-header"], [1, "my-auto"], [1, "form-check"], ["type", "checkbox", 1, "form-check-input", 3, "ngModelChange", "id", "ngModel"], [1, "form-check-label", 3, "for"], [1, "card-body", "h-100", "d-flex", 2, "flex-direction", "column", "overflow", "hidden"], [1, "row", "h-100"], ["class", "col mr-3", "style", "overflow-y: auto", 4, "ngIf"], ["class", "col col-auto d-flex mx-auto", "style", "flex-direction: column", 3, "pl-0", 4, "ngIf"], [1, "col", "mr-3", 2, "overflow-y", "auto"], ["class", "form-check", 4, "ngFor", "ngForOf"], ["type", "radio", 1, "form-check-input", 3, "ngModelChange", "change", "ngModel", "name", "id", "value"], [1, "form-check-label", 2, "max-width", "200px", 3, "for"], [4, "ngIf"], ["class", "color", 3, "background-color", 4, "ngFor", "ngForOf"], ["class", "fa fa-microphone pl-1", "container", "body", "placement", "top", "triggers", "mouseenter:mouseleave", "aria-hidden", "true", 3, "popover", 4, "ngIf"], ["class", "fa fa-random pl-1", "container", "body", "placement", "top", "triggers", "mouseenter:mouseleave", "aria-hidden", "true", 3, "popover", 4, "ngIf"], [1, "color"], ["container", "body", "placement", "top", "triggers", "mouseenter:mouseleave", "aria-hidden", "true", 1, "fa", "fa-microphone", "pl-1", 3, "popover"], ["container", "body", "placement", "top", "triggers", "mouseenter:mouseleave", "aria-hidden", "true", 1, "fa", "fa-random", "pl-1", 3, "popover"], [1, "col", "col-auto", "d-flex", "mx-auto", 2, "flex-direction", "column"], [1, "mb-2", "w-100", "d-flex"], ["type", "text", 1, "mx-auto", 2, "width", "45px", "height", "20px", "text-align", "center", "font-size", "12px", "font-family", "monospace", 3, "ngModelChange", "ngModel"], [1, "mx-auto", "mb-2"], [1, "h-100", "w-100", "d-flex"], [1, "mx-auto", 2, "height", "100%", 3, "change", "value", "orientation", "reversed", "tooltip", "min", "max", "step"], [1, "mx-auto", "mt-2"]], template: function FixtureCapabilityChannelComponent_Template(rf, ctx) { if (rf & 1) {
10133
10325
  i0.ɵɵelementStart(0, "div", 2)(1, "div", 3)(2, "div", 4)(3, "div", 5)(4, "input", 6);
10134
10326
  i0.ɵɵlistener("ngModelChange", function FixtureCapabilityChannelComponent_Template_input_ngModelChange_4_listener($event) { return ctx.changeActive($event); });
10135
10327
  i0.ɵɵelementEnd();
@@ -10137,7 +10329,7 @@ class FixtureCapabilityChannelComponent {
10137
10329
  i0.ɵɵtext(6);
10138
10330
  i0.ɵɵelementEnd()()()();
10139
10331
  i0.ɵɵelementStart(7, "div", 8)(8, "div", 9);
10140
- i0.ɵɵtemplate(9, FixtureCapabilityChannelComponent_div_9_Template, 2, 1, "div", 10)(10, FixtureCapabilityChannelComponent_div_10_Template, 7, 10, "div", 11);
10332
+ i0.ɵɵtemplate(9, FixtureCapabilityChannelComponent_div_9_Template, 2, 1, "div", 10)(10, FixtureCapabilityChannelComponent_div_10_Template, 11, 12, "div", 11);
10141
10333
  i0.ɵɵelementEnd()()();
10142
10334
  } if (rf & 2) {
10143
10335
  i0.ɵɵclassProp("capability-deactivated", ctx.value == undefined);
@@ -10151,12 +10343,12 @@ class FixtureCapabilityChannelComponent {
10151
10343
  i0.ɵɵadvance(3);
10152
10344
  i0.ɵɵproperty("ngIf", (ctx._channel.capabilities == null ? null : ctx._channel.capabilities.length) > 1);
10153
10345
  i0.ɵɵadvance();
10154
- i0.ɵɵproperty("ngIf", ctx.capabilityHasRange());
10155
- } }, dependencies: [i5.NgForOf, i5.NgIf, i4.DefaultValueAccessor, i4.CheckboxControlValueAccessor, i4.RadioControlValueAccessor, i4.NgControlStatus, i4.NgModel, i4$1.SliderComponent, i2.TranslatePipe], changeDetection: 0 }); }
10346
+ i0.ɵɵproperty("ngIf", ctx.hasRange);
10347
+ } }, dependencies: [i5.NgForOf, i5.NgIf, i4.DefaultValueAccessor, i4.CheckboxControlValueAccessor, i4.RadioControlValueAccessor, i4.NgControlStatus, i4.NgModel, i4$1.SliderComponent, i12.PopoverDirective, i2.TranslatePipe], styles: [".color[_ngcontent-%COMP%]{display:inline-block;margin-left:5px;border-radius:50%;width:12px;height:12px;border:1px white solid;vertical-align:middle}"], changeDetection: 0 }); }
10156
10348
  }
10157
10349
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FixtureCapabilityChannelComponent, [{
10158
10350
  type: Component,
10159
- args: [{ selector: 'lib-app-fixture-capability-channel', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"card border-secondary h-100\" [class.capability-deactivated]=\"value == undefined\" style=\"min-height: 200px\">\n <div class=\"card-header\">\n <div class=\"my-auto\">\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n id=\"capabilityGeneric{{ capabilityIndex }}Active\"\n [ngModel]=\"value != undefined\"\n (ngModelChange)=\"changeActive($event)\"\n />\n <label class=\"form-check-label\" for=\"capabilityGeneric{{ capabilityIndex }}Active\">\n {{ _channel?.name }}\n </label>\n </div>\n </div>\n </div>\n\n <div class=\"card-body h-100 d-flex\" style=\"flex-direction: column; overflow: hidden\">\n <div class=\"row h-100\">\n <!-- display all options, if there is more than one -->\n <div *ngIf=\"_channel.capabilities?.length > 1\" class=\"col mr-3\" style=\"overflow-y: auto\">\n <div *ngFor=\"let capability of _channel.capabilities; let i = index\" class=\"form-check\">\n <input\n [(ngModel)]=\"selectedCapability\"\n (change)=\"capabilitySelected()\"\n class=\"form-check-input\"\n type=\"radio\"\n name=\"capabilityGeneric{{ capabilityIndex }}Option\"\n id=\"capabilityGeneric{{ capabilityIndex }}Option{{ i }}\"\n [value]=\"capability\"\n />\n <label style=\"max-width: 200px\" class=\"form-check-label\" for=\"capabilityGeneric{{ capabilityIndex }}Option{{ i }}\">\n {{ 'designer.fixtureCapabilityType.' + capability.capability.type | translate\n }}<small><span *ngIf=\"capability.capability.comment\"> - </span> {{ capability.capability.comment }}</small>\n </label>\n </div>\n </div>\n\n <!-- display a slider to select the value for the currently selected capability, if it has a range -->\n <div\n *ngIf=\"capabilityHasRange()\"\n class=\"col col-auto d-flex mx-auto\"\n [class.pl-0]=\"_channel.capabilities?.length > 1\"\n style=\"flex-direction: column\"\n >\n <div class=\"mb-4 w-100 d-flex\">\n <input\n #sliderValue\n class=\"mx-auto\"\n type=\"text\"\n [ngModel]=\"templateValue\"\n (ngModelChange)=\"setValue($event)\"\n style=\"width: 45px; height: 20px; text-align: center; font-size: 12px; font-family: monospace\"\n />\n </div>\n\n <div class=\"h-100 w-100 d-flex\">\n <mv-slider\n #slider\n class=\"mx-auto\"\n [value]=\"templateValue\"\n (change)=\"setValue($event.newValue)\"\n [orientation]=\"'vertical'\"\n [reversed]=\"true\"\n style=\"height: 100%\"\n [tooltip]=\"'hide'\"\n [min]=\"rangeMin\"\n [max]=\"rangeMax\"\n [step]=\"1\"\n ></mv-slider>\n </div>\n </div>\n </div>\n </div>\n</div>\n" }]
10351
+ args: [{ selector: 'lib-app-fixture-capability-channel', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"card border-secondary h-100\" [class.capability-deactivated]=\"value == undefined\" style=\"min-height: 200px\">\n <div class=\"card-header\">\n <div class=\"my-auto\">\n <div class=\"form-check\">\n <input\n type=\"checkbox\"\n class=\"form-check-input\"\n id=\"capabilityGeneric{{ capabilityIndex }}Active\"\n [ngModel]=\"value != undefined\"\n (ngModelChange)=\"changeActive($event)\"\n />\n <label class=\"form-check-label\" for=\"capabilityGeneric{{ capabilityIndex }}Active\">\n {{ _channel?.name }}\n </label>\n </div>\n </div>\n </div>\n\n <div class=\"card-body h-100 d-flex\" style=\"flex-direction: column; overflow: hidden\">\n <div class=\"row h-100\">\n <!-- display all options, if there is more than one -->\n <div *ngIf=\"_channel.capabilities?.length > 1\" class=\"col mr-3\" style=\"overflow-y: auto\">\n <div *ngFor=\"let capability of _channel.capabilities; let i = index\" class=\"form-check\">\n <input\n [(ngModel)]=\"selectedCapability\"\n (change)=\"capabilitySelected()\"\n class=\"form-check-input\"\n type=\"radio\"\n name=\"capabilityGeneric{{ capabilityIndex }}Option\"\n id=\"capabilityGeneric{{ capabilityIndex }}Option{{ i }}\"\n [value]=\"capability\"\n />\n <label style=\"max-width: 200px\" class=\"form-check-label\" for=\"capabilityGeneric{{ capabilityIndex }}Option{{ i }}\">\n {{ 'designer.fixtureCapabilityType.' + capability.capability.type | translate\n }}<span *ngIf=\"getDescription(capability)\"> ({{ getDescription(capability) }})</span\n ><small\n ><div *ngFor=\"let color of capability.capability.colors\" class=\"color\" [style.background-color]=\"color\"></div>\n <small *ngIf=\"getDescriptionStart(capability)\">\n ({{ getDescriptionStart(capability) }} to {{ getDescriptionEnd(capability) }})</small\n >\n <span *ngIf=\"capability.capability.comment\"> - </span> {{ capability.capability.comment }}</small\n >\n <i\n *ngIf=\"capability.capability.soundControlled\"\n class=\"fa fa-microphone pl-1\"\n popover=\"{{ 'designer.fixture.capability-sound-controlled' | translate }}\"\n container=\"body\"\n placement=\"top\"\n triggers=\"mouseenter:mouseleave\"\n aria-hidden=\"true\"\n ></i>\n <i\n *ngIf=\"capability.capability.randomTiming\"\n class=\"fa fa-random pl-1\"\n popover=\"{{ 'designer.fixture.capability-random-timing' | translate }}\"\n container=\"body\"\n placement=\"top\"\n triggers=\"mouseenter:mouseleave\"\n aria-hidden=\"true\"\n ></i>\n </label>\n </div>\n </div>\n\n <!-- display a slider to select the value for the currently selected capability, if it has a range -->\n <div\n *ngIf=\"hasRange\"\n class=\"col col-auto d-flex mx-auto\"\n [class.pl-0]=\"_channel.capabilities?.length > 1\"\n style=\"flex-direction: column\"\n >\n <div class=\"mb-2 w-100 d-flex\">\n <input\n #sliderValue\n class=\"mx-auto\"\n type=\"text\"\n [ngModel]=\"templateValue\"\n (ngModelChange)=\"setValue($event)\"\n style=\"width: 45px; height: 20px; text-align: center; font-size: 12px; font-family: monospace\"\n />\n </div>\n\n <div class=\"mx-auto mb-2\">{{ descriptionEnd }}</div>\n\n <div class=\"h-100 w-100 d-flex\">\n <mv-slider\n #slider\n class=\"mx-auto\"\n [value]=\"templateValue\"\n (change)=\"setValue($event.newValue)\"\n [orientation]=\"'vertical'\"\n [reversed]=\"true\"\n style=\"height: 100%\"\n [tooltip]=\"'hide'\"\n [min]=\"rangeMin\"\n [max]=\"rangeMax\"\n [step]=\"1\"\n ></mv-slider>\n </div>\n\n <div class=\"mx-auto mt-2\">{{ descriptionStart }}</div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".color{display:inline-block;margin-left:5px;border-radius:50%;width:12px;height:12px;border:1px white solid;vertical-align:middle}\n"] }]
10160
10352
  }], () => [{ type: PresetService }], { sliderValue: [{
10161
10353
  type: ViewChild,
10162
10354
  args: ['sliderValue', { static: false }]