Layers vs Pages visibility study -->> mistakes in documentation

-we know: page<>scene & layer<>tag :stuck_out_tongue_winking_eye:.-

Based on @DanRathbun code and sWilliams idea I have made a dirty plugin for changing several properties of layers at once.

Some more background info

When I’m doing the opacity changes of the layer, in reality, I’m adding new layer with required opacity and “moving” all entities to that new layer and deleting the original layer, then renaming back to original name…Since I want to “inherit” the original visibility of the layers per page I have to store and restore these properties…

Now I’m facing some problems with the documented and real situation.
So, I made some study and concluded that the Ruby API documentation is not correct:
Specifically, this: Page#layers instance method

However, if you read another part of the documentation like:
Layer#page_behavior instance method
You may start to suspect what the situation is…

At first: If a user switched off to save layers visibility aka if page#use_hidden_layers? is false
then page#layers method returns nil. NOT documented!

At secondly: Otherwise page#layers method returns non-visible layers /array with zero or more layers objects/ but, wrongly documented! (You can use this ‘term’ only if page_behavior has not been ‘touched’.)
As my study is colluded: actually, it will return array containing these layers if layer page_behavior ‘flag query’ / LAYER_HIDDEN_BY_DEFAULT / different than default page visibility state (behavior) for that page. I hope this is the right wording and situation. (Note: My mother never talked to me in English.)

As an excuse the SU Ruby API documentation of layer#page_behavior saying: “A page keeps a list of layers that do not have their default behavior.”

Here is the snippet what I have been used and improved during the study:

# -----------
# test in SU2019.3 (2020.02.22) @Dezmo
# I have 3 scenes and Layer0 Layer1 Layer2 in a model
# Layer2 for reference: it is hidden on all scenes and no any changes on it during test
# -----------
#
# This part is my "concluded" method to get if layer hidden or not on the page
def hidden_on_page?(layer, page)
  # If a user switched off to save layers visibility aka
  # if page#use_hidden_layers? is false then page#layers method returns nil (NOT documented!)
  # else #layers method returns non-visible layers /array with zero or more layers objects/
  #      but (WRONGLY documented!) you can use this 'term' only if page_behavior has not been 'touched'.
  #    So: actually it will return array containing this layer if layer page_behavior 'flag query'
  #       / LAYER_HIDDEN_BY_DEFAULT / different than default page visibility state (behavior) for that page.
  #              Easy, isn't it? (Note: My mother never talked to me in English. ;-) )
  # As an excuse the SU Ruby API documentation of layer#page_behavior saying:
  # "A page keeps a list of layers that do not have their default behavior."
  if page.use_hidden_layers?
    return page.layers.include?(layer) == !hidden_by_default?(layer)
  else
    #this is the only visibility I can query if not stored in scene
    return !layer.visible?
  end
end
#check if somebody changed the default page_behavior
def hidden_by_default?(layer)
  ( layer.page_behavior & LAYER_HIDDEN_BY_DEFAULT ) == LAYER_HIDDEN_BY_DEFAULT
end

#Testing Get/set a layer's visibility / page behavior
#parameters:
#   layer_name_or_index: the layer name or index what you want to test/query (default1) or nothing
#   (I'm lazy) set: new page_behavior if you want to set ( 0, 1, 16, 17, 32, 33 ) or nothing
#prints out to console:
#   the detailed page_behavior  (five flags + integer)
#   the result array or nil of page#layers method (for scene 0,1,2)
#   my "concluded" (aka real) hidden state of layer on the page (for scene 0,1,2)
#usage e.g.: vis_behav  or  vis_behav 1  or  vis_behav "Layer1", 1 ...etc.
def vis_behav(layer_name_or_index=1, set=false)
  flags = [ LAYER_VISIBLE_BY_DEFAULT,
            LAYER_HIDDEN_BY_DEFAULT,
            LAYER_USES_DEFAULT_VISIBILITY_ON_NEW_PAGES,
            LAYER_IS_VISIBLE_ON_NEW_PAGES,
            LAYER_IS_HIDDEN_ON_NEW_PAGES
          ]
  layers = Sketchup.active_model.layers
  layer = layers[layer_name_or_index]
  pages = Sketchup.active_model.pages
  
  # during testing layer visibility changed manually on one scene
  # this is just update that current page saving some clicks...
  pages.selected_page.update if !set
  
  # conditionally set page_behavior
  layer.page_behavior = set if set
  # update all pages to get effect of page_behavior
  pages.each(&:update) if set

  # if #use_hidden_layers? is false then #layers method                     returns nil !!
  layers_hidden_onpage0 = (pages[0].layers)? pages[0].layers.map(&:name) : pages[0].layers
  layers_hidden_onpage1 = (pages[1].layers)? pages[1].layers.map(&:name) : pages[1].layers
  layers_hidden_onpage2 = (pages[2].layers)? pages[2].layers.map(&:name) : pages[2].layers
  
  #query page_behavior
  behavs = []
  flags.each_with_index { |flag, i|
    behavs[i] = (layer.page_behavior & flag ) == flag
  }
  #Just to see the integer 
  behavs<<layer.page_behavior
  
  # printouts
  puts "#{layer.name} page_behavior: #{behavs}"
  puts
  puts "layers_hidden_on page0 #{layers_hidden_onpage0}"
  puts "#{layer.name} hidden_on page0 #{hidden_on_page?(layer, pages[0])}"
  puts
  puts "layers_hidden_on page1 #{layers_hidden_onpage1}"
  puts "#{layer.name} hidden_on page1 #{hidden_on_page?(layer, pages[1])}"
  puts
  puts "layers_hidden_on page2 #{layers_hidden_onpage2}"
  puts "#{layer.name} hidden_on page2 #{hidden_on_page?(layer, pages[2])}"
  puts "-------------------------------------"
end
vis_behav

I leave the suggestion on how to change the documentation to those who are better in English and in Ruby. :blush:
Sure, there can be mistakes and possible improvements…
Of course, any comments or corrections are welcome. :raising_hand_man:t4:

We also discussed this situation in the API Issue Tracker …


Julia is two months ahead of you …

And I logged this …

Oops. I did not follow github :blush: (bookmarked now…)

I’m glad to see if Julia (@eneroth3) thinks similar than me.
However she’s method will fail if page#use_hidden_layers? is false

I experimented further …
Specially I find Layer#page_behaviora little over-complicated…and confused.

First of all the questions:

  1. Does the user have any opportunity (ability) to influence or change Layer#page_behavior at all? I mean in the SU user interface. (I guess not.)
  2. The doc say: “A page keeps a list of layers that do not have their default behavior.
    If a layer does not have his default behavior, than what else he have? How can I determine it? I can query by the 2nd flag (LAYER_HIDDEN_BY_DEFAULT) and if it is true, then that layer will not have his default behavior?? Or what else “does not have his default behavior” is means?
  3. The doc say: The #page_behavior= method is used to control the layer’s visibility behavior on existing and new pages.
    When you Update a page (as opposed to creating a new page) the current visibility of the layer is used.
    So, then what does page_behavior have to do with visibility?
    I concluded in my study that: nothing really directly, it is only to influence the list of layers stored in the page#layers method. Am I right?
    ( …* as opposed to creating a new page* has not been studied yet… but would be nice to get an explanation too …)

According to my current thoughts I have been tuned my method further. Despite the fact that I did not understand everything, It seems it is doing what it’s have to do…

#This part is my "concluded" method to get if layer hidden or not on the page
# @param layer (Sketchup::Layer)
# @param page (Sketchup::Page)
# @return true or false (Boolean)
def hidden_on_page?(layer, page)
  # If a user switched off to save layers visibility aka
  # if page#use_hidden_layers? is false then page#layers method returns nil (NOT documented!)
  # else #layers method returns non-visible layers /array with zero or more layers objects/
  #      but (WRONGLY documented!) you can use this 'term' only if page_behavior has not been 'touched'.
  #    So: actually it will return array containing this layer if layer page_behavior 'flag query'
  #       / LAYER_HIDDEN_BY_DEFAULT / different than default page visibility state (behavior) for that page.
  #              Easy, isn't it? (Note: My mother never talked to me in English. ;-) )
  # As an excuse the SU Ruby API documentation of layer#page_behavior saying:
  # "A page keeps a list of layers that do not have their default behavior."
  if page.use_hidden_layers?
    return page.layers.include?(layer) == !hidden_by_default?(layer)
  else
    #'snapshot' if the page is the current...
    return !layer.visible? if page.parent.selected_page == page
    #...otherwise determined by selected_page (current page)
    return hidden_on_page?(layer, page.parent.selected_page)
  end
end
#check layer#page_behavior by 2nd flag
# @param layer (Sketchup::Layer)
# @return true or false (Boolean)
def hidden_by_default?(layer)
  ( layer.page_behavior & LAYER_HIDDEN_BY_DEFAULT ) == LAYER_HIDDEN_BY_DEFAULT
end

Can anyone give an advice, guidance, comment?

This isn’t necessarily a bad thing. I’d rather have it raise an exception when making such a call than returning an arbitrary value for what is undetermined. If you check if a layer is hidden or not in a scene that doesn’t save layer visibility, there is probably a to call to use_hidden_layers? missing previously in your code.

Sure not. :slight_smile:
The bad thing is: page#layers method returns nil is NOT documented!
image
So, something like this

The layers method retrieves array containing these layers if layer page_behavior ‘flag query’ / LAYER_HIDDEN_BY_DEFAULT / different than default page visibility state (behavior) for that page
Returns:

  • layers - an array with zero or more Layers objects.
  • or nil if page#use_hidden_layers? is false

__

First I have to determine if a layer is hidden or not : this is my goal with the study.
Save it and restore will be more easy afterwards, i guess… :slight_smile:

BTW use_hidden_layers? has been “hidden” there:

# if #use_hidden_layers? is false then #layers method                     returns nil !!
  layers_hidden_onpage0 = (pages[0].layers)? pages[0].layers.map(&:name) : pages[0].layers
  layers_hidden_onpage1 = (pages[1].layers)? pages[1].layers.map(&:name) : pages[1].layers
  layers_hidden_onpage2 = (pages[2].layers)? pages[2].layers.map(&:name) : pages[2].layers

However, There a lots of things are missing in my code above…It is just a kind “of study brute-force” method to check how SU is react …
As you see my post above yours, I have plenty of other confusions… :blush: Any advice?

I came to this discussion because I have a similar problem with the subject of entities hidden in a scene.

because only entities at the root are given in the table obtained with Sketchup::active_model.pages.selected_page.hidden_entities, if a drawingelement remains visible within a hidden group, the drawingelement is considered as visible (not hidden),

2024-08-21_10h51_24

So, from the hidden entities for a scene, we are forced to go through the entire tree to find out if an element is visible or not.

but it’s very heavy to do this in a loop to test the visibility of each entity

One solution I found was to reconstruct the table of hidden entities during a scene change event and make a visibility test function that will consult this array,

It’s not very reliable, because if I change the visibility of some entities, it will not change this array, and I can have errors, (in this array, il put only component or subgroup, no face, edge etc ).

Actually my purpose is to make at the end of the project, a list of all the elements visible in a given scene, because I have other scenes with the cut plan of some elements, but this elements are added in this list, so I can have errors quote with elements in excess.

So a more reliable solution is to rebuild the hidden_entities array, before I do the treatment that gives me this list

Sorry, but no. The topic is about Layers visibility on a scenes, but you are talking about the entities…however the entity overall visibility will depends also which layer associated to it.

You may need to check the #drawing_element_visible? method (and search for it in this forum… for example this topic )

Sorry, I had tested this method #drawing_element_visible? but I definitely made a bug because it works
(EDIT : :thinking:, it doesn’t work properly, I give details in this topic)

Do you want me to delete my misleading post?

you can leave it like that…i don’t mind, just wanted to point out… :wink: