2024-04-17 15:45:37 +02:00
|
|
|
from argparse import ArgumentParser
|
2024-04-15 20:40:15 +02:00
|
|
|
from csv import reader as csv_reader
|
2024-04-17 15:33:10 +02:00
|
|
|
from pprint import pformat
|
|
|
|
from xml.dom.minidom import parseString as xml_parser
|
2024-04-17 21:23:26 +02:00
|
|
|
from xml.etree.ElementTree import Element as XMLElement
|
|
|
|
from xml.etree.ElementTree import SubElement as XMLSubElement
|
|
|
|
from xml.etree.ElementTree import tostring as element_stringifier
|
2024-04-15 20:40:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
class System:
|
|
|
|
"""Represents an L-System"""
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
name: str,
|
2024-04-17 15:37:25 +02:00
|
|
|
base: list[str],
|
2024-04-15 20:40:15 +02:00
|
|
|
axiom: str,
|
|
|
|
substitutions: list[tuple[str, str]],
|
|
|
|
interpretations: list[tuple[str, str]],
|
|
|
|
):
|
|
|
|
self.name = name
|
|
|
|
"""System name"""
|
|
|
|
|
|
|
|
self.base = base
|
|
|
|
"""Set used"""
|
|
|
|
|
|
|
|
self.axiom = axiom
|
|
|
|
"""Axiom used at the start of iterations"""
|
|
|
|
|
|
|
|
self.substitutions = substitutions
|
|
|
|
"""
|
|
|
|
Substitution for each member of the base,
|
|
|
|
represented as a couple (member, substitution)
|
|
|
|
"""
|
|
|
|
|
|
|
|
self.interpretations = interpretations
|
|
|
|
"""
|
|
|
|
Interpretation for each member of the base,
|
|
|
|
represented as a couple (member, interpretation)
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Interpretation of extra symbols added if necessary
|
|
|
|
extra_symbols = ["[", "]"]
|
|
|
|
for _, substitution in substitutions:
|
|
|
|
if any(symbol in substitution for symbol in extra_symbols):
|
|
|
|
self.interpretations.extend(
|
|
|
|
[(extra_symbols[0], "STORE"), (extra_symbols[1], "RESTORE")]
|
|
|
|
)
|
|
|
|
break
|
|
|
|
|
|
|
|
def __repr__(self):
|
2024-04-26 21:22:36 +02:00
|
|
|
return pformat(object=self.__dict__, compact=True, width=120, sort_dicts=False)
|
2024-04-15 20:40:15 +02:00
|
|
|
|
2024-04-17 21:23:26 +02:00
|
|
|
def to_xml(self) -> XMLElement:
|
2024-04-17 15:33:10 +02:00
|
|
|
"""Convert the current system into an XML element"""
|
2024-04-17 21:23:26 +02:00
|
|
|
system = XMLElement("lsystem")
|
2024-04-17 15:33:10 +02:00
|
|
|
|
2024-04-17 21:23:26 +02:00
|
|
|
base = XMLSubElement(system, "name")
|
2024-04-17 21:18:36 +02:00
|
|
|
base.text = self.name
|
|
|
|
|
2024-04-17 21:23:26 +02:00
|
|
|
base = XMLSubElement(system, "base")
|
2024-04-17 15:33:10 +02:00
|
|
|
base.text = "".join(self.base)
|
|
|
|
|
2024-04-17 21:23:26 +02:00
|
|
|
axiom = XMLSubElement(system, "axiom")
|
2024-04-17 15:33:10 +02:00
|
|
|
axiom.text = self.axiom
|
|
|
|
|
2024-04-17 21:23:26 +02:00
|
|
|
substitutions = XMLSubElement(system, "substitutions")
|
2024-04-17 15:33:10 +02:00
|
|
|
for member, substitution in self.substitutions:
|
2024-04-17 21:23:26 +02:00
|
|
|
sub_element = XMLSubElement(substitutions, "substitution")
|
2024-04-17 15:33:10 +02:00
|
|
|
sub_element.set("member", member)
|
|
|
|
sub_element.text = substitution
|
|
|
|
|
2024-04-17 21:23:26 +02:00
|
|
|
interpretations = XMLSubElement(system, "interpretations")
|
2024-04-17 15:33:10 +02:00
|
|
|
for member, interpretation in self.interpretations:
|
2024-04-17 21:23:26 +02:00
|
|
|
inter_element = XMLSubElement(interpretations, "interpretation")
|
2024-04-17 15:33:10 +02:00
|
|
|
inter_element.set("member", member)
|
|
|
|
inter_element.text = interpretation
|
|
|
|
|
|
|
|
return system
|
|
|
|
|
2024-04-15 20:40:15 +02:00
|
|
|
|
|
|
|
def data_reader(path: str, delimiter: str = ","):
|
|
|
|
"""Read a CSV file and returns a list of L-System"""
|
|
|
|
res: list[System] = []
|
|
|
|
with open(path) as csv_file:
|
|
|
|
data = csv_reader(csv_file, delimiter=delimiter)
|
|
|
|
for system in data:
|
|
|
|
name = system[0]
|
2024-04-17 15:37:25 +02:00
|
|
|
base = list(system[1])
|
2024-04-15 20:40:15 +02:00
|
|
|
axiom = system[2]
|
|
|
|
substitutions = [(v, system[3 + i]) for i, v in enumerate(base)]
|
|
|
|
interpretations = [
|
|
|
|
(v, system[3 + i + len(substitutions)]) for i, v in enumerate(base)
|
|
|
|
]
|
|
|
|
|
|
|
|
res.append(System(name, base, axiom, substitutions, interpretations))
|
2024-04-17 15:37:25 +02:00
|
|
|
|
2024-04-15 20:40:15 +02:00
|
|
|
return res
|
|
|
|
|
|
|
|
|
2024-04-17 21:23:26 +02:00
|
|
|
def lsystems_xml(systems: list[System]) -> XMLElement:
|
2024-04-17 15:33:10 +02:00
|
|
|
"""Convert list of L-system structure into XML"""
|
|
|
|
|
2024-04-17 21:23:26 +02:00
|
|
|
root = XMLElement("lsystems")
|
2024-04-17 15:33:10 +02:00
|
|
|
|
|
|
|
for system in systems:
|
|
|
|
root.append(system.to_xml())
|
|
|
|
|
|
|
|
return root
|
|
|
|
|
|
|
|
|
2024-04-15 20:40:15 +02:00
|
|
|
if __name__ == "__main__":
|
2024-04-17 15:45:37 +02:00
|
|
|
parser = ArgumentParser(description="Generate XML representation of L-systems")
|
|
|
|
parser.add_argument(
|
|
|
|
"file", metavar="FILE", help="CSV file containing L-system data"
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"-o", "--output", metavar="OUTPUT_FILE", help="Specify output file for XML"
|
|
|
|
)
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
2024-04-17 15:33:10 +02:00
|
|
|
|
|
|
|
# Read data
|
2024-04-17 15:45:37 +02:00
|
|
|
lsystems = data_reader(args.file)
|
2024-04-15 20:40:15 +02:00
|
|
|
|
2024-04-17 15:33:10 +02:00
|
|
|
# Generate XML
|
|
|
|
xml = lsystems_xml(lsystems)
|
2024-04-15 20:45:09 +02:00
|
|
|
|
2024-04-26 21:22:36 +02:00
|
|
|
# Add XML model declaration
|
2024-05-05 18:36:42 +02:00
|
|
|
xml_string = (
|
|
|
|
'<?xml-model href="l-systems.xsd"?>\n' + element_stringifier(xml).decode()
|
|
|
|
)
|
2024-04-26 21:22:36 +02:00
|
|
|
|
2024-04-17 15:45:37 +02:00
|
|
|
# Output XML
|
2024-04-26 21:22:36 +02:00
|
|
|
dom = xml_parser(xml_string)
|
2024-05-06 14:43:07 +02:00
|
|
|
pretty_xml = dom.toprettyxml(indent=" ", encoding="UTF-8").decode()
|
2024-04-17 15:45:37 +02:00
|
|
|
if args.output:
|
|
|
|
with open(args.output, "w") as output_file:
|
|
|
|
output_file.write(pretty_xml)
|
|
|
|
else:
|
|
|
|
print(pretty_xml)
|