Skip to content

Thick Rings Texture Python Script

Max Hyper edited this page Jun 3, 2025 · 3 revisions

The following python script can be executed to generate the textures for the thick rings from a base texture

import tkinter as tk
from tkinter import filedialog
from enum import Enum
import numpy as np
import cv2
import sys

class Direction(object):
    def __init__(self, name, xOffset, zOffset, axis, rotateClockWiseIndex):
        self.name = name
        self._xOffset = xOffset
        self._zOffset = zOffset
        self._axis = axis
        self._rotateClockWiseIndex = rotateClockWiseIndex

    @property
    def xOffset(self):
        return self._xOffset
    @property
    def zOffset(self):
        return self._zOffset
    @property
    def axis(self):
        return self._axis
    @property
    def rotateClockWiseIndex(self):
        return self._rotateClockWiseIndex

    def rotateY(dir):
        return Horizontals[dir.rotateClockWiseIndex]

Horizontals = [Direction("SOUTH", 0, 1, "Z", 1), Direction("WEST", -1, 0, "X", 2), Direction("NORTH", 0, -1, "Z", 3), Direction("EAST", 1, 0, "X", 0)]

def newImage (width, height):
    return np.zeros((width,height,3), np.uint8)

def setPixel (image, x, y, color):
    w, h, channels = image.shape
    if x >= 0 and x < w and y >= 0 and y < h:
        image[x,y] = color

def blit (imageTo, imageFrom, offX, offY, rotCW90):
    w, h, channels = imageFrom.shape
    rotation = rotCW90 & 3
    if rotation == 0:
        for y in range(h):
            for x in range(w):
                setPixel(imageTo, x + offX, y + offY, imageFrom[x,y])
        return
    if rotation == 1:
        for y in range(h):
            for x in range(w):
                destX = h - y - 1
                setPixel(imageTo, destX + offX, x + offY, imageFrom[x,y])
        return
    if rotation == 2:
        for y in range(h):
            for x in range(w):
                destX = w - x - 1
                destY = h - y - 1
                setPixel(imageTo, destX + offX, destY + offY, imageFrom[x,y])
        return
    if rotation == 3:
        for y in range(h):
            for x in range(w):
                destY = w - x - 1
                setPixel(imageTo, y + offX, destY + offY, imageFrom[x,y])
        return
    

def createBarklessAntecedent (baseBuffer):
    baseW, baseH, baseChannels = baseBuffer.shape
    antecedent = newImage(baseW,baseH)

    scale = int(baseW / 16)

    blit(antecedent, baseBuffer, 3*scale, 3*scale, 1)
    blit(antecedent, baseBuffer, -3*scale, 3*scale, 1)
    blit(antecedent, baseBuffer, 3*scale, -3*scale, 1)
    blit(antecedent, baseBuffer, -3*scale, -3*scale, 1)

    ringStrip = newImage(6*scale,scale)
    blit(ringStrip, baseBuffer, -5*scale, -3*scale, 0)
    blit(antecedent, ringStrip, 0*scale, 2*scale, -1)
    blit(antecedent, ringStrip, 15*scale, 8*scale, 1)

    blit(ringStrip, baseBuffer, -5*scale, -12*scale, 0)
    blit(antecedent, ringStrip, 0*scale, 8*scale, 1)
    blit(antecedent, ringStrip, 15*scale, 2*scale, -1)

    ringStrip = newImage(scale,6*scale)
    blit(ringStrip, baseBuffer, -3*scale, -5*scale, 0)
    blit(antecedent, ringStrip, 2*scale, 0*scale, -1)
    blit(antecedent, ringStrip, 8*scale, 15*scale, 1)

    blit(ringStrip, baseBuffer, -12*scale, -5*scale, 0)
    blit(antecedent, ringStrip, 8*scale, 0*scale, 1)
    blit(antecedent, ringStrip, 2*scale, 15*scale, -1)

    center = newImage(14*scale, 14*scale)
    blit(center, baseBuffer, -1*scale, -1*scale, 0)
    blit(antecedent, center, 1*scale, 1*scale, 0)

    return antecedent

def createMajorTexture(baseBuffer):
    baseW, baseH, baseChannels = baseBuffer.shape
    scale = int(baseW / 16)

    majorW = 3*baseW
    majorH = 3*baseH

    antec_image = createBarklessAntecedent(baseBuffer)
    major_image = newImage(majorW,majorH)

    corners = [0]*4
    edges = [0]*4
    for i in range(4):
        corners[i] = newImage(6*scale, 6*scale)
        edges[i] = newImage(4*scale, 6*scale)
        blit(corners[i], antec_image, 0, 0, i)
        blit(edges[i], antec_image, -6*scale, 0, i)

    centerX = 24
    centerY = 24
    for nesting in range(3):
        edge = 2
        imgSel = 0
        for dir in Horizontals:
            ovr = Direction.rotateY(dir)
            offX = dir.xOffset
            offY = dir.zOffset
            compX = (-6 if offX == 1 else 0) + (-2 if dir.axis == "Z" else 0)
            compY = (-6 if offY == 1 else 0) + (-2 if dir.axis == "X" else 0)
            startX = offX * (14 + nesting * 6)
            startY = offY * (14 + nesting * 6)
            for way in [-1, 1]:
                for i in range(4+nesting):
                    rowX = ovr.xOffset * i * way * 4
                    rowY = ovr.zOffset * i * way * 4
                    realX = centerX + startX + compX + rowX
                    realY = centerY + startY + compY + rowY
                    blit(major_image, edges[((imgSel * 13402141) >> 1) & 3], realX * scale, realY * scale, edge)
                    imgSel = imgSel+1
            edge = edge+1

    cornerX = [-1, 1, 1, -1]
    cornerY = [-1, -1, 1, 1]
    blit(major_image, antec_image, 16*scale, 16*scale, 0)
    for nesting in [1,2,3]:
        for corner in range(4):
            corner_pixels = corners[(corner + nesting) & 0x3 ]
            cX = cornerX[corner]
            cY = cornerY[corner]
            offX = cX * 6 * nesting + cX * 5
            offY = cY * 6 * nesting + cY * 5
            realX = 16 + 5 + offX
            realY = 16 + 5 + offY
            blit(major_image, corner_pixels, realX * scale, realY * scale, corner)

    cornersW = scale
    cornersH = scale
    edgesW = 14*scale
    edgesH = scale

    for i in range(4):
        corners[i] = newImage(cornersW, cornersH)
        edges[i] = newImage(edgesW, edgesH)
        blit(corners[i], baseBuffer, 0, 0, i)
        blit(edges[i], baseBuffer, -1*scale, 0, i)

    pixSel = 0
    for row in range(4):
        edge = edges[((pixSel * 13402141) >> 1) & 3]
        pixSel = pixSel+1
        span = edgesW
        blit(major_image, edge, (1 + row * span) * scale, 0, 0)
        blit(major_image, edge, (majorW - edgesH) * scale, (1 + row * span) * scale, 1)
        blit(major_image, edge, (majorW - 1 - span - row * span) * scale, (majorH - edgesH) * scale, 2)
        blit(major_image, edge, 0, (majorH - 1 - edgesW - row * span) * scale, 3)

    return major_image

tk.Tk().withdraw()

if len(sys.argv) > 1:
    paths = sys.argv
    del paths[0]
else:
    paths = filedialog.askopenfilenames()

for path in paths:
    print("Loading file {}".format(path))
    img = cv2.imread(path)
    if img is None:
        print("Unable to load file {}".format(path))
        exit()
    print("Generating texture")
    thick_img = createMajorTexture(img)
    path_split = path.split('.')
    thick_path = path_split[0] + "_thick." + path_split[1]
    cv2.imwrite(thick_path, thick_img)
    print("Successfully created texture {}".format(thick_path))
Clone this wiki locally