From 2956f6bd78fd5560f75aca37d696c6fd1b53dc42 Mon Sep 17 00:00:00 2001 From: Technoduck Date: Mon, 4 Nov 2024 01:13:39 -0500 Subject: [PATCH] ui more or less done, testing things in progress --- app.py | 124 ++++++++++++++++++++++++++++++++++++++++++----------- skydome.py | 98 +++++++++++------------------------------- 2 files changed, 124 insertions(+), 98 deletions(-) diff --git a/app.py b/app.py index 8446f19..05375d3 100644 --- a/app.py +++ b/app.py @@ -43,17 +43,23 @@ class renderedImageZoom: command=self.get_altitude, pass_coords=True) - #self.map_widget.add_right_click_menu_command(label="open_window", - # command=self.show_image, - # pass_coords=True) + + self.image_window = image_window + + self.image_window.title("Simulated Sunset") + self.image_window.config(width=256,height=256) + #image = skydome.renderFromCamera(coords) + self.canvas = tkinter.Canvas(self.image_window,bg="white") + self.canvas.pack(fill=tkinter.BOTH,expand=True) + self.environment_window_filler() + + def environment_window_filler(self): self.aerosol_window.title("options selector") - - environemnt_info_text = tkinter.Label(self.aerosol_window, text="Please choose in which environment the sunset is occuring") - environemnt_info_text.pack(side='top',pady=20,padx=10) + environemnt_info_text.grid(column=0,row=0,columnspan=2,pady=20,padx=10) #Continental Clean - 26e-6 g = 0.709 @@ -73,43 +79,84 @@ class renderedImageZoom: #Antarctic - 11e-6 g = 0.784 buton_cont_clean = tkinter.Button(self.aerosol_window,text="Continental Clean",command=lambda: self.env_setter(26e-6,0.709)); - buton_cont_clean.pack(side='top',padx=10,pady=5) + buton_cont_clean.grid(column=0,row=1,padx=10,pady=5) buton_cont_avr = tkinter.Button(self.aerosol_window,text="Continental Average ",command=lambda: self.env_setter(75e-6,0.793)); - buton_cont_avr.pack(side='top',padx=10,pady=5) + buton_cont_avr.grid(column=0,row=2,padx=10,pady=5) buton_cont_poll = tkinter.Button(self.aerosol_window,text="Continental Polluted ",command=lambda: self.env_setter(175e-6,0.698)); - buton_cont_poll.pack(side='top',padx=10,pady=5) + buton_cont_poll.grid(column=0,row=3,padx=10,pady=5) buton_urban = tkinter.Button(self.aerosol_window,text="Urban ",command=lambda: self.env_setter(353e-6,0.689)); - buton_urban.pack(side='top',padx=10,pady=5) + buton_urban.grid(column=0,row=4,padx=10,pady=5) buton_desert = tkinter.Button(self.aerosol_window,text="Desert ",command=lambda: self.env_setter(145e-6,0.729)); - buton_desert.pack(side='top',padx=10,pady=5) + buton_desert.grid(column=0,row=5,padx=10,pady=5) buton_mar_clean = tkinter.Button(self.aerosol_window,text="Maritime Clean ",command=lambda: self.env_setter(90e-6,0.772)); - buton_mar_clean.pack(side='top',padx=10,pady=5) + buton_mar_clean.grid(column=1,row=1,padx=10,pady=5) buton_mar_poll = tkinter.Button(self.aerosol_window,text="Maritime Polluted ",command=lambda: self.env_setter(115e-6,0.756)); - buton_mar_poll.pack(side='top',padx=10,pady=5) + buton_mar_poll.grid(column=1,row=2,padx=10,pady=5) buton_mar_tro = tkinter.Button(self.aerosol_window,text="Maritime Tropic ",command=lambda: self.env_setter(43e-6,0.774)); - buton_mar_tro.pack(side='top',padx=10,pady=5) + buton_mar_tro.grid(column=1,row=3,padx=10,pady=5) buton_arctic = tkinter.Button(self.aerosol_window,text="Arctic ",command=lambda: self.env_setter(23e-6,0.721)); - buton_arctic.pack(side='top',padx=10,pady=5) + buton_arctic.grid(column=1,row=4,padx=10,pady=5) buton_antarctic = tkinter.Button(self.aerosol_window,text="Antarctic ",command=lambda: self.env_setter(11e-6,0.784)); - buton_antarctic.pack(side='top',padx=10,pady=5) + buton_antarctic.grid(column=1,row=5,padx=10,pady=5) + + environemnt_info_text = tkinter.Label(self.aerosol_window, text="Please enter the temperature and pressure (in C, and mmHg)") + environemnt_info_text.grid(column=0,row=6,columnspan=2,pady=20,padx=10) - self.image_window = image_window + t_frame = tkinter.Frame(self.aerosol_window) + text_t = tkinter.Text(t_frame,height=1,width=10) + submit_t_button = tkinter.Button(t_frame,text="set temperature",command=lambda: self.set_temp(text_t.get("1.0","end-1c"))) + text_t.pack(side=tkinter.LEFT) + submit_t_button.pack(side=tkinter.RIGHT) + + t_frame.grid(column=0,row=7,columnspan=2,padx=20) + + p_frame = tkinter.Frame(self.aerosol_window) + text_p = tkinter.Text(p_frame,height=1,width=10) + submit_p_button = tkinter.Button(p_frame,text="set pressure",command=lambda: self.set_press(text_p.get("1.0","end-1c"))) + text_p.pack(side=tkinter.LEFT) + submit_p_button.pack(side=tkinter.RIGHT) + + p_frame.grid(column=0,row=8,columnspan=2,padx=20) + + + testing_info_text = tkinter.Label(self.aerosol_window, text="Change y heights between which average redness and blueness will be calculated") + testing_info_text.grid(column=0,row=9,columnspan=2,pady=20,padx=10) + + r_frame = tkinter.Frame(self.aerosol_window) + text_r_l = tkinter.Text(r_frame,height=1,width=10) + text_r_u = tkinter.Text(r_frame,height=1,width=10) + submit_r_button = tkinter.Button(r_frame,text="calculate_average_red",command=lambda: average_r_label.config( + text=self.get_image_average("red",int(text_r_l.get("1.0","end-1c")),int(text_r_u.get("1.0","end-1c"))))) + average_r_label = tkinter.Label(r_frame,text="no average yet") + text_r_l.pack(side=tkinter.LEFT) + text_r_u.pack(side=tkinter.LEFT) + average_r_label.pack(side=tkinter.RIGHT) + submit_r_button.pack(side=tkinter.RIGHT) + r_frame.grid(column=0,row=10,columnspan=2,padx=20) + + + b_frame = tkinter.Frame(self.aerosol_window) + text_b_l = tkinter.Text(b_frame,height=1,width=10) + text_b_u = tkinter.Text(b_frame,height=1,width=10) + submit_b_button = tkinter.Button(b_frame,text="calculate_average_blue",command=lambda: average_b_label.config( + text=self.get_image_average("blue",int(text_b_l.get("1.0","end-1c")),int(text_b_u.get("1.0","end-1c"))))) + average_b_label = tkinter.Label(b_frame,text="no average yet") + text_b_l.pack(side=tkinter.LEFT) + text_b_u.pack(side=tkinter.LEFT) + average_b_label.pack(side=tkinter.RIGHT) + submit_b_button.pack(side=tkinter.RIGHT) + b_frame.grid(column=0,row=11,columnspan=2,padx=20) - self.image_window.title("Simulated Sunset") - self.image_window.config(width=256,height=256) - #image = skydome.renderFromCamera(coords) - self.canvas = tkinter.Canvas(self.image_window,bg="white") - self.canvas.pack(fill=tkinter.BOTH,expand=True) #self.img = Image.fromarray(image,mode="RGB") #self.tk_image = ImageTk.PhotoImage(width=256,height=256,image=self.img) #img = ImageTk.PhotoImage(Image.open("highpress_camera.png")) @@ -125,13 +172,42 @@ class renderedImageZoom: self.canvas.bind("",self.zoom_in) self.canvas.bind("", self.zoom_out) + def set_temp(self,temp): + if temp is None: + self.temperature = 0 + elif int(temp) < -270 or int(temp) > 100: + print("temperature unrealistic, resetting to 0 degrees") + self.temperature = 0 + else: + self.temperature = int(temp) + + def set_press(self,press): + if press is None: + self.pressure = 720 + elif int(press) < 100 or int(press) > 1300: + print("pressure unrealistic, resetting to 720 mmHg") + self.pressure = 720 + else: + self.pressure = int(press) + + + def get_image_average(self,color,low, high): + if color == "blue": + slice = self.image[:][low:high] + print(slice.size) + print(slice) + return "{} + {}".format(low,high) + else: + return "red".format(low,high) + + def env_setter(self,environment_constant,g): self.betaM = skydome.Vec3(environment_constant,environment_constant,environment_constant) self.g = g def render(self): - image = skydome.renderFromCamera(self.coords,self.betaM,self.g,self.altitude) - self.img = Image.fromarray(image,mode="RGB") + self.image = skydome.renderFromCamera(self.coords,self.betaM,self.g,self.altitude,self.temperature,self.pressure) + self.img = Image.fromarray(self.image,mode="RGB") self.tk_image = ImageTk.PhotoImage(width=256,height=256,image=self.img) #img = ImageTk.PhotoImage(Image.open("highpress_camera.png")) diff --git a/skydome.py b/skydome.py index e665ef8..c4f9c1f 100644 --- a/skydome.py +++ b/skydome.py @@ -1,61 +1,6 @@ #!/usr/bin/env python # coding: utf-8 -# https://journals.ametsoc.org/view/journals/bams/79/5/1520-0477_1998_079_0831_opoaac_2_0_co_2.xml?tab_body=pdf - for aerosols and clouds -# -# https://iopscience.iop.org/article/10.1088/1757-899X/1269/1/012011/pdf - for Rayleigh extinction -# -# https://sci-hub.se/10.1364/josa.48.1018_1 - temperature dependance Rayleigh scattering -# -# https://sci-hub.se/https://doi.org/10.1364/JOSA.47.000176 - Rayleigh coefficients (index of refraction) - Penndorf -# -# We are going to use the real rayleigh scattering coefficient: -# -# $\beta_R(h, \lambda) = \frac{8\pi(n^2 - 1)^2}{2N\lambda^4} e^{-\frac{h}{H_R}}$ -# -# n = index of refraction, these are calculated in the 4th paper, temperature dependance -# -# N = molecular density, dependent on humidity -# -# $\lambda$ = wavelength, but we only care for RGB (440, 550, 680 nm) -# -# From Penndorf, the dispersion equation is: -# -# $(n_s - 1) \times 10^{-8} = 6432.8 + \frac{2949810}{146 - v^2} + \frac{25540}{41 - v^2}$ -# -# It is important to note that $v=1/f$ which is measured in um -# -# Once we have calculated our dispersion index of refraction, we can see what our new value for n will be (it is dependant on temperature and pressure) -# -# $$ -# (n - 1) = (n_s - 1) \left( \frac{(1 + \alpha t_s)}{(1 + \alpha t)} \right) \frac{p}{p_s} -# $$ -# -# Here, we are using $n_s$ from the dispersion equation, $t_s$ is 15 degrees Celsius, and $p_s$ is 760 mmHg. $t$ represents the actual temperature, and $p$ represents the actual pressure. $\alpha$ is the coefficient of linear expansion of air = 0.00367 in units of inverse celcius. We see that the influence of air pressure really is critical -# -# -# We also need to find N, the molecular density (depends on temperature, humidity, pressure): We are going to use the value at the altitude where our person is standing and then we scale it using the scale factor. Penndorf uses N_s = 2.54743e19, but i am not sure if it is applicable to our scenario. -# -# -# -# - -# Ok, so things work... but they are not super realistic. I think we should be updating the dispersion equation because Penndorf's paper is super old. We want the value of the index of refraction at the location of the observer - this is going to depend on the users inputs. -# -# https://refractiveindex.info/?shelf=other&book=air&page=Ciddor (1996) -# -# New calculation for refractive index (where lambda is in microns) -# -# This applies for : Standard air: dry air at 15 °C, 101.325 kPa and with 450 ppm CO2 content. -# -# $n - 1 = \frac{0.05792105}{238.0185 - \lambda^{-2}} + \frac{0.00167917}{57.362 - \lambda^{-2}}$ -# -# -# - -# Something really cool here is that we can use the color of the sunset to predict the weather: "Red at night, sailors delight. Red in morning, sailors take warning" -# When there is a bright red sunset at night, it means there is likely a high pressure cell off to the west. With west to east weather patterns, this means a high pressure cell is about to move in, high pressure = pleasant weather, "sailors delight". When there is a red sunrise in the east, it means the high pressure cell has already passed and a low pressure cell is likely to replace it. Low pressure = poor weather, "sailors take warning". -# # Low pressure: 720mmHg # High pressure: 780 mmHg @@ -131,8 +76,8 @@ wavelengths = Vec3(0.68, 0.55, 0.44) # These are the wavelengths in um @cython.cfunc def refraction_calculator(T:cython.int, P:cython.int) -> tuple[Vec3, Vec3]: alpha:cython.float = 0.00367 # in inverse Celsius - t_s:cython.int = 15 # degrees Celsius - p_s:cython.int = 760 # mmHg + t_s:cython.int = T # degrees Celsius + p_s:cython.int = P # mmHg n_s_values = [] # for debugging / comparing to the internet values n_values = [] @@ -304,6 +249,7 @@ def computeIncidentLight(direction:Vec3,betaM,g,observerEarthRadius) -> tuple[fl # Changing the * 20 number just changes the intensity os the light, it does not much change the colors themselves # Well the colors kinda change, but more they just get super pale or super not pale. 20 should be fine I guess + #print("sumR: {}, betaR: {}, phaseR {}\n, sumM {},betaM {}, phaseM {}\n".format(sumR,betaR,phaseR,sumM,betaM,phaseM)) final_color = (sumR * betaR * phaseR + sumM * betaM * phaseM) * 20 return final_color.to_tuple() @@ -343,12 +289,13 @@ def solveQuadratic(a:cython.float, b:cython.float, c:cython.float) -> tuple[cyth @cython.cfunc -def renderSkydome(filename): +def renderSkydome(filename,betaM,g,altitude): width:cython.int height:cython.int width, height = 256,256 image = np.zeros((height, width, 3), dtype=float) + observerEarthRadius = earthRadius + altitude start_time = time.time() # Start timing j:cython.int @@ -369,7 +316,7 @@ def renderSkydome(filename): theta = math.acos(1 - z2) # This changes for each pixel direction = Vec3(math.sin(theta) * math.cos(phi), math.cos(theta), math.sin(theta) * math.sin(phi)) - color = computeIncidentLight(direction) + color = computeIncidentLight(direction,betaM,g,observerEarthRadius) #color = computeIncidentLight(direction) @@ -389,7 +336,12 @@ def renderSkydome(filename): -def renderFromCamera(filename,betaM,g,altitude): +def renderFromCamera(filename,betaM,g,altitude,T,P): + + print(wavelengths) + n_s_values, n_values = refraction_calculator(T, P) + beta_values = beta(n_values) + betaR = Vec3(*beta_values.v) # Keeps it as a Vec3 observerEarthRadius = earthRadius + altitude width:cython.int @@ -411,24 +363,22 @@ def renderFromCamera(filename,betaM,g,altitude): numPixelSamples:cython.int = 4 for y in range(height): for x in range(width): -# for m in range(numPixelSamples): -# for n in range(numPixelSamples): -# rayx = (2 * (x + (m + random.uniform(0, 1)) / numPixelSamples) / float(width) - 1) * aspectRatio * angle -# rayy = (1 - (y + (n + random.uniform(0, 1)) / numPixelSamples) / float(height) * 2) * angle + if y > 70: + # horrible horrible hack, mainly because below the horizon its dark amnyway? + color = (np.nan,np.nan,np.nan) + image[y][x] = color + else: - rayx = (2 * x / float(width) - 1) * aspectRatio * angle - rayy = (1 - y / float(height) * 2) * angle + rayx = (2 * x / float(width) - 1) * aspectRatio * angle + rayy = (1 - y / float(height) * 2) * angle - #This changes for each pixel, it is the direction we are looking in - direction = Vec3(rayx, rayy, -1).normalize() - color = computeIncidentLight(direction,betaM,g,observerEarthRadius) - #image[y, x] = np.array(color) - image[y][x] = np.clip(color, 0, 1) - - #image[y, x] /= numPixelSamples * numPixelSamples # Average color + #This changes for each pixel, it is the direction we are looking in + direction = Vec3(rayx, rayy, -1).normalize() + color = computeIncidentLight(direction,betaM,g,observerEarthRadius) + #image[y, x] = np.array(color) + image[y][x] = np.clip(color, 0, 1) elapsed_time = time.time() - start_time - print("what") print(f"Renderinggg row {y + 1}/{height}, elapsed time: {elapsed_time:.2f} seconds") image = np.clip(image, 0, 1) * 255