kapstok / NHL-DePa2 (public) (License: Unspecified) (since 2018-11-09) (hash sha1)
Design Patterns school project; a GUI using multiple Design Patterns.
List of commits:
Subject Hash Author Date (UTC)
Use different strats instead of seperate Shapes. a56e1e665a0292d15139eddfd8ebdeb8c95251b4 Jan Allersma 2018-10-01 11:37:45
Fix bug in Rect from commit 6c91140 in other Shapes as well. 4165a5da98dd9a548a222c6160390e4e54026d48 Jan Allersma 2018-09-28 14:53:40
Fully implement File I/O. 45cbb48489a24857cfc76144b38b12ccebfd9e61 Jan Allersma 2018-09-27 16:03:40
Fix GroupMenu bug. be41e51819be1a23e7761ea0662eb188ac641aca Jan Allersma 2018-09-27 14:55:20
Fix TODO's from previous commit. 89d8172b77336a6c18f30521147eb81287961f11 Jan Allersma 2018-09-27 14:06:30
Add groups as preparation for step 3. a91368128e7444f69b3a30ee4d17f9063c8846b8 Jan Allersma 2018-09-26 18:31:49
Fix minor bug. Small cleanup 'ellipse' code. 6c911405eb8e48ffe2d63aaff5255adbd4aec889 Jan Allersma 2018-09-21 18:10:01
Implement file I/O. 6219eca932d454a7dab5fe8679f07865ce3cf49d Jan Allersma 2018-09-21 15:52:45
Add shape 'ellipse'. 99d8b029c769bc291af73df3fd445da3cbf5bef4 Jan Allersma 2018-09-21 10:44:36
Add shape 'circle'. 092cb3c3f788b461192a6e164955c53acaf58720 Jan Allersma 2018-09-21 09:35:27
Add resize option (again). 08c95e97268cf9d74bcb963503eec512e528edfe Jan Allersma 2018-09-20 18:09:03
Fix undo bug. 6ab126be633075923cdcce53044e2468f967db41 Jan Allersma 2018-09-20 10:46:50
Implement 'delete shape' feature. 16d42472d44a9dae988c4745c90c791a8770541b Jan Allersma 2018-09-19 09:04:40
Fix undo/redo for 'MoveCmd'. be51bea021bfe5ff79e16a92d350677aed736ddd Jan Allersma 2018-09-19 08:49:55
Implement shapeOptions without undo/redo. bbe699f41e5dacbfdadfaa879452ac2f4501f5a1 Jan Allersma 2018-09-18 19:44:44
WIP: Restore 'shapeOptions.d'. 888b8ea647c0f25cfbeefcd97ee7ebb59e7f0751 Jan Allersma 2018-09-18 15:46:05
Rebuild project from scratch. df0f2f82a86581ba9fa3a169d63a950229341a9f Jan Allersma 2018-09-18 15:04:32
Add resize option. 961e464090918d83690ff66838f2fc96c6ad213a Jan Allersma 2018-09-13 18:29:44
Make `MoveCmd` Command-oriented. 2970a8d862285752551be2464422375ca958357f Jan Allersma 2018-09-12 15:33:17
Change of strategy: Make project command-oriented. ce40dffb1675661922411eeb07f148f61a176c22 Jan Allersma 2018-09-12 11:35:45
Commit a56e1e665a0292d15139eddfd8ebdeb8c95251b4 - Use different strats instead of seperate Shapes.
- Implemented singleton and strategy pattern to achieve this.

- The code could use some optimizations.
Author: Jan Allersma
Author date (UTC): 2018-10-01 11:37
Committer name: Jan Allersma
Committer date (UTC): 2018-10-16 13:42
Parent(s): 4165a5da98dd9a548a222c6160390e4e54026d48
Signing key:
Tree: 62a624b39333a133c5fe156f282349319cc47e35
File Lines added Lines deleted
README.md 2 0
source/canvas.d 10 7
source/entities/circle.d 0 65
source/entities/ellipse.d 0 67
source/entities/rect.d 0 63
source/entities/shape.d 80 22
source/file.d 10 5
source/frontend/menubar.d 0 1
source/strategies/circle.d 48 0
source/strategies/ellipse.d 52 0
source/strategies/rect.d 47 0
source/strategies/strategy.d 32 0
File README.md changed (mode: 100644) (index 45e0812..cda57e6)
... ... use 'dub' to compile and run the code.
30 30 - Grafische indicatie van een groep. De groepen worden wel goed weergegeven, - Grafische indicatie van een groep. De groepen worden wel goed weergegeven,
31 31 maar subgroepen niet. maar subgroepen niet.
32 32
33 - Groups worden nog niet goed geladen: ze verliezen hun kleurtje.
34
33 35 # Vragen # Vragen
34 36
35 37 - Is het Visitor pattern niet al geimplementeerd met Command als Visitor? - Is het Visitor pattern niet al geimplementeerd met Command als Visitor?
File source/canvas.d changed (mode: 100644) (index dd56a96..b7e5700)
... ... import cairo.Context;
6 6 import cairo.ImageSurface; import cairo.ImageSurface;
7 7
8 8 import dp.ent.shape; import dp.ent.shape;
9 import dp.ent.rect, dp.ent.circle, dp.ent.ellipse;
9 import dp.strat.rect;
10 import dp.strat.circle;
11 import dp.strat.ellipse;
10 12 import dp.command.create, dp.command.move; import dp.command.create, dp.command.move;
11 13
12 14 import std.stdio; // For debug import std.stdio; // For debug
 
... ... public class Canvas : DrawingArea {
59 61 Shape s; Shape s;
60 62 final switch (Global.Brush.shape) { final switch (Global.Brush.shape) {
61 63 case "rectangle": case "rectangle":
62 s = new Rectangle(mouseX, mouseY, newContext);
64 s = new Shape(mouseX, mouseY, newContext, rect);
63 65 break; break;
64 66 case "circle": case "circle":
65 s = new Circle(mouseX, mouseY, newContext);
67 s = new Shape(mouseX, mouseY, newContext, circle);
66 68 break; break;
67 69 case "ellipse": case "ellipse":
68 s = new Ellipse(mouseX, mouseY, newContext);
70 s = new Shape(mouseX, mouseY, newContext, ellipse);
69 71 break; break;
70 72 } }
71 73 Global.History.addCommand(new CreateCmd(s)); Global.History.addCommand(new CreateCmd(s));
 
... ... public class Canvas : DrawingArea {
77 79 else if(Global.Brush.shape !is null) { else if(Global.Brush.shape !is null) {
78 80 final switch (Global.Brush.shape) { final switch (Global.Brush.shape) {
79 81 case "rectangle": case "rectangle":
80 Global.History.addCommand(new CreateCmd(new Rectangle(mouseX, mouseY, newContext)));
82 writeln("Canvas instantiating rect..");
83 Global.History.addCommand(new CreateCmd(new Shape(mouseX, mouseY, newContext, rect)));
81 84 break; break;
82 85 case "circle": case "circle":
83 Global.History.addCommand(new CreateCmd(new Circle(mouseX, mouseY, newContext)));
86 Global.History.addCommand(new CreateCmd(new Shape(mouseX, mouseY, newContext, circle)));
84 87 break; break;
85 88 case "ellipse": case "ellipse":
86 Global.History.addCommand(new CreateCmd(new Ellipse(mouseX, mouseY, newContext)));
89 Global.History.addCommand(new CreateCmd(new Shape(mouseX, mouseY, newContext, ellipse)));
87 90 break; break;
88 91 } }
89 92 Global.Brush.shape = null; Global.Brush.shape = null;
File source/entities/circle.d deleted (index 90afccb..0000000)
1 module dp.ent.circle;
2
3 import cairo.Context;
4 import dp.ent.shape;
5 import std.conv;
6
7 import Global = dp.global;
8
9 immutable float pi = 3.141;
10
11 class Circle : Shape {
12
13 this(int x, int y, Context context, double size = 0) {
14 super(x, y, context, size);
15 }
16
17 @property
18 public override string type() {
19 return "circle";
20 }
21
22 @property
23 public override int[2] position() {
24 return [
25 to!int(bounds[0][0] + size),
26 to!int(bounds[0][1] + size)
27 ];
28 }
29
30 public override void resize(int amount) {
31 int[2] newPos;
32
33 // 'size' can become negative number due to 'amount' being a
34 // bigger negative number than 'size' being a positive number.
35 if(size + amount <= 0)
36 return;
37
38 newPos = [
39 to!int(bounds[0][0] + size),
40 to!int(bounds[0][1] + size)
41 ];
42
43 size += amount;
44 this.bounds = calcBounds(newPos[0], newPos[1]);
45 }
46
47 protected override void initSize(double size) {
48 this.size = size != 0 ? size : 50;
49 }
50
51 protected override double[2][2] calcBounds(int x, int y) {
52 double[2][2] result;
53
54 result[0][0] = x - size; // minX
55 result[1][0] = x + size; // maxX
56 result[0][1] = y - size; // minY
57 result[1][1] = y + size; // maxY
58
59 return result;
60 }
61
62 protected override void draw() {
63 c.arc(bounds[0][0] + size, bounds[0][1] + size, size, 0, 2 * pi);
64 }
65 }
File source/entities/ellipse.d deleted (index b69e6a4..0000000)
1 module dp.ent.ellipse;
2
3 import cairo.Context;
4 import dp.ent.shape;
5 import std.conv;
6
7 import Global = dp.global;
8
9 immutable float pi = 3.141;
10
11 class Ellipse : Shape {
12
13 this(int x, int y, Context context, double size = 0) {
14 super(x, y, context, size);
15 }
16
17 @property
18 public override string type() {
19 return "ellipse";
20 }
21
22 @property
23 public override int[2] position() {
24 return [
25 to!int(bounds[0][0] + size / 2),
26 to!int(bounds[0][1] + size)
27 ];
28 }
29
30 public override void resize(int amount) {
31 int[2] newPos;
32
33 // 'size' can become negative number due to 'amount' being a
34 // bigger negative number than 'size' being a positive number.
35 if(size + amount <= 0)
36 return;
37
38 newPos = [
39 to!int(bounds[0][0] + size / 2),
40 to!int(bounds[0][1] + size)
41 ];
42
43 size += amount;
44 this.bounds = calcBounds(newPos[0], newPos[1]);
45 }
46
47 protected override void initSize(double size) {
48 this.size = size != 0 ? size : 50;
49 }
50
51 protected override double[2][2] calcBounds(int x, int y) {
52 double[2][2] result;
53
54 result[0][0] = x - size / 2; // minX
55 result[1][0] = x + size / 2; // maxX
56 result[0][1] = y - size; // minY
57 result[1][1] = y + size; // maxY
58
59 return result;
60 }
61
62 protected override void draw() {
63 c.scale(0.5, 1);
64 c.arc(bounds[0][0] * 2 + size, bounds[0][1] + size, size, 0, 2 * pi);
65 c.scale(2, 1);
66 }
67 }
File source/entities/rect.d deleted (index bed8e8d..0000000)
1 module dp.ent.rect;
2
3 import cairo.Context;
4 import dp.ent.shape;
5 import std.conv;
6
7 import Global = dp.global;
8
9 class Rectangle : Shape {
10
11 this(int x, int y, Context context, double size = 0) {
12 super(x, y, context, size);
13 }
14
15 @property
16 public override string type() {
17 return "rectangle";
18 }
19
20 @property
21 public override int[2] position() {
22 return [
23 to!int(bounds[0][0] + size / 2),
24 to!int(bounds[0][1] + size / 2)
25 ];
26 }
27
28 public override void resize(int amount) {
29 int[2] newPos;
30
31 // 'size' can become negative number due to 'amount' being a
32 // bigger negative number than 'size' being a positive number.
33 if(size + amount <= 0)
34 return;
35
36 newPos = [
37 to!int(bounds[0][0] + size / 2),
38 to!int(bounds[0][1] + size / 2)
39 ];
40
41 size += amount;
42 this.bounds = calcBounds(newPos[0], newPos[1]);
43 }
44
45 protected override void initSize(double size) {
46 this.size = size != 0 ? size : 125;
47 }
48
49 protected override double[2][2] calcBounds(int x, int y) {
50 double[2][2] result;
51
52 result[0][0] = x - size / 2; // minX
53 result[1][0] = x + size / 2; // maxX
54 result[0][1] = y - size / 2; // minY
55 result[1][1] = y + size / 2; // maxY
56
57 return result;
58 }
59
60 protected override void draw() {
61 c.rectangle(bounds[0][0], bounds[0][1], size, size);
62 }
63 }
File source/entities/shape.d changed (mode: 100644) (index 19ce45c..cd6c655)
1 1 module dp.ent.shape; module dp.ent.shape;
2 2
3 import dp.ent.entity;
4 import dp.strat.strat;
3 5 import cairo.Context; import cairo.Context;
4 6 import std.conv; import std.conv;
5 import dp.ent.entity;
6
7 7 import std.stdio; // Debug import std.stdio; // Debug
8 8
9 9 import Global = dp.global; import Global = dp.global;
 
... ... class Shape : Entity {
16 16 bounds[1][1] = maxY bounds[1][1] = maxY
17 17 +/ +/
18 18
19 public double size;
20 protected Context c;
21 protected double[2][2] bounds;
19 public Context c;
20 private double size;
21 private double[2][2] bounds;
22 private Strategy strat;
22 23
23 this(int x, int y, Context context, double size = 0) {
24 this(int x, int y, Context context, Strategy strategy, double size = 0) {
25 writeln("Entered constructor of shape.");
24 26 c = context; c = context;
25 initSize(size);
26 27 active = true; active = true;
28 this.strat = strategy;
29 this.size = size != 0 ? size : strat.initialSize;
27 30 this.bounds = calcBounds(x,y); this.bounds = calcBounds(x,y);
28 render();
31 writeln("Done! " ~ to!string(getBounds) ~ to!string(getSize));
29 32 } }
30 33
31 34 @property @property
32 public override string to_string() {
33 string result = type ~ " ";
34 result ~= to!string(position[0]) ~ " ";
35 result ~= to!string(position[1]) ~ " ";
36 result ~= to!string(size) ~ "\n";
35 public override string type() {
36 return strat.type;
37 }
37 38
38 return result;
39 @property
40 public override string to_string() {
41 return strat.to_string(this);
39 42 } }
40 43
41 44 // Returns center of shape. // Returns center of shape.
42 45 @property @property
43 public abstract int[2] position();
46 public int[2] position() {
47 return [
48 to!int(bounds[0][0] + sizeX),
49 to!int(bounds[0][1] + sizeY)
50 ];
51 }
44 52
45 public abstract void resize(int amount);
53 @property
54 public double[2][2] getBounds() {
55 return bounds;
56 }
46 57
47 protected abstract double[2][2] calcBounds(int x, int y);
48 protected abstract void initSize(double size);
49 protected abstract void draw();
58 @property
59 public double getSize() {
60 return size;
61 }
50 62
51 public void move(int x, int y) {
52 this.bounds = calcBounds(x,y);
63 @property
64 private double sizeX() {
65 return size * strat.sizeXScale;
66 }
67
68 @property
69 private double sizeY() {
70 return size * strat.sizeYScale;
53 71 } }
54 72
55 73 public override void checkBounds(int x, int y) { public override void checkBounds(int x, int y) {
 
... ... class Shape : Entity {
71 89 Global.Brush.red, Global.Brush.green, Global.Brush.red, Global.Brush.green,
72 90 Global.Brush.blue, Global.Brush.alpha Global.Brush.blue, Global.Brush.alpha
73 91 ); );
74 draw();
92 writeln("Rendering..");
93 strat.draw(this);
75 94 c.fill(); c.fill();
95 writeln("Done!");
76 96 } }
77 97 } }
98
99 public void move(int x, int y) {
100 this.bounds = calcBounds(x,y);
101 }
102
103 public void resize(int amount) {
104 int[2] newPos;
105
106 /+
107 'size' can become negative number due to 'amount' being a
108 bigger negative number than 'size' being a positive number.
109
110 Size could be divided by 2 in some cases, depending on it's
111 shape. Therefore, the size will be checked in it's smallest
112 possible size: this.size divided by 2.
113 +/
114 if(size / 2 + amount <= 0)
115 return;
116
117 newPos = [
118 to!int(bounds[0][0] + sizeX),
119 to!int(bounds[0][1] + sizeY)
120 ];
121
122 size += amount;
123 this.bounds = calcBounds(newPos[0], newPos[1]);
124 }
125
126 protected double[2][2] calcBounds(int x, int y) {
127 double[2][2] result;
128
129 result[0][0] = x - sizeX; // minX
130 result[1][0] = x + sizeX; // maxX
131 result[0][1] = y - sizeY; // minY
132 result[1][1] = y + sizeY; // maxY
133
134 return result;
135 }
78 136 } }
File source/file.d changed (mode: 100644) (index a7d8a60..6994ac5)
... ... import std.conv;
6 6 import std.array; import std.array;
7 7 import std.string; import std.string;
8 8
9 import dp.ent.entity, dp.ent.shape;
10 import dp.ent.rect, dp.ent.circle, dp.ent.ellipse, dp.ent.group;
9 import dp.ent.entity, dp.ent.group, dp.ent.shape;
10 import dp.strat.rect;
11 import dp.strat.circle;
12 import dp.strat.ellipse;
11 13 import dp.command.load; import dp.command.load;
12 14
13 15 import Global = dp.global; import Global = dp.global;
 
... ... public class Savefile {
59 61
60 62 final switch(words[0]) { final switch(words[0]) {
61 63 case "rectangle": case "rectangle":
62 return new Rectangle (
64 return new Shape (
63 65 to!int(words[1]), to!int(words[1]),
64 66 to!int(words[2]), to!int(words[2]),
65 67 Global.canvas.newContext, Global.canvas.newContext,
68 rect,
66 69 to!double(words[3]) to!double(words[3])
67 70 ); );
68 71 case "circle": case "circle":
69 return new Circle (
72 return new Shape (
70 73 to!int(words[1]), to!int(words[1]),
71 74 to!int(words[2]), to!int(words[2]),
72 75 Global.canvas.newContext, Global.canvas.newContext,
76 circle,
73 77 to!double(words[3]) to!double(words[3])
74 78 ); );
75 79 case "ellipse": case "ellipse":
76 return new Ellipse (
80 return new Shape (
77 81 to!int(words[1]), to!int(words[1]),
78 82 to!int(words[2]), to!int(words[2]),
79 83 Global.canvas.newContext, Global.canvas.newContext,
84 ellipse,
80 85 to!double(words[3]) to!double(words[3])
81 86 ); );
82 87 } }
File source/frontend/menubar.d changed (mode: 100644) (index 4166939..c3dc6c6)
... ... import gtk.MenuBar, gtk.Menu, gtk.MenuItem;
5 5 import gtk.Widget; import gtk.Widget;
6 6 import gdk.Event; import gdk.Event;
7 7
8 import dp.ent.rect;
9 8 import dp.ent.group; import dp.ent.group;
10 9 import dp.command.load; import dp.command.load;
11 10 import dp.command.create; import dp.command.create;
File source/strategies/circle.d added (mode: 100644) (index 0000000..19ff8f3)
1 module dp.strat.circle;
2
3 import dp.strat.strat;
4
5 private static CircleDrawer _circle;
6
7 public static CircleDrawer circle() {
8 if(_circle is null) {
9 synchronized if(_circle is null) {
10 _circle = new CircleDrawer;
11 }
12 }
13 return _circle;
14 }
15
16 private static class CircleDrawer : Strategy {
17 immutable float pi = 3.141;
18
19 @property
20 public override string type() {
21 return "circle";
22 }
23
24 @property
25 public override double sizeXScale() {
26 return sizeYScale;
27 }
28
29 @property
30 public override double sizeYScale() {
31 return 1;
32 }
33
34 @property
35 public override int initialSize() {
36 return 50;
37 }
38
39 public override void draw(Shape shape) {
40 shape.c.arc (
41 shape.getBounds[0][0] + shape.getSize,
42 shape.getBounds[0][1] + shape.getSize,
43 shape.getSize,
44 0,
45 2 * pi
46 );
47 }
48 }
File source/strategies/ellipse.d added (mode: 100644) (index 0000000..1f0ae3f)
1 module dp.strat.ellipse;
2
3 import dp.strat.strat;
4
5 private static EllipseDrawer _ellipse;
6
7 public static EllipseDrawer ellipse() {
8 if(_ellipse is null) {
9 synchronized if(_ellipse is null) {
10 _ellipse = new EllipseDrawer;
11 }
12 }
13 return _ellipse;
14 }
15
16 private static class EllipseDrawer : Strategy {
17 immutable float pi = 3.141;
18
19 @property
20 public override string type() {
21 return "ellipse";
22 }
23
24 @property
25 public override double sizeXScale() {
26 return 0.5;
27 }
28
29 @property
30 public override double sizeYScale() {
31 return 1;
32 }
33
34 @property
35 public override int initialSize() {
36 return 50;
37 }
38
39 public override void draw(Shape shape) {
40 shape.c.scale(0.5, 1);
41
42 shape.c.arc (
43 shape.getBounds[0][0] * 2 + shape.getSize,
44 shape.getBounds[0][1] + shape.getSize,
45 shape.getSize,
46 0,
47 2 * pi
48 );
49
50 shape.c.scale(2, 1);
51 }
52 }
File source/strategies/rect.d added (mode: 100644) (index 0000000..d84c9b0)
1 module dp.strat.rect;
2
3 import dp.strat.strat;
4 import std.stdio; // Debug
5
6 private static RectDrawer _rect;
7
8 public static RectDrawer rect() {
9 if(_rect is null) {
10 synchronized if(_rect is null) {
11 _rect = new RectDrawer();
12 }
13 }
14 return _rect;
15 }
16
17 private static class RectDrawer : Strategy {
18 @property
19 public override string type() {
20 return "rectangle";
21 }
22
23 @property
24 public override double sizeXScale() {
25 return sizeYScale;
26 }
27
28 @property
29 public override double sizeYScale() {
30 return 0.5;
31 }
32
33 @property
34 public override int initialSize() {
35 return 125;
36 }
37
38 public override void draw(Shape shape) {
39 writeln("Drawing..");
40 shape.c.rectangle (
41 shape.getBounds[0][0],
42 shape.getBounds[0][1],
43 shape.getSize,
44 shape.getSize
45 );
46 }
47 }
File source/strategies/strategy.d added (mode: 100644) (index 0000000..ce753a5)
1 module dp.strat.strat;
2
3 public import dp.ent.shape;
4
5 import std.conv;
6
7 // Used to implement strategy pattern.
8
9 abstract class Strategy {
10 @property
11 public abstract string type();
12
13 public abstract void draw(Shape shape);
14
15 @property
16 public abstract double sizeXScale();
17
18 @property
19 public abstract double sizeYScale();
20
21 @property
22 public abstract int initialSize();
23
24 public string to_string(Shape shape) {
25 string result = type ~ " ";
26 result ~= to!string(shape.position[0]) ~ " ";
27 result ~= to!string(shape.position[1]) ~ " ";
28 result ~= to!string(shape.getSize) ~ "\n";
29
30 return result;
31 }
32 }
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/kapstok/NHL-DePa2

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/kapstok/NHL-DePa2

Clone this repository using git:
git clone git://git.rocketgit.com/user/kapstok/NHL-DePa2

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main