@@ -29,6 +29,8 @@ import { onKeyUp } from '@front/util/keyboard'
29
29
import { useDarkMode } from ' @front/util/theme'
30
30
import { dimColor , boostColor } from ' @front/util/color'
31
31
import { formatTime } from ' @front/util/format'
32
+ import { Queue } from ' @front/util/queue'
33
+ import { nonReactive } from ' @front/util/reactivity'
32
34
33
35
PIXI .settings .ROUND_PIXELS = true
34
36
@@ -46,8 +48,15 @@ export default defineComponent({
46
48
const { startTime, endTime, minTime, maxTime } = useTime ()
47
49
const { darkMode } = useDarkMode ()
48
50
51
+ // Optimize for read in loops
52
+ const nonReactiveTime = {
53
+ startTime: nonReactive (startTime ),
54
+ endTime: nonReactive (endTime ),
55
+ minTime: nonReactive (minTime ),
56
+ }
57
+
49
58
function getTimePosition (time : number ) {
50
- return (time - minTime .value ) / (endTime .value - startTime .value ) * app .view .width
59
+ return (time - nonReactiveTime . minTime .value ) / (nonReactiveTime . endTime .value - nonReactiveTime . startTime .value ) * app .view .width
51
60
}
52
61
53
62
// Reset
@@ -303,34 +312,33 @@ export default defineComponent({
303
312
304
313
let events: TimelineEvent [] = []
305
314
306
- const updateEventPositionQueue = new Set <TimelineEvent >()
307
- let currentEventPositionUpdate: TimelineEvent = null
308
- let updateEventPositionQueued = false
315
+ const updateEventPositionQueue = new Queue <TimelineEvent >()
316
+ let eventPositionUpdateInProgress = false
309
317
310
- function queueEventPositionUpdate (... events : TimelineEvent []) {
318
+ function queueEventPositionUpdate (events : TimelineEvent [], force = false ) {
311
319
for (const e of events ) {
312
320
if (! e .container ) continue
313
321
const ignored = isEventIgnored (e )
314
322
e .container .visible = ! ignored
315
323
if (ignored ) continue
316
324
// Update horizontal position immediately
317
325
e .container .x = Math .round (getTimePosition (e .time ))
326
+ if (! force && e .layer .groupsOnly ) continue
318
327
// Queue vertical position compute
319
328
updateEventPositionQueue .add (e )
320
329
}
321
- if (! updateEventPositionQueued ) {
322
- updateEventPositionQueued = true
330
+ if (! eventPositionUpdateInProgress ) {
331
+ eventPositionUpdateInProgress = true
323
332
Vue .nextTick (() => {
324
333
nextEventPositionUpdate ()
325
- updateEventPositionQueued = false
334
+ eventPositionUpdateInProgress = false
326
335
})
327
336
}
328
337
}
329
338
330
339
function nextEventPositionUpdate () {
331
- if (currentEventPositionUpdate ) return
332
- const event = currentEventPositionUpdate = updateEventPositionQueue .values ().next ().value
333
- if (event ) {
340
+ let event: TimelineEvent
341
+ while ((event = updateEventPositionQueue .shift ())) {
334
342
computeEventVerticalPosition (event )
335
343
}
336
344
}
@@ -340,53 +348,73 @@ export default defineComponent({
340
348
}
341
349
342
350
function computeEventVerticalPosition (event : TimelineEvent ) {
343
- // Skip if the event is not visible
344
- // or if the group graphics is not visible
345
- if ((event .time >= startTime .value && event .time <= endTime .value ) ||
346
- (event .group ?.firstEvent === event && event .group .lastEvent .time >= startTime .value && event .group .lastEvent .time <= endTime .value )) {
351
+ let y = 0
352
+ if (event .group && event !== event .group .firstEvent ) {
353
+ // If the event is inside a group, just use the group position
354
+ y = event .group .y
355
+ } else {
356
+ const firstEvent = event .group ? event .group .firstEvent : event
357
+ const lastEvent = event .group ? event .group .lastEvent : event
358
+
347
359
// Collision offset for non-flamecharts
348
360
const offset = event .layer .groupsOnly ? 0 : 12
349
-
350
- let y = 0
351
- if (event .group && event !== event .group .firstEvent ) {
352
- // If the event is inside a group, just use the group position
353
- y = event .group .y
354
- } else {
355
- const firstEvent = event .group ? event .group .firstEvent : event
356
- const lastEvent = event .group ? event .group .lastEvent : event
357
- const lastOffset = event .layer .groupsOnly && event .group ?.duration > 0 ? - 1 : 0
358
- // Check for 'collision' with other event groups
359
- const l = event .layer .groups .length
360
- let checkAgain = true
361
- while (checkAgain ) {
362
- checkAgain = false
363
- for (let i = 0 ; i < l ; i ++ ) {
364
- const otherGroup = event .layer .groups [i ]
365
- if (
366
- // Different group
367
- (
368
- ! event .group ||
369
- event .group !== otherGroup
370
- ) &&
371
- // Same row
372
- otherGroup .y === y &&
373
- (
374
- // Horizontal intersection (first event)
375
- (
376
- getTimePosition (firstEvent .time ) >= getTimePosition (otherGroup .firstEvent .time ) - offset &&
377
- getTimePosition (firstEvent .time ) <= getTimePosition (otherGroup .lastEvent .time ) + offset + lastOffset
378
- ) ||
379
- // Horizontal intersection (last event)
361
+ // For flamechart allow 1-pixel overlap at the end of a group
362
+ const lastOffset = event .layer .groupsOnly && event .group ?.duration > 0 ? - 1 : 0
363
+ // Flamechart uses time instead of pixel position
364
+ const getPos = event .layer .groupsOnly ? (time : number ) => time : getTimePosition
365
+
366
+ const firstPos = getPos (firstEvent .time )
367
+ const lastPos = event .group ? getPos (lastEvent .time ) : firstPos
368
+
369
+ // Check for 'collision' with other event groups
370
+ const l = event .layer .groups .length
371
+ let checkAgain = true
372
+ while (checkAgain ) {
373
+ checkAgain = false
374
+ for (let i = 0 ; i < l ; i ++ ) {
375
+ const otherGroup = event .layer .groups [i ]
376
+
377
+ if (
378
+ // Different group
379
+ (
380
+ ! event .group ||
381
+ event .group !== otherGroup
382
+ ) &&
383
+ // Same row
384
+ otherGroup .y === y
385
+ ) {
386
+ // // eslint-disable-next-line no-console
387
+ // if (event.layer.groupsOnly) console.log('checking collision with', otherGroup.firstEvent.id, otherGroup.firstEvent.title)
388
+
389
+ const otherGroupFirstPos = getPos (otherGroup .firstEvent .time )
390
+ const otherGroupLastPos = getPos (otherGroup .lastEvent .time )
391
+
392
+ // First position is inside other group
393
+ const firstEventIntersection = (
394
+ firstPos >= otherGroupFirstPos - offset &&
395
+ firstPos <= otherGroupLastPos + offset + lastOffset
396
+ )
397
+
398
+ if (firstEventIntersection || (
399
+ // Additional checks if group
400
+ event .group && (
380
401
(
381
- getTimePosition (lastEvent .time ) >= getTimePosition (otherGroup .firstEvent .time ) - offset - lastOffset &&
382
- getTimePosition (lastEvent .time ) <= getTimePosition (otherGroup .lastEvent .time ) + offset
402
+ // Last position is inside other group
403
+ lastPos >= otherGroupFirstPos - offset - lastOffset &&
404
+ lastPos <= otherGroupLastPos + offset
405
+ ) || (
406
+ // Other group is inside current group
407
+ firstPos < otherGroupFirstPos - offset &&
408
+ lastPos > otherGroupLastPos + offset
383
409
)
384
410
)
385
- ) {
411
+ )) {
386
412
// Collision!
387
413
if (event .group && event .group .duration > otherGroup .duration && firstEvent .time <= otherGroup .firstEvent .time ) {
388
414
// Invert positions because current group has higher priority
389
- queueEventPositionUpdate (otherGroup .firstEvent )
415
+ if (! updateEventPositionQueue .has (otherGroup .firstEvent )) {
416
+ queueEventPositionUpdate ([otherGroup .firstEvent ], event .layer .groupsOnly )
417
+ }
390
418
} else {
391
419
// Offset the current group/event
392
420
y ++
@@ -397,29 +425,24 @@ export default defineComponent({
397
425
}
398
426
}
399
427
}
428
+ }
400
429
401
- // If the event is the first in a group, update group position
402
- if (event .group ) {
403
- event .group .y = y
404
- }
430
+ // If the event is the first in a group, update group position
431
+ if (event .group ) {
432
+ event .group .y = y
433
+ }
405
434
406
- // Might update the layer's height as well
407
- if (y + 1 > event .layer .height ) {
408
- const oldLayerHeight = event .layer .height
409
- const newLayerHeight = event .layer .height = y + 1
410
- if (oldLayerHeight !== newLayerHeight ) {
411
- updateLayerPositions ()
412
- drawLayerBackgroundEffects ()
413
- }
435
+ // Might update the layer's height as well
436
+ if (y + 1 > event .layer .height ) {
437
+ const oldLayerHeight = event .layer .height
438
+ const newLayerHeight = event .layer .height = y + 1
439
+ if (oldLayerHeight !== newLayerHeight ) {
440
+ updateLayerPositions ()
441
+ drawLayerBackgroundEffects ()
414
442
}
415
443
}
416
- event .container .y = (y + 1 ) * LAYER_SIZE
417
444
}
418
-
419
- // Next
420
- updateEventPositionQueue .delete (event )
421
- currentEventPositionUpdate = null
422
- nextEventPositionUpdate ()
445
+ event .container .y = (y + 1 ) * LAYER_SIZE
423
446
}
424
447
425
448
function addEvent (event : TimelineEvent , layerContainer : PIXI .Container ) {
@@ -456,7 +479,11 @@ export default defineComponent({
456
479
events .push (event )
457
480
458
481
refreshEventGraphics (event )
459
- queueEventPositionUpdate (event )
482
+ if (event .container ) {
483
+ queueEventPositionUpdate ([event ], true )
484
+ } else {
485
+ queueEventPositionUpdate ([event .group .firstEvent ], true )
486
+ }
460
487
461
488
return event
462
489
}
@@ -527,10 +554,12 @@ export default defineComponent({
527
554
528
555
function updateEvents () {
529
556
for (const layer of layers .value ) {
530
- layer .height = 1
557
+ if (! layer .groupsOnly ) {
558
+ layer .height = 1
559
+ }
531
560
}
532
561
updateLayerPositions ()
533
- queueEventPositionUpdate (... events )
562
+ queueEventPositionUpdate (events )
534
563
for (const event of events ) {
535
564
if (event .groupG ) {
536
565
drawEventGroup (event )
@@ -826,7 +855,7 @@ export default defineComponent({
826
855
if (event .layer .groupsOnly && event .title && size > 32 ) {
827
856
let t = event .groupT
828
857
if (! t ) {
829
- t = event .groupT = new PIXI .Text (` ${event .title } ${event .subtitle } ` , {
858
+ t = event .groupT = new PIXI .Text (` ${event .id } ${ event . title } ${event .subtitle } ` , {
830
859
fontSize: 10 ,
831
860
fill: darkMode .value ? 0xffffff : 0 ,
832
861
})
0 commit comments