# 2.52

## c）修改square-limit，换一种使用square-of-four的方式，以另一种不同模式组合起各个角区（例如，你可以让大的Rogers先生从正方形的每个角向外看）。

### a）

cons = (x, y) => [x, y];
car = (z) => z[0];
cdr = (z) => z[1];

makeVect = cons;
xCorVect = car;
yCorVect = cdr;

makeSegment = cons;
startSegment = car;
endSegment = cdr;

o = makeVect(0, 0);
e1 = makeVect(300, 0);
e2 = makeVect(0, 150);

s = makeSegment(makeVect(1, 0), makeVect(0, 1));
console.log('s = ', s);

console.log(startSegment(s));
console.log(endSegment(s));

makeFrame = (origin, edge1, edge2) => {
return [origin, edge1, edge2];
};

originFrame = (frame) => frame[0];
edge1Frame = (frame) => frame[1];
edge2Frame = (frame) => frame[2];

addVect = (v1, v2) => makeVect(xCorVect(v1) + xCorVect(v2), yCorVect(v1) + yCorVect(v2));
subVect = (v1, v2) => makeVect(xCorVect(v1) - xCorVect(v2), yCorVect(v1) - yCorVect(v2));
scaleVect = (s, v) => makeVect(s * xCorVect(v), s * yCorVect(v));

frameCoordMap = (frame) => {
return (v) => {
originFrame(frame),
scaleVect(
xCorVect(v),
edge1Frame(frame)
),
scaleVect(
yCorVect(v),
edge2Frame(frame)
)
)
);
};
}

f = makeFrame(o, e1, e2);

drawLine = (v1, v2) => {
return (ctx) => {
ctx.beginPath();
ctx.moveTo(xCorVect(v1), yCorVect(v1));
ctx.lineTo(xCorVect(v2), yCorVect(v2));
ctx.stroke();
}
};

segmentsPainter = (segmentList) => {
return (frame) => (ctx) => {
const m = frameCoordMap(frame);

segmentList.forEach((segment) => {
drawLine(
m(startSegment(segment)),
m(endSegment(segment))
)(ctx);
});
}
}

applyToCanvas = (painter, canvasSelector, setWH = false) => {
const canvas = document.querySelector(canvasSelector);

if(setWH) {
const originalRatio = canvas.width/canvas.height;
canvas.width = 1000;
canvas.height = canvas.width / originalRatio;
}

const ctx = canvas.getContext('2d');

ctx.clearRect(0, 0, canvas.width, canvas.height);

painter(ctx);
}

wavePainter = (frame) => {
const v1 = makeVect(0.4, 0);
const v2 = makeVect(0.6, 0);
const v3 = makeVect(0, 0.2);
const v4 = makeVect(0.3, 0.2);
const v5 = makeVect(0.7, 0.2);
const v6 = makeVect(0, 0.4);
const v7 = makeVect(0.2, 0.4);
const v8 = makeVect(0.3, 0.35);
const v9 = makeVect(0.42, 0.4);
const v10 = makeVect(0.6, 0.4);
const v11 = makeVect(0.8, 0.4);
const v12 = makeVect(0.3, 0.4);
const v13 = makeVect(0.2, 0.55);
const v14 = makeVect(0.31, 0.5);
const v15 = makeVect(0.6, 0.51);
const v16 = makeVect(1, 0.7);
const v17 = makeVect(0.5, 0.8);
const v18 = makeVect(1, 0.85);
const v19 = makeVect(0.35, 1);
const v20 = makeVect(0.4, 1);
const v21 = makeVect(0.6, 1);
const v22 = makeVect(0.7, 1);

const v23 = makeVect(0.5, 0.1);
const v24 = makeVect(0.55, 0.1);

const v25 = makeVect(0.6, 0.1);
const v26 = makeVect(0.65, 0.1);

return segmentsPainter([
makeSegment(v1, v4),
makeSegment(v2, v5),
makeSegment(v3, v7),
makeSegment(v7, v8),
makeSegment(v8, v9),
makeSegment(v9, v4),
makeSegment(v10, v5),
makeSegment(v10, v11),
makeSegment(v6, v13),
makeSegment(v13, v12),
makeSegment(v12, v14),
makeSegment(v16, v11),
makeSegment(v18, v15),
makeSegment(v19, v14),
makeSegment(v20, v17),
makeSegment(v21, v17),
makeSegment(v22, v15),

makeSegment(v23, v24),
makeSegment(v25, v26),
])(frame);
}

applyToCanvas(wavePainter(f), '#wave-canvas');

transformPainter = (painter, origin, corner1, corner2) => {
return (frame) => {
const m = frameCoordMap(frame);
const newOrigin = m(origin);
return painter(
makeFrame(
newOrigin,
subVect(m(corner1), newOrigin),
subVect(m(corner2), newOrigin)
)
);
}
}

flipHoriz = (painter) => {
return transformPainter(
painter,
makeVect(1, 0),
makeVect(0, 0),
makeVect(1, 1)
)
}

rotateLeft180 = (painter) => {
return transformPainter(
painter,
makeVect(1, 1),
makeVect(0, 1),
makeVect(1, 0)
)
}

rotateLeft270 = (painter) => {
return transformPainter(
painter,
makeVect(1, 0),
makeVect(1, 1),
makeVect(0, 0)
)
}


### b

below = (painter1, painter2) => {
const splitPoint = makeVect(0, 0.5);
const paintBelow = transformPainter(
painter1,
splitPoint,
makeVect(1, 0.5),
makeVect(0, 1)
);
const paintUp = transformPainter(
painter2,
makeVect(0, 0),
makeVect(1, 0),
splitPoint
);

return frame => ctx => {
paintBelow(frame)(ctx);
paintUp(frame)(ctx);
}
}

beside = (painter1, painter2) => {
const splitPoint = makeVect(0.5, 0);
const paintLeft = transformPainter(
painter1,
makeVect(0, 0),
splitPoint,
makeVect(0, 1)
);
const paintRight = transformPainter(
painter2,
splitPoint,
makeVect(1, 0),
makeVect(0.5, 1)
);

return frame => ctx => {
paintLeft(frame)(ctx);
paintRight(frame)(ctx);
};
};

rightSplit = (painter, n) => {
if (n === 0) {
return painter;
}

const smaller = rightSplit(painter, n-1);
return beside(painter, below(smaller, smaller));
};

rotateLeft90 = (painter) => {
return transformPainter(
painter,
makeVect(0, 1),
makeVect(0, 0),
makeVect(1, 1)
)
}

cornerSplit = (painter, n) => {
if(n === 0) {
return painter;
}

const right = rightSplit(painter, n-1);
const up = rotateLeft90(
rightSplit(
rotateLeft90(
rotateLeft90(
rotateLeft90(
painter
)
)
),
n-1
)
);

const topLeft = beside(up, up);
const bottomRight = below(right, right);
const corner = cornerSplit(painter, n-1);

return beside(
below(painter, topLeft),
below(bottomRight, corner)
);
}

applyToCanvas(cornerSplit(wavePainter, 4)(f), '#corner-split-canvas');


### c

squareOfFour = (tl, tr, bl, br) => {
return (painter) => {
const top = beside(tl(painter), tr(painter));
const bottom = beside(bl(painter), br(painter));

return below(bottom, top);
};
};

id = x => x;
flipVert = (painter) => {
return transformPainter(
painter,
makeVect(0, 1),
makeVect(1, 1),
makeVect(0, 0)
);
};

squareLimit = (painter, n) => {
const combine4 = squareOfFour(flipHoriz, id, rotateLeft180, flipVert);

return combine4(cornerSplit(painter, n));
};

applyToCanvas(squareLimit(wavePainter, 4)(f), '#square-of-four-canvas');


jeffTianPainter = (frame) => ctx => {
const jeffTian = document.querySelector('#jeff-tian');

const m = frameCoordMap(frame);
const v1 = makeVect(0, 0);
const v2 = makeVect(1, 1);

const x = xCorVect(m(v1));
const y = yCorVect(m(v1));
const w = xCorVect(m(v2)) - x;
const h = yCorVect(m(v2)) - y;

ctx.drawImage(jeffTian, 0, 0, jeffTian.naturalWidth, jeffTian.naturalHeight, x, y, w, h);
};

applyToCanvas(squareLimit(jeffTianPainter, 4)(f), '#solved', false);


segmentsPainter 没有这个问题，因为是矢量形式。要彻底解决画图片的问题，必须对 canvas 做深度介入，或者逐像素读取图片，一个点一个点的去画(将图片上每个点在画布上的位置都做一个计算)。

#### 像素点画家：

pixelPainter = (img) => (frame) => (ctx) => {
const m = frameCoordMap(frame);

const o = originFrame(frame);
const e1 = edge1Frame(frame);
const e2 = edge2Frame(frame);

const frameWidth = Math.round(Math.sqrt(
Math.pow(xCorVect((e1)) - xCorVect(o), 2) +
Math.pow(yCorVect((e1)) - yCorVect(o), 2)
));
const frameHeight = Math.round(Math.sqrt(
Math.pow(xCorVect((e2)) - xCorVect(o), 2) +
Math.pow(yCorVect((e2)) - yCorVect(o), 2)
));

const wStep = 1/frameWidth;
const hStep = 1/frameHeight;

for (let y = 0; y <= 1; y += hStep) {
for (let x = 0; x <= 1; x += wStep) {
const logicalPixel = makeVect(x, y);
const transformedPixel = m(logicalPixel);

const frameX = xCorVect(transformedPixel);
const frameY = yCorVect(transformedPixel);

const imageX = x * img.naturalWidth;
const imageY = y * img.naturalHeight;

ctx.drawImage(img,
imageX, imageY, 1, 1,
frameX, frameY, 1, 1);
}
}
};

applyToCanvas(pixelPainter(document.getElementById('jeff-tian'))(f), '#pixel-canvas', false);


#### 对像素点画家应用 squareLimit 效果

1、性能不够好；

2、清晰度不高。


const jeffTianPixel = pixelPainter(document.getElementById('jeff-tian'));

applyToCanvas(squareLimit(jeffTianPixel, 2)(f), '#square-limit-to-pixel', false);


const squareLimit = (painter, n) => {
const combine4 = squareOfFour(flipHoriz, id, rotateLeft180, flipVert);

return combine4(cornerSplit(painter, n));
};

applyToCanvas(squareLimit(flipHoriz(wavePainter), 4)(f), '#square-of-four-2-canvas');

console.log('done');