"Feature: Integrate app and script and related files for Mindmap
This commit is contained in:
69
app.py
69
app.py
@@ -1931,3 +1931,72 @@ def admin_update_database():
|
|||||||
success = False
|
success = False
|
||||||
|
|
||||||
return render_template('admin/update_database.html', message=message, success=success)
|
return render_template('admin/update_database.html', message=message, success=success)
|
||||||
|
|
||||||
|
@app.route('/api/mindmap/<node_id>')
|
||||||
|
def get_mindmap_node(node_id):
|
||||||
|
"""Liefert die Mindmap-Daten für einen bestimmten Knoten und seine Subthemen."""
|
||||||
|
try:
|
||||||
|
if node_id == 'root':
|
||||||
|
# Hauptknoten (Wissen) abrufen
|
||||||
|
wissen_node = MindMapNode.query.filter_by(name="Wissen").first()
|
||||||
|
if not wissen_node:
|
||||||
|
wissen_node = MindMapNode(
|
||||||
|
name="Wissen",
|
||||||
|
description="Zentrale Wissensbasis",
|
||||||
|
color_code="#4299E1",
|
||||||
|
is_public=True
|
||||||
|
)
|
||||||
|
db.session.add(wissen_node)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Alle direkten Kinder des Wissen-Knotens holen
|
||||||
|
nodes = wissen_node.children.all()
|
||||||
|
else:
|
||||||
|
# Bestimmten Knoten und seine Kinder abrufen
|
||||||
|
parent_node = MindMapNode.query.get_or_404(node_id)
|
||||||
|
nodes = parent_node.children.all()
|
||||||
|
wissen_node = parent_node
|
||||||
|
|
||||||
|
# Ergebnisdaten vorbereiten
|
||||||
|
nodes_data = []
|
||||||
|
edges_data = []
|
||||||
|
|
||||||
|
# Hauptknoten hinzufügen
|
||||||
|
nodes_data.append({
|
||||||
|
'id': wissen_node.id,
|
||||||
|
'name': wissen_node.name,
|
||||||
|
'description': wissen_node.description or '',
|
||||||
|
'color_code': wissen_node.color_code or '#4299E1',
|
||||||
|
'is_center': True,
|
||||||
|
'has_children': len(nodes) > 0
|
||||||
|
})
|
||||||
|
|
||||||
|
# Kinder hinzufügen
|
||||||
|
for node in nodes:
|
||||||
|
nodes_data.append({
|
||||||
|
'id': node.id,
|
||||||
|
'name': node.name,
|
||||||
|
'description': node.description or '',
|
||||||
|
'color_code': node.color_code or '#9F7AEA',
|
||||||
|
'is_center': False,
|
||||||
|
'has_children': len(node.children.all()) > 0
|
||||||
|
})
|
||||||
|
|
||||||
|
# Verbindung zum Elternknoten hinzufügen
|
||||||
|
edges_data.append({
|
||||||
|
'source_id': wissen_node.id,
|
||||||
|
'target_id': node.id,
|
||||||
|
'strength': 0.8
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'nodes': nodes_data,
|
||||||
|
'edges': edges_data
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Fehler beim Abrufen der Mindmap-Daten: {str(e)}")
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Mindmap-Daten konnten nicht geladen werden'
|
||||||
|
}), 500
|
||||||
@@ -129,7 +129,9 @@ const mindmapData = {
|
|||||||
color: '#f5f5f5',
|
color: '#f5f5f5',
|
||||||
icon: 'fa-solid fa-circle',
|
icon: 'fa-solid fa-circle',
|
||||||
fontColor: '#222',
|
fontColor: '#222',
|
||||||
fontSize: 22
|
fontSize: 22,
|
||||||
|
neuronSize: 12,
|
||||||
|
neuronActivity: 1.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'philosophy',
|
id: 'philosophy',
|
||||||
@@ -248,7 +250,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
console.log('Initialisiere Cytoscape...');
|
console.log('Initialisiere Cytoscape...');
|
||||||
|
|
||||||
// Initialisiere Cytoscape mit concentric/circle Layout und Icon-Overlay
|
// Initialisiere Cytoscape mit einem schlichten, modernen Design
|
||||||
window.cy = cytoscape({
|
window.cy = cytoscape({
|
||||||
container: cyContainer,
|
container: cyContainer,
|
||||||
elements: elements,
|
elements: elements,
|
||||||
@@ -262,62 +264,66 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
'text-valign': 'center',
|
'text-valign': 'center',
|
||||||
'text-halign': 'center',
|
'text-halign': 'center',
|
||||||
'font-size': 'data(fontSize)',
|
'font-size': 'data(fontSize)',
|
||||||
'width': 'mapData(neuronSize, 3, 10, 60, 110)',
|
'width': function(ele) {
|
||||||
'height': 'mapData(neuronSize, 3, 10, 60, 110)',
|
return ele.data('isCenter') ? 100 : 80;
|
||||||
'border-width': 4,
|
},
|
||||||
'border-color': '#fff',
|
'height': function(ele) {
|
||||||
'border-opacity': 0.9,
|
return ele.data('isCenter') ? 100 : 80;
|
||||||
'overlay-padding': 4,
|
},
|
||||||
'z-index': 10,
|
'border-width': 2,
|
||||||
|
'border-color': '#ffffff',
|
||||||
|
'border-opacity': 0.8,
|
||||||
'shape': 'ellipse',
|
'shape': 'ellipse',
|
||||||
'background-opacity': 0.95,
|
'background-opacity': 0.9,
|
||||||
'shadow-blur': 20,
|
|
||||||
'shadow-color': 'data(color)',
|
|
||||||
'shadow-opacity': 0.7,
|
|
||||||
'shadow-offset-x': 0,
|
|
||||||
'shadow-offset-y': 0,
|
|
||||||
'text-wrap': 'wrap',
|
'text-wrap': 'wrap',
|
||||||
'text-max-width': 90
|
'text-max-width': 80,
|
||||||
|
'transition-property': 'background-color, border-width',
|
||||||
|
'transition-duration': '0.2s'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: 'node[isCenter]','style': {
|
selector: 'node[isCenter]',
|
||||||
|
style: {
|
||||||
'background-color': '#f5f5f5',
|
'background-color': '#f5f5f5',
|
||||||
'color': '#222',
|
'color': '#222',
|
||||||
'font-size': 22,
|
'font-size': 20,
|
||||||
'border-width': 0,
|
'border-width': 3,
|
||||||
'width': 120,
|
'width': 100,
|
||||||
'height': 120
|
'height': 100
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: 'node:selected',
|
selector: 'node:selected',
|
||||||
style: {
|
style: {
|
||||||
'border-color': '#f59e42',
|
'border-color': '#f59e42',
|
||||||
'border-width': 6,
|
'border-width': 3,
|
||||||
'shadow-blur': 30,
|
'background-opacity': 1
|
||||||
'shadow-opacity': 0.9
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: 'edge',
|
selector: 'edge',
|
||||||
style: {
|
style: {
|
||||||
'width': 'mapData(strength, 0.2, 1, 3, 7)',
|
'width': function(ele) {
|
||||||
'line-color': '#bdbdbd',
|
return ele.data('strength') ? ele.data('strength') * 2 : 1;
|
||||||
'line-opacity': 0.7,
|
},
|
||||||
|
'line-color': function(ele) {
|
||||||
|
const sourceColor = ele.source().data('color');
|
||||||
|
return sourceColor || '#8a8aaa';
|
||||||
|
},
|
||||||
|
'line-opacity': function(ele) {
|
||||||
|
return ele.data('strength') ? ele.data('strength') * 0.6 : 0.4;
|
||||||
|
},
|
||||||
'curve-style': 'bezier',
|
'curve-style': 'bezier',
|
||||||
'target-arrow-shape': 'none',
|
'target-arrow-shape': 'none',
|
||||||
'control-point-distances': [40, -40],
|
'control-point-distances': [30, -30],
|
||||||
'control-point-weights': [0.5, 0.5],
|
'control-point-weights': [0.5, 0.5]
|
||||||
'edge-distances': 'intersection',
|
|
||||||
'line-style': 'solid'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
layout: {
|
layout: {
|
||||||
name: 'concentric',
|
name: 'concentric',
|
||||||
fit: true,
|
fit: true,
|
||||||
padding: 60,
|
padding: 50,
|
||||||
animate: true,
|
animate: true,
|
||||||
concentric: function(node) {
|
concentric: function(node) {
|
||||||
return node.data('isCenter') ? 2 : 1;
|
return node.data('isCenter') ? 2 : 1;
|
||||||
@@ -373,20 +379,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Nach dem Rendern: Icons als HTML-Overlay einfügen
|
// Entferne den Icon-Overlay-Code
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
cy.nodes().forEach(node => {
|
// Entferne alle existierenden Icon-Overlays
|
||||||
const icon = node.data('icon');
|
document.querySelectorAll('.cy-node-icon').forEach(icon => icon.remove());
|
||||||
if (icon) {
|
}, 0);
|
||||||
const dom = cy.getElementById(node.id()).popperRef();
|
|
||||||
const iconDiv = document.createElement('div');
|
|
||||||
iconDiv.className = 'cy-node-icon';
|
|
||||||
iconDiv.innerHTML = `<i class="${icon}" style="font-size:2.2em;"></i>`;
|
|
||||||
document.body.appendChild(iconDiv);
|
|
||||||
// Positionierung mit Popper.js oder absolut über node.position()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 500);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Funktion zum Initialisieren des neuronalen Designs
|
// Funktion zum Initialisieren des neuronalen Designs
|
||||||
@@ -547,17 +544,20 @@ function enhanceMindmap() {
|
|||||||
cy.layout({
|
cy.layout({
|
||||||
name: 'cose',
|
name: 'cose',
|
||||||
animate: true,
|
animate: true,
|
||||||
animationDuration: 1800,
|
animationDuration: 2000,
|
||||||
nodeDimensionsIncludeLabels: true,
|
nodeDimensionsIncludeLabels: true,
|
||||||
padding: 100,
|
padding: 100,
|
||||||
spacingFactor: 1.8,
|
spacingFactor: 2,
|
||||||
randomize: false,
|
randomize: true,
|
||||||
fit: true,
|
fit: true,
|
||||||
componentSpacing: 100,
|
componentSpacing: 150,
|
||||||
nodeRepulsion: 8000,
|
nodeRepulsion: 10000,
|
||||||
edgeElasticity: 100,
|
edgeElasticity: 150,
|
||||||
nestingFactor: 1.2,
|
nestingFactor: 1.5,
|
||||||
gravity: 80
|
gravity: 100,
|
||||||
|
initialTemp: 1000,
|
||||||
|
coolingFactor: 0.95,
|
||||||
|
minTemp: 1
|
||||||
}).run();
|
}).run();
|
||||||
|
|
||||||
// Neuronen-Namen mit besserer Lesbarkeit umgestalten
|
// Neuronen-Namen mit besserer Lesbarkeit umgestalten
|
||||||
@@ -617,152 +617,149 @@ function enhanceMindmap() {
|
|||||||
* @param {Object} cy - Cytoscape-Instanz
|
* @param {Object} cy - Cytoscape-Instanz
|
||||||
*/
|
*/
|
||||||
function applyNeuralNetworkStyle(cy) {
|
function applyNeuralNetworkStyle(cy) {
|
||||||
// Wende erweiterte Stile für Neuronen und Synapsen an
|
|
||||||
cy.style()
|
cy.style()
|
||||||
.selector('node')
|
.selector('node')
|
||||||
.style({
|
.style({
|
||||||
'label': 'data(name)',
|
'label': 'data(label)',
|
||||||
'text-valign': 'bottom',
|
'text-valign': 'center',
|
||||||
'text-halign': 'center',
|
'text-halign': 'center',
|
||||||
'color': '#ffffff',
|
'color': 'data(fontColor)',
|
||||||
'text-outline-width': 1.5,
|
'text-outline-width': 2,
|
||||||
'text-outline-color': '#0a0e19',
|
'text-outline-color': 'rgba(0,0,0,0.8)',
|
||||||
'text-outline-opacity': 0.9,
|
'text-outline-opacity': 0.9,
|
||||||
'font-size': 10,
|
'font-size': 'data(fontSize)',
|
||||||
'text-margin-y': 7,
|
'font-weight': '500',
|
||||||
'width': 'mapData(neuronSize, 3, 10, 15, 40)',
|
'text-margin-y': 8,
|
||||||
'height': 'mapData(neuronSize, 3, 10, 15, 40)',
|
'width': function(ele) {
|
||||||
|
if (ele.data('isCenter')) return 120;
|
||||||
|
return ele.data('neuronSize') ? ele.data('neuronSize') * 10 : 80;
|
||||||
|
},
|
||||||
|
'height': function(ele) {
|
||||||
|
if (ele.data('isCenter')) return 120;
|
||||||
|
return ele.data('neuronSize') ? ele.data('neuronSize') * 10 : 80;
|
||||||
|
},
|
||||||
'background-color': 'data(color)',
|
'background-color': 'data(color)',
|
||||||
'background-opacity': 0.85,
|
'background-opacity': 0.9,
|
||||||
'border-width': 0,
|
'border-width': 2,
|
||||||
|
'border-color': '#ffffff',
|
||||||
|
'border-opacity': 0.8,
|
||||||
'shape': 'ellipse',
|
'shape': 'ellipse',
|
||||||
'shadow-blur': 'mapData(neuronActivity, 0.3, 1, 5, 15)',
|
'transition-property': 'background-color, background-opacity, border-width',
|
||||||
'shadow-color': 'data(color)',
|
'transition-duration': '0.3s',
|
||||||
'shadow-opacity': 0.6,
|
'transition-timing-function': 'ease-in-out'
|
||||||
'shadow-offset-x': 0,
|
|
||||||
'shadow-offset-y': 0
|
|
||||||
})
|
})
|
||||||
.selector('edge')
|
.selector('edge')
|
||||||
.style({
|
.style({
|
||||||
'width': 'mapData(strength, 0.2, 0.8, 0.7, 2)',
|
'width': function(ele) {
|
||||||
|
return ele.data('strength') ? ele.data('strength') * 3 : 1;
|
||||||
|
},
|
||||||
'curve-style': 'bezier',
|
'curve-style': 'bezier',
|
||||||
'line-color': '#8a8aaa',
|
'line-color': function(ele) {
|
||||||
'line-opacity': 'mapData(strength, 0.2, 0.8, 0.4, 0.7)',
|
const sourceColor = ele.source().data('color');
|
||||||
|
return sourceColor || '#8a8aaa';
|
||||||
|
},
|
||||||
|
'line-opacity': function(ele) {
|
||||||
|
return ele.data('strength') ? ele.data('strength') * 0.8 : 0.4;
|
||||||
|
},
|
||||||
'line-style': function(ele) {
|
'line-style': function(ele) {
|
||||||
const strength = ele.data('strength');
|
const strength = ele.data('strength');
|
||||||
|
if (!strength) return 'solid';
|
||||||
if (strength <= 0.4) return 'dotted';
|
if (strength <= 0.4) return 'dotted';
|
||||||
if (strength <= 0.6) return 'dashed';
|
if (strength <= 0.6) return 'dashed';
|
||||||
return 'solid';
|
return 'solid';
|
||||||
},
|
},
|
||||||
'target-arrow-shape': 'none',
|
'target-arrow-shape': 'none',
|
||||||
'source-endpoint': '0% 50%',
|
'source-endpoint': '0% 50%',
|
||||||
'target-endpoint': '100% 50%'
|
'target-endpoint': '100% 50%',
|
||||||
})
|
'transition-property': 'line-opacity, width',
|
||||||
.selector('node[isRoot]')
|
'transition-duration': '0.3s',
|
||||||
.style({
|
'transition-timing-function': 'ease-in-out'
|
||||||
'font-size': 12,
|
|
||||||
'font-weight': 'bold',
|
|
||||||
'width': 50,
|
|
||||||
'height': 50,
|
|
||||||
'background-color': '#6366f1',
|
|
||||||
'shadow-blur': 20,
|
|
||||||
'shadow-color': '#6366f1',
|
|
||||||
'shadow-opacity': 0.8,
|
|
||||||
'text-margin-y': 8
|
|
||||||
})
|
})
|
||||||
.update();
|
.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Vereinfachte neuronale Aktivitätssimulation
|
||||||
* Simuliert neuronale Aktivität in der Mindmap
|
|
||||||
* @param {Object} cy - Cytoscape-Instanz
|
|
||||||
*/
|
|
||||||
function startNeuralActivitySimulation(cy) {
|
function startNeuralActivitySimulation(cy) {
|
||||||
if (window.neuralInterval) clearInterval(window.neuralInterval);
|
if (window.neuralInterval) clearInterval(window.neuralInterval);
|
||||||
|
|
||||||
const nodes = cy.nodes();
|
const nodes = cy.nodes();
|
||||||
const edges = cy.edges();
|
let currentTime = Date.now();
|
||||||
let currentTime = Date.now();
|
|
||||||
|
|
||||||
// Neuronale Aktivität simulieren
|
function simulateNeuralActivity() {
|
||||||
function simulateNeuralActivity() {
|
currentTime = Date.now();
|
||||||
currentTime = Date.now();
|
|
||||||
|
|
||||||
// Zufällige Neuronen "feuern" lassen
|
nodes.forEach(node => {
|
||||||
nodes.forEach(node => {
|
const data = node.data();
|
||||||
const data = node.data();
|
const lastFired = data.lastFired || 0;
|
||||||
const lastFired = data.lastFired || 0;
|
const timeSinceLastFire = currentTime - lastFired;
|
||||||
const timeSinceLastFire = currentTime - lastFired;
|
|
||||||
|
|
||||||
// Prüfen ob Neuron feuern kann (Refraktionsperiode)
|
if (timeSinceLastFire > data.refractionPeriod) {
|
||||||
if (timeSinceLastFire > data.refractionPeriod) {
|
if (Math.random() < data.neuronActivity * 0.1) {
|
||||||
// Zufälliges Feuern basierend auf Aktivität
|
fireNeuron(node, true, currentTime);
|
||||||
if (Math.random() < data.neuronActivity * 0.1) {
|
}
|
||||||
fireNeuron(node, true, currentTime);
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Neuron feuern lassen
|
|
||||||
function fireNeuron(node, state, currentTime) {
|
|
||||||
const data = node.data();
|
|
||||||
data.lastFired = currentTime;
|
|
||||||
|
|
||||||
// Visuelles Feedback
|
|
||||||
node.style({
|
|
||||||
'background-opacity': 1,
|
|
||||||
'shadow-blur': 25,
|
|
||||||
'shadow-opacity': 0.9
|
|
||||||
});
|
|
||||||
|
|
||||||
// Nach kurzer Zeit zurück zum Normalzustand
|
|
||||||
setTimeout(() => {
|
|
||||||
node.style({
|
|
||||||
'background-opacity': 0.85,
|
|
||||||
'shadow-blur': 18,
|
|
||||||
'shadow-opacity': 0.6
|
|
||||||
});
|
|
||||||
}, 200);
|
|
||||||
|
|
||||||
// Signal weiterleiten
|
|
||||||
if (state) {
|
|
||||||
propagateSignal(node, currentTime);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Signal über Kanten weiterleiten
|
function fireNeuron(node, state, currentTime) {
|
||||||
function propagateSignal(sourceNode, currentTime) {
|
const data = node.data();
|
||||||
const outgoingEdges = sourceNode.connectedEdges('out');
|
data.lastFired = currentTime;
|
||||||
|
|
||||||
outgoingEdges.forEach(edge => {
|
node.style({
|
||||||
const targetNode = edge.target();
|
'background-opacity': 1,
|
||||||
const edgeData = edge.data();
|
'border-width': 3
|
||||||
const latency = edgeData.latency;
|
});
|
||||||
|
|
||||||
// Signal mit Verzögerung weiterleiten
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
node.style({
|
||||||
const targetData = targetNode.data();
|
'background-opacity': 0.9,
|
||||||
const timeSinceLastFire = currentTime - (targetData.lastFired || 0);
|
'border-width': 2
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
|
||||||
// Prüfen ob Zielneuron feuern kann
|
if (state) {
|
||||||
if (timeSinceLastFire > targetData.refractionPeriod) {
|
propagateSignal(node, currentTime);
|
||||||
// Signalstärke berechnen
|
|
||||||
const signalStrength = edgeData.strength *
|
|
||||||
edgeData.conductionVelocity *
|
|
||||||
sourceNode.data('neuronActivity');
|
|
||||||
|
|
||||||
// Neuron feuern lassen wenn Signal stark genug
|
|
||||||
if (signalStrength > targetData.threshold) {
|
|
||||||
fireNeuron(targetNode, true, currentTime + latency);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, latency);
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulation starten
|
function propagateSignal(sourceNode, currentTime) {
|
||||||
window.neuralInterval = setInterval(simulateNeuralActivity, 100);
|
const outgoingEdges = sourceNode.connectedEdges();
|
||||||
|
|
||||||
|
outgoingEdges.forEach(edge => {
|
||||||
|
const targetNode = edge.target();
|
||||||
|
const edgeData = edge.data();
|
||||||
|
const latency = edgeData.latency;
|
||||||
|
|
||||||
|
edge.style({
|
||||||
|
'line-opacity': 0.8,
|
||||||
|
'width': edgeData.strength * 3
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
edge.style({
|
||||||
|
'line-opacity': edgeData.strength * 0.6,
|
||||||
|
'width': edgeData.strength * 2
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const targetData = targetNode.data();
|
||||||
|
const timeSinceLastFire = currentTime - (targetData.lastFired || 0);
|
||||||
|
|
||||||
|
if (timeSinceLastFire > targetData.refractionPeriod) {
|
||||||
|
const signalStrength = edgeData.strength *
|
||||||
|
edgeData.conductionVelocity *
|
||||||
|
sourceNode.data('neuronActivity');
|
||||||
|
|
||||||
|
if (signalStrength > targetData.threshold) {
|
||||||
|
fireNeuron(targetNode, true, currentTime + latency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, latency);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.neuralInterval = setInterval(simulateNeuralActivity, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hilfe-Funktion zum Hinzufügen eines Flash-Hinweises
|
// Hilfe-Funktion zum Hinzufügen eines Flash-Hinweises
|
||||||
@@ -803,29 +800,152 @@ function createFlashContainer() {
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Funktion zum Laden der Mindmap-Daten aus der Datenbank
|
||||||
|
async function loadMindmapData(nodeId = null) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/mindmap/${nodeId || 'root'}`);
|
||||||
|
if (!response.ok) throw new Error('Fehler beim Laden der Mindmap-Daten');
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der Mindmap-Daten:', error);
|
||||||
|
showFlash('Fehler beim Laden der Mindmap-Daten', 'error');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Initialisieren der Mindmap
|
||||||
|
async function initializeMindmap() {
|
||||||
|
const mindmapData = await loadMindmapData();
|
||||||
|
if (!mindmapData) return;
|
||||||
|
|
||||||
|
const elements = [
|
||||||
|
// Knoten
|
||||||
|
...mindmapData.nodes.map(node => ({
|
||||||
|
data: {
|
||||||
|
id: node.id,
|
||||||
|
label: node.name,
|
||||||
|
category: node.category,
|
||||||
|
description: node.description,
|
||||||
|
hasChildren: node.has_children,
|
||||||
|
expanded: false,
|
||||||
|
color: node.color_code,
|
||||||
|
fontColor: '#ffffff',
|
||||||
|
fontSize: node.is_center ? 20 : 16
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
// Kanten
|
||||||
|
...mindmapData.edges.map(edge => ({
|
||||||
|
data: {
|
||||||
|
source: edge.source_id,
|
||||||
|
target: edge.target_id,
|
||||||
|
strength: edge.strength || 0.5
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
|
||||||
|
window.cy = cytoscape({
|
||||||
|
container: document.getElementById('cy'),
|
||||||
|
elements: elements,
|
||||||
|
style: [
|
||||||
|
{
|
||||||
|
selector: 'node',
|
||||||
|
style: {
|
||||||
|
'background-color': 'data(color)',
|
||||||
|
'label': 'data(label)',
|
||||||
|
'color': 'data(fontColor)',
|
||||||
|
'text-valign': 'center',
|
||||||
|
'text-halign': 'center',
|
||||||
|
'font-size': 'data(fontSize)',
|
||||||
|
'width': function(ele) {
|
||||||
|
return ele.data('isCenter') ? 100 : 80;
|
||||||
|
},
|
||||||
|
'height': function(ele) {
|
||||||
|
return ele.data('isCenter') ? 100 : 80;
|
||||||
|
},
|
||||||
|
'border-width': 2,
|
||||||
|
'border-color': '#ffffff',
|
||||||
|
'border-opacity': 0.8,
|
||||||
|
'shape': 'ellipse',
|
||||||
|
'background-opacity': 0.9,
|
||||||
|
'text-wrap': 'wrap',
|
||||||
|
'text-max-width': 80,
|
||||||
|
'transition-property': 'background-color, border-width',
|
||||||
|
'transition-duration': '0.2s'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'node[isCenter]',
|
||||||
|
style: {
|
||||||
|
'background-color': '#f5f5f5',
|
||||||
|
'color': '#222',
|
||||||
|
'font-size': 20,
|
||||||
|
'border-width': 3,
|
||||||
|
'width': 100,
|
||||||
|
'height': 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'node:selected',
|
||||||
|
style: {
|
||||||
|
'border-color': '#f59e42',
|
||||||
|
'border-width': 3,
|
||||||
|
'background-opacity': 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'edge',
|
||||||
|
style: {
|
||||||
|
'width': function(ele) {
|
||||||
|
return ele.data('strength') ? ele.data('strength') * 2 : 1;
|
||||||
|
},
|
||||||
|
'line-color': function(ele) {
|
||||||
|
const sourceColor = ele.source().data('color');
|
||||||
|
return sourceColor || '#8a8aaa';
|
||||||
|
},
|
||||||
|
'line-opacity': function(ele) {
|
||||||
|
return ele.data('strength') ? ele.data('strength') * 0.6 : 0.4;
|
||||||
|
},
|
||||||
|
'curve-style': 'bezier',
|
||||||
|
'target-arrow-shape': 'none',
|
||||||
|
'control-point-distances': [30, -30],
|
||||||
|
'control-point-weights': [0.5, 0.5]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
layout: {
|
||||||
|
name: 'concentric',
|
||||||
|
fit: true,
|
||||||
|
padding: 50,
|
||||||
|
animate: true,
|
||||||
|
concentric: function(node) {
|
||||||
|
return node.data('isCenter') ? 2 : 1;
|
||||||
|
},
|
||||||
|
levelWidth: function() { return 1; }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Event-Listener für Knoten-Klicks
|
||||||
|
cy.on('tap', 'node', async function(evt) {
|
||||||
|
const node = evt.target;
|
||||||
|
if (node.data('hasChildren') && !node.data('expanded')) {
|
||||||
|
await loadSubthemes(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Funktion zum Laden der Subthemen
|
// Funktion zum Laden der Subthemen
|
||||||
async function loadSubthemes(node) {
|
async function loadSubthemes(node) {
|
||||||
try {
|
try {
|
||||||
console.log('Loading subthemes for node:', node.id());
|
const mindmapData = await loadMindmapData(node.id());
|
||||||
|
if (!mindmapData) return;
|
||||||
|
|
||||||
// Simuliere Datenbankabfrage
|
|
||||||
const subthemes = subthemesDatabase[node.id()];
|
|
||||||
|
|
||||||
if (!subthemes) {
|
|
||||||
console.log('No subthemes found for node:', node.id());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Animation starten
|
|
||||||
showFlash('Lade Subthemen...', 'info');
|
showFlash('Lade Subthemen...', 'info');
|
||||||
|
|
||||||
// Neue Seite erstellen
|
|
||||||
const mindmapContainer = document.querySelector('.mindmap-container');
|
const mindmapContainer = document.querySelector('.mindmap-container');
|
||||||
const newPage = document.createElement('div');
|
const newPage = document.createElement('div');
|
||||||
newPage.className = 'mindmap-page';
|
newPage.className = 'mindmap-page';
|
||||||
newPage.style.display = 'none';
|
newPage.style.display = 'none';
|
||||||
|
|
||||||
// Kopfzeile erstellen
|
|
||||||
const header = document.createElement('div');
|
const header = document.createElement('div');
|
||||||
header.className = 'mindmap-header';
|
header.className = 'mindmap-header';
|
||||||
header.innerHTML = `
|
header.innerHTML = `
|
||||||
@@ -837,20 +957,38 @@ async function loadSubthemes(node) {
|
|||||||
<h2 class="mindmap-title">${node.data('label')}</h2>
|
<h2 class="mindmap-title">${node.data('label')}</h2>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Container für die neue Mindmap
|
|
||||||
const newContainer = document.createElement('div');
|
const newContainer = document.createElement('div');
|
||||||
newContainer.id = `cy-${node.id()}`;
|
newContainer.id = `cy-${node.id()}`;
|
||||||
newContainer.className = 'mindmap-view';
|
newContainer.className = 'mindmap-view';
|
||||||
|
|
||||||
// Elemente zur Seite hinzufügen
|
|
||||||
newPage.appendChild(header);
|
newPage.appendChild(header);
|
||||||
newPage.appendChild(newContainer);
|
newPage.appendChild(newContainer);
|
||||||
mindmapContainer.appendChild(newPage);
|
mindmapContainer.appendChild(newPage);
|
||||||
|
|
||||||
// Neue Cytoscape-Instanz erstellen
|
|
||||||
const newCy = cytoscape({
|
const newCy = cytoscape({
|
||||||
container: newContainer,
|
container: newContainer,
|
||||||
elements: [],
|
elements: [
|
||||||
|
...mindmapData.nodes.map(node => ({
|
||||||
|
data: {
|
||||||
|
id: node.id,
|
||||||
|
label: node.name,
|
||||||
|
category: node.category,
|
||||||
|
description: node.description,
|
||||||
|
hasChildren: node.has_children,
|
||||||
|
expanded: false,
|
||||||
|
color: node.color_code,
|
||||||
|
fontColor: '#ffffff',
|
||||||
|
fontSize: 16
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
...mindmapData.edges.map(edge => ({
|
||||||
|
data: {
|
||||||
|
source: edge.source_id,
|
||||||
|
target: edge.target_id,
|
||||||
|
strength: edge.strength || 0.5
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
],
|
||||||
style: cy.style(),
|
style: cy.style(),
|
||||||
layout: {
|
layout: {
|
||||||
name: 'cose',
|
name: 'cose',
|
||||||
@@ -861,30 +999,17 @@ async function loadSubthemes(node) {
|
|||||||
padding: 30,
|
padding: 30,
|
||||||
nodeRepulsion: 4500,
|
nodeRepulsion: 4500,
|
||||||
idealEdgeLength: 50,
|
idealEdgeLength: 50,
|
||||||
edgeElasticity: 0.45
|
edgeElasticity: 0.45,
|
||||||
|
randomize: true,
|
||||||
|
componentSpacing: 100,
|
||||||
|
nodeOverlap: 20,
|
||||||
|
gravity: 0.25,
|
||||||
|
initialTemp: 1000,
|
||||||
|
coolingFactor: 0.95,
|
||||||
|
minTemp: 1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Neue Knoten hinzufügen
|
|
||||||
subthemes.forEach(subtheme => {
|
|
||||||
newCy.add({
|
|
||||||
group: 'nodes',
|
|
||||||
data: {
|
|
||||||
id: subtheme.id,
|
|
||||||
label: subtheme.label,
|
|
||||||
category: subtheme.category,
|
|
||||||
description: subtheme.description,
|
|
||||||
hasChildren: subtheme.hasChildren,
|
|
||||||
expanded: false,
|
|
||||||
neuronSize: 6,
|
|
||||||
neuronActivity: 0.7
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Neuronales Design für die neue Mindmap initialisieren
|
|
||||||
initializeNeuralDesign(newCy);
|
|
||||||
|
|
||||||
// Event-Listener für die neue Mindmap
|
// Event-Listener für die neue Mindmap
|
||||||
newCy.on('tap', 'node', async function(evt) {
|
newCy.on('tap', 'node', async function(evt) {
|
||||||
const clickedNode = evt.target;
|
const clickedNode = evt.target;
|
||||||
@@ -897,26 +1022,8 @@ async function loadSubthemes(node) {
|
|||||||
cy.container().style.display = 'none';
|
cy.container().style.display = 'none';
|
||||||
newPage.style.display = 'block';
|
newPage.style.display = 'block';
|
||||||
|
|
||||||
// Layout ausführen
|
|
||||||
newCy.layout().run();
|
|
||||||
|
|
||||||
// Erfolgsmeldung anzeigen
|
|
||||||
showFlash('Subthemen erfolgreich geladen', 'success');
|
showFlash('Subthemen erfolgreich geladen', 'success');
|
||||||
|
|
||||||
// Icons für Subthemen wie oben einfügen
|
|
||||||
setTimeout(() => {
|
|
||||||
newCy.nodes().forEach(node => {
|
|
||||||
const icon = node.data('icon');
|
|
||||||
if (icon) {
|
|
||||||
const dom = newCy.getElementById(node.id()).popperRef();
|
|
||||||
const iconDiv = document.createElement('div');
|
|
||||||
iconDiv.className = 'cy-node-icon';
|
|
||||||
iconDiv.innerHTML = `<i class="${icon}" style="font-size:2em;"></i>`;
|
|
||||||
document.body.appendChild(iconDiv);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden der Subthemen:', error);
|
console.error('Fehler beim Laden der Subthemen:', error);
|
||||||
showFlash('Fehler beim Laden der Subthemen', 'error');
|
showFlash('Fehler beim Laden der Subthemen', 'error');
|
||||||
@@ -1002,3 +1109,6 @@ style.textContent = `
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
// Initialisiere die Mindmap beim Laden der Seite
|
||||||
|
document.addEventListener('DOMContentLoaded', initializeMindmap);
|
||||||
@@ -1107,3 +1107,65 @@ window.addEventListener('beforeunload', () => {
|
|||||||
window.neuralNetworkBackground.destroy();
|
window.neuralNetworkBackground.destroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function applyNeuralNetworkStyle(cy) {
|
||||||
|
cy.style()
|
||||||
|
.selector('node')
|
||||||
|
.style({
|
||||||
|
'label': 'data(label)',
|
||||||
|
'text-valign': 'center',
|
||||||
|
'text-halign': 'center',
|
||||||
|
'color': 'data(fontColor)',
|
||||||
|
'text-outline-width': 2,
|
||||||
|
'text-outline-color': 'rgba(0,0,0,0.8)',
|
||||||
|
'text-outline-opacity': 0.9,
|
||||||
|
'font-size': 'data(fontSize)',
|
||||||
|
'font-weight': '500',
|
||||||
|
'text-margin-y': 8,
|
||||||
|
'width': function(ele) {
|
||||||
|
if (ele.data('isCenter')) return 120;
|
||||||
|
return ele.data('neuronSize') ? ele.data('neuronSize') * 10 : 80;
|
||||||
|
},
|
||||||
|
'height': function(ele) {
|
||||||
|
if (ele.data('isCenter')) return 120;
|
||||||
|
return ele.data('neuronSize') ? ele.data('neuronSize') * 10 : 80;
|
||||||
|
},
|
||||||
|
'background-color': 'data(color)',
|
||||||
|
'background-opacity': 0.9,
|
||||||
|
'border-width': 2,
|
||||||
|
'border-color': '#ffffff',
|
||||||
|
'border-opacity': 0.8,
|
||||||
|
'shape': 'ellipse',
|
||||||
|
'transition-property': 'background-color, background-opacity, border-width',
|
||||||
|
'transition-duration': '0.3s',
|
||||||
|
'transition-timing-function': 'ease-in-out'
|
||||||
|
})
|
||||||
|
.selector('edge')
|
||||||
|
.style({
|
||||||
|
'width': function(ele) {
|
||||||
|
return ele.data('strength') ? ele.data('strength') * 3 : 1;
|
||||||
|
},
|
||||||
|
'curve-style': 'bezier',
|
||||||
|
'line-color': function(ele) {
|
||||||
|
const sourceColor = ele.source().data('color');
|
||||||
|
return sourceColor || '#8a8aaa';
|
||||||
|
},
|
||||||
|
'line-opacity': function(ele) {
|
||||||
|
return ele.data('strength') ? ele.data('strength') * 0.8 : 0.4;
|
||||||
|
},
|
||||||
|
'line-style': function(ele) {
|
||||||
|
const strength = ele.data('strength');
|
||||||
|
if (!strength) return 'solid';
|
||||||
|
if (strength <= 0.4) return 'dotted';
|
||||||
|
if (strength <= 0.6) return 'dashed';
|
||||||
|
return 'solid';
|
||||||
|
},
|
||||||
|
'target-arrow-shape': 'none',
|
||||||
|
'source-endpoint': '0% 50%',
|
||||||
|
'target-endpoint': '100% 50%',
|
||||||
|
'transition-property': 'line-opacity, width',
|
||||||
|
'transition-duration': '0.3s',
|
||||||
|
'transition-timing-function': 'ease-in-out'
|
||||||
|
})
|
||||||
|
.update();
|
||||||
|
}
|
||||||
BIN
systades.db
Normal file
BIN
systades.db
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user