now possible to base files on each other

feat/atomation
ENDERZOMBI102 2 years ago
parent 74a88cf3af
commit cc121610d5

@ -1,12 +1,10 @@
name: 'blobfox' name: 'blobfox'
basedOff: basedOff: null
- null # all variants from 'blob' will be imported in this unit, unless they are already present
variants: variants:
- name: 'base' - name: 'base'
src: '../vector/blobfox.svg' src: '../vector/blobfox.svg'
- name: 'boop' - name: 'boop'
base: 'base'
objects: objects:
- type: svg - type: svg
src: 'resources/lgbtq_heart.svg' src: 'resources/lgbtq_heart.svg'

@ -0,0 +1,11 @@
name: 'neugeme'
basedOff: 'blobfox' # all variants from 'blobfox' will be imported in this unit, unless they are already present
variants:
- name: 'base'
overwrites:
- id: 'body'
color: 0x02FFFD
- id: 'hair'
color: 0x02FFFD
- id: 'blobfox'
remove: true

@ -6,10 +6,10 @@ from data.object import Object
from data.overwrite import Overwrite from data.overwrite import Overwrite
@dataclass(frozen=True) @dataclass
class Emote: class Emote:
name: str name: str
origin: str origin: list[ str ] #: the origins in order of definition
base: str | None = None base: str | None = None
src: Path | None = None src: Path | None = None
objects: list[Object] = None objects: list[Object] = None
@ -19,7 +19,7 @@ class Emote:
def load( cls, data: list[dict], origin: str ) -> list[Self]: def load( cls, data: list[dict], origin: str ) -> list[Self]:
return [ return [
Emote( Emote(
origin=origin, origin=[ origin ],
**entry | { **entry | {
'overwrites': Overwrite.load( entry.get( 'overwrites', [ ] ) ), 'overwrites': Overwrite.load( entry.get( 'overwrites', [ ] ) ),
'objects': Object.load( entry.get( 'objects', [ ] ) ) 'objects': Object.load( entry.get( 'objects', [ ] ) )

@ -5,7 +5,7 @@ from typing_extensions import Self
@dataclass(frozen=True) @dataclass(frozen=True)
class Overwrite: class Overwrite:
id: str id: str
color: str | None color: str | None = None
remove: bool = False remove: bool = False
@classmethod @classmethod

@ -1,52 +0,0 @@
from dataclasses import dataclass
from typing import Final
from collections.abc import Collection, Iterator
from data.emote import Emote
# noinspection PyFinal
@dataclass(frozen=True, slots=True)
class VariantList(Collection[Emote]):
""" An immutable variant list """
name: Final[ str ]
basedOff: Final[ list[ str ] ]
variants: Final[ tuple[Emote] ]
def __init__( self, name: str, variants: list[ dict ], basedOff: list[str] | None = None ) -> None:
object.__setattr__(
self,
'variants',
tuple( Emote.load( variants, name ) )
)
object.__setattr__( self, 'basesOff', basedOff or [] )
object.__setattr__( self, 'name', name )
def __iter__( self ) -> Iterator[Emote ]:
return self.variants.__iter__()
def __contains__( self, item: object ) -> bool:
if isinstance( item, str ):
for elem in self.variants:
if elem.name == item:
return True
return False
return item in self.variants
def __getitem__( self, item: int | str ) -> Emote:
if isinstance( item, int ):
return self.variants[ item ]
if isinstance( item, str ):
for elem in self.variants:
if elem.name == item:
return elem
raise KeyError( f'A variant with name "{item}" does not exist' )
raise ValueError( f'Invalid __getitem__ input: {item}' )
def __len__( self ) -> int:
return len( self.variants )
def __repr__( self ) -> str:
return f'VariantList{{name={self.name}, variants={repr(self.variants)}}}'

@ -0,0 +1,80 @@
from __future__ import annotations
import typing
from dataclasses import dataclass
from pathlib import Path
from typing import Final
from collections.abc import Collection, Iterator
import util
from data.emote import Emote
if typing.TYPE_CHECKING:
from generator import Generator
# noinspection PyFinal
@dataclass(frozen=True, slots=True)
class Variants( Collection[Emote] ):
""" An immutable variant list """
name: Final[ str ]
basedOff: Final[ tuple[ str ] ]
variants: Final[ tuple[ Emote ] ]
def __init__( self, path: Path, gen: Generator ) -> None:
data = util.load( path )
object.__setattr__( self, 'name', data[ 'name' ] )
object.__setattr__( self, 'basedOff', tuple( data[ 'basedOff' ] or [ ] ) )
if data['basedOff']:
# loading a set based off another, load the base one before this
base = gen.load( path.parent / f'{data["basedOff"]}.yml' )
# additional emotes may be defined
additional = [ ]
# apply overwrites and append new emotes to `additional`
for emote in Emote.load( data['variants'], self.name ):
if emote.name in base:
base._applyOverwrite( emote )
else:
additional += [ emote ]
# save the newly created set of emotes
object.__setattr__( self, 'variants', base.variants + tuple( additional ) )
else:
# loading a baseless set, just load it directly
object.__setattr__( self, 'variants', tuple( Emote.load( data['variants'], self.name ) ) )
def __iter__( self ) -> Iterator[Emote ]:
return self.variants.__iter__()
def __contains__( self, item: object ) -> bool:
if isinstance( item, str ):
for elem in self.variants:
if elem.name == item:
return True
return False
return item in self.variants
def __getitem__( self, item: int | str ) -> Emote:
if isinstance( item, int ):
return self.variants[ item ]
if isinstance( item, str ):
for elem in self.variants:
if elem.name == item:
return elem
raise KeyError( f'A variant with name "{item}" does not exist' )
raise ValueError( f'Invalid __getitem__ input: {item}' )
def __len__( self ) -> int:
return len( self.variants )
def _applyOverwrite( self, overwriter: Emote ) -> None:
emote = self[ overwriter.name ]
emote.origin += overwriter.origin
emote.overwrites += overwriter.overwrites
emote.objects += overwriter.objects

@ -4,25 +4,21 @@ import yaml
from cairosvg.surface import Surface from cairosvg.surface import Surface
from data.variantList import VariantList from data.variants import Variants
class Generator: class Generator:
declarations: dict[ str, VariantList ] declarations: dict[ str, Variants ]
surfaces: dict[ str, Surface ] surfaces: dict[ str, Surface ]
def __init__( self, declFile: Path ) -> None: def __init__( self, declFile: Path ) -> None:
if not declFile.exists(): if not declFile.exists():
raise FileNotFoundError('Declaration file does not exist!') raise FileNotFoundError('Declaration file does not exist!')
self.recursiveLoad( declFile ) self.declarations = { declFile.name[:-4]: self.load( declFile ) }
def recursiveLoad( self, declFile: Path ) -> None: def load( self, declFile: Path ) -> Variants:
variants = VariantList( **yaml.load( declFile.read_text(), yaml.FullLoader ) ) return Variants( declFile, self )
self.declarations[ variants.name ] = variants
if variants.basedOff is not None:
self.recursiveLoad( declFile.parent / f'{variants.basedOff}.yml' )
def generate( self, outputDir: Path = Path('.') ) -> None: def generate( self, outputDir: Path = Path('.') ) -> None:
""" """
@ -33,9 +29,8 @@ class Generator:
if not outputDir.exists(): if not outputDir.exists():
outputDir.mkdir() outputDir.mkdir()
for variant in self.variants: print( self.declarations['neugeme'] )
pass
if __name__ == '__main__': if __name__ == '__main__':
Generator( Path('./resources/blobfox.yml') ).generate( Path('./run') ) Generator( Path('./resources/neugeme.yml') ).generate( Path('./run') )

@ -0,0 +1,14 @@
from pathlib import Path
from typing import Any
import yaml
def load( path: Path ) -> Any:
""" Parse the first YAML document in a stream and produce the corresponding Python object. """
return yaml.load( path.read_text(), yaml.FullLoader )
def loads( data: str ) -> Any:
""" Parse the first YAML document in a string and produce the corresponding Python object. """
return yaml.load( data, yaml.FullLoader )
Loading…
Cancel
Save