-
-
-
- {{
- selectedEmoji == null || selectedEmoji.length < 1
- ? t("noemoji")
- : selectedEmoji
- }}
+
+
-
+
+
+ +
+
+
+
+ {{
+ selectedEmoji == null || selectedEmoji.length < 1
+ ? t("noemoji")
+ : selectedEmoji
+ }}
+
-
-
-
-
-
-
-
-
-
-
@@ -115,7 +115,7 @@
@@ -128,7 +128,7 @@
@@ -203,7 +203,7 @@ export default defineComponent({
});
// generic event handlers from form to vuex
- const commitEdit = (evt: InputEvent, field: keyof Ego) => {
+ const commitEdit = (evt: UIEvent, field: keyof Ego) => {
const value = (evt.target as InputType).value.trim();
if (value !== store.state.nwk.ego[field]) {
const payload = { [field]: value };
@@ -216,7 +216,7 @@ export default defineComponent({
store.commit("editEgo", payload);
};
- function onSelectEmoji(emoji: any) {
+ function onSelectEmoji(emoji: EmojiType) {
selectedEmoji.value = emoji.i;
showEmojiPicker.value = false;
commitEditEmoji(emoji.i);
@@ -260,6 +260,8 @@ export default defineComponent({
egoName,
invalidName,
egoGender,
+ egoAge: computed(() => store.state.nwk.ego.age),
+ egoNote: computed(() => store.state.nwk.ego.note),
commitEdit,
genderOptions,
editEgoFinished,
diff --git a/src/components/StatisticsPanel.vue b/src/components/StatisticsPanel.vue
index b51f142..48394e3 100644
--- a/src/components/StatisticsPanel.vue
+++ b/src/components/StatisticsPanel.vue
@@ -1,6 +1,10 @@
@@ -35,6 +43,7 @@ import { computed, defineComponent, ref } from "vue";
import { useStore } from "@/store";
import StatisticsTable from "@/components/StatisticsTable.vue";
import StatisticsTableCategories from "@/components/StatisticsTableCategories.vue";
+import StatisticsTableClique from "@/components/StatisticsTableClique.vue";
import {
allAlterCategorizationKeys,
getAlterCategorization,
@@ -60,7 +69,11 @@ export default defineComponent({
}
},
},
- components: { StatisticsTable, StatisticsTableCategories },
+ components: {
+ StatisticsTableClique,
+ StatisticsTable,
+ StatisticsTableCategories,
+ },
setup() {
const store = useStore();
diff --git a/src/components/StatisticsTableClique.vue b/src/components/StatisticsTableClique.vue
new file mode 100644
index 0000000..c22a998
--- /dev/null
+++ b/src/components/StatisticsTableClique.vue
@@ -0,0 +1,118 @@
+
+ > {
+ const adjacencyList = new Map>();
+ for (const alter of nwk.alteri) {
+ adjacencyList.set(alter.id, new Set());
+ }
+ for (const conn of nwk.connections) {
+ adjacencyList.get(conn.id1)?.add(conn.id2);
+ adjacencyList.get(conn.id2)?.add(conn.id1);
+ }
+ return adjacencyList;
+}
+
+// Bron-Kerbosch-Algorithmus zur Berechnung der maximalen Cliquen
+function bronKerbosch(
+ R: Set,
+ P: Set,
+ X: Set,
+ cliques: Array>,
+ adjacencyList: Map>
+) {
+ if (P.size === 0 && X.size === 0) {
+ cliques.push(Array.from(R));
+ return;
+ }
+ const PArray = Array.from(P);
+ for (const v of PArray) {
+ const neighbors = adjacencyList.get(v) || new Set();
+ bronKerbosch(
+ new Set([...R, v]),
+ new Set([...P].filter((x) => neighbors.has(x))),
+ new Set([...X].filter((x) => neighbors.has(x))),
+ cliques,
+ adjacencyList
+ );
+ P.delete(v);
+ X.add(v);
+ }
+}
diff --git a/src/de.ts b/src/de.ts
index 5c99523..7ed1ae7 100644
--- a/src/de.ts
+++ b/src/de.ts
@@ -213,6 +213,7 @@ export default {
noemoji: "Noch kein Emoji gewählt",
selectemoji: "Emoji auswählen",
removeemoji: "Emoji entfernen",
+ amountClique: "Anzahl Cliquen",
changesizeEmoji: "Icon Größe: ",
},
};
diff --git a/src/en.ts b/src/en.ts
index 57aae00..946e62a 100644
--- a/src/en.ts
+++ b/src/en.ts
@@ -209,6 +209,7 @@ export default {
noemoji: "No emoji chosen yet",
selectemoji: "Select emoji",
removeemoji: "Remove emoji",
+ amountClique: "Number of Cliques",
changesizeEmoji: "Icon size: ",
},
};
+
+
+
+
+
+
+
+
+
diff --git a/src/data/AlterCategories.ts b/src/data/AlterCategories.ts
index 55d3257..60d369e 100644
--- a/src/data/AlterCategories.ts
+++ b/src/data/AlterCategories.ts
@@ -130,6 +130,18 @@ const PROFI: AlterCategorization = {
categories: ["prof. Hilfe", "Netzwerk ohne prof. Hilfe", "gesamtes Netzwerk"],
};
+const CLIQUE: AlterCategorization = {
+ label: "Clique",
+ inCategory: (catIndex: number, a: Alter): boolean =>
+ sectorIndex(a) === catIndex,
+ categories: [
+ "Familie",
+ "Freund*innen / Bekannte",
+ "Kolleg*innen",
+ "prof. Helfer*innen",
+ ],
+};
+
/* obsolete categorizations (since Jun 2024)
const HORIZON_CUM: AlterCategorization = {
label: "Horizont (kumulativ)",
@@ -173,7 +185,13 @@ const ALL: AlterCategorization = {
};
export function getAlterCategorization(key = ""): AlterCategorization {
- return key === "sector" ? SECTOR : key === "profi" ? PROFI : ALL;
+ return key === "sector"
+ ? SECTOR
+ : key === "profi"
+ ? PROFI
+ : key === "clique"
+ ? CLIQUE
+ : ALL;
// : key === "horizon"
// ? HORIZON
// : key === "horizon_cum"
@@ -188,6 +206,7 @@ export const allAlterCategorizationKeys = [
"",
"sector",
"profi",
+ "clique",
// "horizon",
// "horizon_cum",
// "gender",
diff --git a/src/data/NetworkClustering.ts b/src/data/NetworkClustering.ts
new file mode 100644
index 0000000..b1151a0
--- /dev/null
+++ b/src/data/NetworkClustering.ts
@@ -0,0 +1,62 @@
+import { Alter } from "./Alter";
+import { NWK } from "./NWK";
+
+export function findCliques(nwk: NWK): Alter[][] {
+ const adjacencyList = createAdjacencyList(nwk);
+ const cliques: number[][] = [];
+
+ bronKerbosch(
+ new Set(),
+ new Set(adjacencyList.keys()),
+ new Set(),
+ cliques,
+ adjacencyList
+ );
+
+ return cliques
+ .filter((clique) => clique.length >= 3)
+ .sort((a, b) => b.length - a.length)
+ .map((clique) =>
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ clique.map((id) => nwk.alteri.find((alter) => alter.id === id)!)
+ );
+}
+
+function createAdjacencyList(nwk: NWK): Map{{ t("amountClique") }} | +{{ cliquesList.length }} | +
---|---|
+ + , + {{ a.name }} + + | ++ {{ clique.description }} + | +