diff --git a/draw/Iinstruction.py b/draw/Iinstruction.py index b6f1719..40e1684 100644 --- a/draw/Iinstruction.py +++ b/draw/Iinstruction.py @@ -1,8 +1,8 @@ from typing import Iterable, List -from abc import abstractmethod +from abc import ABCMeta, abstractmethod from draw import code_to_image as cti -class Iinstruction: +class Iinstruction(metaclass=ABCMeta): """Base class for all instructions""" def __init__(self, instruction_text: str) -> None: @@ -13,16 +13,23 @@ class Iinstruction: pass @abstractmethod - def getblksize(self) -> float: - return self._getblksize() + def getblkheight(self) -> float: + pass + + @abstractmethod + def getblkwidth(self) -> float: + pass @abstractmethod def __str__(self) -> str: pass - def _getblksize(self) -> float: + def _getblkheight(self) -> float: return cti.get_text_size(self.instruction_text)[1] + def _getblkwidth(self) -> float: + return cti.get_text_size(self.instruction_text)[0]+50 #padding + class generic_instruction(Iinstruction): """Any instruction that is not a control structure""" @@ -31,7 +38,13 @@ class generic_instruction(Iinstruction): Iinstruction.__init__(self, instruction_text) def to_image(self, x:int, y:int, x_sz: int) -> Iterable[float]: - return cti.draw_generic_instruction(self.instruction_text, x, y, x_sz, self.getblksize()) + return cti.draw_generic_instruction(self.instruction_text, x, y, x_sz, self.getblkheight()) + + def getblkheight(self) -> float: + return self._getblkheight() + + def getblkwidth(self) -> float: + return self._getblkwidth() def __str__(self) -> str: return self.instruction_text @@ -48,29 +61,52 @@ class if_instruction(Iinstruction): self.true_case = true_case self.false_case = false_case - def get_truesize(self) -> float: + def get_trueheight(self) -> float: sz = 0.0 for inst in self.true_case: - sz += inst.getblksize() + sz += inst.getblkheight() return sz - def get_falsesize(self) -> float: + def get_falseheight(self) -> float: sz = 0.0 if self.false_case: for inst in self.false_case: - sz += inst.getblksize() + sz += inst.getblkwidth() return sz - def getblksize(self) -> float: - return self._getblksize() + max(self.get_truesize(), self.get_falsesize()) + def getblkheight(self) -> float: + return self._getblkheight() + max(self.get_trueheight(), self.get_falseheight()) + + def getblkwidth(self) -> float: + return max(self._getblkwidth(), self.get_truewidth() + self.get_falsewidth()) + + def get_truewidth(self) -> float: + w = 0.0 + + for inst in self.true_case: + w += inst.getblkwidth() + + return w + + def get_falsewidth(self) -> float: + w = 0.0 + + if self.false_case: + for inst in self.false_case: + w += inst.getblkwidth() + + return w + def to_image(self, x:int, y:int, x_sz: int) -> Iterable[float]: + true_w = self.get_truewidth() + false_w = self.get_falseheight() true_x, true_y, true_sz_x, _, false_x, false_y, false_sz_x, _ = cti.draw_if_statement( - self.instruction_text, x, y, x_sz, self.getblksize() + self.instruction_text, x, y, true_w, false_w, self.getblkheight() ) self.draw_true_case(true_x, true_y, true_sz_x) self.draw_false_case(false_x, false_y, false_sz_x) - blk_size = self.getblksize() + blk_size = self.getblkheight() return x, y + blk_size def draw_true_case(self, x: float, y:float, x_sz:float): @@ -118,26 +154,35 @@ class while_instruction_front(Iinstruction): Iinstruction.__init__(self, condition) self.child_instructions = instructions - def get_children_size(self) -> float: + def get_children_height(self) -> float: children_sz = 0 for inst in self.child_instructions: - children_sz += inst.getblksize() + children_sz += inst.getblkheight() return children_sz - def getblksize(self) -> float: - return self._getblksize() + self.get_children_size() + def get_children_width(self) -> float: + w = 0.0 + for inst in self.child_instructions: + w += inst.getblkheight() + return w + + def getblkheight(self) -> float: + return self._getblkheight() + self.get_children_height() + + def getblkwidth(self) -> float: + return max(self._getblkwidth(), self.get_children_width()) def to_image(self, x:int, y:int, x_sz: int) -> Iterable[float]: - children_x, children_y, children_sz_x = cti.draw_while_loop_front(self.instruction_text, x, y, x_sz, self.getblksize()) + children_x, children_y, children_sz_x = cti.draw_while_loop_front(self.instruction_text, x, y, x_sz, self.getblkheight()) self.draw_children(children_x, children_y, children_sz_x) - return x, y + self.getblksize() + return x, y + self.getblkheight() def draw_children(self, x:float, y:float, x_sz:float): for inst in self.child_instructions: x, y = inst.to_image(x, y, x_sz) - return self.get_children_size() + return self.get_children_height() def __str__(self) -> str: res = "while(" + self.instruction_text + "){\n" @@ -152,7 +197,7 @@ class while_instruction_back(while_instruction_front): while_instruction_front.__init__(self, condition, instructions) def to_image(self, x:int, y:int, x_sz: int): - children_x, children_y, children_sz_x = cti.draw_while_loop_back(self.instruction_text, x, y, x_sz, self.getblksize()) + children_x, children_y, children_sz_x = cti.draw_while_loop_back(self.instruction_text, x, y, x_sz, self.getblkheight()) self.draw_children(children_x, children_y, children_sz_x) return x, y + self.getblksize() diff --git a/draw/code_to_image.py b/draw/code_to_image.py index 19d2e26..ffde39e 100644 --- a/draw/code_to_image.py +++ b/draw/code_to_image.py @@ -51,28 +51,30 @@ def draw_generic_instruction(instruction: str, x, y, xsize, ysize) -> Iterable[f return x, y + ysize -def draw_if_statement(condition: str, x: int, y: int, xsize: int, ysize: int): +def draw_if_statement(condition: str, x: int, y: int, true_sz: int, false_sz: 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") text_y_size = font.getsize(condition)[1] - output_img.line((x,y) + (x + xsize / 2, y + text_y_size), fill=(0)) - output_img.line((x + xsize, y) + (x + xsize / 2, y + text_y_size), fill=(0)) - output_img.rectangle((x, y + text_y_size) + (x + xsize, y + ysize), outline=(0), width=1) - output_img.rectangle((x, y) + (x + xsize, y + text_y_size), outline=(0), width=1) - output_img.line((x + xsize / 2, y + text_y_size) + (x + xsize / 2, y + ysize), fill=(0)) + box_sz = true_sz + false_sz + + output_img.line((x,y) + (x + box_sz/2, y + text_y_size), fill=(0)) + output_img.line((x + box_sz, y) + (x + box_sz/2, y + text_y_size), fill=(0)) + output_img.rectangle((x, y + text_y_size) + (x + box_sz, y + ysize), outline=(0), width=1) + output_img.rectangle((x, y) + (x + box_sz, y + text_y_size), outline=(0), width=1) + output_img.line((x + true_sz, y + text_y_size) + (x + true_sz, y + ysize), fill=(0)) # condition text - output_img.multiline_text((x + xsize / 2, y + text_y_size / 2), condition, fill=(0), font=font, anchor="mm", spacing=4, align='right') + output_img.multiline_text((x + box_sz / 2, y + text_y_size / 2), condition, fill=(0), font=font, anchor="mm", spacing=4, align='right') # true / false output_img.text((x + 5, y + text_y_size), "true", font = font, fill = (0), anchor="ld") - output_img.text((x + xsize - 5, y + text_y_size), "false", font = font, fill = (0), anchor="rd") + output_img.text((x + box_sz - 5, y + text_y_size), "false", font = font, fill = (0), anchor="rd") - #first x,y,xsize,ysize of first box then of second first true and then false - return x, y + text_y_size, xsize / 2, ysize - text_y_size, x + xsize / 2, y + text_y_size, xsize / 2, ysize - text_y_size + #first x, y, xsize and ysize of "true" label then of "false" label + return x, y + text_y_size, true_sz, ysize - text_y_size, x + true_sz, y + text_y_size, false_sz, ysize - text_y_size def draw_while_loop_front(condition: str, x: int, y: int, xsize: int, ysize: int): @@ -118,4 +120,5 @@ def draw_while_loop_back(condition: str, x: int, y: int, xsize: int, ysize: int) def NSD_save(filepath: str): """Save the created file""" + filepath = filepath.removesuffix(".png") img.save(filepath + datei_endung ,"PNG") \ No newline at end of file diff --git a/gui/utils.py b/gui/utils.py index 9fb5cb1..2ed610b 100644 --- a/gui/utils.py +++ b/gui/utils.py @@ -24,7 +24,7 @@ def nassi(input_path: str, output_path: str, outputname: str, types, remove_tags custom_tags = {"comments" : comments, "ignore" : remove_tags, "types" : types} NSD.load_from_file(input_path, custom_tags) - NSD.convert_to_image(output_directory, on_conflict=behaviour, x_size=750) + NSD.convert_to_image(output_directory, on_conflict=behaviour) return output_directory diff --git a/interpreter/NassiShneidermann.py b/interpreter/NassiShneidermann.py index ce966a1..227b55b 100644 --- a/interpreter/NassiShneidermann.py +++ b/interpreter/NassiShneidermann.py @@ -1,3 +1,4 @@ +from os import stat from interpreter.interpret_source import Function_scope from typing import Dict, List, Optional import logging @@ -23,25 +24,29 @@ class NassiShneidermanDiagram: self.function_scopes: List[Function_scope] = [] self.init_logging(do_debug) - def init_logging(self, debug: bool): + @staticmethod + def init_logging(debug: bool): logLevel = logging.INFO if debug: logLevel = logging.DEBUG logging.basicConfig(level=logLevel) - def set_font(self, font_filepath: str): + @staticmethod + def set_font(font_filepath: str): cti.set_font(font_filepath) - def _save_scope(self, scope: Function_scope, output_path: str, x_size: int): - """DEBUGING ONLY""" + @staticmethod + def _save_scope(scope: Function_scope, output_path: str): image_y_sz = scope.get_height() + x_size = scope.get_width() with NSD_writer(output_path, x_size, image_y_sz): x, y = 0, 0 for instruction in scope.contents: x, y = instruction.to_image(x, y, x_size) - def check_conflicts(self, filepath:str, behavoiur: Overwrite_behaviour): + @staticmethod + def check_conflicts(filepath:str, behavoiur: Overwrite_behaviour): if os.path.exists(filepath + ".png"): if behavoiur == OB.SKIP: return None @@ -53,7 +58,7 @@ class NassiShneidermanDiagram: return filepath return filepath - def convert_to_image(self, output_path: str, on_conflict: Overwrite_behaviour=OB.SKIP, x_size: int=200): + def convert_to_image(self, output_path: str, on_conflict: Overwrite_behaviour=OB.SKIP): for scope in self.function_scopes: filepath = f"{output_path}/{scope.name}" filepath = self.check_conflicts(filepath, on_conflict) @@ -61,7 +66,7 @@ class NassiShneidermanDiagram: logging.info(f"Saving NSD to {filepath}.png...") try: - self._save_scope(scope, filepath, x_size) + self._save_scope(scope, filepath) except Exception as e: logging.error(f"Failed to save image {filepath} with error '{e}'") raise e diff --git a/interpreter/interpret_source.py b/interpreter/interpret_source.py index 15b8724..dce5a6a 100644 --- a/interpreter/interpret_source.py +++ b/interpreter/interpret_source.py @@ -25,9 +25,15 @@ class Function_scope(Iterable): def get_height(self) -> int: h = 0.0 for inst in self.contents: - h += inst.getblksize() + h += inst.getblkheight() return int(h) + def get_width(self) -> int: + w = 0.0 + for inst in self.contents: + w = max(w, inst.getblkwidth()) + return int(max(200, w)) + def __iter__(self): return self.contents.__iter__()