Я не думаю, что я могу просто выполнить математику на высоте/ширине изображения, потому что формы повторяются, что могут представлять только фракции. Мне нужны изображения, чтобы сидеть в точных границах, продиктованных путем вычисления отраженных точек (которые жестко кодируются в моем примере, извините). < /P>
Я сделал более минимальную версию моего кода, чтобы показать здесь. У него просто несколько предопределенных тестовых линий/форм, и, к сожалению, не охватывает все сценарии. Оригинальный код слишком большой, чтобы публиковать. Часть холста, которая читает «1», должна извлечь из начала координат. < /P>
canvas {
border: 1px solid grey;
margin: 2px;
}
"use strict";
const canvasWidth = 600;
const canvasHeight = 830;
const pieces = [
[{points:[new DOMPoint(140, 50),new DOMPoint(140.00000000000003, -90),new DOMPoint(90.00000000000001, -90),new DOMPoint(90, 0),], line:{ start: new DOMPoint(283.6663192636163, 193.66631926361632), end: new DOMPoint(-52.666319263616316, -142.66631926361632) }, original:[new DOMPoint(140, 50),new DOMPoint(0, 50),new DOMPoint(0, 0),new DOMPoint(90, 0),], intersects:[new DOMPoint(90, 0),new DOMPoint(140, 50),], origTopLeft:new DOMPoint(0, 0),width:50.00000000000003, height:50.00000000000003}, {points:[new DOMPoint(158.36517719568567, 44.67326250912334),new DOMPoint(163.97896049896048, -53.783451143451146),new DOMPoint(213.82095634095634, -49.58802494802492),new DOMPoint(211.1386748844376, -2.5451301597599514),], line:{ start: new DOMPoint(252.24682141160773, -39.3261033682806), end: new DOMPoint(101.75317858839227, 95.3261033682806) }, original:[new DOMPoint(158.36517719568567, 44.67326250912335),new DOMPoint(256.8378378378378, 50),new DOMPoint(258.18918918918916, -1.6317320576126618e-15),new DOMPoint(211.1386748844376, -2.5451301597599563),], intersects:[new DOMPoint(211.1386748844376, -2.5451301597599514),new DOMPoint(158.36517719568567, 44.67326250912334),], origTopLeft:new DOMPoint(158.36517719568567, -2.5451301597599563),width:55.45577914527067, height:55.45577914527067},{points:[new DOMPoint(198.38255973344914, 8.868236027966603),new DOMPoint(-153.64897521683866, 5.578032470538176),new DOMPoint(-154.11627140114496, 55.57584876561373),new DOMPoint(143.07549812764987, 58.3535016752606),], line:{ start: new DOMPoint(436.3443301443184, -204.04492697123226), end: new DOMPoint(-82.3443301443184, 260.04492697123226) }, original:[new DOMPoint(198.3825597334491, 8.868236027966553),new DOMPoint(162.65825355141538, 359.09787638799855),new DOMPoint(112.9163540869709, 354.0240772523315),new DOMPoint(143.0754981276499, 58.353501675260645),], intersects:[new DOMPoint(143.07549812764987, 58.3535016752606),new DOMPoint(198.38255973344914, 8.868236027966603),], origTopLeft:new DOMPoint(112.9163540869709, 8.868236027966553),width:352.49883113459407, height:352.49883113459407}, ],
[{points:[new DOMPoint(183, 0),new DOMPoint(-115.80000000000018, -398.4000000000001),new DOMPoint(-155.80000000000018, -368.4000000000001),new DOMPoint(158, 50),], line:{ start: new DOMPoint(466.81944546997806, -567.6388909399561), end: new DOMPoint(-126.81944546997806, 619.6388909399561) }, original:[new DOMPoint(183.00000000000003, 0),new DOMPoint(681, 0),new DOMPoint(681, 50),new DOMPoint(158, 50),], intersects:[new DOMPoint(158, 50),new DOMPoint(183, 0),], originalTopLeft:new DOMPoint(158, 0),width:338.8000000000002, height:338.8000000000002}, ],
[{points:[new DOMPoint(157.50666666666666, 24.98461538461538),new DOMPoint(232.01174895512395, 458.84515237596656),new DOMPoint(182.7330781575854, 467.307575458501),new DOMPoint(121.1733333333333, 108.830769230769),], line:{ start: new DOMPoint(358.8607804360353, -439.6787240831585), end: new DOMPoint(-43.86078043603533, 489.6787240831585) }, original:[new DOMPoint(157.50666666666666, 24.9846153846154),new DOMPoint(-210.00917431192647, 267.30275229357795),new DOMPoint(-182.48623853211006, 309.045871559633),new DOMPoint(121.17333333333352, 108.83076923076914),], intersects:[new DOMPoint(121.1733333333333, 108.830769230769),new DOMPoint(157.50666666666666, 24.98461538461538),], originalTopLeft:new DOMPoint(-210.00917431192647, 24.9846153846154),width:110.83841562179065, height:110.83841562179065}, {points:[new DOMPoint(118.49999999999997, 49.99999999999999),new DOMPoint(207.78082191780817, 127.91780821917807),new DOMPoint(240.6575342465753, 90.24657534246575),new DOMPoint(137.25, -4.9897642155143516e-15),], line:{ start: new DOMPoint(199.2848941516392, -165.42638440437122), end: new DOMPoint(55.71510584836079, 217.42638440437122) }, original:[new DOMPoint(118.5, 50),new DOMPoint(0, 50),new DOMPoint(0, 0),new DOMPoint(137.25, 0),], intersects:[new DOMPoint(137.25, -4.9897642155143516e-15),new DOMPoint(118.49999999999997, 49.99999999999999),], originalTopLeft:new DOMPoint(0, 0),width:122.15753424657532, height:122.15753424657532}]
];
// reflect, rotate by angle of the longest edge of the pre-reflected shape so that the image renders at the right angle on the page
function getReflectionMatrix(piece, ctx) {
const { line, original, points, intersects } = piece;
const anchor = intersects[0]; // point where the line and the other edges meet, used as an origin for reflection
const display = new DOMMatrix();
reflectMatrix(display, line, anchor);
rotateMatrix(display, original, anchor); // i do this so the image shows up at the right angle on the canvas
translateMatrix(display, original, points, ctx);
return display;
}
function reflectMatrix(matrix, line, anchor) {
const radians = getAngleFromOrigin(line);
const angle = getDegreesFromRadians(radians);
matrix.translateSelf(anchor.x, anchor.y);
matrix.rotateSelf(angle);
matrix.scaleSelf(1, -1);
matrix.rotateSelf(-angle);
}
function rotateMatrix(matrix, originalPoints, anchor) {
const longestEdgeAngle = getLongestEdgeAngle(originalPoints);
const degrees = getDegreesFromRadians(longestEdgeAngle);
matrix.rotateSelf(degrees);
matrix.translateSelf(-anchor.x, -anchor.y);
}
function translateMatrix(matrix, originalPoints, newPoints, ctx) {
const { width, height } = getDimensions(newPoints);
const { pointsUp, pointsLeft } = getMatrixDirection(matrix);
let { x, y } = getTopLeft(newPoints);
if (pointsUp) y += height;
if (pointsLeft) x += width;
//const target = findClosestPoint(newPoints, x, y); // if you look at the top left test, the bottom of the image is too high on the yellow shape
const target = new DOMPoint(x, y); // make a new dompoint for it just for ease of switching between variables in testing
const pt0T = new DOMPoint(0, 0).matrixTransform(matrix);
const dx = target.x - pt0T.x;
const dy = target.y - pt0T.y;
const translated = new DOMMatrix().translateSelf(dx, dy);
matrix.preMultiplySelf(translated);
drawDebugMarker(target.x, target.y, "blue", ctx);
}
// helpers for getting shape dimensions etc.
function getAngleFromOrigin(line) {
const { start, end } = line;
const dx = end.x - start.x;
const dy = end.y - start.y;
return Math.atan2(dy, dx);
}
function getLongestEdgeAngle(points) {
let maxLength = 0;
let bestAngle = 0;
for (let i = 0; i < points.length; i++) {
const a = points;
const b = points[(i + 1) % points.length];
const dx = b.x - a.x;
const dy = b.y - a.y;
const length = Math.hypot(dx, dy);
if (length > maxLength) {
maxLength = length;
bestAngle = Math.atan2(dy, dx);
}
}
return bestAngle;
}
function getDegreesFromRadians(angle) {
const degrees = angle * 180 / Math.PI;
return ((degrees % 360) + 360) % 360;
}
function getTopLeft(points) {
const { minX, maxX, minY, maxY } = getBoundingBox(points);
return new DOMPoint(minX, minY);
}
function getBoundingBox(points) {
const coordsX = points.map(point => point.x);
const minX = Math.min(...coordsX);
const maxX = Math.max(...coordsX);
const coordsY = points.map(point => point.y);
const minY = Math.min(...coordsY);
const maxY = Math.max(...coordsY);
return { minX, maxX, minY, maxY };
}
function getDimensions(points) {
const { minX, maxX, minY, maxY } = getBoundingBox(points);
const width = maxX - minX;
const height = maxY - minY;
return { width, height };
}
function getMatrixDirection(matrix) {
const rightX = matrix.a;
const rightY = matrix.b;
const downX = matrix.c;
const downY = matrix.d;
const pointsLeft = Math.abs(rightX) >= Math.abs(rightY) ? rightX < 0 : rightY < 0;
const pointsUp = Math.abs(downY) >= Math.abs(downX) ? downY < 0 : downX < 0;
return { pointsLeft, pointsUp };
}
function findClosestPoint(points, x, y) {
let minDist = Infinity;
let closest = points[0];
for (const point of points) {
const dist = Math.hypot(point.x - x, point.y - y);
if (dist < minDist) {
minDist = dist;
closest = point;
}
}
return closest;
}
// drawing
function loopThroughPieces(test, ctx) {
for (let i = 0; i < test.length; i++) {
ctx.setTransform(canvasTransform);
const piece = test;
const colour = getColour(i);
const display = getReflectionMatrix(piece, ctx);
drawPiece(piece, colour, display, ctx);
}
}
function getColour(i) {
// red comes first
const hue = (i * 45) % 360;
const lightness = 100 - (40 + 10);
const alpha = 0.5;
return `hsla(${hue}, 90%, ${lightness}%, ${alpha})`;
}
function drawPiece(piece, colour, display, ctx) {
ctx.save();
tracePiecePath(piece.points, ctx);
ctx.globalAlpha = 0.65;
//ctx.clip(); // it's supposed to be clipped, but i unclipped for visualisation, since sometimes the image floats outside of the outline
ctx.setTransform(canvasTransform.multiply(display));
ctx.drawImage(image, 0, 0, image.width, image.height);
ctx.strokeStyle = colour;
ctx.lineWidth = 3;
ctx.globalAlpha = 1;
ctx.stroke();
ctx.restore();
ctx.save();
}
function tracePiecePath(points, ctx) {
ctx.beginPath();
const firstPoint = points[0];
ctx.moveTo(firstPoint.x, firstPoint.y);
points.slice(1).forEach(point => {
ctx.lineTo(point.x, point.y);
});
ctx.closePath();
}
function drawDebugMarker(x, y, colour, ctx) {
ctx.beginPath();
ctx.arc(x, y, 5, 0, 2 * Math.PI);
ctx.fillStyle = colour;
ctx.fill();
}
// everything below is just assembling test cases etc. and rendering them
function makeCanvasTransform() {
canvasTransform.scaleSelf(0.6, 0.6);
canvasTransform.translateSelf(canvasWidth/2, canvasHeight/2);
}
function drawDebugImage() {
const imgCtx = image.getContext("2d");
imgCtx.fillStyle = "white";
imgCtx.fillRect(0, 0, image.width, image.height);
imgCtx.font = "20px arial";
imgCtx.textAlign = "center";
imgCtx.fillStyle = "black";
const segmentWidth = image.width/12;
let offsetX = 0;
for (let i = 0; i < Math.ceil(image.width/segmentWidth); i++) {
imgCtx.strokeRect(offsetX, 0, segmentWidth, image.height);
imgCtx.fillText(i + 1, offsetX + segmentWidth/2, image.height/2);
offsetX += segmentWidth;
}
}
function gatherCtxs() {
const ctxs = [];
for (let i = 0; i < pieces.length; i++) {
const canvas = document.createElement("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
canvases.appendChild(canvas);
if (i % 2 == 1) {
const br = document.createElement("br");
canvases.appendChild(br);
}
ctxs.push(canvas.getContext("2d"));
}
return ctxs;
}
const image = document.getElementById("image");
const canvases = document.getElementById("canvases");
const canvasTransform = new DOMMatrix();
drawDebugImage();
makeCanvasTransform();
const ctxs = gatherCtxs();
for (let i = 0; i < pieces.length; i++) {
loopThroughPieces(pieces, ctxs);
}
< /code>
Спасибо за чтение!

Подробнее здесь: https://stackoverflow.com/questions/796 ... t-etc-of-a