I am running the below code 400 times. I have 60 charts on the sheet. Execution time is 300 sec. If I remove this line
minVal = 0.02 * (cht.Chart.Axes(xlValue).MaximumScale - cht.Chart.Axes(xlValue).MinimumScale)
the speed improves to 190 seconds. This line impacts nothing given minVal is overwritten by 0 right after (for the purpose of the test). I am looking to understand why accessing the axis of the chart is so time consuming and for a workaround.
Sub quickAdjustLabels()
Dim cht As Excel.ChartObject
For Each cht In ActiveSheet.ChartObjects
isProdChart = 0
If cht.Chart.SeriesCollection(1).ChartType <> 5 Then 'different from pie
minVal = 0.02 * (cht.Chart.Axes(xlValue).MaximumScale - cht.Chart.Axes(xlValue).MinimumScale)
minVal = 0
For Each myCollection In cht.Chart.SeriesCollection
'if Stack and if not white visible (white visible are the bottom of waterfall charts / white unvisible are the NC stacks) => remove label is too small
If (myCollection.ChartType = xlColumnStacked Or myCollection.ChartType = xlColumnStacked100) And (myCollection.Format.Fill.Visible = msoFalse Or myCollection.Format.Fill.ForeColor.RGB <> 16777215) Then
myCollection.ApplyDataLabels
vals = myCollection.Values
For i = LBound(vals) To UBound(vals)
If Abs(vals(i)) < minVal Then myCollection.Points(i).HasDataLabel = False
Next
End If
If myCollection.Name = Range("Client") Then isProdChart = 1 'Identify productivity charts
Next myCollection
'Remove labels on productivity charts
If isProdChart = 1 Then
For Each myCollection In cht.Chart.SeriesCollection
If myCollection.ChartType = xlColumnStacked Then myCollection.DataLabels.Delete
Next
End If
End If
Next cht
End Sub
Your problem is not the statement that you pointed out, but actually the statements that apply the DataLabels:
myCollection.ApplyDataLabels
myCollection.Points(i).HasDataLabel = False
Setting the DataLabels take longer time the more points you have in your graph. So trying to avoid running these commands unnecessarily could potentially save you some time. Before setting the values, verify that it is necessary to change them
If Not myCollection.HasDataLabels Then
myCollection.ApplyDataLabels
End If
For i = LBound(Vals) To UBound(Vals)
shouldHaveLabel = True
If Abs(Vals(i)) < MinVal Then
shouldHaveLabel = False
End If
If myCollection.Points(i).HasDataLabel <> shouldHaveLabel Then
myCollection.Points(i).HasDataLabel = shouldHaveLabel
End If
Next
I hope this helps you.
I came to this conclusion by running your code on one of my excel-files with 56 graphs.
I added a time-measure that would tell me at the end of the execution how long time it took to execute, and ran it over and over again, commenting out different blocks of code until I could pinpoint which block was the one taking long time.
Dim tm As Date
tm = Now() 'get timestamp when execution started
...here goes the code to measure...
Debug.Print(Now()-tm)*24*60*60 'Show how many seconds execution took
Related
new to Lua and love. I'm trying to animate a character for my platform game. I know my key binds and all work but I have never animated before, so I don't even know if I'm going in the right direction or not. I've tried looking up guides so I can compare my code to someone else's but it seems that most of them are outdated. Originally my player was just a white rectangle but now im trying to actually give it a sprite.
Here's my main.lua
local STI = require("sti")
local anim8 = require('anim8')
require("MC")
function love.load()
map1 = STI("map/map1.lua", {"box2d"})
Physics = love.physics.newWorld(0,0)
Physics:setCallbacks(beginContact, endContact)
map1:box2d_init(Physics)
map1.layers.Solid.visible = false
background = love.graphics.newImage("assets/Base pack/bg.png")
mc:load()
end
function love.update(dt)
Physics:update(dt)
mc:update(dt)
end
function love.draw()
love.graphics.push()
love.graphics.scale(5,3)
love.graphics.draw(background)
love.graphics.pop()
map1:draw(0, 0, 2, 2)
love.graphics.push()
love.graphics.scale(2,2)
mc:draw()
love.graphics.pop()
end
function love.keypressed(key)
mc:jump(key)
if keypressed == "escape" then
love.event.quit(0)
end
end
function beginContact(a, b, collision)
mc:beginContact(a, b, collision)
end
function endContact(a, b, collision)
mc:endContact(a, b, collision)
end
I don't believe that there is anything wrong with my main but i think it's necessary to recreate the problem (sorry if it makes this too long). Now my mc.lua is where I'm having the problem since im trying to animate the player themselves by giving them a jump idle and walk animation, I think i will give them a damaged and death animation later.
local anim8 = require('anim8')
mc = {}
spr_mc_walk = love.graphics.newImage("assets/mc sprites/1 Pink_Monster/Pink_Monster_Walk_6.png")
local w = anim8.newGrid(32, 32, spr_mc_walk:getWidth(), spr_mc_walk:getHeight())
walk = anim8.newAnimation(w('1-6', 1), 0.1)
spr_mc_idle = love.graphics.newImage("assets/mc sprites/1 Pink_Monster/Pink_Monster_Idle_4.png")
local i = anim8.newGrid(32, 32, spr_mc_idle:getWidth(), spr_mc_idle:getHeight())
idle = anim8.newAnimation(i('1-4', 1), 0.1)
spr_mc_jump = love.graphics.newImage("assets/mc sprites/1 Pink_Monster/Pink_Monster_Jump_8.png")
local j = anim8.newGrid(32, 32, spr_mc_jump:getWidth(), spr_mc_jump:getHeight())
jump = anim8.newAnimation(j('1-8', 1), 0.1)
obj_mc = walk
function mc:load()
self.x = 100
self.y = 0
self.width = 20
self.height = 60
self.xvel = 0
self.yvel = 0
self.maxspeed = 200
self.acceleration = 4000 -- max speed
self.friction = 3900 -- how long it takes them to reach max speed
self.gravity = 1000
self.jumpAmount = -500
self.grounded = false
self.physics = {}
self.physics.body = love.physics.newBody(Physics, self.x, self.y, "dynamic")
self.physics.body:setFixedRotation(true)
self.physics.shape = love.physics.newRectangleShape(self.width, self.height)
self.physics.fixture = love.physics.newFixture(self.physics.body, self.physics.shape)
end
function mc:draw()
love.graphics.draw(obj_mc, self.x, self.y)
end
This is most of my mc or player function, except for movement and such but this has all the code that I'm confused with. I'm pretty sure I wrote the code right I'm just confused on how to actually implement it now that I have it written down. I thought just doing the draw function would actually draw the idle version of the character but then there's where I get the error. If you need anymore code or anymore details just let me know.
The error that this is happening at is
Error
MC.lua:41: bad argument #1 to 'draw' (Drawable expected, got table)
Traceback
[C]: in function 'draw'
MC.lua:41: in function 'draw'
main.lua:30: in function 'draw'
[C]: in function 'xpcall'
I haven't done any of that for a while, but I believe obj_mc, in this case, is a table of the frames. You need to loop through them during update.
local anim8timer = 0
local anim8index = 1
local anim8rate = 0.2 -- pick an animation rate that looks good for your game
function mc:update( dt )
anim8timer = anim8timer +dt -- add delta time
if anim8timer >= anim8rate then
anim8timer = anim8timer -anim8rate -- reset timer
anim8index = anim8index +1 -- next frame
if anim8index > #obj_mc then anim8index = 1 end -- loop frames
end
end
function mc:draw()
love.graphics.draw( obj_mc[ anim8index ], self.x, self.y )
end
They may have a built-in function that accomplishes some of that, so look for anything that resembles this in the Love2D forum.
I'm new to programming in LUA, although I've learned similar languages like JS. It's frustrating if I have to alter the same script in many parts in a group by replacing each script, and I don't know of an elegant way to do it. Instead, I decided to nest all the parts inside of the script. I've seen some examples and I've tried to adapt some of them, but they don't exactly apply to what I want to do and I can't get them to work.
In essence, what I'm trying to do is monitor all the bricks for a player to contact them. I took the original disappearing brick script that was nested inside each brick and modified it. If a part (brick) is touched, that should call the onTouch function, which will make the brick's transparency decrease over time until the in pairs loop is done, after which the brick disappears and CanCollide is turned off. After 2 seconds, it then returns back to normal. I think the problem is with the coding I used to monitor the parts as I don't really understand the right way to monitor multiple objects. Can someone please help? Thanks!
File structure:
function onTouched(brick)
local delay = .1 -- the delay between each increase in transparency (affects speed of disappearance)
local RestoreDelay = 2 -- delay before the brick reappears
local inc = .1 -- how much the brick disappears each time
-- All characters have a Humanoid object
-- if the model has one, it is a character
local h = script.Child:findFirstChild("Humanoid") -- Find Humanoids in whatever touched this
if (h ~=nil) then -- If there is a Humanoid then
h.Health = h.MaxHealth -- Set the health to maximum (full healing)
for x=0,1, inc do
script.Child.Transparency = x+inc
script.Child.CanCollide = true
wait(delay)
end
wait(delay)
script.Child.Transparency = 1
script.Child.CanCollide = false
wait(RestoreDelay)
script.Child.Transparency = 0
script.Child.CanCollide = true
else
end
end
while true do
local bricks=script:GetChildren():IsA("basic.part")
for x=1,brick in pairs(bricks) do
brick.Touched:connect(onTouched(brick)) -- Make it call onTouched when touched
end
end
end
For the most part, you've gotten it right, but you've got a few syntax errors where there are different conventions between JavaScript and Lua.
In JS, you would fetch an array of objects and then bee able to filter it immediately, but in Lua, there is limited support for that. So a JavaScript line like :
var bricks = script.GetChildren().filter(function(item) {
return item === "basic.part"
})
cannot be done all in one line in Lua without assistance from some library. So you'll need to move the check into the loop as you iterate over the objects.
Other than that, the only other thing to change is the onTouched handler's function signature. The BasePart.Touched event tells you which object has touched the brick, not the brick itself. But by creating a higher order function, it's easy to get access to the brick, and the thing that touched it.
-- create a helper function to access the brick and the thing that touched it
function createOnTouched(brick)
-- keep track whether the animation is running
local isFading = false
return function(otherPart)
-- do not do the animation again if it has already started
if isFading then
return
end
local delay = .1 -- the delay between each increase in transparency (affects speed of disappearance)
local restoreDelay = 2 -- delay before the brick reappears
local inc = .1 -- how much the brick disappears each time
-- All characters have a Humanoid object, check for one
local h = otherPart.Parent:FindFirstChild("Humanoid")
if h then
-- heal the player
h.Health = h.MaxHealth
-- start fading the brick
isFading = true
brick.CanCollide = true
for i = 0, 1, inc do
brick.Transparency = i
wait(delay)
end
-- turn off collision for the brick
wait(delay)
brick.Transparency = 1
brick.Anchored = true
brick.CanCollide = false
-- turn the part back on
wait(restoreDelay)
brick.Transparency = 0
brick.CanCollide = true
-- reset the animation flag
isFading = false
end
end
end
-- loop over the children and connect touch events
local bricks = script:GetChildren()
for i, brick in ipairs(bricks) do
if brick:IsA("BasePart") then
local onTouchedFunc = createOnTouched(brick)
brick.Touched:Connect(onTouchedFunc)
end
end
Each brick in my game has a value, and it gets added to leaderstats. But, I want a GUI to show how many BRICKS they have collected. For example, 2 bricks in my game are worth 32 points to be stored in leaderstats. Instead of showing the total, 64, i want it to show the amount of bricks i collected: 2.
Here is the code that collects the bricks and stores them in leaderstats:
script.Parent.Touched:Connect(function(hit)
if hit.Parent:FindFirstChild("Humanoid") ~= nil then
if db == true then
db = false
script.Parent.Transparency = 1
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
player.leaderstats.ElectoralVotes.Value = player.leaderstats.ElectoralVotes.Value + 37.5
script.Sound:Play()
wait(1)
script.Parent:Remove()
end
end
end)
I want to keep the leaderstats in the top right, but on the screen i also want it to show the amount of bricks collected. Does anyone know how i could implement this into my game?
make a Main GUi and insert an TextLabel inside it insert Int value and a script and put the MainGUI in The Starter Gui Pack
next type the below code in the script if TextLabel:
local my_text_gui = script.Parent.TextLabel
local brick_count = my_gui.IntValue
local function change_value()
my_text_gui.Text = brick_count.Value
brick_count:GetPropertyChangedSignal("Value"):Connect(change_value())
Then change the touch detection script and add a value adding code given bellow(works only when the touch detection script is not a local script)
script.Parent.Touched:Connect(function(hit)
if hit.Parent:FindFirstChild("Humanoid") ~= nil then
if db == true then
db = false
script.Parent.Transparency = 1
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
player.leaderstats.ElectoralVotes.Value = player.leaderstats.ElectoralVotes.Value + 37.5
hit.Parent.PlayerGui.Your_Brick_Counter_GUI.IntValue = hit.Parent.PlayerGui.Your_Brick_Counter_GUI.IntValue + 1
script.Sound:Play()
wait(1)
script.Parent:Remove()
end
end
end)
Thanks!
Ok so I have a simple maze game and i'm trying to make it so that when the user wins, a random number of points between 1-7 is generated, which to be clear I got to work. What I can't figure out is when the user resets the game I want the points to stay displayed until the user wins again. When the user wins, I want another number between 1-7 to be added to the previous number, again and again. How do I add to a previous number?
Public Sub Form4_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
Dim r As New Random
Dim r2 As New Random
Dim randoms1 = r2.Next(1, 20)
Dim randoms = r.Next(1, 7)
Integer.TryParse(Label3.Text, total)
If PictureBox2.Bounds.IntersectsWith(finishLabel.Bounds) Then
Label3.Text = 0
Label3.Text = total + randoms
total = Label3.Text
PictureBox2.Location = New Point(350, 30)
ElseIf Nothing Then
finally got it to work, here was what I was looking for.
How can I make the below code more efficient, at the moment it takes roughly 30 seconds to complete
The code will clear all cells within a selection which have a white background & then clear all diagonal borders within a selection.
With only the clear cells with white background code it finishes instantly but as soon as I added the remove borders code it became slow.
Sub ADULTClearOnly()
Set sh2 = ThisWorkbook.Worksheets("ADULT Sign On Sheet")
sh2.Select
Cells.Range("B1:F756").Select
For Each Cell In Selection
If Cell.Interior.Color = Excel.XlRgbColor.rgbWhite Then
Cell.Value = ""
End If
Next
Cells.Range("G1:AO757").Select
For Each Cell In Selection
If Cell.Borders(xlDiagonalUp).LineStyle = xlDiagonalUp Then
Cell.Borders(xlDiagonalUp).LineStyle = xlNone
End If
Next
End Sub
There are several issues in your code:
You don't need to, and should not Select to do these actions
Your logic is flawed: xlDiagonalUp is not a LineStyle
Because you are setting any Diagonal Up borders to none, you don't need to iterate the range, do it in one step
So, replace
Cells.Range("G1:AO757").Select
For Each Cell In Selection
If Cell.Borders(xlDiagonalUp).LineStyle = xlDiagonalUp Then
Cell.Borders(xlDiagonalUp).LineStyle = xlNone
End If
Next
with
sh2.Range("G1:AO757").Borders(xlDiagonalUp).LineStyle = xlNone
Similarly,
sh2.Select
Cells.Range("B1:F756").Select
For Each Cell In Selection
If Cell.Interior.Color = Excel.XlRgbColor.rgbWhite Then
Cell.Value = ""
End If
Next
can be reduced to
For Each Cell In sh2.Range("B1:F756")
If Cell.Interior.Color = Excel.XlRgbColor.rgbWhite Then
Cell.ClearContents
End If
Next