@overtone-art/canvas-editor-core 0.5.0 → 0.5.1
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.d.mts +42 -2
- package/dist/index.d.ts +42 -2
- package/dist/index.global.js +28 -28
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +77 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +77 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -966,8 +966,10 @@ function computeTilePositions(config, targetW, targetH, baseW, baseH, origin) {
|
|
|
966
966
|
}
|
|
967
967
|
return placements;
|
|
968
968
|
}
|
|
969
|
-
function drawTiles(ctx, img, config, targetW, targetH, baseW, baseH, origin) {
|
|
969
|
+
function drawTiles(ctx, img, config, targetW, targetH, baseW, baseH, origin, draw) {
|
|
970
970
|
const anchor = origin ?? { x: targetW / 2, y: targetH / 2 };
|
|
971
|
+
const drawW = draw && draw.width > 0 ? draw.width : baseW;
|
|
972
|
+
const drawH = draw && draw.height > 0 ? draw.height : baseH;
|
|
971
973
|
ctx.save();
|
|
972
974
|
ctx.translate(anchor.x, anchor.y);
|
|
973
975
|
ctx.rotate(config.angle * Math.PI / 180);
|
|
@@ -975,7 +977,7 @@ function drawTiles(ctx, img, config, targetW, targetH, baseW, baseH, origin) {
|
|
|
975
977
|
ctx.save();
|
|
976
978
|
ctx.translate(tile.x, tile.y);
|
|
977
979
|
ctx.rotate(tile.rotation);
|
|
978
|
-
ctx.drawImage(img, -
|
|
980
|
+
ctx.drawImage(img, -drawW / 2, -drawH / 2, drawW, drawH);
|
|
979
981
|
ctx.restore();
|
|
980
982
|
}
|
|
981
983
|
ctx.restore();
|
|
@@ -997,6 +999,7 @@ function mod2(n) {
|
|
|
997
999
|
var MAX_SNAPSHOT_PIXELS = 16e6;
|
|
998
1000
|
var MAX_SNAPSHOT_SCALE = 8;
|
|
999
1001
|
var SNAPSHOT_SHRINK_FACTOR = 2;
|
|
1002
|
+
var TILE_BLEED_DEVICE_PX = 2;
|
|
1000
1003
|
var TiledPatternObject = class _TiledPatternObject extends import_fabric2.FabricObject {
|
|
1001
1004
|
static type = "TiledPattern";
|
|
1002
1005
|
/** The layer's real object. Never rendered directly (it is kept at opacity 0). */
|
|
@@ -1086,7 +1089,8 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric2.Fabric
|
|
|
1086
1089
|
const { width, height } = this.area;
|
|
1087
1090
|
if (width <= 0 || height <= 0) return;
|
|
1088
1091
|
const { tileW, tileH } = this.liveTile();
|
|
1089
|
-
const
|
|
1092
|
+
const scale = contextScale(ctx);
|
|
1093
|
+
const snapshot = this.ensureSnapshot(scale * (tileW / Math.max(1, this.baseW())));
|
|
1090
1094
|
if (!snapshot) return;
|
|
1091
1095
|
const centre = this.getCenterPoint();
|
|
1092
1096
|
const angle = this.liveAngle();
|
|
@@ -1097,7 +1101,17 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric2.Fabric
|
|
|
1097
1101
|
const shift = this.shiftVector(tileW, tileH, angle);
|
|
1098
1102
|
const origin = { x: centre.x - shift.x, y: centre.y - shift.y };
|
|
1099
1103
|
const config = { ...this.config, angle, offsetX: 0, offsetY: 0 };
|
|
1100
|
-
drawTiles(
|
|
1104
|
+
drawTiles(
|
|
1105
|
+
ctx,
|
|
1106
|
+
snapshot,
|
|
1107
|
+
config,
|
|
1108
|
+
width,
|
|
1109
|
+
height,
|
|
1110
|
+
tileW,
|
|
1111
|
+
tileH,
|
|
1112
|
+
origin,
|
|
1113
|
+
this.drawSize(tileW, tileH, TILE_BLEED_DEVICE_PX / scale)
|
|
1114
|
+
);
|
|
1101
1115
|
ctx.restore();
|
|
1102
1116
|
}
|
|
1103
1117
|
/** Raster fallback for SVG export — one `<image>` covering the print area. */
|
|
@@ -1123,7 +1137,9 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric2.Fabric
|
|
|
1123
1137
|
height,
|
|
1124
1138
|
tileW,
|
|
1125
1139
|
tileH,
|
|
1126
|
-
{ x: centre.x - shift.x, y: centre.y - shift.y }
|
|
1140
|
+
{ x: centre.x - shift.x, y: centre.y - shift.y },
|
|
1141
|
+
// The fallback raster is built at 1:1, so a device pixel is a canvas unit.
|
|
1142
|
+
this.drawSize(tileW, tileH, TILE_BLEED_DEVICE_PX)
|
|
1127
1143
|
);
|
|
1128
1144
|
return [
|
|
1129
1145
|
`<g transform="rotate(${-angle}) translate(${-centre.x} ${-centre.y})">`,
|
|
@@ -1163,18 +1179,64 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric2.Fabric
|
|
|
1163
1179
|
});
|
|
1164
1180
|
return Promise.resolve(copy);
|
|
1165
1181
|
}
|
|
1182
|
+
/**
|
|
1183
|
+
* The source's tile footprint: its bounding box, minus a stroke it reserves
|
|
1184
|
+
* room for but never paints.
|
|
1185
|
+
*
|
|
1186
|
+
* fabric keeps `strokeWidth` inside an object's box whether or not a `stroke`
|
|
1187
|
+
* colour paints it, and the built-in shapes arrive with `strokeWidth: 1` and no
|
|
1188
|
+
* stroke. Stepping the grid by that box puts a transparent seam between every
|
|
1189
|
+
* pair of neighbours at zero spacing: the artwork is a pixel narrower than the
|
|
1190
|
+
* box it is stepped by.
|
|
1191
|
+
*/
|
|
1192
|
+
sourceBox() {
|
|
1193
|
+
const rect = this.source.getBoundingRect();
|
|
1194
|
+
if (paintsStroke(this.source)) return { width: rect.width, height: rect.height };
|
|
1195
|
+
const dims = this.source._getTransformedDimensions({ strokeWidth: 0 });
|
|
1196
|
+
const radians = (this.source.angle ?? 0) * Math.PI / 180;
|
|
1197
|
+
const cos = Math.abs(Math.cos(radians));
|
|
1198
|
+
const sin = Math.abs(Math.sin(radians));
|
|
1199
|
+
return {
|
|
1200
|
+
width: dims.x * cos + dims.y * sin,
|
|
1201
|
+
height: dims.x * sin + dims.y * cos
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1166
1204
|
/** The source's on-canvas width, before the tile scale. */
|
|
1167
1205
|
baseW() {
|
|
1168
|
-
return Math.max(1, this.
|
|
1206
|
+
return Math.max(1, this.sourceBox().width);
|
|
1207
|
+
}
|
|
1208
|
+
/** The source's on-canvas height, before the tile scale. */
|
|
1209
|
+
baseH() {
|
|
1210
|
+
return Math.max(1, this.sourceBox().height);
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Size to paint the snapshot at, for a tile of `tileW × tileH`.
|
|
1214
|
+
*
|
|
1215
|
+
* The snapshot is NOT exactly the source's box: `toCanvasElement` rounds the
|
|
1216
|
+
* raster up to whole pixels and pads it further for a shadow. Painting it into
|
|
1217
|
+
* the tile step would squeeze the artwork inside that padding — every tile
|
|
1218
|
+
* shrinks by up to a pixel and the grid shows transparent seams at zero
|
|
1219
|
+
* spacing. So the bitmap is painted at its own footprint, scaled by the same
|
|
1220
|
+
* factor the tile is, and the step stays the source's box.
|
|
1221
|
+
*/
|
|
1222
|
+
drawSize(tileW, tileH, bleed = 0) {
|
|
1223
|
+
const snapshot = this.snapshotEl;
|
|
1224
|
+
if (!snapshot || this.snapshotScale <= 0) {
|
|
1225
|
+
return { width: tileW + bleed, height: tileH + bleed };
|
|
1226
|
+
}
|
|
1227
|
+
return {
|
|
1228
|
+
width: snapshot.width / this.snapshotScale * (tileW / this.baseW()) + bleed,
|
|
1229
|
+
height: snapshot.height / this.snapshotScale * (tileH / this.baseH()) + bleed
|
|
1230
|
+
};
|
|
1169
1231
|
}
|
|
1170
1232
|
/** Tile size from the config alone, ignoring any in-flight gesture. */
|
|
1171
1233
|
baseTile() {
|
|
1172
|
-
const
|
|
1234
|
+
const box = this.sourceBox();
|
|
1173
1235
|
const scale = Math.max(1, this.config.scale ?? 100) / 100;
|
|
1174
1236
|
const floor = this.tileFloor();
|
|
1175
1237
|
return {
|
|
1176
|
-
tileW: Math.max(floor,
|
|
1177
|
-
tileH: Math.max(floor,
|
|
1238
|
+
tileW: Math.max(floor, box.width * scale),
|
|
1239
|
+
tileH: Math.max(floor, box.height * scale)
|
|
1178
1240
|
};
|
|
1179
1241
|
}
|
|
1180
1242
|
/** Tile size as drawn right now, including a live scale gesture. */
|
|
@@ -1237,6 +1299,12 @@ var TiledPatternObject = class _TiledPatternObject extends import_fabric2.Fabric
|
|
|
1237
1299
|
return Math.max(0.05, Math.min(requested, budgeted));
|
|
1238
1300
|
}
|
|
1239
1301
|
};
|
|
1302
|
+
function paintsStroke(object) {
|
|
1303
|
+
if (!object.strokeWidth || object.strokeWidth <= 0) return false;
|
|
1304
|
+
const { stroke } = object;
|
|
1305
|
+
if (typeof stroke === "string") return stroke !== "" && stroke !== "transparent";
|
|
1306
|
+
return stroke !== null && stroke !== void 0;
|
|
1307
|
+
}
|
|
1240
1308
|
function clampPercent(value) {
|
|
1241
1309
|
if (typeof value !== "number" || !Number.isFinite(value)) return 0;
|
|
1242
1310
|
return Math.max(-100, Math.min(100, value));
|