Integrate symbols/ into main repo

- Added symbols/ folder (previously separate repo)
- Removed symbols/ from .gitignore
- Updated CLAUDE.md documentation
- Deleted separate Symbols repo on Gitea

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
architeur
2025-12-14 20:53:14 +01:00
parent e43d4bde1f
commit 7672653254
19 changed files with 9497 additions and 4 deletions

View File

@@ -0,0 +1,371 @@
/**
* SVG Generator mit SVG.js
* Erzeugt Text-Symbole mit Rahmen und Pfeilen
*/
var SvgGenerator = {
getDashArray: function(style, weight) {
switch(style) {
case 'dashed': return (weight * 4) + ',' + (weight * 2);
case 'dotted': return weight + ',' + (weight * 1.5);
default: return null;
}
},
measureText: function(text, fontSize) {
var lines = text.split('\n');
var charWidth = fontSize * 0.6;
var lineHeight = fontSize * 1.3;
var maxLength = 0;
for (var i = 0; i < lines.length; i++) {
if (lines[i].length > maxLength) maxLength = lines[i].length;
}
if (maxLength === 0) maxLength = 4;
return {
lines: lines,
width: maxLength * charWidth,
height: lines.length * lineHeight,
lineHeight: lineHeight,
charWidth: charWidth
};
},
createShape: function(draw, shape, x, y, width, height, style) {
var shapeEl;
var halfW = width / 2;
var halfH = height / 2;
var cx = x + halfW;
var cy = y + halfH;
switch(shape) {
case 'rect':
shapeEl = draw.rect(width, height).move(x, y).radius(4);
break;
case 'rounded':
shapeEl = draw.rect(width, height).move(x, y).radius(Math.min(halfW, halfH) * 0.5);
break;
case 'oval':
shapeEl = draw.ellipse(width, height).center(cx, cy);
break;
case 'diamond':
shapeEl = draw.polygon([
[cx, y],
[x + width, cy],
[cx, y + height],
[x, cy]
]);
break;
default:
return null;
}
if (shapeEl) {
shapeEl.fill('none').stroke({ color: style.color, width: style.weight });
if (style.dashArray) {
shapeEl.attr('stroke-dasharray', style.dashArray);
}
}
return shapeEl;
},
calculateArrowHead: function(endX, endY, prevX, prevY, size, tipLength) {
// Berechne den Winkel der Pfeilrichtung
var angle = Math.atan2(endY - prevY, endX - prevX);
// Die zwei hinteren Punkte der Pfeilspitze:
// - tipLength zurueck in Pfeilrichtung
// - size/2 seitlich (links und rechts)
var baseX = endX - tipLength * Math.cos(angle);
var baseY = endY - tipLength * Math.sin(angle);
// Senkrecht zur Pfeilrichtung (90 Grad = PI/2)
var perpAngle = angle + Math.PI / 2;
var halfWidth = size / 2;
return [
[endX, endY], // Spitze
[baseX + halfWidth * Math.cos(perpAngle), baseY + halfWidth * Math.sin(perpAngle)], // Links
[baseX - halfWidth * Math.cos(perpAngle), baseY - halfWidth * Math.sin(perpAngle)] // Rechts
];
},
createArrow: function(draw, direction, frameRect, options) {
var length = options.length;
var angle = options.angle;
var bendPos = options.bendPos;
var color = options.color;
var weight = options.weight;
var dashArray = options.dashArray;
var arrowSize = options.arrowSize;
var tipLength = options.tipLength;
var angleRad = angle * Math.PI / 180;
var bend = bendPos / 100;
var startX, startY, midX, midY, endX, endY;
var fx = frameRect.x;
var fy = frameRect.y;
var fw = frameRect.width;
var fh = frameRect.height;
// Fuer Standalone-Pfeile (width=0, height=0) ist fx,fy der Startpunkt
var isStandalone = (fw === 0 && fh === 0);
var cx = isStandalone ? fx : fx + fw / 2;
var cy = isStandalone ? fy : fy + fh / 2;
switch(direction) {
case 'top':
startX = cx;
startY = isStandalone ? fy : fy;
midX = startX; midY = startY - length * bend;
endX = midX + Math.sin(angleRad) * (length * (1 - bend));
endY = midY - Math.cos(angleRad) * (length * (1 - bend));
break;
case 'bottom':
startX = cx;
startY = isStandalone ? fy : fy + fh;
midX = startX; midY = startY + length * bend;
endX = midX + Math.sin(angleRad) * (length * (1 - bend));
endY = midY + Math.cos(angleRad) * (length * (1 - bend));
break;
case 'left':
startX = isStandalone ? fx : fx;
startY = cy;
midX = startX - length * bend; midY = startY;
endX = midX - Math.cos(angleRad) * (length * (1 - bend));
endY = midY + Math.sin(angleRad) * (length * (1 - bend));
break;
case 'right':
startX = fx + fw; startY = cy;
midX = startX + length * bend; midY = startY;
endX = midX + Math.cos(angleRad) * (length * (1 - bend));
endY = midY + Math.sin(angleRad) * (length * (1 - bend));
break;
default:
return null;
}
var points = (angle !== 0 && bend > 0 && bend < 1)
? [[startX, startY], [midX, midY], [endX, endY]]
: [[startX, startY], [endX, endY]];
var pathData = '';
for (var i = 0; i < points.length; i++) {
pathData += (i === 0 ? 'M' : 'L') + points[i][0] + ' ' + points[i][1] + ' ';
}
var line = draw.path(pathData).fill('none').stroke({ color: color, width: weight });
if (dashArray) line.attr('stroke-dasharray', dashArray);
var lastPoint = points[points.length - 1];
var prevPoint = points[points.length - 2];
var headPoints = this.calculateArrowHead(lastPoint[0], lastPoint[1], prevPoint[0], prevPoint[1], arrowSize, tipLength);
var arrowHead = draw.polygon(headPoints).fill(color).stroke({ color: color, width: 1 });
return draw.group().add(line).add(arrowHead);
},
createText: function(draw, text, x, y, options) {
var fontSize = options.fontSize;
var color = options.color;
var lines = options.lines;
var lineHeight = options.lineHeight;
var group = draw.group();
var totalHeight = lines.length * lineHeight;
var startY = y - (totalHeight / 2) + (fontSize * 0.8);
for (var i = 0; i < lines.length; i++) {
var lineY = startY + (i * lineHeight);
var lineText = lines[i] || ' ';
// Verwende plain SVG text element fuer bessere Kontrolle
var textEl = draw.plain(lineText)
.font({
family: 'Arial, sans-serif',
size: fontSize,
anchor: 'middle'
})
.fill(color)
.attr('x', x)
.attr('y', lineY)
.attr('text-anchor', 'middle');
group.add(textEl);
}
return group;
},
generate: function(state) {
var s = state;
var textMetrics = this.measureText(s.text || 'Text', s.fontSize);
var frameScale = s.frameScale / 100;
var frameWidth = (textMetrics.width + s.paddingLeft + s.paddingRight) * frameScale;
var frameHeight = (textMetrics.height + s.paddingTop + s.paddingBottom) * frameScale;
var minWidth = Math.max(frameWidth, 40);
var minHeight = Math.max(frameHeight, 30);
// Pfeil-Raum berechnen inkl. Winkel-Abweichung
var arrowSpace = 0;
var arrowSideSpace = 0;
if (s.arrow !== 'none') {
var angleRad = Math.abs(s.arrowAngle) * Math.PI / 180;
var bendFactor = s.arrowBend / 100;
var effectiveLength = s.arrowLength * (1 - bendFactor);
// Hauptrichtung: volle Laenge + Spitze + Puffer
arrowSpace = s.arrowLength + s.arrowTipLength + 20;
// Seitliche Abweichung durch Winkel
arrowSideSpace = Math.abs(Math.sin(angleRad) * effectiveLength) + s.arrowSize + 10;
}
var svgWidth = minWidth;
var svgHeight = minHeight;
var offsetX = 0, offsetY = 0;
switch(s.arrow) {
case 'left':
svgWidth += arrowSpace;
svgHeight = Math.max(svgHeight, minHeight + arrowSideSpace * 2);
offsetX = arrowSpace;
offsetY = (svgHeight - minHeight) / 2;
break;
case 'right':
svgWidth += arrowSpace;
svgHeight = Math.max(svgHeight, minHeight + arrowSideSpace * 2);
offsetY = (svgHeight - minHeight) / 2;
break;
case 'top':
svgHeight += arrowSpace;
svgWidth = Math.max(svgWidth, minWidth + arrowSideSpace * 2);
offsetY = arrowSpace;
offsetX = (svgWidth - minWidth) / 2;
break;
case 'bottom':
svgHeight += arrowSpace;
svgWidth = Math.max(svgWidth, minWidth + arrowSideSpace * 2);
offsetX = (svgWidth - minWidth) / 2;
break;
}
var draw = SVG().size(svgWidth, svgHeight);
var frameX = offsetX;
var frameY = offsetY;
var frameCx = frameX + minWidth / 2;
var frameCy = frameY + minHeight / 2;
// Text-Verschiebung basierend auf asymmetrischem Padding
// Wenn links mehr Padding ist, verschiebt sich der Text nach rechts
var textOffsetX = (s.paddingLeft - s.paddingRight) / 2;
var textOffsetY = (s.paddingTop - s.paddingBottom) / 2;
var textCx = frameCx + textOffsetX;
var textCy = frameCy + textOffsetY;
var dashArray = this.getDashArray(s.lineStyle, s.lineWeight);
var strokeStyle = { color: s.frameColor, weight: s.lineWeight, dashArray: dashArray };
if (s.shape !== 'none') {
this.createShape(draw, s.shape, frameX, frameY, minWidth, minHeight, strokeStyle);
}
if (s.arrow !== 'none') {
this.createArrow(draw, s.arrow, {
x: frameX, y: frameY, width: minWidth, height: minHeight
}, {
length: s.arrowLength, angle: s.arrowAngle, bendPos: s.arrowBend,
color: s.frameColor, weight: s.lineWeight, dashArray: dashArray,
arrowSize: s.arrowSize, tipLength: s.arrowTipLength
});
}
this.createText(draw, s.text, textCx, textCy, {
fontSize: s.fontSize, color: s.textColor,
lines: textMetrics.lines, lineHeight: textMetrics.lineHeight
});
return draw.svg();
},
generateArrowOnly: function(state) {
var s = state;
if (s.arrow === 'none') return null;
var length = s.arrowLength;
var tipLength = s.arrowTipLength;
var arrowSize = s.arrowSize;
var padding = 40;
// Berechne die tatsaechlichen Endpunkte des Pfeils
var angleRad = s.arrowAngle * Math.PI / 180;
var bendFactor = s.arrowBend / 100;
var straightPart = length * bendFactor;
var angledPart = length * (1 - bendFactor);
// Seitliche Ausdehnung durch den Winkel (mit Vorzeichen)
var sideExtent = Math.sin(angleRad) * angledPart;
var mainExtent = Math.cos(angleRad) * angledPart;
// Mindestgroesse fuer die seitliche Ausdehnung
var minSideSpace = Math.abs(sideExtent) + arrowSize + tipLength + padding;
var width, height, startX, startY;
switch(s.arrow) {
case 'right':
width = straightPart + mainExtent + tipLength + padding * 2;
height = Math.max(arrowSize * 4, minSideSpace * 2);
startX = padding;
// Bei positivem Winkel geht der Pfeil nach unten, also Start weiter oben
startY = height / 2 - sideExtent / 2;
break;
case 'left':
width = straightPart + mainExtent + tipLength + padding * 2;
height = Math.max(arrowSize * 4, minSideSpace * 2);
startX = width - padding;
startY = height / 2 + sideExtent / 2;
break;
case 'bottom':
width = Math.max(arrowSize * 4, minSideSpace * 2);
height = straightPart + mainExtent + tipLength + padding * 2;
startX = width / 2 - sideExtent / 2;
startY = padding;
break;
case 'top':
width = Math.max(arrowSize * 4, minSideSpace * 2);
height = straightPart + mainExtent + tipLength + padding * 2;
startX = width / 2 + sideExtent / 2;
startY = height - padding;
break;
}
// Stelle sicher, dass alle Werte positiv sind
width = Math.max(width, 80);
height = Math.max(height, 60);
var draw = SVG().size(width, height);
// Simuliere einen Rahmen-Punkt als Startpunkt
var frameRect = {
x: startX,
y: startY,
width: 0,
height: 0
};
this.createArrow(draw, s.arrow, frameRect, {
length: s.arrowLength, angle: s.arrowAngle, bendPos: s.arrowBend,
color: s.frameColor, weight: s.lineWeight,
dashArray: this.getDashArray(s.lineStyle, s.lineWeight),
arrowSize: s.arrowSize, tipLength: s.arrowTipLength
});
return draw.svg();
}
};
window.SvgGenerator = SvgGenerator;