Last active
August 2, 2020 16:52
-
-
Save PardhavMaradani/fc072ddf64cb7e589588fb492ff602e1 to your computer and use it in GitHub Desktop.
Blog: Auto Generated Mandalas
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # https://pardhav-m.blogspot.com/2020/08/auto-generated-mandalas.html | |
| # https://trinket.io/embed/python/8dffbb47db | |
| # Auto Generated Mandalas | |
| # Random Walker | |
| import math | |
| import turtle | |
| from slider import Slider | |
| from line_clip import cohensutherland | |
| import time | |
| import random | |
| # global vars | |
| g_sectors = 16 | |
| g_pensize = 1 | |
| g_mirror = 1 | |
| g_show_sectors = 1 | |
| g_color = 'black' | |
| g_colors = ["black", "dark grey", "cornflower blue", "deep sky blue", "dark turquoise", "aquamarine", "medium sea green", "khaki", "sienna", "firebrick", "coral", "red", "crimson", "hot pink", "dark orchid", "medium slate blue"] | |
| g_pause_for_click = False | |
| # screen setup | |
| g_screen = turtle.Screen() | |
| g_wh = int(g_screen.window_height()) | |
| g_ww = int(1.5 * g_wh) | |
| g_screen_radius = g_wh / 2 | |
| g_screen.setup(g_ww, g_wh) | |
| g_screen.tracer(0) | |
| g_center = [g_ww / 6, 0] | |
| g_minx = -g_ww / 6 | |
| g_maxx = g_ww / 2 | |
| g_miny = -g_wh / 2 | |
| g_maxy = g_wh / 2 | |
| # random walker vars | |
| g_steps_max = int(g_screen_radius) | |
| g_magnitude_max = int(g_screen_radius * 0.08) | |
| g_steps = int(g_steps_max / 2) | |
| g_magnitude = int(g_magnitude_max / 2) | |
| # internal | |
| g_prev_ct = None | |
| g_redraw = False | |
| g_paused = False | |
| # new turtle | |
| def new_turtle(): | |
| t = turtle.Turtle() | |
| t.ht() | |
| t.speed(0) | |
| return t | |
| # sector turtle | |
| st = new_turtle() | |
| st.color('lightgray') | |
| # main drawing turtle | |
| t = new_turtle() | |
| # draw a border | |
| def draw_border(): | |
| t = new_turtle() | |
| t.pu() | |
| t.goto(-g_ww/2, g_miny) | |
| t.pd() | |
| t.goto(g_ww/2, g_miny) | |
| t.goto(g_ww/2, g_maxy) | |
| t.goto(-g_ww/2, g_maxy) | |
| t.goto(-g_ww/2, g_miny) | |
| # partition | |
| t.color('gray') | |
| t.pu() | |
| t.goto(g_minx, g_miny) | |
| t.pd() | |
| t.goto(g_minx, g_maxy) | |
| t.update() | |
| # handle slider updates | |
| def handle_slider_update(id, value): | |
| global g_sectors | |
| global g_pensize | |
| global g_mirror | |
| global g_show_sectors | |
| global g_steps | |
| global g_magnitude | |
| global g_redraw | |
| if id == 0: | |
| g_sectors = value | |
| draw_sectors() | |
| elif id == 1: | |
| g_pensize = value | |
| elif id == 2: | |
| g_mirror = value | |
| elif id == 3: | |
| g_show_sectors = value | |
| draw_sectors() | |
| elif id == 4: | |
| g_steps = value | |
| elif id == 5: | |
| g_magnitude = value | |
| g_redraw = True | |
| # Color button class | |
| class ColorButton(turtle.Turtle): | |
| def __init__(self, x, y, color, click_callback = None): | |
| turtle.Turtle.__init__(self) | |
| self.shape('circle') | |
| self.speed(0) | |
| self.my_color = color | |
| self.click_callback = click_callback | |
| self.color(color) | |
| self.pu() | |
| self.goto(x, y) | |
| self.left(90) | |
| self.onclick(self.onclick_handler) | |
| self.update() | |
| # handle button click | |
| def onclick_handler(self, x, y): | |
| if self.click_callback: | |
| self.click_callback(self, self.my_color) | |
| self.shape('turtle') | |
| self.update() | |
| # color button click handler | |
| def handle_color_click(ct, color): | |
| global g_color | |
| global g_prev_ct | |
| global g_redraw | |
| g_prev_ct.shape('circle') | |
| g_prev_ct.update() | |
| g_color = color | |
| g_prev_ct = ct | |
| g_redraw = True | |
| # create UI | |
| def create_ui(): | |
| global g_prev_ct | |
| x = -g_ww / 2 + 20 | |
| y = g_wh / 2 - 20 | |
| normal_length = g_screen_radius * 0.55 | |
| toggle_length = g_screen_radius * 0.15 | |
| # sliders | |
| Slider(0, x, y, normal_length, 1, 32, 1, g_sectors, 'sectors', handle_slider_update) | |
| Slider(1, x, y - 30, normal_length, 1, 16, 1, g_pensize, 'pensize', handle_slider_update) | |
| Slider(2, x, y - 60, toggle_length, 0, 1, 1, g_mirror, 'mirror', handle_slider_update) | |
| Slider(3, x, y - 90, toggle_length, 0, 1, 1, g_show_sectors, 'show_sectors', handle_slider_update) | |
| # color buttons | |
| ci = 0 | |
| for r in range(2): | |
| cx = x | |
| cy = y - (150 + (30 * r)) | |
| for c in range(8): | |
| color = g_colors[ci] | |
| cb = ColorButton(cx + (c * 25), cy, color, handle_color_click) | |
| if color == g_color: | |
| cb.shape('turtle') | |
| g_prev_ct = cb | |
| ci += 1 | |
| Slider(4, x, y - 240, normal_length * 0.85, 1, g_steps_max, 10, g_steps, 'steps', handle_slider_update) | |
| Slider(5, x, y - 270, normal_length * 0.85, 1, g_magnitude_max, 1, g_magnitude, 'magnitude', handle_slider_update) | |
| # draw sectors | |
| def draw_sectors(): | |
| st.clear() | |
| if g_show_sectors == 0: | |
| return | |
| sector_angle = 360 / g_sectors | |
| cx, cy = g_center | |
| for i in range(g_sectors): | |
| st.pu() | |
| st.goto(cx, cy) | |
| st.seth(sector_angle * (i + 1)) | |
| st.fd(g_ww) | |
| (x, y) = st.pos() | |
| (nx1, ny1, nx2, ny2) = cohensutherland(g_minx, g_maxy, g_maxx, g_miny, cx, cy, x, y) | |
| if nx1 != None: | |
| st.goto(nx1, ny1) | |
| st.pd() | |
| st.goto(nx2, ny2) | |
| st.update() | |
| # rotate point | |
| def rotate_point(x, y, angle): | |
| cx, cy = g_center | |
| x -= cx | |
| y -= cy | |
| sin = math.sin(math.radians(angle)) | |
| cos = math.cos(math.radians(angle)) | |
| rx = cx + (x * cos - y * sin) | |
| ry = cy + (x * sin + y * cos) | |
| return (rx, ry) | |
| # angle of point wrt center | |
| def point_angle(x, y): | |
| cx, cy = g_center | |
| return math.degrees(math.atan2(y - cy, x - cx)) | |
| # draw line between two points | |
| def draw_line(t, px, py, x, y, angle): | |
| (rpx, rpy) = rotate_point(px, py, angle) | |
| (rx, ry) = rotate_point(x, y, angle) | |
| if rpx < g_minx or rx < g_minx: | |
| return | |
| t.pu() | |
| t.goto(rpx, rpy) | |
| t.pd() | |
| t.goto(rx, ry) | |
| # draw line segment | |
| def draw_segment(t, px, py, x, y, sectors, mirror): | |
| sector_angle = 360 / sectors | |
| h_sector_angle = sector_angle / 2 | |
| for i in range(sectors): | |
| draw_line(t, px, py, x, y, sector_angle * i) | |
| if mirror == 1: | |
| pa = point_angle(px, py) % sector_angle | |
| pma = h_sector_angle - pa | |
| (rpx, rpy) = rotate_point(px, py, 2 * pma) | |
| a = point_angle(x, y) % sector_angle | |
| ma = h_sector_angle - a | |
| (rx, ry) = rotate_point(x, y, 2 * ma) | |
| if abs(pma - ma) > h_sector_angle: | |
| continue | |
| draw_line(t, rpx, rpy, rx, ry, sector_angle * i) | |
| # draw points | |
| def draw_points(points): | |
| t.clear() | |
| t.pensize(g_pensize) | |
| t.color(g_color) | |
| (px, py) = points[0] | |
| for v in points: | |
| x, y = v | |
| draw_segment(t, px, py, x, y, g_sectors, g_mirror) | |
| px, py = x, y | |
| t.update() | |
| if g_redraw: | |
| break | |
| # draw | |
| def draw(): | |
| global g_redraw | |
| g_redraw = False | |
| cx, cy = g_center | |
| points = [] | |
| # px, py = 0, 0 | |
| px = random.randint(0, int(g_screen_radius * 0.5)) | |
| py = random.randint(0, int(g_screen_radius * 0.5)) | |
| for i in range(g_steps): | |
| dx = random.randint(-g_magnitude, g_magnitude) | |
| dy = random.randint(-g_magnitude, g_magnitude) | |
| x = px + dx | |
| y = py + dy | |
| points.append((cx + x, cy + y)) | |
| px, py = x, y | |
| draw_points(points) | |
| # draw mandalas in loop | |
| def draw_loop(): | |
| global g_paused | |
| while True: | |
| draw() | |
| if g_pause_for_click and not g_redraw: | |
| g_paused = True | |
| break | |
| if not g_redraw: | |
| time.sleep(1) | |
| # handle screen click | |
| def handle_screen_click(x, y): | |
| global g_paused | |
| if g_pause_for_click and g_paused: | |
| g_paused = False | |
| draw_loop() | |
| # setup | |
| def setup(): | |
| draw_border() | |
| create_ui() | |
| draw_sectors() | |
| g_screen.onclick(handle_screen_click) | |
| # main | |
| setup() | |
| draw_loop() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # https://pardhav-m.blogspot.com/2020/08/auto-generated-mandalas.html | |
| # https://trinket.io/embed/python/63e95daeca | |
| # Auto Generated Mandalas | |
| # Perlin Noise Walker | |
| import math | |
| import turtle | |
| from slider import Slider | |
| from line_clip import cohensutherland | |
| import time | |
| import random | |
| from noise import SimplexNoise | |
| # global vars | |
| g_sectors = 16 | |
| g_pensize = 1 | |
| g_mirror = 1 | |
| g_show_sectors = 1 | |
| g_color = 'black' | |
| g_colors = ["black", "dark grey", "cornflower blue", "deep sky blue", "dark turquoise", "aquamarine", "medium sea green", "khaki", "sienna", "firebrick", "coral", "red", "crimson", "hot pink", "dark orchid", "medium slate blue"] | |
| g_pause_for_click = False | |
| # screen setup | |
| g_screen = turtle.Screen() | |
| g_wh = int(g_screen.window_height()) | |
| g_ww = int(1.5 * g_wh) | |
| g_screen_radius = g_wh / 2 | |
| g_screen.setup(g_ww, g_wh) | |
| g_screen.tracer(0) | |
| g_center = [g_ww / 6, 0] | |
| g_minx = -g_ww / 6 | |
| g_maxx = g_ww / 2 | |
| g_miny = -g_wh / 2 | |
| g_maxy = g_wh / 2 | |
| # perlin walker vars | |
| g_noise_object = SimplexNoise() | |
| g_steps_max = int(g_screen_radius * 2) | |
| g_steps = int(g_steps_max / 2) | |
| g_magnitude = 0.8 | |
| g_speed = 2 | |
| g_show_path = 1 | |
| # internal | |
| g_prev_ct = None | |
| g_redraw = False | |
| g_paused = False | |
| # new turtle | |
| def new_turtle(): | |
| t = turtle.Turtle() | |
| t.ht() | |
| t.speed(0) | |
| return t | |
| # sector turtle | |
| st = new_turtle() | |
| st.color('lightgray') | |
| # walker turtle | |
| wt = new_turtle() | |
| wt.color('red') | |
| # main drawing turtle | |
| t = new_turtle() | |
| # draw a border | |
| def draw_border(): | |
| t = new_turtle() | |
| t.pu() | |
| t.goto(-g_ww/2, g_miny) | |
| t.pd() | |
| t.goto(g_ww/2, g_miny) | |
| t.goto(g_ww/2, g_maxy) | |
| t.goto(-g_ww/2, g_maxy) | |
| t.goto(-g_ww/2, g_miny) | |
| # partition | |
| t.color('gray') | |
| t.pu() | |
| t.goto(g_minx, g_miny) | |
| t.pd() | |
| t.goto(g_minx, g_maxy) | |
| t.update() | |
| # handle slider updates | |
| def handle_slider_update(id, value): | |
| global g_sectors | |
| global g_pensize | |
| global g_mirror | |
| global g_show_sectors | |
| global g_steps | |
| global g_magnitude | |
| global g_speed | |
| global g_show_path | |
| global g_redraw | |
| if id == 0: | |
| g_sectors = value | |
| draw_sectors() | |
| elif id == 1: | |
| g_pensize = value | |
| elif id == 2: | |
| g_mirror = value | |
| elif id == 3: | |
| g_show_sectors = value | |
| draw_sectors() | |
| elif id == 4: | |
| g_steps = value | |
| elif id == 5: | |
| g_magnitude = value | |
| elif id == 6: | |
| g_speed = value | |
| elif id == 7: | |
| g_show_path = value | |
| g_redraw = True | |
| # Color button class | |
| class ColorButton(turtle.Turtle): | |
| def __init__(self, x, y, color, click_callback = None): | |
| turtle.Turtle.__init__(self) | |
| self.shape('circle') | |
| self.speed(0) | |
| self.my_color = color | |
| self.click_callback = click_callback | |
| self.color(color) | |
| self.pu() | |
| self.goto(x, y) | |
| self.left(90) | |
| self.onclick(self.onclick_handler) | |
| self.update() | |
| # handle button click | |
| def onclick_handler(self, x, y): | |
| if self.click_callback: | |
| self.click_callback(self, self.my_color) | |
| self.shape('turtle') | |
| self.update() | |
| # color button click handler | |
| def handle_color_click(ct, color): | |
| global g_color | |
| global g_prev_ct | |
| global g_redraw | |
| g_prev_ct.shape('circle') | |
| g_prev_ct.update() | |
| g_color = color | |
| g_prev_ct = ct | |
| g_redraw = True | |
| # create UI | |
| def create_ui(): | |
| global g_prev_ct | |
| x = -g_ww / 2 + 20 | |
| y = g_wh / 2 - 20 | |
| normal_length = g_screen_radius * 0.55 | |
| toggle_length = g_screen_radius * 0.15 | |
| # sliders | |
| Slider(0, x, y, normal_length, 1, 32, 1, g_sectors, 'sectors', handle_slider_update) | |
| Slider(1, x, y - 30, normal_length, 1, 16, 1, g_pensize, 'pensize', handle_slider_update) | |
| Slider(2, x, y - 60, toggle_length, 0, 1, 1, g_mirror, 'mirror', handle_slider_update) | |
| Slider(3, x, y - 90, toggle_length, 0, 1, 1, g_show_sectors, 'show_sectors', handle_slider_update) | |
| # color buttons | |
| ci = 0 | |
| for r in range(2): | |
| cx = x | |
| cy = y - (150 + (30 * r)) | |
| for c in range(8): | |
| color = g_colors[ci] | |
| cb = ColorButton(cx + (c * 25), cy, color, handle_color_click) | |
| if color == g_color: | |
| cb.shape('turtle') | |
| g_prev_ct = cb | |
| ci += 1 | |
| Slider(4, x, y - 240, normal_length * 0.85, 1, g_steps_max, 10, g_steps, 'steps', handle_slider_update) | |
| Slider(5, x, y - 270, normal_length * 0.85, 0, 1, 0.05, g_magnitude, 'magnitude', handle_slider_update) | |
| Slider(6, x, y - 300, normal_length * 0.85, 1, 5, 1, g_speed, 'speed', handle_slider_update) | |
| Slider(7, x, y - 360, toggle_length, 0, 1, 1, g_show_path, 'show_path', handle_slider_update) | |
| # draw sectors | |
| def draw_sectors(): | |
| st.clear() | |
| if g_show_sectors == 0: | |
| return | |
| sector_angle = 360 / g_sectors | |
| cx, cy = g_center | |
| for i in range(g_sectors): | |
| st.pu() | |
| st.goto(cx, cy) | |
| st.seth(sector_angle * (i + 1)) | |
| st.fd(g_ww) | |
| (x, y) = st.pos() | |
| (nx1, ny1, nx2, ny2) = cohensutherland(g_minx, g_maxy, g_maxx, g_miny, cx, cy, x, y) | |
| if nx1 != None: | |
| st.goto(nx1, ny1) | |
| st.pd() | |
| st.goto(nx2, ny2) | |
| st.update() | |
| # rotate point | |
| def rotate_point(x, y, angle): | |
| cx, cy = g_center | |
| x -= cx | |
| y -= cy | |
| sin = math.sin(math.radians(angle)) | |
| cos = math.cos(math.radians(angle)) | |
| rx = cx + (x * cos - y * sin) | |
| ry = cy + (x * sin + y * cos) | |
| return (rx, ry) | |
| # angle of point wrt center | |
| def point_angle(x, y): | |
| cx, cy = g_center | |
| return math.degrees(math.atan2(y - cy, x - cx)) | |
| # draw line between two points | |
| def draw_line(t, px, py, x, y, angle): | |
| (rpx, rpy) = rotate_point(px, py, angle) | |
| (rx, ry) = rotate_point(x, y, angle) | |
| if rpx < g_minx or rx < g_minx: | |
| return | |
| t.pu() | |
| t.goto(rpx, rpy) | |
| t.pd() | |
| t.goto(rx, ry) | |
| # draw line segment | |
| def draw_segment(t, px, py, x, y, sectors, mirror): | |
| sector_angle = 360 / sectors | |
| h_sector_angle = sector_angle / 2 | |
| for i in range(sectors): | |
| draw_line(t, px, py, x, y, sector_angle * i) | |
| if mirror == 1: | |
| pa = point_angle(px, py) % sector_angle | |
| pma = h_sector_angle - pa | |
| (rpx, rpy) = rotate_point(px, py, 2 * pma) | |
| a = point_angle(x, y) % sector_angle | |
| ma = h_sector_angle - a | |
| (rx, ry) = rotate_point(x, y, 2 * ma) | |
| if abs(pma - ma) > h_sector_angle: | |
| continue | |
| draw_line(t, rpx, rpy, rx, ry, sector_angle * i) | |
| # draw points | |
| def draw_points(points): | |
| t.clear() | |
| t.pensize(g_pensize) | |
| t.color(g_color) | |
| (px, py) = points[0] | |
| for v in points: | |
| x, y = v | |
| draw_segment(t, px, py, x, y, g_sectors, g_mirror) | |
| px, py = x, y | |
| t.update() | |
| if g_redraw: | |
| break | |
| # Perlin Walker | |
| class PerlinWalker(): | |
| def __init__(self, x, y, steps, noise_object, magnitude, speed): | |
| self.xv = 0 | |
| self.yv = 0 | |
| self.x = x | |
| self.y = y | |
| self.lx = x | |
| self.ly = y | |
| self.steps = steps | |
| self.noise_object = noise_object | |
| self.magnitude = 0.01 * magnitude | |
| self.speed = speed | |
| # return all points of full walk | |
| def getPoints(self): | |
| points = [] | |
| for i in range(self.steps): | |
| points.append(self.update()) | |
| return points | |
| # update each step | |
| def update(self): | |
| n1 = self.noise_object.noise2(self.x * self.magnitude, self.y * self.magnitude) | |
| n2 = self.noise_object.noise2(self.x * self.magnitude, self.y * self.magnitude) | |
| self.xv = math.cos(n1 * 2 * math.pi) | |
| self.yv = -math.sin(n2 * 2 * math.pi) | |
| self.x += self.xv * self.speed | |
| self.y += self.yv * self.speed | |
| self.lx = self.x | |
| self.ly = self.y | |
| return (self.x, self.y) | |
| # draw walker path | |
| def draw_walker_path(points): | |
| wt.clear() | |
| if not g_show_path: | |
| return | |
| wt.pensize(g_pensize + 4) | |
| wt.pu() | |
| for point in points: | |
| wt.goto(point) | |
| if not wt.isdown(): | |
| wt.pd() | |
| wt.update() | |
| # center points | |
| def center_points(center, points): | |
| cp = [] | |
| cx, cy = center | |
| for point in points: | |
| x, y = point | |
| cp.append((cx + x, cy + y)) | |
| return cp | |
| # draw | |
| def draw(): | |
| global g_redraw | |
| g_redraw = False | |
| cx, cy = g_center | |
| points = [] | |
| # sx, sy = 0, 0 | |
| sx = random.randint(0, int(g_screen_radius * 0.5)) | |
| sy = random.randint(0, int(g_screen_radius * 0.5)) | |
| g_noise_object.randomize() | |
| walker = PerlinWalker(sx, sy, g_steps, g_noise_object, g_magnitude, g_speed) | |
| points = walker.getPoints() | |
| cp = center_points(g_center, points) | |
| draw_walker_path(cp) | |
| draw_points(cp) | |
| # draw mandalas in loop | |
| def draw_loop(): | |
| global g_paused | |
| while True: | |
| draw() | |
| if g_pause_for_click and not g_redraw: | |
| g_paused = True | |
| break | |
| if not g_redraw: | |
| time.sleep(1) | |
| # handle screen click | |
| def handle_screen_click(x, y): | |
| global g_paused | |
| if g_pause_for_click and g_paused: | |
| g_paused = False | |
| draw_loop() | |
| # setup | |
| def setup(): | |
| draw_border() | |
| create_ui() | |
| draw_sectors() | |
| g_screen.onclick(handle_screen_click) | |
| # main | |
| setup() | |
| draw_loop() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # https://pardhav-m.blogspot.com/2020/08/auto-generated-mandalas.html | |
| # https://trinket.io/embed/python/53254a5f55 | |
| # Auto Generated Mandalas | |
| # Perlin Noise Walker - Circular | |
| import math | |
| import turtle | |
| from slider import Slider | |
| from line_clip import cohensutherland | |
| import time | |
| import random | |
| from noise import SimplexNoise | |
| # global vars | |
| g_sectors = 16 | |
| g_pensize = 1 | |
| g_mirror = 0 | |
| g_show_sectors = 1 | |
| g_color = 'black' | |
| g_colors = ["black", "dark grey", "cornflower blue", "deep sky blue", "dark turquoise", "aquamarine", "medium sea green", "khaki", "sienna", "firebrick", "coral", "red", "crimson", "hot pink", "dark orchid", "medium slate blue"] | |
| g_pause_for_click = False | |
| # screen setup | |
| g_screen = turtle.Screen() | |
| g_wh = int(g_screen.window_height()) | |
| g_ww = int(1.5 * g_wh) | |
| g_screen_radius = g_wh / 2 | |
| g_screen.setup(g_ww, g_wh) | |
| g_screen.tracer(0) | |
| g_center = [g_ww / 6, 0] | |
| g_minx = -g_ww / 6 | |
| g_maxx = g_ww / 2 | |
| g_miny = -g_wh / 2 | |
| g_maxy = g_wh / 2 | |
| # perlin walker - circular - vars | |
| g_noise_object = SimplexNoise() | |
| g_step_angle = 5 | |
| g_magnitude = 0.8 | |
| g_frequency = 0.5 | |
| g_radius_max = int(g_screen_radius / 1.5) | |
| g_radius = int(g_radius_max / 1.25) | |
| g_show_path = 1 | |
| # internal | |
| g_prev_ct = None | |
| g_redraw = False | |
| g_paused = False | |
| # new turtle | |
| def new_turtle(): | |
| t = turtle.Turtle() | |
| t.ht() | |
| t.speed(0) | |
| return t | |
| # sector turtle | |
| st = new_turtle() | |
| st.color('lightgray') | |
| # walker turtle | |
| wt = new_turtle() | |
| wt.color('red') | |
| # main drawing turtle | |
| t = new_turtle() | |
| # draw a border | |
| def draw_border(): | |
| t = new_turtle() | |
| t.pu() | |
| t.goto(-g_ww/2, g_miny) | |
| t.pd() | |
| t.goto(g_ww/2, g_miny) | |
| t.goto(g_ww/2, g_maxy) | |
| t.goto(-g_ww/2, g_maxy) | |
| t.goto(-g_ww/2, g_miny) | |
| # partition | |
| t.color('gray') | |
| t.pu() | |
| t.goto(g_minx, g_miny) | |
| t.pd() | |
| t.goto(g_minx, g_maxy) | |
| t.update() | |
| # handle slider updates | |
| def handle_slider_update(id, value): | |
| global g_sectors | |
| global g_pensize | |
| global g_mirror | |
| global g_show_sectors | |
| global g_step_angle | |
| global g_magnitude | |
| global g_frequency | |
| global g_radius | |
| global g_show_path | |
| global g_redraw | |
| if id == 0: | |
| g_sectors = value | |
| draw_sectors() | |
| elif id == 1: | |
| g_pensize = value | |
| elif id == 2: | |
| g_mirror = value | |
| elif id == 3: | |
| g_show_sectors = value | |
| draw_sectors() | |
| elif id == 4: | |
| g_step_angle = value | |
| elif id == 5: | |
| g_magnitude = value | |
| elif id == 6: | |
| g_frequency = value | |
| elif id == 7: | |
| g_radius = value | |
| elif id == 8: | |
| g_show_path = value | |
| g_redraw = True | |
| # Color button class | |
| class ColorButton(turtle.Turtle): | |
| def __init__(self, x, y, color, click_callback = None): | |
| turtle.Turtle.__init__(self) | |
| self.shape('circle') | |
| self.speed(0) | |
| self.my_color = color | |
| self.click_callback = click_callback | |
| self.color(color) | |
| self.pu() | |
| self.goto(x, y) | |
| self.left(90) | |
| self.onclick(self.onclick_handler) | |
| self.update() | |
| # handle button click | |
| def onclick_handler(self, x, y): | |
| if self.click_callback: | |
| self.click_callback(self, self.my_color) | |
| self.shape('turtle') | |
| self.update() | |
| # color button click handler | |
| def handle_color_click(ct, color): | |
| global g_color | |
| global g_prev_ct | |
| global g_redraw | |
| g_prev_ct.shape('circle') | |
| g_prev_ct.update() | |
| g_color = color | |
| g_prev_ct = ct | |
| g_redraw = True | |
| # create UI | |
| def create_ui(): | |
| global g_prev_ct | |
| x = -g_ww / 2 + 20 | |
| y = g_wh / 2 - 20 | |
| normal_length = g_screen_radius * 0.55 | |
| toggle_length = g_screen_radius * 0.15 | |
| # sliders | |
| Slider(0, x, y, normal_length, 1, 32, 1, g_sectors, 'sectors', handle_slider_update) | |
| Slider(1, x, y - 30, normal_length, 1, 16, 1, g_pensize, 'pensize', handle_slider_update) | |
| Slider(2, x, y - 60, toggle_length, 0, 1, 1, g_mirror, 'mirror', handle_slider_update) | |
| Slider(3, x, y - 90, toggle_length, 0, 1, 1, g_show_sectors, 'show_sectors', handle_slider_update) | |
| # color buttons | |
| ci = 0 | |
| for r in range(2): | |
| cx = x | |
| cy = y - (150 + (30 * r)) | |
| for c in range(8): | |
| color = g_colors[ci] | |
| cb = ColorButton(cx + (c * 25), cy, color, handle_color_click) | |
| if color == g_color: | |
| cb.shape('turtle') | |
| g_prev_ct = cb | |
| ci += 1 | |
| Slider(4, x, y - 240, normal_length * 0.85, 1, 20, 1, g_step_angle, 'step_angle', handle_slider_update) | |
| Slider(5, x, y - 270, normal_length * 0.85, 0, 1, 0.05, g_magnitude, 'magnitude', handle_slider_update) | |
| Slider(6, x, y - 300, normal_length * 0.85, 0, 1, 0.05, g_frequency, 'frequency', handle_slider_update) | |
| Slider(7, x, y - 330, normal_length * 0.85, 10, g_radius_max, 10, g_radius, 'radius', handle_slider_update) | |
| Slider(8, x, y - 390, toggle_length, 0, 1, 1, g_show_path, 'show_path', handle_slider_update) | |
| # draw sectors | |
| def draw_sectors(): | |
| st.clear() | |
| if g_show_sectors == 0: | |
| return | |
| sector_angle = 360 / g_sectors | |
| cx, cy = g_center | |
| for i in range(g_sectors): | |
| st.pu() | |
| st.goto(cx, cy) | |
| st.seth(sector_angle * (i + 1)) | |
| st.fd(g_ww) | |
| (x, y) = st.pos() | |
| (nx1, ny1, nx2, ny2) = cohensutherland(g_minx, g_maxy, g_maxx, g_miny, cx, cy, x, y) | |
| if nx1 != None: | |
| st.goto(nx1, ny1) | |
| st.pd() | |
| st.goto(nx2, ny2) | |
| st.update() | |
| # rotate point | |
| def rotate_point(x, y, angle): | |
| cx, cy = g_center | |
| x -= cx | |
| y -= cy | |
| sin = math.sin(math.radians(angle)) | |
| cos = math.cos(math.radians(angle)) | |
| rx = cx + (x * cos - y * sin) | |
| ry = cy + (x * sin + y * cos) | |
| return (rx, ry) | |
| # angle of point wrt center | |
| def point_angle(x, y): | |
| cx, cy = g_center | |
| return math.degrees(math.atan2(y - cy, x - cx)) | |
| # draw line between two points | |
| def draw_line(t, px, py, x, y, angle): | |
| (rpx, rpy) = rotate_point(px, py, angle) | |
| (rx, ry) = rotate_point(x, y, angle) | |
| if rpx < g_minx or rx < g_minx: | |
| return | |
| t.pu() | |
| t.goto(rpx, rpy) | |
| t.pd() | |
| t.goto(rx, ry) | |
| # draw line segment | |
| def draw_segment(t, px, py, x, y, sectors, mirror): | |
| sector_angle = 360 / sectors | |
| h_sector_angle = sector_angle / 2 | |
| for i in range(sectors): | |
| draw_line(t, px, py, x, y, sector_angle * i) | |
| if mirror == 1: | |
| pa = point_angle(px, py) % sector_angle | |
| pma = h_sector_angle - pa | |
| (rpx, rpy) = rotate_point(px, py, 2 * pma) | |
| a = point_angle(x, y) % sector_angle | |
| ma = h_sector_angle - a | |
| (rx, ry) = rotate_point(x, y, 2 * ma) | |
| if abs(pma - ma) > h_sector_angle: | |
| continue | |
| draw_line(t, rpx, rpy, rx, ry, sector_angle * i) | |
| # draw points | |
| def draw_points(points): | |
| t.clear() | |
| t.pensize(g_pensize) | |
| t.color(g_color) | |
| (px, py) = points[0] | |
| for v in points: | |
| x, y = v | |
| draw_segment(t, px, py, x, y, g_sectors, g_mirror) | |
| px, py = x, y | |
| t.update() | |
| if g_redraw: | |
| break | |
| # draw walker path | |
| def draw_walker_path(points): | |
| wt.clear() | |
| if not g_show_path: | |
| return | |
| wt.pensize(g_pensize + 4) | |
| wt.pu() | |
| for point in points: | |
| wt.goto(point) | |
| if not wt.isdown(): | |
| wt.pd() | |
| wt.update() | |
| # draw | |
| def draw(): | |
| global g_redraw | |
| g_redraw = False | |
| cx, cy = g_center | |
| t = 0 | |
| points = [] | |
| g_noise_object.randomize() | |
| for a in range(0, 360 + g_step_angle, g_step_angle): | |
| noise = g_noise_object.noise2(t, 0.1) | |
| xradius = g_radius | |
| yradius = g_radius * (1 - (noise * g_magnitude)) | |
| x = cx + (xradius * math.cos(math.radians(a))) | |
| y = cy + (yradius * math.sin(math.radians(a))) | |
| points.append((x, y)) | |
| t += 0.01 * (10 * g_frequency) | |
| draw_walker_path(points) | |
| draw_points(points) | |
| # draw mandalas in loop | |
| def draw_loop(): | |
| global g_paused | |
| while True: | |
| draw() | |
| if g_pause_for_click and not g_redraw: | |
| g_paused = True | |
| break | |
| if not g_redraw: | |
| time.sleep(1) | |
| # handle screen click | |
| def handle_screen_click(x, y): | |
| global g_paused | |
| if g_pause_for_click and g_paused: | |
| g_paused = False | |
| draw_loop() | |
| # setup | |
| def setup(): | |
| draw_border() | |
| create_ui() | |
| draw_sectors() | |
| g_screen.onclick(handle_screen_click) | |
| # main | |
| setup() | |
| draw_loop() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import turtle | |
| # register square thumb shape | |
| thumb_size = 7 | |
| screen = turtle.Screen() | |
| screen.register_shape('thumb', ((-thumb_size, -thumb_size), (thumb_size, -thumb_size), (thumb_size, thumb_size), (-thumb_size, thumb_size))) | |
| # Slider Class | |
| class Slider(turtle.Turtle): | |
| def __init__(self, id, x, y, length, min, max, step, initial_value, label, callback): | |
| turtle.Turtle.__init__(self) | |
| self.shape('thumb') | |
| self.speed(0) | |
| self.id = id | |
| self.x = x | |
| self.y = y | |
| self.length = length | |
| self.min = min | |
| self.step = step | |
| self.label = label | |
| self.callback = callback | |
| self.clicked = False | |
| self.dragging = False | |
| self.steps = (max - min) / step | |
| # draw slider line | |
| self.pu() | |
| self.goto(x, y) | |
| self.pd() | |
| self.fd(length) | |
| # turtle to write label text and value | |
| self.lt = turtle.Turtle() | |
| self.lt.speed(0) | |
| self.lt.pu() | |
| self.lt.goto(self.pos()) | |
| self.lt.fd(20) | |
| self.lt.right(90) | |
| self.lt.fd(thumb_size/2) | |
| self.lt.ht() | |
| # move thumb to initial position | |
| self.bk(length) | |
| initial_length = length * ((initial_value - min) / (max - min)) | |
| self.fd(initial_length) | |
| self.value = initial_value | |
| # update label | |
| self.update_label() | |
| # register mouse handlers | |
| self.onclick(self.onclick_handler) | |
| self.onrelease(self.onrelease_handler) | |
| self.ondrag(self.ondrag_handler) | |
| # write label text and value | |
| def update_label(self): | |
| self.lt.clear() | |
| self.lt.write(self.label + ' = ' + str(self.value), font=("Arial", 10, "normal")) | |
| self.update() | |
| # get value based on slider position | |
| def get_value(self, x): | |
| unit_value = (x - self.x) / self.length | |
| v1 = unit_value * self.steps * self.step | |
| v1 = int(v1 / self.step) * self.step | |
| return self.min + v1 | |
| # onclick handler | |
| def onclick_handler(self, x, y): | |
| self.clicked = True | |
| # onrelease handler | |
| def onrelease_handler(self, x, y): | |
| self.clicked = False | |
| # ondrag handler | |
| def ondrag_handler(self, x, y): | |
| if not self.clicked: | |
| return | |
| if self.dragging: | |
| return | |
| # stop drag if mouse moves away in y direction | |
| if abs(y - self.y) > 20: | |
| self.clicked = False | |
| self.dragging = False | |
| self.callback(self.id, self.value) | |
| return | |
| self.dragging = True | |
| # limit drag within the slider | |
| if x < self.x: | |
| x = self.x | |
| if x > self.x + self.length: | |
| x = self.x + self.length | |
| # move thumb to new position | |
| self.goto(x, self.y) | |
| new_value = self.get_value(x) | |
| # call the callback function if value changes | |
| if new_value != self.value: | |
| self.value = new_value | |
| self.update_label() | |
| self.callback(self.id, self.value) | |
| self.update() | |
| self.dragging = False |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Source: https://github.com/scivision/lineclipping-python-fortran/blob/master/pylineclip/__init__.py | |
| ''' | |
| The MIT License (MIT) | |
| Copyright (c) 2014 Michael Hirsch | |
| reference: http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm | |
| * The best way to Numba JIT this would probably be in the function calling this, | |
| to include the loop itself inside the jit decoration. | |
| ''' | |
| def cohensutherland(xmin, ymax, xmax, ymin, x1, y1, x2, y2): | |
| """Clips a line to a rectangular area. | |
| This implements the Cohen-Sutherland line clipping algorithm. xmin, | |
| ymax, xmax and ymin denote the clipping area, into which the line | |
| defined by x1, y1 (start point) and x2, y2 (end point) will be | |
| clipped. | |
| If the line does not intersect with the rectangular clipping area, | |
| four None values will be returned as tuple. Otherwise a tuple of the | |
| clipped line points will be returned in the form (cx1, cy1, cx2, cy2). | |
| """ | |
| INSIDE, LEFT, RIGHT, LOWER, UPPER = 0, 1, 2, 4, 8 | |
| def _getclip(xa, ya): | |
| # if dbglvl>1: print('point: '),; print(xa,ya) | |
| p = INSIDE # default is inside | |
| # consider x | |
| if xa < xmin: | |
| p |= LEFT | |
| elif xa > xmax: | |
| p |= RIGHT | |
| # consider y | |
| if ya < ymin: | |
| p |= LOWER # bitwise OR | |
| elif ya > ymax: | |
| p |= UPPER # bitwise OR | |
| return p | |
| # check for trivially outside lines | |
| k1 = _getclip(x1, y1) | |
| k2 = _getclip(x2, y2) | |
| # %% examine non-trivially outside points | |
| # bitwise OR | | |
| while (k1 | k2) != 0: # if both points are inside box (0000) , ACCEPT trivial whole line in box | |
| # if line trivially outside window, REJECT | |
| if (k1 & k2) != 0: # bitwise AND & | |
| # if dbglvl>1: print(' REJECT trivially outside box') | |
| # return nan, nan, nan, nan | |
| return None, None, None, None | |
| # non-trivial case, at least one point outside window | |
| # this is not a bitwise or, it's the word "or" | |
| opt = k1 or k2 # take first non-zero point, short circuit logic | |
| if opt & UPPER: # these are bitwise ANDS | |
| x = x1 + (x2 - x1) * (ymax - y1) / (y2 - y1) | |
| y = ymax | |
| elif opt & LOWER: | |
| x = x1 + (x2 - x1) * (ymin - y1) / (y2 - y1) | |
| y = ymin | |
| elif opt & RIGHT: | |
| y = y1 + (y2 - y1) * (xmax - x1) / (x2 - x1) | |
| x = xmax | |
| elif opt & LEFT: | |
| y = y1 + (y2 - y1) * (xmin - x1) / (x2 - x1) | |
| x = xmin | |
| else: | |
| raise RuntimeError('Undefined clipping state') | |
| if opt == k1: | |
| x1, y1 = x, y | |
| k1 = _getclip(x1, y1) | |
| elif opt == k2: | |
| x2, y2 = x, y | |
| k2 = _getclip(x2, y2) | |
| return x1, y1, x2, y2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Source: https://github.com/caseman/noise/blob/master/perlin.py | |
| # Copyright (c) 2008, Casey Duncan (casey dot duncan at gmail dot com) | |
| # see LICENSE.txt for details | |
| """Perlin noise -- pure python implementation""" | |
| __version__ = '$Id: perlin.py 521 2008-12-15 03:03:52Z casey.duncan $' | |
| from math import floor, sqrt | |
| from random import randint | |
| # 3D Gradient vectors | |
| _GRAD3 = ((1,1,0),(-1,1,0),(1,-1,0),(-1,-1,0), | |
| (1,0,1),(-1,0,1),(1,0,-1),(-1,0,-1), | |
| (0,1,1),(0,-1,1),(0,1,-1),(0,-1,-1), | |
| (1,1,0),(0,-1,1),(-1,1,0),(0,-1,-1), | |
| ) | |
| # 4D Gradient vectors | |
| _GRAD4 = ((0,1,1,1), (0,1,1,-1), (0,1,-1,1), (0,1,-1,-1), | |
| (0,-1,1,1), (0,-1,1,-1), (0,-1,-1,1), (0,-1,-1,-1), | |
| (1,0,1,1), (1,0,1,-1), (1,0,-1,1), (1,0,-1,-1), | |
| (-1,0,1,1), (-1,0,1,-1), (-1,0,-1,1), (-1,0,-1,-1), | |
| (1,1,0,1), (1,1,0,-1), (1,-1,0,1), (1,-1,0,-1), | |
| (-1,1,0,1), (-1,1,0,-1), (-1,-1,0,1), (-1,-1,0,-1), | |
| (1,1,1,0), (1,1,-1,0), (1,-1,1,0), (1,-1,-1,0), | |
| (-1,1,1,0), (-1,1,-1,0), (-1,-1,1,0), (-1,-1,-1,0)) | |
| # A lookup table to traverse the simplex around a given point in 4D. | |
| # Details can be found where this table is used, in the 4D noise method. | |
| _SIMPLEX = ( | |
| (0,1,2,3),(0,1,3,2),(0,0,0,0),(0,2,3,1),(0,0,0,0),(0,0,0,0),(0,0,0,0),(1,2,3,0), | |
| (0,2,1,3),(0,0,0,0),(0,3,1,2),(0,3,2,1),(0,0,0,0),(0,0,0,0),(0,0,0,0),(1,3,2,0), | |
| (0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0), | |
| (1,2,0,3),(0,0,0,0),(1,3,0,2),(0,0,0,0),(0,0,0,0),(0,0,0,0),(2,3,0,1),(2,3,1,0), | |
| (1,0,2,3),(1,0,3,2),(0,0,0,0),(0,0,0,0),(0,0,0,0),(2,0,3,1),(0,0,0,0),(2,1,3,0), | |
| (0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0), | |
| (2,0,1,3),(0,0,0,0),(0,0,0,0),(0,0,0,0),(3,0,1,2),(3,0,2,1),(0,0,0,0),(3,1,2,0), | |
| (2,1,0,3),(0,0,0,0),(0,0,0,0),(0,0,0,0),(3,1,0,2),(0,0,0,0),(3,2,0,1),(3,2,1,0)) | |
| # Simplex skew constants | |
| _F2 = 0.5 * (sqrt(3.0) - 1.0) | |
| _G2 = (3.0 - sqrt(3.0)) / 6.0 | |
| _F3 = 1.0 / 3.0 | |
| _G3 = 1.0 / 6.0 | |
| class BaseNoise: | |
| """Noise abstract base class""" | |
| permutation = (151,160,137,91,90,15, | |
| 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, | |
| 190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, | |
| 88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166, | |
| 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, | |
| 102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196, | |
| 135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123, | |
| 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, | |
| 223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9, | |
| 129,22,39,253,9,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228, | |
| 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, | |
| 49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254, | |
| 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180) | |
| period = len(permutation) | |
| # Double permutation array so we don't need to wrap | |
| permutation = permutation * 2 | |
| randint_function = randint | |
| def __init__(self, period=None, permutation_table=None, randint_function=None): | |
| """Initialize the noise generator. With no arguments, the default | |
| period and permutation table are used (256). The default permutation | |
| table generates the exact same noise pattern each time. | |
| An integer period can be specified, to generate a random permutation | |
| table with period elements. The period determines the (integer) | |
| interval that the noise repeats, which is useful for creating tiled | |
| textures. period should be a power-of-two, though this is not | |
| enforced. Note that the speed of the noise algorithm is indpendent of | |
| the period size, though larger periods mean a larger table, which | |
| consume more memory. | |
| A permutation table consisting of an iterable sequence of whole | |
| numbers can be specified directly. This should have a power-of-two | |
| length. Typical permutation tables are a sequnce of unique integers in | |
| the range [0,period) in random order, though other arrangements could | |
| prove useful, they will not be "pure" simplex noise. The largest | |
| element in the sequence must be no larger than period-1. | |
| period and permutation_table may not be specified together. | |
| A substitute for the method random.randint(a, b) can be chosen. The | |
| method must take two integer parameters a and b and return an integer N | |
| such that a <= N <= b. | |
| """ | |
| if randint_function is not None: # do this before calling randomize() | |
| if not hasattr(randint_function, '__call__'): | |
| raise TypeError( | |
| 'randint_function has to be a function') | |
| self.randint_function = randint_function | |
| if period is None: | |
| period = self.period # enforce actually calling randomize() | |
| if period is not None and permutation_table is not None: | |
| raise ValueError( | |
| 'Can specify either period or permutation_table, not both') | |
| if period is not None: | |
| self.randomize(period) | |
| elif permutation_table is not None: | |
| self.permutation = tuple(permutation_table) * 2 | |
| self.period = len(permutation_table) | |
| def randomize(self, period=None): | |
| """Randomize the permutation table used by the noise functions. This | |
| makes them generate a different noise pattern for the same inputs. | |
| """ | |
| if period is not None: | |
| self.period = period | |
| perm = list(range(self.period)) | |
| perm_right = self.period - 1 | |
| for i in list(perm): | |
| #j = self.randint_function(0, perm_right) | |
| j = randint(0, perm_right) | |
| perm[i], perm[j] = perm[j], perm[i] | |
| self.permutation = tuple(perm) * 2 | |
| class SimplexNoise(BaseNoise): | |
| """Perlin simplex noise generator | |
| Adapted from Stefan Gustavson's Java implementation described here: | |
| http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf | |
| To summarize: | |
| "In 2001, Ken Perlin presented 'simplex noise', a replacement for his classic | |
| noise algorithm. Classic 'Perlin noise' won him an academy award and has | |
| become an ubiquitous procedural primitive for computer graphics over the | |
| years, but in hindsight it has quite a few limitations. Ken Perlin himself | |
| designed simplex noise specifically to overcome those limitations, and he | |
| spent a lot of good thinking on it. Therefore, it is a better idea than his | |
| original algorithm. A few of the more prominent advantages are: | |
| * Simplex noise has a lower computational complexity and requires fewer | |
| multiplications. | |
| * Simplex noise scales to higher dimensions (4D, 5D and up) with much less | |
| computational cost, the complexity is O(N) for N dimensions instead of | |
| the O(2^N) of classic Noise. | |
| * Simplex noise has no noticeable directional artifacts. Simplex noise has | |
| a well-defined and continuous gradient everywhere that can be computed | |
| quite cheaply. | |
| * Simplex noise is easy to implement in hardware." | |
| """ | |
| def noise2(self, x, y): | |
| """2D Perlin simplex noise. | |
| Return a floating point value from -1 to 1 for the given x, y coordinate. | |
| The same value is always returned for a given x, y pair unless the | |
| permutation table changes (see randomize above). | |
| """ | |
| # Skew input space to determine which simplex (triangle) we are in | |
| s = (x + y) * _F2 | |
| i = floor(x + s) | |
| j = floor(y + s) | |
| t = (i + j) * _G2 | |
| x0 = x - (i - t) # "Unskewed" distances from cell origin | |
| y0 = y - (j - t) | |
| if x0 > y0: | |
| i1 = 1; j1 = 0 # Lower triangle, XY order: (0,0)->(1,0)->(1,1) | |
| else: | |
| i1 = 0; j1 = 1 # Upper triangle, YX order: (0,0)->(0,1)->(1,1) | |
| x1 = x0 - i1 + _G2 # Offsets for middle corner in (x,y) unskewed coords | |
| y1 = y0 - j1 + _G2 | |
| x2 = x0 + _G2 * 2.0 - 1.0 # Offsets for last corner in (x,y) unskewed coords | |
| y2 = y0 + _G2 * 2.0 - 1.0 | |
| # Determine hashed gradient indices of the three simplex corners | |
| perm = self.permutation | |
| ii = int(i) % self.period | |
| jj = int(j) % self.period | |
| gi0 = perm[ii + perm[jj]] % 12 | |
| gi1 = perm[ii + i1 + perm[jj + j1]] % 12 | |
| gi2 = perm[ii + 1 + perm[jj + 1]] % 12 | |
| # Calculate the contribution from the three corners | |
| tt = 0.5 - x0**2 - y0**2 | |
| if tt > 0: | |
| g = _GRAD3[gi0] | |
| noise = tt**4 * (g[0] * x0 + g[1] * y0) | |
| else: | |
| noise = 0.0 | |
| tt = 0.5 - x1**2 - y1**2 | |
| if tt > 0: | |
| g = _GRAD3[gi1] | |
| noise += tt**4 * (g[0] * x1 + g[1] * y1) | |
| tt = 0.5 - x2**2 - y2**2 | |
| if tt > 0: | |
| g = _GRAD3[gi2] | |
| noise += tt**4 * (g[0] * x2 + g[1] * y2) | |
| return noise * 70.0 # scale noise to [-1, 1] | |
| def lerp(t, a, b): | |
| return a + t * (b - a) | |
| def grad3(hash, x, y, z): | |
| g = _GRAD3[hash % 16] | |
| return x*g[0] + y*g[1] + z*g[2] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment