diff --git a/Iinstruction.py b/Iinstruction.py new file mode 100644 index 0000000..7561dad --- /dev/null +++ b/Iinstruction.py @@ -0,0 +1,135 @@ +import code_to_image as cti +from typing import Iterable, List +from abc import abstractmethod + +from code_to_image import NSD_init, NSD_save + + +class Iinstruction: + """Base class for all instructions""" + + def __init__(self, instruction_text: str) -> None: + self.instruction_text = instruction_text + + @abstractmethod + def to_image(self, x:float, y:float, x_sz: float, y_sz: float) -> Iterable[float]: + pass + + +class generic_instruction(Iinstruction): + """Any instruction that is not a control structure""" + + def __init__(self, instruction_text: str) -> None: + Iinstruction.__init__(self, instruction_text) + + def to_image(self, x:int, y:int, x_sz: int, y_sz: int) -> Iterable[float]: + new_x, new_y = cti.draw_generic_instruction(self.instruction_text, x, y, x_sz, y_sz) + return new_x, new_y + + + +class if_instruction(Iinstruction): + """Conditional structure + NOT. + A. + LOOP + """ + + def __init__(self, instruction_text: str, true_case: List[Iinstruction], false_case: List[Iinstruction]=None) -> None: + Iinstruction.__init__(self, instruction_text) + self.true_case = true_case + self.false_case = false_case + + def to_image(self, x:int, y:int, x_sz: int, y_sz: int) -> Iterable[float]: + true_x, true_y, true_sz_x, true_sz_y, false_x, false_y, false_sz_x, false_sz_y = cti.draw_if_statement( + self.instruction_text, + x, y, x_sz, y_sz) + + true_blk_sz = self.draw_true_case(true_x, true_y, true_sz_x, true_sz_y) + false_blk_sz = self.draw_false_case(false_x, false_y, false_sz_x, false_sz_y) + blk_sz = max(true_blk_sz, false_blk_sz) + return x, true_y + blk_sz + + def draw_true_case(self, x: float, y:float, x_sz:float, y_sz:float) -> float: + start_y = y + y_sz /= len(self.true_case) + for instruction in self.true_case: + x, y = instruction.to_image(x, y, x_sz, y_sz) + return y - start_y + + def draw_false_case(self, x: float, y:float, x_sz:float, y_sz:float) -> float: + start_y = y + if self.false_case: + y_sz /= len(self.false_case) + for instruction in self.false_case: + x, y = instruction.to_image(x, y, x_sz, y_sz) + return y - start_y + +#TODO +# class switch_instruction(Iinstruction): +# """Switch structure""" + +# def __init__(self, instruction_text: str, cases: List[List[Iinstruction]]) -> None: +# Iinstruction.__init__(self, instruction_text) +# self.child_cases = cases + +# def to_image(self, x:int, y:int, x_sz: int, y_sz: int) -> Iterable[float]: +# """TODO: implement""" +# return [] + +# def draw_children(self, x:float, y:float, x_sz:float, y_sz:float) -> float: +# """TODO: implement""" +# return 0.0 + + + +class while_instruction_front(Iinstruction): + + def __init__(self, condition: str, instructions: List[Iinstruction]) -> None: + Iinstruction.__init__(self, condition) + self.child_instructions = instructions + + def to_image(self, x:int, y:int, x_sz: int, y_sz: int): + children_x, children_y, children_sz_x, children_sz_y = cti.draw_while_loop_front(self.instruction_text, x, y, x_sz, y_sz) + blk_size = self.draw_children(children_x, children_y, children_sz_x, children_sz_y) + return x, y + blk_size + + def draw_children(self, x:float, y:float, x_sz:float, y_sz:float) -> float: + y_sz /= len(self.child_instructions) + for instruction in self.child_instructions: + x, y = instruction.to_image(x, y, x_sz, y_sz) + return y_sz + + + +class while_instruction_back(while_instruction_front): + def __init__(self, condition: str, instructions: List[Iinstruction]) -> None: + while_instruction_front.__init__(self, condition, instructions) + + def to_image(self, x:int, y:int, x_sz: int, y_sz: int): + children_x, children_y, children_sz_x, children_sz_y = cti.draw_while_loop_back(self.instruction_text, x, y, x_sz, y_sz) + blk_size = self.draw_children(children_x, children_y, children_sz_x, children_sz_y) + return x, y + blk_size + + def draw_children(self, x:float, y:float, x_sz:float, y_sz:float) -> float: + y_sz /= len(self.child_instructions) + for instruction in self.child_instructions: + x, y = instruction.to_image(x, y, x_sz, y_sz) + return y_sz + + +if __name__ == "__main__": + """Debugging""" + test = if_instruction("shouldNiet()", [ + generic_instruction("Niet()"), + generic_instruction("Niet()"), + generic_instruction("Niet()"), + generic_instruction("Niet()"), + ], [ + generic_instruction("hiet()"), + generic_instruction("hiet()"), + if_instruction("shouldNiet()", [ generic_instruction("hiet()") ], [generic_instruction("hiet()")]), + ]) + NSD_init(500, 500) + test.to_image(0, 0, 250, 500) + NSD_save("Iinstruction") \ No newline at end of file diff --git a/NassiShneidermann.py b/NassiShneidermann.py new file mode 100644 index 0000000..a5f3d67 --- /dev/null +++ b/NassiShneidermann.py @@ -0,0 +1,63 @@ +from code_to_image import NSD_save +from Iinstruction import Iinstruction +import logging +import re + +class NassiShneidermanDiagram: + + def __init__(self, debug: bool=False) -> None: + self.instructions: dict[str, Iinstruction] = {} + self.init_logging(debug) + + def init_logging(self, debug: bool): + logLevel = logging.INFO + if debug: + logLevel = logging.DEBUG + + logging.basicConfig(level=logLevel) + + def add_instruction(self, instruction: Iinstruction): + instruction_key = "instruction#" + str(len(self.instructions)) + self.instructions[instruction_key] = instruction + logging.debug("added instruction %s : %s", instruction_key, instruction.instruction_text) + + def convert_to_image(self, filename: str, x_size=200): + logging.info(f"Saving NSD to {filename}.png") + cti.NSD_init(x_size, 1000) + x, y, x_sz = 0, 0, x_size + for _k, instruction in self.instructions.items(): + x, y = instruction.to_image(x, y, x_sz, 200) + cti.NSD_save(filename) + + @staticmethod + def load_code_lines(filepath): + lines = [] + try: + with open(filepath) as file: + for _line in file: + line:str = _line.strip() + if line and not re.match(r"""^//|^#|^COMMENT|^--""", line): + lines.append(line) + except: + logging.error(f"Failed to open input path {filepath}!") + + return lines + + def load_from_file(self, filepath: str): + filtered_lines = self.load_code_lines(filepath) + print(filtered_lines) + num_brace + for line in filtered_lines: + if line.startswith("while("): + + +if __name__ == "__main__": + """for debugging""" + + from Iinstruction import * + + NSD = NassiShneidermanDiagram(True) + + NSD.load_from_file("res/input/input.java") + + NSD.convert_to_image("Nina", 500) diff --git a/code_to_image.py b/code_to_image.py new file mode 100644 index 0000000..a5ee50c --- /dev/null +++ b/code_to_image.py @@ -0,0 +1,115 @@ +import sys +from typing import Iterable +from PIL import Image, ImageDraw, ImageFont + +output_dir = "res/output/" +datei_endung = ".png" + +img = None +output_img = None +font = None + + +def NSD_init(x: float, y: float): + #get input_img + global img, output_img, font + #img = Image.open(input_dir + file_name + datei_endung) + img = Image.new("RGB", (x, y), "white") + output_img = ImageDraw.Draw(img) + #font = ImageFont.load_default() + font = ImageFont.truetype("res/fonts/NotoSans-Regular.ttf", 12) + +def draw_generic_instruction(instruction: str, x, y, xsize, ysize) -> Iterable[float]: + if not output_img: + raise Exception("Output image was not initialized! Make sure to call NSD_init first") + + #size shit + text_y_size = font.getsize(instruction, direction="ltr")[1] + ysize = max(text_y_size, ysize) # ensure it is alway at least big enought to fit the text + + #draw shit + output_img.rectangle((x,y) + (x + xsize, y + ysize), outline=(0), width=1) + + #text shit + output_img.multiline_text((x + 5, y + ysize * .5), instruction, font=font, anchor="lm", align="right", fill=(0)) + + return x, y + ysize + + + + +def draw_if_statement(condition: str, x: int, y: int, xsize: int, ysize: int): + """Draw an if statement into the NSD""" + if not output_img: + raise Exception("Output image was not initialized! Make sure to call NSD_init first") + + output_img.line((x,y) + (x + xsize / 2, y + ysize / 4), fill=(0)) + output_img.line((x + xsize, y) + (x + xsize / 2, y + ysize / 4), fill=(0)) + output_img.rectangle((x, y + ysize / 4) + (x + xsize, y + ysize), outline=(0), width=1) + output_img.rectangle((x, y) + (x + xsize, y + ysize / 4), outline=(0), width=1) + output_img.line((x + xsize / 2, y + ysize / 4) + (x + xsize / 2, y + ysize), fill=(0)) + + # condition text + output_img.multiline_text((x + xsize / 2, y + ysize * .05 ), condition, fill=(0), font=font, anchor="mm", spacing=4, align='right') + + # true / false + output_img.text((x + 5, y + ysize * .1875), "true", font = font, fill = (0), anchor="lm") + output_img.text((x + xsize - 5, y + ysize * .1875), "false", font = font, fill = (0), anchor="rm") + + #first x,y,xsize,ysize of first box then of second first true and then false + return x, y + ysize / 4, xsize / 2, ysize * .75, x + xsize / 2, y + ysize / 4, xsize / 2, ysize * .75 + +def draw_while_loop_front(condition: str, x: int, y: int, xsize: int, ysize: int): + + if not output_img: + raise Exception("Output image was not initialized! Make sure to call NSD_init first") + + #ole #TODO + + #the box + output_img.line((x,y) + (x + xsize, y), fill=(0)) + output_img.line((x,y) + (x, y + ysize), fill=(0)) + output_img.line((x + xsize * .1, y + ysize * .1) + (x + xsize, y + ysize * .1), fill=(0)) + output_img.line((x + xsize, y) + (x + xsize, y + ysize * .1), fill=(0)) + output_img.line((x, y + ysize) + (x + xsize * .1, y + ysize ), fill=(0)) + output_img.line((x + xsize * .1, y + ysize) + (x + xsize * .1, y + ysize * .1), fill=(0)) + + #the text + output_img.text((x + xsize * .1, y + ysize * .025), condition, font = font, fill = (0), anchor="lm") + + #the x, y offset then the x,y draw size (the canvas) + return x + xsize * .1, y + ysize * .1, xsize * .9, ysize * .9 + +def draw_while_loop_back(condition: str, x: int, y: int, xsize: int, ysize: int): + + if not output_img: + raise Exception("Output image was not initialized! Make sure to call NSD_init first") + + #ole #TODO + + #the box + output_img.line((x,y) + (x + xsize * .1, y), fill=0) + output_img.line((x + xsize * .1, y) + (x + xsize * .1, y + ysize * .9), fill=0) + output_img.line((x + xsize * .1, y + ysize * .9) + (x + xsize, y + ysize * .9), fill=0) + output_img.line((x + xsize, y + ysize * .9) + (x + xsize, y + ysize), fill=0) + output_img.line((x,y + ysize) + (x + xsize, y + ysize), fill=0) + output_img.line((x,y) + (x, y + ysize), fill=0) + + #the text + output_img.text((x + xsize * .1, y + ysize * .95), condition, font = font, fill = (0), anchor="lm") + + #the x, y offset then the x,y draw size (the canvas) + return x + xsize * .1, y, xsize * .9, ysize * .9 + +def NSD_save(filename: str): + """Save the created file""" + img.save(output_dir + filename + datei_endung , "PNG") + +#x_offset , y_offset, x_size, y_size = draw_while_loop("lol", 0, 0, 100, 200) +if __name__ == "__main__": + """Debugging :^)""" + NSD_init(300, 500) + #draw_if_statement("wenn das dann mach das", 0, 0, 100, 200) + #print(x,y,xsize,ysize) + #draw_generic_instruction(r"""Wolfgang.fuck("Nina")""", x, y, xsize, ysize) + NSD_save("testink") \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..013da5f --- /dev/null +++ b/main.py @@ -0,0 +1,67 @@ + +import PySimpleGUI as sg +import os.path + + +file_list_column = [ + [ + sg.Text('Image Folder'), + sg.In(size=(25, 1), enable_events=True, key="-FOLDER-"), + sg.FolderBrowse(), + ], + [ + sg.Listbox( + values=[], enable_events=True, size=(40, 20), key="-FILE LIST-" + ) + ], +] + +diagramm_viewer_column = [ + [sg.Text("Choose your Code from the left: ")], + [sg.Text(size=(40, 1), key="-TOUT-")], + [sg.Image(key='-IMAGE-')], +] + + + +layout = [ + [ + sg.Column(file_list_column), + sg.VSeparator(), + sg.Column(diagramm_viewer_column), + ] +] + +window = sg.Window('Nassi Viewer', layout) + +while True: + event, values = window.read() + + if event == 'Exit' or event == sg.WIN_CLOSED: + break + + if event == '-FOLDER-': + folder = values['-FOLDER-'] + try: + file_list = os.listdir(folder) + except: + file_list = [] + fnames = [ + f + for f in file_list + if os.path.isfile(os.path.join(folder, f)) + and f.lower().endswith(('.png', '.gif')) + ] + window['-FILE LIST-'].update(fnames) + elif event == '-FILE LIST-': + try: + filename = os.path.join( + values["-FOLDER-"], values["-FILE LIST-"][0] + ) + window["-TOUT-"].update(filename) + window["-IMAGE-"].update(filename=filename) + + except: + pass + +window.close() diff --git a/res/fonts/NotoSans-Bold.ttf b/res/fonts/NotoSans-Bold.ttf new file mode 100644 index 0000000..6f46d69 Binary files /dev/null and b/res/fonts/NotoSans-Bold.ttf differ diff --git a/res/fonts/NotoSans-BoldItalic.ttf b/res/fonts/NotoSans-BoldItalic.ttf new file mode 100644 index 0000000..6f46d69 Binary files /dev/null and b/res/fonts/NotoSans-BoldItalic.ttf differ diff --git a/res/fonts/NotoSans-Italic.ttf b/res/fonts/NotoSans-Italic.ttf new file mode 100644 index 0000000..6f46d69 Binary files /dev/null and b/res/fonts/NotoSans-Italic.ttf differ diff --git a/res/fonts/NotoSans-Regular.ttf b/res/fonts/NotoSans-Regular.ttf new file mode 100644 index 0000000..6f46d69 Binary files /dev/null and b/res/fonts/NotoSans-Regular.ttf differ diff --git a/res/fonts/arial.ttf b/res/fonts/arial.ttf new file mode 100644 index 0000000..6f46d69 Binary files /dev/null and b/res/fonts/arial.ttf differ diff --git a/res/input/Wolfgang.png b/res/input/Wolfgang.png new file mode 100644 index 0000000..6f46d69 Binary files /dev/null and b/res/input/Wolfgang.png differ diff --git a/res/input/input.java b/res/input/input.java new file mode 100644 index 0000000..3f49a82 --- /dev/null +++ b/res/input/input.java @@ -0,0 +1,46 @@ +#comment +//comment +COMMENT this is a comment +--comment + +fahre(); +fahre(); + +while(shouldNiet()) { + niet(); + niet(); + if(true) { + niet(); + niet(); + } +} +niet(); +niet(); + +// drehe("links"); +// while(huegelVorhanden("rechts")) +// { +// gesteinSammeln(); +// fahre(); +// } +// drehe("rechts"); +// gesteinSammeln(); + +// fahre(); +// while(huegelVorhanden("rechts")) +// { +// gesteinSammeln(); +// fahre(); +// } +// drehe("rechts"); +// gesteinSammeln(); +// fahre(); +// while(huegelVorhanden("rechts")) +// { +// gesteinSammeln(); +// if(!huegelVorhanden("vorne")) +// { +// fahre(); +// } +// } +// drehe("rechts"); \ No newline at end of file diff --git a/res/output/Iinstruction.png b/res/output/Iinstruction.png new file mode 100644 index 0000000..6f46d69 Binary files /dev/null and b/res/output/Iinstruction.png differ diff --git a/res/output/Nina.png b/res/output/Nina.png new file mode 100644 index 0000000..6f46d69 Binary files /dev/null and b/res/output/Nina.png differ diff --git a/res/output/testink.png b/res/output/testink.png new file mode 100644 index 0000000..6f46d69 Binary files /dev/null and b/res/output/testink.png differ