2024-05-26 19:05:42 +00:00
|
|
|
import type { Writable } from "svelte/store";
|
|
|
|
|
2024-05-26 18:42:15 +00:00
|
|
|
export type Location = Object;
|
|
|
|
|
|
|
|
// disambiguated (physical or online) library object
|
|
|
|
export type Item = {
|
|
|
|
title: String,
|
|
|
|
MMS: String,
|
|
|
|
Barcode: String, // String because of preceding 0
|
|
|
|
Publisher: String,
|
|
|
|
_original: Object
|
|
|
|
}
|
|
|
|
|
|
|
|
// A movement of the object (Request in Alma Analytics)
|
|
|
|
export type Movement = {
|
|
|
|
nr: number, // unique identifier in the set
|
|
|
|
source: Location,
|
|
|
|
target: Location,
|
|
|
|
item: Item,
|
|
|
|
_original: Object
|
|
|
|
// also contains additional request data (see requests.csv)
|
|
|
|
d: String
|
|
|
|
};
|
|
|
|
|
2024-05-26 19:05:42 +00:00
|
|
|
export type Occurences = Map<Item, Movement[]>
|
|
|
|
|
|
|
|
export interface Data {
|
|
|
|
locations: Map<String, Location>,
|
|
|
|
items: Map<string, Item>,
|
|
|
|
movements: Movement[],
|
|
|
|
occurences: Occurences
|
|
|
|
}
|
2024-05-26 18:42:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
// An event to trigger drawing an Edge
|
|
|
|
export type Motion = {
|
|
|
|
duration: number,
|
|
|
|
movement: Movement,
|
|
|
|
}
|
|
|
|
|
2024-05-26 19:05:42 +00:00
|
|
|
export type Log = {
|
2024-05-26 18:42:15 +00:00
|
|
|
date: Date,
|
|
|
|
title: String,
|
|
|
|
description: String
|
|
|
|
}
|
|
|
|
|
2024-05-26 19:05:42 +00:00
|
|
|
|
|
|
|
// Type used by the scenes with all reactive Writables for drawable objects
|
|
|
|
export type VizData = {
|
|
|
|
drawn_motions: Writable<Motion[]>,
|
|
|
|
overlay_motions: Writable<Motion[]>,
|
|
|
|
events: Writable<Log[]>
|
|
|
|
}
|
|
|
|
|
2024-05-26 18:42:15 +00:00
|
|
|
export function get_path_d(movement: Movement) {
|
|
|
|
const m = movement;
|
|
|
|
// console.log(m)
|
|
|
|
let sourceX, targetX, midX, dx, dy, angle;
|
|
|
|
|
|
|
|
// This mess makes the arrows exactly perfect.
|
|
|
|
// thanks to http://bl.ocks.org/curran/9b73eb564c1c8a3d8f3ab207de364bf4
|
|
|
|
if (m.source.x < m.target.x) {
|
|
|
|
sourceX = m.source.x;
|
|
|
|
targetX = m.target.x;
|
|
|
|
} else if (m.target.x < m.source.x) {
|
|
|
|
targetX = m.target.x;
|
|
|
|
sourceX = m.source.x;
|
|
|
|
} else if (m.target.isCircle) {
|
|
|
|
targetX = sourceX = m.target.x;
|
|
|
|
} else if (m.source.isCircle) {
|
|
|
|
targetX = sourceX = m.source.x;
|
|
|
|
} else {
|
|
|
|
midX = (m.source.x + m.target.x) / 2;
|
|
|
|
if (midX > m.target.x) {
|
|
|
|
midX = m.target.x;
|
|
|
|
} else if (midX > m.source.x) {
|
|
|
|
midX = m.source.x;
|
|
|
|
} else if (midX < m.target.x) {
|
|
|
|
midX = m.target.x;
|
|
|
|
} else if (midX < m.source.x) {
|
|
|
|
midX = m.source.x;
|
|
|
|
}
|
|
|
|
targetX = sourceX = midX;
|
|
|
|
}
|
|
|
|
|
|
|
|
dx = targetX - sourceX;
|
|
|
|
dy = m.target.y - m.source.y;
|
|
|
|
angle = Math.atan2(dx, dy);
|
|
|
|
|
|
|
|
var srcSize = 5; //_mapGraph.getSizeForNode(m.source);
|
|
|
|
var tgtSize = 5; //_mapGraph.getSizeForNode(m.target);
|
|
|
|
|
|
|
|
// Compute the line endpoint such that the arrow
|
|
|
|
// it not in the center, but rather slightly out of it
|
|
|
|
// use a small ofset for the Movemente to compensate roughly for the curve
|
|
|
|
let m_sourceX = sourceX + Math.sin(angle + 0.5) * srcSize;
|
|
|
|
let m_targetX = targetX - Math.sin(angle - 0.5) * tgtSize;
|
|
|
|
let m_sourceY = m.source.y + Math.cos(angle + 0.5) * srcSize;
|
|
|
|
let m_targetY = m.target.y - Math.cos(angle - 0.5) * tgtSize;
|
|
|
|
|
|
|
|
// find radius of arc based on distance between points
|
|
|
|
// add a jitter to spread out the lines when links are stacked
|
|
|
|
const dr = Math.sqrt(dx * dx + dy * dy) * (0.7 + Math.random() * 0.5);
|
|
|
|
|
|
|
|
// "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y
|
|
|
|
return `M ${m_sourceX},${m_sourceY} A ${dr},${dr} 0 0,1 ${m_targetX},${m_targetY}`;
|
|
|
|
}
|