1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
|
//############################################################################
// Programmation IV - 2002 - Week 06
//############################################################################
/** Two-dimensional vector. */
class Vector (_x: Double, _y: Double) {
def x: Double = _x;
def y: Double = _y;
def +(that: Vector): Vector = new Vector(x + that.x, y + that.y);
def *(scalar: Double): Vector = new Vector(x * scalar, y * scalar);
def -(that: Vector): Vector = new Vector(x - that.x, y - that.y);
def /(scalar: Double): Vector = new Vector(x / scalar, y / scalar);
def norm: Double = Math.sqrt(x * x + y * y);
}
//############################################################################
/** Frame. */
class Frame (_origin: Vector, _edgeX: Vector, _edgeY: Vector) {
def origin: Vector = _origin;
def edgeX: Vector = _edgeX;
def edgeY: Vector = _edgeY;
/** The vector v in the absolute (drawing) coordinate system */
def coordMap(v: Vector): Vector = origin + (edgeX * v.x) + (edgeY * v.y);
}
//############################################################################
/** Space on which we can draw lines. */
abstract class Graphics(_width: Double, _height: Double) {
/** Width of the picture.*/
def width: Double = _width;
/** Height of the picture.*/
def height: Double = _height;
/** Frame that represents the drawable area of the output device*/
val frame: Frame;
/** Draw a line in device coordinates*/
def plotLine(x1: Double, y1: Double, x2: Double, y2: Double): Unit;
/** Draw a line in logical coordinates*/
def drawLine(v1: Vector, v2: Vector): Unit = {
val _v1 = frame.coordMap(v1);
val _v2 = frame.coordMap(v2);
plotLine(_v1.x, _v1.y, _v2.x, _v2.y);
}
/** Draw a segment of the picture.*/
def drawSegment(frm: Frame)(v1: Vector, v2: Vector): Unit = {
val _v1 = frm.coordMap(v1);
val _v2 = frm.coordMap(v2);
drawLine(_v1, _v2);
}
/** Draw a list of segments on the picture.*/
def drawSegments(frm: Frame)(segments: List[Tuple2[Vector, Vector]]): Unit =
if (segments.isEmpty) ()
else {
drawSegment(frm)(segments.head._1, segments.head._2);
drawSegments(frm)(segments.tail)
}
/** Draw a list of continuous segments on the picture.*/
def drawPolySegment(frm: Frame)(points: List[Vector]) : Unit =
if (!points.tail.isEmpty) {
drawSegment(frm)(points.head, points.tail.head);
drawPolySegment(frm)(points.tail);
}
/** updates the contents of the output device*/
def repaint = ();
/** Add the last touch to the picture.*/
def close : Unit;
}
//############################################################################
/** Provides PostScript output. The name of the file is the first parameter
* of the constructor. The width and height determine the aspect ratio
*/
class PostScript (filename: String, _width: Double, _height: Double)
extends Graphics(_width, _height) {
/** Convert mm into 72th of inch.*/
def mm2ps(x: Double) : Double = round(x * 72.0 / 25.4);
def round(x: Double): Double =
Math.floor(x * 100.0 + 0.5) / 100.0;
def scaleAndCenter(frm: Frame, ratio:Double): Frame = {
val currentRatio = frm.edgeX.norm / frm.edgeY.norm;
if (currentRatio < ratio) {
val newEdgeX = frm.edgeX;
val newEdgeY = frm.edgeY * (currentRatio /ratio);
val newOrigin = frm.origin + ((frm.edgeY - newEdgeY) / 2);
new Frame(newOrigin, newEdgeX, newEdgeY)
}
else {
val newEdgeX = frm.edgeX * (ratio / currentRatio);
val newEdgeY = frm.edgeY;
val newOrigin = frm.origin + ((frm.edgeX - newEdgeX) / 2);
new Frame(newOrigin, newEdgeX, newEdgeY)
}
}
/** Line thickness in millimeters.*/
val line_thickness : Double = 0.05;
/** Width, height, left and right margins in mm.*/
val psWidth: Double = 210.0;
val psHeight: Double = 297.0;
val psWidthMargin: Double = 15.0;
val psHeightMargin: Double = 15.0;
val frame: Frame = {
val origin = new Vector(mm2ps(psWidthMargin), mm2ps(psHeightMargin));
val edgeX = new Vector(mm2ps(psWidth) - 2 * mm2ps(psWidthMargin), 0);
val edgeY = new Vector(0, mm2ps(psHeight) - 2 * mm2ps(psHeightMargin));
scaleAndCenter(new Frame(origin, edgeX, edgeY), width / height)
}
def plotLine(x1: Double, y1: Double, x2: Double, y2: Double): Unit = {
Console.println(round(x1) + " " + round(y1) + " m " +
round(x2) + " " + round(y2) + " l");
}
/** Print the PS header.*/
Console.println("%!PS-Adobe-3.0 EPSF-3.0\n%%Title: ProgrammationIV");
Console.println("%%Creator: LAMP");
Console.println("%%BoundingBox: 0 0 " + mm2ps(psWidth) + " " + mm2ps(psHeight));
Console.println("%%EndComments\n");
Console.println("/m {moveto} bind def\n/l {lineto} bind def\n");
Console.println(mm2ps(line_thickness) + " setlinewidth\nnewpath");
/** Terminate the PS document and close the file stream. */
def close : Unit = {
Console.println("stroke\nshowpage\n%%EOF");
Console.flush;
}
}
//############################################################################
object M0 {
/** Define the type of a painter as a function that takes a frame,
* draws itself onto it and returns nothing
*/
type Painter = (Frame) => Unit;
/** Transform the frame in which the painter is to be drawn, hence
* changing the appearance of the painter
*/
def transformPainter(origin: Vector, newX: Vector, newY: Vector)(painter: Painter): Painter = {
frame: Frame => {
val newOrigin = frame.coordMap(origin);
val newFrame = new Frame(newOrigin,
frame.coordMap(newX) - newOrigin,
frame.coordMap(newY) - newOrigin);
painter(newFrame)
}
}
/** Flip the painter vertically
*/
def flipVert: Painter => Painter =
transformPainter(new Vector(0.0, 1.0),
new Vector(1.0, 1.0),
new Vector(0.0, 0.0));
/** Flip the painter horizontally
*/
def flipHoriz: Painter => Painter =
transformPainter(new Vector(1.0, 0.0),
new Vector(0.0, 0.0),
new Vector(1.0, 1.0));
/** Compose a painter that draws p1 on the left of p2
*/
def beside(p1: Painter, p2: Painter) : Painter = {
frame: Frame => {
transformPainter(new Vector(0.0, 0.0),
new Vector(0.5, 0.0),
new Vector(0.0, 1.0))(p1)(frame);
transformPainter(new Vector(0.5, 0.0),
new Vector(1.0, 0.0),
new Vector(0.5, 1.0))(p2)(frame)
}
}
/** Compose a painter that draws p1 below p2
*/
def below(p1: Painter, p2: Painter): Painter = {
frame: Frame => {
transformPainter(new Vector(0.0, 0.0),
new Vector(1.0, 0.0),
new Vector(0.0, 0.5))(p1)(frame);
transformPainter(new Vector(0.0, 0.5),
new Vector(1.0, 0.5),
new Vector(0.0, 1.0))(p2)(frame)
}
}
def rightSplit(painter: Painter, n: Int): Painter = {
if (n == 0) painter
else {
val smaller = rightSplit(painter, n-1);
beside(painter, below(smaller, smaller))
}
}
// A small test painter.
def house(canvas: Graphics)(frame: Frame): Unit = {
canvas.drawPolySegment(frame)(List(new Vector(0.0, 0.0),
new Vector(1.0, 0.0),
new Vector(1.0, 2.0/3.0),
new Vector(0.0, 2.0/3.0),
new Vector(0.5, 1.0),
new Vector(1.0, 2.0/3.0),
new Vector(0.0, 0.0),
new Vector(0.0, 2.0/3.0),
new Vector(1.0, 0.0)));
canvas.repaint
}
def test = {
val psfile = "-";
val canvas: Graphics = new PostScript(psfile, 2, 2);
// the identity frame
val identFrame = new Frame(new Vector(0.0,0.0),
new Vector(1.0,0.0),
new Vector(0.0,1.0));
// Create a basic painter...
val p: Painter = house(canvas);
// ...then compose it with itself.
val threeHouses = beside(p, beside(p, flipVert(p)));
// Use the painter to draw the final image.
threeHouses(identFrame);
// Don't forget to close the canvas!
canvas.close
}
}
//############################################################################
object Test {
def main(args: Array[String]): Unit = {
M0.test;
()
}
}
//############################################################################
|