How do I change the level of a floor through the API? - revit-api

I have read this article that explains how to set the level of a floor without moving it. The article refers to the Building Coder where the BuiltInParameter.LEVEL_PARAM is used. However this method no longer works due to updates in the API. I am able to find the new ForgeTypeId of the parameter, but I am told that the LevelId is a Read-Only parameter when I try to run my code. How do I change the level of a floor? In the GUI it's easy, how can this be so hard in the API and so easy in the GUI?
Doing this in RevitPythonShell, my code is the following:
typeid = s0.LookupParameter("Level").GetTypeId()
floorid = ElementId(5873761)
with Transaction(revit.doc,"change level") as t:
p = s0.GetParameter(typeid)
t.Start()
p.Set(floorid)
t.Commit()
Grateful for any help!

You shouldnt have to make a new floor - you can change the level of a floor just like any other Parameter:
levels = list(FilteredElementCollector(doc).OfClass(Level))
newLevelName = 'Level 2'
newLevel = [i for i in levels if i.Name == newLevelName][0]
floor = s0 # your selected floor here
levelParam = floor.LookupParameter('Level')
t = Transaction(doc, 'Changing Floor Level to '+newLevelName)
t.Start()
try:
levelParam.Set(newLevel.Id)
print 'changed level of floor to',level.Name
except Exception as e:
print '!!!',e
t.Commit()
Interestingly, the UserModifiable value of the levelParam is False - turns out users can still modify it though!

Related

What changes occur when using tf_agents.environments.TFPyEnvironment to convert a Python RL environment into a TF environment?

I noticed something weird happening when converting a Python environment into a TF environment using tf_agents.environments.TFPyEnvironment and I'd like to ask you what general changes occur.
To clarify the question please find below my code. I want the environment to simulate (in an oversimplied manner) interactions with a customers who want to buy fruits or vegetables. The agent should learn that when a customer asks for fruits, action 0 should be executed for example.
class CustomEnv(py_environment.PyEnvironment):
def __init__(self):
self._action_spec = array_spec.BoundedArraySpec(
shape=(), dtype=np.int32, minimum=0, maximum=1)
self._observation_spec = array_spec.BoundedArraySpec(
shape=(1,1), dtype=np.int32, minimum=0, maximum=1)
self._state = [0]
self._counter = 0
self._episode_ended = False
self.dictionary = {0: ["Fruits"],
1: ["Vegetables"]}
def action_spec(self):
return self._action_spec
def observation_spec(self):
return self._observation_spec
def _reset(self):
self._state = [0]
self._counter = 0
self._episode_ended = False
return ts.restart(np.array([self._state], dtype=np.int32))
def preferences(self):
return np.random.randint(2)
def pickedBasket(self, yes):
reward = -1.0
if yes:
reward = 0.0
return reward
def _step(self, action):
if self._episode_ended:
self._reset()
if self._counter<50:
self._counter += 1
basket = self.preferences()
condition = basket in self.dictionary[action]
reward = self.pickedBasket(condition)
self._state[0] = basket
if self._counter==50:
self._episode_ended=True
return ts.termination(np.array([self._state],
dtype=np.int32),
reward,
1)
else:
return ts.transition(np.array([self._state],
dtype=np.int32),
reward,
discount=1.0)
When I execute the following to code to check everything is working just fine:
py_env = ContextualMBA()
tf_env = tf_py_environment.TFPyEnvironment(py_env)
time_step = tf_env.reset()
action = 0
next_time_step = tf_env.step(action)
I get an unhashable type: 'numpy.ndarray' for the line condition = basket in self.dictionary[action] so I changed it into condition = basket in self.dictionary[int(action)] and it worked just fine. I'd also like to precise that it worked as a Python environment even without adding the int part. So I'd like to ask what changes the tf_agents.environments.TFPyEnvironment. I don't see how it can influence the type of action action since it isn't related to action_spec or anything (at least directly in the code).
Put basically, tf_agents.environments.TFPyEnvironment is a translator working between your Python environment and the TF-Agents API. The TF-Agents API does not know how many actions it is allowed to choose from, what data to observe and learn from or specially how the choice of actions will influence your custom environment.
Your custom environment is there to provide the rules of the environment and it follows some standards in order for the TFPyEnvironment to be able to translate it correctly so the TF-Agent can work with it. You need to define elements and methods in your custom environment, for example, such as:
__init__()
self._action_spec
self._observation_spec
_reset()
_step()
I'm not sure if your doubt came from the fact that you gave an action = 0 for the agent and, unrelated to the action_spec, the agent actually worked. The action_spec had no relation with your _step() function, and that is correct. Your step function takes some action and applies it to the environment. How this action is shaped is the real point.
The problem is you chose the value and gave it to the tf_env.step() function. If you had actually delegated the choice of action to the agent, by tf_env.step(agent.policy.action) (or tf_env.step(agent.policy.action.action), sometimes TF-Agents make me confuse), the agent would have to look to your action_spec definition to understand what the environment expects the action to look like.
If action_spec is not defined, the agent would not know what to choose between 0 for "Fruits", 1 for "Vegetables" - that you wanted, and defined - or unexpected results as 2 for "Meat", or [3, 2] for 2 bottles of water, since 3 could stand for "Bottle of Water". The TF-Agent needs these definitions so it knows the rules of your environment.
As for the actual changes and what they do with your custom environment code, I believe you would get a better idea by looking at the source code of the TF-Agents library.

calling defined functions in another function in Python3

I am just starting to code. Got a job as a junior developer thanks to other skills. And right now I am self studying to learn Python 3, Django and React. I am 3 weeks in, and i like it thus far.
While learning to code i found some exercises to do and gave my own swing at it. So I am trying to make kind of a lite rpg, where the player and comp chooses an attack and it gets executed in random order.
But i am having trouble making the attacks and everything else to be in callable functions. I could write out the whole code to the length of times, but i want to make it easier to code and expand on. This is what i have at the moment. I think the mistake is in the parameters in the functions? Which make some local instead of global values.
anyways, thank you in advance.
So i tried a lot of different parameters in different places. cant figure it out. also tried to call the move functions inside the player_move function.
import random
def tackle():
dmg = random.randint(150, 280)
return dmg
def bite():
dmg = random.randint(110, 350)
return dmg
def heal():
dmg = random.randint(130, 260)
return dmg
player_health = 1000
comp_health = 1000
alive = True
def player_move():
move = input("Use one of the following moves: \n"
"1. Tackle\n"
"2. Bite\n"
"3. Heal\n"
"Choose the Move: ")
if move == 1 or move == "Tackle" or move == "tackle":
comp_health -= tackle()
elif move == 2 or move == "Bite" or move == "bite":
comp_health -= bite()
elif move == 3 or move == "Heal" or move == "heal":
comp_health -= heal()
return comp_health
player_move()
print(comp_health)
So i figured a few things out. Most of it was in the scope of the functions. Had to call the functions before using them. I ended up rewriting most of it, but got it to work. (edit) The question still stands though. How can I call a function out of scope??

Godot 3.1 telling me that lock() and get_pixel() are nonexistent functions

I'm attempting to generate a height map from a noise texture. As far as I understand, in order to call get_pixel() on an image in this context, the image must first be locked. However, when I attempt to run the program, it exits with the error: Invalid call. Nonexistent function 'lock' in base 'StreamTexture'.
If I attempt to run it without locking the image, I get the error: Invalid call. Nonexistent function 'get_pixel' in base 'StreamTexture'.
I am certain that the instructions that I am following are for the same version of Godot I am running (3.1), so why is the engine telling me that lock() and get_pixel() are nonexistent functions?
My code is here:
extends Spatial
var width
var height
var heightData = {}
var vertices = PoolVector3Array()
var drawMesh = Mesh.new()
func _ready():
var noiseTexture = load("res://noiseTexture.png")
width = noiseTexture.get_width()
height = noiseTexture.get_height()
noiseTexture.lock()
for x in range(0, width):
for y in range(0, height):
heightData[Vector2(x,y)] = noiseTexture.get_pixel(x,y).r
noiseTexture.unlock()
for x in range(0, width-1):
for y in range(0, height-1):
createQuad(x,y)
var surfTool = SurfaceTool.new()
surfTool.begin(Mesh.PRIMITIVE_TRIANGLES)
for i in vertices.size():
surfTool.add_vertex(vertices[i])
surfTool.commit(drawMesh)
$MeshInstance.mesh = drawMesh
func createQuad(x,y):
#First half
vertices.push_back(Vector3(x, heightData[Vector2(x,y)], -y))
vertices.push_back(Vector3(x, heightData[Vector2(x,y+1)], -y-1))
vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y+1)], -y-1))
#Second Half
vertices.push_back(Vector3(x, heightData[Vector2(x,y)], -y))
vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y+1)], -y-1))
vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y)], -y))
Any help is greatly appreciated.
EDIT - I have (tried) to implement the changes that were suggested in the comments (yet I still don't know what to do with the color variable) and have attached a screenshot of my resulting code, as well as some comments I have made to try and explain to myself why the process SHOULD be working (I think). It also shows my node structure, which is why I opted to display this as an image. However, when I try to run this, the program crashes with the error displayed.
Check the docs;
StreamTexture does not have the method lock.
I think the class you are looking to use is Image. The Texture class is typically intended for drawing on the screen or applying to a Material
var noiseImage = Image.new()
noiseImage.load("res://noiseTexture.png")
noiseImage.lock() # Lock the image here
var color = noiseImage.get_pixel(10, 10) # Replace with your height map population
PS:
Just to let you know, I had a lot of issues with memory usage here so make sure you test that also (C# has bad garbage collector though).
You might need to dispose of the image, surface tool, and array mesh (If you remove the terrain object) to maintain optimum performance.
I have run into similar issues with generating heightmap terrains.
nathanfranke is correct and his solution will work.
If you for some reason use an ImageTexture you can call get_data() on that to get the underlying Image. Then you call lock() on the Image just like nathan says in his answer.
Take care to check that your coordinates for get_pixel() are correct. You can set a breakpoint by clicking on the very left edge of the line of code.
I mention this because I was very frustrated until I realized that my coordinate calculations were all int which resulted in the sampled pixel always being at <0,0>.
Here is part of my code for sampling an image into the HeightMapShape.map_data for Bullet heightmap collisions:
var map_w = shape.map_width
var map_h = shape.map_depth
var img_w = img.get_width()
var img_h = img.get_height()
img.lock()
for y in range(0, map_h):
py = float(img_h) * (float(y) / float(map_h))
for x in range(0, map_w):
px = float(img_w) * (float(x) / float(map_w))
index = y * img_h + x
pix = img.get_pixel(px, py)
shp.map_data[index] = pix.r * heightmap_height + heightmap_offset

Revit API Invalid Object

The below snippet is supposed to delete any view that isn't on a sheet OR has no value present in the project view parameter called "View Owner". I've tested this on a blank project and it appears to work as planned. However, on the "real" project, after churning and churning the following error is returned....
Autodesk.Revit.Exceptions.InvalidObjectException: The referenced object is not valid, possibly because it has been deleted from the database, or its creation was undone. at validateNativeInstance(Void* ptr) at Autodesk.Revit.RevitAPIManagedWeakPointer.getValidPtr() at Autodesk.Revit.DB.Element.get_Id() at Microsoft.Scripting.Interpreter.FuncCallInstruction2.Invoke(Object arg0) at IronPython.Runtime.Binding.PythonGetMemberBinder.FastPropertyGet1.GetProperty(CallSite site, TSelfType target, CodeContext context) at Microsoft.Scripting.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame) at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame) at Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1) at IronPython.Compiler.PythonScriptCode.RunWorker(CodeContext ctx) at Microsoft.Scripting.Hosting.ScriptSource.Execute(ScriptScope scope) at RevitPythonShell.RpsRuntime.ScriptExecutor.ExecuteScript(String source)...
I'm not really sure what to make of this. For starters, what is it and what does it mean? Secondly - how would I "catch" this and prevent it from throwing the error? I assume that one of the collected elements is "invalid"? Is there some way to determine if an object is invalid and ignore it? is there a way to get rid of the invalid object? what makes an object invalid?
__window__.Width = 1100
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory, View, Transaction
uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
selection = [ doc.GetElement( elId ) for elId in __revit__.ActiveUIDocument.Selection.GetElementIds() ]
views = []
viewstodelete = []
#Build the list full of views
if len(selection) == 0:
cl_views = FilteredElementCollector(doc)
views = cl_views.OfCategory( BuiltInCategory.OST_Views ).WhereElementIsNotElementType().ToElements()
else:
for sel in selection:
if isinstance(sel, View):
views.append(sel)
count = 0
#Get all views with a view owner
for v in views:
if (v.LookupParameter("Sheet Number") is None or v.LookupParameter("Sheet Number").AsString() == "---") and (v.LookupParameter("View Owner").AsString() is None or v.LookupParameter("View Owner").AsString() == ""):
if v.LookupParameter("View Name") is not None:
vOwner = v.LookupParameter("View Name").AsString()
count= count+1
viewstodelete.append(v)
else:
vOwner = "[View Template] - Not Deleted"
print(vOwner)
t = Transaction(doc, 'Delete Views')
t.Start()
for el in viewstodelete:
doc.Delete(el.Id)
t.Commit()
print "Views in Project: %s" % len(views)
print "Deleted views: %s" % count
I've made the following edit which allows the script to continue running, however, each of these "possibly deleted from the database" errors are quite time consuming to process...
for el in viewstodelete:
t.Start()
try:
doc.Delete(el.Id)
except Exception as e:
print("Error: %s" %str(e))
t.Commit()
There is an IsValidObject method on all Revit elements. You can use it to check if your .NET wrapper is still valid.
The documentation says:
If the corresponding Revit native object is destroyed, or creation of the corresponding object is undone, a managed API object containing it is no longer valid. API methods cannot be called on invalidated wrapper objects.
In your case, may be there is some dependency between the views, so when you delete one, another one is deleted by propagation.
Probably some of the elements you are retrieving are required by the system and are impossible to delete. Your exception handler looks like a good step in the right direction. Look at the printout of that to identify the problematic views and determine how to skip them in the first place. There is probably a good reason why they cannot be deleted. Here is some further analysis of related issues:
http://thebuildingcoder.typepad.com/blog/2012/03/melbourne-devlab.html
http://thebuildingcoder.typepad.com/blog/2015/10/rtc-classes-and-getting-started-with-revit-macros.html#24

How to create a Button Listener in TKinter

I am trying to make a simon says game and am having trouble collecting the users choice to compare against the computer's pattern. I am using four buttons to select the colors that flash on the screen. The problem I am having is my program continues to chug along even if my user hasn't selected a color yet, despite my best efforts to stop it, what can I do to stop the program? Here is my code...
Sequence[]
PosColors = ["Yellow","Red","Blue","Green"]
PlayerChoice[]
Score = 0
def Game(Sequence, PlayerChoice,Score):
Score += 1
PlayerChoice = []
Number = randint(0,3)
Sequence.append(PosColors[Number])
for i in Sequence:
RunTime = Score * 1000
Gui.after(1000, blink, rect, SquareCanvas , i)
RunTime = (Score + 1) * 1000
Gui.after(2000, blink, rect, SquareCanvas , White)
X = len(Sequence)
Index = 0
Color = " "
while Color != " ":
BlueButton.config(command = partial(setSelection,NeonBlue))
RedButton.config(command = partial(setSelection,NeonRed))
YellowButton.config(command = partial(setSelection,NeonYellow))
GreenButton.config(command = partial(setSelection,NeonGreen))
Color = getSelection()
print(Color)
while Color != " ":
PlayerTurn(Sequence,PlayerChoice,Score,Index)
X -= 1
Index +=1
def setSelection(Color):
if Color == NeonBlue:
return "NeonBlue"
elif Color == NeonRed:
return "NeonRed"
elif Color == NeonGreen:
return "NeonGreen"
elif Color == NeonYellow:
return "NeonYellow"
def getSelection():
return TheColor
def PlayerTurn(Sequence, PlayerChoice,Score,Index):
PlayerChoice.append(Color)
print(PlayerChoice)
Label1.config(text = 'Well done! \nYou advance to the next round!')
I am planning on passing this through a checker and to loop it until there is an error but I need to get past this front loop first... I know my logic works as I created it using just lists and the command line before moving on the the graphical portion I just need to know how to get the program to stop to collect the user guess, especially as the length of the pattern gets larger. Originally I had the command function included later where I build my Buttons but this placement seems to get as close as possible to what I am looking for. Any help is appreciated thank you
You're going to need to rethink your main program logic. Tkinter is designed such that you should never have your own infinite loop. Tkinter already has an infinite loop -- the mainloop() method of the root window. You can't write logic that waits for input from the user. You have to think of the GUI as in a perpetual state of waiting, and you need set up handlers (button callbacks or event bindings) to react to events.
You also need to understand that a button command can't "return" anything. "Can't" is a bit strong since it obviously can, but there's nothing waiting for the result -- no place to return it to. You need to design your button function such that it sets a global or instance variable.

Resources