/***********************************************************************\ * triangles3.d * * * * Triangle Challenge * * * * GUI version * * * * by Stewart Gordon * * October 2004 * \***********************************************************************/ import smjg.libs.sdwf.application; import smjg.libs.sdwf.gdiobject; import smjg.libs.sdwf.geometry; import smjg.libs.sdwf.window; import smjg.libs.sdwf.dialog; import smjg.libs.sdwf.control; import std.string; import std.conv; extern (C) void gc_init(); extern (C) void gc_term(); extern (C) void _minit(); extern (C) void _moduleCtor(); extern (C) void _moduleUnitTests(); extern (Windows) int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int result; gc_init(); _minit(); try { _moduleCtor(); _moduleUnitTests(); return doit(); } catch (Object o) { MessageBoxA(null, toStringz(o.toString()), "Fatal Internal Error", MB_OK | MB_ICONEXCLAMATION); return 0; } finally { gc_term(); } } int doit() { Application a = new Application; a.mainWindow = new TriangleWindow(a); return a.run(); } TriangleModel model; enum { SCALE = 5000, BORDER = 1000 } class TriangleWindow : Window { Navigator nav; Brush currentPolygonBrush, nullBrush; Pen nullPen; this(Application a) { super("Triangles", a); currentPolygonBrush = new Brush(RGBColour.CYAN); nullBrush = new Brush(BRUSH.NULL); nullPen = new Pen(PEN.NULL); } override void setup() { nav = new Navigator(this); model = new TriangleModel; nav.show(); } override void setWindowScale() { auto WindowDC dc = new WindowDC(this); dc.mappingMode = MM.ANISOTROPIC; dc.setScale(clientArea, Rect(-SCALE - BORDER, SCALE + BORDER, SCALE + BORDER, -BORDER)); } Point coords(int axis1, int axis2) { int div = axis1 * axis2 - axis1 * model.side2 - axis2 * model.side1; Point coord; coord.x = SCALE * (axis1 * model.side2 - axis2 * model.side1) / div; coord.y = -SCALE * axis1 * axis2 / div; return coord; } override void paint(PaintDC dc, PAINTSTRUCT ps) { // fill in the current triangle Triangle piece = model.triangles[nav.nowShowing]; dc.save(); dc.select(currentPolygonBrush); dc.select(nullPen); if (piece.lower1 == 0 && piece.lower2 == 0) { Point[3] currentVertices; currentVertices[0] = Point(-SCALE, 0); currentVertices[1] = Point(SCALE, 0); currentVertices[2] = coords(piece.upper1, piece.upper2); dc.polygon(currentVertices); } else { Point[4] currentVertices; currentVertices[0] = coords(piece.lower1, piece.lower2); currentVertices[1] = coords(piece.upper1, piece.lower2); currentVertices[2] = coords(piece.upper1, piece.upper2); currentVertices[3] = coords(piece.lower1, piece.upper2); dc.polygon(currentVertices); } // draw the whole triangle const Point[3] whole = [ { 0, SCALE }, { -SCALE, 0 }, { SCALE, 0 } ]; dc.restore(); dc.save(); dc.select(nullBrush); dc.polygon(whole); // draw the lines for (int line = 1; line < model.side1; line++) { int coord = SCALE * line / model.side1; dc.moveTo(-SCALE, 0); dc.lineTo(SCALE - coord, coord); } for (int line = 1; line < model.side2; line++) { int coord = SCALE * line / model.side2; dc.moveTo(SCALE, 0); dc.lineTo(coord - SCALE, coord); } dc.restore(); } } class Navigator : Dialog { EditBox leftBox; EditBox rightBox; Control nowShowingDisp; int nowShowing = 0; this(TriangleWindow w) { super(1, w, false); notifyHandler[103] = &previous; notifyHandler[104] = &next; } override void close(int exitCode) { parent.close(); } override void setup() { leftBox = new EditBox(this, 101); rightBox = new EditBox(this, 102); nowShowingDisp = new Control(this, 105); } int previous(uint notify, uint id, void* info) { if (leftBox.modified() || rightBox.modified()) { updateModel(); nowShowing = model.triangles.length - 1; } else if (--nowShowing < 0) { nowShowing = model.triangles.length - 1; } updateDisplay(); return 0; } int next(uint notify, uint id, void* info) { if (leftBox.modified() || rightBox.modified()) { updateModel(); nowShowing = 0; } else if (++nowShowing >= model.triangles.length) { nowShowing = 0; } updateDisplay(); return 0; } void updateModel() { try { model.update(toInt(leftBox.text), toUint(rightBox.text)); leftBox.modified = rightBox.modified = false; } catch (ConvError e) { messageBox("Please enter a positive integer", "Error", MB.ICONEXCLAMATION | MB.OK); } } void updateDisplay() { nowShowingDisp.text = format("Now showing %d of %d", nowShowing + 1, model.triangles.length); (cast(Window) parent).repaint(true); } } struct Triangle { int lower1, upper1, lower2, upper2; } class TriangleModel { int side1; int side2; Triangle[] triangles; this() { update(3, 3); } void update(int s1, int s2) out { assert (triangles.length == s1 * s2 * (s1 + s2) / 2); } body { side1 = s1; side2 = s2; triangles = null; // left-vertex triangles for (int lowerEdge = 1; lowerEdge < s1; lowerEdge++) { for (int upperEdge = lowerEdge + 1; upperEdge <= s1; upperEdge++) { for (int len = 1; len <= s2; len++) { addTriangle(lowerEdge, upperEdge, 0, len); } } } // right-vertex triangles for (int lowerEdge = 1; lowerEdge < s2; lowerEdge++) { for (int upperEdge = lowerEdge + 1; upperEdge <= s2; upperEdge++) { for (int len = 1; len <= s1; len++) { addTriangle(0, len, lowerEdge, upperEdge); } } } // both-vertex triangles for (int axis1 = 1; axis1 <= s1; axis1++) { for (int axis2 = 1; axis2 <= s2; axis2++) { addTriangle(0, axis1, 0, axis2); } } } void addTriangle(int lower1, int upper1, int lower2, int upper2) { Triangle t; t.lower1 = lower1; t.upper1 = upper1; t.lower2 = lower2; t.upper2 = upper2; triangles ~= t; } }