Skip to content

Commit a8c64c9

Browse files
Imad HachimiGitHub Enterprise
authored andcommitted
Merge pull request #1 from IN1910/part2
Part2
2 parents 0f5a9bd + 4464ff7 commit a8c64c9

File tree

8 files changed

+156
-10
lines changed

8 files changed

+156
-10
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ Last project in IN1910
1212

1313

1414
## Notes
15-
15+
Jeg velger å starte med oppgave 2 også løse 1 med den generelle.
16+
2b) Den siste linjen blir ikke plottet, men punktene ser bra ut.
17+
1b) og 2c) Startpunktene har en tendens til å klumpe seg i midten. få punkter langt ute.
18+
2d) vet ikke hvordan strukturere data på bra måte og jeg jobber med immutable tuples
19+
mye bruk av np.asarray() på lister fordi jeg er usikker med å velge dtype manuelt
20+
2f) jeg må velge farger for kantene som kan være mange. jeg bare velger tilfeldige farger.
1621

1722

1823
## Authors

chaos_game.py

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
import numpy as np
22
import matplotlib.pyplot as plt
3+
import pathlib
34

45

56
class ChaosGame:
67
def __init__(self, n: int = 3, r: float = 0.5):
78
"""Need to make sure input is of right type, and 0 < r < 1, n > 2.
8-
This checks first, then sets the attributes after. sets to default if not possible. """
9+
This checks first, then sets the attributes after. sets to default if not possible.
10+
_corners are 2d arrays of floats, so are _points. """
11+
self._n: int
12+
self._r: float
13+
self._solved = False
14+
# not sure how to type corners and points,
15+
# which are lists of tuples then converted to np arrays
16+
self._corners: list
17+
self._points = np.asarray([(0., 0., 0)])
18+
19+
# better way to handle all situations? this looks a bit ugly to me in constructor
920
try:
1021
if not isinstance(n, int):
1122
raise TypeError("n should be an int")
@@ -15,25 +26,132 @@ def __init__(self, n: int = 3, r: float = 0.5):
1526
n = int(n)
1627
if n < -2:
1728
print("n need to be int > 2, converting to positive int")
18-
self.n = self.n * -1
29+
self._n = self._n * -1
1930
elif -2 <= n <= 2:
31+
print("n has to be int > 2")
2032
print("using default n = 3")
21-
self.n = 3
33+
self._n = 3
2234
else:
23-
self.n = n
35+
self._n = n
2436

2537
try:
2638
if not isinstance(r, float):
27-
raise TypeError("r should be a float 0 < r < 1")
39+
raise TypeError("TypeError: r should be a float 0 < r < 1")
2840
except TypeError as terr:
2941
print(terr.args)
3042
print("trying to convert")
3143
r = float(r)
3244
if not (0 < r < 1):
33-
print("using default = 0.5")
34-
self.r = 0.5
45+
print("r should be a float 0 < r < 1")
46+
print("using default r = 0.5")
47+
self._r = 0.5
48+
else:
49+
self._r = r
50+
51+
self._generate_ngon()
52+
53+
def _generate_ngon(self):
54+
"""Generates and saves the corner points of the ngon.
55+
saved as 2d array of floats"""
56+
theta = 2 * np.pi / self._n
57+
corners = [(np.sin(theta), np.cos(theta))] * self._n
58+
for i in range(self._n):
59+
corners[i] = (np.sin(theta * i), np.cos(theta * i))
60+
self._corners = np.asarray(corners)
61+
62+
def plot_ngon(self):
63+
plt.plot(self._corners[:, 0], self._corners[:, 1])
64+
65+
def _starting_point(self):
66+
"""Randomly selects a starting point inside the ngon
67+
:return: python list with 2 elements of float type
68+
"""
69+
spoint = [0, 0]
70+
w = [0.] * self._n
71+
for i in range(self._n):
72+
w[i] = np.random.random()
73+
wsum = sum(w)
74+
for i in range(self._n):
75+
w[i] = w[i] / wsum
76+
spoint[0] += w[i] * self._corners[i][0]
77+
spoint[1] += w[i] * self._corners[i][1]
78+
return spoint
79+
80+
def iterate(self, steps: int = 10, discard: int = 5):
81+
"""Discards the first discard points.
82+
The third element in each tuple is the random chosen corner index
83+
be aware that, currently, index is a float"""
84+
current_point = np.asarray(self._starting_point())
85+
86+
self._points = [(0., 0., 0)] * (steps - discard)
87+
self._points = np.asarray(self._points)
88+
for i in range(discard):
89+
c = self._corners[np.random.randint(0, self._n)]
90+
current_point = self._r * current_point + (1 - self._r) * c
91+
92+
for i in range(steps - discard):
93+
cind = np.random.randint(0, self._n)
94+
current_point = self._r * current_point + (1 - self._r) * self._corners[cind]
95+
self._points[i] = *current_point, cind
96+
self._solved = True
97+
98+
def plot(self, color: bool, cmap: str):
99+
"""Colors is a tuple of the corner indices, when color=True. """
100+
colors = "black"
101+
if color:
102+
colors = self.gradient_color
103+
if self._solved:
104+
plt.scatter(self._points[:, 0], self._points[:, 1], c=colors,
105+
cmap=cmap, s=0.1)
106+
else:
107+
raise Exception("Need to iterate() before plotting")
108+
109+
def show(self, color=False, cmap="rainbow"):
110+
plt.axis("Equal")
111+
plt.axis('off')
112+
self.plot(color, cmap)
113+
plt.show()
114+
115+
def savepng(self, outfile: str, color=False, cmap="rainbow"):
116+
plt.axis("Equal")
117+
plt.axis('off')
118+
self.plot(color, cmap)
119+
plt.savefig(pathlib.Path(__file__).parent.resolve().__str__() + '\\figures\\' + outfile,
120+
dpi=400)
121+
122+
@property
123+
def gradient_color(self):
124+
"""Returns an array with each points rgb colors calculated based on
125+
the selected corner and the previous points color
126+
gc = gradiant colors, cc = corner colors
127+
128+
:return: array of shape (x, 3). x depends on how many iterated points
129+
"""
130+
if self._solved:
131+
colors = iter([plt.cm.tab20(i) for i in range(20)])
132+
cc = []
133+
i = 0
134+
# TODO: this can only create a limited number of colors then repeat
135+
while len(cc) < self._corners.shape[0]:
136+
cc.append(next(colors))
137+
i += 1
138+
if i == 20:
139+
colors = iter([plt.cm.tab20b(i) for i in range(20)])
140+
i = 0
141+
cc = np.delete(np.asarray(cc), 3, 1)
142+
# probably exists better way to initialize gc than converting back to list
143+
gc = np.asarray([cc[int(self._points[0][2])]] * self._points.shape[0])
144+
# i need an arbitrary amount of chosen colors for the corners
145+
for i in range(1, self._points.shape[0]):
146+
gc[i] = (gc[i - 1] + cc[int(self._points[i][2])]) / 2
147+
return gc
35148
else:
36-
self.r = r
149+
raise Exception("Need to iterate() before creating colors")
150+
151+
# testing stuff
152+
# game = ChaosGame(6, 1/3)
37153

154+
# game.iterate(20000, 100)
38155

39-
game = ChaosGame()
156+
# game.show(True)
157+
# game.savepng("stonks", True)

figures/1.png

770 KB
Loading

figures/2.png

245 KB
Loading

figures/3.png

623 KB
Loading

figures/4.png

1.14 MB
Loading

figures/5.png

1020 KB
Loading

test_chaos_game.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import chaos_game as cg
2+
import pytest as pt
3+
4+
5+
def test_chaos_constructor():
6+
g = cg.ChaosGame(1)
7+
assert g._n == 3
8+
9+
def test_show_exception():
10+
with pt.raises(Exception):
11+
g = cg.ChaosGame()
12+
g.show(True)
13+
14+
def test_savepng_exception():
15+
with pt.raises(Exception):
16+
g = cg.ChaosGame()
17+
g.savepng("bigmanting")
18+
19+
def test_plot_exception():
20+
with pt.raises(Exception):
21+
g = cg.ChaosGame()
22+
g.plot("bigmanting")
23+

0 commit comments

Comments
 (0)