Quantcast
The ring theory part two - Page 5 - WoWInterface
Thread Tools Display Modes
05-27-16, 03:32 PM   #81
sirann
A Flamescale Wyrmkin
Join Date: Mar 2007
Posts: 142
Originally Posted by semlar View Post
I agree with zork, it would make a lot more sense to generate your animation texture in the shape that you want than trying to fix it in-game.

It is possible to do what you want using rotations, but I don't think I can explain how to combine matrix transformations well enough to someone who doesn't already know something about matrices to make sense of it.

Essentially you would have to apply your translation, which is where you move the texture to a particular location (one of the 60 frames of your animation), then apply a rotation matrix centered around that point.

This page explains how to do the math involved (2 dimensional rotation), but really without some background on this subject already you're going to have a hard time applying it to what you're trying to do.

I would only do it this way if you really have to have your textures look like this and you can't generate them in the shape that you want.
When your addon requires knowledge of what I believe would be fourier transformations (right, rotating a square to make a circular pattern?) it can probably be done more efficiently :P
  Reply With Quote
05-27-16, 03:43 PM   #82
syncrow
A Flamescale Wyrmkin
 
syncrow's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 149
There is an issues with zork's method:
  • pre-shaping such textures works for fixed bar sizes.
  • Resizing the bar (different unit frame sizes with different healthbars....will crop the animation terribly
  • The animation will also be compressed during statusbar transition

I tested out something like this:



...to fix the problem with multiple sizes. Using that method, results in fixated widths in relation to its height to guarantee pixelperfekt 45 cropping. (failure)

Thx for that link btw, i will check that!
__________________

Last edited by syncrow : 05-27-16 at 03:46 PM.
  Reply With Quote
05-27-16, 10:17 PM   #83
lightspark
A Rage Talon Dragon Guard
 
lightspark's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2012
Posts: 328
Originally Posted by syncrow View Post
The animation will also be compressed during statusbar transition
You have to create an invisible statusbar, that'll be used to do the maths, and add a texture that'll be your fake statusbar, and then crop it via SetTexCoord, when you set invisible statusbar values.

Sry, wrong link there, just woke up T_T
__________________

Last edited by lightspark : 05-27-16 at 10:29 PM.
  Reply With Quote
05-28-16, 01:00 AM   #84
syncrow
A Flamescale Wyrmkin
 
syncrow's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 149
Originally Posted by lightspark View Post
You have to create an invisible statusbar, that'll be used to do the maths, and add a texture that'll be your fake statusbar, and then crop it via SetTexCoord, when you set invisible statusbar values.
That doesn't fix the whole issue =/
__________________
  Reply With Quote
07-09-16, 08:29 AM   #85
Infus
A Deviate Faerie Dragon
Join Date: Jul 2016
Posts: 13
Originally Posted by semlar View Post
I wanted to post here because I came up with a technique based on the one outlined in this topic which can be applied to any source texture, rather than relying on semi-circles.
And somehow this broke in Legion. I'm not at the bottom of all the problems, but I have something that works on Legion.

Code:
-- Usage:
-- spinner = CreateSpinner(parent)
-- spinner:SetTexture('texturePath')
-- spinner:SetBlendMode('blendMode')
-- spinner:SetVertexColor(r, g, b)
-- spinner:SetClockwise(boolean) -- true to fill clockwise, false to fill counterclockwise
-- spinner:SetReverse(boolean) -- true to empty the bar instead of filling it
-- spinner:SetValue(percent) -- value between 0 and 1 to fill the bar to

-- Some math stuff
local cos, sin, pi2, halfpi = math.cos, math.sin, math.rad(360), math.rad(90)
local function Transform(tx, x, y, angle, aspect) -- Translates texture to x, y and rotates about its center
    local c, s = cos(angle), sin(angle)
    local y, oy = y / aspect, 0.5 / aspect
    local ULx, ULy = 0.5 + (x - 0.5) * c - (y - oy) * s, (oy + (y - oy) * c + (x - 0.5) * s) * aspect
    local LLx, LLy = 0.5 + (x - 0.5) * c - (y + oy) * s, (oy + (y + oy) * c + (x - 0.5) * s) * aspect
    local URx, URy = 0.5 + (x + 0.5) * c - (y - oy) * s, (oy + (y - oy) * c + (x + 0.5) * s) * aspect
    local LRx, LRy = 0.5 + (x + 0.5) * c - (y + oy) * s, (oy + (y + oy) * c + (x + 0.5) * s) * aspect
    tx:SetTexCoord(ULx, ULy, LLx, LLy, URx, URy, LRx, LRy)
end

-- Permanently pause our rotation animation after it starts playing
local function OnPlayUpdate(self)
    self:SetScript('OnUpdate', nil)
    self:Pause()
end

local function OnPlay(self)
    self:SetScript('OnUpdate', OnPlayUpdate)
end

local function SetValue(self, value)
    -- Correct invalid ranges, preferably just don't feed it invalid numbers
    if value > 1 then value = 1
    elseif value < 0 then value = 0 end

    -- Reverse our normal behavior
    if self._reverse then
        value = 1 - value
    end

    -- Determine which quadrant we're in
    local q, quadrant = self._clockwise and (1 - value) or value -- 4 - floor(value / 0.25)
    if q >= 0.75 then
        quadrant = 1
    elseif q >= 0.5 then
        quadrant = 2
    elseif q >= 0.25 then
        quadrant = 3
    else
        quadrant = 4
    end

    if self._quadrant ~= quadrant then
        self._quadrant = quadrant
        -- Show/hide necessary textures if we need to
        if self._clockwise then
            for i = 1, 4 do
                self._textures[i]:SetShown(i < quadrant)
            end
        else
            for i = 1, 4 do
                self._textures[i]:SetShown(i > quadrant)
            end
        end
        -- Move scrollframe/wedge to the proper quadrant
        self._scrollframe:Hide();
        self._scrollframe:SetAllPoints(self._textures[quadrant])
        self._scrollframe:Show();
    end

    -- Rotate the things
    local rads = value * pi2
    if not self._clockwise then rads = -rads + halfpi end
    Transform(self._wedge, -0.5, -0.5, rads, self._aspect)
    self._rotation:SetDuration(0.000001)
    self._rotation:SetEndDelay(2147483647)
    self._rotation:SetOrigin('BOTTOMRIGHT', 0, 0)
    self._rotation:SetRadians(-rads);
    self._group:Play();
end

local function SetClockwise(self, clockwise)
    self._clockwise = clockwise
end

local function SetReverse(self, reverse)
    self._reverse = reverse
end

local function OnSizeChanged(self, width, height)
    self._wedge:SetSize(width, height) -- it's important to keep this texture sized correctly
    self._aspect = width / height -- required to calculate the texture coordinates
end

-- Creates a function that calls a method on all textures at once
local function CreateTextureFunction(func, self, ...)
    return function(self, ...)
        for i = 1, 4 do
            local tx = self._textures[i]
            tx[func](tx, ...)
        end
        self._wedge[func](self._wedge, ...)
    end
end

-- Pass calls to these functions on our frame to its textures
local TextureFunctions = {
    SetTexture = CreateTextureFunction('SetTexture'),
    SetBlendMode = CreateTextureFunction('SetBlendMode'),
    SetVertexColor = CreateTextureFunction('SetVertexColor'),
}

local function CreateSpinner(parent)
    local spinner = CreateFrame('Frame', nil, parent)

    -- ScrollFrame clips the actively animating portion of the spinner
    local scrollframe = CreateFrame('ScrollFrame', nil, spinner)
    scrollframe:SetPoint('BOTTOMLEFT', spinner, 'CENTER')
    scrollframe:SetPoint('TOPRIGHT')
    spinner._scrollframe = scrollframe

    local scrollchild = CreateFrame('frame', nil, scrollframe)
    scrollframe:SetScrollChild(scrollchild)
    scrollchild:SetAllPoints(scrollframe)

    -- Wedge thing
    local wedge = scrollchild:CreateTexture()
    wedge:SetPoint('BOTTOMRIGHT', spinner, 'CENTER')
    spinner._wedge = wedge

    -- Top Right
    local trTexture = spinner:CreateTexture()
    trTexture:SetPoint('BOTTOMLEFT', spinner, 'CENTER')
    trTexture:SetPoint('TOPRIGHT')
    trTexture:SetTexCoord(0.5, 1, 0, 0.5)

    -- Bottom Right
    local brTexture = spinner:CreateTexture()
    brTexture:SetPoint('TOPLEFT', spinner, 'CENTER')
    brTexture:SetPoint('BOTTOMRIGHT')
    brTexture:SetTexCoord(0.5, 1, 0.5, 1)

    -- Bottom Left
    local blTexture = spinner:CreateTexture()
    blTexture:SetPoint('TOPRIGHT', spinner, 'CENTER')
    blTexture:SetPoint('BOTTOMLEFT')
    blTexture:SetTexCoord(0, 0.5, 0.5, 1)

    -- Top Left
    local tlTexture = spinner:CreateTexture()
    tlTexture:SetPoint('BOTTOMRIGHT', spinner, 'CENTER')
    tlTexture:SetPoint('TOPLEFT')
    tlTexture:SetTexCoord(0, 0.5, 0, 0.5)

    -- /4|1\ -- Clockwise texture arrangement
    -- \3|2/ --

    spinner._textures = {trTexture, brTexture, blTexture, tlTexture}
    spinner._quadrant = nil -- Current active quadrant
    spinner._clockwise = true -- fill clockwise
    spinner._reverse = false -- Treat the provided value as its inverse, eg. 75% will display as 25%
    spinner._aspect = 1 -- aspect ratio, width / height of spinner frame
    spinner:HookScript('OnSizeChanged', OnSizeChanged)

    for method, func in pairs(TextureFunctions) do
        spinner[method] = func
    end

    spinner.SetClockwise = SetClockwise
    spinner.SetReverse = SetReverse
    spinner.SetValue = SetValue

    local group = wedge:CreateAnimationGroup()
    group:SetScript('OnFinished', function() group:Play() end);
    local rotation = group:CreateAnimation('Rotation')
    spinner._rotation = rotation
    spinner._group = group;
    return spinner
end

----------
-- Demo
----------

local spinner1 = CreateSpinner(UIParent)
spinner1:SetPoint('BOTTOMRIGHT', UIParent, 'CENTER', -2, 2)
spinner1:SetSize(64, 64)
spinner1:SetTexture('interface/icons/inv_mushroom_11')

spinner1:SetClockwise(false)
spinner1:SetReverse(false)

local spinner2 = CreateSpinner(UIParent)
spinner2:SetPoint('BOTTOMLEFT', UIParent, 'CENTER', 2, 2)
spinner2:SetSize(64, 64)
spinner2:SetTexture('interface/icons/inv_mushroom_11')

spinner2:SetClockwise(true)
spinner2:SetReverse(false)

local spinner3 = CreateSpinner(UIParent)
spinner3:SetPoint('TOPRIGHT', UIParent, 'CENTER', -2, -2)
spinner3:SetSize(64, 64)
spinner3:SetTexture('interface/icons/inv_mushroom_11')

spinner3:SetClockwise(true)
spinner3:SetReverse(true)

local spinner4 = CreateSpinner(UIParent)
spinner4:SetPoint('TOPLEFT', UIParent, 'CENTER', 2, -2)
spinner4:SetSize(64, 64)
spinner4:SetTexture('interface/icons/inv_mushroom_11')

spinner4:SetClockwise(false)
spinner4:SetReverse(true)

local f = CreateFrame('frame')
local timespent = 0
f:SetScript('OnUpdate', function(self, elapsed)
    timespent = timespent + elapsed
    if timespent >= 3 then
        timespent = 0
    end

    local value = timespent / 3
    spinner1:SetValue(value)
    spinner2:SetValue(value)
    spinner3:SetValue(value)
    spinner4:SetValue(value)
end)
And the diff:
Code:
diff --git a/test4.lua b/test4.lua
index 5dbe0bc..8a0ccfe 100644
--- a/test4.lua
+++ b/test4.lua
@@ -64,14 +64,20 @@ local function SetValue(self, value)
             end
         end
         -- Move scrollframe/wedge to the proper quadrant
+        self._scrollframe:Hide();
         self._scrollframe:SetAllPoints(self._textures[quadrant])
+        self._scrollframe:Show();
     end

     -- Rotate the things
     local rads = value * pi2
     if not self._clockwise then rads = -rads + halfpi end
     Transform(self._wedge, -0.5, -0.5, rads, self._aspect)
-    self._rotation:SetRadians(-rads)
+    self._rotation:SetDuration(0.000001)
+    self._rotation:SetEndDelay(2147483647)
+    self._rotation:SetOrigin('BOTTOMRIGHT', 0, 0)
+    self._rotation:SetRadians(-rads);
+    self._group:Play();
 end

 local function SetClockwise(self, clockwise)
@@ -166,14 +172,10 @@ local function CreateSpinner(parent)
     spinner.SetValue = SetValue

     local group = wedge:CreateAnimationGroup()
+    group:SetScript('OnFinished', function() group:Play() end);
     local rotation = group:CreateAnimation('Rotation')
     spinner._rotation = rotation
-    rotation:SetDuration(0)
-    rotation:SetEndDelay(1)
-    rotation:SetOrigin('BOTTOMRIGHT', 0, 0)
-    group:SetScript('OnPlay', OnPlay)
-    group:Play()
-
+    spinner._group = group;
     return spinner
 end
  Reply With Quote
07-09-16, 09:57 AM   #86
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,259
SetRadians is broken in Legion. I've reported it but....
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.
  Reply With Quote
07-10-16, 08:51 AM   #87
Infus
A Deviate Faerie Dragon
Join Date: Jul 2016
Posts: 13
Originally Posted by Fizzlemizz View Post
SetRadians is broken in Legion. I've reported it but....
Not in my experience. The code I posted uses SetRadians and works.
  Reply With Quote

WoWInterface » Developer Discussions » General Authoring Discussion » The ring theory part two

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off