From 8a0ed6a97837a5fb8faa10ab415759b445aaa2fe Mon Sep 17 00:00:00 2001
From: Terry Truong <terry06890@gmail.com>
Date: Wed, 19 Oct 2022 13:28:56 +1100
Subject: Prevent jumps in event line angle transitions

Instead of directly using new angles, update old ones
Also prevent event lines from 'detaching' from the mainline during transitions
Also make events displayed in circles
---
 src/components/TimeLine.vue | 48 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 34 insertions(+), 14 deletions(-)

(limited to 'src/components/TimeLine.vue')

diff --git a/src/components/TimeLine.vue b/src/components/TimeLine.vue
index 77ef5cc..01561a1 100644
--- a/src/components/TimeLine.vue
+++ b/src/components/TimeLine.vue
@@ -33,7 +33,7 @@
 	</svg>
 	<!-- Events -->
 	<div v-for="id in idToPos.keys()" :key="id"
-		class="absolute bg-black text-white rounded animate-fadein text-sm"
+		class="absolute bg-black text-white rounded-full border border-amber-400 animate-fadein text-sm text-center"
 		:style="eventStyles(id)">
 		{{idToEvent.get(id)!.title}}
 	</div>
@@ -47,7 +47,7 @@
 </template>
 
 <script setup lang="ts">
-import {ref, onMounted, computed, watch, PropType, Ref} from 'vue';
+import {ref, onMounted, computed, watch, watchEffect, PropType, Ref, shallowRef, ShallowRef} from 'vue';
 // Components
 import IconButton from './IconButton.vue';
 // Icons
@@ -329,30 +329,50 @@ const idToPos = computed(() => {
 	//
 	return map;
 });
-const eventLines = computed(() => {
-	let lineCoords: Map<number, [number, number, number, number]> = new Map();
-		// Maps event ID to x, y, length, and angle
+type LineCoords = [number, number, number, number];
+const eventLines: ShallowRef<Map<number, LineCoords>> = shallowRef(new Map());
+	// Maps event ID to event line data (x, y, length, and angle)
+watchEffect(() => { // Used instead of computed() in order to access old values
+	let newEventLines: Map<number, LineCoords> = new Map();
 	let numUnits = getNumVisibleUnits();
 	for (let [id, [eventX, eventY, eventW, eventH]] of idToPos.value){
-		let x = eventX + eventW/2;
-		let y = eventY + eventH/2;
-		let x2: number;
+		let x: number; // For line end on mainline
+		let y: number;
+		let x2: number; // For line end at event
+			// Note: Drawing the line in the reverse direction causes 'detachment' from the mainline during transitions
 		let y2: number;
 		let event = idToEvent.value.get(id)!;
 		let unitOffset = getUnitDiff(event.start, startDate.value, scale.value) + startOffset.value;
 		let posFrac = unitOffset / numUnits;
 		if (props.vert){
-			x2 = availBreadth.value / 2;
-			y2 = posFrac * availLen.value;
+			x = availBreadth.value / 2;
+			y = posFrac * availLen.value;
 		} else {
-			x2 = posFrac * availLen.value;
-			y2 = availBreadth.value / 2;
+			x = posFrac * availLen.value;
+			y = availBreadth.value / 2;
 		}
+		x2 = eventX + eventW/2;
+		y2 = eventY + eventH/2;
 		let l = Math.sqrt((x-x2)**2 + (y-y2)**2);
 		let a = Math.atan2(y2-y, x2-x) * 180 / Math.PI;
-		lineCoords.set(id, [x, y, l, a]);
+		if (eventLines.value.has(id)){ // Check if event had previous angle
+			let oldA = eventLines.value.get(id)![3];
+			// Update old angle with difference, to avoid angle transition jumps (eg: change 170 to 185 instead of -175)
+			let rOldA = oldA >= 0 ? // oldA limited to -180 to 180
+				(oldA % 180) + (Math.floor(oldA / 180) % 2 == 0 ? 0 : -180) :
+				(oldA % 180) + (Math.ceil(oldA / 180) % 2 == 0 ? 0 : 180);
+			let angleDiff = (a - rOldA) % 360;
+			if (angleDiff > 180){
+				a = oldA - (360 - angleDiff);
+			} else if (angleDiff < -180){
+				a = oldA + (360 + angleDiff);
+			} else {
+				a = oldA + angleDiff;
+			}
+		}
+		newEventLines.set(id, [x, y, l, a]);
 	}
-	return lineCoords;
+	eventLines.value = newEventLines;
 });
 
 // For panning/zooming
-- 
cgit v1.2.3

