added support for full dynamic sizing of instruction blocks
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
from typing import Iterable, List
|
from typing import Iterable, List
|
||||||
from abc import abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
from draw import code_to_image as cti
|
from draw import code_to_image as cti
|
||||||
|
|
||||||
class Iinstruction:
|
class Iinstruction(metaclass=ABCMeta):
|
||||||
"""Base class for all instructions"""
|
"""Base class for all instructions"""
|
||||||
|
|
||||||
def __init__(self, instruction_text: str) -> None:
|
def __init__(self, instruction_text: str) -> None:
|
||||||
@@ -13,16 +13,23 @@ class Iinstruction:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def getblksize(self) -> float:
|
def getblkheight(self) -> float:
|
||||||
return self._getblksize()
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def getblkwidth(self) -> float:
|
||||||
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _getblksize(self) -> float:
|
def _getblkheight(self) -> float:
|
||||||
return cti.get_text_size(self.instruction_text)[1]
|
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):
|
class generic_instruction(Iinstruction):
|
||||||
"""Any instruction that is not a control structure"""
|
"""Any instruction that is not a control structure"""
|
||||||
@@ -31,7 +38,13 @@ class generic_instruction(Iinstruction):
|
|||||||
Iinstruction.__init__(self, instruction_text)
|
Iinstruction.__init__(self, instruction_text)
|
||||||
|
|
||||||
def to_image(self, x:int, y:int, x_sz: int) -> Iterable[float]:
|
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:
|
def __str__(self) -> str:
|
||||||
return self.instruction_text
|
return self.instruction_text
|
||||||
@@ -48,29 +61,52 @@ class if_instruction(Iinstruction):
|
|||||||
self.true_case = true_case
|
self.true_case = true_case
|
||||||
self.false_case = false_case
|
self.false_case = false_case
|
||||||
|
|
||||||
def get_truesize(self) -> float:
|
def get_trueheight(self) -> float:
|
||||||
sz = 0.0
|
sz = 0.0
|
||||||
for inst in self.true_case:
|
for inst in self.true_case:
|
||||||
sz += inst.getblksize()
|
sz += inst.getblkheight()
|
||||||
return sz
|
return sz
|
||||||
|
|
||||||
def get_falsesize(self) -> float:
|
def get_falseheight(self) -> float:
|
||||||
sz = 0.0
|
sz = 0.0
|
||||||
if self.false_case:
|
if self.false_case:
|
||||||
for inst in self.false_case:
|
for inst in self.false_case:
|
||||||
sz += inst.getblksize()
|
sz += inst.getblkwidth()
|
||||||
return sz
|
return sz
|
||||||
|
|
||||||
def getblksize(self) -> float:
|
def getblkheight(self) -> float:
|
||||||
return self._getblksize() + max(self.get_truesize(), self.get_falsesize())
|
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]:
|
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(
|
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_true_case(true_x, true_y, true_sz_x)
|
||||||
self.draw_false_case(false_x, false_y, false_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
|
return x, y + blk_size
|
||||||
|
|
||||||
def draw_true_case(self, x: float, y:float, x_sz:float):
|
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)
|
Iinstruction.__init__(self, condition)
|
||||||
self.child_instructions = instructions
|
self.child_instructions = instructions
|
||||||
|
|
||||||
def get_children_size(self) -> float:
|
def get_children_height(self) -> float:
|
||||||
children_sz = 0
|
children_sz = 0
|
||||||
for inst in self.child_instructions:
|
for inst in self.child_instructions:
|
||||||
children_sz += inst.getblksize()
|
children_sz += inst.getblkheight()
|
||||||
return children_sz
|
return children_sz
|
||||||
|
|
||||||
def getblksize(self) -> float:
|
def get_children_width(self) -> float:
|
||||||
return self._getblksize() + self.get_children_size()
|
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]:
|
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)
|
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):
|
def draw_children(self, x:float, y:float, x_sz:float):
|
||||||
for inst in self.child_instructions:
|
for inst in self.child_instructions:
|
||||||
x, y = inst.to_image(x, y, x_sz)
|
x, y = inst.to_image(x, y, x_sz)
|
||||||
return self.get_children_size()
|
return self.get_children_height()
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
res = "while(" + self.instruction_text + "){\n"
|
res = "while(" + self.instruction_text + "){\n"
|
||||||
@@ -152,7 +197,7 @@ class while_instruction_back(while_instruction_front):
|
|||||||
while_instruction_front.__init__(self, condition, instructions)
|
while_instruction_front.__init__(self, condition, instructions)
|
||||||
|
|
||||||
def to_image(self, x:int, y:int, x_sz: int):
|
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)
|
self.draw_children(children_x, children_y, children_sz_x)
|
||||||
return x, y + self.getblksize()
|
return x, y + self.getblksize()
|
||||||
|
|
||||||
|
|||||||
@@ -51,28 +51,30 @@ def draw_generic_instruction(instruction: str, x, y, xsize, ysize) -> Iterable[f
|
|||||||
|
|
||||||
return x, y + ysize
|
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"""
|
"""Draw an if statement into the NSD"""
|
||||||
if not output_img:
|
if not output_img:
|
||||||
raise Exception("Output image was not initialized! Make sure to call NSD_init first")
|
raise Exception("Output image was not initialized! Make sure to call NSD_init first")
|
||||||
|
|
||||||
text_y_size = font.getsize(condition)[1]
|
text_y_size = font.getsize(condition)[1]
|
||||||
|
|
||||||
output_img.line((x,y) + (x + xsize / 2, y + text_y_size), fill=(0))
|
box_sz = true_sz + false_sz
|
||||||
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.line((x,y) + (x + box_sz/2, y + text_y_size), fill=(0))
|
||||||
output_img.rectangle((x, y) + (x + xsize, y + text_y_size), outline=(0), width=1)
|
output_img.line((x + box_sz, y) + (x + box_sz/2, y + text_y_size), fill=(0))
|
||||||
output_img.line((x + xsize / 2, y + text_y_size) + (x + xsize / 2, y + ysize), 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
|
# 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
|
# true / false
|
||||||
output_img.text((x + 5, y + text_y_size), "true", font = font, fill = (0), anchor="ld")
|
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
|
#first x, y, xsize and ysize of "true" label then of "false" label
|
||||||
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
|
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):
|
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):
|
def NSD_save(filepath: str):
|
||||||
"""Save the created file"""
|
"""Save the created file"""
|
||||||
|
filepath = filepath.removesuffix(".png")
|
||||||
img.save(filepath + datei_endung ,"PNG")
|
img.save(filepath + datei_endung ,"PNG")
|
||||||
@@ -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}
|
custom_tags = {"comments" : comments, "ignore" : remove_tags, "types" : types}
|
||||||
|
|
||||||
NSD.load_from_file(input_path, custom_tags)
|
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
|
return output_directory
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from os import stat
|
||||||
from interpreter.interpret_source import Function_scope
|
from interpreter.interpret_source import Function_scope
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
import logging
|
import logging
|
||||||
@@ -23,25 +24,29 @@ class NassiShneidermanDiagram:
|
|||||||
self.function_scopes: List[Function_scope] = []
|
self.function_scopes: List[Function_scope] = []
|
||||||
self.init_logging(do_debug)
|
self.init_logging(do_debug)
|
||||||
|
|
||||||
def init_logging(self, debug: bool):
|
@staticmethod
|
||||||
|
def init_logging(debug: bool):
|
||||||
logLevel = logging.INFO
|
logLevel = logging.INFO
|
||||||
if debug:
|
if debug:
|
||||||
logLevel = logging.DEBUG
|
logLevel = logging.DEBUG
|
||||||
|
|
||||||
logging.basicConfig(level=logLevel)
|
logging.basicConfig(level=logLevel)
|
||||||
|
|
||||||
def set_font(self, font_filepath: str):
|
@staticmethod
|
||||||
|
def set_font(font_filepath: str):
|
||||||
cti.set_font(font_filepath)
|
cti.set_font(font_filepath)
|
||||||
|
|
||||||
def _save_scope(self, scope: Function_scope, output_path: str, x_size: int):
|
@staticmethod
|
||||||
"""DEBUGING ONLY"""
|
def _save_scope(scope: Function_scope, output_path: str):
|
||||||
image_y_sz = scope.get_height()
|
image_y_sz = scope.get_height()
|
||||||
|
x_size = scope.get_width()
|
||||||
with NSD_writer(output_path, x_size, image_y_sz):
|
with NSD_writer(output_path, x_size, image_y_sz):
|
||||||
x, y = 0, 0
|
x, y = 0, 0
|
||||||
for instruction in scope.contents:
|
for instruction in scope.contents:
|
||||||
x, y = instruction.to_image(x, y, x_size)
|
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 os.path.exists(filepath + ".png"):
|
||||||
if behavoiur == OB.SKIP:
|
if behavoiur == OB.SKIP:
|
||||||
return None
|
return None
|
||||||
@@ -53,7 +58,7 @@ class NassiShneidermanDiagram:
|
|||||||
return filepath
|
return filepath
|
||||||
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:
|
for scope in self.function_scopes:
|
||||||
filepath = f"{output_path}/{scope.name}"
|
filepath = f"{output_path}/{scope.name}"
|
||||||
filepath = self.check_conflicts(filepath, on_conflict)
|
filepath = self.check_conflicts(filepath, on_conflict)
|
||||||
@@ -61,7 +66,7 @@ class NassiShneidermanDiagram:
|
|||||||
logging.info(f"Saving NSD to {filepath}.png...")
|
logging.info(f"Saving NSD to {filepath}.png...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._save_scope(scope, filepath, x_size)
|
self._save_scope(scope, filepath)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Failed to save image {filepath} with error '{e}'")
|
logging.error(f"Failed to save image {filepath} with error '{e}'")
|
||||||
raise e
|
raise e
|
||||||
|
|||||||
@@ -25,9 +25,15 @@ class Function_scope(Iterable):
|
|||||||
def get_height(self) -> int:
|
def get_height(self) -> int:
|
||||||
h = 0.0
|
h = 0.0
|
||||||
for inst in self.contents:
|
for inst in self.contents:
|
||||||
h += inst.getblksize()
|
h += inst.getblkheight()
|
||||||
return int(h)
|
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):
|
def __iter__(self):
|
||||||
return self.contents.__iter__()
|
return self.contents.__iter__()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user