Compare commits
	
		
			5 Commits
		
	
	
		
			dd2ffebca2
			...
			752cf011c3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 752cf011c3 | |||
| 7647f870a6 | |||
| 75d145096d | |||
| d61a707591 | |||
| 5a4d30e162 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,3 @@ | |||||||
| __pycache__ | __pycache__ | ||||||
| *.jpg | *.jpg | ||||||
|  | *.png | ||||||
| @@ -17,5 +17,8 @@ | |||||||
|     "margins": [20, 20, 20, 20], |     "margins": [20, 20, 20, 20], | ||||||
|     "arrowMargin": 4, |     "arrowMargin": 4, | ||||||
|     "valuesGap": 5, |     "valuesGap": 5, | ||||||
|     "arrowLabelDistance": 5 |     "arrowLabelDistance": 5, | ||||||
|  |     "forceDescsOnSide": false, | ||||||
|  |     "width": 1200, | ||||||
|  |     "height": 800 | ||||||
| } | } | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| import json | import json | ||||||
| import re | import re | ||||||
|  |  | ||||||
|  |  | ||||||
| class Config: | class Config: | ||||||
|     DEFAULT_FONT_FAMILY = "Ubuntu Mono" |     DEFAULT_FONT_FAMILY = "Ubuntu Mono" | ||||||
|     DEFAULT_FONT_SIZE = 16 |     DEFAULT_FONT_SIZE = 16 | ||||||
| @@ -21,6 +22,10 @@ class Config: | |||||||
|     ARROW_MARGIN = 4 |     ARROW_MARGIN = 4 | ||||||
|     VALUES_GAP = 5 |     VALUES_GAP = 5 | ||||||
|     ARROW_LABEL_DISTANCE = 5 |     ARROW_LABEL_DISTANCE = 5 | ||||||
|  |     FORCE_DESCS_ON_SIDE = False | ||||||
|  |     LEFT_LABELS = False | ||||||
|  |     WIDTH = 1200 | ||||||
|  |     HEIGHT = 800 | ||||||
|      |      | ||||||
|     def __init__(self, path: str = "config.json") -> None: |     def __init__(self, path: str = "config.json") -> None: | ||||||
|         self.load(path) |         self.load(path) | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								main.py
									
									
									
									
									
								
							| @@ -3,6 +3,7 @@ import os | |||||||
|  |  | ||||||
| from schema import InstructionSetSchema | from schema import InstructionSetSchema | ||||||
|  |  | ||||||
|  |  | ||||||
| description = """Examples: | description = """Examples: | ||||||
|     - Default theme (black on white): |     - Default theme (black on white): | ||||||
|         python main.py schema.xml -o out.jpg |         python main.py schema.xml -o out.jpg | ||||||
| @@ -15,20 +16,51 @@ description = """Examples: | |||||||
|  |  | ||||||
|     - Transparent background: |     - Transparent background: | ||||||
|         python main.py schema.xml -o out.png -c transparent.json |         python main.py schema.xml -o out.png -c transparent.json | ||||||
|  |      | ||||||
|  |     - Directory mode: | ||||||
|  |         python main.py -d -o images/ schemas/ | ||||||
| """ | """ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def processFile(inPath, outPath, confPath, display): | ||||||
|  |     schema = InstructionSetSchema(inPath, confPath, display) | ||||||
|  |     schema.save(outPath) | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     parser = argparse.ArgumentParser(description=description, formatter_class=argparse.RawTextHelpFormatter) |     parser = argparse.ArgumentParser(description=description, formatter_class=argparse.RawTextHelpFormatter) | ||||||
|     parser.add_argument("schema", help="Path to the schema description. Accepted formats are: YAML, JSON and XML") |     parser.add_argument("schema", help="Path to the schema description. Accepted formats are: YAML, JSON and XML") | ||||||
|     parser.add_argument("-o", "--output", help="Output path. By default, the output file will have the same name as the schema description with the extension .png") |     parser.add_argument("-o", "--output", help="Output path. By default, the output file will have the same name as the schema description with the extension .png") | ||||||
|     parser.add_argument("-c", "--config", help="Path to the config file", default="config.json") |     parser.add_argument("-c", "--config", help="Path to the config file", default="config.json") | ||||||
|     parser.add_argument("-D", "--display", help="Enable pygame display of the result", action="store_true") |     parser.add_argument("-D", "--display", help="Enable pygame display of the result", action="store_true") | ||||||
|  |     parser.add_argument("-d", "--directory", help="Enable directory mode. If set, the input and output paths are directories and all files inside the input directory are processed", action="store_true") | ||||||
|      |      | ||||||
|     args = parser.parse_args() |     args = parser.parse_args() | ||||||
|      |      | ||||||
|  |     if args.directory: | ||||||
|  |         if not os.path.isdir(args.schema): | ||||||
|  |             print(f"{args.schema} is not a directory") | ||||||
|  |             exit(-1) | ||||||
|  |          | ||||||
|  |         output = args.output | ||||||
|  |         if output is None: | ||||||
|  |             output = args.schema | ||||||
|  |          | ||||||
|  |         if not os.path.isdir(output): | ||||||
|  |             print(f"{output} is not a directory") | ||||||
|  |             exit(-1) | ||||||
|  |          | ||||||
|  |         paths = os.listdir(args.schema) | ||||||
|  |         for path in paths: | ||||||
|  |             inPath = os.path.join(args.schema, path) | ||||||
|  |             outPath = os.path.join(output, path) | ||||||
|  |             outPath = os.path.splitext(outPath)[0] + ".png" | ||||||
|  |              | ||||||
|  |             processFile(inPath, outPath, args.config, args.display) | ||||||
|  |      | ||||||
|  |     else: | ||||||
|         output = args.output |         output = args.output | ||||||
|         if output is None: |         if output is None: | ||||||
|             output = os.path.splitext(args.schema)[0] + ".png" |             output = os.path.splitext(args.schema)[0] + ".png" | ||||||
|          |          | ||||||
|     schema = InstructionSetSchema(args.schema, args.config, args.display) |         processFile(args.schema, output, args.config, args.display) | ||||||
|     schema.save(output) |  | ||||||
							
								
								
									
										3
									
								
								range.py
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								range.py
									
									
									
									
									
								
							| @@ -1,6 +1,8 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from typing import Union | from typing import Union | ||||||
|  |  | ||||||
|  |  | ||||||
| class Range: | class Range: | ||||||
|     def __init__(self, |     def __init__(self, | ||||||
|                  start: int, |                  start: int, | ||||||
| @@ -16,6 +18,7 @@ class Range: | |||||||
|         self.description = description |         self.description = description | ||||||
|         self.values = values |         self.values = values | ||||||
|         self.dependsOn = dependsOn |         self.dependsOn = dependsOn | ||||||
|  |         self.lastValueY = -1 | ||||||
|      |      | ||||||
|     @property |     @property | ||||||
|     def bits(self) -> int: |     def bits(self) -> int: | ||||||
|   | |||||||
							
								
								
									
										79
									
								
								renderer.py
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								renderer.py
									
									
									
									
									
								
							| @@ -1,28 +1,28 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| import os | import os | ||||||
| from typing import TYPE_CHECKING | from typing import TYPE_CHECKING | ||||||
| if TYPE_CHECKING: |  | ||||||
|     from config import Config |  | ||||||
|     from range import Range |  | ||||||
|     from schema import InstructionSetSchema |  | ||||||
|  |  | ||||||
| import pygame | import pygame | ||||||
|  |  | ||||||
| from structure import Structure | from structure import Structure | ||||||
| from vec import Vec | from vec import Vec | ||||||
|  |  | ||||||
| class Renderer: | if TYPE_CHECKING: | ||||||
|     WIDTH = 1200 |     from config import Config | ||||||
|     HEIGHT = 800 |     from range import Range | ||||||
|  |     from schema import InstructionSetSchema | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Renderer: | ||||||
|     def __init__(self, config: Config, display: bool = False) -> None: |     def __init__(self, config: Config, display: bool = False) -> None: | ||||||
|         self.config = config |         self.config = config | ||||||
|         self.display = display |         self.display = display | ||||||
|         pygame.init() |         pygame.init() | ||||||
|         if self.display: |         if self.display: | ||||||
|             self.win = pygame.display.set_mode([Renderer.WIDTH, Renderer.HEIGHT]) |             self.win = pygame.display.set_mode([self.config.WIDTH, self.config.HEIGHT]) | ||||||
|          |          | ||||||
|         self.surf = pygame.Surface([Renderer.WIDTH, Renderer.HEIGHT], pygame.SRCALPHA) |         self.surf = pygame.Surface([self.config.WIDTH, self.config.HEIGHT], pygame.SRCALPHA) | ||||||
|         self.font = pygame.font.SysFont(self.config.DEFAULT_FONT_FAMILY, self.config.DEFAULT_FONT_SIZE) |         self.font = pygame.font.SysFont(self.config.DEFAULT_FONT_FAMILY, self.config.DEFAULT_FONT_SIZE) | ||||||
|         self.italicFont = pygame.font.SysFont(self.config.ITALIC_FONT_FAMILY, self.config.ITALIC_FONT_SIZE, italic=True) |         self.italicFont = pygame.font.SysFont(self.config.ITALIC_FONT_FAMILY, self.config.ITALIC_FONT_SIZE, italic=True) | ||||||
|          |          | ||||||
| @@ -31,7 +31,12 @@ class Renderer: | |||||||
|     def render(self, schema: InstructionSetSchema) -> None: |     def render(self, schema: InstructionSetSchema) -> None: | ||||||
|         self.surf.fill(self.config.BACKGROUND_COLOR) |         self.surf.fill(self.config.BACKGROUND_COLOR) | ||||||
|          |          | ||||||
|         self.drawStructure(schema.structures["main"], schema.structures, self.margins[3], self.margins[0]) |         mainStruct: Structure = schema.structures["main"] | ||||||
|  |         ox = self.margins[3] | ||||||
|  |         if self.config.LEFT_LABELS: | ||||||
|  |             ox = self.config.WIDTH - self.margins[3] - mainStruct.bits * self.config.BIT_WIDTH | ||||||
|  |          | ||||||
|  |         self.drawStructure(schema.structures["main"], schema.structures, ox, self.margins[0]) | ||||||
|          |          | ||||||
|         if self.display: |         if self.display: | ||||||
|             name = os.path.basename(schema.path) |             name = os.path.basename(schema.path) | ||||||
| @@ -45,8 +50,7 @@ class Renderer: | |||||||
|      |      | ||||||
|     def drawStructure(self, |     def drawStructure(self, | ||||||
|                       struct: Structure, |                       struct: Structure, | ||||||
|                       structures: dict[str, |                       structures: dict[str, Structure], | ||||||
|                       Structure], |  | ||||||
|                       ox: float = 0, |                       ox: float = 0, | ||||||
|                       oy: float = 0) -> float: |                       oy: float = 0) -> float: | ||||||
|          |          | ||||||
| @@ -77,7 +81,21 @@ class Renderer: | |||||||
|                 pygame.draw.line(self.surf, borderCol, [bitX, bitsY], [bitX, bitsY + bitH]) |                 pygame.draw.line(self.surf, borderCol, [bitX, bitsY], [bitX, bitsY + bitH]) | ||||||
|          |          | ||||||
|         ranges = struct.getSortedRanges() |         ranges = struct.getSortedRanges() | ||||||
|         descX = ox + max(0, (struct.bits-12) * bitW) |         if self.config.LEFT_LABELS: | ||||||
|  |             ranges.reverse() | ||||||
|  |          | ||||||
|  |         if self.config.FORCE_DESCS_ON_SIDE: | ||||||
|  |             if self.config.LEFT_LABELS: | ||||||
|  |                 descX = self.config.WIDTH - self.margins[3] - structures["main"].bits * bitW | ||||||
|  |             else: | ||||||
|  |                 descX = self.margins[3] + structures["main"].bits * bitW | ||||||
|  |          | ||||||
|  |         else: | ||||||
|  |             if self.config.LEFT_LABELS: | ||||||
|  |                 descX = ox + struct.bits * bitW | ||||||
|  |             else: | ||||||
|  |                 descX = ox | ||||||
|  |          | ||||||
|         descY = bitsY + bitH * 2 |         descY = bitsY + bitH * 2 | ||||||
|          |          | ||||||
|         # Names + simple descriptions |         # Names + simple descriptions | ||||||
| @@ -123,10 +141,16 @@ class Renderer: | |||||||
|         bitH = self.config.BIT_HEIGHT |         bitH = self.config.BIT_HEIGHT | ||||||
|         arrowMargin = self.config.ARROW_MARGIN |         arrowMargin = self.config.ARROW_MARGIN | ||||||
|          |          | ||||||
|  |         if endX > startX: | ||||||
|  |             endX -= arrowMargin | ||||||
|  |          | ||||||
|  |         else: | ||||||
|  |             endX += arrowMargin | ||||||
|  |          | ||||||
|         pygame.draw.lines(self.surf, self.config.LINK_COLOR, False, [ |         pygame.draw.lines(self.surf, self.config.LINK_COLOR, False, [ | ||||||
|             [startX, startY + bitH*1.5], |             [startX, startY + bitH*1.5], | ||||||
|             [startX, endY + bitH/2], |             [startX, endY + bitH/2], | ||||||
|             [endX - arrowMargin, endY + bitH/2] |             [endX, endY + bitH/2] | ||||||
|         ]) |         ]) | ||||||
|      |      | ||||||
|     def drawDescription(self, |     def drawDescription(self, | ||||||
| @@ -140,6 +164,9 @@ class Renderer: | |||||||
|         bitW = self.config.BIT_WIDTH |         bitW = self.config.BIT_WIDTH | ||||||
|         bitH = self.config.BIT_HEIGHT |         bitH = self.config.BIT_HEIGHT | ||||||
|          |          | ||||||
|  |         if self.config.LEFT_LABELS: | ||||||
|  |             descX = min(descX, rStartX + rWidth/2 - bitW) | ||||||
|  |         else: | ||||||
|             descX = max(descX, rStartX + rWidth/2 + bitW) |             descX = max(descX, rStartX + rWidth/2 + bitW) | ||||||
|          |          | ||||||
|         self.drawUnderbracket(rStartX, rStartX + rWidth, rStartY) |         self.drawUnderbracket(rStartX, rStartX + rWidth, rStartY) | ||||||
| @@ -148,12 +175,17 @@ class Renderer: | |||||||
|         self.drawLink(midX, rStartY, descX, descY) |         self.drawLink(midX, rStartY, descX, descY) | ||||||
|          |          | ||||||
|         descTxt = self.font.render(range_.description, True, self.config.TEXT_COLOR) |         descTxt = self.font.render(range_.description, True, self.config.TEXT_COLOR) | ||||||
|         self.surf.blit(descTxt, [descX, descY + (bitH - descTxt.get_height())/2]) |         txtX = descX | ||||||
|  |          | ||||||
|  |         if self.config.LEFT_LABELS: | ||||||
|  |             txtX -= descTxt.get_width() | ||||||
|  |          | ||||||
|  |         self.surf.blit(descTxt, [txtX, descY + (bitH - descTxt.get_height())/2]) | ||||||
|          |          | ||||||
|         descY += descTxt.get_height() |         descY += descTxt.get_height() | ||||||
|          |          | ||||||
|         if range_.values is not None and range_.dependsOn is None: |         if range_.values is not None and range_.dependsOn is None: | ||||||
|             descX, descY = self.drawValues(range_.values, descX, descY) |             txtX, descY = self.drawValues(range_.values, txtX, descY) | ||||||
|          |          | ||||||
|         descY += self.config.DESCRIPTION_MARGIN |         descY += self.config.DESCRIPTION_MARGIN | ||||||
|          |          | ||||||
| @@ -191,8 +223,13 @@ class Renderer: | |||||||
|         rWidth = range_.bits * bitW |         rWidth = range_.bits * bitW | ||||||
|          |          | ||||||
|         self.drawUnderbracket(rStartX, rStartX + rWidth, bitsY) |         self.drawUnderbracket(rStartX, rStartX + rWidth, bitsY) | ||||||
|         prevY = bitsY + bitH * 1.5 |  | ||||||
|         dependRange = struct.ranges[range_.dependsOn] |         dependRange = struct.ranges[range_.dependsOn] | ||||||
|  |         prevRangeY = bitsY + bitH * 1.5 | ||||||
|  |         if dependRange.lastValueY == -1: | ||||||
|  |             prevDependY = bitsY + bitH * 1.5 | ||||||
|  |         else: | ||||||
|  |             prevDependY = dependRange.lastValueY | ||||||
|  |          | ||||||
|         dependStartI = struct.bits - dependRange.end - 1 |         dependStartI = struct.bits - dependRange.end - 1 | ||||||
|         dependStartX = bitsX + dependStartI * bitW |         dependStartX = bitsX + dependStartI * bitW | ||||||
|         dependWidth = dependRange.bits * bitW |         dependWidth = dependRange.bits * bitW | ||||||
| @@ -200,7 +237,7 @@ class Renderer: | |||||||
|         self.drawUnderbracket(dependStartX, dependStartX + dependWidth, bitsY) |         self.drawUnderbracket(dependStartX, dependStartX + dependWidth, bitsY) | ||||||
|          |          | ||||||
|         for val, data in sorted(range_.values.items(), key=lambda vd: vd[0]): |         for val, data in sorted(range_.values.items(), key=lambda vd: vd[0]): | ||||||
|             self.drawArrow(dependMid, prevY, dependMid, descY - arrowMargin) |             self.drawArrow(dependMid, prevDependY, dependMid, descY - arrowMargin) | ||||||
|              |              | ||||||
|             valRanges = {} |             valRanges = {} | ||||||
|             for i in range(dependRange.bits): |             for i in range(dependRange.bits): | ||||||
| @@ -232,11 +269,13 @@ class Renderer: | |||||||
|             self.drawArrow(x1, y, x2, y, data["description"]) |             self.drawArrow(x1, y, x2, y, data["description"]) | ||||||
|              |              | ||||||
|             self.drawArrow(rStartX + rWidth - bitW, |             self.drawArrow(rStartX + rWidth - bitW, | ||||||
|                            prevY, |                            prevRangeY, | ||||||
|                            rStartX + rWidth - bitW, |                            rStartX + rWidth - bitW, | ||||||
|                            descY + bitH - arrowMargin) |                            descY + bitH - arrowMargin) | ||||||
|  |  | ||||||
|             prevY = descY + bitH*2 + arrowMargin |             prevDependY = descY + bitH*2 + arrowMargin | ||||||
|  |             prevRangeY =prevDependY | ||||||
|  |             dependRange.lastValueY = prevDependY | ||||||
|             descY = self.drawStructure(structures[data["structure"]], structures, rStartX, descY) |             descY = self.drawStructure(structures[data["structure"]], structures, rStartX, descY) | ||||||
|          |          | ||||||
|         return (descX, descY) |         return (descX, descY) | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import json | import json | ||||||
| import os | import os | ||||||
|  |  | ||||||
| import yaml | import yaml | ||||||
|  |  | ||||||
| from config import Config | from config import Config | ||||||
| @@ -7,9 +8,11 @@ from renderer import Renderer | |||||||
| from structure import Structure | from structure import Structure | ||||||
| from xml_loader import XMLLoader | from xml_loader import XMLLoader | ||||||
|  |  | ||||||
|  |  | ||||||
| class UnsupportedFormatException(Exception): | class UnsupportedFormatException(Exception): | ||||||
|     ... |     ... | ||||||
|  |  | ||||||
|  |  | ||||||
| class InstructionSetSchema: | class InstructionSetSchema: | ||||||
|     VALID_EXTENSIONS = ("yaml", "json", "xml") |     VALID_EXTENSIONS = ("yaml", "json", "xml") | ||||||
|      |      | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from range import Range | from range import Range | ||||||
|  |  | ||||||
|  |  | ||||||
| class Structure: | class Structure: | ||||||
|     def __init__(self, |     def __init__(self, | ||||||
|                  name: str, |                  name: str, | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								vec.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								vec.py
									
									
									
									
									
								
							| @@ -1,6 +1,8 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from math import sqrt | from math import sqrt | ||||||
|  |  | ||||||
|  |  | ||||||
| class Vec: | class Vec: | ||||||
|     def __init__(self, x: float = 0, y: float = 0) -> None: |     def __init__(self, x: float = 0, y: float = 0) -> None: | ||||||
|         self.x = x |         self.x = x | ||||||
|   | |||||||
| @@ -1,7 +1,9 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
| from bs4 import BeautifulSoup |  | ||||||
| from typing import TYPE_CHECKING | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
|  | from bs4 import BeautifulSoup | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from io import TextIOWrapper |     from io import TextIOWrapper | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user