2.51

练习 2.51 定义对画家的 below 操作,它以两个画家为参数。在给定了一个框架后,由 below 得到的画家将要求第一个画家在框架的下部画图,要求第二个画家在框架的上部画图。请按两种方式定义 below:首先写出一个类似于上面 beside 的过程;另一个则直接通过beside和适当的旋转操作(来自练习2.50)完成有关工作。


为了方便直观地体验,将采用 js 实现。

先复用一下上一练习的代码:

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) => {
        return addVect(
            originFrame(frame),
            addVect(
                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) => {
        segmentList.forEach((segment) => {
            drawLine(
                (frameCoordMap(frame))(startSegment(segment)),
                (frameCoordMap(frame))(endSegment(segment))
            )(ctx);
        });
    }
}

applyToCanvas = (painter, canvasSelector) => {
    const canvas = document.querySelector(canvasSelector);
    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);

    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),
    ])(frame); 
}
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)
    )
}

第一种方式:

const 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);
    }
}

applyToCanvas(below(wavePainter, wavePainter)(f), '#below-1-canvas');

第二种方式

const 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);
    };
};

const below = beside;

applyToCanvas(
    rotateLeft270(
        below(
            rotateLeft270(rotateLeft270(rotateLeft270(wavePainter))), 
            rotateLeft270(rotateLeft270(rotateLeft270(wavePainter)))
        )
    )(f), 
    '#below-2-canvas');
console.log('done');

results matching ""

    No results matching ""