canvu-react 0.3.32 → 0.3.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1043,24 +1043,153 @@ function svgNumber(value) {
1043
1043
  const rounded = Math.round(value * 100) / 100;
1044
1044
  return Number.isInteger(rounded) ? String(rounded) : String(rounded);
1045
1045
  }
1046
- function approximateEllipsePerimeter(rx, ry) {
1047
- if (rx <= 0 || ry <= 0) return 0;
1048
- return Math.PI * (3 * (rx + ry) - Math.sqrt((3 * rx + ry) * (rx + 3 * ry)));
1049
- }
1050
- function architecturalCloudScallopCount(rx, ry, amplitude) {
1051
- const perimeter = approximateEllipsePerimeter(rx, ry);
1052
- const targetScallopLength = Math.max(10, amplitude * 2);
1046
+ function architecturalCloudScallopCount(perimeter, amplitude) {
1047
+ const targetScallopLength = Math.max(18, amplitude * 2.45);
1053
1048
  let count = Math.max(12, Math.round(perimeter / targetScallopLength));
1054
1049
  if (count % 2 === 1) count += 1;
1055
1050
  return count;
1056
1051
  }
1057
- function pointOnSuperellipse(centerX, centerY, radiusX, radiusY, theta, exponent) {
1058
- const cosTheta = Math.cos(theta);
1059
- const sinTheta = Math.sin(theta);
1060
- return [
1061
- centerX + Math.sign(cosTheta) * radiusX * Math.abs(cosTheta) ** (2 / exponent),
1062
- centerY + Math.sign(sinTheta) * radiusY * Math.abs(sinTheta) ** (2 / exponent)
1063
- ];
1052
+ function roundedRectMetrics(width, height, inset, radius) {
1053
+ const left = inset;
1054
+ const top = inset;
1055
+ const right = width - inset;
1056
+ const bottom = height - inset;
1057
+ const rectWidth = Math.max(0, right - left);
1058
+ const rectHeight = Math.max(0, bottom - top);
1059
+ const normalizedRadius = Math.max(
1060
+ 0,
1061
+ Math.min(radius, rectWidth / 2, rectHeight / 2)
1062
+ );
1063
+ const centerX = width / 2;
1064
+ const topHalfLength = Math.max(0, right - normalizedRadius - centerX);
1065
+ const horizontalLength = Math.max(0, rectWidth - normalizedRadius * 2);
1066
+ const verticalLength = Math.max(0, rectHeight - normalizedRadius * 2);
1067
+ const arcLength = normalizedRadius * (Math.PI / 2);
1068
+ return {
1069
+ left,
1070
+ top,
1071
+ right,
1072
+ bottom,
1073
+ radius: normalizedRadius,
1074
+ centerX,
1075
+ topHalfLength,
1076
+ horizontalLength,
1077
+ verticalLength,
1078
+ arcLength,
1079
+ perimeter: horizontalLength * 2 + verticalLength * 2 + Math.PI * 2 * normalizedRadius
1080
+ };
1081
+ }
1082
+ function pointOnLine(startX, startY, endX, endY, t) {
1083
+ return [startX + (endX - startX) * t, startY + (endY - startY) * t];
1084
+ }
1085
+ function pointOnArc(centerX, centerY, radius, startAngle, endAngle, t) {
1086
+ const theta = startAngle + (endAngle - startAngle) * t;
1087
+ return [centerX + Math.cos(theta) * radius, centerY + Math.sin(theta) * radius];
1088
+ }
1089
+ function pointOnRoundedRectPath(metrics, distance) {
1090
+ if (metrics.perimeter <= 0) return [metrics.centerX, metrics.top];
1091
+ let remaining = (distance % metrics.perimeter + metrics.perimeter) % metrics.perimeter;
1092
+ const consume = (length) => {
1093
+ if (length <= 1e-9) return null;
1094
+ if (remaining <= length) return remaining / length;
1095
+ remaining -= length;
1096
+ return null;
1097
+ };
1098
+ let t = consume(metrics.topHalfLength);
1099
+ if (t != null) {
1100
+ return pointOnLine(
1101
+ metrics.centerX,
1102
+ metrics.top,
1103
+ metrics.right - metrics.radius,
1104
+ metrics.top,
1105
+ t
1106
+ );
1107
+ }
1108
+ t = consume(metrics.arcLength);
1109
+ if (t != null) {
1110
+ return pointOnArc(
1111
+ metrics.right - metrics.radius,
1112
+ metrics.top + metrics.radius,
1113
+ metrics.radius,
1114
+ -Math.PI / 2,
1115
+ 0,
1116
+ t
1117
+ );
1118
+ }
1119
+ t = consume(metrics.verticalLength);
1120
+ if (t != null) {
1121
+ return pointOnLine(
1122
+ metrics.right,
1123
+ metrics.top + metrics.radius,
1124
+ metrics.right,
1125
+ metrics.bottom - metrics.radius,
1126
+ t
1127
+ );
1128
+ }
1129
+ t = consume(metrics.arcLength);
1130
+ if (t != null) {
1131
+ return pointOnArc(
1132
+ metrics.right - metrics.radius,
1133
+ metrics.bottom - metrics.radius,
1134
+ metrics.radius,
1135
+ 0,
1136
+ Math.PI / 2,
1137
+ t
1138
+ );
1139
+ }
1140
+ t = consume(metrics.horizontalLength);
1141
+ if (t != null) {
1142
+ return pointOnLine(
1143
+ metrics.right - metrics.radius,
1144
+ metrics.bottom,
1145
+ metrics.left + metrics.radius,
1146
+ metrics.bottom,
1147
+ t
1148
+ );
1149
+ }
1150
+ t = consume(metrics.arcLength);
1151
+ if (t != null) {
1152
+ return pointOnArc(
1153
+ metrics.left + metrics.radius,
1154
+ metrics.bottom - metrics.radius,
1155
+ metrics.radius,
1156
+ Math.PI / 2,
1157
+ Math.PI,
1158
+ t
1159
+ );
1160
+ }
1161
+ t = consume(metrics.verticalLength);
1162
+ if (t != null) {
1163
+ return pointOnLine(
1164
+ metrics.left,
1165
+ metrics.bottom - metrics.radius,
1166
+ metrics.left,
1167
+ metrics.top + metrics.radius,
1168
+ t
1169
+ );
1170
+ }
1171
+ t = consume(metrics.arcLength);
1172
+ if (t != null) {
1173
+ return pointOnArc(
1174
+ metrics.left + metrics.radius,
1175
+ metrics.top + metrics.radius,
1176
+ metrics.radius,
1177
+ Math.PI,
1178
+ Math.PI * 1.5,
1179
+ t
1180
+ );
1181
+ }
1182
+ t = consume(metrics.topHalfLength);
1183
+ if (t != null) {
1184
+ return pointOnLine(
1185
+ metrics.left + metrics.radius,
1186
+ metrics.top,
1187
+ metrics.centerX,
1188
+ metrics.top,
1189
+ t
1190
+ );
1191
+ }
1192
+ return [metrics.centerX, metrics.top];
1064
1193
  }
1065
1194
  function buildRectSvg(width, height, style = DEFAULT_STROKE_STYLE) {
1066
1195
  return `<rect width="${width}" height="${height}" fill="none" stroke="${style.stroke}" stroke-width="${style.strokeWidth}" rx="4"${strokeOpacityAttr(style)} />`;
@@ -1075,35 +1204,34 @@ function buildArchitecturalCloudPathD(width, height, strokeWidth = DEFAULT_STROK
1075
1204
  const h = Math.max(0, height);
1076
1205
  if (w <= 0 || h <= 0) return "";
1077
1206
  const inset = Math.max(0.5, strokeWidth / 2);
1078
- const outerRx = Math.max(0, w / 2 - inset);
1079
- const outerRy = Math.max(0, h / 2 - inset);
1080
- if (outerRx <= 0 || outerRy <= 0) return "";
1081
- const baseExponent = 3.6;
1207
+ const outerWidth = Math.max(0, w - inset * 2);
1208
+ const outerHeight = Math.max(0, h - inset * 2);
1209
+ if (outerWidth <= 0 || outerHeight <= 0) return "";
1082
1210
  const amplitude = Math.min(
1083
- outerRx * 0.45,
1084
- outerRy * 0.45,
1085
- Math.max(2, Math.min(7, Math.min(w, h) * 0.035))
1211
+ outerWidth * 0.12,
1212
+ outerHeight * 0.12,
1213
+ Math.max(5, Math.min(14, Math.min(w, h) * 0.07))
1086
1214
  );
1087
- const innerRx = Math.max(0, outerRx - amplitude);
1088
- const innerRy = Math.max(0, outerRy - amplitude);
1089
- const scallopCount = architecturalCloudScallopCount(innerRx, innerRy, amplitude);
1090
- const angleStep = Math.PI * 2 / scallopCount;
1091
- const cx = w / 2;
1092
- const cy = h / 2;
1093
- const startAngle = -Math.PI / 2;
1094
- const pointOnArchitecturalCloud = (theta, radiusX, radiusY) => pointOnSuperellipse(cx, cy, radiusX, radiusY, theta, baseExponent);
1095
- const [startX, startY] = pointOnArchitecturalCloud(startAngle, innerRx, innerRy);
1215
+ const radius = Math.min(
1216
+ outerWidth / 2,
1217
+ outerHeight / 2,
1218
+ Math.max(amplitude * 2.5, outerHeight * 0.43)
1219
+ );
1220
+ const outer = roundedRectMetrics(w, h, inset, radius);
1221
+ const inner = roundedRectMetrics(
1222
+ w,
1223
+ h,
1224
+ inset + amplitude,
1225
+ Math.max(0, radius - amplitude)
1226
+ );
1227
+ const scallopCount = architecturalCloudScallopCount(outer.perimeter, amplitude);
1228
+ const [startX, startY] = pointOnRoundedRectPath(inner, 0);
1096
1229
  const segments = [`M${svgNumber(startX)} ${svgNumber(startY)}`];
1097
1230
  for (let index = 0; index < scallopCount; index += 1) {
1098
- const segmentStart = startAngle + index * angleStep;
1099
- const segmentMid = segmentStart + angleStep / 2;
1100
- const segmentEnd = segmentStart + angleStep;
1101
- const [controlX, controlY] = pointOnArchitecturalCloud(
1102
- segmentMid,
1103
- outerRx,
1104
- outerRy
1105
- );
1106
- const [endX, endY] = pointOnArchitecturalCloud(segmentEnd, innerRx, innerRy);
1231
+ const controlDistance = (index + 0.5) / scallopCount * outer.perimeter;
1232
+ const endDistance = (index + 1) / scallopCount * inner.perimeter;
1233
+ const [controlX, controlY] = pointOnRoundedRectPath(outer, controlDistance);
1234
+ const [endX, endY] = pointOnRoundedRectPath(inner, endDistance);
1107
1235
  segments.push(
1108
1236
  `Q${svgNumber(controlX)} ${svgNumber(controlY)} ${svgNumber(endX)} ${svgNumber(endY)}`
1109
1237
  );