mirror of
https://github.com/ldericher/fftcgtool
synced 2025-01-15 15:02:59 +00:00
206 lines
6.3 KiB
Python
206 lines
6.3 KiB
Python
import queue
|
|
import threading
|
|
import logging
|
|
import json
|
|
|
|
from card import Card
|
|
from PIL import Image
|
|
|
|
|
|
# multithreaded card loading
|
|
class cardLoader(threading.Thread):
|
|
def __init__(self, opusid, queue, cards):
|
|
threading.Thread.__init__(self)
|
|
self.__opusid = opusid
|
|
self.__queue = queue
|
|
self.__cards = cards
|
|
|
|
def run(self):
|
|
logger = logging.getLogger(__name__)
|
|
while not self.__queue.empty():
|
|
# take next card number
|
|
cardid = self.__queue.get()
|
|
|
|
# try to load that card
|
|
card = Card()
|
|
if card.load(self.__opusid, cardid):
|
|
# success!
|
|
self.__cards.append(card)
|
|
logger.info("found %s" % (card))
|
|
else:
|
|
# seems like the end of this opus!
|
|
logger.info("exiting")
|
|
break
|
|
if self.__queue.empty():
|
|
logger.warn("empty queue, maxsize might be too small!")
|
|
|
|
|
|
# multithreaded card image loading
|
|
class imageLoader(threading.Thread):
|
|
def __init__(self, queue, composite, composite_lock, grid, resolution):
|
|
threading.Thread.__init__(self)
|
|
self.__queue = queue
|
|
self.__composite = composite
|
|
self.__lock = composite_lock
|
|
self.__grid = grid
|
|
self.__resolution = resolution
|
|
|
|
def run(self):
|
|
logger = logging.getLogger(__name__)
|
|
# shorthands
|
|
r, c = self.__grid # rows and columns per sheet
|
|
w, h = self.__resolution # width, height per card
|
|
|
|
while not self.__queue.empty():
|
|
# take next card
|
|
i, card = self.__queue.get()
|
|
|
|
# fetch card image
|
|
logger.info("get image for card %s" % (card))
|
|
im = card.get_image(self.__resolution)
|
|
|
|
# paste image in correct position
|
|
self.__lock.acquire()
|
|
x, y = (i % c) * w, (i // c) * h
|
|
logger.info("paste image %s at P%d(%d, %d)" % (im.mode, i, x, y))
|
|
self.__composite.paste(im, (x, y, x+w, y+h))
|
|
self.__lock.release()
|
|
|
|
# image is processed
|
|
self.__queue.task_done()
|
|
|
|
|
|
class Opus:
|
|
def load(self, opusid, maxsize=500, threadnum=16):
|
|
self._opusid = opusid
|
|
self._cards = []
|
|
|
|
# enqueue all card ids
|
|
idQueue = queue.Queue()
|
|
for cardid in range(1, maxsize + 1):
|
|
idQueue.put(cardid)
|
|
|
|
# start multithreading, wait for finish
|
|
threads = [cardLoader(opusid, idQueue, self._cards) for _ in range(threadnum)]
|
|
for thread in threads:
|
|
thread.start()
|
|
for thread in threads:
|
|
thread.join()
|
|
|
|
# sort every element alphabetically
|
|
self._cards.sort(key=lambda x: x._cardid)
|
|
self._cards.sort(key=lambda x: x._name)
|
|
self._cards.sort(key=lambda x: x._element)
|
|
|
|
def __get_sheets(self, grid):
|
|
# cards per sheet
|
|
count = grid[0]*grid[1] - 1
|
|
# flat copy
|
|
cards = self._cards
|
|
|
|
# while there are cards
|
|
while cards:
|
|
# get a chunk
|
|
yield cards[:count]
|
|
# remove that chunk
|
|
cards = cards[count:]
|
|
|
|
def get_images(self, grid, resolution, threadnum=16):
|
|
logger = logging.getLogger(__name__)
|
|
# shorthands
|
|
r, c = grid # rows and columns per sheet
|
|
w, h = resolution # width, height per card
|
|
|
|
for sheet in self.__get_sheets(grid):
|
|
# enqueue a sheet of cards
|
|
cardQueue = queue.Queue()
|
|
for i, card in enumerate(sheet):
|
|
cardQueue.put((i, card))
|
|
|
|
# add hidden face
|
|
hidden = Card()
|
|
hidden.load(0, 0)
|
|
cardQueue.put((r * c - 1, hidden))
|
|
|
|
# create a new card sheet
|
|
sheet = Image.new("RGB", (c*w, r*h))
|
|
logger.info("New image: %dx%d" % sheet.size)
|
|
|
|
# beware concurrent paste
|
|
sheet_lock = threading.Lock()
|
|
|
|
# start multithreading, wait for finish
|
|
for _ in range(threadnum):
|
|
imageLoader(cardQueue, sheet, sheet_lock, grid, resolution).start()
|
|
cardQueue.join()
|
|
|
|
# sheet image is generated, return now
|
|
yield sheet
|
|
|
|
def get_json(self, deckname, grid, cardfilter, faceurls):
|
|
# shorthands
|
|
r, c = grid # rows and columns per sheet
|
|
|
|
# get BackURL
|
|
back = Card()
|
|
back.load(0, 0)
|
|
backurl = back._iurl
|
|
|
|
jsondict = { "ObjectStates": [ {
|
|
"Name": "Deck",
|
|
"Nickname": "Opus %d %s" % (self._opusid, deckname),
|
|
"Description": "",
|
|
|
|
"Transform": {
|
|
"scaleX": 2.17822933,
|
|
"scaleY": 1.0,
|
|
"scaleZ": 2.17822933
|
|
},
|
|
|
|
"Locked": False,
|
|
"Grid": True,
|
|
"Snap": True,
|
|
"Autoraise": True,
|
|
"Sticky": True,
|
|
"Tooltip": True,
|
|
"GridProjection": False,
|
|
"Hands": False,
|
|
"SidewaysCard": False,
|
|
|
|
"DeckIDs": [],
|
|
"CustomDeck": {},
|
|
"ContainedObjects": []
|
|
} ] }
|
|
|
|
for sheetnum, sheet in enumerate(self.__get_sheets(grid)):
|
|
# get current face
|
|
faceurl = faceurls[sheetnum]
|
|
# first sheet is "CustomDeck 1"
|
|
sheetnum = sheetnum + 1
|
|
|
|
# recurring sheet dictionary
|
|
sheetdict = { str(sheetnum): {
|
|
"FaceURL": faceurl,
|
|
"BackURL": backurl,
|
|
"NumWidth": c,
|
|
"NumHeight": r
|
|
} }
|
|
|
|
for cardnum, card in enumerate(sheet):
|
|
if not cardfilter(card):
|
|
continue
|
|
|
|
# cardid 123 is on "CustomDeck 1", 3rd row, 4th column
|
|
cardid = sheetnum * 100 + (cardnum // c) * 10 + (cardnum % c) * 1
|
|
jsondict["ObjectStates"][0]["DeckIDs"].append(cardid)
|
|
|
|
# Add card object to ContainedObjects
|
|
carddict = card.get_dict()
|
|
carddict["CardID"] = cardid
|
|
carddict["CustomDeck"] = sheetdict
|
|
jsondict["ObjectStates"][0]["ContainedObjects"].append(carddict)
|
|
|
|
# Add sheet to CustomDecks
|
|
jsondict["ObjectStates"][0]["CustomDeck"].update(sheetdict)
|
|
|
|
return json.dumps(jsondict, indent=2)
|