treeform / pixie

Full-featured 2d graphics library for Nim.
MIT License
742 stars 28 forks source link

SVG radialGradient defs/url support #535

Open konsumer opened 1 year ago

konsumer commented 1 year ago

Hi, I really like your ideas from fidget, but I was getting a bunch of errors with exported code (and even your more complex included demos) so I thought I would try to load the SVG on it's own, so at least the static parts would be all set, and I could add in dynamic parts. I am trying to make a UI (for a steamdeck-OS looking app) using an SVG outputted from figma:

details

And I get errors like this:

Error: unhandled exception: Missing SVG resource paint0_radial_3_427 [PixieError]

For things like this:

<rect width="1440" height="900" fill="url(#paint0_radial_3_427)" fill-opacity="0.2"/>

That are in <defs> like this:

<radialGradient id="paint0_radial_3_427" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(48 106) rotate(23.0285) scale(1304.99 2087.99)">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</radialGradient>

I thought it might be that it was using urls before the <defs> (figma puts them at the end) but re-arranging it did not help. If I remove all the fill="url(#WHATEVER)" stuff, it works, but is missing all the fills, obviously.

Here is the code I am using (window-scaling stuff from boxy#56):

import boxy, opengl, windy

let windowSize = ivec2(1440, 900)
let window = newWindow("Boxy Example", windowSize)
makeContextCurrent(window)
loadExtensions()

let bxy = newBoxy()

bxy.addImage("details", readImage("details.svg")) 

let ratio = windowSize.x / windowSize.y
var scale = 1.0
var offset = vec2(0, 0)
var vs:Vec2
let ws = windowSize.vec2

window.onFrame = proc() =
  vs = window.size.vec2
  if vs.x > (vs.y * ratio):
    scale = vs.y / ws.y
    offset.x = (vs.x - (ws.x * scale)) / 2
    offset.y = 0
  else:
    scale = vs.x / ws.x
    offset.y = (vs.y - (ws.y * scale)) / 2
    offset.x = 0

  bxy.beginFrame(window.size)
  bxy.saveTransform()
  bxy.translate(offset)
  bxy.scale(scale)

  bxy.drawImage("details", vec2(0, 0))

  bxy.restoreTransform()
  bxy.endFrame()
  window.swapBuffers()

while not window.closeRequested:
  pollEvents()
konsumer commented 1 year ago

I think I could definitely break this into a bunch of draw commands by hand, and for this UI it might work better (interactions, images loaded from other sources, etc) but it would be cool to just import all the static parts at once, complete with gradients and stuff.

konsumer commented 1 year ago

This is the working SVG version:

details

Same code runs fine, just stripped out the url/defs that were having trouble, and I removed the embedded images to cut down on the size. Seems like they are both radialGradient defs.

konsumer commented 1 year ago

I think it's definitely just gradients. This simple SVG fails, too:

<svg width="380" height="236" viewBox="0 0 380 236" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="380" height="236" fill="url(#paint0_radial_1_3)"/>
<defs>
<radialGradient id="paint0_radial_1_3" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(242 62) rotate(89.3772) scale(92.0054 148.144)">
<stop stop-color="#D01010"/>
<stop offset="1" stop-color="#D9D9D9" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

And this:

<svg width="380" height="236" viewBox="0 0 380 236" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="380" height="236" fill="url(#paint0_linear_1_3)"/>
<defs>
<linearGradient id="paint0_linear_1_3" x1="245" y1="160" x2="245" y2="236" gradientUnits="userSpaceOnUse">
<stop stop-color="#D01010"/>
<stop offset="1" stop-color="#D9D9D9" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>