"Feature: Integrate app and script and related files for Mindmap
This commit is contained in:
71
app.py
71
app.py
@@ -1930,4 +1930,73 @@ def admin_update_database():
|
||||
message = f"Fehler: {str(e)}"
|
||||
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',
|
||||
icon: 'fa-solid fa-circle',
|
||||
fontColor: '#222',
|
||||
fontSize: 22
|
||||
fontSize: 22,
|
||||
neuronSize: 12,
|
||||
neuronActivity: 1.0
|
||||
},
|
||||
{
|
||||
id: 'philosophy',
|
||||
@@ -248,7 +250,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
console.log('Initialisiere Cytoscape...');
|
||||
|
||||
// Initialisiere Cytoscape mit concentric/circle Layout und Icon-Overlay
|
||||
// Initialisiere Cytoscape mit einem schlichten, modernen Design
|
||||
window.cy = cytoscape({
|
||||
container: cyContainer,
|
||||
elements: elements,
|
||||
@@ -262,62 +264,66 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
'text-valign': 'center',
|
||||
'text-halign': 'center',
|
||||
'font-size': 'data(fontSize)',
|
||||
'width': 'mapData(neuronSize, 3, 10, 60, 110)',
|
||||
'height': 'mapData(neuronSize, 3, 10, 60, 110)',
|
||||
'border-width': 4,
|
||||
'border-color': '#fff',
|
||||
'border-opacity': 0.9,
|
||||
'overlay-padding': 4,
|
||||
'z-index': 10,
|
||||
'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.95,
|
||||
'shadow-blur': 20,
|
||||
'shadow-color': 'data(color)',
|
||||
'shadow-opacity': 0.7,
|
||||
'shadow-offset-x': 0,
|
||||
'shadow-offset-y': 0,
|
||||
'background-opacity': 0.9,
|
||||
'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',
|
||||
'color': '#222',
|
||||
'font-size': 22,
|
||||
'border-width': 0,
|
||||
'width': 120,
|
||||
'height': 120
|
||||
'font-size': 20,
|
||||
'border-width': 3,
|
||||
'width': 100,
|
||||
'height': 100
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'node:selected',
|
||||
style: {
|
||||
'border-color': '#f59e42',
|
||||
'border-width': 6,
|
||||
'shadow-blur': 30,
|
||||
'shadow-opacity': 0.9
|
||||
'border-width': 3,
|
||||
'background-opacity': 1
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'width': 'mapData(strength, 0.2, 1, 3, 7)',
|
||||
'line-color': '#bdbdbd',
|
||||
'line-opacity': 0.7,
|
||||
'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': [40, -40],
|
||||
'control-point-weights': [0.5, 0.5],
|
||||
'edge-distances': 'intersection',
|
||||
'line-style': 'solid'
|
||||
'control-point-distances': [30, -30],
|
||||
'control-point-weights': [0.5, 0.5]
|
||||
}
|
||||
}
|
||||
],
|
||||
layout: {
|
||||
name: 'concentric',
|
||||
fit: true,
|
||||
padding: 60,
|
||||
padding: 50,
|
||||
animate: true,
|
||||
concentric: function(node) {
|
||||
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(() => {
|
||||
cy.nodes().forEach(node => {
|
||||
const icon = node.data('icon');
|
||||
if (icon) {
|
||||
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);
|
||||
// Entferne alle existierenden Icon-Overlays
|
||||
document.querySelectorAll('.cy-node-icon').forEach(icon => icon.remove());
|
||||
}, 0);
|
||||
});
|
||||
|
||||
// Funktion zum Initialisieren des neuronalen Designs
|
||||
@@ -547,17 +544,20 @@ function enhanceMindmap() {
|
||||
cy.layout({
|
||||
name: 'cose',
|
||||
animate: true,
|
||||
animationDuration: 1800,
|
||||
animationDuration: 2000,
|
||||
nodeDimensionsIncludeLabels: true,
|
||||
padding: 100,
|
||||
spacingFactor: 1.8,
|
||||
randomize: false,
|
||||
spacingFactor: 2,
|
||||
randomize: true,
|
||||
fit: true,
|
||||
componentSpacing: 100,
|
||||
nodeRepulsion: 8000,
|
||||
edgeElasticity: 100,
|
||||
nestingFactor: 1.2,
|
||||
gravity: 80
|
||||
componentSpacing: 150,
|
||||
nodeRepulsion: 10000,
|
||||
edgeElasticity: 150,
|
||||
nestingFactor: 1.5,
|
||||
gravity: 100,
|
||||
initialTemp: 1000,
|
||||
coolingFactor: 0.95,
|
||||
minTemp: 1
|
||||
}).run();
|
||||
|
||||
// Neuronen-Namen mit besserer Lesbarkeit umgestalten
|
||||
@@ -617,152 +617,149 @@ function enhanceMindmap() {
|
||||
* @param {Object} cy - Cytoscape-Instanz
|
||||
*/
|
||||
function applyNeuralNetworkStyle(cy) {
|
||||
// Wende erweiterte Stile für Neuronen und Synapsen an
|
||||
cy.style()
|
||||
.selector('node')
|
||||
.style({
|
||||
'label': 'data(name)',
|
||||
'text-valign': 'bottom',
|
||||
'label': 'data(label)',
|
||||
'text-valign': 'center',
|
||||
'text-halign': 'center',
|
||||
'color': '#ffffff',
|
||||
'text-outline-width': 1.5,
|
||||
'text-outline-color': '#0a0e19',
|
||||
'color': 'data(fontColor)',
|
||||
'text-outline-width': 2,
|
||||
'text-outline-color': 'rgba(0,0,0,0.8)',
|
||||
'text-outline-opacity': 0.9,
|
||||
'font-size': 10,
|
||||
'text-margin-y': 7,
|
||||
'width': 'mapData(neuronSize, 3, 10, 15, 40)',
|
||||
'height': 'mapData(neuronSize, 3, 10, 15, 40)',
|
||||
'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.85,
|
||||
'border-width': 0,
|
||||
'background-opacity': 0.9,
|
||||
'border-width': 2,
|
||||
'border-color': '#ffffff',
|
||||
'border-opacity': 0.8,
|
||||
'shape': 'ellipse',
|
||||
'shadow-blur': 'mapData(neuronActivity, 0.3, 1, 5, 15)',
|
||||
'shadow-color': 'data(color)',
|
||||
'shadow-opacity': 0.6,
|
||||
'shadow-offset-x': 0,
|
||||
'shadow-offset-y': 0
|
||||
'transition-property': 'background-color, background-opacity, border-width',
|
||||
'transition-duration': '0.3s',
|
||||
'transition-timing-function': 'ease-in-out'
|
||||
})
|
||||
.selector('edge')
|
||||
.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',
|
||||
'line-color': '#8a8aaa',
|
||||
'line-opacity': 'mapData(strength, 0.2, 0.8, 0.4, 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.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%'
|
||||
})
|
||||
.selector('node[isRoot]')
|
||||
.style({
|
||||
'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
|
||||
'target-endpoint': '100% 50%',
|
||||
'transition-property': 'line-opacity, width',
|
||||
'transition-duration': '0.3s',
|
||||
'transition-timing-function': 'ease-in-out'
|
||||
})
|
||||
.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simuliert neuronale Aktivität in der Mindmap
|
||||
* @param {Object} cy - Cytoscape-Instanz
|
||||
*/
|
||||
// Vereinfachte neuronale Aktivitätssimulation
|
||||
function startNeuralActivitySimulation(cy) {
|
||||
if (window.neuralInterval) clearInterval(window.neuralInterval);
|
||||
|
||||
const nodes = cy.nodes();
|
||||
const edges = cy.edges();
|
||||
let currentTime = Date.now();
|
||||
|
||||
// Neuronale Aktivität simulieren
|
||||
function simulateNeuralActivity() {
|
||||
currentTime = Date.now();
|
||||
if (window.neuralInterval) clearInterval(window.neuralInterval);
|
||||
|
||||
// Zufällige Neuronen "feuern" lassen
|
||||
nodes.forEach(node => {
|
||||
const data = node.data();
|
||||
const lastFired = data.lastFired || 0;
|
||||
const timeSinceLastFire = currentTime - lastFired;
|
||||
|
||||
// Prüfen ob Neuron feuern kann (Refraktionsperiode)
|
||||
if (timeSinceLastFire > data.refractionPeriod) {
|
||||
// Zufälliges Feuern basierend auf Aktivität
|
||||
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;
|
||||
const nodes = cy.nodes();
|
||||
let currentTime = Date.now();
|
||||
|
||||
// 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 propagateSignal(sourceNode, currentTime) {
|
||||
const outgoingEdges = sourceNode.connectedEdges('out');
|
||||
|
||||
outgoingEdges.forEach(edge => {
|
||||
const targetNode = edge.target();
|
||||
const edgeData = edge.data();
|
||||
const latency = edgeData.latency;
|
||||
|
||||
// Signal mit Verzögerung weiterleiten
|
||||
setTimeout(() => {
|
||||
const targetData = targetNode.data();
|
||||
const timeSinceLastFire = currentTime - (targetData.lastFired || 0);
|
||||
function simulateNeuralActivity() {
|
||||
currentTime = Date.now();
|
||||
|
||||
// Prüfen ob Zielneuron feuern kann
|
||||
if (timeSinceLastFire > targetData.refractionPeriod) {
|
||||
// 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);
|
||||
}
|
||||
nodes.forEach(node => {
|
||||
const data = node.data();
|
||||
const lastFired = data.lastFired || 0;
|
||||
const timeSinceLastFire = currentTime - lastFired;
|
||||
|
||||
if (timeSinceLastFire > data.refractionPeriod) {
|
||||
if (Math.random() < data.neuronActivity * 0.1) {
|
||||
fireNeuron(node, true, currentTime);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fireNeuron(node, state, currentTime) {
|
||||
const data = node.data();
|
||||
data.lastFired = currentTime;
|
||||
|
||||
node.style({
|
||||
'background-opacity': 1,
|
||||
'border-width': 3
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
node.style({
|
||||
'background-opacity': 0.9,
|
||||
'border-width': 2
|
||||
});
|
||||
}, 200);
|
||||
|
||||
if (state) {
|
||||
propagateSignal(node, currentTime);
|
||||
}
|
||||
}, latency);
|
||||
});
|
||||
}
|
||||
|
||||
// Simulation starten
|
||||
window.neuralInterval = setInterval(simulateNeuralActivity, 100);
|
||||
}
|
||||
|
||||
function propagateSignal(sourceNode, currentTime) {
|
||||
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
|
||||
@@ -803,29 +800,152 @@ function createFlashContainer() {
|
||||
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
|
||||
async function loadSubthemes(node) {
|
||||
try {
|
||||
console.log('Loading subthemes for node:', node.id());
|
||||
|
||||
// Simuliere Datenbankabfrage
|
||||
const subthemes = subthemesDatabase[node.id()];
|
||||
|
||||
if (!subthemes) {
|
||||
console.log('No subthemes found for node:', node.id());
|
||||
return;
|
||||
}
|
||||
const mindmapData = await loadMindmapData(node.id());
|
||||
if (!mindmapData) return;
|
||||
|
||||
// Animation starten
|
||||
showFlash('Lade Subthemen...', 'info');
|
||||
|
||||
// Neue Seite erstellen
|
||||
const mindmapContainer = document.querySelector('.mindmap-container');
|
||||
const newPage = document.createElement('div');
|
||||
newPage.className = 'mindmap-page';
|
||||
newPage.style.display = 'none';
|
||||
|
||||
// Kopfzeile erstellen
|
||||
const header = document.createElement('div');
|
||||
header.className = 'mindmap-header';
|
||||
header.innerHTML = `
|
||||
@@ -837,20 +957,38 @@ async function loadSubthemes(node) {
|
||||
<h2 class="mindmap-title">${node.data('label')}</h2>
|
||||
`;
|
||||
|
||||
// Container für die neue Mindmap
|
||||
const newContainer = document.createElement('div');
|
||||
newContainer.id = `cy-${node.id()}`;
|
||||
newContainer.className = 'mindmap-view';
|
||||
|
||||
// Elemente zur Seite hinzufügen
|
||||
newPage.appendChild(header);
|
||||
newPage.appendChild(newContainer);
|
||||
mindmapContainer.appendChild(newPage);
|
||||
|
||||
// Neue Cytoscape-Instanz erstellen
|
||||
const newCy = cytoscape({
|
||||
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(),
|
||||
layout: {
|
||||
name: 'cose',
|
||||
@@ -861,30 +999,17 @@ async function loadSubthemes(node) {
|
||||
padding: 30,
|
||||
nodeRepulsion: 4500,
|
||||
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
|
||||
newCy.on('tap', 'node', async function(evt) {
|
||||
const clickedNode = evt.target;
|
||||
@@ -897,26 +1022,8 @@ async function loadSubthemes(node) {
|
||||
cy.container().style.display = 'none';
|
||||
newPage.style.display = 'block';
|
||||
|
||||
// Layout ausführen
|
||||
newCy.layout().run();
|
||||
|
||||
// Erfolgsmeldung anzeigen
|
||||
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) {
|
||||
console.error('Fehler beim Laden der Subthemen:', error);
|
||||
showFlash('Fehler beim Laden der Subthemen', 'error');
|
||||
@@ -1001,4 +1108,7 @@ style.textContent = `
|
||||
text-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Initialisiere die Mindmap beim Laden der Seite
|
||||
document.addEventListener('DOMContentLoaded', initializeMindmap);
|
||||
@@ -1106,4 +1106,66 @@ window.addEventListener('beforeunload', () => {
|
||||
if (window.neuralNetworkBackground) {
|
||||
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