All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.primogemstudio.advancedfmk.fontengine.gen.SVGQueue.kt Maven / Gradle / Ivy

package com.primogemstudio.advancedfmk.fontengine.gen

import com.primogemstudio.advancedfmk.fontengine.CharGlyph
import com.primogemstudio.advancedfmk.util.conic
import com.primogemstudio.advancedfmk.util.cubic
import org.joml.Vector2f
import java.util.*

class SVGQueue(private val dimension: Vector2f) : Vector() {
    fun toVertices(precision: Int): MultiPolygon {
        fun MutableList.addN(d: Vector2f) {
            if (isEmpty()) add(d)
            if (get(size - 1) != d) add(d)
        }

        val polygons = MultiPolygon(dimension)

        val vertices = mutableListOf()
        val spl = {
            polygons.add(Polygon(ArrayList(vertices)))
            vertices.clear()
        }
        var trig = false
        forEach {
            when (it.type) {
                SVGOperation.OpType.MOVE -> {
                    if (trig) spl()
                    else trig = true

                    vertices.addN(it.target)
                }

                SVGOperation.OpType.LINE -> vertices.addN(it.target)
                SVGOperation.OpType.CONIC -> {
                    val pos = vertices[vertices.size - 1]
                    for (i in 0 until precision) {
                        vertices.addN(conic(pos, it.control1!!, it.target, i.toFloat() / precision))
                    }
                }

                SVGOperation.OpType.CUBIC -> {
                    val pos = vertices[vertices.size - 1]
                    for (i in 0 until precision) {
                        vertices.addN(cubic(pos, it.control1!!, it.control2!!, it.target, i.toFloat() / precision))
                    }
                }
            }
        }
        spl()

        val areas = polygons.toMutableList()
        val infos = mutableListOf()
        var currentDepth = 0

        while (areas.isNotEmpty()) {
            for (i in areas) {
                var isTop = true
                for (j in areas) {
                    if (i != j && j.contains(i)) {
                        isTop = false
                    }
                }
                if (isTop) infos.add(infos.firstOrNull { it.depth == currentDepth - 1 && it.poly.contains(i) }?.bindNew(i)?: PolygonInfo(i, 0, null))
            }
            areas.removeIf { infos.map { p -> p.poly }.contains(it) }
            currentDepth ++
        }
        polygons.removeIf { true }
        polygons.addAll(infos.filter { it.depth % 2 == 0 }.map { pi ->
            infos.filter { it.depth == pi.depth + 1 && it.parent == pi.poly }.map { it.poly }.forEach {
                pi.poly.vertices.addAll(it.vertices)
                pi.poly.holes.add(it)
            }
            pi.poly
        })

        return polygons
    }
}

data class PolygonInfo(
    val poly: Polygon,
    val depth: Int,
    val parent: Polygon?
) {
    fun bindNew(p: Polygon): PolygonInfo = PolygonInfo(p, depth + 1, poly)
}

class MultiPolygon(private val dimension: Vector2f) : Vector() {
    fun bake(): CharGlyph {
        val vertices = mutableListOf()
        val indices = mutableListOf()
        var base = 0
        forEach { n ->
            vertices.addAll(n.vertices.map { it.div(1024f) })
            indices.addAll(n.toTriangles().map { it + base })
            base = vertices.size
        }

        return CharGlyph(dimension, vertices.toTypedArray(), indices.toIntArray())
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy